EDIT: Also just double-checked to make it work in HyperTerminal
Code: Select all
/*
Copyright 2011 Nathan Hellweg all rights reserved
Macrolua interepreter for Arduino UNO
Macrolua by Dammit
*/
#define MAX_MACRO 1024
char macro[MAX_MACRO]; /* the macro */
char keys[128]={
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0
};
/* Macro Execution Globals */
int cur_pos=0; //Macro Execution Pointer
//Ticks are usecs...
unsigned int frame_ticks=16677;
unsigned int frame_ticks_num=5;
unsigned int frame_ticks_den=10;
//unsigned int frame_ticks=5559;
//unsigned int frame_ticks_num=16666;
//unsigned int frame_ticks_den=100000;
boolean incomment=false;
boolean firstplayer=true;
boolean var_waiting=false; //Whether there is a wait loop
int variable_wait=0; //Wait loop value
int wait=0;
int loop_level=-1; //Repeat stack pointer
int loop_start[8];//Repeat block start list
int loop_count[8];//Repeat count
unsigned int held=0;
unsigned int next=0;
void setpins(unsigned int pins) {
int i,j;
// Serial.print(pins,BIN);
// Serial.println(" setpins");
for(i=0;i<16;i++) {
j=pins&(1<<i);
if(0==j) {
digitalWrite(i+2,HIGH);
} else {
digitalWrite(i+2,LOW);
}
}
}
void loadmacro() {
int pointer=0;
int last=-1;
int paren=0;
int comment=0;
Serial.println("nEnter the Macro Ending in '!':");
while(pointer<MAX_MACRO) {
last=Serial.read();
if(-1 != last) {
macro[pointer]=last;
Serial.write(last);
pointer++;
if(comment) {
if(10==last || 13==last) { /* Carrige Return / Line Feed */
comment=0;
}
} else {
if ('(' == last) {
paren++;
} else if (')' == last) {
paren--;
if(paren < 0) {
Serial.println("Unmatched ()s");
}
} else if ('#' == last ) {
comment=1;
}
if ('!' == last) {
if(paren) {
Serial.println("Unmatched ()s");
}
break;
}
}
if (0 == last) {
if(paren) {
Serial.println("Unmatched ()s");
}
break;
}
}
}
if(pointer > MAX_MACRO) {
Serial.println("Macro Overrun");
while (1)
;
} else {
while(pointer < MAX_MACRO && macro[pointer]) {
macro[pointer]=0;
pointer++;
}
}
Serial.write("n");
}
void printmacro() {
Serial.println(macro);
}
void grabcomment() { //Moves the execution pointer to the end of the comment.
while(1) {
if(cur_pos<MAX_MACRO && ( 0==macro[cur_pos] || 10==macro[cur_pos] || 13==macro[cur_pos])) {
return;
}
cur_pos++;
}
}
int grabnumber() { //Grabs a number. Checks for 'variable wait' and returns it
int i=0;
char buffer[]={0,0,0,0,0,0};
// Serial.println("grabnumber");
while(i<5 && macro[cur_pos]>47 && macro[cur_pos]<58) {
buffer[i]=macro[cur_pos];
cur_pos++;
i++;
}
if(i>=5) {
Serial.println("No support for oversize numbers");
}
if(0==i) {
buffer[0]='0';
buffer[1]=0;
}
i=atoi(buffer);
if('?'==macro[cur_pos]) {
if(!var_waiting) {
variable_wait=i;
var_waiting=true;
} else {
i=variable_wait;
}
cur_pos++;
}
return(i);
}
void hold () {
// Serial.println("hold");
if(keys[macro[cur_pos]]) {
held=held|(1<<(keys[macro[cur_pos]]-1));
} else {
Serial.println("Ignoring Null Hold");
}
cur_pos++;
}
void release () {
// Serial.println("release");
if(keys[macro[cur_pos]]) {
held=held&(~1<<(keys[macro[cur_pos]]-1));
} else {
Serial.println("Ignoring Null Release");
}
cur_pos++;
}
void startloop() {
// Serial.println("startloop");
loop_level++;
loop_start[loop_level]=cur_pos;
}
void endloop() {
// Serial.println("endloop");
if(loop_level<0) {
Serial.println("Loop underflow");
return;
}
if(0==loop_count[loop_level]) {//First Iteration
loop_count[loop_level]=grabnumber();
} else {
loop_count[loop_level]--;
}
if(loop_count[loop_level]>0) {
cur_pos=loop_start[loop_level];
} else {
loop_count[loop_level]=0; //This should be unnecessary;
loop_level--;
}
}
boolean stepmacro() {
int input=-1;
char curchar;
int number;
boolean done=false;
setpins(next);
next=held;
// Serial.println(micros());
if(Serial.available()) { //Break on user input. -- Maybe sync support later
return false;
} else if(wait>0) { //Multiframe wait.
wait--;
} else {
while(false==done) {
curchar=macro[cur_pos];
cur_pos++;
if (false==firstplayer) {
if('+'==curchar || '>'==curchar || '<'== curchar) { // '+','>' or '<' reset to P1
firstplayer=true;
}
} else {
switch (curchar) {
case '!': //End of Marco
case 0:
if(var_waiting) {
variable_wait++;
Serial.print("Incrementing Variable: ");
Serial.print(variable_wait,DEC);
Serial.write("n");
held=0;
cur_pos=0;
} else {
return(0);
break;
}
case 'W':
case 'w':
wait=grabnumber()-1;
done=true;
break;
case '.':
done=true;
break;
case '<': //Multi-character controls
case '+': //These indicate P1 which should be ignorable.
break;
case '/': //Multi-character controls
case '-': //These indicate non-P1 status, so stop reading
firstplayer=false;
Serial.println("WARNING: No multiplayer synchronization");
break;
case '>': //Ends < / / > block. Should be ignorable.
break;
/*I suppose I should really have separate states for the two different
mult-character command systems and/or the ability to run scripts as
P2-Pn. The </> block timing support stuff is a little annoying. */
case '$': //Save and restore commands. Not supported.
case '&': //But it's relatively easy to handle these better...
Serial.println("Save and Restore are not supported");
grabnumber(); //Skipping the state number.
break;
case '?': //Not sure what ??? does. The variable wait should
//Should be handled by grabnumber();
Serial.println("??? command is not supported.");
break;
case '#':
grabcomment();
break;
case '_':
hold();
break;
case '^':
release();
break;
case '*':
held=0;
break;
case '(':
startloop();
break;
case ')':
endloop();
break;
default :
if(curchar<128 and keys[curchar]) {
next=next|int(1<<(keys[curchar]-1));
}
}
}
}
next=(held|next);
}
return true;
}
void runmacro() {
int i;
unsigned long NextFrame;
unsigned long FractionalTicks;
boolean wrap=false;
// Serial.println("runmacro");
cur_pos=0;
var_waiting=false;
variable_wait=0;
loop_level=0;
FractionalTicks=0;
held=0;
next=0;
for(i=0;i<8;i++) {
loop_start[i]=0;
loop_count[i]=0;
}
/* Set Pins to output */
for(i=0;i<16;i++) {
pinMode(i+2,OUTPUT); /* Pins 2-13, A0-A3 */
digitalWrite(i+2,HIGH);
}
NextFrame=micros()+frame_ticks;
do {
NextFrame+=frame_ticks;
FractionalTicks+=frame_ticks_num;
if(FractionalTicks>frame_ticks_den) {
NextFrame++;
FractionalTicks-=frame_ticks_den;
}
// Serial.print("Next Frame:");
// Serial.println(NextFrame);
// Serial.print("Fractional Ticks:");
// Serial.println(FractionalTicks);
if(NextFrame<=frame_ticks) {
wrap=true;
} else {
wrap=false;
}
// Serial.println(NextFrame);
while(wrap && micros()>=frame_ticks)
;
while(micros()<NextFrame)
;
// Serial.println(micros());
} while (stepmacro());
/* Set pins to input */
for(i=0;i<16;i++) {
pinMode(i+2,INPUT); /* Pins 2-13, A0-A3 */
}
}
void setup () {
int i;
Serial.begin(115200);
for(i=0;i<MAX_MACRO;i++) {
macro[i]=0;
}
/* Key (Pin=Button) */
/* 16=r, 15=l, 14=u, 13=d */
/* 12=back, 11=start, 10=RT, 9=LT */
/* 8=RB, 7=LB, 6=Y, 5=X */
/* 4=B, 3=A */
keys['r']=16;
keys['R']=16;
keys['l']=15;
keys['L']=15;
keys['u']=14;
keys['U']=14;
keys['D']=13;
keys['d']=13;
keys['1']=5;
keys['2']=6;
keys['3']=8;
keys['4']=7;
keys['5']=3;
keys['6']=4;
keys['7']=10;
keys['8']=9;
keys['s']=11;
keys['S']=11;
keys['b']=12;
keys['B']=12;
}
long serialreadnum() {
char buffer[]={0,0,0,0,0,0,0,0,0,0};
int i=0;
long input;
do {
input=Serial.peek();
if(47 < input && 58 > input && i<10) {
buffer[i]=input;
i++;
Serial.read();
Serial.write(input);
}
} while (-1==input || (47<input && 58 > input && i<10));
if(0<i && i<10) {
return (atol(buffer));
}
Serial.println("Problem Reading Serial Input Number");
return 0;
}
void loop () {
int cmd;
long number;
Serial.println("");
Serial.print("Ticks per frame: ");
Serial.print(frame_ticks);
Serial.print(" and ");
Serial.print(frame_ticks_num);
Serial.print("/");
Serial.println(frame_ticks_den);
Serial.println("(l)oad,(p)rint,(r)un,(f)rame ticks,(n)umerator,(d)enominator");
while(0==Serial.available())
;
if(Serial.available()) {
cmd=Serial.read();
switch (cmd) {
case 'l':
case 'L':
loadmacro();
break;
case 'p':
case 'P':
printmacro();
break;
case 'r':
case 'R':
runmacro();
break;
case 'f':
case 'F':
Serial.print
ln("Enter frame tick count:");
number=serialreadnum();
if(number) {
frame_ticks=number;
}
break;
case 'n':
case 'N':
Serial.println("Enter frame tick count fractional numerator");
number=serialreadnum();
if(number) {
frame_ticks_num=number;
}
break;
case 'd':
case 'D':
Serial.println("Enter frame tick count fractional denominator");
number=serialreadnum();
if(number) {
frame_ticks_den=number;
}
break;
default :
Serial.println("Unknown Command");
}
}
}
Multiple macro support.
Multi-player flow control & Better handling of control structure interactions.
Slaving Arduinos for multi-controller support.
Input recording.
General cleanliness of the code.
fix apparent loop bug