Tag Archives: tutorial

Arduino Tutorials – Chapter 30 – twitter

In this article you will learn how to send messages from an Ethernet-enabled Arduino to twitter. For the uninitiated who may be thinking “what is all this twitter nonsense about?”, twitter is a form of microblogging. 

You can create a message with a maximum length of 140 characters, and broadcast this on the twitter service. For people to receive your messages (or tweets) they also need to be a member of twitter and choose to subscribe to your tweets.

The neat thing about twitter on a mobile device is that if your username is mentioned in a tweet, you will be notified pretty well immediately as long as you have mobile data access. More on that later. In some areas, you can set twitter to send tweets from a certain user to your mobile phone via SMS – however if doing so be careful to confirm possible charges to your mobile phone account.

Finally, if you are worried about privacy with regards to your tweets, you can set your account to private and only allow certain people to follow your tweets.

So let’s get started.

First of all – you will need a twitter account. If you do not have one, you can sign up for one here. If you already have a twitter account, you can always open more for other uses – such as an Arduino.

For example, our twitter account is @tronixstuff, but the demonstration machine twitter account is @tronixstuff2. Then we have set the primary account to follow my machine’s twitter account.

Now log into twitter with using the account you will have for your Arduino and visit this page and get yourself a token by following the Step One link. The process will take you through authorising the “tweet library” page to login to your twitter account – this is ok. It will then present you with a long text called a “token”, for example:

twitter_token

Save your token somewhere safe, as you will need to insert it into your Arduino sketch. Finally, don’t give it to others as then they will be able to post onto twitter using your account. Next, follow step two from the same page – which involves download and installation of the required Arduino library.

Now for the hardware.

You will need an Arduino Uno or compatible board with an Ethernet shield that uses the W5100 Ethernet controller IC (pretty much all of them).

Furthermore you will need to power the board via the external DC socket – the W5100 IC uses more current than the USB power can supply. A 9V 1.5A plug pack/wall wart will suffice.

From this point it would be a good idea to check your hardware is working. To do so, please run the webserver example sketch as explained in chapter sixteen (Ethernet). While you do that, we’ll have a break…

lopburi-0606

Sending your first tweet

If you want your Arduino to send a simple tweet consider the following sketch. We have a simple function tweet() which simply sends a line of text (which has a maximum length of 140 characters). Don’t forget to update your IP address, MAC address and token:

// Simple twitter interface

#include <SPI.h>
#include <Ethernet.h>
#include <Twitter.h>

// Alter IP address to suit your own network!
byte mac[] = {   0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; // create MAC address for ethernet shield
byte ip[] = {   192, 168, 0, 99}; // choose your own IP for ethernet shield
Twitter twitter("aaaaaaa"); // replace aaaaaaa with your token

void setup()
{
  delay(5000);
  Ethernet.begin(mac, ip);
  Serial.begin(9600);
}

void tweet(char msg[])
{
  Serial.println("connecting ...");
  if (twitter.post(msg))
  {
    // Specify &Serial to output received response to Serial.
    // If no output is required, you can just omit the argument, e.g.
    // int status = twitter.wait();
    int status = twitter.wait(&Serial);
    if (status == 200)
    {
      Serial.println("OK.");
    } 
    else
    {
      Serial.print("failed : code ");
      Serial.println(status);
    }
  } 
  else
  {
    Serial.println("connection failed.");
  }
}

void loop()
{
  delay(1000);
  tweet("Purple monkey dishwasher");
  do{} while(1>0); // endless loop
}

You can check the status of the tweeting via the serial monitor. For example, if the tweet was successful you will see:

arduino-twitter-success-2014

However if you try to send the same tweet more than once in a short period of time, or another error takes place – twitter will return an error message, for example:

arduino-twitter-duplicate

And finally if it works, the tweet will appear:

Arduino-twitter-works-2014

Previously we mentioned that you can be alerted to a tweet by your mobile device. This can be done by putting your own twitter account name in the contents of the tweet.

For example – our normal twitter account is @tronixstuff. If we put the text “@tronixstuff” in the text tweeted by the Arduino’s twitter account – the twitter app on our smartphone will let us know we have been mentioned – as shown in the following video:

You may have noticed in the video that a text message arrived as well – that service is a function of our cellular carrier (Telstra) and may not be available to others. Nevertheless this is a neat way of getting important messages from your Arduino to a smart phone or other connected device.

Sending data in a tweet

So what if you have  a sensor or other device whose data you want to know about via twitter? You can send data generated from an Arduino sketch over twitter without too much effort.

In the following example we’ll send the value from analogue pin zero (A0) in the contents of a tweet. And by adding your twitter @username you will be notified by your other twitter-capable devices:

// Simple twitter interface

#include <SPI.h>
#include <Ethernet.h>
#include <Twitter.h>

// Alter IP address to suit your own network!
byte mac[] = {   0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; // create MAC address for ethernet shield
byte ip[] = {   192, 168, 0, 99}; // choose your own IP for ethernet shield
Twitter twitter("aaaaaaa"); // replace aaaaaaa with your token

int analogZero;
char tweetText[140];

void setup()
{
  delay(5000);
  Ethernet.begin(mac, ip);
  Serial.begin(9600);
}

void tweet(char msg[])
{
  Serial.println("connecting ...");
  if (twitter.post(msg))
  {
    // Specify &Serial to output received response to Serial.
    // If no output is required, you can just omit the argument, e.g.
    // int status = twitter.wait();
    int status = twitter.wait(&Serial);
    if (status == 200)
    {
      Serial.println("OK.");
    } 
    else
    {
      Serial.print("failed : code ");
      Serial.println(status);
    }
  } 
  else
  {
    Serial.println("connection failed.");
  }
}

void loop()
{
  // get some data from A0. 
  analogZero=analogRead(0);

  // assemble message to send. This inserts the value of "analogZero" into the variable "tweetText" at point %d
  sprintf(tweetText, "Pin analogue zero reads: %d. @username.", analogZero); // change @username to your twitter account name

  delay(1000);
  tweet(tweetText);
  do{ } 
  while(1>0); // endless loop
}

You may have noticed a sneaky sprintf function in void loop(). This is used to insert the integer analogZero into the character array tweetText that we send with the tweet() function. And the results of the example:

Arduino-Twitter-Tutorial-success

So you can use the previous sketch as a framework to create your own Arduino-powered data twittering machine. Send temperature alerts, tank water levels, messages from an alarm system, or just random tweets to your loved one.

Conclusion

So there you have it, another useful way to send information from your Arduino to the outside world.

This post is brought to you by pmdway.com – everything for makers and electronics enthusiasts, with free delivery worldwide.

To keep up to date with new posts at tronixstuff.com, please subscribe to the mailing list in the box on the right, or follow us on twitter @tronixstuff.

Arduino Tutorials – Chapter 15 – RFID

RFID – radio frequency identification. Some of us have already used these things, and they have become part of everyday life. For example, with electronic vehicle tolling, door access control, public transport fare systems and so on. It sounds complex – but isn’t.

To explain RFID for the layperson, we can use a key and lock analogy. Instead of the key having a unique pattern, RFID keys hold a series of unique numbers which are read by the lock. It is up to our Arduino sketch to determine what happens when the number is read by the lock.

The key is the tag, card or other small device we carry around or have in our vehicles. We will be using a passive key, which is an integrated circuit and a small aerial. This uses power from a magnetic field associated with the lock. Here are some key or tag examples:

125kHz RFID tags from PMD Way

In this tutorial we’ll be using 125 kHz tags – for example. To continue with the analogy our lock is a small circuit board and a loop aerial. This has the capability to read the data on the IC of our key, and some locks can even write data to keys. Here is our reader (lock) example:

RDM630 125kHz RFID reader from PMD Way

These readers are quite small and inexpensive – however the catch is that the loop aerial is somewhat fragile.

Setting up the RFID reader

This is a short exercise to check the reader works and communicates with the Arduino. You will need:

Simply insert the RFID reader main board into a solderless breadboard as shown below. Then use jumper wires to connect the second and third pins at the top-left of the RFID board to Arduino 5V and GND respectively. The RFID coil connects to the two pins on the top-right (they can go either way). Finally, connect a jumper wire from the bottom-left pin of the RFID board to Arduino digital pin 2:

RDM630 125kHz RFID reader from PMD Way

Next, upload the following sketch to your Arduino and open the serial monitor window in the IDE:

#include <SoftwareSerial.h>
SoftwareSerial RFID(2, 3); // RX and TX

int i;

void setup()
{
  RFID.begin(9600);    // start serial to RFID reader
  Serial.begin(9600);  // start serial to PC 
}

void loop()
{
  if (RFID.available() > 0) 
  {
     i = RFID.read();
     Serial.print(i, DEC);
     Serial.print(" ");
  }
}

If you’re wondering why we used SoftwareSerial – if you connect the data line from the RFID board to the Arduino’s RX pin – you need to remove it when updating sketches, so this is more convenient.

Now start waving RFID cards or tags over the coil. You will find that they need to be parallel over the coil, and not too far away. You can experiment with covering the coil to simulate it being installed behind protective surfaces and so on. Watch this short video which shows the resulting RFID card or tag data being displayed in the Arduino IDE serial monitor.

As you can see from the example video, the reader returns the card’s unique ID number which starts with a 2 and ends with a 3. While you have the sketch operating, read the numbers from your RFID tags and note them down, you will need them for future sketches.

To do anything with the card data, we need to create some functions to retrieve the card number when it is read and place in an array for comparison against existing card data (e.g. a list of accepted cards) so your systems will know who to accept and who to deny. Using those functions, you can then make your own access system, time-logging device and so on.

Let’s demonstrate an example of this. It will check if a card presented to the reader is on an “accepted” list, and if so light a green LED, otherwise light a red LED. Use the hardware from the previous sketch, but add a typical green and red LED with 560 ohm resistor to digital pins 13 and 12 respectively. Then upload the following sketch:

#include <SoftwareSerial.h>
SoftwareSerial RFID(2, 3); // RX and TX

int data1 = 0;
int ok = -1;
int yes = 13;
int no = 12;

// use first sketch in http://wp.me/p3LK05-3Gk to get your tag numbers
int tag1[14] = {2,52,48,48,48,56,54,66,49,52,70,51,56,3};
int tag2[14] = {2,52,48,48,48,56,54,67,54,54,66,54,66,3};
int newtag[14] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0}; // used for read comparisons

void setup()
{
  RFID.begin(9600);    // start serial to RFID reader
  Serial.begin(9600);  // start serial to PC 
  pinMode(yes, OUTPUT); // for status LEDs
  pinMode(no, OUTPUT);
}

boolean comparetag(int aa[14], int bb[14])
{
  boolean ff = false;
  int fg = 0;
  for (int cc = 0 ; cc < 14 ; cc++)
  {
    if (aa[cc] == bb[cc])
    {
      fg++;
    }
  }
  if (fg == 14)
  {
    ff = true;
  }
  return ff;
}

void checkmytags() // compares each tag against the tag just read
{
  ok = 0; // this variable helps decision-making,
  // if it is 1 we have a match, zero is a read but no match,
  // -1 is no read attempt made
  if (comparetag(newtag, tag1) == true)
  {
    ok++;
  }
  if (comparetag(newtag, tag2) == true)
  {
    ok++;
  }
}

void readTags()
{
  ok = -1;

  if (RFID.available() > 0) 
  {
    // read tag numbers
    delay(100); // needed to allow time for the data to come in from the serial buffer.

    for (int z = 0 ; z < 14 ; z++) // read the rest of the tag
    {
      data1 = RFID.read();
      newtag[z] = data1;
    }
    RFID.flush(); // stops multiple reads

    // do the tags match up?
    checkmytags();
  }

  // now do something based on tag type
  if (ok > 0) // if we had a match
  {
    Serial.println("Accepted");
    digitalWrite(yes, HIGH);
    delay(1000);
    digitalWrite(yes, LOW);

    ok = -1;
  }
  else if (ok == 0) // if we didn't have a match
  {
    Serial.println("Rejected");
    digitalWrite(no, HIGH);
    delay(1000);
    digitalWrite(no, LOW);

    ok = -1;
  }
}

void loop()
{
  readTags();
}

In the sketch we have a few functions that take care of reading and comparing RFID tags. Notice that the allowed tag numbers are listed at the top of the sketch, you can always add your own and more – as long as you add them to the list in the function checkmytags() which determines if the card being read is allowed or to be denied.

The function readTags() takes care of the actual reading of the tags/cards, by placing the currently-read tag number into an array which is them used in the comparison function checkmytags(). Then the LEDs are illuminated depending on the status of the tag at the reader. You can watch a quick demonstration of this example in this short video.

Conclusion

After working through this chapter you should now have a good foundation of knowledge on using the inexpensive RFID readers and how to call functions when a card is successfully read. For example, use some extra hardware to control a door strike, buzzer, etc.

Now it’s up to you to use them as a form of input with various access systems, tracking the movement of people or things and much more.

This post is brought to you by pmdway.com – everything for makers and electronics enthusiasts, with free delivery worldwide.

To keep up to date with new posts at tronixstuff.com, please subscribe to the mailing list in the box on the right, or follow us on twitter @tronixstuff.

Tutorial – Arduino and the TLC5940 PWM LED Driver IC

In this article we are going to examine the Texas Instruments TLC5940 16-channel LED driver IC. Our reason for doing this is to demonstrate another, easier way of driving many LEDs – and also servos.  First up, here is a few examples of the TLC5940:

TLC5940 LED driver IC from PMD Way

The TLC5940 is available in the DIP version above, and also surface-mount. It really is a convenient part, allowing you to adjust the brightness of sixteen individual LEDs via PWM (pulse-width modulation) – and you can also daisy-chain more than one TLC5940 to control even more.

During this tutorial we’ll explain how to control one or more TLC5940 ICs with LEDs and also look at controlling servos. At this point, please download a copy of the TLC5940_data_sheet (.pdf) as you will refer to it through this process. Furthermore, please download and install the TLC5940 Arduino library by Alex Leone which can be found here. If you’re not sure how to install a library, click here.

Build a TLC5940 demonstration circuit

The following circuit is the minimum required to control sixteen LEDs from your Arduino or compatible. You can use it to experiment with various functions and get an idea of what is possible. You will need:

Take note of the LED orientation – and remember the TLC5940 is a common-anode LED driver – so all the LED anodes are connected together and then to 5V:

TLC5940 LED driver IC from PMD Way

For this particular circuit, you won’t need an external 5V power supply – however you may need one in the future. The purpose of the resistor is to control the amount of current that can flow through the LEDs. The required resistor value is calculated with the following formula:

R = 39.06 / Imax

where R (in Ohms)  is the resistor value and Imax (in Amps) is the maximum amount of current you want to flow through the LEDs. For example, if you have LEDs with a 20 mA forward current – the resistor calculation would be:

R = 39.06 / 0.02 = 1803 Ohms.

Once you have the circuit assembled – open up the Arduino IDE and upload the sketch BasicUse.pde  which is in the example folder for the TLC5940 library. You should be presented with output similar to what is shown in the following video:

Controlling the TLC5940

Now that the circuit works, how do we control the TLC5940? First, the mandatory functions – include the library at the start of the sketch with:

#include "Tlc5940.h"

and then initialise the library by placing the following into void setup():

Tlc.init(x);

x is an optional parameter – if you want to set all the channels to a certain brightness as soon as the sketch starts, you can insert a value between 0 and 4095 for in the Tlc.init() function.

Now to turn a channel/LED on or off. Each channel is numbered from 0 to 15, and each channel’s brightness can be adjusted between 0 and 4095.

This is a two-part process…

First – use one or more of the following functions to set up the required channels and respective brightness (PWM level):

Tlc.set(channel, brightness);

For example, if you wanted to have the first three channels on at full brightness, use:

Tlc.set(0, 4095);
Tlc.set(1, 4095);
Tlc.set(2, 4095);

The second part is to use the following to update the TLC5940 with the required instructions from part one:

Tlc.update();

If you want to turn off all channels at once, simply use:

Tlc.clear();

You don’t need to call a TLC.update() after the clear function. The following is a quick example sketch that sets the brightness/PWM values of all the channels to different levels:

#include "Tlc5940.h"
void setup()
{
  Tlc.init(0); // initialise TLC5940 and set all channels off
}

void loop()
{
  for (int i = 0; i < 16; i++)
  {
    Tlc.set(i, 1023);
  }
  Tlc.update();
  delay(1000);
  for (int i = 0; i < 16; i++)
  {
    Tlc.set(i, 2046);
  }
  Tlc.update();
  delay(1000);
  for (int i = 0; i < 16; i++)
  {
    Tlc.set(i, 3069);
  }
  Tlc.update();
  delay(1000);
  for (int i = 0; i < 16; i++)
  {
    Tlc.set(i, 4095);
  }
  Tlc.update();
  delay(1000);
}

and the sketch in action:

The ability to control individual brightness for each channel/LED can also be useful when controlling RGB LEDs – you can then easily select required colours via different brightness levels for each element.

Using two or more TLC5940s

You can daisy-chain quite a few TLC5940s together to control more LEDs. First – wire up the next TLC5940 to the Arduino as shown in the demonstration circuit – except connect the SOUT pin (17) of the first TLC5940 to the SIN pin (26) of the second TLC5940 – as the data travels from the Arduino, through the first TLC5940 to the second and so on. Then repeat the process if you have a third, etc. Don’t forget the resisotr that sets the current!

Next, open the file tlc_config.h located in the TLC5940 library folder. Change the value of NUM_TLCS to the number of TLC5940s you have connected together, then save the file and also delete the file Tlc5940.o also located in the same folder. Finally restart the IDE. You can then refer to the channels of the second and further TLC5940 sequentially from the first. That is, the first is 0~15, the second is 16~29, and so on.

Controlling servos with the TLC5940

As the TLC5940 generates PWM (pulse-width modulation) output, it’s great for driving servos as well. Just like LEDs – you can control up to sixteen at once. Ideal for creating spider-like robots, strange clocks or making some noise. When choosing your servo, ensure that it doesn’t draw more than 120 mA when operating (the maximum current per channel) and also heed the “Managing current and heat” section at the end of this tutorial. And use external power with servos, don’t rely on the Arduino’s 5V line.

To connect a servo is simple – the GND line connects to GND, the 5V (or supply voltage lead) connects to your 5v (or other suitable supply) and the servo control pin connects to one of the TLC5940’s outputs. Finally – and this is important – connect a 2.2kΩ resistor between the TLC5940 output pin(s) being used and 5V.

Controlling a servo isn’t that different to an LED. You need the first two lines at the start of the sketch:

#include "Tlc5940.h"
#include "tlc_servos.h"

then the following in void setup():

tlc_initServos();

Next, use the following function to select which servo (channel) to operate and the required angle (angle):

tlc_setServo(channel, angle);

Just like the LEDs you can bunch a few of these together, and then execute the command with:

Tlc.update();

So let’s see all that in action. The following example sketch sweeps four servos across 90 degrees:

#include "Tlc5940.h"
#include "tlc_servos.h"

void setup()
{
  tlc_initServos();  // Note: this will drop the PWM freqency down to 50Hz.
}

void loop()
{
  for (int angle = 0; angle < 90; angle++) {
    tlc_setServo(0, angle);
    tlc_setServo(1, angle);
    tlc_setServo(2, angle);
    tlc_setServo(3, angle);    
    Tlc.update();
    delay(5);
  }
  for (int angle = 90; angle >= 0; angle--) {
    tlc_setServo(0, angle);
    tlc_setServo(1, angle);
    tlc_setServo(2, angle);
    tlc_setServo(3, angle);    
    Tlc.update();
    delay(5);
  }
}

And the following video captures those four servos in action:

If you servos are not rotating to the correct angle – for example you ask for 180 degrees and they only rotate to 90 or thereabouts, a little extra work is required. You need to open the tlc_servos.h file located in the TLC5940 Arduino library folder and experiment with the values for SERVO_MIN_WIDTH and SERVO_MAX_WIDTH. For example change SERVO_MIN_WIDTH from 200 to 203 and SERVO_MAX_WIDTH from 400 to 560.

Managing current and heat 

As mentioned earlier, the TLC5940 can handle a maximum of 120 mA per channel. After some experimenting you may notice that the TLC5940 does get warm – and that’s ok. However there is a maximum limit to the amount of power that can be dissipated before destroying the part. If you are just using normal garden-variety LEDs or smaller servos, power won’t be a problem. However if you’re planning on using the TLC5940 to the max – please review the notes provided by the library authors.

Conclusion

Once again you’re on your way to controlling an incredibly useful part with your Arduino. Now with some imagination you can create all sorts of visual displays or have fun with many servos.

This post is brought to you by pmdway.com – everything for makers and electronics enthusiasts, with free delivery worldwide.

To keep up to date with new posts at tronixstuff.com, please subscribe to the mailing list in the box on the right, or follow us on twitter @tronixstuff.

Tutorial – Arduino and the MAX7219 LED Display Driver IC

Sooner or later Arduino enthusiasts and beginners alike will come across the MAX7219 IC. And for good reason, it’s a simple and somewhat inexpensive method of controlling 64 LEDs in either matrix or numeric display form. Furthermore they can be chained together to control two or more units for even more LEDs. Overall – they’re a lot of fun and can also be quite useful, so let’s get started.

Here’s an example of a MAX7219 and another IC which is a functional equivalent, the AS1107 from Austria Microsystems. You might not see the AS1107 around much, but it can be cheaper – so don’t be afraid to use that instead:

MAX7219 LED driver IC from PMD Way

 At first glance you may think that it takes a lot of real estate, but it saves some as well. As mentioned earlier, the MAX7219 can completely control 64 individual LEDs – including maintaining equal brightness, and allowing you to adjust the brightness of the LEDs either with hardware or software (or both). It can refresh the LEDs at around 800 Hz, so no more flickering, uneven LED displays.

You can even switch the display off for power saving mode, and still send it data while it is off. And another good thing – when powered up, it keeps the LEDs off, so no wacky displays for the first seconds of operation. For more technical information, here is the data sheet: MAX7219.pdf. Now to put it to work for us – we’ll demonstrate using one or more 8 x 8 LED matrix displays, as well as 8 digits of 7-segment LED numbers.

Before continuing, download and install the LedControl Arduino library as it is essential for using the MAX7219.

Controlling LED matrix displays with the MAX7219

First of all, let’s examine the hardware side of things. Here is the pinout diagram for the MAX7219:

MAX7219 LED driver IC from PMD Way

The MAX7219 drives eight LEDs at a time, and by rapidly switching banks of eight your eyes don’t see the changes. Wiring up a matrix is very simple – if you have a common matrix with the following schematic:

MAX7219 LED driver IC from PMD Way


connect the MAX7219 pins labelled DP, A~F to the row pins respectively, and the MAX7219 pins labelled DIG0~7 to the column pins respectively. A total example circuit with the above matrix  is as follows:

MAX7219 LED driver IC from PMD Way

The circuit is quite straight forward, except we have a resistor between 5V and MAX7219 pin 18. The MAX7219 is a constant-current LED driver, and the value of the resistor is used to set the current flow to the LEDs. Have a look at table eleven on page eleven of the data sheet:

MAX7219 LED driver IC from PMD Way

You’ll need to know the voltage and forward current for your LED matrix or numeric display, then match the value on the table. E.g. if you have a 2V 20 mA LED, your resistor value will be 28kΩ (the values are in kΩ). Finally, the MAX7219 serial in, load and clock pins will go to Arduino digital pins which are specified in the sketch. We’ll get to that in the moment, but before that let’s return to the matrix modules.

In the last few months there has been a proliferation of inexpensive kits that contain a MAX7219 or equivalent, and an LED matrix. These are great for experimenting with and can save you a lot of work – some examples of which are shown below:

MAX7219 LED driver IC from PMD Way

Now for the sketch. You need the following two lines at the beginning of the sketch:

#include "LedControl.h" 
LedControl lc=LedControl(12,11,10,1);

The first pulls in the library, and the second line sets up an instance to control. The four parameters are as follows:

  1. the digital pin connected to pin 1 of the MAX7219 (“data in”)
  2. the digital pin connected to pin 13 of the MAX7219 (“CLK or clock”)
  3. the digital pin connected to pin 12 of the MAX7219 (“LOAD”)
  4. The number of MAX7219s connected.

If you have more than one MAX7219, connect the DOUT (“data out”) pin of the first MAX7219 to pin 1 of the second, and so on. However the CLK and LOAD pins are all connected in parallel and then back to the Arduino.

Next, two more vital functions that you’d normally put in void setup():

lc.shutdown(0,false);
lc.setIntensity(0,8);

The first line above turns the LEDs connected to the MAX7219 on. If you set TRUE, you can send data to the MAX7219 but the LEDs will stay off. The second line adjusts the brightness of the LEDs in sixteen stages. For both of those functions (and all others from the LedControl) the first parameter is the number of the MAX7219 connected. If you have one, the parameter is zero… for two MAX7219s, it’s 1 and so on.

Finally, to turn an individual LED in the matrix on or off, use:

lc.setLed(0,col,row,true);

which turns on an LED positioned at col, row connected to MAX7219 #1. Change TRUE to FALSE to turn it off. These functions are demonstrated in the following sketch:

#include "LedControl.h" //  need the library
LedControl lc=LedControl(12,11,10,1); // 

// pin 12 is connected to the MAX7219 pin 1
// pin 11 is connected to the CLK pin 13
// pin 10 is connected to LOAD pin 12
// 1 as we are only using 1 MAX7219

void setup()
{
  // the zero refers to the MAX7219 number, it is zero for 1 chip
  lc.shutdown(0,false);// turn off power saving, enables display
  lc.setIntensity(0,8);// sets brightness (0~15 possible values)
  lc.clearDisplay(0);// clear screen
}
void loop()
{
  for (int row=0; row<8; row++)
  {
    for (int col=0; col<8; col++)
    {
      lc.setLed(0,col,row,true); // turns on LED at col, row
      delay(25);
    }
  }

  for (int row=0; row<8; row++)
  {
    for (int col=0; col<8; col++)
    {
      lc.setLed(0,col,row,false); // turns off LED at col, row
      delay(25);
    }
  }
}

And a quick video of the results:

How about controlling two MAX7219s? Or more? The hardware modifications are easy – connect the serial data out pin from your first MAX7219 to the data in pin on the second (and so on), and the LOAD and CLOCK pins from the first MAX7219 connect to the second (and so on). You will of course still need the 5V, GND, resistor, capacitors etc. for the second and subsequent MAX7219.

You will also need to make a few changes in your sketch. The first is to tell it how many MAX7219s you’re using in the following line:

LedControl lc=LedControl(12,11,10,X);

by replacing X with the quantity. Then whenever you’re using  a MAX7219 function, replace the (previously used) zero with the number of the MAX7219 you wish to address. They are numbered from zero upwards, with the MAX7219 directly connected to the Arduino as unit zero, then one etc. To demonstrate this, we replicate the previous example but with two MAX7219s:

#include "LedControl.h" //  need the library
LedControl lc=LedControl(12,11,10,2); // 

// pin 12 is connected to the MAX7219 pin 1
// pin 11 is connected to the CLK pin 13
// pin 10 is connected to LOAD pin 12
// 1 as we are only using 1 MAX7219

void setup()
{
  lc.shutdown(0,false);// turn off power saving, enables display
  lc.setIntensity(0,8);// sets brightness (0~15 possible values)
  lc.clearDisplay(0);// clear screen

  lc.shutdown(1,false);// turn off power saving, enables display
  lc.setIntensity(1,8);// sets brightness (0~15 possible values)
  lc.clearDisplay(1);// clear screen
}

void loop()
{
  for (int row=0; row<8; row++)
  {
    for (int col=0; col<8; col++)
    {
      lc.setLed(0,col,row,true); // turns on LED at col, row
      lc.setLed(1,col,row,false); // turns on LED at col, row
      delay(25);
    }
  }

  for (int row=0; row<8; row++)
  {
    for (int col=0; col<8; col++)
    {
      lc.setLed(0,col,row,false); // turns off LED at col, row
      lc.setLed(1,col,row,true); // turns on LED at col, row      
      delay(25);
    }
  }
}

And again, a quick demonstration:

Another fun use of the MAX7219 and LED matrices is to display scrolling text. For the case of simplicity we’ll use the LedControl library and the two LED matrix modules from the previous examples.

First our example sketch – it is quite long however most of this is due to defining the characters for each letter of the alphabet and so on. We’ll explain it at the other end!

// based on an orginal sketch by Arduino forum member "danigom"
// http://forum.arduino.cc/index.php?action=profile;u=188950

#include <avr/pgmspace.h>
#include <LedControl.h>

const int numDevices = 2;      // number of MAX7219s used
const long scrollDelay = 75;   // adjust scrolling speed

unsigned long bufferLong [14] = {0}; 

LedControl lc=LedControl(12,11,10,numDevices);

prog_uchar scrollText[] PROGMEM ={
    "  THE QUICK BROWN FOX JUMPED OVER THE LAZY DOG 1234567890 the quick brown fox jumped over the lazy dog   "};

void setup(){
    for (int x=0; x<numDevices; x++){
        lc.shutdown(x,false);       //The MAX72XX is in power-saving mode on startup
        lc.setIntensity(x,8);       // Set the brightness to default value
        lc.clearDisplay(x);         // and clear the display
    }
}

void loop(){ 
    scrollMessage(scrollText);
    scrollFont();
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////

prog_uchar font5x7 [] PROGMEM = {      //Numeric Font Matrix (Arranged as 7x font data + 1x kerning data)
    B00000000,	//Space (Char 0x20)
    B00000000,
    B00000000,
    B00000000,
    B00000000,
    B00000000,
    B00000000,
    6,

    B10000000,	//!
    B10000000,
    B10000000,
    B10000000,
    B00000000,
    B00000000,
    B10000000,
    2,

    B10100000,	//"
    B10100000,
    B10100000,
    B00000000,
    B00000000,
    B00000000,
    B00000000,
    4,

    B01010000,	//#
    B01010000,
    B11111000,
    B01010000,
    B11111000,
    B01010000,
    B01010000,
    6,

    B00100000,	//$
    B01111000,
    B10100000,
    B01110000,
    B00101000,
    B11110000,
    B00100000,
    6,

    B11000000,	//%
    B11001000,
    B00010000,
    B00100000,
    B01000000,
    B10011000,
    B00011000,
    6,

    B01100000,	//&
    B10010000,
    B10100000,
    B01000000,
    B10101000,
    B10010000,
    B01101000,
    6,

    B11000000,	//'
    B01000000,
    B10000000,
    B00000000,
    B00000000,
    B00000000,
    B00000000,
    3,

    B00100000,	//(
    B01000000,
    B10000000,
    B10000000,
    B10000000,
    B01000000,
    B00100000,
    4,

    B10000000,	//)
    B01000000,
    B00100000,
    B00100000,
    B00100000,
    B01000000,
    B10000000,
    4,

    B00000000,	//*
    B00100000,
    B10101000,
    B01110000,
    B10101000,
    B00100000,
    B00000000,
    6,

    B00000000,	//+
    B00100000,
    B00100000,
    B11111000,
    B00100000,
    B00100000,
    B00000000,
    6,

    B00000000,	//,
    B00000000,
    B00000000,
    B00000000,
    B11000000,
    B01000000,
    B10000000,
    3,

    B00000000,	//-
    B00000000,
    B11111000,
    B00000000,
    B00000000,
    B00000000,
    B00000000,
    6,

    B00000000,	//.
    B00000000,
    B00000000,
    B00000000,
    B00000000,
    B11000000,
    B11000000,
    3,

    B00000000,	///
    B00001000,
    B00010000,
    B00100000,
    B01000000,
    B10000000,
    B00000000,
    6,

    B01110000,	//0
    B10001000,
    B10011000,
    B10101000,
    B11001000,
    B10001000,
    B01110000,
    6,

    B01000000,	//1
    B11000000,
    B01000000,
    B01000000,
    B01000000,
    B01000000,
    B11100000,
    4,

    B01110000,	//2
    B10001000,
    B00001000,
    B00010000,
    B00100000,
    B01000000,
    B11111000,
    6,

    B11111000,	//3
    B00010000,
    B00100000,
    B00010000,
    B00001000,
    B10001000,
    B01110000,
    6,

    B00010000,	//4
    B00110000,
    B01010000,
    B10010000,
    B11111000,
    B00010000,
    B00010000,
    6,

    B11111000,	//5
    B10000000,
    B11110000,
    B00001000,
    B00001000,
    B10001000,
    B01110000,
    6,

    B00110000,	//6
    B01000000,
    B10000000,
    B11110000,
    B10001000,
    B10001000,
    B01110000,
    6,

    B11111000,	//7
    B10001000,
    B00001000,
    B00010000,
    B00100000,
    B00100000,
    B00100000,
    6,

    B01110000,	//8
    B10001000,
    B10001000,
    B01110000,
    B10001000,
    B10001000,
    B01110000,
    6,

    B01110000,	//9
    B10001000,
    B10001000,
    B01111000,
    B00001000,
    B00010000,
    B01100000,
    6,

    B00000000,	//:
    B11000000,
    B11000000,
    B00000000,
    B11000000,
    B11000000,
    B00000000,
    3,

    B00000000,	//;
    B11000000,
    B11000000,
    B00000000,
    B11000000,
    B01000000,
    B10000000,
    3,

    B00010000,	//<
    B00100000,
    B01000000,
    B10000000,
    B01000000,
    B00100000,
    B00010000,
    5,

    B00000000,	//=
    B00000000,
    B11111000,
    B00000000,
    B11111000,
    B00000000,
    B00000000,
    6,

    B10000000,	//>
    B01000000,
    B00100000,
    B00010000,
    B00100000,
    B01000000,
    B10000000,
    5,

    B01110000,	//?
    B10001000,
    B00001000,
    B00010000,
    B00100000,
    B00000000,
    B00100000,
    6,

    B01110000,	//@
    B10001000,
    B00001000,
    B01101000,
    B10101000,
    B10101000,
    B01110000,
    6,

    B01110000,	//A
    B10001000,
    B10001000,
    B10001000,
    B11111000,
    B10001000,
    B10001000,
    6,

    B11110000,	//B
    B10001000,
    B10001000,
    B11110000,
    B10001000,
    B10001000,
    B11110000,
    6,

    B01110000,	//C
    B10001000,
    B10000000,
    B10000000,
    B10000000,
    B10001000,
    B01110000,
    6,

    B11100000,	//D
    B10010000,
    B10001000,
    B10001000,
    B10001000,
    B10010000,
    B11100000,
    6,

    B11111000,	//E
    B10000000,
    B10000000,
    B11110000,
    B10000000,
    B10000000,
    B11111000,
    6,

    B11111000,	//F
    B10000000,
    B10000000,
    B11110000,
    B10000000,
    B10000000,
    B10000000,
    6,

    B01110000,	//G
    B10001000,
    B10000000,
    B10111000,
    B10001000,
    B10001000,
    B01111000,
    6,

    B10001000,	//H
    B10001000,
    B10001000,
    B11111000,
    B10001000,
    B10001000,
    B10001000,
    6,

    B11100000,	//I
    B01000000,
    B01000000,
    B01000000,
    B01000000,
    B01000000,
    B11100000,
    4,

    B00111000,	//J
    B00010000,
    B00010000,
    B00010000,
    B00010000,
    B10010000,
    B01100000,
    6,

    B10001000,	//K
    B10010000,
    B10100000,
    B11000000,
    B10100000,
    B10010000,
    B10001000,
    6,

    B10000000,	//L
    B10000000,
    B10000000,
    B10000000,
    B10000000,
    B10000000,
    B11111000,
    6,

    B10001000,	//M
    B11011000,
    B10101000,
    B10101000,
    B10001000,
    B10001000,
    B10001000,
    6,

    B10001000,	//N
    B10001000,
    B11001000,
    B10101000,
    B10011000,
    B10001000,
    B10001000,
    6,

    B01110000,	//O
    B10001000,
    B10001000,
    B10001000,
    B10001000,
    B10001000,
    B01110000,
    6,

    B11110000,	//P
    B10001000,
    B10001000,
    B11110000,
    B10000000,
    B10000000,
    B10000000,
    6,

    B01110000,	//Q
    B10001000,
    B10001000,
    B10001000,
    B10101000,
    B10010000,
    B01101000,
    6,

    B11110000,	//R
    B10001000,
    B10001000,
    B11110000,
    B10100000,
    B10010000,
    B10001000,
    6,

    B01111000,	//S
    B10000000,
    B10000000,
    B01110000,
    B00001000,
    B00001000,
    B11110000,
    6,

    B11111000,	//T
    B00100000,
    B00100000,
    B00100000,
    B00100000,
    B00100000,
    B00100000,
    6,

    B10001000,	//U
    B10001000,
    B10001000,
    B10001000,
    B10001000,
    B10001000,
    B01110000,
    6,

    B10001000,	//V
    B10001000,
    B10001000,
    B10001000,
    B10001000,
    B01010000,
    B00100000,
    6,

    B10001000,	//W
    B10001000,
    B10001000,
    B10101000,
    B10101000,
    B10101000,
    B01010000,
    6,

    B10001000,	//X
    B10001000,
    B01010000,
    B00100000,
    B01010000,
    B10001000,
    B10001000,
    6,

    B10001000,	//Y
    B10001000,
    B10001000,
    B01010000,
    B00100000,
    B00100000,
    B00100000,
    6,

    B11111000,	//Z
    B00001000,
    B00010000,
    B00100000,
    B01000000,
    B10000000,
    B11111000,
    6,

    B11100000,	//[
    B10000000,
    B10000000,
    B10000000,
    B10000000,
    B10000000,
    B11100000,
    4,

    B00000000,	//(Backward Slash)
    B10000000,
    B01000000,
    B00100000,
    B00010000,
    B00001000,
    B00000000,
    6,

    B11100000,	//]
    B00100000,
    B00100000,
    B00100000,
    B00100000,
    B00100000,
    B11100000,
    4,

    B00100000,	//^
    B01010000,
    B10001000,
    B00000000,
    B00000000,
    B00000000,
    B00000000,
    6,

    B00000000,	//_
    B00000000,
    B00000000,
    B00000000,
    B00000000,
    B00000000,
    B11111000,
    6,

    B10000000,	//`
    B01000000,
    B00100000,
    B00000000,
    B00000000,
    B00000000,
    B00000000,
    4,

    B00000000,	//a
    B00000000,
    B01110000,
    B00001000,
    B01111000,
    B10001000,
    B01111000,
    6,

    B10000000,	//b
    B10000000,
    B10110000,
    B11001000,
    B10001000,
    B10001000,
    B11110000,
    6,

    B00000000,	//c
    B00000000,
    B01110000,
    B10001000,
    B10000000,
    B10001000,
    B01110000,
    6,

    B00001000,	//d
    B00001000,
    B01101000,
    B10011000,
    B10001000,
    B10001000,
    B01111000,
    6,

    B00000000,	//e
    B00000000,
    B01110000,
    B10001000,
    B11111000,
    B10000000,
    B01110000,
    6,

    B00110000,	//f
    B01001000,
    B01000000,
    B11100000,
    B01000000,
    B01000000,
    B01000000,
    6,

    B00000000,	//g
    B01111000,
    B10001000,
    B10001000,
    B01111000,
    B00001000,
    B01110000,
    6,

    B10000000,	//h
    B10000000,
    B10110000,
    B11001000,
    B10001000,
    B10001000,
    B10001000,
    6,

    B01000000,	//i
    B00000000,
    B11000000,
    B01000000,
    B01000000,
    B01000000,
    B11100000,
    4,

    B00010000,	//j
    B00000000,
    B00110000,
    B00010000,
    B00010000,
    B10010000,
    B01100000,
    5,

    B10000000,	//k
    B10000000,
    B10010000,
    B10100000,
    B11000000,
    B10100000,
    B10010000,
    5,

    B11000000,	//l
    B01000000,
    B01000000,
    B01000000,
    B01000000,
    B01000000,
    B11100000,
    4,

    B00000000,	//m
    B00000000,
    B11010000,
    B10101000,
    B10101000,
    B10001000,
    B10001000,
    6,

    B00000000,	//n
    B00000000,
    B10110000,
    B11001000,
    B10001000,
    B10001000,
    B10001000,
    6,

    B00000000,	//o
    B00000000,
    B01110000,
    B10001000,
    B10001000,
    B10001000,
    B01110000,
    6,

    B00000000,	//p
    B00000000,
    B11110000,
    B10001000,
    B11110000,
    B10000000,
    B10000000,
    6,

    B00000000,	//q
    B00000000,
    B01101000,
    B10011000,
    B01111000,
    B00001000,
    B00001000,
    6,

    B00000000,	//r
    B00000000,
    B10110000,
    B11001000,
    B10000000,
    B10000000,
    B10000000,
    6,

    B00000000,	//s
    B00000000,
    B01110000,
    B10000000,
    B01110000,
    B00001000,
    B11110000,
    6,

    B01000000,	//t
    B01000000,
    B11100000,
    B01000000,
    B01000000,
    B01001000,
    B00110000,
    6,

    B00000000,	//u
    B00000000,
    B10001000,
    B10001000,
    B10001000,
    B10011000,
    B01101000,
    6,

    B00000000,	//v
    B00000000,
    B10001000,
    B10001000,
    B10001000,
    B01010000,
    B00100000,
    6,

    B00000000,	//w
    B00000000,
    B10001000,
    B10101000,
    B10101000,
    B10101000,
    B01010000,
    6,

    B00000000,	//x
    B00000000,
    B10001000,
    B01010000,
    B00100000,
    B01010000,
    B10001000,
    6,

    B00000000,	//y
    B00000000,
    B10001000,
    B10001000,
    B01111000,
    B00001000,
    B01110000,
    6,

    B00000000,	//z
    B00000000,
    B11111000,
    B00010000,
    B00100000,
    B01000000,
    B11111000,
    6,

    B00100000,	//{
    B01000000,
    B01000000,
    B10000000,
    B01000000,
    B01000000,
    B00100000,
    4,

    B10000000,	//|
    B10000000,
    B10000000,
    B10000000,
    B10000000,
    B10000000,
    B10000000,
    2,

    B10000000,	//}
    B01000000,
    B01000000,
    B00100000,
    B01000000,
    B01000000,
    B10000000,
    4,

    B00000000,	//~
    B00000000,
    B00000000,
    B01101000,
    B10010000,
    B00000000,
    B00000000,
    6,

    B01100000,	// (Char 0x7F)
    B10010000,
    B10010000,
    B01100000,
    B00000000,
    B00000000,
    B00000000,
    5
};

void scrollFont() {
    for (int counter=0x20;counter<0x80;counter++){
        loadBufferLong(counter);
        delay(500);
    }
}

// Scroll Message
void scrollMessage(prog_uchar * messageString) {
    int counter = 0;
    int myChar=0;
    do {
        // read back a char 
        myChar =  pgm_read_byte_near(messageString + counter); 
        if (myChar != 0){
            loadBufferLong(myChar);
        }
        counter++;
    } 
    while (myChar != 0);
}
// Load character into scroll buffer
void loadBufferLong(int ascii){
    if (ascii >= 0x20 && ascii <=0x7f){
        for (int a=0;a<7;a++){                      // Loop 7 times for a 5x7 font
            unsigned long c = pgm_read_byte_near(font5x7 + ((ascii - 0x20) * 8) + a);     // Index into character table to get row data
            unsigned long x = bufferLong [a*2];     // Load current scroll buffer
            x = x | c;                              // OR the new character onto end of current
            bufferLong [a*2] = x;                   // Store in buffer
        }
        byte count = pgm_read_byte_near(font5x7 +((ascii - 0x20) * 8) + 7);     // Index into character table for kerning data
        for (byte x=0; x<count;x++){
            rotateBufferLong();
            printBufferLong();
            delay(scrollDelay);
        }
    }
}
// Rotate the buffer
void rotateBufferLong(){
    for (int a=0;a<7;a++){                      // Loop 7 times for a 5x7 font
        unsigned long x = bufferLong [a*2];     // Get low buffer entry
        byte b = bitRead(x,31);                 // Copy high order bit that gets lost in rotation
        x = x<<1;                               // Rotate left one bit
        bufferLong [a*2] = x;                   // Store new low buffer
        x = bufferLong [a*2+1];                 // Get high buffer entry
        x = x<<1;                               // Rotate left one bit
        bitWrite(x,0,b);                        // Store saved bit
        bufferLong [a*2+1] = x;                 // Store new high buffer
    }
}  
// Display Buffer on LED matrix
void printBufferLong(){
  for (int a=0;a<7;a++){                    // Loop 7 times for a 5x7 font
    unsigned long x = bufferLong [a*2+1];   // Get high buffer entry
    byte y = x;                             // Mask off first character
    lc.setRow(3,a,y);                       // Send row to relevent MAX7219 chip
    x = bufferLong [a*2];                   // Get low buffer entry
    y = (x>>24);                            // Mask off second character
    lc.setRow(2,a,y);                       // Send row to relevent MAX7219 chip
    y = (x>>16);                            // Mask off third character
    lc.setRow(1,a,y);                       // Send row to relevent MAX7219 chip
    y = (x>>8);                             // Mask off forth character
    lc.setRow(0,a,y);                       // Send row to relevent MAX7219 chip
  }
}

The pertinent parts are at the top of the sketch – the following line sets the number of MAX7219s in the hardware:

const int numDevices = 2;

The following can be adjusted to change the speed of text scrolling:

const long scrollDelay = 75;

… then place the text to scroll in the following (for example):

prog_uchar scrollText[] PROGMEM ={
    "  THE QUICK BROWN FOX JUMPED OVER THE LAZY DOG 1234567890 the quick brown fox jumped over the lazy dog   "};

Finally – to scroll the text on demand, use the following:

scrollMessage(scrollText);

You can then incorporate the code into your own sketches. And a video of the example sketch in action:

Although we used the LedControl library, there are many others out there for scrolling text. One interesting example is Parola  – which is incredibly customisable.

Controlling LED numeric displays with the MAX7219

Using the MAX7219 and the LedControl library you can also drive numeric LED displays – up to eight digits from the one MAX7219. This gives you the ability to make various numeric displays that are clear to read and easy to control. When shopping around for numeric LED displays, make sure you have the common-cathode type.

Connecting numeric displays is quite simple, consider the following schematic which should appear familiar by now:

MAX7219 LED driver IC from PMD Way

The schematic shows the connections for modules or groups of up to eight digits. Each digit’s A~F and dp (decimal point) anodes connect together to the MAX7219, and each digit’s cathode connects in order as well. The MAX7219 will display each digit in turn by using one cathode at a time. Of course if you want more than eight digits, connect another MAX7219 just as we did with the LED matrices previously.

The required code in the sketch is identical to the LED matrix code, however to display individual digits we use:

lc.setDigit(A, B, C, D);

where A is the MAX7219 we’re using, B is the digit to use (from a possible 0 to 7), C is the digit to display (0~9… if you use 10~15 it will display A~F respectively) and D is false/true (digit on or off). You can also send basic characters such as a dash “-” with the following:

lc.setChar(A, B,'-',false);

Now let’s put together an example of eight digits:

#include "LedControl.h" //  need the library
LedControl lc=LedControl(12,11,10,1); // lc is our object
// pin 12 is connected to the MAX7219 pin 1
// pin 11 is connected to the CLK pin 13
// pin 10 is connected to LOAD pin 12
// 1 as we are only using 1 MAX7219
void setup()
{
  // the zero refers to the MAX7219 number, it is zero for 1 chip
  lc.shutdown(0,false);// turn off power saving, enables display
  lc.setIntensity(0,8);// sets brightness (0~15 possible values)
  lc.clearDisplay(0);// clear screen
}
void loop()
{
  for (int a=0; a<8; a++)
  {
    lc.setDigit(0,a,a,true);
    delay(100);
  }
  for (int a=0; a<8; a++)
  {
    lc.setDigit(0,a,8,1);
    delay(100);
  }
  for (int a=0; a<8; a++)
  {
    lc.setDigit(0,a,0,false);
    delay(100);
  }
  for (int a=0; a<8; a++)
  {
    lc.setChar(0,a,' ',false);
    delay(100);
  }
  for (int a=0; a<8; a++)
  {
    lc.setChar(0,a,'-',false);
    delay(100);
  }
  for (int a=0; a<8; a++)
  {
    lc.setChar(0,a,' ',false);
    delay(100);
  }
}

and the sketch in action:

Conclusion

We have only scratched the surface of what is possible with the MAX7219 and compatible parts. They’re loads of fun and quite useful as well.

This post brought to you by pmdway.com – everything for makers and electronics enthusiasts, with free delivery worldwide.

To keep up to date with new posts at tronixstuff.com, please subscribe to the mailing list in the box on the right, or follow us on twitter @tronixstuff.