Help - Need suggestion on multithread/tasking

i have a project i am working on my mega for a lighting project. it uses several functions to produce a set of light patterns. my issue is i am using a psx controller to select to which pattern to use (including no pattern and all patterns). but i am having troubles getting the input from the controller while in the one of the functions for the patterns. is there any way to interupt the pattern when a button on the controller is pressed

This may sound radical to you, but I'd suggest completely changing how you do your patterns.

I'm assuming that at the moment you are setting digital pins high/low, and using delay() between combinations, yes?

You'd be better off having a routine which runs from a timer as an interrupt and reads the combinations from an array. That way your main loop is free to do what it wants, and your lights will run "in the background".

I agree with majenko here. A free-running timer that generates an interrupt at the appropriate rate (ie. how often the pattern changes) could just pick up a pattern from an array in memory. Your main loop can then change that array, check buttons etc. when it wants to.

so what is the easiest way of doing this...i was originally thinking something like this:

  • make a new delay function that checks if there are any changes in the buttons while using a loop to impart the delay...if there is a change, break the function

is that the right way to start?? or is there a simpler way to achieve this???

The Multi_Blink example in the Playground may also show you a way to use structures to define the patterns you need. A really useful approach is to write generic code that is driven by data so that you only ever need to change the data (like a script) for different patterns.

The attached will:

  • Allow serial input to program a sequence with a simple set of commands:
    (dnn. is a delay, D is repeat last delay, 0123 are output values, Z ends sequence)
  • Allow the sequence to be viewed (as it goes through the sequence, with print commands)
  • Store the sequence in EPROM
  • Recover the sequence from EPROM
  • the command h gives initial help

As written, it uses only two outputs, but could be modified for more or outputting more complicated commands at the appropriate time. It was based on Blinkwithoutdelay. I hope it helps you.

#include <EEPROM.h>

/*
  Pulse gen, specify pattern output for two lines,
 pattern can be saved and restored from eeprom

 This version autorestores unless h is put down the serial line
 
 */

const byte ledpin13 =  13; 
const byte ledPin10 =  11;

enum COMMANDS { 
  SETOUT, DELAY, END };

struct CA {
  COMMANDS c;
  union  {
    unsigned  newout;
    unsigned  del;
  };
};


class c_last_delpos
{
public:
  c_last_delpos() { 
    _ca.c = DELAY; 
    _ca.del = 100; 
  } 
  ;
  void set(CA toset) { 
    _ca = toset; 
  };
  CA get() { 
    return _ca; 
  };
private:
  CA _ca;
};

boolean g_printflag = false;
boolean g_pattern_in = false;
boolean g_autorestoreflag = true;
int g_insertpos;
c_last_delpos last_delpos;
int last_del_printed;

int curpapos = 0;
const int pattmax = 50;
CA arr_ca[pattmax];

long previousMillis;        // will store last time LED was updated
long autorestoreMillis;
int g_autorestoreseconds = 10;

void store_pattern()
{
  Serial.print(sizeof(arr_ca));
  byte *bp = (byte *)&arr_ca;
  for (int i = 0; i < sizeof(arr_ca); i++)
  {
    EEPROM.write(0x20 + i, *bp);
    bp ++;
  }
  Serial.println("byte, store done");

}

void restore_pattern()
{
  Serial.print(sizeof(arr_ca));
  byte *bp = (byte *)&arr_ca;
  for (int i = 0; i < sizeof(arr_ca); i++)
  {
    *bp = EEPROM.read(0x20 + i);
    bp ++;
  }
  // could be rubbish in there though, so make sure last ele of array safe
  arr_ca[pattmax - 1].c = END;
  Serial.println(" bytes, retrieve done");

}


void reset_all_pattern_states()
{
  curpapos = 0;
  g_printflag = false;
  g_pattern_in = false;
  arr_ca[0].c = DELAY;
  arr_ca[0].del = 100;

  arr_ca[1].c = SETOUT;
  arr_ca[1].newout = '0';

  arr_ca[2].c = DELAY;
  arr_ca[2].del = 100;

  arr_ca[3].c = SETOUT;
  arr_ca[3].newout = '3';

  arr_ca[4].c = END;
}

void movepatternpointer()
{
  if (g_insertpos < pattmax)
  {  
    g_insertpos++;
  }
  else
  {
    Serial.print("No more pattern space");
  }
  arr_ca[g_insertpos].c = END;  
}


void setup() {
  // set the digital pin as output:
  pinMode(ledpin13, OUTPUT);
  pinMode(ledPin10, OUTPUT); 
  Serial.begin(9600); 
  reset_all_pattern_states();
  previousMillis = millis();
  autorestoreMillis = millis();
}


void loop()
{
  unsigned long currentMillis = millis();

  switch (arr_ca[curpapos].c)
  {

  case END:
    curpapos = 0;
    if (g_printflag)
    {
      Serial.println('Z');
      last_del_printed = 0;
    }
    break;

  case SETOUT:
    digitalWrite(ledpin13, 0x01 & arr_ca[curpapos].newout);
    digitalWrite(ledPin10, 0x02 & arr_ca[curpapos].newout);
    if (g_printflag)
    { 
      Serial.print((char)(arr_ca[curpapos].newout)); 
    }
    curpapos ++;
    break;

  case DELAY:
    if (currentMillis - previousMillis > (unsigned long)arr_ca[curpapos].del) {
      // time to move on
      previousMillis = currentMillis;
      if (g_printflag)
      {
        if (last_del_printed != arr_ca[curpapos].del)
        {
          Serial.print('d'); 
          Serial.print(arr_ca[curpapos].del); 
          Serial.print(".");
          last_del_printed = arr_ca[curpapos].del;
        }
        else
        {
          Serial.print('D');
        }
      }
      curpapos ++;
    }
    break;    
  } // end interpret pattern switch
  
  if (g_autorestoreflag && 
      currentMillis - autorestoreMillis > (unsigned long)1000)
  {
    g_autorestoreseconds -= 1;
    autorestoreMillis = currentMillis;
    Serial.print("To cancel press h, eeprom restore ");
    Serial.println(g_autorestoreseconds);
    
    if (g_autorestoreseconds < 1)
    {
      g_autorestoreflag = false;
      restore_pattern();
    }
    
  }


  if (Serial.available())
  {
    char c = Serial.read();
    switch (c)
    {

    case 'p':
      g_printflag = !g_printflag; // toggle printing flag
      last_del_printed = 0;
      break;


    case 'P':
      Serial.println("Enter new pattern [0123] or dn[nn]. or D or Z");
      g_pattern_in = true;
      g_insertpos = 0;
      arr_ca[g_insertpos].c = END; // always ensure the end position is an END
      break;

    case '0':
    case '1':
    case '2':
    case '3': // these are the bits for setting the digital lines out
      if (g_pattern_in)
      {
        arr_ca[g_insertpos].c = SETOUT;
        arr_ca[g_insertpos].newout = c;
        movepatternpointer();   
      }
      break;

    case 'd':
      if (g_pattern_in)
      {
        char c1 = '0';
        Serial.print(c);
        arr_ca[g_insertpos].c = DELAY;
        arr_ca[g_insertpos].del = 0;
        while(c1 != '.')
        {
          c1 = Serial.read();
          Serial.print(c1);
          if (c1 >= '0' && c1 <= '9')
          {           
            arr_ca[g_insertpos].del *= 10;
            arr_ca[g_insertpos].del += c1 - '0';
          }
        }
        last_delpos.set(arr_ca[g_insertpos]); // save the delay value for later with D command
        movepatternpointer();            
      }
      break;

    case 'D': // repeat last interval setting 
      if (g_pattern_in)
      {
        arr_ca[g_insertpos] = last_delpos.get();
        movepatternpointer();           
      }
      break;   

    case 'Z':
      g_pattern_in = false;
      Serial.println(c);
      break; 

    case 'r':
    case 'R':
      reset_all_pattern_states();
      Serial.println(c);
      break;

    case 'h':
    case 'H':
      g_autorestoreflag = false;
      Serial.println("h help, Ss Store/Retrieve, p print on/off\n P new pattern, Rr reset pattern to preset"); 
      break;

    case 'S':
      store_pattern();
      break;

    case 's':
      restore_pattern();
      break;
    } // end switch
  } // end if Serial.available()


} // end loop

i sorta do like the multi blink concept...i think i can use it if i make the patterns more script like...and it will allow me to i think more quickly write more patterns

ok…i have hit a brick wall…it is not that code “ISNT” working…it is just that i am VB programmer…(i know…go ahead and say it)…but here is…in VB you can do this:

dim i(10) = {1,2,3,4,5,6,7,8,9,0}
redim 1(5)

you can do a single command and get the number of elements in the array…in the blink example…there r a few structs…in the one there are a defined amount of elements shown here:

typedef struct
{
  uint8_t  activeVal;
  uint16_t activeTime;
} stateDef;

typedef struct
{
  uint8_t  ledPin;        
  uint8_t  currentState;
  stateDef state[2];     //<----RIGHT HERE!!!
  uint32_t lastTransTime; 
} ledTable;

How do i go about making it a more dynamic array…ex// pattern1 has 120 commands and pattern2 has 1938 commands…can you point me in the right direction

In the microcontrollers you don't really have a lot of memory, so dynamic memory allocation is not such a great idea. It does exist in C/C++ as the mem*() series of functions (specifically here malloc() and free(), which are not mem*() :0). You would define a pointer to the memory as part of the struct and then assign the allocated memory pointer to it when it is allocated. However, this is only done at run time, so you can't set up tables like in the multi_blink code now. In that situation you would have the data sitting somewhere else (file or equivalent) and read it in (ie, the data is external to the program).

You can define an array to be as big as your maximum and then only use a portion of it (set up a variable that tells you what that is). The actual size in elements of an array can also be found by calculating sizeof(array_name)/sizeof(element_name) (eg, sizof(buffer)/sizeof(int)). If you are looking for sizes as big as 2000 elements of structures then you may nopt even have enough RAM to do what you need.

Also note that all array indices start at 0, unlike VB where you have the option.

i don't think it is 2000 elements...i was just being extreme...but do u think it is better to just make it as large as the biggest???

Yes if you have enough space. I know it sounds inefficient on memory but it is safer than dynamic allocation in these small devices.

GGGRRR…i had this working yesterday and now it is broke…can you guys take a look and let me know what i am missing…it is erroring out on the AllOff()…during one of the Toff()…but i don’t know why…here is the code:

#include <LightTree.h>
#include <LiquidCrystal.h>
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
boolean bBreak = false;
int p1u = 30;
int p1l = 32;
int p1d = 34;
int p1r = 36;
int p2u = 38;
int p2l = 40;
int p2d = 42;
int p2r = 44;
int p3u = 46;
int p3l = 48;
int p3d = 50;
int p3r = 52;
boolean b1u = false;
boolean b1l = false;
boolean b1d = false;
boolean b1r = false;
boolean b2u = false;
boolean b2l = false;
boolean b2d = false;
boolean b2r = false;
boolean b3u = false;
boolean b3l = false;
boolean b3d = false;
boolean b3r = false;

void setup() 
{                
    Serial.begin(9600);
    Serial.println("StartSteup:");
    pinMode(p1u, OUTPUT); 
    digitalWrite(p1u, HIGH);    
    pinMode(p1d, OUTPUT); 
    digitalWrite(p1d, HIGH);    
    pinMode(p1l, OUTPUT); 
    digitalWrite(p1l, HIGH);    
    pinMode(p1r, OUTPUT); 
    digitalWrite(p1r, HIGH);    
    Serial.println("level 1");
    pinMode(p2u, OUTPUT); 
    digitalWrite(p2u, HIGH);    
    pinMode(p2d, OUTPUT); 
    digitalWrite(p2d, HIGH);    
    pinMode(p2l, OUTPUT); 
    digitalWrite(p2l, HIGH);    
    pinMode(p2r, OUTPUT); 
    digitalWrite(p2r, HIGH);    
    Serial.println("level 2");
    pinMode(p3u, OUTPUT); 
    digitalWrite(p3u, HIGH);    
    pinMode(p3d, OUTPUT); 
    digitalWrite(p3d, HIGH);    
    pinMode(p3l, OUTPUT); 
    digitalWrite(p3l, HIGH);    
    pinMode(p3r, OUTPUT); 
    digitalWrite(p3r, HIGH);    
    Serial.println("level 3");
    lcd.begin(16, 2);
    Serial.println("lcd");
    Toff("p1u");
    Toff("p1d");
    Toff("p1r");
    Toff("p1l");
    Toff("p2u");
    Toff("p2d");
    Toff("p2l");
    Toff("p2r");
    Toff("p3u");
    Toff("p3d");
    Toff("p3l");
    Toff("p3r");
    delay(100);   
    Serial.println("endSteup:");
    AllOn();
    delay(1000);
    AllOff();
    delay(1000);
}
void loop()
{
  /*
  //DoLcd();
  for(int x = 0; x<Pats.maxState; x++)
  {

    Serial.print(x);
    Serial.print("-");
    boolean dDone=false;
    Serial.print("StartWhile ");
    if(Pats.state[x].activeVal)
      Ton(Pats.state[x].pinVal);
    else
      Toff(Pats.state[x].pinVal);
    
    while(bBreak==false && dDone==false)
    {
      if (millis() >= Pats.lastTransTime + Pats.state[x].delayTime)
      {
        Pats.lastTransTime = millis();
      
        Serial.print("K ");
        Serial.println(x);
        
        dDone=true;
      }
/*
      if (DoEvents())
      {
        bBreak = true;
        break;
      }  
*/
/*
    }
    dDone=false;
    Serial.print("EndWhile ");

    Serial.println(bBreak);
    if (bBreak)
    {
      bBreak=false;
      break;
    }
  }
  Serial.println("");
  AllOff();
  */
}
void Ton(String istr)
{
    int i;
    if (istr=="p1u")
    {
            i = p1u;
            b1u = true;
    }
    else if(istr=="p1d")
    {
            i = p1d;
            b1d = true;
    }
    else if(istr=="p1l")
    {
            i = p1l;
            b1l = true;
    }
    else if(istr=="p1r")
    {
            i = p1r;
            b1r = true;
    }
    else if (istr=="p2u")
    {
            i = p2u;
            b2u = true;
    }
    else if(istr=="p2d")
    {
            i = p2d;
            b2d = true;
    }
    else if(istr=="p2l")
    {
            i = p2l;
            b2l = true;
    }
    else if(istr=="p2r")
    {
            i = p2r;
            b2r = true;
    }
    else if (istr=="p3u")
    {
            i = p3u;
            b3u = true;
    }
    else if(istr=="p3d")
    {
            i = p3d;
            b3d = true;
    }
    else if(istr=="p3l")
    {
            i = p3l;
            b3l = true;
    }
    else if(istr=="p3r")
    {
            i = p3r;
            b3r = true;
    }
    else
    {
    }
    if (i>0)
    {
      digitalWrite(i, HIGH);    
    }
}
void Toff(String istr)
{
    int i=0;
    if (istr=="p1u")
    {
            i = p1u;
            b1u = false;
    }
    else if(istr=="p1d")
    {
            i = p1d;
            b1d = false;
    }
    else if(istr=="p1l")
    {
            i = p1l;
            b1l = false;
    }
    else if(istr=="p1r")
    {
            i = p1r;
            b1r = false;
    }
    else if (istr=="p2u")
    {
            i = p2u;
            b2u = false;
    }
    else if(istr=="p2d")
    {
            i = p2d;
            b2d = false;
    }
    else if(istr=="p2l")
    {
            i = p2l;
            b2l = false;
    }
    else if(istr=="p2r")
    {
            i = p2r;
            b2r = false;
    }
    else if (istr=="p3u")
    {
            i = p3u;
            b3u = false;
    }
    else if(istr=="p3d")
    {
            i = p3d;
            b3d = false;
    }
    else if(istr=="p3l")
    {
            i = p3l;
            b3l = false;
    }
    else if(istr=="p3r")
    {
            i = p3r;
            b3r = false;
    }
    else
    {
    }
    if (i>0)
    {
      digitalWrite(i, LOW);    
    }
}
void AllOn()
{
    Ton("p1u");
    Ton("p1d");
    Ton("p1l");
    Ton("p1r");
    Ton("p2u");
    Ton("p2d");
    Ton("p2l");
    Ton("p2r");
    Ton("p3u");
    Ton("p3d");
    Ton("p3l");
    Ton("p3r");
}
void AllOff()
{
    Toff("p1u");
    Toff("p1d");
    Toff("p1l");
    Toff("p1r");
    Toff("p2u");
    Toff("p2d");
    Toff("p2l");
    Toff("p2r");
    Toff("p3u");
    Toff("p3d");
    Toff("p3l");
    Toff("p3r");
}
patTable Pats =
  {"TEST",1,24,0,
    {
      {"p1u",1,0},
      {"p1l",1,0},
      {"p1d",1,0},
      {"p1r",1,0},
      {"p2u",1,0},
      {"p2l",1,0},
      {"p2d",1,0},
      {"p2r",1,0},
      {"p3u",1,0},
      {"p3l",1,0},
      {"p3d",1,0},
      {"p3r",1,1000},
      {"p1u",0,0},
      {"p1l",0,0},
      {"p1d",0,0},
      {"p1r",0,0},
      {"p2u",0,0},
      {"p2l",0,0},
      {"p2d",0,0},
      {"p2r",0,0},
      {"p3u",0,0},
      {"p3l",0,0},
      {"p3d",0,0},
      {"p3r",0,1000}
    }
  };

and the header

#include <Arduino.h>

#ifndef  LIGHTTREE
#define  LIGHTTREE
typedef struct
{
  String   pinVal;        // Which globe to change
  uint8_t  activeVal;     // Weather to turn globe on/off
  uint16_t delayTime;    // Time to delay before next action
} stateDef;

typedef struct
{
  
  String   patName;        // Pattern name
  uint8_t  patId;          // Pattern id number
  uint8_t  maxState;       // max number od states
  uint32_t lastTransTime;  // the 'time' of the last state transition - saves the millis() value
  stateDef state[700];       // the ON and OFF state definitions. Add more states if required
} patTable;
#endif

it is erroring out on the AllOff()...during one of the Toff()...but i don't know why

Once more, in English, please.

i did some troubleshooting...it has to do with patTable...more specifically the like of stateDef state[700];...it works with stateDef state[2];...why???

Probably running out of memory.

it must be…during the setup loop, i have the Toff(“p1u”)…the parm is actually coming thru as different text…it is weird

How much memory do you estimate patTable is taking? My estimate is 6300 bytes out of your available 8192.

Plus the strings in the code will be taking a bit.

Plus, instantiating 700 String objects will probably take quite a bit of memory. A quick test indicates 13 bytes per String, before you put data into them, sounds about right.

So, 700 * 13 = 9100, which exceeds your available memory. Let alone the other stuff you have.

Test code:

#include <memdebug.h>

const int NUM_STRINGS = 20;

void setup ()
{
Serial.begin (115200);

Serial.println ();

size_t before = getFreeMemory ();
Serial.print ("Before = ");
Serial.println (before);

String * foo [NUM_STRINGS];

for (int i = 0; i < NUM_STRINGS; i++)
  foo [i] = new String;

size_t after = getFreeMemory ();
Serial.print ("After = ");
Serial.println (after);

Serial.print ("Difference = ");
Serial.println (before - after);

Serial.print ("Per string = ");
Serial.println ((before - after) / NUM_STRINGS);

} // end of setup

void loop () {}

Output:

Before = 1747
After = 1487
Difference = 260
Per string = 13

hmm...even tho my mega say it allow 126k...BUT...i did take your advice and go with char pinVal[4] instead...it works with 700 elements now