How to sequence or switch case automatically?

Hi,
I made a lot of cases to trigger them with the keyboard. Now, I wish I could sequence the cases in a special order and manage their duration.

For example with the Arduino IDE, I made case ‘0’ and case ‘4’’

In void loop, i manage the sequence and the duration like this :

void loop() { 

static uint8_t ETAT = 0; // begin with the case '0'
static float reftempsGENERAL = millis(); // time in the loop is in milliseconds
switch (ETAT) {

case 0:

yoyo45to75 (45, 16);  // do yoyo45to75--> speed 45 to 75   for 16s 
yoyo75to60 (75, 16*2); // do yoyo75to60 --> speed 75 to 60 for 32s
yoyo60to45 (45, 16*2); // 

if(millis() - reftempsGENERAL > 16000*5) { // if time is upper than 16000 millisecond*5 --> switch on CASE 4
      reftempsGENERAL = millis();
     ETAT=4;

     /*  TEST IF MOTOR STOP AT THE GOOD POSITION
 
       for(uint8_t i = 0; i < 5; i++) { 
       setMoteur(i, 0); 
      } 
     while(1);
*/ 
      }

      break;

case 4:

recallage2to60 (60,8); // MOVE MOTOR TO THE SECOND ONE

if(millis() - reftempsGENERAL > 8000) { // IF time > 8 sec go back to case 0
      reftempsGENERAL = millis();
       ETAT=0;
      
 /*
       for(uint8_t i = 0; i < 5; i++) { 
       setMoteur(i, 0); 
      } 
     while(1);

*/      
      }
break;
 }

And later, would it be possible to directly select my cases with the keyboard and have a program record the keys and the duration between them. So that this program will sequence the cases as it recorded it?
This second question is less important than the first for now but maybe it’s simpler to program.
So I’ll every option you propose to me!!
Thank you for your help and your kind lights.

You can see below the way I coded my case.

void keyPressed() {//**** faire position de base à l'arret avec ecrart equidistant qui s'incrementent entre G et le chiffre
 
 // SET POSITION
 if (keyCode == CONTROL) { println(" GO to NEXT POSITION "); // Faire control -
   for (int i = 0; i < net.networkSize; i++) {
   // net.phase[i] += net.phase[i]+2*PI/net.networkSize;// (incremente ecrat des dernieres phases --> si nul elles n'evoluent pas
    net.phase[i] = net.phase[i]+2*PI/net.networkSize; // (faire un nouvel ecart à corrélé selon l'ecart precedent )
   //    net.phase[i] += 2*i*PI/net.networkSize; // positionne avec un ecart qui s'increment et se supperposent les uns sur les autres
    print ("phase "); print (i);  print (" ");
    print (net.phase[i]); print (" ");
    print ("frequency "); print (i); print (" ");
    println (net.naturalFrequency[i]);  
     }
}
// SET POSITION
 if (keyCode == ALT) { println(" SHIFT POSITION "); // Faire ALT -
   for (int i = 0; i < net.networkSize; i++) {
    net.phase[i] += net.phase[i]+2*PI/net.networkSize;// (incremente ecrat des dernieres phases --> si nul elles n'evoluent pas
  //  net.phase[i] = net.phase[i]+2*PI/net.networkSize; // (faire un nouvel ecart à corrélé selon l'ecart precedent )
   //    net.phase[i] += 2*i*PI/net.networkSize; // positionne avec un ecart qui s'increment et se supperposent les uns sur les autres
    print ("phase "); print (i);  print (" ");
    print (net.phase[i]); print (" ");
    print ("frequency "); print (i); print (" ");
    println (net.naturalFrequency[i]);  
     }
}

 
 
 //***********************  ADDING "LIVE" in the hole movement
   if (key == 'Q') {println ("Noise in Phases");
    background (0);
       for (int i = 0; i < net.networkSize; i++) {
         //  net.phase[i] = TWO_PI *   noise(i*0.1); 
            net.phase[i] =  net.phase[i]  +  noise(i*0.1); // Add noise in former 
           
    print ("phase "); print (i);  print (" ");
    print (net.phase[i]); print (" ");
    print ("frequency "); print (i); print (" ");
    println (net.naturalFrequency[i]);
     }
  } 
  if (key == 'q') {println ("Noise in Frequencies");
  background (0); 
       for (int i = 0; i < net.networkSize; i++) {
           net.naturalFrequency[i] = TWO_PI *   noise(i*0.1); //p0.01 to begin slower
    print ("phase "); print (i);  print (" ");
    print (net.phase[i]); print (" ");
    print ("frequency "); print (i); print (" ");
    println (net.naturalFrequency[i]);    
     }
} 
1 Like

maybe there is a library for this

read this : Short film created solely on Processing

and Album cover for Floating Spectrum

1 Like

Thanks Chrisir.

But I have red your two links and the guy has sequenced his program with Blender not with Processing (if I have well understood).

maybe there is a library for this

I hope so.

I have seen this " to deal with data" but it seem very complicated.
https://processing.org/tutorials/data/.

1 Like

Did you check the library website?

Here an example of a program that records keys and timing:

// press number keys between 0 and 9, ESC to end
PrintWriter output;

void setup() {
  size(200, 200);
  output = createWriter("data.txt");
  output.println("0:0");
  textSize(100);
}

void draw() {
  background(0);
  text(key, 20, height - 20);
}

void keyPressed() {
  if (key == ESC) {
    output.flush();
    output.close();
    exit();
  } else {
    output.println(frameCount + ":" + key);
  }
}

And a program that plays the recording back:

String[] lines;
int index = 0, nextFrame = 0, theKey = 0; 

void setup() {
  size(200, 200);
  lines = loadStrings("data.txt");
  readOneLine();
  textSize(100);
}
void draw() {
  background(0);
  text(theKey, 20, height - 20);
  if(frameCount == nextFrame) {
    readOneLine();
  }
}
void readOneLine() {
    theKey =  int(split(lines[index], ':')[1]);
    if(++index == lines.length - 1) {
      exit();
    }
    nextFrame = int(split(lines[index], ':')[0]);
}

Possible improvements:

  • record times based on millis() instead of frameCount. But that requires more logic, you can’t compare times using == as a specific time in milliseconds may happen between two animation frames.
  • Allow recording any keys. Then you can’t store the variables as int.
  • Maybe don’t crash with invalid data?
1 Like

Here is an example without recording.

It reads a csv file and uses the millis from it


/*
expects a csv file named "planer1.csv" like (without spaces before each line). The millis is the absolute time since the sketch has started; this is when the line starts
 millis,command,param1,param2
 0,11,45,16
 1222,20,75,32
 2444,30,45,32
 4566,40,12,
 7000,50,12,
 */

// data for show
Table planer; 

// example 
EllipseMy[] ellipseMy = new EllipseMy[6];   

// ------------------------------------------------------------------------------

void setup() {
  size(900, 400); 

  // load
  planer = loadTable("planer1.csv", "header");

  //prepare example 
  for (int i=0; i < ellipseMy.length; i++) {
    ellipseMy[i]=new EllipseMy(66+i*100);
  }//for
}//func 

void draw() {
  background(0);

  // show row number 
  int rowNumber = findRowNumberToExecute();  
  text(rowNumber, 100, 100);

  //run commands 
  if (rowNumber>-1) {
    doCommand(rowNumber);
  }//if

  //example 
  fill (255, 1, 1); // RED 
  for (int i=0; i < ellipseMy.length; i++) {
    ellipseMy[i].display();
  }//for
}//func 

// ------------------------------------------------------------------------------

void doCommand(int rowNumber) {
  TableRow row = planer.getRow(rowNumber);
  int commandFromTable = int(row.getString("command"));
  int param1FromTable = int(row.getString("param1"));
  int param2FromTable = int(row.getString("param2"));

  text(str(commandFromTable) + " -> with " 
    + param1FromTable
    + " and "
    + param2FromTable, 
    width/2, 111); 

  switch(commandFromTable) {
  case 11:
    ellipseMy[0].y+=-1;
    break;
  case 20:
    ellipseMy[1].y+=-1;
    break;
  case 30:
    ellipseMy[2].y+=-1;
    break;
  case 40:
    ellipseMy[3].y+=-1;
    break;
  case 50:
    ellipseMy[4].y+=-1;
    break;
  default:
    // ignore 
    break;
  }//switch
}//func 

int findRowNumberToExecute() {
  boolean prevRow=false;
  int result = -1; 

  for (int i = 0; i < planer.getRowCount(); i++) {

    TableRow row = planer.getRow(i);
    int millisFromTable = int(row.getString("millis"));

    if (millisFromTable>millis()) {
      // found 
      prevRow=true;
      result=i-1; 
      break;
    }//if
  }// for

  // found 
  if (prevRow) {
    return result;
  }//if

  // no found 
  return -1;
}//func 

// ============================================================================

class EllipseMy {
  int x, y=height-66; 

  EllipseMy(int x_) {
    x=x_;
  }//constr

  void display() {
    ellipse(x, y, 17, 17);
  }//method
  //
}//class
//
1 Like

It relies on the order of millis. Each Line has a milliseconds value that tells the line when to start (or the previous line to end).

There is only one active line at any given moment.

Improvement could be to have a start and a stop time (in millis) and use this. Then you could have multiple tasks at once.

Each line has a command number that can be evaluated and executed in function doCommand(). It has also 2 parameters you could pass to your function yoyo75to60().

There are things to discuss though. For example when you load a lot of images in setup() the assumption that the millis() are 0 at start of draw() is wrong. Therefore we could set a timer at the end of setup() and define it as 0.

Also when we have processes we don’t know how long they take (ball flies to target) the program could wait until the event happens. Only then the next task would be started.
But then the following millis values would have to be relative to this event and not absolute (from the start of the sketch) anymore.

Chrisir

Hi,

I have tested yours programs. But the second one have a bug here
theKey = int(split(lines[index], ‘:’)[1]);
Processing says:
ArrayIndexOutOfBoundsException: 0

May be I have to mix your 2 program to record a sequence ( with the good time between case) but I m not be able to do that.

Actually I would like to trig event one after one. I have several motors turning at different speed with only one case.
I would like the simulation of speeds to be close to reality. Maybe recorded time with FrameCount will be the same as millis() :thinking:

Here you can see a simulation (in French sorry).

When motors change speed or way of rotation that’s mean I have changed cases.

So now I have to test your programs.
Could you mix them for me?

Thank you very much

1 Like

You have to copy the result of the first sketch into the file data.txt that gets loaded into the second sketch. /// copy the file

I wouldn’t merge the 2 sketches because one is for recording the other for playback

2 Likes

Great!

It works perfectly.
:smiling_face_with_three_hearts: :innocent:

1 Like

Yes thanks for mentioning that.

You either copy the file produced by program 1 into program 2, or you use an absolute path in both cases like /tmp/data.txt or C:/some/folder/data.txt. I’m not on Windows so not sure how you make absolute paths in Processing on Windows.

I think you can also drag and drop the data.txt file into the second sketch.

2 Likes

You are absolutely right.
But, for the moment, I will try to motorize the above simulation hoping that the motors react in “real time”. I will get back to you after a few tests. Thank you.
Sincerly

2 Likes