Program listing is after the jump. (It's been tweaked slightly since first publication; now uses PROGMEM to store the digit bitmaps and instructions in program memory, sparing precious Arduino RAM.)
/*
Arduinodometer!
(GLO-216 and Arduino Uno) This program demonstrates a fun visual effect
in which the final digit of a counter is animated to 'roll up,' like
the tenths-of-a-mile digit on a mechanical odometer.
Connect the GLO- to the Arduino +5 and GND, with serial input from
pin 3 (using NewSoftSerial library or Arduino v1.0+). If the Spol jumper
on your GLO- has been cut (for UART-direct input), change the parameter
"inverted" to "noninverted" in the program line:
SoftwareSerial mySerial = SoftwareSerial(rxPin, txPin, inverted);
*/
#include <avr/pgmspace.h> // needed for PROGMEM
#include <SoftwareSerial.h> // use any pin for serial out
#define rxPin 255 // Not used, set to invalid pin #
#define txPin 3 // Plug GLO's serial input into this pin.
#define inverted 1 // If GLO- Spol jumper is intact (COM-polarity)
#define noninverted 0 // If GLO Spol jumper is cut (UART polarity)
#define odoChar 0x8F // Custom character 15, at ASCII 143
// Bitmaps for the digits "0" - "9". Using PROGMEM to store them
// in Flash. This saves 80 bytes of Arduino RAM (at the expense of
// some code overhead).
const byte digitPatterns[] PROGMEM = {
0x0E,0x11,0x13,0x15,0x19,0x11,0x0E,0x80, // 0
0x04,0x0C,0x04,0x04,0x04,0x04,0x1F,0x80, // 1
0x0E,0x11,0x11,0x02,0x04,0x08,0x1F,0x80, // 2
0x1F,0x02,0x04,0x02,0x01,0x11,0x0E,0x80, // 3
0x02,0x06,0x0A,0x12,0x1F,0x02,0x02,0x80, // 4
0x1F,0x10,0x1E,0x01,0x01,0x11,0x0E,0x80, // 5
0x06,0x08,0x10,0x1E,0x11,0x11,0x0E,0x80, // 6
0x1F,0x01,0x02,0x04,0x08,0x08,0x08,0x80, // 7
0x0E,0x11,0x11,0x0E,0x11,0x11,0x0E,0x80, // 8
0x0E,0x11,0x11,0x0F,0x01,0x02,0x0C,0x80 // 9
}
;
// This program includes a GLO-216 instruction that expects 8 bytes of data.
// If the program is reset during this download, the GLO would consume
// our setup instructions thinking they were the expected data. So we'll
// feed it some harmless characters as insurance. After that, clear
// the screen (0x0C), set the tall 1x16 font (0x03,0x02) and specify
// text-style numbers (0x14).
const prog_char clsSetBigFont[ ] PROGMEM = {
0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, //reset insurance
0x0C,0x03,0x02,0x14,0x00 }
;
// This instruction consists of ctrl-A, ctrl-R, and the
// text number "6". Translation: 'Move to screen position 0,
// then right-align the number that follows in a 6-character
// field.' Starting at position 0 and moving backward with ctrl-R
// causes the text to align to the righthand end of the screen.
const prog_char setPosRightAlign[ ] PROGMEM = {
0x01,0x12,0x36,0x00}
;
// Instruction ESC d 7, sets up redefinition of
// custom character 15 (ASCII 143). The eight bytes sent
// after this instruction define the character bitmap.
const prog_char defineCC[ ] PROGMEM = {
0x1B,0x64,0x37,0x00}
;
// Main-count variable: 0-65535
unsigned int odoCount = 1024 ;
// RAM buffer for strings used by serial output, which can't
// directly access strings stored in program memory with PROGMEM.
char buffer[15] ;
// Define a new serial port using the pin definitions above.
// Using inverted serial; if Spol is cut, change to noninverted.
SoftwareSerial mySerial = SoftwareSerial(rxPin, txPin, inverted);
// SETUP:
// Initialize the serial output, send some bytes to complete a
// pending instruction, clear the screen and set the font, then
// print a label.
void setup() {
digitalWrite(txPin, LOW); // Stop bit state for inverted serial
pinMode(txPin, OUTPUT);
// set the data rate for the SoftwareSerial port
mySerial.begin(9600);
// give the GLO ample time to start up
delay(500);
strcpy_P(buffer, clsSetBigFont);
mySerial.print(buffer) ;
mySerial.print("Odometer:") ;
}
// LOOP:
// Print the main count, invoke the function that updates the
// rolling custom-character pattern, and update the main count
// when the final digit rolls over.
void loop() {
// right align at the end of the display
strcpy_P(buffer, setPosRightAlign);
mySerial.print(buffer);
// print the main count
mySerial.print(odoCount) ;
// now print the rolling final digit
mySerial.write(odoChar) ;
// roll up the last digit, and if it has
// rolled over to 0, increment main count
if (incOdoChar() == 0) {
odoCount++ ;
}
delay(25) ;
}
// incOdoChar()
// Moves a pointer (odoIndex) through the digitPattern[]
// array to redefine the odo-digit custom character.
// This causes the "0" bitmap to scroll up row-by-pixel-
// row to be replaced by the "1" bitmap, which scrolls up...
// Function returns the index value; when this rolls over
// to zero, the odoCount value should increment, just like
// a real mechanical odometer.
byte incOdoChar() {
static byte odoIndex = 0 ;
// send the instruction to redefine custom character
strcpy_P(buffer, defineCC);
mySerial.print(buffer);
// send the eight-byte bitmap of the new CC pattern
for (int i=0; i < 8; i++) {
//retrieve the next byte from the flash/program memory table
//and write it to the serial output
mySerial.write(pgm_read_byte(&digitPatterns[(i+odoIndex)%80]));
}
// increment for next time-- %80 to restrict
// to 0-79 (valid index range of digitPatterns[])
odoIndex = ++odoIndex%80 ;
// return index to signal rollover (0)
return odoIndex ;
}
Nice poject, but what is it do.
ReplyDeleteArduino
Thanks. Program shows how to communicate with the GLO-216 from an Arduino, how to define a custom character, and how to use a custom character to create an animated effect. And it shows off the bright, hi-contrast OLED in one of its four font sizes (unlike LCDs, it can make seamless characters from 2 lines by 16 to 1 line by 8 (1x16 shown).
ReplyDeleteOther than that, nothing much :)