Home > NXC > NXTCam NXC interface

NXTCam NXC interface

NXTCam V2.0I wrote the whole night on this to get it finished. There are a few dirty parts when implementing a fully functional NXTCam code in NXC. But nevertheless it’s finished and has following features:

  • Fully automatic sensor configuration (only enter port and optionally I2C address)
  • Object/Line tracking
  • Send colormap to NXTCam
  • Get colormap from NXTCam
  • Load colormap from file stored on NXT
  • Store colormap into file on NXT

And soon I hope to implement some code to read the image registers, if this works. According to the documentation it should (I’m speaking of V2.0), but the documentation doesn’t tell you the whole truth… as always. And I learned that in some hard lessons this night.

The first thing I stumbled upon was, that ReadI2CRegister() wouldn’t accept any variables as register address. But after a more or less short time I fixed it and sent the discovered bug with solution to the NXC Bugreport. I also added a few lines of code to redefine the macro that includes the bug. So my code works without waiting for the next NXC release.

After that I tried to figure out for almost three hours why my NXTCam would sometimes return a correct number of objects and sometimes just “78″ which can’t be right of course. I found out that with “78″ the NXTCam tries to tell you “You didn’t enabled tracking properly”… okay. The camera could had said that in a more understandable way. But for most it was my fault, because I didn’t let the code wait for an I2C command to finish before sending the next one. Actually I did that, except for the enable-tracking command.

Last but not least, I had to figure out why my code was returning a wrong colormap when sending a correct one to the camera engine and then getting it back. First I thought my file loading/saving routines were buggy, then I thought my colormap setting/getting function were buggy. But no, I just have to call “get colormap” twice, before actually reading it from the NXTCam’s registers.

This is the story of my night. I hope it can help you, when you are trying to implement a NXTCam-NXC interface or anything similar terrible thing.

And of course the sourcecode (I’ll try to fix the indenting issue):

/* nxtcam.nxh
* Routines for using the Mindsensors NXTCam
*  Copyright (C) 2011  Janosch Gräf
*
*  This program is free software: you can redistribute it and/or modify
*  it under the terms of the GNU Lesser General Public License as published by
*  the Free Software Foundation, either version 3 of the License, or
*  (at your option) any later version.
*
*  This program is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU Lesser General Public License for more details.
*
*  You should have received a copy of the GNU Lesser General Public License
*  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*
* NOTE: Tested with Enhanced Firmware 1.28 and NXTCam 2.0
*/

#ifndef _NXTCAM_NXH_
#define _NXTCAM_NXH_ 1

/* Storage type for sensor port and address */
struct nxtcam_t {
byte port;
byte addr;
byte ver_major;
byte ver_minor;
};

/* Storage type for objects */
struct nxtcam_obj_t {
byte color;
byte x1, y1;
byte x2, y2;
};

/* Storage type for colormap
* NOTE: Exactly this format is used by the camera and by the aNXTCam file format
*/
struct nxtcam_colormap_t {
byte r[16];
byte g[16];
byte b[16];
};

/* Default I2C address */
#define NXTCAM_DEFAULT_I2CADDRESS 0x02

/* Default sensor port */
#define NXTCAM_DEFAULT_PORT 3

/* Max. number of objects */
#define NXTCAM_MAX_OBJECTS 8

/* Size of NXTCam's frame */
#define NXTCAM_FRAME_WIDTH  176
#define NXTCAM_FRAME_HEIGHT 144

/* Signature of Colormap file format
*  NOTE: We use the file format also used by aNXTCam
*/
#define NXTCAM_COLORMAP_FILESIG     "#aNXTCam COLORMAP\n"
#define NXTCAM_COLORMAP_FILESIG_LEN 18

/* Registers */
#define NXTCAM_REG_NUMOBJECTS  0x42
#define NXTCAM_REG_OBJCOLOR(i) (0x43+i*5)
#define NXTCAM_REG_OBJX1(i)    (0x44+i*5)
#define NXTCAM_REG_OBJY1(i)    (0x45+i*5)
#define NXTCAM_REG_OBJX2(i)    (0x46+i*5)
#define NXTCAM_REG_OBJY2(i)    (0x47+i*5)
#define NXTCAM_REG_COLORMAPR(i) (0x80+i)
#define NXTCAM_REG_COLORMAPG(i) (0x90+i)
#define NXTCAM_REG_COLORMAPB(i) (0xA0+i)
/* at the moment no more registers are needed */

/* Commands */
#define NXTCAM_CMD_SORTBYSIZE       'A'
#define NXTCAM_CMD_SELECTOBJTRACK   'B'
#define NXTCAM_CMD_DISABLETRACKING  'D'
#define NXTCAM_CMD_ENABLETRACKING   'E'
#define NXTCAM_CMD_GETCOLORMAP      'G'
#define NXTCAM_CMD_SELECTLINETRACK  'L'
#define NXTCAM_CMD_ADPAON           'N'
#define NXTCAM_CMD_ADPAOFF          'O'
#define NXTCAM_CMD_RESET            'R'
#define NXTCAM_CMD_SETCOLORMAP      'S'
#define NXTCAM_CMD_SORTBYCOLOR      'U'
#define NXTCAM_CMD_NOSORT           'X'

/* Definitions for nxtcam_reset() */
#define nxtcam_reset(cam)  nxtcam_i2c_cmd(cam, NXTCAM_CMD_RESET)

/* Definitions for nxtcam_sort() */
#define nxtcam_sort(cam, m)    nxtcam_cmd(cam, (m))
#define NXTCAM_SORT_DISABLE    NXTCAM_CMD_NOSORT
#define NXTCAM_SORT_SIZE       NXTCAM_CMD_SORTBYSIZE
#define NXTCAM_SORT_COLOR      NXTCAM_CMD_SORTBYCOLOR

/* Definitions for nxtcam_set_trackingmode() */
#define nxtcam_settrackingmode(cam, m) nxtcam_i2c_cmd(cam, m)
#define NXTCAM_TRACK_OBJECT            NXTCAM_CMD_SELECTOBJTRACK
#define NXTCAM_TRACK_LINE              NXTCAM_CMD_SELECTLINETRACK

/* Definitions for nxtcam_enable_tracking() */
#define nxtcam_enable_tracking(cam, on_off) nxtcam_i2c_cmd(cam, on_off?NXTCAM_CMD_ENABLETRACKING:NXTCAM_CMD_DISABLETRACKING)

/* Definitions for nxtcam_enable_adpa() */
#define nxtcam_enable_adpa(cam, on_off) nxtcam_i2c_cmd(cam, on_off?NXTCAM_CMD_ADPAON:NXTCAM_CMD_ADPAOFF)

/* Definitions for nxtcam_get_version() */
#define nxtcam_version(cam) I2CVersion(cam.port, cam.addr)

/* Definitions for nxtcam_get_vendorid() */
#define nxtcam_vendorid(cam) I2CVendorId(cam.port, cam.addr)

/* Definitions for nxtcam_get_deviceid() */
#define nxtcam_deviceid(cam) I2CDeviceId(cam.port, cam.addr)

/* waits until I2C is ready */
inline void nxtcam_i2c_wait(nxtcam_t &cam) {
while (I2CCheckStatus(cam.port)==STAT_COMM_PENDING) {
Wait(1);
}
}

/* Executes I2C command on NXTCAM */
void nxtcam_i2c_cmd(nxtcam_t &cam, byte cmd) {
nxtcam_i2c_wait(cam);
I2CSendCommand(cam.port, cam.addr, cmd);
}

/* read NXTCam I2C register */
byte nxtcam_i2c_regr(nxtcam_t &cam, byte r) {
byte v = 0x00;

nxtcam_i2c_wait(cam);
ReadI2CRegister(cam.port, cam.addr, r, v);

return v;
}

/******************************************************************************
* NOTE: Fix for __MSWriteToRegister
* Removed line "set __WDSC_SensorRegister, _reg \" and replaced it with
* "mov __WDSC_SensorRegister, _reg \" since "_reg" can be a variable.
* TODO Remove this fix when bug is patched in next official release of NBC.
*/
#undef __MSWriteToRegister

#define __MSWriteToRegister(_port, _i2caddr, _reg, _bytes, _result) \
acquire __WDSCmutex \
mov __WDSC_Port, _port \
mov __WDSC_SensorAddress, _i2caddr \
mov __WDSC_SensorRegister, _reg \
arrbuild __WDSC_WriteBytes, _bytes \
call __MSWriteBytesSub \
mov _result, __WDSC_LSStatus \
release __WDSCmutex
/*****************************************************************************/

/* write NXTCam I2C register */
inline void nxtcam_i2c_regw(nxtcam_t &cam, byte r, byte v) {
nxtcam_i2c_wait(cam);
WriteI2CRegister(cam.port, cam.addr, r, v);
}

/* Configure sensor as NXTCam
*  cam      Camera handle
*  port     Sensor port (default: 4)
*  i2caddr  I2C address (default: 0x02)
*  reset    Whether to reset the camera on initialization
*/
bool nxtcam_init(nxtcam_t &cam, int port = NXTCAM_DEFAULT_PORT, int i2caddr = NXTCAM_DEFAULT_I2CADDRESS, bool reset = FALSE) {
string ver, vid, did;
byte n;
byte junk[];

/* set port and address */
cam.port = port;
cam.addr = i2caddr;

/* configure sensor as lowspeed */
SetSensorLowspeed(cam.port);

/* clear I2C buffer
* TODO don't know if I should do that
*/
nxtcam_i2c_wait(cam);
n = I2CBytesReady(cam.port);
ArrayInit(junk, 0, n);
I2CRead(cam.port, n, junk);

/* check vendor ID and device ID */
vid = SubStr(nxtcam_vendorid(cam), 0, 7);
if (strcmp(vid, "mndsnsr")!=0) {
return FALSE;
}
did = SubStr(nxtcam_deviceid(cam), 0, 6); /* string includes an unallowed 0 at position 6 */
if (strcmp(did, "NXTCAM")!=0) {
return FALSE;
}

/* optional: reset */
if (reset) {
nxtcam_reset(cam);
}

/* read version
* NOTE: this can be used for version-dependent code
*/
ver = nxtcam_version(cam);
cam.ver_major = StrToNum(SubStr(ver, 1, 1));
cam.ver_minor = StrToNum(SubStr(ver, 3, 2));

return TRUE;
}

/* Read object (blob) information
*  cam     Camera handle
*  objbuf  Buffer for objects; must be big enough for NXTCAM_MAX_OBJECTS (8) objects
* returns number of objects returned
*/
byte nxtcam_get_objects(nxtcam_t &cam, nxtcam_obj_t &objbuf[]) {
byte r, b, n;
int i;

/* read number of objects */
n = nxtcam_i2c_regr(cam, NXTCAM_REG_NUMOBJECTS);
/* if tracking isn't enabled, this returns 78 (WTF?)... worked hours on it! */
if (n>NXTCAM_MAX_OBJECTS) {
n = 0;
}

/* read objects */
for (i=0; i
objbuf[i].color = nxtcam_i2c_regr(cam, NXTCAM_REG_OBJCOLOR(i));
objbuf[i].x1 = nxtcam_i2c_regr(cam, NXTCAM_REG_OBJX1(i));
objbuf[i].y1 = nxtcam_i2c_regr(cam, NXTCAM_REG_OBJY1(i));
objbuf[i].x2 = nxtcam_i2c_regr(cam, NXTCAM_REG_OBJX2(i));
objbuf[i].y2 = nxtcam_i2c_regr(cam, NXTCAM_REG_OBJY2(i));
}

return n;
}

/* init colormap
* WTF? WHY DO I HAVE TO INITIALIZE THIS?
*/
void nxtcam_init_colormap(nxtcam_colormap_t &colormap) {
ArrayInit(colormap.r, 0, 16);
ArrayInit(colormap.g, 0, 16);
ArrayInit(colormap.b, 0, 16);
}

/* Get colormap from camera engine
*  cam       Camera handle
*  colormap  Buffer for colormap data
* returns whether function was successful
*/
bool nxtcam_get_colormap(nxtcam_t &cam, nxtcam_colormap_t &colormap) {
int i;
byte r, b;

nxtcam_init_colormap(colormap);

/* Send command
* NOTE: WTF? why do I have to send this twice to get correct results
*/
nxtcam_i2c_cmd(cam, NXTCAM_CMD_GETCOLORMAP);
nxtcam_i2c_cmd(cam, NXTCAM_CMD_GETCOLORMAP);

/* Read RGB values */
for (i=0; i<16; i++) {
colormap.r[i] = nxtcam_i2c_regr(cam, NXTCAM_REG_COLORMAPR(i));
colormap.g[i] = nxtcam_i2c_regr(cam, NXTCAM_REG_COLORMAPG(i));
colormap.b[i] = nxtcam_i2c_regr(cam, NXTCAM_REG_COLORMAPB(i));
}

return TRUE;
}

/* Send colormap to camera engine
*  cam       Camera handle
*  colormap  Colormap data
* returns whether function was successful
*/
bool nxtcam_set_colormap(nxtcam_t &cam, nxtcam_colormap_t &colormap) {
int i;
byte t;

/* Write RGB values */
for (i=0; i<16; i++) {
nxtcam_i2c_regw(cam, NXTCAM_REG_COLORMAPR(i), colormap.r[i]);
nxtcam_i2c_regw(cam, NXTCAM_REG_COLORMAPG(i), colormap.g[i]);
nxtcam_i2c_regw(cam, NXTCAM_REG_COLORMAPB(i), colormap.b[i]);
}

/* Send command */
nxtcam_i2c_cmd(cam, NXTCAM_CMD_SETCOLORMAP);

return TRUE;
}

/* Load colormap from file
*  filename  Filename of colormap file
*  colormap  Buffer for colormap data
* returns whether function was successful
*/
unsigned int nxtcam_load_colormap(string filename, nxtcam_colormap_t &colormap) {
byte fh;
unsigned int n;
byte sigbuf[NXTCAM_COLORMAP_FILESIG_LEN];
string sig;

nxtcam_init_colormap(colormap);

if (OpenFileRead(filename, n, fh)==LDR_SUCCESS) {
/* read and check signature */
n = NXTCAM_COLORMAP_FILESIG_LEN;
ReadBytes(fh, n, sigbuf);
sig = ByteArrayToStr(sigbuf);
if (strcmp(sig, NXTCAM_COLORMAP_FILESIG)!=0) {
return FALSE;
CloseFile(fh);
}

/* read RGB data */
n = 16;
ReadBytes(fh, n, colormap.r);
ReadBytes(fh, n, colormap.g);
ReadBytes(fh, n, colormap.b);

/* close file */
CloseFile(fh);

if (n!=16) {
return FALSE;
}

return TRUE;
}
else {
return FALSE;
}
}

/* Store colormap file
*  filename  Filename of colormap file
*  colormap  Colormap data
* returns whether function was successful
* NOTE: If file exists, file will be deleted and recreated
*/
int nxtcam_store_colormap(string filename, nxtcam_colormap_t &colormap) {
byte fh;
byte sig[NXTCAM_COLORMAP_FILESIG_LEN+1];
unsigned int n = 3*16+NXTCAM_COLORMAP_FILESIG_LEN;

/* first try to delete file if it already exists */
DeleteFile(filename);

/* create file for writing */
if (CreateFile(filename, n, fh)==LDR_SUCCESS) {
/* write signature */
StrToByteArray(NXTCAM_COLORMAP_FILESIG, sig);
n = NXTCAM_COLORMAP_FILESIG_LEN;
WriteBytes(fh, sig, n);
if (n!=NXTCAM_COLORMAP_FILESIG_LEN) {
CloseFile(fh);
return FALSE;
}

/* write RGB data */
WriteBytes(fh, colormap.r, n);
WriteBytes(fh, colormap.g, n);
WriteBytes(fh, colormap.b, n);

/* close file */
CloseFile(fh);

if (n!=16) {
return FALSE;
}

return TRUE;
}
else {
return FALSE;
}
}

#endif /* _NXTCAM_NXH_ */

And two test programs:

/* test_nxtcam1.nxh
* Test program for nxtcam.nxh
*  Copyright (C) 2011  Janosch Gräf
*
*  This program is free software: you can redistribute it and/or modify
*  it under the terms of the GNU Lesser General Public License as published by
*  the Free Software Foundation, either version 3 of the License, or
*  (at your option) any later version.
*
*  This program is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU Lesser General Public License for more details.
*
*  You should have received a copy of the GNU Lesser General Public License
*  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*
* NOTE: Loads colormap and tracks objects
*/

#include "nxtcam.nxh"

inline void status(string s) {
ClearScreen();
TextOut(0, LCD_LINE1, s);
Wait(500);
}

inline void error() {
TextOut(0, LCD_LINE2, "ERROR");
while (TRUE) {
Wait(10000);
}
}

inline void done() {
TextOut(0, LCD_LINE2, "done");
Wait(500);
}

task main() {
nxtcam_t cam;
nxtcam_obj_t objects[NXTCAM_MAX_OBJECTS];
nxtcam_colormap_t colormap;
byte i, n;
int x1, x2, y1, y2;

/* initialize NXTCam */
status("init nxtcam");
bool x = nxtcam_init(cam, IN_2);
ClearScreen();
TextOut(0, LCD_LINE1, x?"true":"false");
NumOut(0, LCD_LINE2, cam.ver_major);
NumOut(50, LCD_LINE2, cam.ver_minor);
Wait(5000);
if (!x) { /* use Port 2, I2C address 0x02 (default) */
error();
}
done();

/* load colormap from file */
status("load colormap");
if (!nxtcam_load_colormap("colormap.clm", colormap)) {
error();
}
done();

/* send colormap to camera engine */
status("set colormap");
if (!nxtcam_set_colormap(cam, colormap)) {
error();
}
done();

/* set trackingmode and enable */
status("set mode");
nxtcam_settrackingmode(cam, NXTCAM_TRACK_OBJECT);
done();
status("start tracking");
nxtcam_enable_tracking(cam, TRUE);
done();

/* start tracking */
ClearScreen();
TextOut(0, LCD_LINE1, "start tracking");
TextOut(0, LCD_LINE2, "in 5s");
TextOut(0, LCD_LINE3, "press</pre>
<center>");</center>
<pre>
TextOut(0, LCD_LINE4, "to quit");
Wait(5000);

/* track objects */
while (!ButtonPressed(BTNCENTER, FALSE)) {
n = nxtcam_get_objects(cam, objects);
ClearScreen();
for (i=0; i<n; i++) {
x1 = objects[i].x1; /* objects[i].x1*DISPLAY_WIDTH/NXTCAM_FRAME_WIDTH; */
x2 = objects[i].x2; /* objects[i].x2*DISPLAY_WIDTH/NXTCAM_FRAME_WIDTH; */
y1 = objects[i].y1*DISPLAY_HEIGHT/NXTCAM_FRAME_HEIGHT;
y2 = objects[i].y2*DISPLAY_HEIGHT/NXTCAM_FRAME_HEIGHT;
RectOut(x1, y1, x2-x1, y2-y1);
}
NumOut(0, LCD_LINE1, n);
Wait(100);
}

status("stop tracking");
nxtcam_enable_tracking(cam, FALSE);
done();
}

and:

/* test_nxtcam2.nxh
* Test program for nxtcam.nxh
*  Copyright (C) 2011  Janosch Gräf
*
*  This program is free software: you can redistribute it and/or modify
*  it under the terms of the GNU Lesser General Public License as published by
*  the Free Software Foundation, either version 3 of the License, or
*  (at your option) any later version.
*
*  This program is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU Lesser General Public License for more details.
*
*  You should have received a copy of the GNU Lesser General Public License
*  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*
* NOTE: Loads colormap from file, sends it to camera, gets it from camera
*       again and stores it into another file.
*/

#include "nxtcam.nxh"

inline void status(string s) {
ClearScreen();
TextOut(0, LCD_LINE1, s);
Wait(500);
}

inline void error() {
TextOut(0, LCD_LINE2, "ERROR");
while (TRUE) {
Wait(10000);
}
}

inline void done() {
TextOut(0, LCD_LINE2, "done");
Wait(500);
}

void print_colormap_channel(byte &a[], byte x0, byte y0) {
int i;
byte l[17];
string s;

for (i=0; i<16; i++) {
if (isprint(a[i]) && a[i]<0x80) {
l[i] = a[i];
}
else {
l[i] = 0x2E; // '.'
}
}
l[16] = 0;
s = ByteArrayToStr(l);
TextOut(x0, y0, s);
}

void print_colormap(nxtcam_colormap_t &colormap, byte x0, byte y0) {
print_colormap_channel(colormap.r, x0, y0);
print_colormap_channel(colormap.g, x0, y0-8);
print_colormap_channel(colormap.b, x0, y0-16);
}

task main() {
nxtcam_t cam;
nxtcam_obj_t objects[NXTCAM_MAX_OBJECTS];
nxtcam_colormap_t colormap1, colormap2;
byte i, n;
int x1, x2, y1, y2;

/* initialize NXTCam */
status("init nxtcam");
if (!nxtcam_init(cam, IN_2)) { /* use Port 2, I2C address 0x02 (default) */
error();
}
done();

/* load colormap from file */
status("load colormap1");
if (!nxtcam_load_colormap("colormap.clm", colormap1)) {
error();
}
done();

/* send colormap to camera engine */
status("set colormap1");
if (!nxtcam_set_colormap(cam, colormap1)) {
error();
}
done();

/* get colormap from camera engine */
status("get colormap2");
if (!nxtcam_get_colormap(cam, colormap2)) {
error();
}
done();

/* store colormap on NXT */
status("store colormap2");
if (!nxtcam_store_colormap("colormap2.clm", colormap2)) {
error();
}
done();

/* display colormaps */
ClearScreen();
TextOut(0, LCD_LINE1, "Colormap1:");
print_colormap(colormap1, 0, LCD_LINE2);
TextOut(0, LCD_LINE5, "Colormap2:");
print_colormap(colormap2, 0, LCD_LINE6);

while (TRUE) {
Wait(10000);
}
}
About these ads
Categories: NXC Tags: ,
  1. HaWe
    November 25, 2011 at 3:25 pm

    hallo,
    das sieht ja ungeheuer interessant aus…!
    Wozu dienen deine Color-Maps bzw. wie unterscheiden sich die von dem Mindstorms-Tool (nxtCamViewer), mit dem man ja auch Farben samt Farbspielräumen am PC programmieren kann?
    Gibt es bei deinen Tools die Möglichkeit, z.B. auch 6 oder 7 Farben für eine Blob-Erkennung zu programmieren, mit der man z.B. dann alle Farben auf dem Rubik’s Cube erkennen kann?

  2. HaWe
    November 25, 2011 at 3:48 pm

    ps
    … für den Rubic’s Cube braucht man neben 6 Farben ntl dann auch noch 9 BLOBs, die erkannt werden müssen, nicht die vorgegebenen max. 8!

  3. November 28, 2011 at 4:07 pm

    Der Unterschied bei diesem Code ist der, dass das Programm direkt auf dem Mindstorms läuft und trotzdem z.B. Colormaps laden kann. Normal muss man ja z.B. mit nxtCamViewer die NXTCam per USB mit dem Computer verbinden. Hier kann man die Colormap einfach als Datei auf dem NXT ablegen und dann laden, wenn ein Programm gestartet wird.
    Natürlich muss man sich irgendwo die Colormaps zusammenklicken. Das kann man dann mit aNXTCam machen. Damit kann man dann Colormaps als Datei speichern und mit nxt_upload (in aNXT enthalten) auf den NXT übertragen.
    Also 8 Farbbereiche kann man mit der NXTCam einstellen. Leider kann man halt nur 8 Blobs erkennen. Für den Rubic’s Cube reicht das dann nicht. Ich hatte aber auch schon versucht einzelne Pixel der NXTCam auszulesen. Mit der NXTCam v3 soll das funktionieren. Dann könnte man damit die einzelnen Quadrate des Rubic’s Cube erkennen.

  4. HaWe
    December 2, 2011 at 7:01 pm

    ah, ich glaube, ich verstehe!
    super Sache!

  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: