Here is my base program that I generally use as a core for most of my other programs. I left the pseudo thread processor (arduino has only one core so I fake it) in the code as well GILtimer (Great Industrious Little Timer), The main program demonstrates to how set up different process that will get called sequentially without regard to time. For timing you set the value and when zero it has timed out. There is another count up as well. For all of this to work you must call millitime() at least once in the main loop each type it cycles. It works on the ESP8266, Mega, Uno, and Nano. The setup() does several things but says hello and gives the file name etc. Let me know. If you like you can start a new post with questions and post the code with it. It works nice for combining different codes but you need to be sure the hardware is correct.`
char Gil[] = "\n\tVersion: 1.0A 200614"; // This is printed in the hello message at setup
/**************************************[ FYI ]***********************************************
** **
** Code compiled on AMD 4-core processor running Linux Mint 18.3 Sylvia **
** This core module uses a state sequencer (threads) to control the processes and there **
** pseudo priority and processing rate. this structure is such as the higher the sequence **
** number the higher pseudo priority of the task(higher occurs first). Also contained in **
** this Base Module is Gil_Timer() Great Industrious Little _Timer. Although not super **
** accurate it will work for a lot of applications and keep the mills portion simple, it **
** is now based on mills rather then interrupts. The time base can be changed at will by **
** changing Tick. This can be done in the program by changing to int Tick = #; **
** I have used this on Arduino (Nano, Uno,Mega) OEM and China versions. **
** This code is written in the way I like it. I have a few years programming in various **
** various assemblers, I am now in the process of learning the higher level languages. **
** If you do not like the format either change it or do not use it! The original of this **
** was written in about 1985 in assembler so it is creaky old. **
** Gil Shultz 200614 - June 14, 2020 **
*********************************************************************************************/
// Set this up at the beginning of the program before setup() or loop()
/***************************[ Great Industrious Little Timer ]***********************************
* *
* This defines the offset (Gil Timer Number) into the timing array. Each of these timers is *
* counted down x times a second, when zero is reached the timer is no longer decremented. *
* To use the timer simply place the delay time you want (16 bit unsigned integer) in the *
* appropriate Array location and check for zero which indicates the time has expired. The *
* only limit is the amount of Ram available and the amount of time available for servicing *
* the pseudo interrupt. The following constructs are offsets into the timer array Gil_Timers *
* "num_Gil_Timers" determines the number of timers that will be serviced. You can increase or *
* decrease the number This code has been modified to use the millis() function in militime() *
* Currently we are set up to count 0.01 seconds so 500 = 5 seconds, 100 = 1 second. *
* *
*****************[ This has been modified from an Arduino interrupt ISR ]***********************/
const int num_Timers = 10; // Number of Timers Zero used for Debug
int Gil_Timer[num_Timers] = {0}; // Array defining the storage location for the timers.
const int BugTimer = 0; // This timer is reserved for Debugging, do not use
const int BugTime = 500; // Temporary time to be changed as needed
const int Alrm_Timer = 1; // offset into array for alarm timer
const int Alrm_Time = 100; // Alarm blink Delay 100 = 1 second or 0.01
const int Case_Timer = 2; // offset in array for the Case Timer
const int Case_Time = 150; // Delay time for the case timer
const int A_test_timer = 3;
const int A_test_time = 100; // These are samples that do nothing but print a number
const int B_test_timer = 4; // on the console. It tries to demonstrate that the
const int B_test_time = 90; // timers although have the same time base operate
const int C_test_timer = 5; // independently from each other.
const int C_test_time = 80;
const int D_test_timer = 6;
const int D_test_time = 70;
const int E_test_timer = 7;
const int E_test_time = 150;
const int F_test_timer = 8;
const int F_test_time = 150;
const int G_test_timer = 9;
const int G_test_time = 150;
// Fill in with additional Timers!
// Change to: int Tick; for your code to dynamically change the time base.
const int Tick = 10; // Time base for Gil_Timers 1=1ms 10=10ms 100=100ms
unsigned long Last_Time = 0; // This is part of the time base for Gil_Timers
// State equates
const int Max_State = 50; // Maximum number of states (Threads)
int stateM = 0; // Our state machine counter
// Hardware Equates I always use a test pin as it makes debugging easer
const int TestPin = D0; // This is pinned as D2 on my Arduino D1 R2 Board
//***********[ Location and ID information ] ************************************
char Revision_Date[] = "Revision\0\0"; // This is a dummy for the revision Number
char TmpBuffer[16] {0}; // Used for string calculations
//***************[ These are used for the console Hello Message ]****************************
const char Source_file[] = __FILE__; // Main File Name
const char compile_date[] = __DATE__ " " __TIME__; // Time and Date from Complier
// Put this with your Functions
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@[ Functions]@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
/******************************[ Functions Installed ]****************************************
* *
* militime() + used to replace interrupt timer base. *
* Mark_It() + Generates a pulse on the Test Pin, used for timing etc. *
* logname(1,2,3) + Configure year (4 dig) month day as a string. Use compiler info *
* LastLine() + Used as part of the serial number must be at end *
* *
* ISR(TIMER1_COMPA_vect) + This is the Gtimer interrupt replaced with millies *
* *
****************************[ Listed in program order ]***************************************/
/****************************[ Process the software timers here ]************************
* *
* Timers[?] is processed Here and called from the main loop *
* *
* This updates the (Gil_Timers) every 0.1 second called in the main loop by utilizing *
* the Mills function. This then decrements each timer every time period until they *
* reach zero. This function yielding almost an unlimited number of timers only *
* constrained by the amount of memory available. *
* *
* Set timers[?] to the delay time wanted see Tick for period. The value at timers[?] *
* has the time remaining until time out (tick). The timer will will stop at zero when *
* timed out. It is the responsibility of the application to check the timers[?] for *
* zero. Note there is some jitter because of interrupts etc. This Unlike the "delay()" *
* function is non blocking. This was derived from my ISR with the same name. *
* 0.14 us *
*******************[ Note these Gil_Timers are NOT for critical timing]******************/
void militime() {
unsigned long currentTime = millis(); // Updates frequently
if (currentTime - Last_Time >= Tick) { // This is the time base for Gil_Timers
int i; int x; // Temporary save RAM
for (i = 0; i < num_Timers; i++) { // Initialize our loop, i is our timer pointer
x = Gil_Timer[i]; // Get the Timer we are pointing at
if (x >00) { // If zero we are finished, if not decrement it by 1
x--; // if not subtract a second
(Gil_Timer[i]) = x; // and update the timer location
} // End of X > zero
} // End of for i loop
Last_Time = currentTime; // Update the timing for the next time around
} // end of event time test
} // End of militime
/*******************************************************
* *
* Send Marker to trigger Logic Analyzer or Scope. *
* *
*******************************************************/
void Mark_It() {
// if (Gil_Timer[Alrm_Timer] == 0) { // Loop Delay
// Gil_Timer[Alrm_Timer] = Alrm_Time;
digitalWrite(TestPin, HIGH);
// } else {
digitalWrite(TestPin, LOW);
// } // End of Else
} // End of Mark_It
/*******************************************************************
* *
* Configure year (4 dig) month day as a string. Use compiler info *
* Print with Buffer +2 to eliminate the century. *
* *
********************************************************************/
void logname(char const *date, char *buff) {
int month, day, year;
static const char month_names[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
sscanf(date, "%s %d %d", buff, &day, &year);
month = (strstr(month_names, buff)-month_names)/3+1;
sprintf(buff, "%d%02d%02d.txt", year, month, day);
}
//@@@@@@@@@@@@@@@@@@@@@@@@@[ Code Setup ]@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
/***********************[ Our code starts here }************************
** **
** This is where we define our starting point This is entered **
** only once when the processor starts Example, Startup or reset **
** button or from shield This way we start with everything in a **
** known state **
** **
** put your setup code here, to run once. **
** **
***********************************************************************/
void setup() { // put your setup code here, to run once:
Serial.begin(115200); // This is for Debug
pinMode(TestPin, OUTPUT); // Set up our TestPin as an output
//************ Set up Revision Date Buffer (Part of Setup) ***************
// This contains the Date as YYMMDD and the last line number LLN so we get YYMMDDLLN
logname(__DATE__, TmpBuffer); // Get date string
for (int i = 0; i < 8; i++) { // Initialize our loop, to move yyyymmdd (8 Characters)
Revision_Date[i] = TmpBuffer[i];
Revision_Date[i+1] = 0; // Add tag for end of string
} // End of for i loop buffer parsed
Serial.print("\n\n\n\n\n\n\t\t Hello Gil!");// Clean up the screen a bit
Serial.print(F("\n\tThis Program is filed as: ")); // Tell who we are
Serial.print(Source_file); // Display the source file name
Serial.print(Gil);
Serial.print(F("\n\tRevision: ")); // Show the rest of the info such as compiled date
Serial.print(Revision_Date+2); // Skip past the century (YYYYMMDD)
Serial.print(LastLine()); // Show the current revision date inserted manually
Serial.print(F("\n\tcompiled: ")); // Show date of compilation
Serial.print(compile_date); // Get it from the compiler data
Serial.print(F("\n\tProcessor clock operating at: "));
float x = F_CPU;
Serial.print((x)/1000000);
Serial.print(F("Mhz."));
Serial.print(F("\n\n")); // Show date of compilation
delay(5000); // To prevent serial freeze in uno
}
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@[ Main Code ]@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
/**************************************************************************************
* *
* The Main Program loop is here. It. dispatches to all Sub Processes! *
* NOTE: Loop delays will Kill militime which needs to be cycled each pass through *
* your loop. On My Arduino D1 R3 (ESP8266)) This loop cycles 65,800 times a second *
* or 0.000015198 of a second per pass. This changes with clocks and processors. *
* *
**************************************************************************************/
void loop() {
// wdt_reset(); // Pet the dog so it does not bite!
// militime() is our pseudo timer for software delays. This must run every pass of the loop
militime(); // Cycle our program timer for events
Mark_It(); // Send pulse to Logic analyzer for timing
/*************************[ Program States ]***************************
** **
** The is the main program loop, it controls all functions **
** Implementing a pseudo state machine. Utilizing states **
** accessed by using this statement: [if (stateM == ?) {} **
** State: **
** 3. Test Timer 3 Just for Demo and in no order **
** 5. Test Timer 5 Just for Demo and in no order **
** 13. Test Timer 13 Just for Demo and in no order **
** 22. Test Timer 22 Just for Demo and in no order **
** 44. Test Timer G Just for Demo and in no order **
** 43. Test Timer I Just for Demo and in no order **
** 42 Test Timer L Just for Demo and in no order **
** 25. Test Timer Case Just and gives us a clean line. **
** 50. ---------------------------- **
** stateCount -1 update PWM channels (Currently 50 states) **
** NOTE: States can be in any order in the source code but will **
** be executed in numerical sequence. To disable a state **
** simply change its state number to something > Max_States **
** **
**********************************************************************/
// Example of a disabled state
if (stateM == 61) {
digitalWrite(TestPin, LOW); // Debug;
}
//****[ Sample of a thread with independent timer ]****
if (stateM == 3) {
// Mark_It();
if (Gil_Timer[A_test_timer] == 0) {
Gil_Timer[A_test_timer] = A_test_time;
Serial.print("#3 "); // Our code controlled by processor and a timer
}
// Mark_It();
}// End of stateM == 3
//***********
if (stateM == 5) {
if (Gil_Timer[B_test_timer] == 0) {
Gil_Timer[B_test_timer] = A_test_time;
Serial.print("#5 ");
}
}// End of stateM == 5
//***********
if (stateM == 13) {
if (Gil_Timer[C_test_timer] == 0) {
Gil_Timer[C_test_timer] = A_test_time;
Serial.print("#13 ");
}
}// End of stateM == 13
//***********
if (stateM == 22) {
if (Gil_Timer[D_test_timer] == 0) {
Gil_Timer[D_test_timer] = A_test_time;
Serial.print("#22 ");
}
}// End of stateM == 22
//***********
if (stateM == 44) {
if (Gil_Timer[E_test_timer] == 0) {
Gil_Timer[E_test_timer] = A_test_time;
Serial.print("#G ");
}
}// End of stateM == 44
//***********
if (stateM == 43) {
if (Gil_Timer[F_test_timer] == 0) {
Gil_Timer[F_test_timer] = A_test_time;
Serial.print("#I ");
}
}// End of stateM == 43
//***********
if (stateM == 42) {
if (Gil_Timer[G_test_timer] == 0) {
Gil_Timer[G_test_timer] = A_test_time;
Serial.print("#L ");
}
}// End of stateM == 43
// Example of a disabled state
if (stateM == 625) {
digitalWrite(TestPin, HIGH); // A single thread is taking about 8uS
}
/**************[ Display Case Temperature ]***************
* State: #25 *
* Update the displays on a regular basis. *
* 200215 *
**********************************************************/
if (stateM == 25) { // Check our thread - Remove if you are not using threads
if (Gil_Timer[Case_Timer] == 0) { // See if we timed out
Gil_Timer[Case_Timer] = Case_Time; // Yes reset timer and process it
Serial.print("\nCase: ");
Mark_It(); // Function code removed
} // End of if timers
} // End of stateM == 2 // Remove if you are not using threads
/*@@@@@@@@@@@@@@@@@@@@@@@@@[ State Machine ]@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@ @@
@@ This is the state count controller for the pseudo state machine. @@
@@ The state machine is counted down. Leave this at the end of @@
@@ the main loop! @@
@@ @@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/
if (stateM > 0) { // Check current state
(stateM = (stateM -1)); // If greater then 0 subtract 1
} else {
(stateM = (Max_State)) ;
} // If zero update count
} // End of Main Loop
/**********************[ Used as part of the Revision number]************************
* *
* This allows us to use the last line number to be used as part of the revision *
* number. It is used only once during setup. Revision = Last Line/YY/MM/DD *
* --us *
********[ THIS MUST BE AT THE END OF THE OF THE PROGRAM! to operate properly] *****/
int LastLine(){
int x = __LINE__; // This returns the current line number
return x+2; // Got to get it correct
} // END OF THE PROGRAM!