OliB150
Published

Scrolling Text Display

A scrolling text display, using 294 LEDs to simulate a stock exchange ticker.

IntermediateShowcase (no instructions)18,367
Scrolling Text Display

Things used in this project

Hardware components

Arduino UNO
Arduino UNO
×1
WS2812 Addressable LED Strip
Digilent WS2812 Addressable LED Strip
This wasn't the actual product used, but uses the same technology.
×1
5V 4000mA Switching Power Supply
Digilent 5V 4000mA Switching Power Supply
Again, this wasn't the actual product used, but serves the same purpose.
×1

Software apps and online services

Arduino IDE
Arduino IDE

Story

Read more

Schematics

Schematic for scrolling text display

Code

Scrolling Text Display - Code

Arduino
Scrolling text display - code
Hard coded string & output colours due to issues that I didn't have time to fully debug.
I appreciate it's not the prettiest code, and there are certainly going to be more efficient ways to do it, but I ran out of time to make a perfect solution, and this worked for how I needed it.
#include <Adafruit_NeoPixel.h>
#include <Stdint.h>
  
#ifdef __AVR__
#include <avr/power.h>
#endif

#define stripPin 6
#define stripPinB 9

#define ledTotal 150 // zero indexed, so one less than you need. I.e. 50 leds = 49 here
#define ledTotalB 144 // zero indexed...
#define rowSize 6 // zero indexed...
#define columnSize 41 // zero indexed...
#define charWidth 5 // ** one indexed **

byte displayOutput[columnSize + 1] = {0};
byte displayColors[columnSize + 1] = {0};
byte currentStatus = 0; // 0 = off, 1 = on
unsigned int maxLoad = 0;

// was 68 with lower case chars, 42 without -- 40 without l/r arrow -- 42 WITH PLUS AND DOT
byte displayLetters[42][charWidth] = {  // [A] = chars/digits contained in array, [B] = charWidth (no need to add 1, as was 'one indexed' at #define)
  {0x3e, 0x51, 0x49, 0x45, 0x3e}, // 0 // , 0x00
  {0x00, 0x42, 0x7f, 0x40, 0x00}, // 1
  {0x42, 0x61, 0x51, 0x49, 0x46}, // 2
  {0x21, 0x41, 0x45, 0x4b, 0x31}, // 3
  {0x18, 0x14, 0x12, 0x7f, 0x10}, // 4
  {0x27, 0x45, 0x45, 0x45, 0x39}, // 5
  {0x3c, 0x4a, 0x49, 0x49, 0x30}, // 6
  {0x01, 0x71, 0x09, 0x05, 0x03}, // 7
  {0x36, 0x49, 0x49, 0x49, 0x36}, // 8
  {0x06, 0x49, 0x49, 0x29, 0x1e}, // 9

  {0x7e, 0x11, 0x11, 0x11, 0x7e}, // A
  {0x7f, 0x49, 0x49, 0x49, 0x36}, // B
  {0x3e, 0x41, 0x41, 0x41, 0x22}, // C
  {0x7f, 0x41, 0x41, 0x22, 0x1c}, // D
  {0x7f, 0x49, 0x49, 0x49, 0x41}, // E
  {0x7f, 0x09, 0x09, 0x09, 0x01}, // F
  {0x3e, 0x41, 0x49, 0x49, 0x7a}, // G
  {0x7f, 0x08, 0x08, 0x08, 0x7f}, // H
  {0x00, 0x41, 0x7f, 0x41, 0x00}, // I
  {0x20, 0x40, 0x41, 0x3f, 0x01}, // J
  {0x7f, 0x08, 0x14, 0x22, 0x41}, // K
  {0x7f, 0x40, 0x40, 0x40, 0x40}, // L
  {0x7f, 0x02, 0x0c, 0x02, 0x7f}, // M
  {0x7f, 0x04, 0x08, 0x10, 0x7f}, // N
  {0x3e, 0x41, 0x41, 0x41, 0x3e}, // O
  {0x7f, 0x09, 0x09, 0x09, 0x06}, // P
  {0x3e, 0x41, 0x51, 0x21, 0x5e}, // Q
  {0x7f, 0x09, 0x19, 0x29, 0x46}, // R
  {0x46, 0x49, 0x49, 0x49, 0x31}, // S
  {0x01, 0x01, 0x7f, 0x01, 0x01}, // T
  {0x3f, 0x40, 0x40, 0x40, 0x3f}, // U
  {0x1f, 0x20, 0x40, 0x20, 0x1f}, // V
  {0x3f, 0x40, 0x38, 0x40, 0x3f}, // W
  {0x63, 0x14, 0x08, 0x14, 0x63}, // X
  {0x07, 0x08, 0x70, 0x08, 0x07}, // Y
  {0x61, 0x51, 0x49, 0x45, 0x43}, // Z

  {0x00, 0x00, 0x00, 0x00, 0x00}, // space
  {0x10, 0x18, 0x1c, 0x18, 0x10}, // new up arrow
  {0x04, 0x0c, 0x1c, 0x0c, 0x04}, // new down arrow
  {0x08, 0x08, 0x08, 0x08, 0x08}, // dash / maintain
  {0x08, 0x08, 0x3E, 0x08, 0x08}, // plus
  {0x00, 0x60, 0x60, 0x00, 0x00} // full stop
};

const int stockQty = 17; // this is the amount of different stock values to be contained, *!* ONE indexed *!*
const int stockStepQty = 5; // *!* ONE indexed *!*
int stockStep = 0;
/*String stockNames[stockQty] = {
  "MH", "EJ", "SG", "MC", "AS", "JWL", "RP", "HCJ", "JT", "KC", "HCN", "MK", "JWK", "RB", "KS", "EC", "AW" 
};*/
/*int stockValues[stockStepQty][stockQty] = { // dies 3 from end, color wise
  {5, 12, 41, 52, 157, 74, 63, 96, 17, 86, 104, 49, 76, 46, 91, 61, 121},
  {6, 13, 40, 52, 155, 73, 60, 98, 18, 85, 105, 48, 77, 47, 90, 59, 125},
  {6, 12, 39, 53, 155, 75, 58, 99, 18, 84, 104, 46, 78, 46, 88, 60, 123},
  {7, 11, 42, 55, 156, 74, 59, 98, 17, 85, 106, 47, 77, 45, 92, 58, 124},
  {6, 13, 40, 54, 157, 75, 60, 97, 18, 85, 105, 48, 77, 46, 95, 60, 122}
};*/

Adafruit_NeoPixel strip = Adafruit_NeoPixel(ledTotal + 1, stripPin, NEO_RGB + NEO_KHZ800);
Adafruit_NeoPixel stripB = Adafruit_NeoPixel(ledTotalB + 1, stripPinB, NEO_RGB + NEO_KHZ800);

void setup() {
  strip.begin();
  strip.show();
  stripB.begin();
  stripB.show();

  pinMode(13, OUTPUT);
}
String inputString = "MJC|-5 CDL|-1 HJC^+10 MAK^+2 GJK|-8 EBJ^+5 HJC^+10 JCW^+0.02 ADS|-3 SEG^+5 RFB^+7 KGO^+2 MJH|-0.043 KHS^+0.054 SIC^0.008 EJC^+0.001 AMW^+5 LDK|-7 JCT^+9 JKM^+4 RJP|-4 SKC^+9 JLW|-5 HKC^+5 LMP^+7 ANS^+5 ";
String colString   = "0001110000111000022220000222000011100002220000222200002222220000111000022200002220000222000011111110000222222200002222220000222222200002220000111000022200002220000111000022200001110000222000022200002220";

void loop() {
  for (int i = 0; i < inputString.length(); i++) {
    char currentChar = inputString[i];
    int currentCol, colorMode;// = int(colString[i]) - 48; // minus 48 because int("0") will return the ascii value of char '0' (48)

    if ((currentChar >= 'A') && (currentChar <= 'Z')) {
      currentCol = 0;
    } else if (((currentChar >= '0') && (currentChar <= '9')) || (currentChar == '+') || (currentChar == '-')) {
      currentCol = colorMode;
    } else if (currentChar == '^') {
      colorMode = 2;
      currentCol = colorMode;
    } else if (currentChar == '|') {
      colorMode = 1;
      currentCol = colorMode;
    }

    int indexValue;
    if ((currentChar >= '0') && (currentChar <= '9')) indexValue = currentChar - 48; // 0-9
    else if ((currentChar >= 'A') && (currentChar <= 'Z')) indexValue = currentChar - 55; // upper case
    //else if ((currentChar >= 'a') && (currentChar <= 'z')) indexValue = currentChar - 61; // lower case , check that these are in the right place / the offset is correct, as i may have put the array chunk back in the wrong place!!
    else if ((currentChar == ' ')) indexValue = 36; // space, was 62 when lower case chars
    else if ((currentChar == '^')) indexValue = 37; // up arrow, was 63 when lower case chars
    else if ((currentChar == '|')) indexValue = 38; // down arrow, was 64 when lower case chars
//    else if ((currentChar == '<')) indexValue = 39; // left arrow, was 65 when lower case chars
//    else if ((currentChar == '>')) indexValue = 40; // right arrow, was 66 when lower case chars
    else if ((currentChar == '-')) indexValue = 39; // dash / maintain, was 67 when lower case chars, 41 with l/r arrows
    else if ((currentChar == '+')) indexValue = 40; // plus
    else if ((currentChar == '.')) indexValue = 41; // full stop

    for (int x = 0; x < charWidth; x++) {
      if (currentStatus == 0) currentStatus = 1;
      else if (currentStatus == 1) currentStatus = 0;
      setStatusIndicator(currentStatus);
      
      nudgeColumns(displayLetters[indexValue][x], currentCol); // inputCols[i]); // strip.Color(0,0,255));
      displayUpdate(0);

      delay(100);
    }
    nudgeColumns(0, 0); // - 1 might break things here? or make things go white due to the lookup default being white
    if (currentStatus == 0) currentStatus = 1;
    else if (currentStatus == 1) currentStatus = 0;
    setStatusIndicator(currentStatus);
    displayUpdate(0);
    delay(100); // this will be 200 as per the rest once confirmed that the double jump issue is done!
  }

  if (stockStep == (stockStepQty - 1)) stockStep = 0; // is this used anymore?!
  else stockStep++; // as above
}

int getIntLength(int value){
  // Function created by "zed_0xff" at: https://stackoverflow.com/a/3068412/7905386
  int l=!value;
  while(value){ l++; value/=10; }
  return l;
}

int getMappedPixel(int columnID, int rowID){
  int z;

  z = columnSize - columnID;
  if (columnID % 2) z++;

  z = z* (rowSize + 1);
  if (columnID % 2) z = z - rowID - 1;
  else z = z + rowID;

  if (z>= 120) z++;
  return z;
}

void setMappedColor(int columnID, int rowID, long pixelColor) {
  int mappedPixel = getMappedPixel(columnID, rowID);

  if (mappedPixel < ledTotal) {
    strip.setPixelColor(mappedPixel, pixelColor);
  }
  else {
    mappedPixel = mappedPixel - ledTotal;
    stripB.setPixelColor(mappedPixel, pixelColor);
  }
}

void colorAll(long color) {
  for (int i = 0; i <= rowSize; i++) {
    for (int j = 0; j <= columnSize; j++) {
      setMappedColor(j, i, color);
    }
  }
}

void convertToBits(byte inputValue, int outputArray[]) {
  //if (inputValue & 0x80) outputArray[0] = 1; // not used?
  if (inputValue & 0x40) outputArray[0] = 1;
  if (inputValue & 0x20) outputArray[1] = 1;
  if (inputValue & 0x10) outputArray[2] = 1;
  if (inputValue & 0x08) outputArray[3] = 1;
  if (inputValue & 0x04) outputArray[4] = 1;
  if (inputValue & 0x02) outputArray[5] = 1;
  if (inputValue & 0x01) outputArray[6] = 1;
}

void displayUpdate(long backColor) {
  colorAll(backColor);

  for (int i = 0; i <= columnSize; i++) {
    int columnData[rowSize + 1] = {0};
    convertToBits(displayOutput[i], columnData);

    for (int j = 0; j <= rowSize; j++) {
      if (columnData[rowSize-j] == 1) setMappedColor(i, j, colorLookup(displayColors[i])); // was displayColors[i][j] before memory save
    }
  }

  strip.show();
  stripB.show();
}

void nudgeColumns(byte newData, byte newColor) { 
  for (int i = 0; i < columnSize; i++) {
    displayOutput[i] = displayOutput[i + 1];
    for (int j = 0; j <= rowSize; j++) {
      displayColors[i] = displayColors[i + 1];
    }
  }
  displayOutput[columnSize] = newData;

  if (newColor >= 0) { // created for memory save
    for (int k = 0; k <= rowSize; k++) {
      displayColors[columnSize] = newColor;
    }
  }
}

long colorLookup(int inputColor) {
    long outputColor = 0;
    switch (inputColor) {
      case 0: 
        outputColor = strip.Color(255,255,255); // white
        break;
      case 1:
        outputColor = strip.Color(255,0,0); // red
        break;
      case 2:
        outputColor = strip.Color(0,255,0); // green
        break;
      case 3:
        outputColor = strip.Color(0,0,255); // blue
        break;
      case 4:
        outputColor = strip.Color(255,255,0); // yellow/amber
        break;
      case 5:
        outputColor = strip.Color(0,255,255); // cyan
        break;
      case 6:
        outputColor = strip.Color(255,0,255); // magenta
        break;
      default:
        outputColor = strip.Color(255,255,255); // white
        break;
    }

    return outputColor;
}

void setStatusIndicator(byte inputStatus) {
  if (inputStatus == 0) {
    digitalWrite(13, LOW);
  } else {
    digitalWrite(13, HIGH);
  }
}

Credits

OliB150

OliB150

0 projects • 5 followers

Comments