Projects

Mini LED IoT Display

Liam Davies

Issue 41, December 2020

This article includes additional downloadable resources.
Please log in to access.

Log in

Build an eye-catching LED display capable of displaying data from the internet of your choosing!

BUILD TIME: 1 HOUR
DIFFICULTY RATING: IntermediatE

Whether it’s the time, weather, current news, stock prices, or even your own internet-stored data from your own DIY dataloggers, this affordable project is a great way to display data on your desk, at a glance!

THE BROAD OVERVIEW

Internet Of Things, as the name implies, is a common description of combining software-driven data and control with hardware-based interfaces. Last month, we looked at the Arduino MKR NB 1500 and it’s awesome power to unite the versatility of Arduino with the widespread coverage of Telstra networks.

This project will be a simple but highly expandable peek into the world of Internet Of Things, building our own IoT device with our own API calls and user interface! We’ll provide an introduction to how you can build your own streams of data from webservers, whether they are publicly available on the internet or sourced from your own devices.

The electronics for this project is very simple, consisting of only a LED matrix and an ESP8266 module. The focus of this project is on the software itself, but both the electronics and software can be expanded to build functionality for your own DIY electronics gadgets. Let’s get into it!

HOW IT WORKS

LED Display

The LED Display we have chosen for this project is Jaycar’s 8x16 single-colour LED matrix. 128 LEDs is enough to display alphanumeric characters, making it perfect for a simple yet effective IoT-connected display. While the display is quite small at only 72mm by 32mm, its the perfect size for a desk gadget that can provide periodical data updates.

In terms of controlling this display, it’s very simple. We do not need to worry about the specifics of driving the display, since the AIP1640 chip mounted on the back of the board takes care of it for us. We just need to feed in a I2C connection from our microcontroller and interface with it in code.

XC3746 8x16 LED Matrix with I2C connection.
XC4623 Blue LED Dot Matrix Display for Arduino from Jaycar.

Of course, there are countless other LED displays that you could use for this project. Here is Jaycar’s 32x16 large LED matrix, which could be used as a wall-mount IoT Display!

Note that as you increase the size and therefore required power of your displays, you will need an appropriately powerful 5V supply to match the LED display. You should only be drawing less than half an amp of current at the most through a standard USB computer port before a dedicated 5V power supply should be used!

Multiplexing the display

As a side note, using 128 microcontroller pins for driving one LED each is incredibly inefficient. The matrix includes a AIP1640 chip capable of driving 16 columns of 8 segments, by multiplexing between each row and column. This technique of driving large LED matrices has been explained in detail before in DIYODE, but here is a refresher if you’re curious!

LEDs, to light, need to have both a current source and a location to “sink” current to ground – in other, words, a complete circuit. It is difficult to individually control large banks of LEDs by independently controlling the sources and sinks of each. By applying a voltage to all LEDs within a row and then controlling which LEDs can sink current, a specific combination of LEDs can be lit. The next row is then enabled, and the process repeats. It is possible to trick the human eye into believing all LEDs are lit simultaneously if the multiplexing process is done fast enough – similar to how old CRT screens draw a scanline across the screen 60 times per second to create an illusion of a solid image.

The downside of multiplexing in this way is that because LEDs are turned quickly on and off over time, it can create a jittery and slightly uncomfortable viewing experience if the LEDs are moved around. Likewise, recording videos near the display can generate flickering in the video because of the difference in shutter speed to the LED multiplexing rate – keep this in mind if you plan to record videos.

WiFi Microcontroller

At the heart of our LED display is the ESP8266 microcontroller. If you have done anything with IoT before, you will no doubt heard of this little monster! It packs a capable microcontroller, GPIO pins, WiFi connectivity and compatibility with Arduino code into a package no bigger than an Aussie 20-cent coin.

Since the focus of this project is not on the electronics and rather on the software that controls our overall IoT system, we have chosen not to build our own circuit based on the ESP8266 and have instead chosen a pre-built board that includes everything we need.

ESP8266 from Jaycar

The weapon of choice is the WiFi Mini from Jaycar, a very compact microcontroller board built around the ESP8266. The only real setup required for this chip is soldering on its header pins, which are included with the board.

When building this project, you could of course use your own variant of ESP8266 microcontroller boards. Some are shown here if you prefer a more expanded pin selection and more room to work.

ADA2471 Adafruit HUZZAH ESP8266 Breakout from Core Electronics.
WRL-13804 Sparkfun ESP8266 Thing Dev Board from Core Electronics.

Makers experienced with the ESP8266 may have noticed a problem with using this microcontroller with the LED display. The ESP8266 operates on 3.3V, while the LED display operates on 5V! This raises concerns about the operating voltage and logic levels of these two interfacing devices. To solve our issue with operating voltage (the voltage required to safely operate each device), we’ll be using the inbuilt 3.3V regulator in the WiFi Mini, which can regulate the 5V input of the MicroUSB port down to a safe 3.3V for the microcontroller. Since the 5V from the WiFi Mini can still be used from its 5V pin, we will connect the LED Matrix’s VCC pin straight to it. The logic levels, if powered from a reasonable 5V source, should allow the threshold voltage of the LED matrix driver chip to be met, which needs to be over 3.5V (70% of 5V according to the AIP1640 datasheet). If you end up having trouble getting your Matrix to work, connect a logic level shifter to boost the I2C communication up to 5V!

Instead of building this as a set-in-stone project, we’ll show you how to program and implement a variety of different IoT devices into the same LED display. That way, you could add your own data sources to the project, or even use a different display interface – all based on the ESP8266 microcontroller.

The Build:

Spoiler alert: It’s super easy! It’s more or less a matter of simply connecting the LED display to power and data from our WiFi Mini board, and connecting the Micro-USB cable to the board.

Parts Required:JaycarAltronicsCore Electronics
1 x Logic Level ConverterXC4486Z6390BOB-12009
1 x ESP8266 WiFi Chip^XC3802Z6381ADA2471 or WRL-13804
1 x 8x16 LED MatrixXC3746-CE07473

Parts Required:

You’ll also need a breadboard and prototyping hardware. ^ The layout and size of ESP8266-based boards differ. We used Jaycars XC3802.

To start putting together the project, it’s a good idea to solder male header pins onto the ESP8266 board. We did ours with the long side of the pins (that connect to the female headers) facing out on the same side as the ESP8266 chip itself. That way, the pin labels are clearly visible and the micro-USB port is on the top. If you want to make sure the pins are square and aligned, you can use a solderless breadboard to align the pins into the board while soldering – just don’t hold the iron on them too long to avoid damaging the breadboard or pins.

That’s the tricky part done! All we need to do now, on the electronics front of the project, at least is connect the power and data pins to the display. We connected Pin D1 on the ESP8266 board to SCL and Pin D2 to SDA. This can be done directly with the connector included with the matrix. We then connected the 5V and Ground (Labelled G) pins on the board to the, you guessed it, 5V and ground pins on the display.

Note: We used some jumper wires to visually space out the power connections on the breadboard, this doesn’t need to be done if you’re making it yourself.

The code

Libraries

Arduino IDE Library Manager:

ARDUINOJSON: Handles and parses JSON data from HTTP responses. (Use version 5.13.5 of the Arduino IDE library manager!)

AdDAFRUIT_GFX: Adafruit’s widely used library for formatting graphics on displays

ESP8266WIFI &ESP8266WIFIHTTPCLIENT: Included with ESP8266 board install. Provides HTTP functions.

TM16XX GITHUB REPOSITORY:

TM1640 & TM16XXMATRICGFX: Provides interface with AIP1640 chip in Jaycar’s 16x8 LED matrix.

Before we implement any APIs, we need to import required libraries to both scrape data from the internet and interface with our LED panel. Remember, the full code files can be found in the project files if you want to reference from it!

From the Arduino IDE, go to File > Preferences and insert the following URL into the Additional Boards Manager URLs field:

http://arduino.esp8266.com/stable/package_esp8266com_index.json

This will allow our Arduino IDE to install the ESP8266 board. To do this, head up to Tools > Board > Board Manager, and search for ESP8266. Install the boards package and then go back to the boards menu in the IDE. Depending on the variant of ESP8266 board you are using, select the exact board from the list.

Since we are using our WiFi Mini board, we selected the WeMos D1 R1 board – the closest to the Jaycar board. All of the other board settings can be left as is, with the exception of changing the COM port to the USB port you have the ESP8266 board plugged into.

#include <ArduinoJson.h>
#include <Adafruit_GFX.h>
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <TM1640.h>
#include <TM16xxMatrixGFX.h>

The four top libraries above need to be imported from the Arduino Library Manager, which can be found by selecting Sketch > Include Library > Manage Libraries from the Arduino IDE. Simply search up their names and install them.

Note: We had some trouble using Version 6 of the ArduinoJson library, so use the older version 5.13.5 instead if you come across errors as we did.

The two bottom-most libraries that allow interfacing with our LED Matrix cannot be found on the Library Manager. You will need to download them from MaxInt’s TM16xx GitHub repository, which can be found at https://github.com/maxint-rd/TM16xx

After unzipping the archive, drag it into your Arduino IDE’s library folder, which can be found alongside your other Arduino sketch folders. If you haven’t modified its location, the sketches directory can be found in Documents/Arduino.

Setup Code

We need to set up our LED matrix and our WiFi connection before we can display anything! We first want to set up some global variables and definitions so we can reference them later.

TM1640 module(4,5);
#define MATRIX_NUMCOLUMNS 16
#define MATRIX_NUMROWS 8
TM16xxMatrixGFX matrix(&module, MATRIX_NUMCOLUMNS, MATRIX_NUMROWS); 
const char* ssid = "YOUR_SSID_HERE";
const char* password = "PASSWORD123";

The first four lines of the above code sets up our Matrix object, using the SDA and SCL pins of the WiFi Mini (4 and 5). We also set up some constants for our WiFi network that we want our WiFi Mini to connect to – obviously, replace the constants above with the specifics of your WiFi router. Your SSID is the name of the WiFi, and the password is the WiFi password. Remember, both SSIDs and passwords are case-sensitive, so make sure you type them in correctly!

void setup() {
  matrix.setRotation(3);
  matrix.setMirror(false, true);
  matrix.setTextWrap(false);
  Serial.begin(115200);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) 
  {
    delay(1000);
    Serial.println("Connecting...");
  }
}

In our setup function, we first want to set up our LED matrix so it displays data properly when the Adafruit_GFX module is called. We found, by default, displaying letters on the matrix causes them to be mirrored vertically and rotated by 90 degrees. The two first lines of code correct this. If you want to change the orientation of your matrix – for instance, if you wanted the four-cable LED matrix connector to be at the bottom instead of the top – increment or decrement the setRotation function to change the text direction.

The serial display is initialised so we can output debugging information, then we attempt to connect to our network. While the WiFi is waiting to be connected, we output to the Serial Monitor saying that we are waiting to be added to the network. By the way, do not panic when if running this code, it takes upwards of 15 seconds to connect to the network. The ESP8266 must wait for the access point to allocate it a new local IP address before it is properly connected, and some routers aren’t the fastest in the world in doing that. Be patient!

Fetching the Weather

To display the weather in the local area, we will be using OpenWeather’s API to gather data. However, you can’t just throw a website address into our Arduino program. We first need to register an account and set up our API key, which prevents open and unlimited use of website API data – otherwise, users may try to pull very large amounts of data, overloading the server.

After heading to https://openweathermap.org/api, sign up for a free account. Without paying for an API subscription, we do not have access to historical weather data or more than 60 API calls per minute – which is plenty enough for us!

Once signed up, head over to your API Keys section of your account. There should be a pre-generated API key that you can paste directly into your program.

IMPORTANT NOTE: Treat your API key like a login password! Do not give it out to anybody as they effectively have access to your account usage of the API. The API keys we show in this project are all edited to be invalid for this reason.

Now, we can try fetching the current weather for a location. To try getting Sydney’s weather in your browser, type the following URL in and insert your own API key:

http://api.openweathermap.org/data/2.5/weather?q=Sydney&appid=YOUR_API_KEY

Trying this directly after registering for an account returns an Invalid API key error – the service clearly takes some time to register accounts. Wait 10 minutes or so and try again. If everything works as expected, you should be greeted with something like this:

{
  "coord": {
    "lon": 151.21,
    "lat": -33.87
  },
  "weather": [
    {
      "id": 802,
      "main": "Clouds",
      "description": "scattered clouds",
      "icon": "03d"
    }
  ],
  "base": "stations",
  "main": {
    "temp": 293.39,
    "feels_like": 288.69,
    "temp_min": 292.59,
    "temp_max": 294.26,
    "pressure": 1020,
    "humidity": 60
  },
  "visibility": 10000,
  "wind": {
    "speed": 7.7,
    "deg": 130
  },
  "clouds": {
    "all": 40
  },
  "dt": 1605568387,
  "sys": {
    "type": 1,
    "id": 9600,
    "country": "AU",
    "sunrise": 1605552148,
    "sunset": 1605602281
  },
  "timezone": 39600,
  "id": 2147714,
  "name": "Sydney",
  "cod": 200
}

It’s important to realise that this isn’t actually a HTML webpage! This is JavaScript Object Notation, or JSON for short. The OpenWeatherMap API has given us a JSON response, which is a fairly universal data format that virtually any computer or microcontroller can process and interpret (albeit with some libraries). We’re using the HTTPClient to fetch the results of the JSON response we showed above from the ESP8266, as shown below.

int getTemperature() {
  float temp = 0;
  if (WiFi.status() == WL_CONNECTED) 
  {
    HTTPClient http; 
http.begin("http://api.openweathermap.org/data/2.5/weather?q=Sydney&appid=
b342348f43a9d57e9d483320e8d50c494");
    int httpCode = http.GET();
    DynamicJsonBuffer jb;
    JsonObject& root = jb.parseObject(http.getStream());
    if (httpCode > 0) 
    {
      temp = root["main"]["temp"];
      return temp - 273.15;
    }
    http.end();
  }
  return temp;
}

After retrieving the JSON data, we use ArduinoJson to parse the results into a dynamic array. That way, variable length Json results (e.g. with different lengths of strings or integers) can easily be handled. After we have received the “root” of the JSON object, we can then reference nested data in it. You can identify the structure of the JSON object by inspecting the curly brackets and indents – since the temperature data is in the “main” object, we want to find the key “main” and repeat the process for “temp”. Great, now we’ve got the temperature – only problem is, it’s in Kelvin!

To convert from degrees Kelvin to degrees Celsius, it’s simply a matter of subtracting the temperature of absolute zero; -273.15°C. We can now call the GetTemperature function continuously in our loop() function.

Unfortunately, our display isn’t actually big enough to fit more than two characters simultaneously, so we have to come up with a more creative solution to change the size of the font. You’ll notice the five pixel wide letter “C” is slightly cut off. The default Adafruit GFX font is eight pixels high and five pixels wide, which can only be scaled up by integer amounts – a character sixteen pixels high and ten pixels wide (at a scale factor of 2) isn’t much use to us.One solution to fix this would be to make our own graphics library and font, using much thinner letters to allow fitting four or even five characters on the screen simultaneously. However, that means we’d have to make our own character bitmaps and write code for implementing it properly – that’s a lot of work.

Instead, we’re going to write our own code to scroll text across the display at a preset speed, allowing us to display any large text message we want!

void scrollMessage(String message, int rate) {
  int cursor_pos = 16;
  for(int i = 0; i < 22 + 
(message.length()+1) * 5; i++) {
    matrix.fillScreen(0);
    matrix.setCursor(cursor_pos,0);
    matrix.print((String)message);
    matrix.write();
    cursor_pos -= 1;
    delay(rate);
  }
}

And voila! This code should do the trick. Notice that we’ve written this as a function rather than having hardcoded a message so we can use it with other information in this project. You only need to call scrollMessage(“Hello”, 20) to scroll the world Hello relatively fast across the LED matrix. The function works by continuously writing to, then removing text from the matrix a short time later. Before writing the next block of text, the cursor (the point where the Adafruit_GFX library begins writing text) is moved one LED to the left. To figure out when we’ve scrolled the text all the way off the display, we need to know when all text characters are no longer visible. This is the purpose of the loop condition: i < 22 + (message.length()+1) * 5

The message length is incremented by one, to compensate for the one LED gap between each character, and then multiplied by five to get the total width of the message. Since the cursor starts at the right edge of the display and scrolls left, we need to wait for the entire message to disappear across the 16 pixels, plus some breathing room to space out subsequent message, which is the purpose of adding 22 to the condition. The result is an awesome scrolling text effect, from right to left. We set our scroll rate to 80, which is a rate that is comfortable to read the text at.

Of course, this function is only an example of what you could write to display information on your LED matrix. If you have access to weather data over time – say, you have a weather station entering data values into a MySQL database – you could instead plot the data as a graph on the matrix! It then becomes easy to see the continuously changing temperature, humidity or any other data source you have!

Displaying Time

We can write a similar function to obtain the time from the internet. Since we do not have a real-time clock within our ESP8266 board, whenever the WiFi Mini board is disconnected from power, it will forget it’s previously saved time. In other Arduino projects, we would have to buy a real-time clock module, like Jaycar’s XC4450 RTC module, but not here. We have the internet.

By using the WorldTimeAPI, we can obtain real-time data about the exact time, date and timezone. For this project, we simply want to get the time in 24-hour time.

Unlike the previous API, we don’t need an API key for WorldTimeAPI! By entering the URL http://worldtimeapi.org/api/timezone/Australia/Sydney into your browser, you’ll receive another JSON response like the one below:

{
  "abbreviation": "AEDT",
  "client_ip": "1.23.45.67.89",
  "datetime": "2020-11-18T14:17:07.204918+11:00",
  "day_of_week": 3,
  "day_of_year": 323,
  "dst": true,
  "dst_from": "2020-10-03T16:00:00+00:00",
  "dst_offset": 3600,
  "dst_until": "2021-04-03T16:00:00+00:00",
  "raw_offset": 36000,
  "timezone": "Australia/Sydney",
  "unixtime": 1605669427,
  "utc_datetime": 
"2020-11-18T03:17:07.204918+00:00",
  "utc_offset": "+11:00",
  "week_number": 47
}

The JSON field we are interested in here is, you guessed it, the “datetime” field. However, we only want the part of the string that contains the current 24-hour time. Counting from where the “T” is present in the datetime field, we can count 5 characters to the right and only select the hours and minutes. If you also prefer to have seconds, count another 3 to include the seconds field! To do this in Arduino code, we find the index of “T” in the datetime string and extracting the time in the substring() function. The substring function operates on a String object and returns the resulting string between the starting index (the first argument) and the ending index (the last argument).

Note: The starting index of the substring function is inclusive, while the ending index is exclusive. This means that the character at the starting index is included in the returned string, while the character at the ending index is not. That’s why the code below has one higher ending index than the diagram shown.

if (httpCode > 0) 
  {
    String temp_time = root["datetime"];
    int timeIndex = temp_time.indexOf("T");
    timeString = 
temp_time.substring(timeIndex + 1,timeIndex + 6);
    return timeString;
  }

Often, in IoT devices, you will need to process data streams to only get the data we need. There are often libraries available for automatically filtering and extracting required data, such as the ArduinoJSON library, while other times it is more convenient to write custom functions to extract data – like we have done.

While what we’ve shown here is a basic example, IoT data sources often have their own data format which may need to be converted into a user readable format. For example, some Arduino sensor modules may return their data in specific bit orders with a data stream.

If a library does not exist for the module, makers have to write custom functionality to handle it! This could involve processes such as bitshifting and bit-wise operators to accomplish data extraction efficiently. This is, of course, a more advanced example compared to the simple substring function we used in our code, but the concept is the same.

Tying everything together

Of course, writing a couple of functions and leaving them in our Arduino .ino code file does nothing without calling them. To make this project work, we are going to display the time, then the weather every 3 minutes. That way, the display is not going to be continually distracting on your desk.

void loop() {
  String time = getTime();
  scrollMessage(time,80);
  int temp = getTemperature();
  scrollMessage("Sydney:" + 
(String)temp + "C",80);
  delay(3 * 60 * 1000);
}

After uploading the code to our ESP8266, ensuring that a data-compatible micro-USB cable is used (some cables do not transmit data!), the board will wait until it connects to the target WiFi. After that, enjoy the awesome display of your hand-picked internet data. Check out our social media on Instagram and Facebook for clips of the display in action!

Troubleshooting

While this project is generally fairly reliable, there are still some small bugs that are common while making it. Thankfully, most are easy to iron out, so don’t panic!

If nothing is appearing on your LED matrix after uploading the code, there could be a couple of various culprits – either software or hardware related. The first thing to check is that your SDA and SCL pins on your LED matrix is connected to the correct pins on the WiFi Mini – Pin D1 for SCL and Pin D2 for SDA.

You might need to swap them. If that doesn’t work, check that your 5V and Ground pins on your LED matrix are being powered. Use a multimeter in voltage mode between them to ensure it is connected properly.

Still not cooperating? It could be software related. Our program has been written so that no messages will display until the ESP8266 successfully connects to the target WiFi network. Is the ESP8266 in range of the WiFi hotspot? Have you entered the SSID and Password correctly? - As we mentioned, it is case sensitive! Open the serial monitor in the IDE to figure out whether the board has connected to the network.

If it has connected to the network and still does not want to display anything, there could be a problem with fetching internet data. The ESP8266 might be connected to the router, but is the router actually connected to the internet? Try putting the HTTP URL from the Arduino program into your computer’s web browser and see if it returns an expected response. If you just registered for an API account, you may have to wait until your API key is activated.

Where To From Here?

This project isn’t particularly aesthetically appealing at the moment! What about 3D-printing a small enclosure to fit both the LED matrix and the WiFi Mini. It’ll polish it up, hiding all of those ugly wires! You’ll only need a micro-USB cable hanging out of the back of it to connect to a computer or 5V USB charger.

If you find the message display distracting while sitting stationary at your desk, you could add a motion sensor to the project, so it only activates when you walk up to or wave your hand near the matrix.

As we mentioned, there really is no limit to what you can swap out the display for in this project. You could hook it up to an RGB matrix, a 7-segment display for numerical data, or even the Quad-LED project from Issue 34.

Then, it’s only up to you to write for your chosen data display. As far as the capabilities of the mighty ESP8266 goes, it is an extraordinarily versatile chip, capable of doing most tasks the standard Arduino Uno is known for, with the addition of data sourced from the internet!

Liam Davies

Liam Davies

DIYODE Staff Writer