Friday, January 7, 2022

72 x 16 WS2812B Addressable LED sign with Arduino Nano

I rewired another of my signs to have all of the LED's wired in series.  They will work with an ESP8266 or newer processor wired that way.  However I have, after a couple of days playing with the programming, done it with an Arduino Nano.  FastLed and AdaFruit NeoMatrix all create a large array then output it. This uses a lot of memory and hence limits the use of older processors. 

My software creates the output directly, eliminating the array stage.  The biggest issue is keeping the software fast.  For instance the Arduino Output "digitalWrite" command cannot be used, it is too slow.  I have to use direct port manipulation.  In the past I have outputted 8 bits to 8 LED strips using that technique.  Hower, by adding some shifting commands to the software I have done the same thing but I am only using D8 to actually talk to the LED sign.

This is what the LED sign looks like from the back side.

Here are some pictures of the text once I got the sign working.



The youTube video is at: https://youtu.be/rKZ0HcD0yVk

Here is the code:

// For Color Text on 1wire, Series wired WS2812 strings
// Features: Bluetooth support, 2 Lines, 5x7 Font
// by: BOB Davis January 2022

// Uses Port B for faster output of data
// Displays 2 lines of text switching between them.
// PORTB is Digital Pins 8-13 (Uses D8 actually)
// Colors: R=Red, G=Green, B=Blue, W=White
// Colors: Y=Yellow, P=Purple, C=Cyan

#define PIN 8 // D0 of port B - Actual pin to send data on

// Variables for colors of letters and background

int red=1; int green=1; int blue=1;

// Format: R/G/B=Foreground then R/G/B=Background then 1/2=line then text
String text1="G1ABCDEFGHIJKLMNOPQRSTUVWXYZ ";
String text2="R21234567890!@#$%^&*()_+{}[] ";
String textn=" "; // New text

// Send the next row to the pin 8 times.
// The delay timing is for an Arduino UNO or Nano.

void sendBits( byte bits ) {
// setting the MSB to 0 reduces brightness + power consumption
PORTB=0x0F; // turn on
PORTB=0x0F; // delay
PORTB=0x0F; // delay
PORTB=0x0F; // delay (add more for faster processors)
PORTB=0x00; // send data
PORTB=0x00; // delay
PORTB=0x00; // delay
PORTB=0x00; // delay
PORTB=0x00; // Turn off
PORTB=0x00; // Turn off
PORTB=0x00; // Turn off

for (int b=0; b<7; b++) {
PORTB=0x0F; // turn on
PORTB=0x0F; // delay
PORTB=0x0F; // delay
PORTB=0x0F; // delay (add more for faster processors)
PORTB=bits; // send data
PORTB=bits; // delay
PORTB=bits; // delay
PORTB=bits; // delay
PORTB=0x00; // Turn off
PORTB=0x00; // Turn off
PORTB=0x00; // Turn off
}
}

// Send bytes to LED's, avoid loops for maximum speed!
void sendPixelCol(byte Col) {
if (green)sendBits(Col); else sendBits(0x00);
if (red)sendBits(Col); else sendBits(0x00);
if (blue)sendBits(Col); else sendBits(0x00);
}

byte font5x7[][6] = { // font from AdaFruit
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Space
0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, // !
0x00, 0x07, 0x00, 0x07, 0x00, 0x00, //"
0x14, 0x7F, 0x14, 0x7F, 0x14, 0x00, //#
0x24, 0x2A, 0x7F, 0x2A, 0x12, 0x00,
0x23, 0x13, 0x08, 0x64, 0x62, 0x00,
0x36, 0x49, 0x56, 0x20, 0x50, 0x00,
0x00, 0x08, 0x07, 0x03, 0x00, 0x00, //'
0x00, 0x1C, 0x22, 0x41, 0x00, 0x00, //(
0x00, 0x41, 0x22, 0x1C, 0x00, 0x00, //)
0x2A, 0x1C, 0x7F, 0x1C, 0x2A, 0x00,
0x08, 0x08, 0x3E, 0x08, 0x08, 0x00,
0x00, 0x80, 0x70, 0x30, 0x00, 0x00,
0x08, 0x08, 0x08, 0x08, 0x08, 0x00,
0x00, 0x00, 0x60, 0x60, 0x00, 0x00,
0x20, 0x10, 0x08, 0x04, 0x02, 0x00, // /
0x3E, 0x51, 0x49, 0x45, 0x3E, 0x00, // 0
0x00, 0x42, 0x7F, 0x40, 0x00, 0x00, // 1
0x72, 0x49, 0x49, 0x49, 0x46, 0x00,
0x21, 0x41, 0x49, 0x4D, 0x33, 0x00,
0x18, 0x14, 0x12, 0x7F, 0x10, 0x00,
0x27, 0x45, 0x45, 0x45, 0x39, 0x00,
0x3C, 0x4A, 0x49, 0x49, 0x31, 0x00,
0x41, 0x21, 0x11, 0x09, 0x07, 0x00,
0x36, 0x49, 0x49, 0x49, 0x36, 0x00,
0x46, 0x49, 0x49, 0x29, 0x1E, 0x00, // 9
0x00, 0x00, 0x14, 0x00, 0x00, 0x00, // :
0x00, 0x40, 0x34, 0x00, 0x00, 0x00, // ;
0x00, 0x08, 0x14, 0x22, 0x41, 0x00,
0x14, 0x14, 0x14, 0x14, 0x14, 0x00,
0x00, 0x41, 0x22, 0x14, 0x08, 0x00,
0x02, 0x01, 0x59, 0x09, 0x06, 0x00,
0x3E, 0x41, 0x5D, 0x59, 0x4E, 0x00,
0x7C, 0x12, 0x11, 0x12, 0x7C, 0x00, //A
0x7F, 0x49, 0x49, 0x49, 0x36, 0x00,
0x3E, 0x41, 0x41, 0x41, 0x22, 0x00,
0x7F, 0x41, 0x41, 0x41, 0x3E, 0x00,
0x7F, 0x49, 0x49, 0x49, 0x41, 0x00,
0x7F, 0x09, 0x09, 0x09, 0x01, 0x00,
0x3E, 0x41, 0x41, 0x51, 0x73, 0x00,
0x7F, 0x08, 0x08, 0x08, 0x7F, 0x00,
0x00, 0x41, 0x7F, 0x41, 0x00, 0x00,
0x20, 0x40, 0x41, 0x3F, 0x01, 0x00,
0x7F, 0x08, 0x14, 0x22, 0x41, 0x00,
0x7F, 0x40, 0x40, 0x40, 0x40, 0x00,
0x7F, 0x02, 0x1C, 0x02, 0x7F, 0x00, //M
0x7F, 0x04, 0x08, 0x10, 0x7F, 0x00,
0x3E, 0x41, 0x41, 0x41, 0x3E, 0x00,
0x7F, 0x09, 0x09, 0x09, 0x06, 0x00,
0x3E, 0x41, 0x51, 0x21, 0x5E, 0x00,
0x7F, 0x09, 0x19, 0x29, 0x46, 0x00,
0x26, 0x49, 0x49, 0x49, 0x32, 0x00,
0x03, 0x01, 0x7F, 0x01, 0x03, 0x00,
0x3F, 0x40, 0x40, 0x40, 0x3F, 0x00,
0x1F, 0x20, 0x40, 0x20, 0x1F, 0x00,
0x3F, 0x40, 0x38, 0x40, 0x3F, 0x00,
0x63, 0x14, 0x08, 0x14, 0x63, 0x00,
0x03, 0x04, 0x78, 0x04, 0x03, 0x00,
0x61, 0x59, 0x49, 0x4D, 0x43, 0x00, //Z
0x00, 0x7F, 0x41, 0x41, 0x41, 0x00,
0x02, 0x04, 0x08, 0x10, 0x20, 0x00,
0x00, 0x41, 0x41, 0x41, 0x7F, 0x00,
0x04, 0x02, 0x01, 0x02, 0x04, 0x00,
0x40, 0x40, 0x40, 0x40, 0x40, 0x00,
0x00, 0x03, 0x07, 0x08, 0x00, 0x00,
0x20, 0x54, 0x54, 0x78, 0x40, 0x00, //a
0x7F, 0x28, 0x44, 0x44, 0x38, 0x00,
0x38, 0x44, 0x44, 0x44, 0x28, 0x00,
0x38, 0x44, 0x44, 0x28, 0x7F, 0x00,
0x38, 0x54, 0x54, 0x54, 0x18, 0x00,
0x00, 0x08, 0x7E, 0x09, 0x02, 0x00,
0x18, 0xA4, 0xA4, 0x9C, 0x78, 0x00,
0x7F, 0x08, 0x04, 0x04, 0x78, 0x00,
0x00, 0x44, 0x7D, 0x40, 0x00, 0x00,
0x20, 0x40, 0x40, 0x3D, 0x00, 0x00,
0x7F, 0x10, 0x28, 0x44, 0x00, 0x00,
0x00, 0x41, 0x7F, 0x40, 0x00, 0x00,
0x7C, 0x04, 0x78, 0x04, 0x78, 0x00, //m
0x7C, 0x08, 0x04, 0x04, 0x78, 0x00,
0x38, 0x44, 0x44, 0x44, 0x38, 0x00,
0xFC, 0x18, 0x24, 0x24, 0x18, 0x00,
0x18, 0x24, 0x24, 0x18, 0xFC, 0x00,
0x7C, 0x08, 0x04, 0x04, 0x08, 0x00,
0x48, 0x54, 0x54, 0x54, 0x24, 0x00,
0x04, 0x04, 0x3F, 0x44, 0x24, 0x00,
0x3C, 0x40, 0x40, 0x20, 0x7C, 0x00,
0x1C, 0x20, 0x40, 0x20, 0x1C, 0x00,
0x3C, 0x40, 0x30, 0x40, 0x3C, 0x00,
0x44, 0x28, 0x10, 0x28, 0x44, 0x00,
0x4C, 0x90, 0x90, 0x90, 0x7C, 0x00,
0x44, 0x64, 0x54, 0x4C, 0x44, 0x00, //z
0x00, 0x08, 0x36, 0x41, 0x00, 0x00,
0x00, 0x00, 0x77, 0x00, 0x00, 0x00,
0x00, 0x41, 0x36, 0x08, 0x00, 0x00,
0x02, 0x01, 0x02, 0x04, 0x02, 0x00,
0x3C, 0x26, 0x23, 0x26, 0x3C, 0x00,
0x1E, 0xA1, 0xA1, 0x61, 0x12, 0x00,
0x3A, 0x40, 0x40, 0x20, 0x7A, 0x00,
0x38, 0x54, 0x54, 0x55, 0x59, 0x00,
0x21, 0x55, 0x55, 0x79, 0x41, 0x00,
};

void sendline(String line) {
cli(); // No time for interruptions!
for (int c=0; c<8; c++){ // shift and send columns
for (int l=2; l<14; l++){ // Send characters
sendPixelCol((font5x7[(line)[l]-32][0])>>c);
sendPixelCol((font5x7[(line)[l]-32][1])>>c);
sendPixelCol((font5x7[(line)[l]-32][2])>>c);
sendPixelCol((font5x7[(line)[l]-32][3])>>c);
sendPixelCol((font5x7[(line)[l]-32][4])>>c);
sendPixelCol((font5x7[(line)[l]-32][5])>>c);
}
}
for (int c=0; c<8; c++){ // shift and send columns
for (int l=14; l<26; l++){ // Send characters
sendPixelCol((font5x7[(line)[l]-32][0])>>c);
sendPixelCol((font5x7[(line)[l]-32][1])>>c);
sendPixelCol((font5x7[(line)[l]-32][2])>>c);
sendPixelCol((font5x7[(line)[l]-32][3])>>c);
sendPixelCol((font5x7[(line)[l]-32][4])>>c);
sendPixelCol((font5x7[(line)[l]-32][5])>>c);
}
}
sei();
}

void setup() {
Serial.begin(9600); // opens serial port, data rate 9600 bps
pinMode(PIN, OUTPUT); // alternative set pin to output
}

void loop() {
if (Serial.available() > 0) {
textn=Serial.readString();
if ((textn[1])=='1') text1=textn;
if ((textn[1])=='2') text2=textn;
}
Serial.println(text1);
Serial.println(text2);

// Pad length to 40 characters
for(int i = text1.length(); i < 30; i++){ text1 += ' '; }
for(int i = text2.length(); i < 30; i++){ text2 += ' '; }

// Read colors from string,
if ((text1[0])=='R') red=1, green=0, blue=0;
if ((text1[0])=='G') red=0, green=1, blue=0;
if ((text1[0])=='B') red=0, green=0, blue=1;
if ((text1[0])=='W') red=1, green=1, blue=1;
if ((text1[0])=='Y') red=1, green=1, blue=0;
if ((text1[0])=='P') red=1, green=0, blue=1;
if ((text1[0])=='C') red=0, green=1, blue=1;

sendline(text1);
delay(4000); // Wait to display results

// Get New foreground and background colors
if ((text2[0])=='R') red=1, green=0, blue=0;
if ((text2[0])=='G') red=0, green=1, blue=0;
if ((text2[0])=='B') red=0, green=0, blue=1;
if ((text2[0])=='W') red=1, green=1, blue=1;
if ((text2[0])=='Y') red=1, green=1, blue=0;
if ((text2[0])=='P') red=1, green=0, blue=1;
if ((text2[0])=='C') red=0, green=1, blue=1;

sendline(text2);
delay(4000); // Wait to display results
return;
}

I have done some basic graphics, in this case a flag.  Basically its one byte per pixel.  Its easy to do as there is no math needed.  The big issue is the need for two 5 volt 15 amp power supplies to power it up.




No comments: