The Arduino code is responsible for connecting to your local WiFi, connecting to the Adafruit IO service, retrieving the value stored in the feed, decoding the formatting and displaying the message on the RGB Panel. However, in order to use it, you will need to supply several parameters in the configuration.

Install Arduino Libraries

In order for Metro M4 Express Airlift to work, you will need to install several Arduino libraries. First, to use a variant of the Arduino WiFiNINA library, which is amazing and written by the Arduino team! Adafruit made a fork that you should install to get the functionality for the AirLift.

Click here to download the library:

 

Within the Arduino IDE, select Install library from ZIP...

And select the zip you just downloaded.

Six more libraries need to be installed using the Arduino Library Manager…this is the preferred and modern way. From the Arduino “Sketch” menu, select “Include Library” then “Manage Libraries…”

Type “gfx” in the search field to quickly find the first library — Adafruit_GFX:

Repeat the search and install steps, looking for the Adafruit Zero DMAAdafruit MQTT Library, RGB matrix Panel, ArduinoHttpClient, and Adafruit IO Arduino libraries.

Download the Sketch

Next you will need to download the sketch. Click on the following button to be taken to Github and download the .ino file and open it up in the Arduino IDE.

The main sketch starts off by initializing the RGB and then connecting to Adafruit IO and listening for messages. When new messages come in, it calculates the starting point of each line with formatting applied first and then using those calculations, it draws and formats the message to the RGB Matrix Panel.

// Message Panel
// Reads an Adafruit IO Feed, then formats and displays the message
// Author: Melissa LeBlanc-Williams

#include <RGBmatrixPanel.h>
#include <SPI.h>

#define BASE_CHAR_WIDTH 6   // 5 pixels + 1 space
#define BASE_CHAR_HEIGHT 8  // 7 pixels + 1 space

// Most of the signal pins are configurable, but the CLK pin has some
// special constraints.  On 8-bit AVR boards it must be on PORTB...
// Pin 8 works on the Arduino Uno & compatibles (e.g. Adafruit Metro),
// Pin 11 works on the Arduino Mega.  On 32-bit SAMD boards it must be
// on the same PORT as the RGB data pins (D2-D7)...
// Pin 8 works on the Adafruit Metro M0 or Arduino Zero,
// Pin A4 works on the Adafruit Metro M4 (if using the Adafruit RGB
// Matrix Shield, cut trace between CLK pads and run a wire to A4).

//#define CLK  8   // USE THIS ON ADAFRUIT METRO M0 ir adapting to use Airlift, etc.
#define CLK A4 // USE THIS ON METRO M4 (not M0)
#define OE   9
#define LAT 10
#define A   A0
#define B   A1
#define C   A2
#define D   A3

RGBmatrixPanel matrix(A, B, C, D, CLK, LAT, OE, false, 64);

#include "config.h"

// set up the 'messagepanel' feed
AdafruitIO_Feed *counter = io.feed("messagepanel");

void drawText(const char *text, bool resetPosition = true, uint16_t color = 0xffff, uint16_t textSize = 1) {
  matrix.setTextSize(textSize);     // size 1 == 8 pixels high
  if (resetPosition) {
    matrix.setCursor(0, 0);    // start at top left, with 8 pixel of spacing
  }
  matrix.setTextColor(color);
  matrix.print(text);
}

// This function is called whenever a 'messagepanel' message
// is received from Adafruit IO. it was attached to
// the counter feed in the setup() function above.
void handleMessage(AdafruitIO_Data *data) {
  String message = data->toString();
  String plainText = data->toString();
  uint16_t color = matrix.Color333(7, 7, 7);
  uint16_t textSize = 1;
  uint16_t colorStartIndex = 0, colorEndIndex = 0;
  uint16_t sizeStartIndex = 0, sizeEndIndex = 0;
  uint16_t strpos = 0;
  byte lineLengths[] = {0, 0, 0, 0};
  byte lineNum = 0;
  byte messageHeight = 0;
  byte lineHeight = 0;
  // Calculate line lengths
  boolean paramRead = false;
  boolean newLine = false;
  
  matrix.setCursor(0, 0);
  matrix.fillScreen(matrix.Color333(0, 0, 0));

  // Strip out all color data first
  while(strpos < plainText.length()) {
    colorStartIndex = plainText.indexOf('{');
    colorEndIndex = plainText.indexOf('}');
    plainText.remove(colorStartIndex, colorEndIndex - colorStartIndex + 1);
    strpos++;
  }

  // Calculate the line lengths in pixels for fixed width text
  strpos = 0;
  while(strpos < plainText.length()) {
    sizeStartIndex = plainText.indexOf('<');
    sizeEndIndex = plainText.indexOf('>');

    if (strpos == sizeStartIndex) {
      textSize = atoi(plainText.substring(sizeStartIndex + 1, sizeEndIndex).c_str());
      plainText.remove(sizeStartIndex, sizeEndIndex - sizeStartIndex + 1);
    }
    
    if (plainText.charAt(strpos) != '\n') {
      lineLengths[lineNum] += textSize * BASE_CHAR_WIDTH;
      if (textSize * BASE_CHAR_HEIGHT > lineHeight) {
        lineHeight = textSize * BASE_CHAR_HEIGHT;
      }
    }

    // We want to keep adding up the characters * textSize until we hit a newline character
    // or we reach the width of the message panel. Then we go down to the next line
    if (plainText.charAt(strpos) == '\n' || lineLengths[lineNum] >= matrix.width()) {
      messageHeight += lineHeight;
      lineHeight = 0;
      lineNum++;
    }
    
    strpos++;
  }

  // Add the last line
  messageHeight += lineHeight;

  textSize = 1;
  lineNum = 0;
  for(uint16_t i=0; i<message.length(); i++) {
    if (message.charAt(i) == '{') {
      paramRead = true;
      colorStartIndex = i + 1;
    } else if (message.charAt(i) == '}') {
      paramRead = false;
      int wheelPos = atoi(message.substring(colorStartIndex, i).c_str());
      if (wheelPos < 24) {
        color = Wheel(wheelPos);
      } else {
        color = matrix.Color333(7, 7, 7);
      }
    } else if (message.charAt(i) == '<') {
      paramRead = true;
      sizeStartIndex = i + 1;
    } else if (message.charAt(i) == '>') {
      paramRead = false;
      textSize = atoi(message.substring(sizeStartIndex, i).c_str());
    } else {
      if (paramRead) continue;

      if (matrix.getCursorX() == 0 && matrix.getCursorY() == 0) {
        matrix.setCursor(floor((matrix.width() / 2) - (lineLengths[lineNum] / 2)), matrix.height() / 2 - messageHeight / 2);
      } else if (newLine) {
        matrix.setCursor(floor((matrix.width() / 2) - (lineLengths[++lineNum] / 2)), matrix.getCursorY());
        newLine = false;
      }
      drawText(message.substring(i, i+1).c_str(), false, color, textSize);
      if (message.charAt(i) == '\n' || matrix.getCursorX() >= matrix.width()) {
        newLine = true;
      }
    }
  } 
}

void setup() {
  matrix.begin();

  // fill the screen with 'black'
  matrix.fillScreen(matrix.Color333(0, 0, 0));
  // draw some text!
  matrix.setTextWrap(true);
  drawText("Connecting...");
  Serial.begin(115200);
  io.connect();

  counter->onMessage(handleMessage);

  while(io.mqttStatus() < AIO_CONNECTED) {
    drawText(".");
    delay(500);
  }  

  counter->get();

  Serial.println();
  Serial.println(io.statusText());
}

void loop() {
  io.run();
}

// Input a value 0 to 23 to get a color value.
// The colours are a transition r - g - b - back to r.
uint16_t Wheel(byte WheelPos) {
  if(WheelPos < 8) {
   return matrix.Color333(7 - WheelPos, WheelPos, 0);
  } else if(WheelPos < 16) {
   WheelPos -= 8;
   return matrix.Color333(0, 7 - WheelPos, WheelPos);
  } else {
   WheelPos -= 16;
   return matrix.Color333(WheelPos, 0, 7 - WheelPos);
  }
}

One other file is used and that is config.h. This holds the connection information for WiFi and connecting to Adafruit IO. This is the same file is included with many of the Adafruit IO Library examples.

/************************ Adafruit IO Config *******************************/

// visit io.adafruit.com if you need to create an account,
// or if you need your Adafruit IO key.
#define IO_USERNAME "your_username"
#define IO_KEY "your_key"

/******************************* WIFI **************************************/

// the AdafruitIO_WiFi client will work with the following boards:
//   - HUZZAH ESP8266 Breakout -> https://www.adafruit.com/products/2471
//   - Feather HUZZAH ESP8266 -> https://www.adafruit.com/products/2821
//   - Feather HUZZAH ESP32 -> https://www.adafruit.com/product/3405
//   - Feather M0 WiFi -> https://www.adafruit.com/products/3010
//   - Feather WICED -> https://www.adafruit.com/products/3056
//   - Adafruit PyPortal -> https://www.adafruit.com/product/4116
//   - Adafruit Metro M4 Express AirLift Lite -> https://www.adafruit.com/product/4000
//   - Adafruit AirLift Breakout -> https://www.adafruit.com/product/4201
//   - Adafruit AirLift Shield -> https://www.adafruit.com/product/4285
//   - Adafruit AirLift FeatherWing -> https://www.adafruit.com/product/4264

#define WIFI_SSID "your_ssid"
#define WIFI_PASS "your_pass"

// uncomment the following line if you are using airlift
// #define USE_AIRLIFT

// uncomment the following line if you are using winc1500
// #define USE_WINC1500

// comment out the following lines if you are using fona or ethernet
#include "AdafruitIO_WiFi.h"

#if defined(USE_AIRLIFT) || defined(ADAFRUIT_METRO_M4_AIRLIFT_LITE)
  // Configure the pins used for the ESP32 connection
  #if !defined(SPIWIFI_SS) // if the wifi definition isnt in the board variant
    // Don't change the names of these #define's! they match the variant ones
    #define SPIWIFI SPI
    #define SPIWIFI_SS 10  // Chip select pin
    #define NINA_ACK 9    // a.k.a BUSY or READY pin
    #define NINA_RESETN 6 // Reset pin
    #define NINA_GPIO0 -1 // Not connected
  #endif
  AdafruitIO_WiFi io(IO_USERNAME, IO_KEY, WIFI_SSID, WIFI_PASS, SPIWIFI_SS, NINA_ACK, NINA_RESETN, NINA_GPIO0, &SPIWIFI);
#else
  AdafruitIO_WiFi io(IO_USERNAME, IO_KEY, WIFI_SSID, WIFI_PASS);
#endif
/******************************* FONA **************************************/

// the AdafruitIO_FONA client will work with the following boards:
//   - Feather 32u4 FONA -> https://www.adafruit.com/product/3027

// uncomment the following two lines for 32u4 FONA,
// and comment out the AdafruitIO_WiFi client in the WIFI section
// #include "AdafruitIO_FONA.h"
// AdafruitIO_FONA io(IO_USERNAME, IO_KEY);

/**************************** ETHERNET ************************************/

// the AdafruitIO_Ethernet client will work with the following boards:
//   - Ethernet FeatherWing -> https://www.adafruit.com/products/3201

// uncomment the following two lines for ethernet,
// and comment out the AdafruitIO_WiFi client in the WIFI section
// #include "AdafruitIO_Ethernet.h"
// AdafruitIO_Ethernet io(IO_USERNAME, IO_KEY);

Configuring the Sketch

There are a few things you will need to edit to get the message panel up and running. Once you have opened up the sketch in the Arduino IDE successfully, click on the tab labeled config.h.

Fill in your IO_USERNAME and IO_KEY with your Adafruit IO Username and Key. If you're not sure where to find them, there's a section about doing that on the Stream Deck Plugin page.

Fill in the WIFI_SSID and WIFI_PASS with your WiFi name and password.

Make sure #define USE_AIRLIFT is uncommented.

Go back to the MessagePanel tab and if you decided to use a different feed name, find the following line and change it.

Download: file
AdafruitIO_Feed *counter = io.feed("messagepanel");

Be sure the correct board (Adafruit Metro M4 Airlift Lite) and port are selected and upload the sketch to your Metro M4 Express Airlift.

This guide was first published on Aug 21, 2019. It was last updated on Aug 21, 2019. This page (Arduino Code) was last updated on Apr 01, 2020.