[SOLVED] set struct size from application code

Hi,

I want to declare or initialize a struct that is global in source file from Arduino sketch.

Here's my code:

  1. task manager library which should run the sequence + timing for all project functions of other libraries
#ifndef task_manager_h
#define task_manager_h

#include <inttypes.h>
#include <Arduino.h>
#include <SPI.h>
#include <string.h>
#include <avr/pgmspace.h>
#include <avr/io.h>

// definitions
#define SYSTEM_TASKS 1

// variables
static uint8_t task_counter;
static uint8_t task_counts;

// structs
typedef struct {
    uint32_t task_st;
    uint32_t task_pr;
    bool task_start_lock;
    bool task_full_lock;
}TASK_MANAGER;

// functions
void task_manager_init(uint8_t tasks);
void task_manager(void);

#endif
#include "task_manager.h"

////////////////////////////////////////////////////////////////////////////////////////////
// global variables
TASK_MANAGER task[SYSTEM_TASKS] = {0};
TASK_MANAGER *task_ptr;

////////////////////////////////////////////////////////////////////////////////////////////
// main functions definitions
void task_manager_init(uint8_t tasks){
	Serial.println("system init");
	task_ptr = &task[0];
	task_counts = tasks;
	task_counter = 0;
}


void task_manager(void){
	if(task_counter < task_counts){
		if(task_ptr[task_counter].task_start_lock){
			if(millis() - task_ptr[task_counter].task_st >= task_ptr[task_counter].task_pr){
				task_ptr[task_counter].task_start_lock = 0;
				task_counter++;
			}
		}
	} else{ task_counter = 0; }
}

My question now, is that how to set the size of the struct array from Arduino sketch ?

TASK_MANAGER task[SYSTEM_TASKS] = {0};

In this case I must set the size in the header file, but I want to set the size from the Arduino sketch.


Another question I want to ask about the struct scope to other libraries, is my setting for the struct good ? It's working for me with this setting and I ran a function from another libraries with this setting.

#ifndef sensors_modules_h
#define sensors_modules_h

#include <inttypes.h>
#include <Arduino.h>
#include <SPI.h>
#include <string.h>
#include <avr/pgmspace.h>
#include <avr/io.h>

// functions
void sensors_modules_init(void);
void blink_led(uint32_t time, uint8_t task_number);

#endif
#include <Arduino.h>
#include <SPI.h>
#include "task_manager.h"
#include "sensors_modules.h"

////////////////////////////////////////////////////////////////////////////////////////////
// variables
bool led_state;
extern TASK_MANAGER *task_ptr;

////////////////////////////////////////////////////////////////////////////////////////////
// main functions definitions
void sensors_modules_init(void){
	pinMode(13,OUTPUT);
}

void blink_led(uint32_t time, uint8_t task_number){
	if(!task_ptr[task_number].task_start_lock){
		if(!led_state){ led_state = 1; }else{ led_state = 0; }
		task_ptr[task_number].task_pr = time;
		task_ptr[task_number].task_st = millis();
		task_ptr[task_number].task_start_lock = 1;
		digitalWrite(13,led_state);
	}
}

Simple answer, and this is asked a thousand times, you don't. Your task_manager.cpp is compiled without ANY knowledge of your main sketch in which you include it.

So you have to set it in task_manager.h/cpp or define the sctruct in you main sketch and point the library to it.

Last option is to rewrite the whole library to be object oriented and define 'SYSTEM_TASKS'-number of objects in your main sketch.

Linked list?

That's how I manage background tasks. Each is a link for a linked list. Each time (typically through the loop() when the user calls idle() ) The processor just runs down the list calling everyone's doAction() method. Its super simple.

-jim lee

Yeah, linked list as nice as well. But if you go that way I would say wrapping it in OO logic does make things simpler :slight_smile:

I set up mine for doing simple things in the background like blinking LEDs. Pretty quickly I found that about 95% of my code now runs on that background list. It is the best "do many things without delay()" method I've ever seen.

-jim lee

To answer OP's first question ... Yes the array size can be set at run-time if you use dynamic allocation. See: new and delete Operators in C++ For Dynamic Memory - GeeksforGeeks

septillion:
Simple answer, and this is asked a thousand times, you don't. Your task_manager.cpp is compiled without ANY knowledge of your main sketch in which you include it.

So you have to set it in task_manager.h/cpp or define the struct in you main sketch and point the library to it.

Last option is to rewrite the whole library to be object oriented and define 'SYSTEM_TASKS'-number of objects in your main sketch.

Yes, I learned stuff in C++ which are setters and getters.
Another wonderful way is to use the constructor of the library, that's brilliant ! Just pass the initialize values to the private variables and the code is good to go.
But I want to know how this is available in C. My intention actually is to experience as much approaches as I can in C, then getting into C++ would be meaningful.

jimLee:
Linked list?

That's how I manage background tasks. Each is a link for a linked list. Each time (typically through the loop() when the user calls idle() ) The processor just runs down the list calling everyone's doAction() method. Its super simple.

-jim lee

Is there a library for this method, or you prefer I get into the principal of linked lists and try to build that in my task manager ?

gfvalvo:
To answer OP's first question ... Yes the array size can be set at run-time if you use dynamic allocation. See: new and delete Operators in C++ For Dynamic Memory - GeeksforGeeks

I want you to know that I want this struct to be shared with other libraries too.
So in this way I have to declare the struct in the header file of task_manager.h

Then I defined it with setting the size of struct array.

I don't understand the question. But, I think my answer remains the same. If you want to set an array size at run time, you must use dynamic allocation.

wolfrose:
Is there a library for this method, or you prefer I get into the principal of linked lists and try to build that in my task manager ?

LC_baseTools from your friendly IDE library manager has a link list set of classes in there. link list, double linked list, queue, stack. Link list base classes. It also has the idler class you can see how that was put together.

-jim lee

gfvalvo:
I don't understand the question. But, I think my answer remains the same. If you want to set an array size at run time, you must use dynamic allocation.

I used this now and it compiled without problems.

void task_manager_init(uint8_t tasks){
	Serial.println("system init");
	task_ptr = (TASK_MANAGER*)calloc(tasks,sizeof(TASK_MANAGER));
	task_counts = tasks;
	task_counter = 0;
}

Thanks for the help.

Another question now, would this method use high RAM; e.g. cause a system high overhead ? Is my method of using a struct good ? are there better more simpler methods and use less RAM ?

jimLee:
LC_baseTools from your friendly IDE library manager has a link list set of classes in there. link list, double linked list, queue, stack. Link list base classes. It also has the idler class you can see how that was put together.

-jim lee

Thanks :slight_smile:
The one thing I'm thinking about now is that using the task_manager library would cause me a lot of work putting the struct elements in each function of each library; for example, a normal function like:

void lcd128x64_data(uint8_t data, uint8_t tk_no){
	if(!task_ptr[tk_no].task_start_lock){
		SPI.transfer(DATA_MSK);		SPI.transfer(data & 0xf0);
		SPI.transfer(data << 4);
		task_ptr[tk_no].task_start_lock = 1;
		task_ptr[tk_no].task_pr = 72;
		task_ptr[tk_no].task_st = micros();		
	}
}

The routine for each function is the following:

  1. I have to check that the function isn't already locked for delay period
  2. If it isn't, then function executes
  3. locking the function
  4. setting the delay period
  5. Taking time snap
    It's ok I'm not so much complaining about all the steps I have to do, but what I want to be sure of is this method I'm following is practical and accepted in programming community, so I know I'm moving in the right path as everyone else.

My solution methods:

////////////////////////////////////////////////////////////////////////////////////////////////
// allocating multitasking nodes for system tasks for dynamic with calloc and static with normal
// struct object initialize

# First declare task_manager struct
typedef struct {
    uint32_t task_st;
    uint32_t task_pr;
    bool task_lock;
}TASK_MANAGER;

# Then, you have two ways of initializing struct objects:

////////////////////////////////////////
// 1: use typedef stuct object array
// global
TASK_MANAGER task[SYSTEM_TASKS] = {0};
TASK_MANAGER *task_ptr;

// in init function
task_ptr = &task[0];

// in other source files we usue external variables
extern TASK_MANAGER *task_ptr;
////////////////////////////////////////
// 2: use calloc
// global
TASK_MANAGER *task_ptr;

// in init function
task_ptr = (TASK_MANAGER*)calloc(tasks,sizeof(TASK_MANAGER));

// in other source files we usue external variables
extern TASK_MANAGER *task_ptr;

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.