Here is some code I’ve been working on for a bike tail light / turn signal. It is working great. It is written for the RG matrix.
/* Code to control a bike light and turn signal. Two LED SPI matricies available
from SparkFun are chained to the arduino
arduino->Master->Slave
This created a 16x8 matrix. Two consecutive writes fills both master and slave. If you have a AVR
programmer, you can just program the LED matrices them selves (Version 2).
Author
– Justin Shaw wyojustin@gmail.com
*/
/* define constants */
#define DATAOUT 11 //MOSI
#define DATAIN 12 //MISO // NOT USED
#define SPICLOCK 13 //sck
#define SLAVESELECT 10 //ss
#define LEFT_ENABLE 3
#define RIGHT_ENABLE 2
#define LEFT_PIN 4
#define RIGHT_PIN 5
#define ACC_X_PIN 6
#define ACC_Y_PIN 7
#define BREAK_PIN 6
#define WAIT 50 // min delay between frames.
#define BLACK 0
#define RED 1
#define GREEN 2
#define ORANGE 3
// globals
int count = 0; // how long has currtent mode been active?
int mode_id = 0; // current mode id to detect a change in modes
int target = 0; // nominal accel value, set in calibrate()
int tol = 0; // accel tolarance, set in calibrate()
char out_buffer[64]; // output buffer for led matrix
struct Mode{
// mode has two screens that alternate at period rate.
int id; // unique id
int foreground_color1;
int background_color1;
int foreground_color2;
int background_color2;
int period; // # of WAIT periods
byte *foreground; // bitmap image
};
Mode normal, left, right, left_break, right_break, breaklight, current_mode;
// here are the bitmaps that the various modes use.
byte BACKGROUND = {
B11111111,
B11111111,
B11111111,
B11111111,
B11111111,
B11111111,
B11111111,
B11111111
};
byte X = {
B11100111,
B01000010,
B00100100,
B00011000,
B00011000,
B00100100,
B01000010,
B11100111
};
byte CHECKED = {
B00001111,
B00001111,
B00001111,
B00001111,
B11110000,
B11110000,
B11110000,
B11110000
};
byte STOPSIGN = {
B00111100,
B01111110,
B11111111,
B11111111,
B11111111,
B11111111,
B01111110,
B00111100
};
byte UP = {
B00011000,
B00111100,
B01111110,
B11111111,
B00111100,
B00111100,
B00111100,
B00111100
};
byte DOWN = {
B00010000,
B00110000,
B01111111,
B11111111,
B11111111,
B01111111,
B00110000,
B00010000
};
byte RIGHT = {
B00001000,
B00001100,
B11111110,
B11111111,
B11111111,
B11111110,
B00001100,
B00001000
};
byte LEFT = {
B00010000,
B00110000,
B01111111,
B11111111,
B11111111,
B01111111,
B00110000,
B00010000
};
/* fix arduino bug that gets wrong PW */
int myPulseIn(int pin){
long start = millis();
long pulse_start;
// wait for low
while(millis() < start + 1000 && digitalRead(pin) == HIGH){
}
return pulseIn(pin, HIGH);
}
/*
Average several pulsewidth readings
pin – pin number
n – number of samples to average
*/
int ave_pulse(int pin, int n){
float out = 0.;
for(int i = 0; i < n; i++){
// out += pulseIn(pin, HIGH);
out += myPulseIn(pin);
}
Serial.println((long) (out / n));
return (int) (out / n);
}
/*
Write image to output buffer.
Should be preceeded by a call to fill_buffer()
/
void write_buffer(){
digitalWrite(SLAVESELECT,LOW); // enable
delay(5);
for(int i = 0; i < 64; i++){
shiftOut(DATAOUT, SPICLOCK, MSBFIRST, out_buffer);*
- } *
- digitalWrite(SLAVESELECT,HIGH); // disable device*
- delay(5);*
}
/*
Fill a buffer usting bitmap (data) in the specified color
Bit locations with a 0 are untouched so layers can be added consecutivly
A call to fill_buffer() should preceed write_buffer.
The buffer is filled first to avoid timing errors involved in non-sequential writes
*/
void fill_buffer(byte* data, int color){
- byte bit;*
- for (int i = 0; i < 8; i++)*
- {*
- for(int j = 0; j < 8; j++){*
- bit = data[7 - i] >> j & B0000001;*
- if(bit){*
out_buffer[i * 8 + j] = color % 4;
- }*
- }*
- }*
}
/*
Check turn signals and accel to determine mode
set current_mode as per the sensor state
reset count if mode has changed
*/
void get_mode(){
- boolean left_val, right_val, break_val;*
- int acc_y, pulse_y;*
- Mode out;*
- // get mode from turn signals*
- left_val = digitalRead(LEFT_PIN) == LOW;*
- right_val = digitalRead(RIGHT_PIN) == LOW;*
- // read accel*
- pulse_y = ave_pulse(ACC_Y_PIN, 1);*
- if (mode_id == breaklight.id ||*
- mode_id == right_break.id ||*
- mode_id == left_break.id){*
- break_val = pulse_y < target - tol / 2;*
- }*
- else{*
- break_val = pulse_y < target - tol;*
- } *
- // select mode*
- if(left_val && break_val){*
- out = left_break;*
- }*
- else if(left_val){*
- out = left;*
- }*
- else if(right_val && break_val){*
- out = right_break;*
- }*
- else if(right_val){*
- out = right;*
- }*
- else if(break_val){*
- out = breaklight;*
- }*
- else{*
- out = normal;*
- }*
- current_mode = out;*
- // reset count if mode has changed*
- if(current_mode.id != mode_id){*
- count = 0;*
- }*
- mode_id = current_mode.id;*
}
/*
Calibrate accel. This should be stored but that would cost more hardware!
Assume level and still to calibrate horizontal.
*/
void calibrate(){
- int maxx = 0, minn = 15000, n = 5;*
- int y;*
- float mean;*
- fill_buffer(BACKGROUND, BLACK);*
- fill_buffer(X, RED);*
- write_buffer();*
- write_buffer();*
- for(int i = 0; i < n; i++){*
- y = ave_pulse(ACC_Y_PIN, 1);*
- if(y > maxx) maxx = y;*
- if(y < minn) minn = y;*
- mean += y;*
- }*
- mean /= n;*
- target = mean;*
- tol = 150;*
- fill_buffer(BACKGROUND, BLACK);*
- fill_buffer(X, GREEN);*
- write_buffer();*
- write_buffer();*
- delay(1000);*
}
/*
Initialize pins and modes, calibrate.
*/
void setup(){
- Serial.begin(9600);*
- pinMode(DATAOUT, OUTPUT);*
- pinMode(DATAIN, INPUT); // NOT USED*
- pinMode(SPICLOCK,OUTPUT);*
- pinMode(SLAVESELECT,OUTPUT);*
- digitalWrite(SLAVESELECT,HIGH); //disable device*
- pinMode(RIGHT_ENABLE, OUTPUT);*
- digitalWrite(RIGHT_ENABLE, HIGH);*
- pinMode(LEFT_ENABLE, OUTPUT);*
- digitalWrite(LEFT_ENABLE, HIGH);*
- // set up sensor inputs*
- pinMode(LEFT_PIN,INPUT);*
- pinMode(RIGHT_PIN,INPUT);*
- pinMode(BREAK_PIN,INPUT);*
- pinMode(ACC_X_PIN, INPUT);*
- pinMode(ACC_Y_PIN, INPUT);*
- // initialize modes*
- normal.id = 0;*
- normal.foreground_color1 = ORANGE;*
- normal.background_color1 = BLACK;*
- normal.foreground_color2 = BLACK;*
- normal.background_color2 = ORANGE;*
- normal.period = 8;*
- normal.foreground = CHECKED;*
- left.id = 1;*
- left.foreground_color1 = BLACK;*
- left.background_color1 = BLACK;*
- left.foreground_color2 = ORANGE;*
- left.background_color2 = BLACK;*
- left.period = 8;*
- left.foreground = LEFT;*
- right.id = 2;*
- right.foreground_color1 = ORANGE;*
- right.background_color1 = BLACK;*
- right.foreground_color2 = BLACK;*
- right.background_color2 = BLACK;*
- right.period = 8;*
- right.foreground = RIGHT;*
- left_break.id = 3;*
- left_break.foreground_color1 = BLACK;*
- left_break.background_color1 = BLACK;*
- left_break.foreground_color2 = RED;*
- left_break.background_color2 = BLACK;*
- left_break.period = 4;*
- left_break.foreground = LEFT;*
- right_break.id = 4;*
- right_break.foreground_color1 = RED;*
- right_break.background_color1 = BLACK;*
- right_break.foreground_color2 = BLACK;*
- right_break.background_color2 = BLACK;*
- right_break.period = 4;*
- right_break.foreground = RIGHT;*
- breaklight.id = 5;*
- breaklight.foreground_color1 = RED;*
- breaklight.background_color1 = BLACK;*
- breaklight.foreground_color2 = BLACK;*
- breaklight.background_color2 = BLACK;*
- breaklight.period = 2;*
- breaklight.foreground = BACKGROUND;*
- calibrate();*
}
/*
Get current mode, display current mode, pause.
*/
void loop(){
- digitalWrite(RIGHT_ENABLE, HIGH);*
- digitalWrite(LEFT_ENABLE, HIGH);*
- get_mode();*
- if(count == 0){*
- // mode has changed, fill both screens.*
- fill_buffer(BACKGROUND, current_mode.background_color2);*
- fill_buffer(current_mode.foreground, current_mode.foreground_color2);*
- write_buffer();*
- fill_buffer(BACKGROUND, current_mode.background_color1);*
- fill_buffer(current_mode.foreground, current_mode.foreground_color1);*
- write_buffer();*
- digitalWrite(RIGHT_ENABLE, HIGH);*
- digitalWrite(LEFT_ENABLE, HIGH);*
- }*
- else if(count % current_mode.period == 0){*
- fill_buffer(BACKGROUND, current_mode.background_color1);*
- fill_buffer(current_mode.foreground, current_mode.foreground_color1);*
- write_buffer();*
- digitalWrite(RIGHT_ENABLE, HIGH);*
- digitalWrite(LEFT_ENABLE, HIGH);*
- } *
- else if (count % current_mode.period == current_mode.period / 2){*
- fill_buffer(BACKGROUND, current_mode.background_color2);*
- fill_buffer(current_mode.foreground, current_mode.foreground_color2);*
- write_buffer();*
- }*
- else if( count % current_mode.period > current_mode.period / 2){*
- digitalWrite(RIGHT_ENABLE, LOW);*
- digitalWrite(LEFT_ENABLE, LOW);*
- }*
- delay(WAIT);*
- count+= 1;*
}