-
-
Notifications
You must be signed in to change notification settings - Fork 1
API Documentation
jescore is a C/C++ library built on top of FreeRTOS. It is developed in the PlatformIO development environment and can be easily integrated into other projects based on that framework:
; platformio.ini:
[env:my_board]
...
lib_deps =
https://github.com/jesdev-io/jescore ; as repo
; or
jescore ; as PlatformIO registry libraryRight now, jescore supports the ESP32 platform with the Arduino FW and the STM32 platform with the STM32Cube HAL. The following devices were tested:
- ESP32-WROOM
- ESP32-C3
- ESP32-S3
- ESP32-WROVER
- STM32 Nucleo-L476RG
- STM32 Nucleo-L432KC
- STM32 Nucleo-G431KB
- STM32 Nucleo-H753ZI
- STM32U585 (Arduino Uno Q)
Theoretically, all STM and ESP boards are supported, but some of them need hardware specific macros in place. Please refer to the guide on adding board support.
You can now import jescore:
// your_project/src/main.cpp
...
#include <jescore.h>
void setup(){
}
void loop(){
}// your_project/src/main.c
...
#include <jescore.h>
int main(){
}For other IDEs, you can clone the repo with git clone https://github.com/jesdev-io/jescore and append the libraries to your Makefile like you would with any other source files.
jescore is all about the user, but you still have to adhere to its rules if you want to use it. For this reason, all functions you intend to run with it have to have the mentioned signature. No return, a single void* as parameter, similar to FreeRTOS (you can guess why). However, this parameter p is always loaded with the handle to the job which belongs to the function, as it may be useful from within the function, if you know what you are doing, see Backend documentation. But for most cases, especially for simple jobs, this can be ignored.
Brief: Start the core and all of its abilities.
Returns: Status. Returnse_err_no_errin case of successful launch.
Use jes_init() in your setup() or any other initialization code. jescore needs dynamic memory and will tell you if not enough is available. Be sure to always check the return value of jes_err_t! The error types are explained in jes_err_t.
Brief: Let the core take over your program flow.
This activates the task scheduler of FreeRTOS and will never return! Call this at the end of your main() function. This is needed because on the STM32, the program entry is main(), which is not a task. On the ESP32 with the Arduino FW, setup() and loop() already operate with a running scheduler, similar to the ESP-IDF app_main().
jes_err_t jes_register_job(const char* name, uint32_t mem_size, uint8_t priority, void (*function)(void* p), uint8_t is_loop, uint8_t is_singleton)
Brief: Add a job (function block) to the list of all known jobs.
name: Name of job. Can't be longer thanMAX_JOB_NAME_LEN_BYTE.
mem_size: Dynamic memory size for job.
priority: Priority of the job (1 is highest).
function: Function to run when the job is called. Has to be of signaturevoid my_func(void* p).
is_loop: Flag which describes the lifetime of the job.
is_singleton: Flag which describes if only one instance of this job can run at a time (1 = singleton, 0 = multiple instances allowed). Returns: Status. Returnse_err_no_errin case of successful registration.
Use jes_register_job() to make one of your functions known to jescore. By registering it, you append it to the core's list of known jobs and it gets passed to the task scheduler of FreeRTOS. You can give your function a name, which can later be called by the CLI, see CLI documentation, if you want that. Be sure that your name does not contain whitespaces, because jescore uses those to determine additional arguments in your CLI calls. It also should not be longer than MAX_JOB_NAME_LEN_BYTE. Start with a generous memory size, as FreeRTOS keeps a limited stack for each of your registered functions. The size is determined by the amount of local or static variables in your function and the depth of its calls, so estimating a good memory size is quite hard. Start larger than you might initially think, and then lower it over time if everything runs as expected. You also have to specify whether your function has an infinite loop or ends at some point. Additionally, you can specify if the job should be a singleton (only one instance at a time) or can have multiple concurrent instances. Be sure to set these truthfully, as it may affect job behavior.
Brief: Remove a job from the job list and free its resources.
name: Name of job to unregister. Returns: Status. Returnse_err_no_errif OK. Note: Will not unregister a job that has active instances running. Frees the job struct, its queue, and semaphore.
Brief: Start a registered job.
name: String name of job as set injes_register_job().
Returns: Status. Returnse_err_no_errin case of successful launch.
To launch a job, use jes_launch_job() and give it the name of the job you previously registered. If you find this step redundant, see jes_register_and_launch_job().
Brief: Start a registered job with arguments.
name: String name of job as set injes_register_job().
args: String of arguments delimited by whitespaces. Can't be longer thanMAX_JOB_ARGS_LEN_BYTE.
Returns: Status. Returnse_err_no_errin case of successful launch.
Launch a job with arguments. This is useful if you have runtime-dependent launch sequences or want to mimic a CLI call. This makes jescore powerful: during development, the CLI can be used to trigger actions on the MCU. If hardware is added, its interrupt or callback can trigger the exact same job with arguments that the CLI would perform before the input hardware was added.
jes_err_t jes_register_and_launch_job(const char* name, uint32_t mem_size, uint8_t priority, void (*function)(void* p), uint8_t is_loop, uint8_t is_singleton)
Brief: Add a job (function block) to the list of all known jobs and launch it.
name: Name of job. Can't be longer thanMAX_JOB_NAME_LEN_BYTE.
mem_size: Dynamic memory size for job.
priority: Priority of the job (1 is highest).
function: Function to run when the job is called. has to be of signaturevoid my_func(void* p).
is_loop: flag which describes the lifetime of the job.
is_singleton: flag which describes if only one instance of this job can run at a time. Returns: Status. Returnse_err_no_errin case of successful launch.
Use this function to combine the two above. This makes sense for all jobs which get started during the boot process of your project.
Brief: Set the field
argsof the calling job.
s: String to insert intoargsfield.
Returns: status,e_err_no_errif OK.
Each job contains a field of fixed memory size, called args. Its size depends on MAX_JOB_ARGS_LEN_BYTE. It is normally used to store the CLI arguments passed to that particular job, but there might be cases in which this array may be loaded by a program snippet rather than a user handling the CLI. Use this function to put an arbitrary C-string into that buffer. If the string is too long, the error return will tell you that.
Brief: Get the field
argsof the calling job.
Returns: Pointer toargsfield of the job.
This function returns a pointer to the args buffer of the calling job. Since this function is intended to be called from within a job, the memory of the job struct persists, which makes a copy of the arg string redundant. If, however, something goes wrong, this function will return NULL. Use this function to retrieve anything that has been put into the args buffer by either jes_job_set_args() or the CLI.
Brief: Get the next arg from the args field.
Return: Next arg delimited by a whitespace.
Note: Use this in an arg-parsing loop.
This is a practical way of parsing args in a loop. It returns args until none are left, then returning NULL.
Brief: Check if two args are the same.
Param: arg Input arg fromjes_job_get_args()orjes_job_arg_next()
Param: name Name of arg to compare.
Return: 1 if matching, 0 if not.
Note: Use this in an arg-parsing loop.
This function exists to check args against each other.
Brief: Set the field
optionalof the job.
p: Arbitrary reference to parameter.
Returns: status,e_err_no_errif OK.
For any arbitrary argument you might pass to a job, the field param can be used. It is declared as void*, so you can pass anything to it. Keep in mind that you
- need to have persistent storage for the content of the pointer, because
jescoredoes not copy it - make sure that you cast it back into the original type correctly.
In most use-cases, you might pass the reference to a struct to this parameter, because a struct has addressable fields as opposed to an "arbitrary" array length, which means that a single pointer is sufficient to describe it completely.
Brief: Get the field
paramof the calling job.
Returns: Pointer toparamfield of the job.
Use this function to retrieve whatever you put into the param field in your job with jes_job_set_param(). Be careful, this value might be NULL if you never called jes_job_set_param() with anything other than NULL.
Brief: Get the field
errorof a given job.
Param: job_name Name of the job.
Return: Stored error.
Call this function to get the last error that occured in a job. An error can be set by an illegal run-time operation that the core caught or a custom error caused by jes_throw_error().
Brief: Get the first error that of all jobs.
Return: Error of first job that has one.
Note: Returnse_err_no_errin case that every job is error-free.
Note: Use this function to quickly spot if the program is error free.
This function will iterate over all registered jobs and return on the first associated error it finds. This function is useful for checking if all processes are running error free at a given point in time.
Brief: Throw an error that is registered in the core.
Param: e Error to throw.
Note: This is useful to let other jobs or the core know when a job exits based on a user condition.
With this function an error can be thrown manually in a user program if a given condition is met. This will then set the error field of the evoking job.
Brief: Notify a job with an optional message.
Param:name: Name of job which should be notified. Param:notification: Optional pointer to notification value. Returns: status,e_err_no_errif OK.
Call this function to notify a different job. For a job to accept notifications, it has to be waiting for one, see jes_wait_for_notification(). If you don't want to send a notification value, pass NULL to notification.
Brief: Notify a job with an optional message.
Param:name: Name of job which should be notified. Param:notification: Optional pointer to notification value.
Returns: status,e_err_no_errif OK.
Same as jes_notify_job() but for interrupt service routines. Call this from within an interrupt.
Brief: Wait for an incoming message.
Returns: The optional notification value.
This stalls the calling task but is not blocking. The task is simply suspended from execution until a notification arrives. From there on, the task will resume, regardless of the contents of the optional return notification. However, it can be useful to tell the waiting task what to do next.
Brief: Delay a job in milliseconds. Param: ms Milliseconds of delay. Note: Timing is handled by FreeRTOS. This function should be called from within a job context.
Brief: Print a formatted string with job identification.
Note: This macro automatically prefixes the output with the calling job's name. Use it likeprintf(). Example:jes_print("Hello from job!\n")will print[job_name]: Hello from job!
Brief: Print a formatted string with a specific job's name as prefix.
Param: pj Pointer to a job_struct_t. Param: format Format string (like printf). Note: This macro prefixes the output with the specified job's name. Example:jes_print_pj(my_job, "Status: %d\n", status)
This is the standard return type of most jescore functions. They describe if a function call was successful or failed in one or more ways. The errors are shown below:
typedef enum jes_err{
e_err_no_err,
e_err_mem_null,
e_err_is_zero,
e_err_param,
e_err_peripheral_block,
e_err_core_fail,
e_err_duplicate,
e_err_too_long,
e_err_unknown_job,
e_err_leading_whitespace,
e_err_prohibited,
e_err_driver_fail,
e_err_NUM_ERR
}jes_err_t;
e_err_no_err: No error, everything worked.
e_err_mem_null: Dynamic memory operation returnedNULL, out of memory.
e_err_is_zero: Given value is 0 orNULLor a value that should never be 0 is 0 regardless.
e_err_param: An incorrect parameter was given.
e_err_peripheral_block: Calling a hardware peripheral failed because it is busy.
e_err_core_fail: The core dealt with a hard fault.
e_err_duplicate: A job has been registered twice by accident or a job which can only have one launch at a time has been launched twice.
e_err_too_long: A given string argument is too long. Can be caused by both API and CLI usage.
e_err_unknown_job: An unregistered job tried to launch. This is typical for typing errors.
e_err_leading_whitespace: CLI only. A command with a leading whitespace was sent. Whitespaces are used to separate arguments, so this can cause issues.
e_err_prohibited: You tried to call a core job by the API. This is forbidden, as it may crash the system.e_err_driver_fail: A (hardware) driver failure from an underlying HAL function.
e_err_NUM_ERR: Number of error types. Used for error counting and validation.
This macro is used to allocate a character buffer in every job struct. The names of your jobs can never be longer than this, and jes_register_job() will return e_err_too_long in such a case. You can overwrite the default value of 32 bytes by defining MAX_JOB_NAME_LEN_BYTE yourself:
#define MAX_JOB_NAME_LEN_BYTE 64
void setup(){
}
void loop(){
}This will of course cause every job_struct_t to become bigger.
Similar to MAX_JOB_NAME_LEN_BYTE you can change MAX_JOB_ARGS_LEN_BYTE. This is also a character buffer which holds arguments in string format. Both the jes_job_set_args() and jes_job_get_args() use this buffer as well as any usage of the CLI.
jescore logs core interactions during runtime. The log exists in RAM and is as long as JES_LOG_LEN, which by default is set to 8. You can override this macro by passing -DJES_LOG_LEN=16 to your build flags. Keep in mind that this will use extra memory. If you set JES_LOG_LEN to 0, logging will be disabled entirely.