Vous êtes sur la page 1sur 7

Food Living Outside Play Technology Workshop

Arduino Persistence of Vision Display


by Mr.What on June 12, 2012

Table of Contents

Arduino Persistence of Vision Display . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1

Intro: Arduino Persistence of Vision Display . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2

File Downloads . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7

Related Instructables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7

Comments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7

http://www.instructables.com/id/Arduino-Persistence-of-Vision-Display/
Author:Mr.What author's website
Degrees in EE, specializing in Digital Signal Processing. Working as a software engineer for 20+ years.

Intro: Arduino Persistence of Vision Display


A persistence of vision (PoV) display is a row of LED's that flash out columns of a message. When the array of LED's is moved, like when mounted on a bicycle wheel,
the message can be read as if it were many LED's wide, instead of a single row.

The hardware setup for a PoV display is fairly straightforward, but this instructable contains code where the display can be controlled and programmed readily over the
serial connection, and display settings can be saved so that they are automatically loaded and run when powered up from a battery.

You will want an Arduino AVR microcontroller board, such as an Uno, Nano, or mini. Around 10 LED's, and 10 resistors from about 100 to 220 ohms.

For our example, we will assume a 10 LED display. Wire the LED's in series with a resistor to digital I/O pins 3-10, and arrange them in a straight row.

Load the attached sketch.

From the serial monitor (or terminal emulator), type h to get the help menu. This will show several commands.

My Serial reader class compresses whitespace, so you will want to use . to represent "off" in the line settings. See the attached Quelab.dat sample line setting input file.
The lines of this file can be cut and pasted into a terminal emulator to set the PoV message.

Once your desired display has been loaded, use the s) command to save its settings to EEPROM to be used at next reset.

int dummy=0; // this is to force sketch to put arduino include here

#define MODE_UNKNOWN 0
#define MODE_PoV 1
#define MODE_RANDOM 2
#define MODE_CYLON 3

#define MAX_COLS 96
#define SERIAL_BUF_LEN (MAX_COLS+15)
#include
SerialLineBuffer LineBuf;

struct {
short nCols; // no. columns in buffer
short spaceCols; // no. columns "space" time before repeat or reverse
short mode; // MODE_ code from above
short cylonCols; // no. cols of time for each cylon flash
int colTime; // milliseconds/column
int misc[3]; // reserved for future use
short disp[MAX_COLS]; // display flags
} State;

// These are the DI/O pins used for the display


#define NPINS 10
int ledPins[NPINS] = {12,11,10,9,8,7,6,5,4,3};

#include

void loadState()
{
int n = sizeof(State);
byte *bp = (byte *)(&tate);
for(int i=0; i < n; i++, bp++) *bp = EEPROM.read(i);
if (!validState()) initState();
}
void saveState()
{
int n = sizeof(State);
byte *bp = (byte *)(&tate);
for(int i=0; i < n; i++, bp++) EEPROM.write(i,*bp);
}

// set state to a reasonable default


void initState()
{
State.nCols = 2;
State.spaceCols = 1;
State.cylonCols = 10;
State.mode = MODE_PoV;
State.colTime = 10; // ms
for (int i=0; i < MAX_COLS; i++) State.disp[i] = (i&)?0x5555:0x2aaa;
saveState();
}

void setup()
{
int i;
for (i=0;i {
pinMode(ledPins[i],OUTPUT);
}
pinMode(13,OUTPUT); // use on-board LED
// restore state from EEPROM
loadState();
Serial.begin(9600);

http://www.instructables.com/id/Arduino-Persistence-of-Vision-Display/
}

void loop()
{
checkCommand();
int i,dt,k;
dt = State.colTime;
switch (State.mode)
{
case MODE_CYLON:
dt *= State.cylonCols;
for (i=0; i < NPINS; i++)
{
digitalWrite(ledPins[i],HIGH);
delay(dt);
digitalWrite(ledPins[(i+NPINS-1)%NPINS],LOW);
delay(dt);
}
for (i=NPINS-2; i >= 0; i--)
{
digitalWrite(ledPins[i],HIGH);
delay(dt);
digitalWrite(ledPins[(i+1)%NPINS],LOW);
delay(dt);
}
break;
case MODE_PoV:
for (i=0; i < State.nCols; i++)
{
short mask=1;
for (k=0; k < NPINS; k++, mask <<= 1)
digitalWrite(ledPins[k],(mask &State.disp[i])?HIGH:LOW);
}
for (k=0; k < NPINS; k++) digitalWrite(ledPins[k],LOW);
delay(State.spaceCols*dt);
break;
default: // random default
{
dt *= 10;
k = random(100);
int lvl = (k<50)?LOW:HIGH;
int j = random(NPINS);
digitalWrite(ledPins[j],lvl);
delay(dt);
}
}
digitalWrite(13,digitalRead(13)?LOW:HIGH); // toggle heartbeat
}

// poll for commands from serial port


void checkCommand()
{
short mask;
if (!LineBuf.isComplete()) return;
char key = lowCase(*(LineBuf.get()));
switch(key)
{
//short mask;
//int k;
//char *b;
case 'h' :
Serial.println(" h) help (print this message)");
Serial.println(" s) save state");
Serial.println(" r) random lights mode");
Serial.println(" c) cylon mode");
Serial.println(" p) PoV sign mode");
Serial.println(" n) no. cols to display");
Serial.println(" t) col time, ms");
Serial.println(" b) blank cols between repeat");
Serial.println(" i) re-Initialize state");
Serial.print( " Lx) Set pattern for line x, 0<=x<=");
Serial.println(NPINS);
break;
case 's' : saveState(); break;
case 'r' : State.mode = MODE_RANDOM; break;
case 'p' : State.mode = MODE_PoV; break;
case 'c' : State.mode = MODE_CYLON; break;
case 'i' : initState(); break;
case 'n' : State.nCols =nextInt(LineBuf.get()+1); break;
case 't' : State.colTime =nextInt(LineBuf.get()+1); break;
case 'b' : State.spaceCols=nextInt(LineBuf.get()+1); break;
case 'l' :
{
char *b = LineBuf.get()+1;
int k = ((int)(*b)) - ((int)'0');
if ((k<0) || (k > 15)) break;
b++;
short mask = (short)(1< for (int i=0; i < State.nCols; i++, b++)
{
if (isOn(*b)) State.disp[i] |= mask;
else State.disp[i] & ~mask;
}
break;
}
default :
Serial.print("Unrecognized Command : <");
Serial.print(LineBuf.get());
Serial.println(">");
http://www.instructables.com/id/Arduino-Persistence-of-Vision-Display/
Serial.println("Send command h for help.");
}
printState();
printMsg();
}

void printState()
{
Serial.print(State.nCols);
Serial.print(" Columns ");
Serial.print(State.spaceCols);
Serial.print(" ");
Serial.print(State.colTime);
Serial.println("ms/col");
Serial.flush();
}

void printMsg()
{
int i,k;
Serial.println();
for (i=0; i < State.nCols; i++) Serial.print("-");
short mask=1;
for(k=0; k < NPINS; k++, mask <= 1)
{
for (i=0; i < State.nCols; i++) Serial.print(State.disp[k]&ask?"X":" ");
Serial.println("|");
}
Serial.println();
for (i=0; i < State.nCols; i++) Serial.print("-");
Serial.println();
Serial.flush();
}

// parse next int from a string


int nextInt(const char *s)
{
const char *c = s;
int val = 0;
for(;;)
{
int k = ((int)(*c)) - ((int)'0');
if ((k<0)||(k>9)) return val;
val *= 10;
val += k;
c++;
}
}

bool isOn(const char c)


{
if ((c=='0') || (c=='.') || (c==' ') || (c==0)) return false;
//if ((c=='1')||(lowCase(c)=='x')) return true;
return true;
}

bool validState()
{
// check for silly state, set to default if inconsistent
if ((State.mode <= 0) || (State.mode > 3) ||
(State.nCols< 1) || (State.nCols>MAX_COLS)) return false;
if ((State.spaceCols < 1) || (State.spaceCols > 10*MAX_COLS)) return false;
if ((State.colTime < 1) || (State.colTime > 10000)) return false;
if ((State.cylonCols < 1) || (State.cylonCols > 10000)) return false;
return true;
}

-------- ioUtil.h

class SerialLineBufferPrivates;
class SerialLineBuffer
{
public:
SerialLineBuffer();
//~SerialLineBuffer();
bool isComplete(); // reads from serial, return true if 0 or EOLN
void clear();
void begin();
int length() const;
int maxLength() const;
char *get(); // retrieve current buffer and clear
char buf[SERIAL_BUF_LEN+1];
protected:
int _maxLength, _len;
bool _complete;
private:
//class SerialLineBufferPrivates *Priv;
};

char lowCase(const char a);


int caseCmp(const char a0, const char b0);
char *extractKey(char *cmdStr, char **val);
bool keyMatch(const char *key, const char *key1);

------------------------------- ioUtil.cpp

http://www.instructables.com/id/Arduino-Persistence-of-Vision-Display/
#include

#define NULL 0

// don't want to depend on ctype.h, just for this!


bool isBlank(int c)
{
if(c == 7) return(false); // bell
return( (c <= ' ') || (c > '~') );
}

#if defined(ARDUINO) & ARDUINO >= 100


#include
#warning ARDUINO
#else
#error ARDUINO not >= 100
#include
#endif

void SerialLineBuffer::begin()
{
_maxLength = SERIAL_BUF_LEN; // AVR dynamic mem is tricky
_len = 0;
_complete = false;
}
SerialLineBuffer::SerialLineBuffer() { begin(); }
bool isTerminator(int c)
{
if (c == 0) return(true);
if (c == ';') return(true); // sending \n to serial is tricky. accept this too.
//if ((c=='\n') || (c=='\r') || (c=='\m')) return(true);
if ((c>=10) & (c<=13)) return(true); // \r, \n, form feed, vert tab
return(false);
}

/// read from serial, return true if 0 or EOLN


bool SerialLineBuffer::isComplete()
{
if (_complete)
return(true); // don't read more until this line is consumed
// add characters from serial
while(Serial.available() > 0)
{
int nextByte = Serial.read();
//Serial.print("Got ");Serial.println(nextByte);
if ((nextByte < 0) || (nextByte >= 256))
return(_complete);
if (isTerminator(nextByte))
{
//Serial.print("terminator ");Serial.println(nextByte);
buf[_len] = 0;
_complete = (_len > 0);
return(_complete);
}

if (isBlank(nextByte))
{
//Serial.print("blank ");Serial.println(nextByte);
if (_len > 0) // ignore leading whitespace
{
if (buf[_len-1] != ' ') // compact space to 1 space
{
buf[_len++] = ' '; // convert all space to ' '
}
}
}
else
{
buf[_len++] = (char)nextByte;
}

// don't allow overflow


if (_len >= _maxLength)
{
Serial.println("\nOverflow. truncating command string");
_complete = true;
}
}
return(_complete);
}

void SerialLineBuffer::clear()
{
_len = 0;
_complete = false;
}
int SerialLineBuffer::length() const { return(_len); }
int SerialLineBuffer::maxLength() const { return(_maxLength); }

/// retrieve current buffer and clear


char *SerialLineBuffer::get()
{
buf[_len]=0;
clear();
return(buf);
}

//-----------------------------------------------------------
/// split a keyword-value pair string into a key string and value string
http://www.instructables.com/id/Arduino-Persistence-of-Vision-Display/
const char nullChar = 0; // static is scary on AVR
char *extractKey(char *cmdStr, char **val)
{
*val = (char *)&ullChar;
if (cmdStr == NULL) return(NULL);
char *key = cmdStr;
while (*key) // process comments
{
if (*key == '#') *key=0; // comment
else key++;
}
key = cmdStr;
while(*key & isBlank(*key)) key++; // trim leading space
*val = key;
while(**val & !isBlank(**val)) *val += 1; // skip key
**val = 0;
*val += 1;
while(**val & isBlank(**val)) *val += 1; // skip whitespace
return(key);
}

char lowCase(const char a)


{
if ((a >= 'A') & (a <= 'Z'))
{
int sft = ((int)'a') - ((int)'A');
int b = (int)a + sft;
return((char)b);
}
return(a);
}

int caseCmp(const char a0, const char b0)


{
char a = lowCase(a0);
char b = lowCase(b0);
if (a < b) return(-1);
return((a>b)?1:0);
}

bool keyMatch(const char *key0, const char *key1)


{
//Serial.print("keyMatch(");Serial.print(key0);Serial.print(",");Serial.print(key1);Serial.print(")=");
while(*key0 || *key1)
{
if (caseCmp(*key0, *key1))
{
//Serial.println("false");
return(false);
}
if (*key0) key0++;
if (*key1) key1++;
}
//Serial.println("true");
return(true);
}

-------------------------------- Input data example

L0..Q.............l......b....
L1.QQQ............l......b....
L2Q...Q...........l......b....
L3Q...Q.u..u..ee..l..a.a.b.b..
L4Q...Q.u..u.e..e.l.a.aa.bb.b.
L5Q...Q.u..u.e..e.l.a..a.b..b.
L6Q.Q.Q.u..u.eeee.l.a..a.b..b.
L7Q..Q..u..u.e....l.a..a.b..b.
L8.Q.Q..u..u.e..e.l.a.aa.bb.b.
L9.QQ.Q..uu...ee..l..a.a.b.b..

http://www.instructables.com/id/Arduino-Persistence-of-Vision-Display/
File Downloads

ArduinoPoVsource.zip (4 KB)
[NOTE: When saving, if you see .tmp as the file ext, rename it to 'ArduinoPoVsource.zip']

Related Instructables

Arduino-Square Arduino LCD Getting status Androino! Intro to Arduino


with Color LCD Twitter display through serial Control an by randofo
by bsquares by PKM monitor by Arduino from Wireless Altoids
zaghy2zy your Android Display by
device using a Alexdlp
cheap bluetooth
module. by
metanurb

Comments
2 comments Add Comment

Bongmaster says: Jun 12, 2012. 10:04 PM REPLY


got schematic?

Mr.What says: Jun 13, 2012. 7:11 AM REPLY


The schematic would be fairly simple:

Arduino |\ |
D3,4,5,6,7,8, -----/\/\/\/\/\/\------| \|------- Gnd
9,10,11,12 | /|
|/ |

Do this 10 times. One LED/resistor pair on each arduino output.

http://www.instructables.com/id/Arduino-Persistence-of-Vision-Display/

Vous aimerez peut-être aussi