This is something that I haven't seen before on the forums, so I thought I would share the technique.
Its a variable pulse generator, which has been used for the past 12 days to flash some Christmas lights.
I wanted a pulse generator that I could vary without reprogramming. The serial data is used to send a pattern string, and the main
loop interprets each step in the pattern string. The steps in the string can be a delay definition, or a change of output pins,
or termination.
For example, Pd33.1D2D3Dd55.D0Z delays for 33 ms, sets output 1 to on for the delay, sets output 2 to on for the same delay, sets both output 1 and 2 on for 33+55+55 ms, sets outputs to zero, and restarts.
Additional commands allow printing the current pattern in use (hence allowing the pattern to be edited, altered and retried), store the pattern into EEPROM, read back from EEPROM, a help command, and a command to reset the pattern to a known state.
As a safety precaution, the pattern is not read back from EPROM for 10 seconds after startup, and the read back can be aborted by giving a command. I'm not sure in retrospect that this was worth adding, but I did not want to have to reprogram if I managed to store a bad pattern.
#include <EEPROM.h>
/*
Pulse gen, specify pattern output for two lines,
pattern can be saved and restored from eeprom
This version autorestores unless h is put down the serial line
*/
const byte ledpin13 = 13;
const byte ledPin10 = 11;
enum COMMANDS {
SETOUT, DELAY, END };
struct CA {
COMMANDS c;
union {
unsigned newout;
unsigned del;
};
};
class c_last_delpos
{
public:
c_last_delpos() {
_ca.c = DELAY;
_ca.del = 100;
}
;
void set(CA toset) {
_ca = toset;
};
CA get() {
return _ca;
};
private:
CA _ca;
};
boolean g_printflag = false;
boolean g_pattern_in = false;
boolean g_autorestoreflag = true;
int g_insertpos;
c_last_delpos last_delpos;
int last_del_printed;
int curpapos = 0;
const int pattmax = 50;
CA arr_ca[pattmax];
long previousMillis; // will store last time LED was updated
long autorestoreMillis;
int g_autorestoreseconds = 10;
void store_pattern()
{
Serial.print(sizeof(arr_ca));
byte *bp = (byte *)&arr_ca;
for (int i = 0; i < sizeof(arr_ca); i++)
{
EEPROM.write(0x20 + i, *bp);
bp ++;
}
Serial.println("byte, store done");
}
void restore_pattern()
{
Serial.print(sizeof(arr_ca));
byte *bp = (byte *)&arr_ca;
for (int i = 0; i < sizeof(arr_ca); i++)
{
*bp = EEPROM.read(0x20 + i);
bp ++;
}
// could be rubbish in there though, so make sure last ele of array safe
arr_ca[pattmax - 1].c = END;
Serial.println(" bytes, retrieve done");
}
void reset_all_pattern_states()
{
curpapos = 0;
g_printflag = false;
g_pattern_in = false;
arr_ca[0].c = DELAY;
arr_ca[0].del = 100;
arr_ca[1].c = SETOUT;
arr_ca[1].newout = '0';
arr_ca[2].c = DELAY;
arr_ca[2].del = 100;
arr_ca[3].c = SETOUT;
arr_ca[3].newout = '3';
arr_ca[4].c = END;
}
void movepatternpointer()
{
if (g_insertpos < pattmax)
{
g_insertpos++;
}
else
{
Serial.print("No more pattern space");
}
arr_ca[g_insertpos].c = END;
}
void setup() {
// set the digital pin as output:
pinMode(ledpin13, OUTPUT);
pinMode(ledPin10, OUTPUT);
Serial.begin(9600);
reset_all_pattern_states();
previousMillis = millis();
autorestoreMillis = millis();
}
void loop()
{
unsigned long currentMillis = millis();
switch (arr_ca[curpapos].c)
{
case END:
curpapos = 0;
if (g_printflag)
{
Serial.println('Z');
last_del_printed = 0;
}
break;
case SETOUT:
digitalWrite(ledpin13, 0x01 & arr_ca[curpapos].newout);
digitalWrite(ledPin10, 0x02 & arr_ca[curpapos].newout);
if (g_printflag)
{
Serial.print((char)(arr_ca[curpapos].newout));
}
curpapos ++;
break;
case DELAY:
if (currentMillis - previousMillis > (unsigned long)arr_ca[curpapos].del) {
// time to move on
previousMillis = currentMillis;
if (g_printflag)
{
if (last_del_printed != arr_ca[curpapos].del)
{
Serial.print('d');
Serial.print(arr_ca[curpapos].del);
Serial.print(".");
last_del_printed = arr_ca[curpapos].del;
}
else
{
Serial.print('D');
}
}
curpapos ++;
}
break;
} // end interpret pattern switch
if (g_autorestoreflag &&
currentMillis - autorestoreMillis > (unsigned long)1000)
{
g_autorestoreseconds -= 1;
autorestoreMillis = currentMillis;
Serial.print("To cancel press h, eeprom restore ");
Serial.println(g_autorestoreseconds);
if (g_autorestoreseconds < 1)
{
g_autorestoreflag = false;
restore_pattern();
}
}
if (Serial.available())
{
char c = Serial.read();
switch (c)
{
case 'p':
g_printflag = !g_printflag; // toggle printing flag
last_del_printed = 0;
break;
case 'P':
Serial.println("Enter new pattern [0123] or dn[nn]. or D or Z");
g_pattern_in = true;
g_insertpos = 0;
arr_ca[g_insertpos].c = END; // always ensure the end position is an END
break;
case '0':
case '1':
case '2':
case '3': // these are the bits for setting the digital lines out
if (g_pattern_in)
{
arr_ca[g_insertpos].c = SETOUT;
arr_ca[g_insertpos].newout = c;
movepatternpointer();
}
break;
case 'd':
if (g_pattern_in)
{
char c1 = '0';
Serial.print(c);
arr_ca[g_insertpos].c = DELAY;
arr_ca[g_insertpos].del = 0;
while(c1 != '.')
{
c1 = Serial.read();
Serial.print(c1);
if (c1 >= '0' && c1 <= '9')
{
arr_ca[g_insertpos].del *= 10;
arr_ca[g_insertpos].del += c1 - '0';
}
}
last_delpos.set(arr_ca[g_insertpos]); // save the delay value for later with D command
movepatternpointer();
}
break;
case 'D': // repeat last interval setting
if (g_pattern_in)
{
arr_ca[g_insertpos] = last_delpos.get();
movepatternpointer();
}
break;
case 'Z':
g_pattern_in = false;
Serial.println(c);
break;
case 'r':
case 'R':
reset_all_pattern_states();
Serial.println(c);
break;
case 'h':
case 'H':
g_autorestoreflag = false;
Serial.println("h help, Ss Store/Retrieve, p print on/off\n P new pattern, Rr reset pattern to preset");
break;
case 'S':
store_pattern();
break;
case 's':
restore_pattern();
break;
} // end switch
} // end if Serial.available()
} // end loop