Hello, i've been trying to program a scheduler (Protothread) library and i've stubmled upon a weird problem.
The thing is, when I declare the functions that are in my lib in a simple sketch with no classes the code works just fine. But if I use the library it starts doing really weird stuff.
So let me explain step by step.
First I wrote my library that contains this code:
EasyThread.h
#ifndef EasyThread_h
#define EasyThread_h
#include "Arduino.h"
#endif
class EZThread {
public:
// Constructor takes the number of threads
EZThread(int threads);
// Protoype for the new Thread function
void newThread(void (*f)(void), int time);
// Prototype for the schedule function
void schedule();
private:
// Unsigned long pointer that will point to an array containing the timers for every thread
unsigned long *_schedules;
// int pointer that will point to an array containing the intervals for every thread
int *_intervals;
// Function double pointer that will point to an array of function pointers that point to
// the function callback of every thread
void (**_f)();
// Int that stores the number of threads there are
int _schid;
};
EasyThread.cpp
#include "Arduino.h"
#include "EasyThread.h"
// Constructor declaration
EZThread::EZThread(int threads){
// We create the unsigned long array that will store timers for the threads
unsigned long schedules[threads];
// We create the int array that will store the intervals at which every thread should execute
int intervals[threads];
// We create the function pointer array that will point to the callback for every thread.
void (*f[threads])();
//We assign the globals defined in EasyThread.h
_f = f;
_schedules = schedules;
_intervals = intervals;
_schid = 0;
}
// newThread declaration
// This function creates a new Thread
void EZThread::newThread(void (*f)(), int time){
// We store the callback function adress to the function pointer array
*(_f + _schid) = f;
// We store the time interval at which the thread should execute
*(_intervals + _schid) = time;
// We initialize the timer for this thread to 0
*(_schedules + _schid) = (unsigned long) 0;
// We increase the number of threads
_schid++;
}
// schedule declaration
// This function executes the threads that should be executed and resets it's timers
void EZThread::schedule(){
// We store the current time in which we are scheduling
unsigned long now = millis();
// For every thread
for(int i = 0; i < _schid; i++){
// If the scheduling time - the last time that thread executed >= interval at it should execute
if(now - *(_schedules + i) >= *(_intervals + i)){
// We call to the callback function stored in the function pointer array
(*(_f + i))();
// We set the internal timer to the time we started scheduling
*(_schedules + i) = now;
}
}
}
Then, if I try to use my library with a simple sketch like this:
#include <EasyThread.h>
//Constructor with one thread
EZThread scheduler(1);
void setup() {
//Start serial at 9600
Serial.begin(9600);
//Create new thread
scheduler.newThread(printNice, 1000);
}
// Thread callback
void printNice(){
Serial.println("Nice!");
}
void loop() {
//Call to the scheduler
scheduler.schedule();
}
I get this serial output:
12:28:14.681 ->
It should print "Nice" every second.
Now I thought, well there must be some error and the printNice callback is not executing so I put a Serial.println("Setup start"); just after the Serial.begin(9600); like this:
...
//New setup
void setup() {
//Start serial at 9600
Serial.begin(9600);
//New print
Serial.println("Setup start");
//Create new thread
scheduler.newThread(printNice, 1000);
}
...
I get this output:
Random gibberish and invalid characters. So somehow, and I don't know how, my library is messing with the serial output.
I figured out that if I remove the scheduler.schedule(); in the void loop(){} then the Serial.println("Setup start"); works just fine and outputs:
12:46:52.024 -> Setup start
I tried adding Serial.flush(); and messing around with the code only to get even weirder stuff on the serial like the timestamp disappearing and only printing the two first chars of the Serial.println("Setup start"); or even printing in the same line and timestamp over and over.
I ended up with a code that seemed to be resseting itself as the millis() value was going back to zero and the setup was being executed again. I also tried seeing if I was somehow using too much memory and that could cause a stack overflow and a reset of the program. But using GitHub - McNeight/MemoryFree: Arduino MemoryFree library. Hosting it on github for easy access. to check the remaining available memory prints:
13:38:11.961 -> Start
13:38:11.961 -> freeMemory()=1820
13:38:11.961 -> 7
13:38:11.961 -> freeMemory()=1820
13:38:12.992 -> 1030
13:38:12.992 -> freeMemory()=1820
13:38:13.990 -> 2036
So I don't think i'm running out of memory.
Then I finally tried to move the definitions and everything from the library to the sketch, and then it worked:
Sketch:
#include <MemoryFree.h>
#define LED_PIN 12
const int threads = 1;
unsigned long *_schedules;
int *_intervals;
void (**_f)();
int _schid;
void setup() {
unsigned long schedules[threads];
int intervals[threads];
void (*f[threads])();
_f = f;
_schedules = schedules;
_intervals = intervals;
_schid = 0;
Serial.begin(9600);
Serial.println("Start");
newThread(printNice, 1000);
Serial.print("freeMemory()=");
Serial.println(freeMemory());
}
void printNice(){
Serial.println("Nice!");
}
void loop() {
schedule();
Serial.print("freeMemory()=");
Serial.println(freeMemory());
}
void newThread(void (*f)(), int time){
*(_f + _schid) = f;
*(_intervals + _schid) = time;
*(_schedules + _schid) = (unsigned long) 0;
_schid++;
}
void schedule(){
unsigned long now = millis();
Serial.println(now);
Serial.flush();
for(int i = 0; i < _schid; i++){
if(now - *(_schedules + i) >= *(_intervals + i)){
(*(_f + i))();
*(_schedules + i) = now;
}
}
}
Output:
Start
14:01:54.313 -> freeMemory()=1804
14:01:54.313 -> 0
14:01:54.313 -> freeMemory()=1804
....................
14:01:55.302 -> 975
14:01:55.336 -> freeMemory()=1804
14:01:55.336 -> 1001
14:01:55.336 -> Nice!
It's just the same code, but not in the library.
Im really confused and don't know what i'm missing. I would really appreciate some help or guidance on how to solve this problem.
Thanks in advance.
Some system information:
I'm running this in an Arduino UNO, but tested also in an Arduino Nano and got the same results.
Arduino IDE 1.8.13
Windows x64 and I'm using a USB 2.0 (I think)