Wednesday, July 20, 2011

Tutorial: Arduino and the NXP SAA1064 4-digit LED display driver

This is chapter thirty-nine of a series originally titled “Getting Started/Moving Forward with Arduino!” by John Boxall – a series of articles on the Arduino universe. The first chapter is here, the complete series is detailed here. Any files from tutorials will be found here.

Welcome back fellow arduidans!

In this article we investigate controlling the NXP (formerly Philips) SAA1064 4-digit LED display driver IC with Arduino and the I2C bus interface. If you are not familiar with using the I2C bus, please read my tutorials (parts one and two) before moving on. Although the SAA1064 is not the newest on the market, it is still popular, quite inexpensive and easy to source. Furthermore as it is controlled over the I2C bus – you don’t waste any digital I/O pins on your Arduino, and you can also operate up to four SAA1064s at once (allowing 16 digits!). Finally, it has a constant-current output – keeping all the segments of your LED display at a constant brightness (which is also adjustable).  So let’s get started…

Here is an example of the SAA1064 in SOIC surface mount packaging:

It measures around 15mm in length. For use in a solderless breadboard, I have soldered the IC onto a through-hole adaptor:

The SAA1064 is also available in a regular through-hole DIP package. At this point, please download the data sheet (.pdf) as you will need to refer to it during the article. Next, our LED display examples. We need common-anode displays, and for this article we use two Agilent HDSP521G two-digit modules (data sheet [.pdf]) as shown below:

For the uninitiated – a common anode display has all the segments’ anodes connected together, with the cathodes terminated separately. For example, our LED displays are wired as such:

Notice the anodes for the left digit are pin 14, and the right digit pin 13. A device that is connected to all the cathodes (e.g. our SAA1064) will control the current flow through each element – thereby turning each segment on (and controlling the brightness) or off. Our SAA1064 is known as acurrent-sink as the current flows through the LED, and then sinks into the IC.

Now, let’s get it connected. There is an excellent demonstration circuit on page twelve of the data sheet that we will follow for our demonstrations:

It looks pretty straight-forward, and it is. The two transistors are standard NPN-type, such as PN2222. The two transistors are used to each turn on or off a pair of digits – as the IC can only drive digits 1+3 or 2+4 together. (When presented in real life the digits are numbered 4-3-2-1). So the pairs are alternatively turned on and off at a rapid rate, which is controlled by the capacitor between pin 2 and GND. The recommended value is 2.7 nF. At the time of writing, I didn’t have that value in stock, so chose a 3.3 nF instead. However due to the tolerance of the ceramic capacitor it was actually measured to be 2.93 nF:

So close enough to 2.7 nF will be OK. The other capacitor shown between pins 12 and 13 is a standard 0.1 uF smoothing capacitor. Pin 1 on the SAA1064 is used to determine the I2C bus address – for our example we have connected it straight to GND (no resistors at all) resulting in an address of 0×70. See the bottom page five of the data sheet for other address options. Power for the circuit can be taken from your Arduino’s 5V pin – and don’t forget to connect the circuit GND to Arduino GND. You will also use 4.7k ohm pull-up resistors on the SDA and SCL lines of the I2C bus.

The last piece of the schematic puzzle is how to connect the cathodes of the LED displays to the SAA1064. Display pins 14 and 13 are the common anodes of the digits.

The cathodes for the left-hand display module:

  • LED display pins 4, 16, 15, 3, 2, 1, 18 and 17 connect to SAA1064 pins 22, 21, 20, 19, 18, 17, 16 and 15 respectively (that is, LED pin 4 to IC pin 22, etc.);
  • LED display pins 9, 11, 10, 8, 6, 5, 12 and 7 also connect to SAA1064 pins 22, 21, 20, 19, 18, 17, 16 and 15 respectively.
The cathodes for the right-hand display module:
  • LED display pins 4, 16, 15, 3, 2, 1, 18 and 17 connect to SAA1064 pins 3, 4, 5, 6, 7, 8, 9 and 10 respectively;
  • LED display pins  9, 11, 10, 8, 6, 5, 12 and 7 also connect to SAA1064 pins 3, 4, 5, 6, 7, 8, 9 and 10 respectively.
Once your connections have been made, you could end up with spaghetti junction like this…
Note the use of the Arduino Nano – they are very convenient when doing small prototyping on solderless breadboards.
-=-
Now it is time to consider the Arduino sketch to control out SAA1064. Each write request to the SAA1064 requires several bytes. We either send a control command (to alter some of the SAA1064 parameters such as display brightness) or a display command (actually display numbers). For our example sketches the I2C bus address “0×70 >> 1″ is stored in the byte variable saa1064. First of all, let’s look at sending commands, as this is always done first in a sketch to initiate the SAA1064 before sending it data.
As always, we send the address down the I2C bus to awaken the SAA1064 using
Wire.beginTransmission(saa1064);
Then the next byte is the instruction byte. If we send zero:
Wire.send(B00000000);
… the IC expects the next byte down the bus to be the command byte. And finally our command byte:
Wire.send(B01000111);
The control bits are described on page six of the data sheet. However – for four-digit operation bits 0, 1 and 2 should be 1; bit 3 should be 0; and bits 4~6 determine the amount of current allowed to flow through the LED segments. Note that they are cumulative, so if you set bits 5 and 6 to 1 – 18 mA of current will flow. We will demonstrate this in detail later on.
Next, to send actual numbers to be displayed is slightly different. Note that the digits are numbered (from left to right) 4 3 2 1. Again, we first send the address down the I2C bus to awaken the SAA1064 using
Wire.beginTransmission(saa1064);
Then the next byte is the instruction byte. If we send 1, the next byte of data will represent digit 1. If that follows with another byte, it will represent digit 2. And so on. So to send data to digit 1, send
Wire.send(B00000001);
Although sending binary helps with the explanation, you can send decimal equivalents. Next, we send a byte for each digit (from right to left). Each bit in the byte represents a single LED element of the digit as well as the decimal point. Note how the elements are labelled (using A~G and DP) in the following image:
The digit bytes describe which digit elements to turn on or off. The bytes are described as such: Bpgfedcba. (p is the decimal point). So if you wanted to display the number 7, you would send B00000111 – as this would turn on elements a, b and c. To add the decimal point with 7 you would send B10000111. You can also send the byte as a decimal number. So to send the digit 7 as a decimal, you would send 7 – as 00000111 in base-10 is 7. To include the decimal point, send 135 – as 100000111 in base-10 is 135. Easy! You can also create other characters such as A~F for hexadecimal. In fact let’s do that now in the following example sketch (download):
/* Example 39.1 - NXP SAA1064 I2C LED Driver IC Demo I Demonstrating display of digits http://tronixstuff.wordpress.com/tutorials > chapter 39 John Boxall July 2011 | CC by-sa-nc */  #include "Wire.h"  // enable I2C bus  byte saa1064 = 0x70 >> 1; // define the I2C bus address for our SAA1064 (pin 1 to GND)  int digits[16]={63, 6, 91, 79, 102, 109, 125,7, 127, 111, 119, 124, 57, 94, 121, 113}; // these are the byte representations of pins required to display each digit 0~9 then A~F  void setup() { Wire.begin(); // start up I2C bus delay(500); initDisplay(); }  void initDisplay() // turns on dynamic mode and adjusts segment current to 12mA { Wire.beginTransmission(saa1064); Wire.send(B00000000); // this is the instruction byte. Zero means the next byte is the control byte Wire.send(B01000111); // control byte (dynamic mode on, digits 1+3 on, digits 2+4 on, 12mA segment current Wire.endTransmission(); }  void displayDigits() // show all digits 0~9, A~F on all digits of display { for (int z=0; z<16; z++) { Wire.beginTransmission(saa1064); Wire.send(1); // instruction byte - first digit to control is 1 (right hand side) Wire.send(digits[z]); // digit 1 (RHS) Wire.send(digits[z]); // digit 2 Wire.send(digits[z]); // digit 3 Wire.send(digits[z]); // digit 4 (LHS) Wire.endTransmission(); delay(500); }
// now repeat but with decimal point
for (int z=0; z<16; z++) { Wire.beginTransmission(saa1064); Wire.send(1); // instruction byte - first digit to control is 1 (right hand side) Wire.send(digits[z]+128); // digit 1 (RHS) Wire.send(digits[z]+128); // digit 2 Wire.send(digits[z]+128); // digit 3 Wire.send(digits[z]+128); // digit 4 (LHS) Wire.endTransmission(); delay(500); } }  void clearDisplay() // clears all digits { Wire.beginTransmission(saa1064); Wire.send(1); // instruction byte - first digit to control is 1 (right hand side) Wire.send(0); // digit 1 (RHS) Wire.send(0); // digit 2 Wire.send(0); // digit 3 Wire.send(0); // digit 4 (LHS) Wire.endTransmission(); }  void loop() { displayDigits(); clearDisplay(); delay(1000); }

In the function initDisplay() you can see an example of using the instruction then the control byte. In the function clearDisplay() you can see the simplest form of sending digits to the display – we send 0 for each digit to turn off all elements in each digit. The bytes that define the digits 0~9 and A~F are stored in the array digits[]. For example, the digit zero is 63 in decimal, which is B00111111 in binary – which turns on elements a,b,c,d,e and f. Finally, notice the second loop in displayDigits() – 128 is added to each digit value to turn on the decimal point. Before moving on, let’s see it in action:

Our next example revisits the instruction and control byte – we change the brightness of the digits by setting bits 4~6 in the control byte. Each level of brightness is separated into a separate function, and should be self-explanatory. Here is the sketch (download):

/* Example 39.2 - NXP SAA1064 I2C LED Driver IC Demo II Demonstrating brightness levels via adjusting segment current http://tronixstuff.wordpress.com/tutorials > chapter 39 John Boxall July 2011 | CC by-sa-nc */  #include "Wire.h"   // enable I2C bus  byte saa1064 = 0x70 >> 1; // define the I2C bus address for our SAA1064 (pin 1 to GND)  int digits[16]={63, 6, 91, 79, 102, 109, 125,7, 127, 111, 119, 124, 57, 94, 121, 113}; // these are the byte representations of pins required to display each digit 0~9 then A~F  void setup() { Wire.begin(); // start up I2C bus delay(500); init12ma(); }  void init3ma() // turns on dynamic mode and adjusts segment current to 3mA { Wire.beginTransmission(saa1064); Wire.send(B00000000); // this is the instruction byte. Zero means the next byte is the control byte Wire.send(B00010111); // control byte (dynamic mode on, digits 1+3 on, digits 2+4 on, 3mA segment current Wire.endTransmission(); }  void init6ma() // turns on dynamic mode and adjusts segment current to 6mA { Wire.beginTransmission(saa1064); Wire.send(B00000000); // this is the instruction byte. Zero means the next byte is the control byte Wire.send(B00100111); // control byte (dynamic mode on, digits 1+3 on, digits 2+4 on, 6mA segment current Wire.endTransmission(); }  void init9ma() // turns on dynamic mode and adjusts segment current to 9mA { Wire.beginTransmission(saa1064); Wire.send(B00000000); // this is the instruction byte. Zero means the next byte is the control byte Wire.send(B00110111); // control byte (dynamic mode on, digits 1+3 on, digits 2+4 on, 9mA segment current Wire.endTransmission(); }  void init12ma() // turns on dynamic mode and adjusts segment current to 12mA { Wire.beginTransmission(saa1064); Wire.send(B00000000); // this is the instruction byte. Zero means the next byte is the control byte Wire.send(B01000111); // control byte (dynamic mode on, digits 1+3 on, digits 2+4 on, 12mA segment current Wire.endTransmission(); }  void init18ma() // turns on dynamic mode and adjusts segment current to 12mA { Wire.beginTransmission(saa1064); Wire.send(B00000000); // this is the instruction byte. Zero means the next byte is the control byte Wire.send(B01100111); // control byte (dynamic mode on, digits 1+3 on, digits 2+4 on, 18mA segment current Wire.endTransmission(); }  void init21ma() // turns on dynamic mode and adjusts segment current to 12mA { Wire.beginTransmission(saa1064); Wire.send(B00000000); // this is the instruction byte. Zero means the next byte is the control byte Wire.send(B01110111); // control byte (dynamic mode on, digits 1+3 on, digits 2+4 on, 21mA segment current Wire.endTransmission(); }  void loop() { int d=250; // for delay // first, light up all segments Wire.beginTransmission(saa1064); Wire.send(0); // instruction byte - Zero means the next byte is the control byte Wire.send(B01001111); // set current and bit 3 to 1 for all segments on Wire.endTransmission();  // now loop through the brightness levels do { init3ma(); delay(d); init6ma(); delay(d); init9ma(); delay(d); init12ma(); delay(d); init18ma(); delay(d); init21ma(); delay(d); } while (1>0); }

And again, see it in action:

For our final example, there is a function displayInteger(a,b) which can be used to easily display numbers from 0~9999 on the 4-digit display. The parameter a is the number to display, and b is the leading-zero control – zero – off, one – on. The function does some maths on the integet to display and separates the digits for each column, then sends them to the SAA1064 in reverse order. By now you should be able to understand the following sketch (download):

/* Example 39.3 - NXP SAA1064 I2C LED Driver IC Demo III Displaying numbers on command http://tronixstuff.wordpress.com/tutorials > chapter 39 John Boxall July 2011 | CC by-sa-nc */  #include "Wire.h" // enable I2C bus  byte saa1064 = 0x70 >> 1; // define the I2C bus address for our SAA1064 (pin 1 to GND)  int digits[17]={ 63, 6, 91, 79, 102, 109, 125,7, 127, 111, 119, 124, 57, 94, 121, 113, 0}; // these are the byte representations of pins required to display each digit 0~9 then A~F, and blank digit  void setup() { Wire.begin(); // start up I2C bus delay(500); initDisplay(); }  void initDisplay() // turns on dynamic mode and adjusts segment current to 12mA { Wire.beginTransmission(saa1064); Wire.send(B00000000); // this is the instruction byte. Zero means the next byte is the control byte Wire.send(B01000111); // control byte (dynamic mode on, digits 1+3 on, digits 2+4 on, 12mA segment current Wire.endTransmission(); }  void clearDisplay() { Wire.beginTransmission(saa1064); Wire.send(1); // start with digit 1 (right-hand side) Wire.send(0); // blank digit 1 Wire.send(0); // blank digit 2 Wire.send(0); // blank digit 3 Wire.send(0); // blank digit 4 Wire.endTransmission(); }  void displayInteger(int num, int zero) // displays the number 'num' // zero = 0 - no leading zero // zero = 1 - leading zero { int thousand, hundred, ten, one; // breakdown number into columns thousand = num/1000; hundred = (num-(thousand*1000))/100; ten = (num-((thousand*1000)+(hundred*100)))/10; one = num-((thousand*1000)+(hundred*100)+(ten*10)); if (zero==1) // yes to leading zero { Wire.beginTransmission(saa1064); Wire.send(1); Wire.send(digits[one]); Wire.send(digits[ten]); Wire.send(digits[hundred]); Wire.send(digits[thousand]); Wire.endTransmission(); delay(10); } else if (zero==0) // no to leading zero { if (thousand==0) { thousand=16; } if (hundred==0 && num<100) { hundred=16; } if (ten==0 && num<10) { ten=16; } Wire.beginTransmission(saa1064); Wire.send(1); Wire.send(digits[one]); Wire.send(digits[ten]); Wire.send(digits[hundred]); Wire.send(digits[thousand]); Wire.endTransmission(); delay(10); } }  void loop() { for (int a=0; a<251; a++) // display 0~250 without leading zero { displayInteger(a,0); delay(20); } delay(1000); clearDisplay(); delay(1000);  for (int a=0; a<1000; a++) // display 0~999 with leading zero { displayInteger(a,1); delay(5); } }

And the final example in action:

So there you have it – another useful IC that can be used in conjunction with our Arduino systems to make life easier and reduce the required digital output pins. I hope you enjoyed reading this as much as I did writing it for you.

If you have any questions about the processes or details in this article, please ask in our Google Group – dedicated to the projects and related items on this website. Sign up – it’s free, there is the odd competition or give-away –  and we can all learn something. Or follow tronixstuff on twitter and facebook. High resolution images available on flickr.

Otherwise, have fun, stay safe, be good to each other – and make something!

Sunday, July 17, 2011

Kit Review – Snootlab Mémoire SD card/RTC/prototyping shield

Hello Readers

In this article we will examine another product from a bundle sent for review by Snootlab, a Toulouse, France-based company that in their own words:

… designs and develops electronic products with an Open Hardware and Open Source approach. We are particularly specialized in the design of new shields for Arduino. The products we create are licensed under CC BY-SA v3.0 (as shown in documents associated with each of our creations). In accordance with the principles of the definition of Open Source Hardware (OSHW), we have signed it the 10th February 2011. We wish to contribute to the development of the ecosystem of “do it yourself” through original designs of products, uses and events.

Furthermore, all of their products are RoHS compliant and as part of the Open Hardware commitment, all the design files are available from the Snootlab website.

The subject of the review is the Snootlab Mémoire – an SD card data logging shield with on-board DS1307 real time clock [and matching backup battery] and prototyping area. It uses the standard SdFat library to write to normal SD memory cards formatted in FAT16 or FAT32. You can download the library from here. The real time clock IC is an easy to use I2C-interface model, and I have documented its use in great detail in this tutorial.

Once again, shield assembly is simple and quite straightforward. You can download an illustrated assembly guide from here, however it is in French. But everything you need to know is laid out on the PCB silk-screen, or the last page of the instructions. The it arrives in a reusable ESD bag:

… and all the required parts are included – including an IC socket and the RTC backup battery:

… the PCB is thick, with a very detailed silk-screen. Furthermore, it arrives with the SD card and 3.3V LDO (underneath) already pre-soldered – a nice touch:

The order of soldering the components is generally a subjective decision, and in this case I started with the resistors:

… and then worked my way out, but not fitting the battery nor IC until last. Intrestingly, the instructions require the crystal to be tacked down with some solder onto the PCB. Frankly I didn’t think it would withstand the temperature, however it did and all is well:

Which leaves us with a fully-assembled Mémoire shield ready for action:

Please note that a memory card is not included with the kit. If you are following along with your own Mémoirethe first thing to do after inserting the battery, IC and shield into your Arduino board and run some tests to ensure all is well. First thing is to test the DS1307 real-time clock IC. You can use the following sketch from chapter seven of my Arduino tutorial series: (download sketch file)

/* Example 7.3 reading and writing to the Maxim DS1307 real time clock IC tronixstuff.com/tutorials based on code by Maurice Ribble 17-4-2008 - http://www.glacialwanderer.com/hobbyrobotics */  #include "Wire.h" #define DS1307_I2C_ADDRESS 0x68  // Convert normal decimal numbers to binary coded decimal byte decToBcd(byte val) { return ( (val/10*16) + (val%10) ); }  // Convert binary coded decimal to normal decimal numbers byte bcdToDec(byte val) { return ( (val/16*10) + (val%16) ); }  // 1) Sets the date and time on the ds1307 // 2) Starts the clock // 3) Sets hour mode to 24 hour clock  // Assumes you're passing in valid numbers  void setDateDs1307(byte second,        // 0-59 byte minute,        // 0-59 byte hour,          // 1-23 byte dayOfWeek,     // 1-7 byte dayOfMonth,    // 1-28/29/30/31 byte month,         // 1-12 byte year)          // 0-99 { Wire.beginTransmission(DS1307_I2C_ADDRESS); Wire.send(0); Wire.send(decToBcd(second));    // 0 to bit 7 starts the clock Wire.send(decToBcd(minute)); Wire.send(decToBcd(hour)); Wire.send(decToBcd(dayOfWeek)); Wire.send(decToBcd(dayOfMonth)); Wire.send(decToBcd(month)); Wire.send(decToBcd(year)); Wire.send(00010000); // sends 0x10 (hex) 00010000 (binary) to control register - turns on square wave Wire.endTransmission(); }  // Gets the date and time from the ds1307 void getDateDs1307(byte *second, byte *minute, byte *hour, byte *dayOfWeek, byte *dayOfMonth, byte *month, byte *year) { // Reset the register pointer Wire.beginTransmission(DS1307_I2C_ADDRESS); Wire.send(0); Wire.endTransmission();  Wire.requestFrom(DS1307_I2C_ADDRESS, 7);  // A few of these need masks because certain bits are control bits *second     = bcdToDec(Wire.receive() & 0x7f); *minute     = bcdToDec(Wire.receive()); *hour       = bcdToDec(Wire.receive() & 0x3f);  // Need to change this if 12 hour am/pm *dayOfWeek  = bcdToDec(Wire.receive()); *dayOfMonth = bcdToDec(Wire.receive()); *month      = bcdToDec(Wire.receive()); *year       = bcdToDec(Wire.receive()); }  void setup() { byte second, minute, hour, dayOfWeek, dayOfMonth, month, year; Wire.begin(); Serial.begin(9600);  // Change these values to what you want to set your clock to. // You probably only want to set your clock once and then remove // the setDateDs1307 call.  second = 0; minute = 33; hour = 22; dayOfWeek = 2; dayOfMonth = 4; month = 07; year = 11; setDateDs1307(second, minute, hour, dayOfWeek, dayOfMonth, month, year);
}  void loop() { byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;  getDateDs1307(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year); Serial.print(hour, DEC);// convert the byte variable to a decimal number when being displayed Serial.print(":"); if (minute<10) { Serial.print("0"); } Serial.print(minute, DEC); Serial.print(":"); if (second<10) { Serial.print("0"); } Serial.print(second, DEC); Serial.print("  "); Serial.print(dayOfMonth, DEC); Serial.print("/"); Serial.print(month, DEC); Serial.print("/"); Serial.print(year, DEC); Serial.print("  Day of week:"); switch(dayOfWeek){ case 1: Serial.println("Sunday"); break; case 2: Serial.println("Monday"); break; case 3: Serial.println("Tuesday"); break; case 4: Serial.println("Wednesday"); break; case 5: Serial.println("Thursday"); break; case 6: Serial.println("Friday"); break; case 7: Serial.println("Saturday"); break; } //  Serial.println(dayOfWeek, DEC); delay(1000); }

If you are unsure about using I2C, please review my tutorial which can be found here. Don’t forget to update the time and date data in void setup(),and also comment out the setDateDS1307() function and upload the sketch a second time. The sketch output will be found on the serial monitor box – such as:

Those of you familiar with the DS1307 RTC IC know that it can generate a nice 1 Hz pulse. To take advantage of this the SQW pin has an access hole on the PCB, beetween R10 and pin 8 of the IC:

For instruction on how to activate the SQW output, please visit the last section of this tutorial.

The next test is the SD card section of the shield. If you have not already done so, download and install the SdFat libary. Then, in the Arduino IDE, select File > Examples > SdFat > SdFatInfo. Insert the formatted (FAT16/32) SD card into the shield, upload the sketch, then open the serial monitor. You should be presented with something like this:

As you can see the sketch has returned various data about the SD card. Finally, let’s log some data. You can deconstruct the excellent example that comes with the SdFat library titled SdFatAnalogLogger (select File > Examples > SdFat > SdFatAnalogLogger). Using the functions:

file.print();
file.println();

you can “write” to the SD card in the same way as you would the serial output (that is, the serial monitor).

If you have reached this far without any errors – Congratulations! You’re ready to log. If not, remove the battery, SD card and IC from your shield (you used the IC socket, didn’t you?). Check the polarised components are in correctly, double-check your soldering and then reinsert the IC, shield and battery and try again. If that fails, support is available on the Snootlab website, and there is also a customer forum in French (use Google Translate). However as noted previously the team at Snootlab converse in excellent English and have been easy to contact via email if you have any questions. Stay tuned for the final Snootlab product review.

Snootlab products including the Snootlab Mémoire are available directly from their website.

High-resolution images available on flickr.

As always, thank you for reading and I look forward to your comments and so on. Furthermore, don’t be shy in pointing out errors or places that could use improvement. Please subscribe using one of the methods at the top-right of this web page to receive updates on new posts, follow on twitterfacebook, or join our Google Group.

[Disclaimer - the products reviewed in this article are promotional considerations made available by Snootlab]

Otherwise, have fun, be good to each other – and make something! 

Wednesday, July 13, 2011

Learn to solder with David L. Jones!

Hello Readers

How is your soldering? Have you always wanted to improve your soldering skills, or never heated an iron in your life and didn’t know where to start? No matter your level of skill you could do a lot worse than review the following video blogs in this article by David L. Jones.

Who? 

[David] shares some of his 20 years experience in the electronics design industry in his unique non-scripted naturally overly enthusiastic and passionate style.
Bullsh!t and political correctness don’t get a look-in.

Dave started out in hobby electronics over 30 years ago and since then has worked in such diverse areas as design engineering, production engineering, test engineering, electro-mechanical engineering, that wacky ISO quality stuff, field service, concept design, underwater acoustics, ceramic sensors, military sonar systems, red tape, endless paperwork trails, environmental testing, embedded firmware and software application design, PCB design (he’s CID certified), power distribution systems, ultra low noise and low power design, high speed digital design, telemetry systems, and too much other stuff he usually doesn’t talk about.

He has been published in various magazines including: Electronic Today International, Electronics Australia, Silicon Chip, Elektor, Everyday Practical Electronics (EPE), Make, and ReNew.

Few people know Dave is also a world renowned expert and author on Internet Dating, a qualified fitness instructor, geocacher, canyoner, and environmentalist.

Regular readers of this website would know that I rarely publish outside material – however the depth and quality of the tutorials make them a must-see for beginners and experienced people alike. Furthermore, if you have the bandwidth they can be viewed in 1080p. And as a fellow Australian I’m proud to support Dave and his efforts. So I hope you can view, enjoy and possibly learn from the following videos.

The first covers the variety of tools you would use:

And the second covers through-hole PCB soldering:

Finally, watch the procedure for soldering a tiny SMD IC using the ‘dead bug’ method:

And for something completely different:

If you enjoyed those videos then don’t forget to check out what’s new on Dave’s eevblog website and forum. Videos shown are (C) David L. Jones 2011 and embedded with permission.

As always, thank you for reading and I look forward to your comments and so on. Furthermore, don’t be shy in pointing out errors or places that could use improvement. Please subscribe using one of the methods at the top-right of this web page to receive updates on new posts, follow on twitterfacebook, or join our Google Group.

[Disclaimer - I really enjoy the content at eevblog.com]

Otherwise, have fun, be good to each other – and make something! 

Sunday, July 10, 2011

Review: The Gravitech Arduino Nano family

Hello Readers

In this article we will examine a variety of products received for review from Gravitech in the United States – the company that designed and build theArduino Nano. We have a Nano and some very interesting additional modules to have a look at.

So let’s start out review with the Arduino Nano. What is a Nano? A very, very small version of our Arduino Duemilanove boards. It contains the same microcontroller (ATmega328) but in SMD form; has all the I/O pins (plus two extra analogue inputs); and still has a USB interface via the FT232 chip. But more on that later. Nanos arrive in reusable ESD packaging which is useful for storage when not in use:

Patriotic Americans should note that the Nano line is made in the USA. Furthermore, here is a video clip of Nanos being made:

 

 

For those who were unsure about the size of the Nano, consider the following images:

You can easily see all the pin labels and compare them to your Duemilanove or Uno board. There is also a tiny reset button, the usual LEDs, and the in circuit software programmer pins. So you don’t miss out on anything by going to a Nano. When you flip the board over, the rest of the circuitry is revealed, including the FTDI USB>serial converter IC:

Those of you familiar with Arduino systems should immediately recognise the benefit of the Nano – especially for short-run prototype production. The reduction in size really is quite large. In the following image, I have traced the outline of an Arduino Uno and placed the Nano inside for comparison:

So tiny… the board measures 43.1mm (1.7″) by 17.8mm (0.7″). The pins on this example were pre-soldered – and are spaced at standard 2.54mm (0.1″) intervals – perfect for breadboarding or designing into your own PCB -  however you can purchase a Nano without the pins to suit your own mounting purposes. The Nano meets all the specifications of the standard Arduino Duemilanove-style boards, except naturally the physical dimensions.

Power can be supplied to the Nano via the USB cable; feeding 5V directly into the 5V pin, or 7~12 (20 max, not recommended) into the Vin pin. You can only draw 3.3V at up to 50 mA when the Nano is running on USB power, as the 3.3V is sourced from the FTDI USB>serial IC. And the digital I/O pins still allow a current draw up to 40 mA each. From a software perspective you will not have any problems, as the Nano falls under the same board classification as the (for example) Arduino Duemilanove:

Therefore one could take advantage of all the Arduino fun and games – except for the full-size shields. But as you will read soon, Gravitech have got us covered on that front. If the Arduino system is new to you, why not consider following my series of tutorials? They can be found here. In the meanwhile, to put the size into perspective – here is a short video of a Nano blinking some LEDs!

Now back to business. As the Nano does not use standard Arduino shields, the team at Gravitech have got us covered with a range of equivalent shields to enable all sorts of activities. The first of this is their Ethernet and microSD card add-on module:

and the underside:

Again this is designed for breadboarding, or you could most likely remove the pins if necessary. The microSD socket is connected as expected via the SPI bus, and is fully compatible with the default Arduino SD library. As shown in the following image the Nano can slot directly into the ethernet add-in module:

The Ethernet board requires an external power supply, from 7 to 12 volts DC. The controller chip is the usual Wiznet 5100 model, and therefore the Ethernet board is fully compatible with the default Ethernet Arduino library. We tested it with the example web server sketch provided with the Arduino IDE and it all just worked.

The next add-on module to examine is the 2MOTOR board:

… and the bottom:

Using this module allows control of two DC motors with up to two amps of current each via pulse-width modulation. Furthermore, there is a current feedback circuit for each motor so you measure the motor load and adjust power output – interesting. So a motorised device could sense when it was working too hard and ease back a little (like me on a Saturday). All this is made possible by the use of the common L298 dual full-bridge motor driver IC. This is quite a common motor driver IC and is easy to implement in your sketches. The use of this module and the Nano will help reduce the size of any robotics or motorised project. Stay tuned for use of this board in future articles.

Next in this veritable cornucopia of  add-on modules is the USBHOST board:

turning it over …

Using the Maxim MAX3421E host controller IC you can interface with all sorts of devices via USB, as well as work with the new Android ADK. The module will require an external power supply of between 7 and 12 volts DC, with enough current to deal with the board, a Nano and the USB device under control – one amp should be more than sufficient. I will be honest and note that USB and Arduino is completely new to me, however it is somewhat fascinating and I intend to write more about using this module in the near future. In the meanwhile, many examples can be found here.

For a change of scene there is also a group of Xbee wireless communication modules, starting with the Xbee add-on module:

The Xbee itself is not included, only shown for a size comparison. Turning the module over:

It is nice to see a clearly-labelled silk screen on the PCB. If you are unfamiliar with using the Xbee wireless modules for data communication, you may find my introductory tutorial of interest. Furthermore, all of the Gravitech Nano modules are fully software compatible with my tutorial examples, so getting started will be a breeze. Naturally Gravitech also produce an Xbee USB interface board, to enable PC communication over your wireless modules:

Again, note that the Xbee itself is not included, however they can be supplied by Gravitech. Turning the board over reveals another highly-detailed silk screen:

All of the Gravitech Xbee modules support both series 1.0 and 2.5 Xbees, in both standard and professional variants. The USB module also supports the X-CTU configuration software from Digi.

Finally – leaving possibly the most interesting part until last, we have the MP3 Player add-on board:

and on the B-side:

The MP3 board is designed around the VS1053B MP3 decoder IC. It can also decode Ogg Vorbis, AAC, WMA and MID files. There is a 3.5mm stereo output socket to connect headphones and so on. As expected, the microSD card runs from the SPI pins, however SS is pin 4. Although it may be tempting to use this to make a home-brew MP3 player, other uses could include: recorded voice messages for PA systems such as fire alarm notices, adding sound effects to various projects or amusement machines, or whatever else you can come up with.

The Arduino Nano and related boards really are tiny, fully compatible with their larger brethren, and will prove very useful. Although this article was an introductory review, stay tuned for further projects and articles that will make use of the Nano and other boards. If you have any questions or enquiries please direct them to Gravitech via their contact page. Gravitech products including the Arduino Nano family are available directly from theirwebsite or these distributors.

As always, thank you for reading and I look forward to your comments and so on. Furthermore, don’t be shy in pointing out errors or places that could use improvement. Please subscribe using one of the methods at the top-right of this web page to receive updates on new posts, follow on twitterfacebook, or join our Google Group.

[Disclaimer - the products reviewed in this article are promotional considerations made available by Gravitech]

High resolution photos are available on flickr.

Otherwise, have fun, be good to each other – and make something! 

Thursday, July 7, 2011

Tutorial: Arduino and a Thermal Printer

This is chapter thirty-eight of a series originally titled “Getting Started/Moving Forward with Arduino!” by John Boxall – a series of articles on the Arduino universe. The first chapter is here, the complete series is detailed here. Any files from tutorials will be found here.

Welcome back fellow arduidans!

In this article we introduce the inexpensive thermal printer that has recently become widely available from Sparkfun and their resellers. The goal of the article is to be as simple as possible so you can get started without any problems or confusion. In the past getting data from our Arduino to a paper form would either have meant logging it to an SD card then using a PC to finish the job, or perhaps viewing said data on an LCD then writing it down. Not any more – with the use of this cheap and simple serial printer. Before we get started, here is a short demonstration video of it in action:


Not bad at all considering the price. Let’s have a look in more detail. Here is the printer and two matching rolls of thermal paper:

… and the inside of the unit:

Loading paper is quite simple, just drop the roll in with the end of the paper facing away from you, pull it out further than the top of the front lip, then close the lid. The paper rolls required need to be 57mm wide and have a diameter of no more than 39mm. For example. There is a piece of white cardboard stuck to the front – this is an economical cover that hides some of the internals. Nothing of interest for us in there. The button next to the LED on the left is for paper advance, and the LED can blink out the printer status.

From a hardware perspective wiring is also very simple. Looking at the base of the printer:

… there are two connections. On the left is DC power, and data on the right. For this example I have fitted my own rubber feet to stop the printer rocking about. Thankfully the leads are included with the printer and have the plugs already fitted – a great time saver.

Please note – you need an external power supply with a voltage of between 5 and 9 volts DC that can deliver up to 1.5 amps of current. When idling the printer draws less than 10 milliamps, but when printing it peaks at around 1.47 A. So don’t try and run it from your Arduino board. Furthermore you need to ensure the GND from your printer runs to the power supply GND and the Arduino GND. However the data lines are easy, as the printer has a serial interface we only need to connect printer RX (white) to Arduino digital 3, and printer TX (green) to Arduino digital 2. We will use a virtual serial port on pins 2 and 3 as 0 and 1 will be taken for use with the serial monitor window for debugging and possible control purposes.

If you want to quickly test your printer – connect it to the power, drop in some paper, hold down the feed button and turn on the power. It will quickly produce a test print.

Next we need to understand how to control the printer in our sketches. Consider this very simple sketch (download):

/* Example 38.1 - Sparkfun Thermal Printer Test (COM-10438) http://tronixstuff.wordpress.com/tutorials > chapter 38 Based on code by Nathan Seidle of Spark Fun Electronics 2011  http://littlebirdelectronics.com/products/thermal-printer  */  #include NewSoftSerial Thermal(2, 3); // printer RX to digital 2, printer TX to digital 3  int heatTime = 80; int heatInterval = 255; char printDensity = 15; char printBreakTime = 15;  void setup() { Serial.begin(57600); // used for debugging via serial monitor Thermal.begin(19200); // used for writing to the printer initPrinter(); }  void initPrinter() { //Modify the print speed and heat Thermal.print(27, BYTE); Thermal.print(55, BYTE); Thermal.print(7, BYTE); //Default 64 dots = 8*('7'+1) Thermal.print(heatTime, BYTE); //Default 80 or 800us Thermal.print(heatInterval, BYTE); //Default 2 or 20us //Modify the print density and timeout Thermal.print(18, BYTE); Thermal.print(35, BYTE); int printSetting = (printDensity<<4) | printBreakTime; Thermal.print(printSetting, BYTE); //Combination of printDensity and printBreakTime Serial.println(); Serial.println("Printer ready"); }  void loop() { Thermal.println("  Visit http://tronixstuff.com  "); Thermal.print(10, BYTE); // Sends the LF to the printer, advances the paper Thermal.print("    Millis = "); Thermal.println(millis()); Thermal.print(10, BYTE); Thermal.print(10, BYTE); do { } while (1>0); }

After ensuring your printer is connected as described earlier, and has the appropriate power supply and paper – uploading the sketch will result in the following:

Now that the initial burst of printing excitement has passed, let’s look at the sketch and see how it all works. The first part:

#include <NewSoftSerial.h> NewSoftSerial Thermal(2, 3); // printer RX to digital 2, printer TX to digital 3

configures the virtual serial port and creates an instance for us to refer to when writing to the printer. Next, four variables are defined. These hold parameters used for configuring the printer. As the printer works with these settings there is no need to alter them, however if you are feeling experimental nothing is stopping you. Next we have the function initPrinter(). This sets a lot of parameters for the printer to ready itself for work. We callinitPrinter() only once – in void setup(); For now we can be satisfied that it ‘just works’.

Now time for action – void loop(). Writing text to the printer is as simple as:

Thermal.print("text");

You can also use .println to advance along to the next line. Generally this is the same as writing to the serial monitor with Serial.println() etc. So nothing new there. Each line of text can be up to thirty-two characters in length.

The next thing to concern ourselves with is sending commands to the printer. You may have noticed the line

  Thermal.print(10, BYTE);

This sends the command to advance to the next line (in the old days we would say ‘carriage return and line feed’). There are many commands available to do various things. When sending commands, don’t use .println() - as this sends the carriage return as well. At this point you will need to refer to the somewhat amusing user manual.pdf. Open it up and have a look at section 5.2.1 on page ten. Notice how each command has an ASCII, decimal and hexadecimal equivalent? We will use the decimal command values. So to send them, just use:

Thermal.print(value, BYTE);

Easy. If the command has two or more values (for example, to turn the printer offline [page 11] ) – just send each value in a separate statement. For example:

Thermal.print(27, BYTE);
Thermal.print(61, BYTE);
Thermal.print(0, BYTE);

… will put the printer into offline mode.

For out next example, let’s try out a few more commands:

  • Underlined text (the printer seemed to have issues with thick underlining, however your experience may vary)
  • Bold text
  • Double height and width
Here is the sketch (download):
/* Example 38.2 - Sparkfun Thermal Printer Test II (COM-10438) http://tronixstuff.wordpress.com/tutorials > chapter 38 Based on code by Nathan Seidle of Spark Fun Electronics 2011  http://littlebirdelectronics.com/products/thermal-printer  */  #include NewSoftSerial Thermal(2, 3); // printer RX to digital 2, printer TX to digital 3  int heatTime = 80; int heatInterval = 255; char printDensity = 15; char printBreakTime = 15;  void setup() { Serial.begin(57600); // for debug info to serial monitor Thermal.begin(19200); // to write to our new printer initPrinter(); }  void initPrinter() { //Modify the print speed and heat Thermal.print(27, BYTE); Thermal.print(55, BYTE); Thermal.print(7, BYTE); //Default 64 dots = 8*('7'+1) Thermal.print(heatTime, BYTE); //Default 80 or 800us Thermal.print(heatInterval, BYTE); //Default 2 or 20us //Modify the print density and timeout Thermal.print(18, BYTE); Thermal.print(35, BYTE); int printSetting = (printDensity<<4) | printBreakTime; Thermal.print(printSetting, BYTE); //Combination of printDensity and printBreakTime Serial.println(); Serial.println("Printer ready"); }  void loop() { // underline - one pixel Thermal.print(27,BYTE); Thermal.print(45,BYTE); Thermal.print(1,BYTE); Thermal.println("Underline - thin"); Thermal.println("01234567890123456789012345678901"); Thermal.print(10,BYTE);  // underline - two pixels Thermal.print(27,BYTE); Thermal.print(45,BYTE); Thermal.print(2,BYTE); Thermal.println("Underline - thick"); Thermal.println("01234567890123456789012345678901"); Thermal.print(10,BYTE);    // turn off underline Thermal.print(27,BYTE); Thermal.print(45,BYTE); Thermal.print(0,BYTE); delay(3000); Thermal.print(10,BYTE);    // bold text on Thermal.print(27,BYTE); Thermal.print(32,BYTE); Thermal.print(1,BYTE); Thermal.println(" #### Bold text #### "); Thermal.println("01234567890123456789012345678901"); delay(3000);    // bold text off Thermal.print(27,BYTE); Thermal.print(32,BYTE); Thermal.print(0,BYTE); Thermal.print(10, BYTE); //Sends the LF to the printer, advances the paper delay(3000);    // height/width enlarge Thermal.print(29,BYTE); Thermal.print(33,BYTE); Thermal.print(255,BYTE); Thermal.println("ABCDEF"); Thermal.println("012345"); delay(3000);    // back to normal Thermal.print(29,BYTE); Thermal.print(33,BYTE); Thermal.print(0,BYTE); delay(3000);  Thermal.print(10, BYTE); Thermal.println("Back to normal..."); Thermal.print(10, BYTE); Thermal.print(10, BYTE); do { } while (1>0); // do nothing }

And the results:

Frankly bold doesn’t look that bold, so I wouldn’t worry about it too much. However the oversized characters could be very useful, and still print relatively quickly.

Next on our list are barcodes. A normal UPC barcode has 12 digits, and our little printer can generate a variety of barcode types – see page twenty-two of the user manual. For our example we will generate UPC-A type codes and an alphanumeric version. Alphanumeric barcodes need capital letters, the dollar sign, percent sign, or full stop. The data is kept in an array of characters named … barCode[]  and barCode[]2. Consider the functions printBarcode(), printBarcodeThick()  and printBarcodeAlpha() in the following example sketch (download):

/* Example 38.3 - Sparkfun Thermal Printer Test III (COM-10438) http://tronixstuff.wordpress.com/tutorials > chapter 38 Based on code by Nathan Seidle of Spark Fun Electronics 2011  http://littlebirdelectronics.com/products/thermal-printer  */  #include NewSoftSerial Thermal(2, 3); // printer RX to digital 2, printer TX to digital 3  int heatTime = 80; int heatInterval = 255; char printDensity = 15; char printBreakTime = 15; char barCode[]={  '9','2','3','0','5','6','4','8','9','8','4','4'}; char barCode2[]={'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P' };  void setup() { Serial.begin(57600); // for debug info to serial monitor Thermal.begin(19200); // to write to our new printer initPrinter(); }  void initPrinter() { //Modify the print speed and heat Thermal.print(27, BYTE); Thermal.print(55, BYTE); Thermal.print(7, BYTE); //Default 64 dots = 8*('7'+1) Thermal.print(heatTime, BYTE); //Default 80 or 800us Thermal.print(heatInterval, BYTE); //Default 2 or 20us //Modify the print density and timeout Thermal.print(18, BYTE); Thermal.print(35, BYTE); int printSetting = (printDensity<<4) | printBreakTime; Thermal.print(printSetting, BYTE); //Combination of printDensity and printBreakTime Serial.println(); Serial.println("Printer ready"); }  void printBarcode(char zz[]) { Thermal.print(29, BYTE); //GS Thermal.print(107, BYTE); //k Thermal.print(0, BYTE); //m = 0 for (int z=0; z<12; z++) { Thermal.print(zz[z]); } Thermal.print(0, BYTE);  // bar code terminator delay(3000);             // necessary delay Thermal.print(10, BYTE); Thermal.print(10, BYTE); Thermal.print(10, BYTE); }  void printBarcodeThick(char zz[]) { Thermal.print(29, BYTE); // specified height of barcode Thermal.print(104, BYTE); Thermal.print(200, BYTE); // 200 pixels high (default is 50)  Thermal.print(29, BYTE); //GS Thermal.print(107, BYTE); //k Thermal.print(0, BYTE); //m = 0 for (int z=0; z<12; z++) { Thermal.print(zz[z]); } Thermal.print(0, BYTE);  // bar code terminator delay(3000);             // necessary delay Thermal.print(29, BYTE); // specified height of barcode Thermal.print(104, BYTE); Thermal.print(50, BYTE); // need to set back to 50 pixel height delay(3000); Thermal.print(10, BYTE); Thermal.print(10, BYTE); Thermal.print(10, BYTE); }  void printBarcodeAlpha(char zz[]) { Thermal.print(29, BYTE); //GS Thermal.print(107, BYTE); //k Thermal.print(4, BYTE); //m = 0 for (int z=0; z<16; z++) { Thermal.print(zz[z]); } Thermal.print(0, BYTE);  // bar code terminator delay(3000);             // necessary delay Thermal.print(10, BYTE); Thermal.print(10, BYTE); Thermal.print(10, BYTE); }  void loop() { printBarcode(barCode); printBarcodeThick(barCode); printBarcodeAlpha(barCode2); do {} while (1>0); }

Notice in printBarcodeThick() we make use of the ability to change the vertical size of the barcode – the height in pixels is the third parameter in the group. And here is the result:

So there you have it – another practical piece of hardware previously considered to be out of our reach – is now under our control. Now you should have an understanding of the basics and can approach the other functions in the user guide with confidence. Please keep in mind that the price of this printer really should play a large part in determining suitability for a particular task. It does have issues printing large blocks of pixels, such as the double-width underlining and inverse text. This printer is great but certainly not for commercial nor high-volume use. That is what professional POS printers from Brother, Star, Epson, etc., are for. However for low-volume, personal or hobby use this printer is certainly a deal. As always, now it is up to you and your imagination to put this to use or get up to other shenanigans.

This article would not have been possible without the example sketches provided by Nathan Seidle, the founder and CEO of Sparkfun. If you meet him, shout him a beer.  Please don’t knock off bus tickets or so on. I’m sure there are heavy penalties for doing so if caught. Finally, I hope you enjoyed reading this as much as I did writing it for you.

If you have any questions about the processes or details in this article, please ask in our Google Group – dedicated to the projects and related items on this website. Sign up – it’s free, there is the odd competition or give-away –  and we can all learn something. Or follow tronixstuff on twitter and facebook. High resolution images available on flickr.

Otherwise, have fun, stay safe, be good to each other – and make something!