The tasks I programmed to run aren't running

Hi there

I am currently facing an issue with the code I made for an OS simulator for Arduino. I had programmed a few tasks to run as a part of a sample app I made for the platform to test its working. A few tasks seemed to work well and the others didn’t. I know that I have programmed them correctly (despite a few errors I was able to correct due to the compiler’s pointing them out while compiling). Here’s the code from the files I doubt:

  1. MCUOS_Task.h - the header containing the tasks I said
#define STOPPED 0
#define RUNNING 1
#define PAUSED 2



#define SELECT 724 // 'select' button on shield
 
#ifndef MCUOS_Tasks_h
#define MCUOS_Tasks_h

#include "Arduino.h"
#include "MCUOS_Requests.h"
#include "LiquidCrystal.h"

class Task
{
// abstarct class for tasks
	public:
		int status;
		String task_name;
		int priority;
		virtual void run_task() = 0 ;
		String getName(){
			return(task_name);
		}
};

class DisplayTask: public Task
{
	public:
		String input_string = "";
		String task_name = "mcuos.display_task";
		LiquidCrystal lcd = LiquidCrystal(8,9,4,5,6,7);
		int status = PAUSED;
		int priority = 2;
		void set_up(){
			(this->lcd).begin(16, 2);
		}
		void run_task(){
			Serial.println(task_name);
			(this->lcd).clear();
			string_request.request();
			if(Input_String == false){
				(this->lcd).print(input_string);
			}
			delay(1000);
		}		
}dtask;



class Button_Check: public Task
{
	public:
		String task_name = "mcuos.button_check";
		bool enter_app = false;
		bool exit_app = true;
		int status = PAUSED;
		int priority = 2;
		int button_value; 
		void run_task(){
			Serial.println(task_name);
			button_value = analogRead(A0);
			delay(1000);
			if(button_value == SELECT){
				Serial.println("SELECTED!");
				enter_app = !enter_app;
				exit_app = !exit_app;
			}
			delay(1000);
		}
}btask;

class Null_Task:public Task
{
	public:
		int status = PAUSED;
		int priority = 2;
		String task_name = "mcuos.null_task";
		void run_task(){
			Serial.println("Nothing here");
		}
}Null;

#endif
  1. MCUOS_Scheduler.h - the scheduler(mainly supposed to set task status and task priority) and the registry (list of apps and the commands related to it, in my way :sweat_smile:) .
#ifndef MCUOS_Scheduler_h
#define MCUOS_Scheduler_h

#include "Arduino.h"
#include "MCUOS_Task.h"

struct Tasklist{
	int size = 0;
	void add_task(Task &task);
	void remove_task(String taskname);
	void list_tasks();
	void run_tasks();
	Task *tasklist[8];
}registry;

void Tasklist::add_task(Task &task){
		if(tasklist[8] != &Null){
			tasklist[size] = &task;
			size++;
		} else {
			Serial.println("No more tasks can be added");
		}
}
void Tasklist::remove_task(String taskname){
	for(int i = 0; i <= size; i++){
		if (tasklist[i]->task_name == taskname){
			tasklist[i] = &Null;
			for(int j = i; j < size; j++){
				tasklist[j] = tasklist[j + 1];
				tasklist[j + 1] = &Null;
			}
		}
	}
} 
void Tasklist::list_tasks(){
	for(int k = 0; k <= size; k++){
		Serial.println(tasklist[k]->getName());
		delay(1000);
	}
}

void Tasklist::run_tasks(){
	Serial.println("First Priority");
	Serial.println("_______________________");
	for(int task_number = 0; task_number <= size; task_number++){
		if(tasklist[task_number]->priority == 1){
			switch(tasklist[task_number]->status){
				case RUNNING:
					tasklist[task_number]->run_task();
					break;
				case PAUSED:
					Serial.println("Task paused");
					break;
				case STOPPED:
					remove_task(tasklist[task_number]->getName());
			}
		}
	}
	Serial.println("_____________");
	Serial.println("Next priority");
	Serial.println("____________");
	for(int task_number_next = 0; task_number_next <= size; task_number_next++){
		if(tasklist[task_number_next]->priority == 1){
			switch(tasklist[task_number_next]->status){
				case PAUSED:
					Serial.println("Task paused");
					break;
				case STOPPED:
					remove_task(tasklist[task_number_next]->getName());
			}
		}
	}
}

class Scheduler
{
	public:
		void set_status(String taskname, int status);
		void set_priority(String taskname, int priority);
};

void Scheduler::set_status(String taskname, int status){
	for(int counter = 0; counter <= registry.size; counter++){
		if(registry.tasklist[counter]->task_name == taskname){
			registry.tasklist[counter]->status = status;
		}
	}
}

void Scheduler::set_priority(String taskname, int priority){
	for(int counter = 0; counter <= registry.size; counter++){
		if(registry.tasklist[counter]->task_name == taskname){
			registry.tasklist[counter]->priority = priority;
		}
	}
}

extern Scheduler scheduler;

#endif
  1. Test_App.h - the app I made
#ifndef SampleApp_h
#define SampleApp_h

#include "Arduino.h"
#include "MCUOS_AppDev.h"
#include "MCUOS_Scheduler.h"
#include "MCUOS_Requests.h"

class Test_App: public App{
	public:
		String appname = "sample.mcuos.silly_string_lcd"; //app name
		int app_tasks_no = 2;
		void  load();
		void run();
		bool app_exit_status = false;
};

void  Test_App::load(){
	registry.add_task(dtask);
	registry.add_task(btask);
	scheduler.set_status(dtask.task_name, RUNNING);
	scheduler.set_priority(dtask.task_name, 1);
	scheduler.set_status(btask.task_name, RUNNING);
	scheduler.set_priority(btask.task_name, 1);
	Serial.println("Loading app...");
	btask.enter_app = true;
	btask.exit_app = false;
	dtask.set_up();
	Serial.println("App components:");
	Serial.println(registry.tasklist[0]->getName());
	Serial.println(registry.tasklist[1]->getName());
}
void Test_App::run(){
 	while(app_exit_status == false){
		registry.run_tasks();
		Serial.println("I have run the tasks");
		if((btask.exit_app == true)&&(Input_String == false)){
			Serial.println("Oops !");
			scheduler.set_status(dtask.task_name, STOPPED);
			scheduler.set_status(btask.task_name, STOPPED);
			app_exit_status = !app_exit_status;
		} else {	
			string_request.onRequest("Hello", &dtask.input_string);
			registry.run_tasks();
			Serial.println("Run Again");
			scheduler.set_status(dtask.task_name, PAUSED);
		}
		delay(1000);
	}
}


Test_App app;

#endif
  1. MCUOS_Requests.h - handles data requests from tasks
#ifndef Requests_h
#define Requests_h

#include "Arduino.h"

bool Input_String;  // request switch to get input string

template <typename request_type>
class RequestHandler
{
	//a class to handle data requests from tasks
	public:
		virtual void onRequest(request_type data, request_type *location) = 0;
		virtual void request();
};

class RequestString: public RequestHandler<String>
{
	public:
		bool *request_switch;
		RequestString(bool switch_location){
			request_switch = &switch_location;
		}
		void request(){
			*request_switch = true;
		}
		void onRequest(String data, String *location){
			if(Input_String == true){
				location = &data;
			                *request_switch = !(*request_switch);
			}
		}
};

extern RequestString string_request(Input_String);			 
			
#endif

The problem is that in the test app, when I called the run_task() function of a null task, it worked while the others didn’t work ( that was when I had assigned 6 NullTask instances to the Task *tasklist[] array in MCUOS_Scheduler.h; the ones I have posted here doesn’t have any such editings).

It will be great if anybody could help me work it out.

Also the test sketch (AppTest.ino):

#include <MCUOS_AppDev.h>
#include <MCUOS_Requests.h>
#include <MCUOS_Scheduler.h>
#include <MCUOS_Task.h>
#include <TestApp.h>



void setup() {
  Serial.begin(9600);
  app.load();
  app.run();  
  delay(100);
}

void loop() {
  // put your main code here, to run repeatedly:

}

generally, you want to only do setup stuff in the setup() function, and do your real work during loop()

for multiple tasks, i’d create counter that loops 1- and call the next task each time through the loop. something like:


int numTasks = 3;
int taskNum = 0;

void loop() {

if (++taskNum > numTasks) { taskNum = 0; }
switch (taskNum) {
case 0: taskOne(); break;
case 1: taskTwo(); break;
case 2: taskThree(); break;
}

}


or, just do all 3 tasks each time loop() is called. whatever. the point is … use loop(), and make it not take too long, and you’ll be good.

the thing is, some boards (like the ESP boards) get unhappy if your code runs for very long; they have a watchdog timer that restarts the board if too long has gone between checks. you can feed the watchdog, but it’s better to break the functionality into smaller chunks and call them during loop()

also, things like serial input/output don’t happen while your code is running; just another reason to fit more closely into the loop() way of structuring your program

Well, the point is that some part of the serial output is seen. I had also added a few lines of code to print the task names and had added a delay of 1 second(in another version of AppTest.ino), but nothing came of it. Out of the tasks derived from Task class, Null_Task seemed to work.

Well, I am not planning to do something like that, as I want my code to be able to do multitasking.

I haven’t understood that well. Would you mind giving me an example of how that happens on an Arduino ? Mine is an Uno Rev 3.

The problem that I am facing is not only the serial output. My LCD shield isn’t showing up the message I wanted it to show up. Will that be solved if I add loop function as the virtual function to run after load (instead of the run function) in the MCUOS_AppDev.h file? (given below) :

#ifndef MCUOS_AppDev_h
#define MCUOS_AppDev_h

/* Header to develop an app.
Partailly made similar to JohnsProject/Arduios */

#include "Arduino.h"
#include "MCUOS_Task.h"

class App
{
	public:
		virtual void load() = 0;   // setting up the app (esp. assigning values to app variables)
		virtual void run() = 0 ; // runs the app
};

#endif

I want to make the code sufficiently complex but simple for use by the rest of the coding community. The complexity that I desire in my code is its real beauty, as far as I am concerned :slightly_smiling_face:

Sorry, I am not familiar with this MCUOS thing, so I can’t help you. I guess the rules are completely different when that is injected into the solution. I think you’ll probably have to go to whoever developed that for guidance.

Re the watchdog thing, I think so long as you stay away from the ESP boards, it probably won’t be a problem. Then again, this MCUOS thing you are using may have something in there behind the scenes to handle long running tasks WRT a watchdog timer.

After 35+ years of programming, my experience is that adding complexity only makes things harder to debug, more brittle, and much more error-prone. To me, the beauty is in how simple it is. My mantra: a program should be ‘intuitively obvious upon casual inspection’. But that is just me.

3 Likes

I understand that simplicity is beauty, but to me the complexity is the beauty in this case.

Well, it’s me who made it :wink: . I haven’t added any watchdog timer etc. to my code, so perhaps that’s the problem.

The solution I found for the problem related to the Button_Check task is to use a pin change interrupt, but I have no idea on how to implement it (I know that I can use NicoHood’s PinChangeInterrupt library, but I have to plan on how I can use it).

The DisplayTask is having a bit of problem: that’s my place of concern as the required output is not showing up on the LCD screen and on the serial monitor. Also, when I ran the program after a bit of editing ( I added a line to print “Received request” in the onRequest function and “Sent request” in request function in the RequestString class in MCUOS_Requests.h when the tasks with these are run), I only saw “Received request” being displayed. I wonder where has the rest of the tasks have gone ! :laughing:

My aim currently is to develop a platform to develop apps for the specific device the user makes and then to make the UI.

I somehow got hold of the watchdog timer on the Arduino.

I set the timer to an 8-second timeout, deleted all debug messages and let the program run. It showed no signs of being reset by the timer. Further, when the debug messages were there, the app only showed the “Received request” when it entered the condition check, but there was no sign of the output being shown on the LCD screen. Further, when I used the registry.list_tasks() function, it returned the strange characters one would see when he/she opens an executable file using Notepad :laughing:.

I have posted my project on Github :
MCUOS

Here, I haven’t removed the debug messages so that users can really see what’s wrong and how to correct things.

Hey, would adding inline functions reduce the havoc ?

I tried it. Still no solution. The tasks seem to be running in the background, but neither does it stop on a button press (this applies to the previous tries as well) nor does it print a “hello” on the LCD :cry:

You could switch to an ESP32 that has a REAL multitasking OS and the processor / memory resources to support it.

Well, the thing is that situations pertaining to the spread of COVID-19 are worsening in India a bit. Also, I have no electronics store near me that sells microcontroller boards. Hence, I’d have to depend on what I have to work up to my aim. Online shopping is still available, but considering the risks of the spread of COVID-19 from the person who delivers the board, I think it would be better for me to do it all with what I have.

What I mainly require is contributions from experts in this field- contributions as corrections and such.

Are you trying to set up multi tasking for yourself or as something for “the world”?

-jim lee

For “the whole world” :laughing: … I am just making a library to add an OS simulator to small embedded systems projects. Many people may prefer FreeRTOS, but I’d just like to stand out as a unique thing by giving the project a bit of UI as well :wink: .

@jimLee , do you think this can be resolved ? I haven’t been able to try resolving problems today, so perhaps if you could just lend me a helping hand maybe I can have a small beginning to the debugging part.

I can’t make heads or tails over what you are attempting. Its too complicated for my simple brain. I have my own multi tasking scheme that is extremely simple, but it covers all my needs.

-jim lee

1 Like

That’s lovable ! I’d like to see that project of yours so I may learn what’s probably wrong with mine :slight_smile:

My main aim is not only to enable multitasking on Arduino but also to make an OS simulator for it so those wishing to make DIY - smartwatches and stuff may use it .

LIke this?

http://www.leftcoast.biz/iWeb/Left_Coast/Home_made_cellpone.html

Go grab LC_baseTools from the IDE library manager. Run the blinkWithoutDelay_2 example. And you will see a very simple example using my multi threading technique. The cellphone (above) has what is effectively a sketch swapping OS.

-jim lee

1 Like

Not only about swapping sketches, sir :laughing:… I am looking forward to make apps as well and a platform for other users to make apps on their own (but yes, maybe I could imagine that the sketches to be swapped are apps to be used in different use cases; the only downside is that it defies my notion of an OS simulator).

And yes, your project looks cool… the only thing is that I have no provisions to get those things used (and I am not a guy with a great deal of knowledge in electronics[let alone a bit of robotics] , but yes I love a lot of programming and stuff), so I can’t make what you’ve made.

FreeRTOS is definitely superb, but it is an RTOS and hence doesn’t suit my notion (that doesn’t mean I can’t think of using that to make my OS simulator).

The only last thing that came up in my mind, @jimLee, is to scrap the present code (and delete the Github repo I made) and take a deep breath and restart the programming.

@jimLee I feel like I have got what I need.... I have posted it on Github in the same name in which I began by project (also, I have written up a long and (perhaps) stupid wiki as well :sweat_smile:) Here's the link : GitHub - Coder-X15/MCUOS

Yeah, I'm nearly there (I mean, things have started to work) - please check out the link to the Github repo I made and you shall see that I have made a lot of changes and to me things are just great!