//
// ESP32 64x8 64x8 Static, Scroll & Fade Four Messages - this script uses 4 sets of
// two 32x8 panels to create a four 64x8 message boards. It included the capability
// to both scroll and fade each of the messages independently as well as to set
// the maximum brightness of each message.
//
// _SCROLL_ENABLED - set to true or false to enable/disable message scrolling
// _BRIGHTNESS - integer 0 to 255 defining the maximum brightness of the message
// _NON_SCROLL_POSITION - message position in pixels from left side starting with 0 when message isn't scrolling
// _FADE_ENABLED - set to true or false to enable/disable message fading in and out
// _FADE_RATE - an integer value from 0 to 8 defining the fade rate where 0 is the slowest and 8 is the fastest
// this value specifies the number of steps when the message fades in or out
// 0 = 256 steps 1 = 128 step 2 = 64 steps 3 = 32 steps 4 = 16 steps
// 5 = 8 steps 6 = 4 step 7 = 2 steps 8 = 1 step
// _FULL_ON_PAUSE_COUNT - an integer value from 0 to 100 defining the number of steps to pause during the fade
// when message is at completely on at full brightness
// _FULL_OFF_PAUSE_COUNT - an integer value from 0 to 100 defining the number of steps to pause during the fade
// when message is completely off
//
// -- Message Definitions ----------------------------------------------------------
#define MESSAGE_A "TITANIUM"
#define MESSAGE_A_BRIGHTNESS 255
#define MESSAGE_A_SCROLL_ENABLED true // false or true
#define MESSAGE_A_NON_SCROLL_POSITION 21 // Pixels from left side starting with 0
#define MESSAGE_A_FADE_ENABLED false // false or true
#define MESSAGE_A_FADE_RATE 8 // Integer 0 to 8 for fade rate
#define MESSAGE_A_FULL_ON_PAUSE_COUNT 0 // Integer 0 to 100 for pause at fade full brightness
#define MESSAGE_A_FULL_OFF_PAUSE_COUNT 0 // Integer 0 to 100 for pause at fade completely off
#define MESSAGE_B "ELECTRICAL"
#define MESSAGE_B_BRIGHTNESS 64
#define MESSAGE_B_SCROLL_ENABLED false
#define MESSAGE_B_NON_SCROLL_POSITION 15
#define MESSAGE_B_FADE_ENABLED true
#define MESSAGE_B_FADE_RATE 7
#define MESSAGE_B_FULL_ON_PAUSE_COUNT 2
#define MESSAGE_B_FULL_OFF_PAUSE_COUNT 2
#define MESSAGE_C "SOLUTIONS"
#define MESSAGE_C_BRIGHTNESS 255
#define MESSAGE_C_SCROLL_ENABLED false
#define MESSAGE_C_NON_SCROLL_POSITION 18
#define MESSAGE_C_FADE_ENABLED true
#define MESSAGE_C_FADE_RATE 6
#define MESSAGE_C_FULL_ON_PAUSE_COUNT 0
#define MESSAGE_C_FULL_OFF_PAUSE_COUNT 0
#define MESSAGE_D "(512) 560-9551"
#define MESSAGE_D_BRIGHTNESS 255
#define MESSAGE_D_SCROLL_ENABLED false
#define MESSAGE_D_NON_SCROLL_POSITION 2
#define MESSAGE_D_FADE_ENABLED true
#define MESSAGE_D_FADE_RATE 5
#define MESSAGE_D_FULL_ON_PAUSE_COUNT 0
#define MESSAGE_D_FULL_OFF_PAUSE_COUNT 0
#define SCROLL_DELAY 200
#define VERTICAL_CHAR_POSITION 2 // Pixels from top starting with 0
// -- End of Message Defintions -----------------------------------------------------
// Include libraries
#include <Adafruit_NeoPixel.h>
#include <Adafruit_NeoMatrix.h>
//
// Define the four message panels
//
#define PANEL_A_PIN 15
#define PANEL_B_PIN 2
#define PANEL_C_PIN 0
#define PANEL_D_PIN 4
Adafruit_NeoMatrix messagePanelA = Adafruit_NeoMatrix(44, 11, 2, 1, PANEL_A_PIN,
NEO_MATRIX_TOP + NEO_MATRIX_LEFT + NEO_MATRIX_ROWS + NEO_MATRIX_PROGRESSIVE +
NEO_TILE_TOP + NEO_TILE_LEFT + NEO_TILE_ROWS + NEO_TILE_PROGRESSIVE,
NEO_GRB + NEO_KHZ800);
Adafruit_NeoMatrix messagePanelB = Adafruit_NeoMatrix(44, 11, 2, 1, PANEL_B_PIN,
NEO_MATRIX_TOP + NEO_MATRIX_LEFT + NEO_MATRIX_ROWS + NEO_MATRIX_PROGRESSIVE +
NEO_TILE_TOP + NEO_TILE_LEFT + NEO_TILE_ROWS + NEO_TILE_PROGRESSIVE,
NEO_GRB + NEO_KHZ800);
Adafruit_NeoMatrix messagePanelC = Adafruit_NeoMatrix(44, 11, 2, 1, PANEL_C_PIN,
NEO_MATRIX_TOP + NEO_MATRIX_LEFT + NEO_MATRIX_ROWS + NEO_MATRIX_PROGRESSIVE +
NEO_TILE_TOP + NEO_TILE_LEFT + NEO_TILE_ROWS + NEO_TILE_PROGRESSIVE,
NEO_GRB + NEO_KHZ800);
Adafruit_NeoMatrix messagePanelD = Adafruit_NeoMatrix(44, 11, 2, 1, PANEL_D_PIN,
NEO_MATRIX_TOP + NEO_MATRIX_LEFT + NEO_MATRIX_ROWS + NEO_MATRIX_PROGRESSIVE +
NEO_TILE_TOP + NEO_TILE_LEFT + NEO_TILE_ROWS + NEO_TILE_PROGRESSIVE,
NEO_GRB + NEO_KHZ800);
//
// Color declarations
//
// Define rgb colors data type
struct rgbColor
{
byte r;
byte g;
byte b;
};
// Define colors
const struct rgbColor black = { 0, 0, 0};
const struct rgbColor red = {255, 0, 0};
const struct rgbColor white = {255, 255, 255};
const struct rgbColor blue = {0, 0, 255};
const struct rgbColor green = {0, 255, 0};
const struct rgbColor orange = {255, 90, 0};
const struct rgbColor messageATextColor = red;
const struct rgbColor messageBTextColor = white;
const struct rgbColor messageCTextColor = green;
const struct rgbColor messageDTextColor = orange;
const struct rgbColor messageABackgroundColor = white;
const struct rgbColor messageBBackgroundColor = black;
const struct rgbColor messageCBackgroundColor = black;
const struct rgbColor messageDBackgroundColor = blue;
//
// Setup function
//
void setup() {
Serial.begin(115200);
Serial.println("setup function started.");
messagePanelA.begin();
messagePanelA.setTextWrap(false);
messagePanelA.setBrightness(MESSAGE_A_BRIGHTNESS);
messagePanelB.begin();
messagePanelB.setTextWrap(false);
messagePanelB.setBrightness(MESSAGE_B_BRIGHTNESS);
messagePanelC.begin();
messagePanelC.setTextWrap(false);
messagePanelC.setBrightness(MESSAGE_C_BRIGHTNESS);
messagePanelD.begin();
messagePanelD.setTextWrap(false);
messagePanelD.setBrightness(MESSAGE_D_BRIGHTNESS);
Serial.println("setup function complete");
}
//
// Other declarations
//
const char messageA[] = MESSAGE_A;
const char messageB[] = MESSAGE_B;
const char messageC[] = MESSAGE_C;
const char messageD[] = MESSAGE_D;
const int messageACharCount = (sizeof(messageA)/sizeof(messageA[0])) - 1;
const int messageBCharCount = (sizeof(messageB)/sizeof(messageB[0])) - 1;
const int messageCCharCount = (sizeof(messageC)/sizeof(messageC[0])) - 1;
const int messageDCharCount = (sizeof(messageD)/sizeof(messageD[0])) - 1;
const int displayWidth = messagePanelA.width(); // Assumes all four panels are the same width
// Starting charPosition is the rightmost pixel on the display which is
// where the start of the first character is initially displayed.
int messagePanelACharPosition = messagePanelA.width();
int messagePanelBCharPosition = messagePanelA.width();
int messagePanelCCharPosition = messagePanelA.width();
int messagePanelDCharPosition = messagePanelA.width();
// restartPosition is the "virtual" position of the first character when
// the last character scrolls of the leftmost pixel of the display. This
// position assumes each character is 6 pixels wide.
int messageARestartPosition = -((messageACharCount) * 6);
int messageBRestartPosition = -((messageBCharCount) * 6);
int messageCRestartPosition = -((messageCCharCount) * 6);
int messageDRestartPosition = -((messageDCharCount) * 6);
uint16_t messageAColorValue;
uint16_t messageBColorValue;
uint16_t messageCColorValue;
uint16_t messageDColorValue;
int messageABrightnessCount = 0;
int messageBBrightnessCount = 0;
int messageCBrightnessCount = 0;
int messageDBrightnessCount = 0;
int messageABrightnessValue = 255;
int messageBBrightnessValue = 255;
int messageCBrightnessValue = 255;
int messageDBrightnessValue = 255;
const int messageAFadeIncrementor = ((1 << MESSAGE_A_FADE_RATE) % 257);
const int messageBFadeIncrementor = ((1 << MESSAGE_B_FADE_RATE) % 257);
const int messageCFadeIncrementor = ((1 << MESSAGE_C_FADE_RATE) % 257);
const int messageDFadeIncrementor = ((1 << MESSAGE_D_FADE_RATE) % 257);
long iterationCount = 0;
//
// Loop function
//
void loop() {
//Serial.print("messageAFadeIncrementor = ");
//Serial.print(messageAFadeIncrementor);
//Serial.print(" || messageABrightnessCount = ");
//Serial.println(messageABrightnessCount);
Serial.print("loop function started. iterationCount = ");
Serial.println(iterationCount++);
//
// Panel A processing
//
messagePanelA.fillScreen(calculateFadedColorValue(messageABackgroundColor, 255));
if (MESSAGE_A_SCROLL_ENABLED) {
messagePanelA.setCursor(messagePanelACharPosition, VERTICAL_CHAR_POSITION);
if (--messagePanelACharPosition <= messageARestartPosition) {
messagePanelACharPosition = messagePanelA.width();
}
} else {
messagePanelA.setCursor(MESSAGE_A_NON_SCROLL_POSITION, VERTICAL_CHAR_POSITION);
}
if (MESSAGE_A_FADE_ENABLED) {
messageABrightnessCount = updateBrightnessCount(messageABrightnessCount,
messageAFadeIncrementor,
MESSAGE_A_FULL_ON_PAUSE_COUNT,
MESSAGE_A_FULL_OFF_PAUSE_COUNT);
messageABrightnessValue = getBrightnessValue(messageABrightnessCount,
messageAFadeIncrementor,
MESSAGE_A_FULL_ON_PAUSE_COUNT,
MESSAGE_A_FULL_OFF_PAUSE_COUNT);
}
messageAColorValue = calculateFadedColorValue(messageATextColor, messageABrightnessValue);
messagePanelA.setTextColor(messageAColorValue);
messagePanelA.print(messageA);
//
// Panel B processing
//
messagePanelB.fillScreen(calculateFadedColorValue(messageBBackgroundColor, 255));
if (MESSAGE_B_SCROLL_ENABLED) {
messagePanelB.setCursor(messagePanelBCharPosition, VERTICAL_CHAR_POSITION);
if (--messagePanelBCharPosition <= messageBRestartPosition) {
messagePanelBCharPosition = messagePanelB.width();
}
} else {
messagePanelB.setCursor(MESSAGE_B_NON_SCROLL_POSITION, VERTICAL_CHAR_POSITION);
}
if (MESSAGE_B_FADE_ENABLED) {
messageBBrightnessCount = updateBrightnessCount(messageBBrightnessCount,
messageBFadeIncrementor,
MESSAGE_B_FULL_ON_PAUSE_COUNT,
MESSAGE_B_FULL_OFF_PAUSE_COUNT);
messageBBrightnessValue = getBrightnessValue(messageBBrightnessCount,
messageBFadeIncrementor,
MESSAGE_B_FULL_ON_PAUSE_COUNT,
MESSAGE_B_FULL_OFF_PAUSE_COUNT);
}
messageBColorValue = calculateFadedColorValue(messageBTextColor, messageBBrightnessValue);
messagePanelB.setTextColor(messageBColorValue);
messagePanelB.print(messageB);
//
// Panel C processing
//
messagePanelC.fillScreen(calculateFadedColorValue(messageCBackgroundColor, 255));
if (MESSAGE_C_SCROLL_ENABLED) {
messagePanelC.setCursor(messagePanelCCharPosition, VERTICAL_CHAR_POSITION);
if (--messagePanelCCharPosition <= messageCRestartPosition) {
messagePanelCCharPosition = messagePanelC.width();
}
} else {
messagePanelC.setCursor(MESSAGE_C_NON_SCROLL_POSITION, VERTICAL_CHAR_POSITION);
}
if (MESSAGE_C_FADE_ENABLED) {
messageCBrightnessCount = updateBrightnessCount(messageCBrightnessCount,
messageCFadeIncrementor,
MESSAGE_C_FULL_ON_PAUSE_COUNT,
MESSAGE_C_FULL_OFF_PAUSE_COUNT);
messageCBrightnessValue = getBrightnessValue(messageCBrightnessCount,
messageCFadeIncrementor,
MESSAGE_C_FULL_ON_PAUSE_COUNT,
MESSAGE_C_FULL_OFF_PAUSE_COUNT);
}
messageCColorValue = calculateFadedColorValue(messageCTextColor, messageCBrightnessValue);
messagePanelC.setTextColor(messageCColorValue);
messagePanelC.print(messageC);
//
// Panel D processing
//
messagePanelD.fillScreen(calculateFadedColorValue(messageDBackgroundColor, 255));
if (MESSAGE_D_SCROLL_ENABLED) {
messagePanelD.setCursor(messagePanelDCharPosition, VERTICAL_CHAR_POSITION);
if (--messagePanelDCharPosition <= messageDRestartPosition) {
messagePanelDCharPosition = messagePanelD.width();
}
} else {
messagePanelD.setCursor(MESSAGE_D_NON_SCROLL_POSITION, VERTICAL_CHAR_POSITION);
}
if (MESSAGE_D_FADE_ENABLED) {
messageDBrightnessCount = updateBrightnessCount(messageDBrightnessCount,
messageDFadeIncrementor,
MESSAGE_D_FULL_ON_PAUSE_COUNT,
MESSAGE_D_FULL_OFF_PAUSE_COUNT);
messageDBrightnessValue = getBrightnessValue(messageDBrightnessCount,
messageDFadeIncrementor,
MESSAGE_D_FULL_ON_PAUSE_COUNT,
MESSAGE_D_FULL_OFF_PAUSE_COUNT);
}
messageDColorValue = calculateFadedColorValue(messageDTextColor, messageDBrightnessValue);
messagePanelD.setTextColor(messageDColorValue);
messagePanelD.print(messageD);
// Update all four panels
messagePanelA.show();
messagePanelB.show();
messagePanelC.show();
messagePanelD.show();
Serial.println("loop function completed.");
// Delay
delay(SCROLL_DELAY);
}
//
// Support functions
//
int updateBrightnessCount(int currentCount, int fadeIncrementor, int fullOnPause, int fullOffPause) {
long additionalIterations = (fullOnPause * fadeIncrementor) + (fullOffPause * fadeIncrementor);
return (currentCount+fadeIncrementor) % (512+additionalIterations);
}
int getBrightnessValue(int brightnessCount, int fadeIncrementor, int fullOnPause, int fullOffPause) {
int brightness = 0;
long fullOnCounts = fullOnPause * fadeIncrementor;
long fullOffCounts = fullOffPause * fadeIncrementor;
// Determine brightness value
if ((brightnessCount >= 0) && (brightnessCount <= (256+fullOnCounts))) {
brightness = brightnessCount-1;
} else if (brightnessCount <= (512+fullOnCounts+fullOffCounts)) {
brightness = (512-1+fullOnCounts) - brightnessCount;
}
// Limit brightness to values in range 0..255
if (brightness < 0) {brightness = 0;}
if (brightness > 255) {brightness = 255;}
return brightness;
}
uint16_t calculateFadedColorValue(rgbColor messageColor, int brightnessValue) {
int red, green, blue;
uint16_t colorValue;
// Determine rgb values to display desired color and intensity
red = (byte)((brightnessValue/256.0)*messageColor.r);
green = (byte)((brightnessValue/256.0)*messageColor.g);
blue = (byte)((brightnessValue/256.0)*messageColor.b);
colorValue = messagePanelA.Color(red,green,blue);
return colorValue;
}