How I made my programmable sticks

reference materials and general how-to information
Rufus
Posts: 647
Joined: Thu Apr 01, 2010 3:12 am

Re: How I made my programmable sticks

Post by Rufus »

Improved timing, and added frame timing adjustment at the console.
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");
      }
   }
}
To Do:
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
noodalls
Posts: 75
Joined: Sun Jul 18, 2010 11:33 am

Re: How I made my programmable sticks

Post by noodalls »

Dang. Tried this today, but doesn't seem to work with the hold duemilanove. Nothing seems to come up on the serial screen despite uploading. Tried it on my mega, which got the serial part working, but can't get in easily to swap wires around. Will have another go this weekend, perhaps with another mega.
Rufus
Posts: 647
Joined: Thu Apr 01, 2010 3:12 am

Re: How I made my programmable sticks

Post by Rufus »

noodalls wrote:Dang. Tried this today, but doesn't seem to work with the hold duemilanove. Nothing seems to come up on the serial screen despite uploading. Tried it on my mega, which got the serial part working, but can't get in easily to swap wires around. Will have another go this weekend, perhaps with another mega.
If it's anolder duemilanove (AtMega168) then the program is going to use too much memory... you could try changing
#define MAX_MACRO 1024
to
#define MAX_MACRO 128
or something like that, and see if that makes it come up.

I did some 'clever' stuff with the pin mapping. I should be able to make some changes s.t. it works based on the code you posted above.
noodalls
Posts: 75
Joined: Sun Jul 18, 2010 11:33 am

Re: How I made my programmable sticks

Post by noodalls »

Tried it with 512, seems to load up on duelaminalove. Off to work.
Rufus
Posts: 647
Joined: Thu Apr 01, 2010 3:12 am

Re: How I made my programmable sticks

Post by Rufus »

noodalls wrote:Dang. Tried this today, but doesn't seem to work with the hold duemilanove. Nothing seems to come up on the serial screen despite uploading. Tried it on my mega, which got the serial part working, but can't get in easily to swap wires around. Will have another go this weekend, perhaps with another mega.
I think it's easier to make it work by putting these changes into place (not sure this will work right)...

Code: Select all

...
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;
char pinmap[]={0,0,39,41,31,33,37,35,45,43,47,51,25,23,27,29};  //THIS LINE IS ADDED

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(pinmap[i],HIGH); //THIS LINE IS MODIFIED
      } else {
        digitalWrite(pinmap[i],LOW); //THIS LINE IS MODIFIED
      }
   }
}

and change the setup function to:

Code: Select all

void setup () {
   int i;
   Serial.begin(115200);
   for(i=0;i<MAX_MACRO;i++) {
      macro[i]=0;
   }

//THE FOLLOWING SECTION IS MODIFIED
   keys['R']=16;
   keys['L']=15;
   keys['U']=14;
   keys['D']=13;
   keys['A']=5;
   keys['B']=6;
   keys['C']=8;
   keys['X']=7;
   keys['Y']=3;
   keys['Z']=4;
   keys['P']=10;
   keys['Q']=9;
   keys['S']=11;
   keys['B']=12;
}

You should be able to test it if you type:
LU..D..L..R..A..B..C..X..Y..Z..P..Q..!R
at the serial prompt (it's case sensitive).
noodalls
Posts: 75
Joined: Sun Jul 18, 2010 11:33 am

Re: How I made my programmable sticks

Post by noodalls »

Tried it and it seems to be working.

Some issues with pressing button B, which looks to be wired right. Could it be because of the confusion with holding back versus button B?

Also modified some parts in the code, mainly where it referred to pins 2-13 to change it to pins 33-55 for my setup.

Otherwise seems to be working well, amazing stuff.
Rufus
Posts: 647
Joined: Thu Apr 01, 2010 3:12 am

Re: How I made my programmable sticks

Post by Rufus »

noodalls wrote:Tried it and it seems to be working.

Some issues with pressing button B, which looks to be wired right. Could it be because of the confusion with holding back versus button B? [qupte]
Yeah...comment out the " keys['B']=12;" line. or pick a different letter for select.
Also modified some parts in the code, mainly where it referred to pins 2-13 to change it to pins 33-55 for my setup.
That should be mostly comments.
Otherwise seems to be working well, amazing stuff.
Good to know...
Rufus
Posts: 647
Joined: Thu Apr 01, 2010 3:12 am

Re: How I made my programmable sticks

Post by Rufus »

Noodals, this should support both of the controllers you've got wired up simultaneously. Capital letters for one, lowercase for the other. Still no "<//>" or "+...-" support though.

Code: Select all

/*
   Copyright 2011 Nathan Hellweg all rights reserved
   Macrolua interepreter for Arduino UNO
   Macrolua by Dammit
*/


#include <avr/pgmspace.h>
#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
//Frame time for SSF4. Will vary by platform...
unsigned int frame_ticks=16678; 
unsigned int frame_ticks_num=26568;
unsigned int frame_ticks_den=30000;
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 long held=0;
unsigned long next=0;
//Pins 0&1 are reserved for Serial Comm, so I can use 0 as null.
//char pins[]={ 5, 6, 7, 8,  9,10,11,12, 13,14,15,16, 17,18, 0, 0,
//              0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0};
//My pin assignments Key (Arduino Pin=Button)
//18=r,17=l,16=u,15=d,14=back,13=start,12=RT,11=LT,10=RB,9=LB,8=Y,7=X,6=B,5=A
// A, B, X, Y, LB,RB,LT,RT, ST,BA, D, U,  L, R

//For Noodals' setup
//// Y, Z, A, B,  X, C, Q, P, ST,SL, D, U, L, R,
char pins[]={39,41,31,33, 37,35,45,43, 47,51,25,23, 27,29, 0, 0,
             38,40,30,32, 36,34,44,42, 46,50,24,22, 26,28, 0, 0};
//Noodals' pin assignments
//U=23;D=25;L=27;R=29;A=31;B=33;C=35;X=37;Y=39;Z=41;P=43;Q=45;HOM=49;STA=47;SEL=51;
//u=22;d=24;l=26;r=28;a=30;b=32;c=34;x=36;y=38;z=40;p=42;q=44;hom=48;sta=46;sel=50;


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);
}

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]) {
      cur_pos++;
      if(!var_waiting) {
         variable_wait=i;
         var_waiting=true;
      } else {
         i=variable_wait;
      }
   }
   return(i);
}

boolean stepmacro() {
   int input=-1;
   char curchar;
   int number;
   boolean done=false;
   int i,j;

//Set Pins.

   for(i=0;i<32;i++) {
      if(pins[i]) {
         j=next&(1<<i);
         if(0==j) {
           digitalWrite(pins[i],HIGH);
         } else {
           digitalWrite(pins[i],LOW);
         }
      }
   }

   next=held;
   
   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.print(cur_pos,DEC);
                  Serial.println(" ??? command is not supported.");
                  break;
               case '#': //Start Comment
                  while(cur_pos<MAX_MACRO && 0!=macro[cur_pos] && 10!=macro[cur_pos] && 13!=macro[cur_pos]) {
                        cur_pos++;                   
                  }
                  break;
               case '_': //Hold
                  if(keys[macro[cur_pos]]) {
                     held=held|(1<<(keys[macro[cur_pos]]-1));
                  } else {
                     Serial.println(" Ignoring Null Hold");
                  }
                  cur_pos++;
                  break;
               case '^': //Release
                  if(keys[macro[cur_pos]]) {
                     held=held&(~1<<(keys[macro[cur_pos]]-1));
                  } else {
                     Serial.println("Ignoring Null Release");
                  }
                  cur_pos++;
                  break;
               case '*': //Release All
                  held=0;
                  break;
               case '(': //Start Loop
                  loop_level++;
                  loop_start[loop_level]=cur_pos;
                  break;
               case ')': //End Loop
                  if(loop_level<0) {
                     Serial.println("Loop underflow");
                  } else {
                     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;
                        grabnumber(); //ignore loop count.
                        loop_level--;
                     }
                  }
                  break;
               default : //Single Frame Input
                  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<32;i++) {
      if(pins[i]) {
         pinMode(pins[i],OUTPUT);
         digitalWrite(pins[i],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<32;i++) {
      if(pins[i]) {
         pinMode(pins[i],INPUT);
      }
   }
}

void setup () {
   int i;
   Serial.begin(115200);
   for(i=0;i<MAX_MACRO;i++) {
      macro[i]=0;
   }
   /* Key (Arduino Pin=Button) */
   /* 18=r, 17=l, 16=u, 15=d */
   /* 14=back, 13=start, 12=RT, 11=LT */
   /* 10=RB, 9=LB, 8=Y, 7=X */
   /* 6=B, 5=A */
// My buttons...   
// A, B, X, Y, LB,RB,LT,RT, ST,BA, D, U,  L, R
/*
   keys['5']=1;
   keys['6']=2;
   keys['1']=3;
   keys['2']=4;
   keys['4']=5;
   keys['3']=6;
   keys['8']=7;
   keys['7']=7;
   keys['s']=9;
   keys['S']=9;
   keys['b']=10;
   keys['B']=10;  
   keys['D']=11;
   keys['d']=11;
   keys['u']=12;
   keys['U']=12;
   keys['l']=13;
   keys['L']=13;
   keys['r']=14;
   keys['R']=14;
*/
// Noodals' buttons
// Y, Z, A, B,  X, C, Q, P, ST,SL, D, U, L, R,
   keys['Y']=1;
   keys['Z']=2;
   keys['A']=3;
   keys['B']=4;
   keys['X']=5;
   keys['C']=6;
   keys['Q']=7;
   keys['P']=8;
   keys['S']=9; //S for start
   keys['E']=10; //E for select
   keys['D']=11;
   keys['U']=12;
   keys['L']=13;
   keys['R']=14;
   keys['y']=17;
   keys['z']=18;
   keys['a']=19;
   keys['b']=20;
   keys['x']=21;
   keys['c']=22;
   keys['q']=23;
   keys['p']=24;
   keys['s']=25; //s for start
   keys['e']=26; //e for select
   keys['d']=27;
   keys['u']=28;
   keys['l']=29;
   keys['r']=30;
}

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.println("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");
      }
   }
}
noodalls
Posts: 75
Joined: Sun Jul 18, 2010 11:33 am

Re: How I made my programmable sticks

Post by noodalls »

http://www.youtube.com/watch?v=KSWUrTEcbnw

Tried something with the setup last night. It seems to work very well, only one thing I found (and this may be me being unfamiliar with macrolua) is that something like DB.* doesn't work the way I'd expect. It seems to do nothing, as opposed to down+B then release on the next frame.

Otherwise great.
Rufus
Posts: 647
Joined: Thu Apr 01, 2010 3:12 am

Re: How I made my programmable sticks

Post by Rufus »

noodalls wrote:http://www.youtube.com/watch?v=KSWUrTEcbnw

Tried something with the setup last night. It seems to work very well, only one thing I found (and this may be me being unfamiliar with macrolua) is that something like DB.* doesn't work the way I'd expect. It seems to do nothing, as opposed to down+B then release on the next frame.

Otherwise great.
I'm still running down bugs. Is that at the end of the script, or in the middle?
CPS2
Posts: 842
Joined: Mon Nov 09, 2009 11:53 pm

Re: How I made my programmable sticks

Post by CPS2 »

Great stuff guys, I was a bit intimidated by the wiring in the c-stamp to be honest but with the arduino it looks like something i can put together pretty easily, and getting framemame/macrolua style scripts to run is awesome.
Rufus
Posts: 647
Joined: Thu Apr 01, 2010 3:12 am

Re: How I made my programmable sticks

Post by Rufus »

Noodals, also, what is the physical layout for X Y Z , A B C, P and Q?
noodalls
Posts: 75
Joined: Sun Jul 18, 2010 11:33 am

Re: How I made my programmable sticks

Post by noodalls »

P Q


ABC
XYZ

sorry mate, not at home these last few days. Also looks like I might have fried one of the cthulhus, which I'll have to look into when I can. Bad timing indeed.
Rufus
Posts: 647
Joined: Thu Apr 01, 2010 3:12 am

Re: How I made my programmable sticks

Post by Rufus »

noodalls wrote: sorry mate, not at home these last few days. Also looks like I might have fried one of the cthulhus, which I'll have to look into when I can. Bad timing indeed.

No worries. Now that I've got a simple working version, I'm going back and breaking it in the name of 'making it better'....
Rufus
Posts: 647
Joined: Thu Apr 01, 2010 3:12 am

Re: How I made my programmable sticks

Post by Rufus »

Here are some images I'm trying to put together for instructions on how to calibrate the frame rate:
Illustrating how the test works:
Image
Sample of what having the controller frame being too long looks like - fierce to strong transition.
Image
Sample of what having the controller frame being too long looks like - strong to fierce transition.
Image
Demonstration that this does work.
Image
Rufus
Posts: 647
Joined: Thu Apr 01, 2010 3:12 am

Re: How I made my programmable sticks

Post by Rufus »

New version of the code... time to think about what the < / / > and + and - should do.

Code: Select all

/*
   Copyright 2011 Nathan Hellweg all rights reserved
   Macrolua interepreter for Arduino UNO
   Macrolua by Dammit
*/


#include <avr/pgmspace.h>
#define MAX_MACRO 256

char macro[MAX_MACRO]; /* Macro Storage */
int macro_start[10]={-1,-1,-1,-1,-1, -1,-1,-1,-1,-1}; /* Start Locations */
int next_start=0; /* Pointer to free storage */

/*
   Character map for interpretation 
   Hardcoding this to progmem could save space,
   but makes things less readable.
*/

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 */
#define MAX_THREADS 3
int start[0]; //Macro start pointer
int cur_pos[3]; //Macro Execution Pointer 0 default, 1&2 are for threads

/*Frame time.  Defaulting to nominal 59.94 Hz. */
unsigned long frame_ticks=16683;
unsigned long frame_ticks_num=22938;
unsigned long frame_ticks_den=65535;

boolean var_waiting=false; //Whether there is a wait loop
int variable_wait=0; //Wait loop value
int wait=0;

unsigned long NextFrame;
unsigned long FractionalTicks;

#define MAX_LOOPS 4
int loop_level[3]; //Repeat stack pointer
int loop_start[3][4];         //Repeat block start list
int loop_count[3][4];         //Repeat count

unsigned long held=0; //This may neet per-thread...

//Pins 0&1 are reserved for Serial Comm, so I can use 0 as null.
char pins[]={ 6, 7, 9, 8,  4, 5,11,10, 12,13,14,15, 16,17, 0, 0,
              0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0};
// On the controller this works out to
// X, Y,RB,LB, A, B,RT,LT, St,Ba, D, U,  L, R

//For Noodals' setup
//// Y, Z, A, B,  X, C, Q, P, ST,SL, D, U, L, R,
//char pins[]={39,41,31,33, 37,35,45,43, 47,51,25,23, 27,29, 0, 0,
//             38,40,30,32, 36,34,44,42, 46,50,24,22, 26,28, 0, 0};
//Noodals pin assignments
//U=23;D=25;L=27;R=29;A=31;B=33;C=35;X=37;Y=39;Z=41;P=43;Q=45;HOM=49;STA=47;SEL=51;
//u=22;d=24;l=26;r=28;a=30;b=32;c=34;x=36;y=38;z=40;p=42;q=44;hom=48;sta=46;sel=50;


void loadmacro(int macro_id) {
   int pointer=0;
   int last=-1;
   int paren=0;
   int comment=0;
   
   if(macro_id<0 or macro_id>9) {
      Serial.println("Macro id must be 0-9.  Load failed.");
      return;
   }
   if(macro_start[macro_id]>=0) {
      Serial.println("You must erase first.  Load failed.");
      return;
   }
   if(next_start>=MAX_MACRO) {
      Serial.println("Out of Storage.  Load failed.");
      return;
   }
   Serial.print("You have ");
   Serial.print(MAX_MACRO-next_start);
   Serial.print(" of ");
   Serial.print(MAX_MACRO);
   Serial.print(" chararcters remaining");
 
   macro_start[macro_id]=next_start; 
   pointer=next_start;
 
   Serial.println("nEnter the Macro Ending in '!':");
   while(pointer<MAX_MACRO) {
      last=Serial.read();
      if(-1 != last) {
         macro[pointer]=last;
         Serial.write(last);
         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");
               }
               macro[pointer]=0; //Use null instead of '!' to mark end of macro
               break;
            }
         }
         if (0 == last) {
            if(paren) {
               Serial.println("Unmatched ()s");
            }
            break;
         }
         pointer++;
      }
   }
   Serial.write("n");
   pointer++;
   next_start=pointer;
   if(pointer > MAX_MACRO) {
      Serial.println("Macro clipped at end of memory.");
   }
}

void printmacro(int macro_id) {
   int pointer;

   if(macro_start[macro_id]<0) {
      Serial.println("Macro not found");
      return;
   }
   pointer=macro_start[macro_id];
   while(macro[pointer]) {
   Serial.write(macro[pointer]);
      pointer++;
   }
   Serial.println("!");
}

void erasemacro(int macro_id) {
   int i,pointer,delta;

   if(macro_start[macro_id]<0) {
      Serial.println("Macro not found");
      return;
   }
   pointer=macro_start[macro_id];
   for(pointer=macro_start[macro_id];pointer<MAX_MACRO && macro[pointer];pointer++) {
      macro[pointer]=0; //This should be unnecessary...
   }
   for(delta=pointer-macro_start[macro_id];pointer<MAX_MACRO;pointer++) {
      macro[pointer-delta]=macro[pointer];
   }
   for(pointer-=delta;pointer<MAX_MACRO;pointer++) {
      macro[pointer]=0;
   }
   for(i=0;i<10;i++) {
      if(macro_start[i]>macro_start[macro_id])
         macro_start[i]-=delta;
   }
   next_start-=delta;
   macro_start[macro_id]=-1;
   Serial.print("Macro ");
   Serial.print(macro_id);
   Serial.println(" erased.");
}

//Grabs a number.  Checks for 'variable wait' and returns it
int grabnumber(int *ppoint) {
   int i=0;
   char buffer[]={0,0,0,0,0,0};

//   Serial.println("grabnumber");

   for(i=0;i<5 && *ppoint<MAX_MACRO && macro[*ppoint]>47 && macro[*ppoint]<58;i++) {
      buffer[i]=macro[*ppoint];
      (*ppoint)++;
   }
   if(i>=5) {
     Serial.println("No support for oversize numbers");
   }
   if(0==i) {
      buffer[0]='0';
      buffer[1]=0;
   }
   i=atoi(buffer);
   if('?'==macro[*ppoint]) {
      (*ppoint)++;
      if(!var_waiting) {
         variable_wait=i;
         var_waiting=true;
      } else {
         i=variable_wait;
      }
   }
   Serial.print("Grabnumber: ");
   Serial.println(i);
   return(i);
}

void setpins(unsigned long downpins) {
   int i;

   for(i=0;i<32;i++) {
      if(pins[i]) {
         if(downpins&(1<<i)) {
           digitalWrite(pins[i],LOW);
         } else {
           digitalWrite(pins[i],HIGH);
         }
      }
   }
//   Serial.println(downpins|1073741824,BIN);
}

//macro_pos is the execution pointer.
//thread is the thread ID (for use with loops)
//start is the block start position.
//next is a bitfield pointer.

boolean stepmacro(int *macro_pos, unsigned long *next, int start, int thread_id) {
   char curchar;
   boolean done=false;
//   int i;

/*
   Serial.println("Stepmacro");
   Serial.print("Macro Position ");
   Serial.println(*macro_pos);
   Serial.print("Start ");
   Serial.println(start);
   Serial.print("Thread ID");
   Serial.println(thread_id);
*/
   
   if(Serial.available()) { //Break on user input. -- Maybe sync support later
      Serial.println("Broke on user input");
      return false;
   } else if(wait>0) { //Multiframe wait.
      wait--;
   } else  {
      while(false==done) {
         curchar=macro[*macro_pos];
//         Serial.print("*macro_pos=");
//         Serial.println(*macro_pos);
         (*macro_pos)++;
//         Serial.print("curchar=");
//         Serial.println(curchar);
         switch (curchar) {
            case '!': //End of Marco - this should no longer happen.
            case 0:
               Serial.println("End of Macro");
               return(0);
               break;
            case 'W':
            case 'w':
               wait=grabnumber(macro_pos)-1;
               done=true;
               break;
            case '.':
               done=true;
               break;
            case '+': //These P1/Pn setings do not require threading
            case '-': //so they are ignorable.  I'm not going to bother
                      //with B/F support.
               break;
            case '<': //Multi-character controls
               /* FIXME: This needs to be implemented */
               /* startthreads(*macro_pos) */
               /* break; */
               Serial.println("WARNING: No threading support.  Unexpected behavior ahead.");
            case '/': //Multi-character controls
            case '>': //Ends < / / > block.  Should be ignorable.
               /* FIXME: These are end-of-thread when threading works*/
               /* if(endthread(thread_id)) { */
               /*    done=true;  */
               /* } */
               Serial.println("WARNING: No threading support.  Unexpected behavior behind?");
               break;
            case '$': //Save and restore commands.  Not supported.
            case '&': 
               Serial.println("Save and Restore are not supported");
               grabnumber(macro_pos); //Skipping the state number.
               break;
            case '?': //Not sure what ??? does.  The variable wait ?
                      //Should be handled by grabnumber and never show up
               Serial.print(*macro_pos,DEC);
               Serial.println("??? command is not supported.");
               break;
            case '#': //Start Comment
               while(*macro_pos<MAX_MACRO && 0!=macro[*macro_pos] && 10!=macro[*macro_pos] && 13!=macro[*macro_pos]) {
                     (*macro_pos)++;
               }
               break;
            case '_': //Hold
               if(keys[macro[*macro_pos]]) {
                  held=held|(1<<(keys[macro[*macro_pos]]-1));
               } else {
                  Serial.println(" Ignoring Null Hold");
               }
               (*macro_pos)++;
               break;
            case '^': //Release
               if(keys[macro[*macro_pos]]) {
                  held=held&(~1<<(keys[macro[*macro_pos]]-1));
               } else {
                  Serial.println("Ignoring Null Release");
               }
               (*macro_pos)++;
               break;
            case '*': //Release All
               held=0;
               break;
            case '(': //Start Loop
               loop_level[thread_id]++;
               if(loop_level[thread_id]<MAX_LOOPS) {
                  loop_start[thread_id][loop_level[thread_id]]=*macro_pos;
               } else {
                  loop_level[thread_id]--;
                  Serial.println("Loop overflow -- undeflow ahead");
               }
               break;
            case ')': //End Loop
               if(loop_level[thread_id]<0) {
                  Serial.println("Loop underflow");
               } else {
                  if(0==loop_count[thread_id][loop_level[thread_id]]) {//First Iteration
                     loop_count[thread_id][loop_level[thread_id]]=grabnumber(macro_pos);
                  } else {
                     loop_count[thread_id][loop_level[thread_id]]--;
                  }
                  if(loop_count[thread_id][loop_level[thread_id]]>0) {
                     *macro_pos=loop_start[thread_id][loop_level[thread_id]];
                  } else {
                     loop_count[thread_id][loop_level[thread_id]]=0; //This should be unnecessary;
                     grabnumber(macro_pos); //ignore loop count.
                     loop_level[thread_id]--;
                  }
               }
               break;
            default : //Single Frame Input
               if(curchar<128 and keys[curchar]) {
//                  Serial.println(*next,BIN);
                  *next=*next|(1<<(keys[curchar]-1));
//                  Serial.println(keys[curchar],DEC);
//                  Serial.println(pins[keys[curchar]-1],DEC);
//                  Serial.println(*next,BIN);
               }
         }
      }
   }
   return true;
}

//Stub function for now.  Deals with setting up 'threaded' macro execution
void startthreads() {
}

//Stub function for now.  Deals with ending the current thread.
void endthread() {
}

void runmacro(int macro_id) {
   int i,j;
   unsigned long FractionalTicks;
   unsigned long next=0;

//   Serial.println("runmacro");

   if(macro_start[macro_id]<0) {
      Serial.println("Macro ");
      Serial.println(macro_id);
      Serial.println(" not found");
      return;
   }
   
//   Serial.println("runmacro1");
//Reset execution globals.
   cur_pos[0]=macro_start[macro_id];
   start[0]=macro_start[macro_id];
   for(i=1;i<MAX_THREADS;i++) {
      cur_pos[i]=-1;
      start[i]=-1;
   }
   for(i=0;i<MAX_THREADS;i++) {
      loop_level[i]=-1;
      for(j=0;j<4;j++) { //Probably unnecessary
         loop_start[i][j]=0;
         loop_count[i][j]=0;
      }
   }
   var_waiting=false;
   variable_wait=0;
//   held=0;
//   Serial.println("runmacro2");

/* Set Pins to output */
   for(i=0;i<32;i++) {
      if(pins[i]) {
         pinMode(pins[i],OUTPUT);
         digitalWrite(pins[i],HIGH);
      }
   }
   setpins(next|held);
   NextFrame=micros();
   do {
      NextFrame+=frame_ticks;
      FractionalTicks+=frame_ticks_num;
      if(FractionalTicks>frame_ticks_den) {
         NextFrame+=int(FractionalTicks/frame_ticks_den);
         FractionalTicks%=frame_ticks_den;
      }
//      Serial.print("Next Frame:");
//      Serial.println(NextFrame);
//      Serial.print("Fractional Ticks:");
//      Serial.println(FractionalTicks);
      if(NextFrame<=frame_ticks) {
         while(micros()>=4294967295-frame_ticks)
            ;
      }
      while(micros()<NextFrame)
         ;
      setpins(next|held);
      next=0;
   } while (stepmacro(cur_pos,&next,start[0],0));
//Two more frames - one for the last input, one for last stepmacro, and
//one to clear.  There's bound to be an elegant way to handle this, but at
//least this is semi-readable.
   for(i=0;i<2;i++) {
      NextFrame+=frame_ticks;
      FractionalTicks+=frame_ticks_num;
      if(FractionalTicks>frame_ticks_den) {
         NextFrame+=int(FractionalTicks/frame_ticks_den);
         FractionalTicks%=frame_ticks_den;
      }
      if(NextFrame<=frame_ticks) {
         while(micros()>=4294967295-frame_ticks)
            ;
      }
      while(micros()<NextFrame)
         ;
      setpins(next|held);
      next=0;
   }

/* Set pins to input */
   for(i=0;i<32;i++) {
      if(pins[i]) {
         pinMode(pins[i],INPUT);
      }
   }
}

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) {
    Serial.read();
    return (atol(buffer));
  }
  Serial.println("Problem Reading Serial Input Number");
  return -1;
}

void setup () {
   int i;
   Serial.begin(115200);
   for(i=0;i<MAX_MACRO;i++) {
      macro[i]=0;
   }

// My buttons...
// A, B, X, Y, LB,RB,LT,RT, ST,BA, D, U,  L, R
   keys['1']=1;
   keys['2']=2;
   keys['3']=3;
   keys['4']=4;
   keys['5']=5;
   keys['6']=6;
   keys['7']=7;
   keys['8']=8;
   keys['s']=9;
   keys['S']=9;
   keys['b']=10;
   keys['B']=10;
   keys['d']=11;
   keys['D']=11;
   keys['u']=12;
   keys['U']=12;
   keys['l']=13;
   keys['L']=13;
   keys['r']=14;
   keys['R']=14;

// Noodals' buttons
// Y, Z, A, B,  X, C, Q, P, ST,SL, D, U, L, R,
/*
   keys['Y']=1;
   keys['Z']=2;
   keys['A']=3;
   keys['B']=4;
   keys['X']=5;
   keys['C']=6;
   keys['Q']=7;
   keys['P']=8;
   keys['S']=9; //S for start
   keys['E']=10; //E for select
   keys['D']=11;
   keys['U']=12;
   keys['L']=13;
   keys['R']=14;
   keys['y']=17;
   keys['z']=18;
   keys['a']=19;
   keys['b']=20;
   keys['x']=21;
   keys['c']=22;
   keys['q']=23;
   keys['p']=24;
   keys['s']=25; //s for start
   keys['e']=26; //e for select
   keys['d']=27;
   keys['u']=28;
   keys['l']=29;
   keys['r']=30;
*/

   NextFrame=micros()+frame_ticks;
   FractionalTicks=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,(e)rase,(0-9)run macro,(f)rame ticks,(n)umerator,(d)enominator, (t/T) tripple/third frame time");
   while(0==Serial.available()) {
      if(NextFrame<=frame_ticks) {
         while(micros()>4294967295-frame_ticks)
            Serial.println(micros());
      }
      while(micros()>NextFrame) {
         NextFrame+=frame_ticks;
         FractionalTicks+=frame_ticks_num;
         if(FractionalTicks>frame_ticks_den) {
            NextFrame++;
            FractionalTicks-=frame_ticks_den;
         }
      }
   }
   if(Serial.available()) {
      cmd=Serial.read();
      switch (cmd) {
         case 'l':
         case 'L':
           Serial.println("Enter macro id to load:");
           number=serialreadnum();
           if(0<=number && number<10) {
              loadmacro(number);
           } else {
              Serial.println("Invalid macro id");
           }
           break;
         case 'p':
         case 'P':
           Serial.println("Enter macro id to print:");
           number=serialreadnum();
           if(0<=number && number<10) {
              printmacro(number);
           } else {
              Serial.println("Invalid macro id");
           }
           break;
         case '0':
         case '1':
         case '2':
         case '3':
         case '4':
         case '5':
         case '6':
         case '7':
         case '8':
         case '9':
           runmacro(int(cmd-48));
           break;
         case 'e':
         case 'E':
           Serial.println("Enter macro id to erase:");
           number=serialreadnum();
           if(0<=number && number<10) {
              erasemacro(number);
           } else {
              Serial.println("Invalid macro id");
           }
           break;
         case 'f':
         case 'F':
           Serial.println("Enter frame tick count:");
           number=serialreadnum();
           if(number) {
              frame_ticks=number;
           }
           Serial.read();
           break;
         case 'n':
         case 'N':
           Serial.println("Enter frame tick count fractional numerator");
           number=serialreadnum();
           if(number and number<frame_ticks_den) {
              frame_ticks_num=number;
           }
           Serial.read();
           break;
         case 'd':
         case 'D':
           Serial.println("Enter frame tick count fractional denominator");
           number=serialreadnum();
           if(number and number>frame_ticks_num) {
              frame_ticks_den=number;
              FractionalTicks=0;
           }
           Serial.read();
           break;
//FIXME: This gets calibration.  There really should also be
//synchronizaition support.
         case 't'://One third frame time.
           frame_ticks*=3;
           frame_ticks_num*=3;
           frame_ticks+=int(frame_ticks_num/frame_ticks_den);
           frame_ticks_num%=frame_ticks_den;
           Serial.println("Frame Time Trippled");
           break;
         case 'T'://Three times frame time
           frame_ticks_num+=frame_ticks_den*(frame_ticks%3);
           frame_ticks_num/=3;
           frame_ticks/=3;
           Serial.println("Frame Time Thirded");
           break;
         default :
           Serial.println("Unknown Command");
      }
   }
}
noodalls
Posts: 75
Joined: Sun Jul 18, 2010 11:33 am

Re: How I made my programmable sticks

Post by noodalls »

More wows

http://www.youtube.com/watch?v=6O91DLwCozA
Features
- input commands directly via lcd screen
- record inputs (which can also be edited via lcd screen)
- playback
- playback then record to overwrite booboos
- loop
noodalls
Posts: 75
Joined: Sun Jul 18, 2010 11:33 am

Re: How I made my programmable sticks

Post by noodalls »

One thought for testing timing.

Ryu's f+HP d+HP link from what I can see is exactly one second (f+HP on frame 0, d+HP on frame 60, checked with FRAPS and my new setup which seem to agree.) Being a one frame link, if done on F59 it will not come out, and done on F61 it will not combo. Would this provide a convenient way for confirming the internal timing of these sticks? (I checked mine last night aginst this, and it dropped one in sixteen trials). Also helps that SF4 PC shows FPS so you can rule out the game causing timing issues.
Rufus
Posts: 647
Joined: Thu Apr 01, 2010 3:12 am

Re: How I made my programmable sticks

Post by Rufus »

noodalls wrote: Ryu's f+HP d+HP link from what I can see is exactly one second (f+HP on frame 0, d+HP on frame 60, checked with FRAPS and my new setup which seem to agree.) Being a one frame link, if done on F59 it will not come out, and done on F61 it will not combo. Would this provide a convenient way for confirming the internal timing of these sticks?
I'm not sure it's any better than any other one frame link.
Also helps that SF4 PC shows FPS so you can rule out the game causing timing issues.
Because 'frame' can mean 'game step' as well as 'image displayed on screen' that might not be as useful as we would like.
noodalls
Posts: 75
Joined: Sun Jul 18, 2010 11:33 am

Re: How I made my programmable sticks

Post by noodalls »

Programmable stick

When you start up, you will see an LCD screen like this

UDLRABCXYZ 0
1111111111 0

Basically, the arduino stores 90 rows of ten inputs. Each row corresponds to a frame, where row 0 is what happens on frame 0.

For the purposes of this program, 0 is off and 1 is on/pressed.

The keypad has four directions, a button and a reset.

If we press right on the keypad, it will now show.

UDLRABCXYZ 0
1111111111 1

It should blink (but doesn't always), but basically now we're sitting on the D column on frame 0.

If we press up twice, it will show

UDLRABCXYZ 2
1111111111 1

meaning we're looking at D on frame 2.

If we press the button on the LCD now, it will change to

UDLRABCXYZ 2
1011111111 1

Meaning that on frame 2, it will press down.

Let's try that.

Hold the button, and press left on the keypad.

The character on the screen should duck after a very brief pause.

There is another way to program inputs.
Press right until you arrive at position 10.

UDLRABCXYZ 2
1011111111 10

If we press the button now, it will show this

UDLRABCXYZ 2
1111111111 10

So it just removed our input. We could go back to position 2 and re-enter it.
But let's hold down right on the arcade stick, and then press the LCD button again.

It should display

UDLRABCXYZ 2
1110111111 10

We can do as much as we like here, e.g. hold down+right+A+B+C+X+Y+Z on the joystick and then press the button on the LCD.

UDLRABCXYZ 2
1010000000 10

Again, pushing button + left on the LCD will play that back.

It's going to take a long time to program each, frame individually (although accurate) so it might be easier just to record it manually.

If we press button + right, it will prepare to record from frame 0. It actually waits until you do something, so that your first action
appears on frame 0.

If say we input d+MK, fireball here.

If we want to play this back, we just push button + left.

If for example we want to playback just the fireball (later) portion, we can scroll with up to the frame where the d+MK is finished, then
press button + down.

If we want to change it to an ex-fireball, we can go in and either manually edit it, or find the frame whereh the button is pressed and
overwrite it using position ten.

Or we can go to the frame before where the button is pressed, and press button + up. This will playback until the frame we're on, and then record after this.



Two more advanced features.

Let's say we're working on the timing on Cammy's Cannon strike.

We can program qcb+K on F20, and then program uf on F10. If we select F10 and then go to position 11, we can use up and down to move this one frame forwards
and backwards in time.

Or we can do it the other way around.
If we program uf on F0, and the qcb+K on F10, go to position 12 we can move F10 (and all subsequent frames) forwards or backwards.


And one final feature.
Let's say we input D on F0, DR on F1 and R on F2.

We can loop this as follows. The frame selected will be how long it plays before looping.
The position determines how many loops.

So if we're at this
UDLRABCXYZ 2
1110111111 9

It will do qcf 9 times. This is done by holding left and then pressing right.
noodalls
Posts: 75
Joined: Sun Jul 18, 2010 11:33 am

Re: How I made my programmable sticks

Post by noodalls »

This is the code for my current stick. Recording, looping, editing, stuff.

Working on something to cut down the amount of data storage required (sum of squares method, not sure what it's formally called but remember someone trying to explain it to me a few years ago, and now it reappears in my memory).

If anything the formatting and coding is probably worse than ever, haven't had time to do much other than throw in some code, check that it works and then move on.

Code: Select all


#include <LiquidCrystal.h>
#include "TimerOne.h"
int pressed=0;
int playback=0;
int recording=0;
int playback_then_recording=0;
int delay_recording=0;
int repetition_number=0;
int repetition_frame=0;
int repetition_playback=0;
int button=0; //0 for none, 1 for right, 2 for up, 3 for down, 4 for left, 5 for button
int last_button=0;
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
int current_slot = 0;
int cursor_position = 10;
int move_array [360][10];
int i;int j;
int F=16683;int frame_count=0;int frame_advance=0;    
    unsigned long time;
int pins[10]={22,23,24,25,26,27,28,29,30,31};     //**
void setup()
{lcd.begin(16,2);
  lcd.print("RDLUABCXYZ");
  
  Timer1.initialize(F);                                                
  Timer1.pwm(9,900);
  Timer1.attachInterrupt(FrameAdv);
  for(i=0;i<10;i++){pinMode(pins[i],OUTPUT);digitalWrite(pins[i],LOW);}
  
  for (i=0;i<360;i++){for (j=0;j<10;j++){
  move_array[i][j]=0;}}
}
void FrameAdv(){frame_advance=1;}

void loop() {


if (frame_advance==1){


if (analogRead(0)<50){button=6;}
if (analogRead(0)>100 && analogRead(0)<150){button=8;}
if (analogRead(0)>300 && analogRead(0)<350){button=2;}
if (analogRead(0)>450 && analogRead(0)<500){button=4;}
if (analogRead(0)>700 && analogRead(0)<750){button=5;}
if (analogRead(0)>1000){button=0;}

lcd.setCursor(15,0);lcd.print(button);
lcd.setCursor(12,0);lcd.print(current_slot);
lcd.setCursor(12,1);lcd.print(cursor_position);
for (i=0;i<10;i++){lcd.setCursor(i,1);
lcd.print(move_array[current_slot][i]);}

lcd.setCursor(cursor_position,0);
lcd.blink();


if((button==0) && (last_button==5) && (pressed==0) && (current_slot>-1) && (cursor_position >-1) && (cursor_position<10)){
{move_array[current_slot][cursor_position]--;move_array[current_slot][cursor_position]=abs(move_array[current_slot][cursor_position]);}}

if (button==0){pressed=0;}

if ((button==4) && (last_button==5)){pressed=1;frame_count=0;playback=1;}

if ((button==2) && (last_button==5)){pressed=1;frame_count=current_slot;playback=1;}

if ((button==6) && (last_button==5)){for (i=0;i<360;i++){for (j=0;j<10;j++){
  move_array[i][j]=0;}}
pressed=1;frame_count=0;recording=1;}

if ((button==8) && (last_button==5)){for (i=current_slot;i<360;i++){for (j=0;j<10;j++){
  move_array[i][j]=0;}} pressed=1;frame_count=0;playback_then_recording=1;}

if ((button==6) && (last_button==4)){repetition_frame=current_slot;repetition_number=cursor_position;
pressed=1;frame_count=0;repetition_playback=1;}

if ((button==2) && (button!=last_button) && (pressed==0) && (current_slot>0) && (cursor_position<11)){current_slot--;}
if ((button==4) && (button!=last_button) && (pressed==0) && (cursor_position>0)){cursor_position--;}
if ((button==6) && (button!=last_button) && (pressed==0) && (cursor_position<12)){cursor_position++;}
if ((button==8) && (button!=last_button) && (pressed==0) && (current_slot<359) && (cursor_position<11)){current_slot++;}


if ((button==0) && (last_button==5) && (pressed==0) && (cursor_position==10))
{for (i=0;i<10;i++){move_array[current_slot][i]=digitalRead(pins[i]);}}

if ((button==8) && (button!=last_button) && (pressed==0) && (current_slot<359) && (cursor_position==11))
{for (i=0;i<10;i++){move_array[current_slot+1][i]=move_array[current_slot][i];move_array[current_slot][i]=0;
}current_slot++;}

if ((button==8) && (button!=last_button) && (pressed==0) && (current_slot<359) && (cursor_position==12))
{for (j=359;j>current_slot;j--){for (i=0;i<10;i++){move_array[j][i]=move_array[j-1][i];}
}for (i=0;i<10;i++){move_array[current_slot][i]=0;}current_slot++;}


if ((button==2) && (button!=last_button) && (pressed==0) && (current_slot>0) && (cursor_position==11))
{for (i=0;i<10;i++){move_array[current_slot-1][i]=move_array[current_slot][i];move_array[current_slot][i]=0;
}current_slot--;}

if ((button==2) && (button!=last_button) && (pressed==0) && (current_slot<359) && (cursor_position==12))
{for (j=current_slot;j<360;j++){for (i=0;i<10;i++){move_array[j-1][i]=move_array[j][i];}
}current_slot--;}



if(playback_then_recording==1){if (frame_count<current_slot){for (i=0;i<10;i++){digitalWrite(pins[i],move_array[frame_count][i]);}}
if ((frame_count>current_slot-1) && (frame_count < 360)){for (i=0;i<10;i++){move_array[frame_count][i]=digitalRead(pins[i]);}}}

if (repetition_playback==1){
  if (frame_count<360){for (i=0;i<10;i++){digitalWrite(pins[i],move_array[frame_count][i]);}
if ((frame_count==repetition_frame) && (repetition_number>0)){frame_count=-1;repetition_number--;}
}}
  

if(playback==1){
if (frame_count<360){for (i=0;i<10;i++){digitalWrite(pins[i],move_array[frame_count][i]);}}}

if (recording==1){
if(frame_count<360){if (delay_recording==0){frame_count=0;}for (i=0;i<10;i++){move_array[frame_count][i]=digitalRead(pins[i]);if(digitalRead(pins[i])!=0){delay_recording=1;}}}}

if(frame_count>360){for (i=0;i<10;i++){digitalWrite(pins[i],0);playback=0;recording=0;playback_then_recording=0;delay_recording=0;repetition_playback=0;}}

last_button=button;
frame_count++;
frame_advance=0;

}}

noodalls
Posts: 75
Joined: Sun Jul 18, 2010 11:33 am

Re: How I made my programmable sticks

Post by noodalls »

Code: Select all

RNA = 0;
for (i=0;i<10;i++){if (digitalRead(pins[i])==pressed){RNA = (RNA+0.5+pow(2,i));}} 

for(i=0;i<10;i++){protein[i]=unpressed;}

for(i=9;i>-1;i--){if(RNA<(pow(2,i)+0.5)){protein[i]=unpressed;}
                   if(RNA>=(pow(2,i))){protein[i]=pressed;RNA=(RNA-(pow(2,i)));}}

for(i=0;i<10;i++){lcd.setCursor(i,1);lcd.print(protein[i]);}

Basically, this is my code for using the sum of squares to drastically increase the amount that can be saved into 8kb. Previously I got up to 6 seconds (60fps) for 10 buttons. This should allow 60 seconds for 15 buttons in the same space.
noodalls
Posts: 75
Joined: Sun Jul 18, 2010 11:33 am

Re: How I made my programmable sticks

Post by noodalls »

Gah, looks like I should probably learn bitwise. Why do I always find interesting concepts when I really need to be studying something else.
noodalls
Posts: 75
Joined: Sun Jul 18, 2010 11:33 am

Re: How I made my programmable sticks

Post by noodalls »

Did, and my code is noticeably faster.

Code: Select all

RNA = 0;
for (i=0;i<10;i++){if (digitalRead(pins[i])==pressed){RNA = (RNA+ (1 << i));}} //*+100

for(i=0;i<10;i++){protein[i]=unpressed;}

for(i=9;i>-1;i--){if(RNA<(1 << i)){protein[i]=unpressed;}
                   if(RNA>=(1 << i)){protein[i]=pressed;RNA=(RNA-(1 << i));}}

like 14000usec per loop down to 8600 usec.
Rufus
Posts: 647
Joined: Thu Apr 01, 2010 3:12 am

Re: How I made my programmable sticks

Post by Rufus »

[quote="noodalls"]

Code: Select all

RNA = 0;
for (i=0;i<10;i++) {
   if (digitalRead(pins[i])==pressed) {
      RNA = (RNA+ (1 << i));
   }
} //*+100
for(i=0;i<10;i++){
  protein[i]=unpressed;
}
for(i=9;i>-1;i--) {
   if(RNA<(1 << i)) {
      protein[i]=unpressed;
   }
   if(RNA>=(1 << i)) {
      protein[i]=pressed;
      RNA=(RNA-(1 << i));
   }
}
[/code]
How about something like this instead:

Code: Select all

for(i=0;i<10;i++) {
   if(digitalRead(pins[i])==pressed) {
      protein[i]=pressed;
   } else {
      protein[i]=unpressed;
   }
}
What the blazes is this "Sum of squares method" supposed to be?
With 10 buttons, you should have little trouble fitting 4000 frames worth of input - about 60 seconds - into 8kb. Using run length encoding should make that a lot bigger.
Last edited by Rufus on Wed Sep 07, 2011 5:33 am, edited 1 time in total.
noodalls
Posts: 75
Joined: Sun Jul 18, 2010 11:33 am

Re: How I made my programmable sticks

Post by noodalls »

unique bit values I'm told.

The problem I ran into was that it would only let me store my values as variables, when I tried using bits it caused problems displaying them on the LCD (and I'm not convinced that it was working right at all.)

This meant that at most I could get 360F x 10 buttons out of the 8kb, which was pretty limiting.

Using the current method should extend this to 15 buttons x 3600F or so. I managed to put it together this morning and it seems to be working ok (will have to work on timing a bit more, seems to be fluctuating by 0-1F over 60F.
Rufus
Posts: 647
Joined: Thu Apr 01, 2010 3:12 am

Re: How I made my programmable sticks

Post by Rufus »

Does this work on your set-up? I made some minor changes before I tried to clean up the formatting.

Code: Select all

#include <LiquidCrystal.h>
#include "TimerOne.h"

int pressed=0;
int playback=0;
int recording=0;
int playback_then_recording=0;
int delay_recording=0;
int repetition_number=0;
int repetition_frame=0;
int repetition_playback=0;
int button=0; //0 forn one, 1 for right, 2 for up, 3 for down, 4 for left, 5 for button
int last_button=0;
int current_slot = 0;
int cursor_position = 10;

//ADDED BY RUFUS:
char lcd_line[17]="RDLUABCXYZ";

int move_array [360][10];
int i,j;
int F=16683;int frame_count=0;int frame_advance=0;   

LiquidCrystal lcd(8, 9, 4, 5, 6, 7); // Arguments are interface pins.
unsigned long time;
int pins[10]={22,23,24,25,26,27,28,29,30,31};     //**
void setup() {

//Initialize and set the top line of the LCD
   lcd.begin(16,2);
   lcd.print(lcd_line);

//Initialize and set up the timer interrupt
   Timer1.initialize(F);                                               
   Timer1.pwm(9,900);
   Timer1.attachInterrupt(FrameAdv);

//Initialize the output pins. -- This looks like it pushes all the buttons.
   for(i=0;i<10;i++) { 
      pinMode(pins[i],OUTPUT);
      digitalWrite(pins[i],LOW);
   }

//Initialize the move array.
   for(i=0;i<360;i++) {
      for(j=0;j<10;j++) {
         move_array[i][j]=0;
      }
   }
}

//Timer interrupt function.  This sets a global which causes the main loop
//to update button states

void FrameAdv() { 
   frame_advance=1;
}

void loop() {
//Don't do anything unless the frame's advanced.
   if(0==frame_advance) {
     return;
   }

//button is a global, so ifthe analogRead isn't in any of the prescribed
//ranges, it will stay where it was.

   i=analogRead(0)/50;
   if(1>i) { button=6; }
   if(2<i && i<3) {button=8;}
   if(6<i && i<7) {button=2;}
   if(9<i && i<10) {button=4;}
   if(14<i && i>15) {button=5;}
   if(20<i) {button=0;}

   sprintf(lcd_line,"RDLUABCXYZ%3d,%3d",current_slot,button);
   lcd.setCursor(0,0);
   lcd.print(lcd_line);
   sprintf(lcd_line,"            %3d",cursor_position);
   for(i=0;i<10;i++) {
       lcd_line[i]=move_array[current_slot][i]+48;
   }
   lcd.setCursor(0,1);
   lcd_print(lcd_line);
   lcd.setCursor(cursor_position,0);
   lcd.blink();


   if(0==button) {
      if(5==last_button && 0==pressed && -1<current_slot && -1<cursor_position && 10<cursor_position) {
//You seem to be using 1 & 0 so bitwise Xor with 1 should work.
        move_array[current_slot][cursor_position]^=1;
      }
      pressed==0;
   }
   if(5==last_button) {
     pressed=1;
     if(4==button) {
        frame_count=0;
        playback=1;
     } else if(2==button) {
        frame_count=current_slot;
        playback=1;
     } else if(6==button) {
        for(i=0;i<360;i++) {
           for(j=0;j<10;j++) {
              move_array[i][j]=0;
           }
        }
        frame_count=0;
        recording=1; 
     } else if(8==button) {
        for(i=0;i<360;i++) {
           for(j=0;j<10;j++) {
              move_array[i][j]=0;
           }
        }
        frame_count=0;
        playback_then_recording=1; 
     }
   }
   if(4==last_button && 6==button) {
      repetition_frame=current_slot;
      repetition_number=cursor_position;
      pressed=1;
      frame_count=0;
      repetition_playback=1; 
   }
   if(button!=last_button && 0==pressed) {
      if((button==2) && (current_slot>0) && (cursor_position<11)) {
         current_slot--;
      }
      if((button==4) && (cursor_position>0)) {
         cursor_position--;
      }
      if((button==6) && (cursor_position<12)) {
         cursor_position++;
      }
      if((button==8) && (current_slot<359) && (cursor_position<11)) {
         current_slot++;
      }
   }

   if((button==0) && (last_button==5) && (pressed==0) && (cursor_position==10)) {
      for(i=0;i<10;i++) {
         move_array[current_slot][i]=digitalRead(pins[i]);
      }
   }
   if((button==8) && (button!=last_button) && (pressed==0) && (current_slot<359) && (cursor_position==11)) {
      for(i=0;i<10;i++) {
         move_array[current_slot+1][i]=move_array[current_slot][i];move_array[current_slot][i]=0;
      }
      current_slot++;
   }
   if((button==8) && (button!=last_button) && (pressed==0) && (current_slot<359) && (cursor_position==12)) {
      for(j=359;j>current_slot;j--) {
         for(i=0;i<10;i++) {
            move_array[j][i]=move_array[j-1][i];
         }
      }
      for(i=0;i<10;i++) {
         move_array[current_slot][i]=0;
      }
      current_slot++;
   }

   if((button==2) && (button!=last_button) && (pressed==0) && (current_slot>0) && (cursor_position==11)) {
      for(i=0;i<10;i++) {
         move_array[current_slot-1][i]=move_array[current_slot][i];
         move_array[current_slot][i]=0;
      }
      current_slot--;
   }

   if((button==2) && (button!=last_button) && (pressed==0) && (current_slot<359) && (cursor_position==12)) {
      for(j=current_slot;j<360;j++ ) {
         for(i=0;i<10;i++) {
            move_array[j-1][i]=move_array[j][i];
         }
      }
      current_slot--;
   }



   if(playback_then_recording==1){
      if(frame_count<current_slot) {
         for(i=0;i<10;i++) {
            digitalWrite(pins[i],move_array[frame_count][i]);
         }
      }
      if((frame_count>current_slot-1) && (frame_count < 360)) {
         for(i=0;i<10;i++) {
            move_array[frame_count][i]=digitalRead(pins[i]);
         }
      }
   }

   if(repetition_playback==1) {
      if(frame_count<360) {
         for(i=0;i<10;i++) {
            digitalWrite(pins[i],move_array[frame_count][i]);
         }
         if((frame_count==repetition_frame) && (repetition_number>0)) {
            frame_count=-1;repetition_number--;
         }
      }
   }
   if(playback==1) {
      if(frame_count<360) {
         for(i=0;i<10;i++) {
            digitalWrite(pins[i],move_array[frame_count][i]);
         }
      }
   }

   if(recording==1) {
      if(frame_count<360) {
         if(delay_recording==0) {
            frame_count=0;
         }
         for(i=0;i<10;i++) {
            move_array[frame_count][i]=digitalRead(pins[i]);
            if(digitalRead(pins[i])!=0) {
               delay_recording=1;
            }
         }
      }
   }

   if(frame_count>360) {
      for(i=0;i<10;i++) {
         digitalWrite(pins[i],0);
         playback=0;
         recording=0;
         playback_then_recording=0;
         delay_recording=0;
         repetition_playback=0;
      }
   }

   last_button=button;
   frame_count++;
   frame_advance=0;
}
noodalls
Posts: 75
Joined: Sun Jul 18, 2010 11:33 am

Re: How I made my programmable sticks

Post by noodalls »

Thanks for that, will try it out soon.
noodalls
Posts: 75
Joined: Sun Jul 18, 2010 11:33 am

Re: How I made my programmable sticks

Post by noodalls »

Did have a look, it appears to boot up, but unable to move left/right up/down. Right button records current input (as per position ten).

The reason why I took a while to look at this is I've been beavering away on a rewrite for the code, which lead to my testing of the frequency for the Hori FS3 wireless.

The code is below.

Code: Select all

#include <LiquidCrystal.h>
int lagged = 0 ;
int time1=0; 
int time2;

int DNA [3600];
int RNA;
int protein [10];
int mode = -1;

int pressed;
int unpressed;
int button=0; //0 for none, 6 for right, 8 for up, 2 for down, 4 for left, 5 for button
int last_button=0;
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
int current_slot = 0;
int cursor_position = 0;

int i;
//int F=16822;
//int F=16683;
int F=33366;
int frame_count=0;    
    unsigned long time;
int pins[10]={22,23,24,25,26,27,28,29,30,31};     //**
void setup()
{lcd.begin(16,2);
  lcd.print("RDLUABCXYZ");
  


  for(i=0;i<10;i++){pinMode(pins[i],OUTPUT);}
  
  unpressed = digitalRead(pins[0]);
  pressed = abs(unpressed-1);

//for(i=0;i<10;i++){digitalWrite(pins[i],unpressed);}
for(i=0;i<360;i++){DNA[i]=unpressed;}
  

}


void loop() {

  time1=micros();


if(mode==1)
{RNA=DNA[frame_count];
for(i=9;i>-1;i--){if(RNA<(1 << i)){protein[i]=unpressed;}
else
{protein[i]=pressed;RNA=(RNA-(1 << i));}}
for(i=0;i<10;i++){digitalWrite(pins[i],protein[i]);}
if(frame_count>359){mode=-1;}}



if(mode==2)
{RNA = 0;
for (i=0;i<10;i++){if (digitalRead(pins[i])==pressed){RNA = (RNA+ (1 << i));}}
DNA[frame_count]=RNA;
if((RNA==0) && (frame_count==0)){frame_count--;}if(frame_count>359){mode=-1;}
}



if(mode==0)
{RNA = 0;
for (i=0;i<10;i++){if (digitalRead(pins[i])==pressed){RNA = (RNA+ (1 << i));}}
DNA[current_slot]=RNA;
for(i=9;i>-1;i--){lcd.setCursor(i,1);if(RNA<(1 << i)){lcd.print(unpressed);}
                   if(RNA>=(1 << i)){lcd.print(pressed);RNA=(RNA-(1 << i));}}
                   
                   current_slot++;               
               mode=-1;}





if (analogRead(0)<50){button=6;}
if (analogRead(0)>100 && analogRead(0)<150){button=8;}
if (analogRead(0)>300 && analogRead(0)<350){button=2;}
if (analogRead(0)>450 && analogRead(0)<500){button=4;}
if (analogRead(0)>700 && analogRead(0)<750){button=5;}
if (analogRead(0)>1000){button=0;}

if ((button==4) && (cursor_position >0) && (last_button==0)){cursor_position--;mode=-1;}
if ((button==6) && (cursor_position <5) && (last_button==0)){cursor_position++;mode=-1;}
if ((button==8) && (current_slot <360) && (last_button==0 && cursor_position!=5)){
      if (cursor_position==3){DNA[current_slot+1]=DNA[current_slot];DNA[current_slot]=0;}
      if(cursor_position==4){for(i=359;i>=current_slot;i--){DNA[i+1]=DNA[i];}DNA[current_slot]=0;}
    current_slot++;mode=-1;
                    RNA=DNA[current_slot];for(i=9;i>-1;i--){lcd.setCursor(i,1);if(RNA<(1 << i)){lcd.print(unpressed);}
                   if(RNA>=(1 << i)){lcd.print(pressed);RNA=(RNA-(1 << i));}}}
                   if((button==8) && (cursor_position==5)){F++;}
                   
if ((button==2) && (current_slot >0) && (last_button==0) && (cursor_position!=5)){
      if (cursor_position==3){DNA[current_slot-1]=DNA[current_slot];DNA[current_slot]=0;}
      if(cursor_position==4){for(i=current_slot;i<359;i++){DNA[i-1]=DNA[i];}}
  current_slot--;mode=-1;
                    RNA=DNA[current_slot];for(i=9;i>-1;i--){lcd.setCursor(i,1);if(RNA<(1 << i)){lcd.print(unpressed);}
                   if(RNA>=(1 << i)){lcd.print(pressed);RNA=(RNA-(1 << i));}}}

if((button==2) && (cursor_position==5)){F--;}
if((cursor_position==0) && (button==5) && (last_button==0))
{mode=0;}
if((cursor_position==1) && (button==5) && (last_button==0))
{frame_count=-1;mode=1;}
if((cursor_position==2) && (button==5) && (last_button==0))
{for(i=0;i<360;i++){DNA[i]=unpressed;}frame_count=-1;mode=2;}
if((cursor_position==3) && (button==5) && (last_button==0))
{frame_count=-1;mode=1;}
if((cursor_position==4) && (button==5) && (last_button==0))
{frame_count=-1;mode=1;}
if((cursor_position==5) && (button==5) && (last_button==0))
{frame_count=-1;mode=1;}






lcd.setCursor(13,1);lcd.print(current_slot);
lcd.setCursor(10,1);lcd.print(cursor_position);

last_button=button;
frame_count++;

time2=micros();
if ((time2-time1)>(33366)){lagged++;}
lcd.setCursor(3,0);lcd.print(lagged);//*+0
lcd.setCursor(11,0);lcd.print(time2-time1);
//lcd.setCursor(11,0);lcd.print(F);


time2=micros();
delayMicroseconds (33366-(time2-time1));
}




//for(i=0;i<10;i++){lcd.setCursor(i,0);lcd.print(protein[i]);}

//lcd.setCursor(5,1); lcd.print(RNA);
//lcd.setCursor(12,1);lcd.print("p");
//lcd.setCursor(13,1);lcd.print(pressed);
//lcd.setCursor(14,1);lcd.print("u");
//lcd.setCursor(15,1);lcd.print(unpressed);

//lcd.setCursor(10,1);lcd.print(digitalRead(pins[0]));


//lcd.setCursor(15,0);lcd.print(button);

//for (i=0;i<10;i++){lcd.setCursor(i,1);
//lcd.print(move_array[current_slot][i]);}

//lcd.setCursor(cursor_position,0);
//lcd.blink();

//if(mode==1)
//{RNA=DNA[frame_count];
//for(i=9;i>-1;i--){if(RNA<(1 << i)){digitalWrite(pins[i],unpressed);}
//else //if(RNA>=(1 << i))
  //               {digitalWrite(pins[i],pressed);RNA=(RNA-(1 << i));}
    //             }if(frame_count>359){mode=-1;}}
This one basically has 5 positions that can be accessed with left right. Up and down will adjust the frame on which things occur.
0. Input mode, hold down the buttons you want for that frame.
1. Playback mode. Press button to playback
2. Record mode. Press button to record.
3. Copy and paste mode, select a frame then use up and down to alter the timing. Press button to playback.
4. Copy and paste mode, select a frame then use up and down to move it and all subsequent frames. Press button to playback.
5. More of a debug mode, but allows you to alter the timing for F (frame time).
It also displays how long each frame is taking to process (~2000usec when no LCD prinint is required) and how many dropped frames have occurred.

Also detects what it's connected to (as FS3 0 is unpressed and 1 is pressed, HRAP 3 1 is unpressed and 0 is pressed.) Not fully implemented yet.

Will need to go back and tidy up sketch. $4k exam in a week, so this is my fleeting escapism from study.
Post Reply