#define SIMUL 100 /* % PC speed on wokwi.com */
//////////////////////////////////////////////////////////////////////////
///////////////////////      basicthreads.h      /////////////////////////
// "basic meets protothreads". Developed by Adam Dunkels & Bill Gates   //
// inspired by Donald Knuth ('literate programming'/MIXAL/MAD-Magazine) //
//           Bernd vom Berg ("LabVIEW meets µC")                        //
//                Michael H.("zweierkomplement seriel senden?")         //
//       daniel_duesentrieb ("Wie macht man solche Lichtmodes?")        //
//              paulpaulson ("sowas hat man noch in der Bibliothek")    //
//////////////////////////////////////////////////////////////////////////

//-- basic compatibility ---------------------------------------------- //
#define Loop while(1)
#define For_up_TO(var,from,to) for(var=from; var<=to; var++)
#define For_dn_TO(var,from,to) for(var=from; var>=to; var--)
#define For(var,from,dir,to) For_##dir(var,from,to)
#define Next 
#define Function     
#define Procedure void   
#define then    
#define ISR void    
#define end_(rem) 
#define Exist(pointer) (pointer!=NULL)
#define Print(txt)   Serial.print(txt)
#define PrintLn(txt) Serial.println(txt)
 // some mixal like /basic compatibility
#define u_byte uint8_t
#define s_byte int8_t
#define u_word uint16_t
#define s_word int16_t
#define eq == /* MIXALl: equal          */
#define ne != /* MIXALl: not equal      */
#define sh >= /* MIXALl: same or higher */
#define sl <= /* MIXALl: same or lower  */

// name'helper' 
#define t_conc(name) task__ ##name 
#define t_label(name) t_conc(name)
#define t_adr(name) &&t_conc(name)
#if 1
  #define t_dbg(txt)    Serial.print(txt)
  #define t_dbg_ln(txt) Serial.println(txt)
#else
  #define t_dbg(txt)
  #define t_dbg_ln(txt) 
#endif

// for correct use in subs rename 'task' with 'thread' //
// technical requirements: C with gcc-Extension(&& label)

// VAR_tasks(5,int x,y;) use x,y in task__. 
#define HEAP_tsk(_max,...)                \
   const u_byte t_max=_max-1;             \
   typedef struct                         \
      {void *PC; u_word cs; s_word para;  \
       __VA_ARGS__;} t_struct;            \
   static t_struct t_A[_max];             \
   static u_byte   t_cid;     /*current*/ \
   static s_byte   t_tid=-128;/*tmp id */ \
   static u_word   centis;    /*time   */
#define t_cS String(t_cid)
#define t_tS String(t_tid)
// ________ variable 'frame' _________
#define iT_(id,var) t_A[id].var       
#define T_(var)     iT_(t_cid,var)  /*cur !!!*/
#define T_S(var)  String(T_(var))
#define iT_S(var) String(iT_(id,var))

// break: save PC+1  return 
#define t_store_PC(pc) T_(PC) = t_adr(pc)
#define t_break(pc) t_store_PC(pc); goto t_brk_vec
#define t_Break {t_break(__LINE__); t_label(__LINE__):/* come back*/;}
// call  task 
#define t_call(adr) {goto *(adr); t_brk_vec:;}

// task_container{...}_____________________________________________.
#define tsk_container /* run all one step */                       \
 For(t_cid, 0, up_TO, t_max) if Exist(T_(PC)) then t_call(T_(PC)); \
 if(t_tid eq -2)/* prevent execution*/

// task_intit{autostarts} ___________________________________.
#define tsk_init for(;t_tid==-128;t_tid=(t_tid==-128)?0:t_tid)

// setTask(id,name,parameter) __________________________________.
#define setTask(uid,name,p){t_dbg("set:"#name" u:"+String(uid));\
  if((0 sl uid) and (uid sl t_max))                             \
  then { iT_(uid,PC)=t_adr(name); iT_(uid,para)=p;              \
         t_dbg_ln(" OK");            }                          \
  else   t_dbg_ln(" out of range");                             \         
end_(setTask);} 
// newTask(name,parameter)______________________________________.
#define newTask(name,p){ t_dbg("new:"#name" u:");;              \
  For(t_tid,t_max,dn_TO,0)                                      \
      if (iT_(t_tid,PC) eq NULL) {setTask(t_tid,name,p); break;}\
  if(t_tid < 0) t_dbg_ln(" to much tasks");\ 
end_(newTask);} 
// t_callTask(T_(A),test,NULL)_use in TASK______.
#define t_callTask(pid,name,_para)              \
{ newTask(name,_para); pid=t_tid;          \ 
  if(pid sh 0) then t_Wait(!Exist(iT_(pid, PC)))} 
 // stopTask(3) _______________________________________.
#define freeTask(ind){t_dbg_ln("free id:"+String(ind));\
  iT_(ind,PC)=NULL;                                    }
// freeTasks(-1) _____________________________________________.
#define freeTasks(leave){ t_dbg_ln("free all"+String(leave)); \
  For(t_tid,0,up_TO,t_max leave) iT_(t_tid,PC)=NULL;          }

//________  time sharing _________________________
#define t_Yield t_Break  /* syntax compatiblity to scheduled task */
#define t_Wait(cond)     while(!(cond)) t_Yield ;
#define t_Wait_cs(_cs)   { T_(cs) = centis + _cs;   \
                           t_Wait(centis eq T_(cs));}
#define t_Until(cond)    t_Yield; if(cond) break;
#define t_Switch_to(name){ T_(PC)=t_adr(name);goto t_brk_vec;}
#define t_End            { t_dbg_ln("end: id:"+t_cS);        \
                           T_(PC)=NULL;       goto t_brk_vec;}

// _________ task 'frame' _______________________________
#define TASK(name) t_conc(name):                         \
  t_dbg_ln("run:"#name" id:" +  t_cS+" p:"+  T_S(para)); \
  Loop{
#define task_until(cond) t_Until(cond);      }; t_End;         
#define task_next        t_Yield;            };
#define task_end         t_End;              };
#define task_goto(name)  t_Switch_to(name);  }; 
//////////////////////////////////////////////////////////////////////////


//////////////////////////////////////////////////////////////////////////
#define pinsCount 12                      
u_byte pin_sort_[] = {2,3,4,5,6,7,8,9,10,11,12,13};    
u_byte pin_sort[] = {13,12,11,10,9,8,7,6,5,4,3,2};    
u_byte pins_cur[pinsCount];  u_byte pins_des[pinsCount];
bool pins_cur_[pinsCount]; 
bool flag_100hz;

enum {w_dn,w_fade,w_fix};
Procedure wr_pin(u_byte num,u_byte val,u_byte sw){
   switch(sw){
   case w_dn:  pins_des[num-1]=000; pins_cur[num-1]=val;break;
   case w_fade:pins_des[num-1]=val;                     break;
   case w_fix: pins_des[num-1]=val; pins_cur[num-1]=val;break;
   end_(sw);}
end_(wr_pin);}

Procedure w_pin_(u_byte num, u_byte val, s_byte sw){
   pins_des[num-1]=0; pins_cur[num-1]=val;
end_(w_pin);}

ISR simul_100hz_upd(){int i;
  flag_100hz=true;
  For(i,0,up_TO,pinsCount-1){
     if(pins_cur[i]!=pins_des[i])
        if(pins_cur[i]>pins_des[i]) pins_cur[i]--;
        else                        pins_cur[i]++;
     digitalWrite(pin_sort[i], pins_cur[i]);
//   analogWrite(pin_sort[i],pins_cur[i]);   
  Next;}
end_(TSR);}
               
//---------- some buttons ----------//
#define btn_dbg A5
#define btn1 A2
#define btn2 A0
enum { btn_press=1*256,btn_repeat=4*256,
       btn_short=2*256,btn_long  =3*256};
u_byte btn_scan; 

Function u_word button(){
  u_word tmp_return;
  static u_byte time;  
  u_byte scan;    
  scan=1*(digitalRead(btn1)==LOW)+2*(digitalRead(btn2)==LOW);
  if(scan ne btn_scan)
  { if(time<42) tmp_return=btn_scan+btn_short;
    else        tmp_return=btn_scan+btn_long;
    btn_scan=scan;    time=0;                }
  else switch(time++) {
       case   1:tmp_return=scan + btn_press; break;
       case  60:tmp_return=scan + btn_repeat;break;
       case 100:time=59;
       default:tmp_return=0;  }
return tmp_return;}

//////////////////////////////////////////////////////////////////////////

#define ana1 A1
#include <TimerOne.h>

Procedure setup() {int i;
  For (i,0,up_TO,pinsCount-1) pinMode(pin_sort[i], OUTPUT);      
  pinMode(btn1, INPUT_PULLUP); pinMode(btn2, INPUT_PULLUP);
  pinMode(ana1, INPUT);
  Serial.begin(38400);  //  10ms (expected)
  Timer1.initialize((((long)10*1000 *SIMUL/100)));
  Timer1.attachInterrupt(simul_100hz_upd);        
end_(setup);}

#define test_pin(num,duration) \
   {wr_pin(num,25,w_dn); t_Wait_cs(duration); };

Procedure loop() {
 HEAP_tsk(8,int A,B,C);  
 
 if(flag_100hz){flag_100hz=false; centis++;}
 // else return;//'slow' task only 
 //----------------------------------------------------------------------  
 tsk_container{
    
    TASK (blink)
         wr_pin(12,30,w_dn); 
           t_Wait_cs(T_(para));
    task_until(0)

    TASK (on) 
  	   For(T_(A),1,up_TO,pinsCount-1){
             wr_pin(T_(A),200,w_dn); 
             t_Wait_cs(30);
        Next;}
    task_goto(lr_1_12)

    TASK (lr_1_12)
  	   For(T_(A),1,up_TO,pinsCount-1) 
          test_pin(T_(A),T_(para)); 
    task_next

    TASK (kn)
       For(T_(A),-7,up_TO,6) test_pin(abs(T_(A))+1,T_(para)); 
    task_next
    
    TASK (kn_1_8)
       For(T_(A),1,up_TO,8) test_pin(T_(A),T_(para)); 
    task_goto( kn_7_2 )
    
    TASK (kn_7_2)
       For(T_(A),7,dn_TO,2) test_pin(T_(A),T_(para));
    task_goto( kn_1_8 )
            
    TASK (prg_1)
       t_callTask(T_(A),flash,80);t_dbg_ln("T_(A): tmp");
       t_Wait_cs(500);            t_dbg_ln("5 sec");
       t_callTask(T_(A),flash,40);
    task_goto(kn);
    
    TASK (prg_2)
    task_end

    TASK (flash)
       For(T_(A),1,up_TO,5) wr_pin(T_(A),50,w_dn); 
       t_Wait_cs(50);                           
       For(T_(A),5,up_TO,9) wr_pin(T_(A),50,w_dn); 
    task_end

    TASK (btn)
      switch (button()){
       case 1+btn_press : PrintLn("btn 1-Press free    ");freeTask(1);        break;
       case 1+btn_repeat: PrintLn("btn 1-Rep flash 1   ");setTask(1,flash,40);break;
       case 1+btn_short : PrintLn("btn 1-shrt kn slow  ");setTask(1, kn_1_8 ,60);break;
       case 1+btn_long  : PrintLn("btn 1-lng  kn fast  ");setTask(1, kn_7_2 ,30);break;
       case 2+btn_press : PrintLn("btn 2-Press         ");      break;
       case 2+btn_repeat: PrintLn("btn 2-Rep           ");      break;
       case 2+btn_short : PrintLn("btn 2-shrt add max  ");newTask( lr_1_12 ,50);break;
       case 2+btn_long  : PrintLn("btn 2-lng stp all -3");freeTasks(-3);       break;
       end_(sw_button)} 
       t_Wait_cs(1);
    task_next

    TASK( slide)//T_t(var) typical last used id but also used by stop
      static byte ana_val; byte _ana_val;
      _ana_val=(1023-analogRead(ana1))/10+10;
      if(ana_val and (ana_val ne _ana_val) and (t_tid sh 0))
      { iT_(t_tid,para)=ana_val; PrintLn("slide:"+String(ana_val)+" id:"+t_tS);}
      ana_val=_ana_val;
    task_next;

 end_(container);}
//----------------------------------------------------------------------  
 
 tsk_init{ t_dbg_ln("init");
     newTask( slide, NULL);
     newTask( btn,   NULL);
     newTask( blink,  100);
     setTask(-1, lr_1_12, 20);//-1: some kind of don't start
     setTask( 1, kn,  73);
     setTask( 1, prg_1,  73);
 end_(init);}

end_(loop);} 
 
// in memoriam: Sven Nehlsen (DGzRS, DJ, ... )