Saturday, June 27, 2009

Storing strings in EEPROM, byte by careful byte

By popular demand (meaning somebody asked :), in this article I will show how I store ASCII characters(1) in an I2C EEPROM, and then how I fetch them for display purpose.

(1) In this article, “character” and “byte” have the same meaning, since one ASCII character can be stored in a byte.

I used this solution in Wise Clock, where 32KB (aprox 16 book pages) of text(2) is displayed line by line, as a sequence of quotations.
(2) "Text" is a sequence of ASCII characters.
Obviously, before the text is retrieved from EEPROM, the text must be written, once, to EEPROM. So, the very first step is to load the text into the EEPROM. To do this, I use two pieces of software:
  • one sketch (running on Arduino) that reads, from the serial port, byte by byte, and then writes it into the EEPROM;

void loop()
{
if (isWrite)
{
if (Serial.available() > 0)
{
// read next character from COM port;
byte incomingByte = Serial.read();
if (isCommand && (incomingByte == 'r' || incomingByte == 'l'))
{
isWrite = false;
Serial.println("read eprom");
switch (incomingByte)
{
case 'r':
isReadLineByLine = false;
break;
case 'l':
isReadLineByLine = true;
break;
}
}
else
{
isCommand = false;
if (incomingByte == '\\')
{
// don't write the backslash;
isCommand = true;
}
else
{
// the following debug lines (serial.print...) take about 25 more ms than the write itself (10 ms);
// it is a good idea to comment them out if we transfer the content (32K) of a whole file;
/*
Serial.print("write to addr ");
Serial.print(crtWriteAddress);
Serial.print(" byte ");
Serial.println(incomingByte, BYTE);
*/
writeNextByte(incomingByte);
}
}
}
}

  • one application (running on PC and already presented here) that reads the characters from a text file and sends them to serial port, byte by byte.
Once in the EEPROM, the text can be retrieved when required. In Wise Clock, I chose to read the text from the EEPROM one line at a time. Since each line ends with a CR (“carriage return”), ASCII character 13, to get a line of text I read characters until a CR is found


else // reading the eeprom;
{
if (isReadLineByLine)
{
// read a whole line into the message buffer;
lastReadByte = readNextByte();
while (lastReadByte != 13 && lastReadByte != 0xFF)
{
*msgBufferPtr++ = lastReadByte;
lastReadByte = readNextByte();
}
*msgBufferPtr++ = 0;
Serial.println(msgBuffer);
msgBufferPtr = &msgBuffer[0]; // reset buffer pointer;
delay(50);
}
else
{
if (0xFF != lastReadByte)
{
Serial.print("read from addr ");
Serial.print(crtReadAddress);
Serial.print(" byte ");
lastReadByte = readNextByte();
Serial.print(lastReadByte, BYTE);
Serial.print(" (");
Serial.print(lastReadByte, DEC);
Serial.println(")");
delay(50);
}
}
}
}
If you want to cut an paste this code, you may need the following lines as well:
// I2C Bus address of 24LC256 EEPROM;
// if more than one eeprom, they will have different addresses (h/w configured);
#define I2C_ID 0x50
// global address of the last written byte;
unsigned int crtWriteAddress = 0;
// global address of the last read byte;
unsigned int crtReadAddress = 0;
boolean isWrite = true;
byte lastReadByte = 0;
// current line string is built by copying each character to the position of the pointer, then advancing the pointer;
char msgBuffer[200] = {0};
char* msgBufferPtr = &msgBuffer[0];
boolean isCommand = false;
boolean isReadLineByLine = false;
void writeByte(int i2cId, unsigned int eeaddress, byte data)
{
int rdata = data;
Wire.beginTransmission(i2cId);
Wire.send((int)(eeaddress >> 8)); // Address High Byte
Wire.send((int)(eeaddress & 0xFF)); // Address Low Byte
Wire.send(rdata);
Wire.endTransmission();
delay(10); // NEED THIS DELAY!
}
void writeNextByte(byte data)
{
writeByte(I2C_ID, crtWriteAddress, data);
crtWriteAddress++;
}
byte readByte(int i2cId, unsigned int eeaddress)
{
byte rdata = 0xFF;
Wire.beginTransmission(i2cId);
Wire.send((int)(eeaddress >> 8)); // Address High Byte
Wire.send((int)(eeaddress & 0xFF)); // Address Low Byte
Wire.endTransmission();
Wire.requestFrom(i2cId, 1);
if (Wire.available()) rdata = Wire.receive();
return rdata;
}
byte readNextByte()
{
byte rdata = readByte(I2C_ID, crtReadAddress);
crtReadAddress++;
return rdata;
}
(The last 4 I2C functions are copied and adapted from playground.)

Monday, June 22, 2009

Arduino Protoshield Comparison - my 2 cents

I just finished reading this Protoshield Comparison article and I felt the need to add my opinion.
Although I did not use the sparkfun protoshield (too expensive, as mentioned), I have bought and used ladyada's, the one from nkcelectronics and the one from nuelectronics. I also made my own, on standard protoboard, plugable into Seeeduino (with the awkward spacing between D7 and D8 resolved).

One of the most important criterion for me was the price. I would go after the PCB only, rather than the kit (with the few, very easy to find, components). The boards themselves are almost similarly priced, at around US$5-6, so they all score similarly here.

Another inportant criterion was the form factor. There are 2 standard sizes, it seems: one full size (as large as the Arduino board), the other one smaller (leaves the USB connector of the Arduino base board exposed, to avoid short circuits, I suppose).
Ladyada's is the only one (out of the 3) full size. The problem with the underneath USB connector (potential shorting by the metallic socket) is solved with an SMD area (no holes above the USB connector). Smaht :)

A third criterion was versatility, that is, how many components can be fit on the board, can there be SMDs, how much work (trace cutting, wiring/trace forming) one needs to do.
Although it provides ample room for TH components, the protoshield from nkcelectronics does not have and SMD area. I also had to do a lot of trace cutting (5 holes on each side of the middle rails are connected, a bit extreme for really small projects). The shield from nuelectronics had a nice SOIC 16 "footprint", where I could fit two 8 pin SOIC ICs (DS1307 RTC and 24LC256 eeprom). Trace cutting is minimal, since only 3 holes in a row (on each side of the middle rails) are connected. Ladyada's shield also fared well, but was kept behind by the 14 pin SOIC, which can hold just one 8 pin IC.

If stackability is important, you will find the shield from nuelectronics the cheapest to stack up, since it uses regular female headers. To stack the other two, one needs to buy the hard-to-find (or expensive) "stacking headers". These headers have longer (and more fragile, easily bendable) pins.

Personnaly, I prefer the protoshield from nuelectronics, since it met my requirements pretty closely. But then, for around same price ($5-6 a piece), I would rather design my own shield and have the PCBs manufactured by seeedstudio's propaganda service (for example), and replace the "blue collar" work (cutting traces, wiring) with "white collar" work (Eagle). This is what I am talking about.


Thursday, June 4, 2009

Lessons learned 2

This is how the board looks after it was populated.


The main reason for the hack was to install a connector to Tx/Rx pins of the microcontroller. So, always wire Rx/Tx pins of the microcontroller to an external connector (e.g. FTDI) that you can use for serial communication. Otherwise, your software issue (sketch fix/upgrade) becomes hardware.

(to be continued)