// Original by domjules:
// https://wokwi.com/arduino/projects/308344217332089409
//
// Improved by Tim2000:
// https://wokwi.com/arduino/projects/308364652939575872
//
// Changes by Koepel:
// https://wokwi.com/arduino/projects/308383904908182081
// - Pattern as bit pattern
// - Range-based for-loops in setup
// - function with array "by reference"
// - re-ordered the leds and resistors and connections
//
// Changes by Tim2000:
// https://wokwi.com/arduino/projects/309204710595756610
// - Scrolling message using bit shifting of a composite glyph.
const byte Rows[] = { 22, 23, 24, 25, 26, 27, 28, 29 };
const byte Cols[] = { 46, 47, 48, 49, 50, 51, 52, 53 };
const byte glyphs[][8] =
{
{
0b00000000,
0b00000000,
0b00000000,
0b00000000,
0b00000000,
0b00000000,
0b00000000,
0b00000000
},
{
0b00000000,
0b00010000,
0b00010000,
0b00010000,
0b00010000,
0b00000000,
0b00010000,
0b00000000
},
{
0b00000000,
0b00011000,
0b00100100,
0b00100100,
0b00111100,
0b00100100,
0b00100100,
0b00000000
},
{
0b00000000,
0b01110000,
0b01001000,
0b01111000,
0b01000100,
0b01000100,
0b01111000,
0b00000000
},
{
0b00000000,
0b00111000,
0b01000100,
0b01000000,
0b01000000,
0b01000100,
0b00111000,
0b00000000
},
{
0b00000000,
0b01110000,
0b01001000,
0b01000100,
0b01000100,
0b01001000,
0b01110000,
0b00000000
},
{
0b00000000,
0b00111100,
0b00100000,
0b00111000,
0b00100000,
0b00100000,
0b00111100,
0b00000000
},
{
0b00000000,
0b00111100,
0b00100000,
0b00111000,
0b00100000,
0b00100000,
0b00100000,
0b00000000
},
{
0b00000000,
0b00111000,
0b01000100,
0b01000000,
0b01001110,
0b01000100,
0b00111100,
0b00000000
},
{
0b00000000,
0b00100100,
0b00100100,
0b00111100,
0b00100100,
0b00100100,
0b00100100,
0b00000000
},
{
0b00000000,
0b00111000,
0b00010000,
0b00010000,
0b00010000,
0b00010000,
0b00111000,
0b00000000
},
{
0b00000000,
0b00011100,
0b00001000,
0b00001000,
0b00001000,
0b00101000,
0b00111000,
0b00000000
},
{
0b00000000,
0b00100100,
0b00101000,
0b00110000,
0b00101000,
0b00100100,
0b00100100,
0b00000000
},
{
0b00000000,
0b00100000,
0b00100000,
0b00100000,
0b00100000,
0b00100000,
0b00111100,
0b00000000
},
{
0b00000000,
0b01000010,
0b01100110,
0b01011010,
0b01000010,
0b01000010,
0b01000010,
0b00000000
},
{
0b00000000,
0b01000100,
0b01100100,
0b01010100,
0b01001100,
0b01000100,
0b01000100,
0b00000000
},
{
0b00000000,
0b00111100,
0b01000010,
0b01000010,
0b01000010,
0b01000010,
0b00111100,
0b00000000
},
{
0b00000000,
0b00111000,
0b00100100,
0b00100100,
0b00111000,
0b00100000,
0b00100000,
0b00000000
},
{
0b00000000,
0b00111100,
0b01000010,
0b01000010,
0b01001010,
0b01000110,
0b00111110,
0b00000001
},
{
0b00000000,
0b00111000,
0b00100100,
0b00100100,
0b00111000,
0b00100100,
0b00100100,
0b00000000
},
{
0b00000000,
0b00011100,
0b00100000,
0b00010000,
0b00001000,
0b00000100,
0b00111000,
0b00000000
},
{
0b00000000,
0b01111100,
0b00010000,
0b00010000,
0b00010000,
0b00010000,
0b00010000,
0b00000000
},
{
0b00000000,
0b01000100,
0b01000100,
0b01000100,
0b01000100,
0b01000100,
0b00111000,
0b00000000
},
{
0b00000000,
0b01000100,
0b01000100,
0b01000100,
0b01000100,
0b00101000,
0b00010000,
0b00000000
},
{
0b00000000,
0b01000010,
0b01000010,
0b01000010,
0b01000010,
0b01011010,
0b00100100,
0b00000000
},
{
0b00000000,
0b01000010,
0b00100100,
0b00011000,
0b00011000,
0b00100100,
0b01000010,
0b00000000
},
{
0b00000000,
0b01000100,
0b01000100,
0b00101000,
0b00010000,
0b00010000,
0b00010000,
0b00000000
},
{
0b00000000,
0b00111100,
0b00000100,
0b00001000,
0b00010000,
0b00100000,
0b00111100,
0b00000000
}
};
// Prototyping, because the Arduino pre-processor fails on a array "by reference"
void drawGlyph(const byte (&glyph)[8]);
String message = ""; // User message.
byte composite_glyph[8];
void setup()
{
Serial.begin(112500);
for (const auto a:Rows)
{
pinMode(a, OUTPUT);
digitalWrite(a, HIGH); // Switch off the rows.
}
for (const auto a:Cols)
{
pinMode(a, OUTPUT);
digitalWrite(a, LOW); // Switch off the columns.
}
pinMode(LED_BUILTIN, OUTPUT);
Serial.println(sizeof(glyphs));
Serial.println(sizeof(glyphs[0]));
Serial.println("Enter the message you would like displayed: ");
while (Serial.available() == 0) continue;
message = Serial.readString(); // Get user message.
message.trim();
message.toUpperCase();
Serial.println(message);
message = " " + message;
byte glyph1_index = ComputeGlyphIndexFromMessageIndex(message, 0);
byte glyph2_index = ComputeGlyphIndexFromMessageIndex(message, 1);
for (byte row = 0; row < 8; row++)
{
composite_glyph[row] = CompositeRow(glyphs[glyph1_index][row], glyphs[glyph2_index][row], 0);
}
}
void loop()
{
uint16_t timestamp = millis();
//
// TASK 1: Display the composite glyph and scroll it every interval.
//
const uint16_t SCROLL_STEP_INTERVAL = 200;
static uint16_t scroll_step_previous_timestamp = timestamp;
static byte scroll_step = 0;
static byte message_index = 0;
static byte glyph1_index = ComputeGlyphIndexFromMessageIndex(message, message_index);
static byte glyph2_index = ComputeGlyphIndexFromMessageIndex(message, message_index + 1);
// Draw the composite glyph.
drawGlyph(composite_glyph);
// Change the scroll step every interval.
if (timestamp - scroll_step_previous_timestamp >= SCROLL_STEP_INTERVAL)
{
scroll_step++;
if (scroll_step >= 8)
{
scroll_step = 0;
// Change the glyphs when fully scrolled to the next glyph.
message_index++;
if (message_index >= message.length())
{
message_index = 0;
}
glyph1_index = ComputeGlyphIndexFromMessageIndex(message, message_index);
glyph2_index = ComputeGlyphIndexFromMessageIndex(message, message_index + 1);
}
for (byte row = 0; row < 8; row++)
{
composite_glyph[row] = CompositeRow(glyphs[glyph1_index][row], glyphs[glyph2_index][row], scroll_step);
}
scroll_step_previous_timestamp += SCROLL_STEP_INTERVAL;
}
//
// TASK 2: Blink the inbuilt LED.
//
const uint16_t LED_BLINK_INTERVAL = 100;
static uint16_t led_blink_previous_timestamp = timestamp;
static bool led_state = false;
if (timestamp - led_blink_previous_timestamp >= LED_BLINK_INTERVAL)
{
led_state = !led_state;
digitalWrite(LED_BUILTIN, led_state);
led_blink_previous_timestamp += LED_BLINK_INTERVAL;
}
}
//void drawGlyph(const byte glyph[8]) // normal, but what fun is that ?
void drawGlyph(const byte (&glyph)[8]) // an array "by reference"
{
const uint16_t COLUMN_ON_TIME = 100;
uint16_t timestamp = micros();
static uint16_t previous_timestamp = timestamp;
static byte column = 0;
if (timestamp - previous_timestamp >= COLUMN_ON_TIME)
{
digitalWrite(Cols[column], LOW);
column++;
if (column >= 8)
{
column = 0;
}
for (byte row = 0; row < 8; row++)
{
// The first led on the left is column 0 is bit 7
byte ledState = (bitRead( glyph[row], 7 - column) == 1) ? HIGH : LOW;
digitalWrite(Rows[row], ledState);
}
digitalWrite(Cols[column], HIGH);
previous_timestamp += COLUMN_ON_TIME;
}
}
uint8_t CompositeRow(const uint8_t glyph1_row, const uint8_t glyph2_row, const uint8_t scroll_step)
{
uint16_t composite_row = glyph1_row << (8 + scroll_step);
composite_row |= glyph2_row << scroll_step;
composite_row >>= 8;
return (uint8_t)composite_row;
}
uint8_t ComputeGlyphIndexFromMessageIndex(const String& message, uint8_t message_index)
{
if (message_index >= message.length())
{
message_index = 0;
}
uint8_t glyph_index = message[message_index] - 'A' + 2;
if (glyph_index >= sizeof(glyphs) / sizeof(glyphs[0]))
{
glyph_index = 0;
}
return glyph_index;
}