Using the 8x32 LED Matrix To Create
a Mobile, Wirelessly Controlled Display

revised d. bodnar   7-12-2017

I have used LED matrix displays for a number of different projects over the last few years.  These 8x8 LED units have a controller with them that allow an Arduino to talk to them sending text or graphic information that they can display.  These small units can be daisy-chained together to create a long, scrolling display. 

While the displays are visually appealing and easy to use they might not get the amount of attention that one would hope they would generate at a train show or other public train display.

With this in mind I decided to build an on-board train display using three 8x32 LED boards.  Each board is mounted on a car with the three connected together to crate one long scrolling message board.

To make things even more interesting and compelling to visitors the display's message can be changed remotely with a cell phone or computer.

The Displays
The three display units are available from Amazon and other vendors.  Each is made up of four 8x8 display modules combined on a single circuit board.  That makes connections very easy.

The display has a 5 pin input header on its right side and a 5 pin output header on the left.  The Arduino is connected to the left side and additional modules are connected to the left.  The connections are

  • VCC - positive 5 volts
  •  GND - ground
  •  DIN - data in - Arduino pin # 11
  •  CS - chip select - Arduino Pin # 10
  •  CLK - clock - Arduino pin # 13

The displays are not as bright as they could be as they are delivered.  In order to increase the contrast I always cover such displays with transparent red plastic or plastic tape.  To keep the size and weight down I opted for plastic tape for this installation.  I purchased it from eBay.

The two photos below show the dramatic difference in contrast when a piece of red tinted acrylic is put over the display.

Here the temperature (75F) is barely visible on my workbench.

The red acrylic makes the display fully readable, even in a  brightly lit area.

The Circuit
Just about any of the Arduino variants should work.  I chose to use the Pro Mini and the Nano for my two projects as they are much smaller than the UNO and many of its brothers.

This schematic shows the Pro Mini - wiring for the Nano is the same except for the location of some of the pins.

The Software
Three libraries need to be installed.  They are

(  note - The KeySwitch library is left over from the Scroll example and is not needed.  I left it there as some residual code that I modified does not compile unless it is there. ) 

The Arduino sketch that I put together is composed of code from a number of sources including an example, Parola Scrolling, from the MD_Parola library.  This library allows you to select the orientation of the modules and works very well with the 4 module boards. 

In order to configure the library for these boards you must edit one line in the  library.  The file that must be edited is called  MD_MAX72xx.h  

On my Windows computer I found this library in the following directory.
You may find it in another folder.

When  you find the file change the line that reads
#define USE_FC16_HW 0
#define USE_FC16_HW 1

Save the file and restart the Arduino IDE.


Command Options
The software allows you to enter new messages from the Arduino Terminal as shown here

In addition to entering text you can also enter commands that will change the scroll speed, scroll direction and a few others.

  • Scroll Direction - enter    /r      Enter it again to return to normal
  • Scroll Speed - enter   /s#   where # is a digit from 0 to 9 - entering /s0 gives the fastest scroll speed and /s9 the slowest.  (note that /s9 is VERY slow and can't be undone until it scrolls once at that speed)
  • Invert display - enter   /i    to change the display from red on a black background to black on a red background.  Enter it again to return to normal.
  • Set brightness - enter    /b#    where # is a digit from 0 to 9 - entering /b0 gives the dimmest display while /b9 is the brightest
A Wireless Connection
The message that is shown on the display can be edited from within the sketch or changed from the Arduino terminal if the unit is plugged into a computer.  I found this to be tedious as I wanted to be able to have the whole unit moving on a train.  To change the message I would have to stop the train, connect the Arduino to the computer and change the message.  A remote wireless solution was needed.

I considered three different methods of connecting to the moving train wirelessly.  WiFi, Bluetooth and a pair of RF Transceivers.

WiFi is surely doable but has one big disadvantage that removed it from consideration.  In order to use WiFi a wireless network needs to be available.  At some public venues (hotels, science centers, etc) WiFi is not open or not available.  Rather than setting up my own WiFi router in those situations I opted for two simpler wireless options, Bluetooth and HC-12 transceivers.

Bluetooth is the easiest and least expensive wireless option.  I added a simple Bluetooth board to the Arduino that controls the display with only three connections, vcc, ground and txd to Rx on the Arduino.  Note that you need to temporarily disconnect the Bluetooth txd from the Arduino rx to program the Arduino.

The Bluetooth module can be accessed from a cell phone, tablet or PC that has a Bluetooth capability.

On my android cell phone and tablet I used a free Bluetooth terminal program from Kai Morich - see

To start using the app touch the menu bars in the upper left and select Settings.  Make sure you change Send Newline to LF as shown here.

Next go to Bluetooth Devices in the same menu.  You should see the devices you have paired. 

My Bluetooth adapter is named  AMAZON06A and is seen here - select your device then go back to the main screen.

Click the icon that I have circled in yellow and you should connect to your Bluetooth device.

Now you can enter new text to be displayed.  You can also enter commands (see the description of these under Software)
The command shown here changes the brightness to maximum (/b9).

One of the nicest features in this terminal app is its ability to store and send out canned text, called macros.  In this screen shot 10 different macro keys are shown. 
If you hold the phone or tablet vertically only 6 macro keys are shown.

If you hold down one of the macro buttons you can edit what is says.  In this example I replaced M1 with my name.


G-Scale Installation
Software with /i, /r, /b#, s# - working
Filename: Parola_Scrolling-WORKS-v7
// MUST EDIT MD_MAX72xx.h (in users/dave/documents/arduino/libraries/MD_MAX72xx-master)
//   from #define USE_FC16_HW 0   to    #define USE_FC16_HW 1

// 7-10-2017 this one works using // to set reverse - i turned on UI and has to comment out
// the intensity command
//  also works with /i for INVERT black on red display & /r to scroll in reverse

// adding  /s#   for speed (0-9)
// adding /b#   for brightness (0-9)

// Use the Parola library to scroll text on the display
// Demonstrates the use of the scrolling function to display text received
// from the serial interface
// User can enter text on the serial monitor and this will display as a
// scrolling message on the display.
// Speed for the display is controlled by a pot on SPEED_IN analog in.
// Scrolling direction is controlled by a switch on DIRECTION_SET digital in.
// Invert ON/OFF is set by a switch on INVERT_SET digital in.
// Keyswitch library can be found at
// NOTE: MD_MAX72xx library must be installed and configured for the LED
// matrix type being used. Refer documentation included in the MD_MAX72xx
// library or see this link:

#include <MD_Parola.h>
#include <MD_MAX72xx.h>
#include <SPI.h>

// set to 1 if we are implementing the user interface pot, switch, etc
#define USE_UI_CONTROL 1

#include <MD_KeySwitch.h>

// Turn on debug statements to the serial output
#define DEBUG 0

#define PRINT(s, x) { Serial.print(F(s)); Serial.print(x); }
#define PRINTS(x) Serial.print(F(x))
#define PRINTX(x) Serial.println(x, HEX)
#define PRINT(s, x)
#define PRINTS(x)
#define PRINTX(x)

// Define the number of devices we have in the chain and the hardware interface
// NOTE: These pin numbers will probably not work with your hardware and may
// need to be adapted
#define MAX_DEVICES 12 // was 12
#define CLK_PIN   13
#define DATA_PIN  11
#define CS_PIN    10

MD_Parola P = MD_Parola(CS_PIN, MAX_DEVICES);

int flag = 0;
int InvertFlag = 0;
int ReverseFlag = 0;
int SpeedFlag = 0;
int BrightnessFlag = 0;
int SpeedValue = 1;
int BrightnessValue = 9;
// Scrolling parameters
const uint8_t SPEED_IN = SpeedValue;
const uint8_t DIRECTION_SET = 8;  // change the effect
const uint8_t INVERT_SET = 9;     // change the invert

const uint8_t SPEED_DEADBAND = 5;
#endif // USE_UI_CONTROL

uint8_t scrollSpeed = 15;    // default frame delay value  was 25
textEffect_t scrollEffect = PA_SCROLL_LEFT;
textPosition_t scrollAlign = PA_LEFT;
uint16_t scrollPause = 2000; // in milliseconds

// Global message buffers shared by Serial and Scrolling functions
#define	BUF_SIZE	101
char curMessage[BUF_SIZE];
char newMessage[BUF_SIZE];
bool newMessageAvailable = false;


MD_KeySwitch uiDirection(DIRECTION_SET);
MD_KeySwitch uiInvert(INVERT_SET);

void doUI(void)
  // set the speed if it has changed
    int16_t speed = map(analogRead(SPEED_IN), 0, 1023, 10, 150);

    if ((speed >= ((int16_t)P.getSpeed() + SPEED_DEADBAND)) ||
        (speed <= ((int16_t)P.getSpeed() - SPEED_DEADBAND)))
      speed = SpeedValue * 25;
      // Serial.print ("Speed = ");
      P.setIntensity(BrightnessValue);  /// Brightness setting
      //scrollSpeed = speed;
      //  scrollSpeed = SpeedValue * 25;  ///////////////////////////////////////////
      PRINT("\nChanged speed to ", P.getSpeed());

  if (ReverseFlag == 1) // SCROLL DIRECTION
    PRINTS("\nChanging scroll direction");
    scrollEffect = (scrollEffect == PA_SCROLL_LEFT ? PA_SCROLL_RIGHT : PA_SCROLL_LEFT);
    P.setTextEffect(scrollEffect, scrollEffect);
    ReverseFlag = 0;
    flag = 0;
  //  if ( == MD_KeySwitch::KS_PRESS)  // INVERT MODE

  if (InvertFlag == 1) // INVERT MODE
    PRINTS("\nChanging invert mode");
    InvertFlag = 0;
    flag = 0;
#endif // USE_UI_CONTROL

void readSerial(void)
  static char *cp = newMessage;

  while (Serial.available())
    *cp = (char);
    if ((*cp == '\n') || (cp - newMessage >= BUF_SIZE - 2)) // end of message character or full buffer
      *cp = '\0'; // end the string
      // restart the index for next filling spree and flag we have a message waiting
      cp = newMessage;
      newMessageAvailable = true;
    else  // move char pointer to next position

void setup()
  Serial.begin(9600);  // was 57600
  Serial.print("\n[Parola Scrolling Display]\nType a message for the scrolling display\nEnd message line with a newline");

  pinMode(SPEED_IN, INPUT);

#endif // USE_UI_CONTROL


  P.displayText(curMessage, scrollAlign, scrollSpeed, scrollPause, scrollEffect, scrollEffect);

  strcpy(curMessage, "SHMRRC");// - South Hills Model Railroad Club - Modular Train Layout  ");
  newMessage[0] = '\0';

void loop()
#endif // USE_UI_CONTROL
  if (P.displayAnimate())
    if (newMessageAvailable)

      Serial.println("  ");
      Serial.print("true-false ");
      Serial.println(newMessage == "\n");
      Serial.print("  ");
      if (newMessage[0] == '/' && newMessage[1] == 'b') { //   \n is line feed
        BrightnessValue = newMessage[2] - 48;
        Serial.print("GOT /b BRIGHTNESS & ");
        // InvertFlag = 1;
        flag = 1;
        newMessageAvailable = false;
      if (newMessage[0] == '/' && newMessage[1] == 's') { //   \n is line feed
        SpeedValue = newMessage[2] - 48;
        Serial.print("GOT /s SPEED & ");
        // InvertFlag = 1;
        flag = 1;
        newMessageAvailable = false;
      if (newMessage[0] == '/' && newMessage[1] == 'i') { //   \n is line feed
        Serial.println("GOT /i  INVERT");
        InvertFlag = 1;
        flag = 1;
        newMessageAvailable = false;
      if (newMessage[0] == '/' && newMessage[1] == 'r') { //   \n is line feed
        Serial.println("GOT /r  REVERSE");
        ReverseFlag = 1;
        flag = 1;
        newMessageAvailable = false;
      if (flag == 0) {
        strcpy(curMessage, newMessage);
        Serial.print("Cur-message = ");
        newMessageAvailable = false;

        InvertFlag = 0;

Note that the transmitter unit that connects to the phone uses the power from the cell phone to power both the USB to serial adapter and the HC-12.  If you prefer you can remove the VCC to VCC connection and apply 5 volts to the VCC connection on the HC-12.

In order to send a new text string to the display a serial terminal program needs to be installed on the cell phone.  On my Android phone I used a free app called Serial USB Terminal - works well and includes some macros to record some common announcements - nice!


To protect the transmitter module I designed a small plastic box using "Box Designer". This was imported into CorelDraw, modified a bit and cut out with my laser cutter.

Here the transmitter and case parts are shown...

... and here the module is in the case.