API Documentation - jake-is-ESD-protected/jescore GitHub Wiki
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/jake-is-ESD-protected/jescore
Right now, jescore
supports any board which is compatible with the Arduino FW ports of other FWs. Up until now, it has only been actually tested on a few ESP32s:
- ESP32-WROOM
- ESP32-C3
- ESP32-S3
You can now import jescore
:
// your_project/src/main.cpp
...
#include <jescore.h>
void setup(){
}
void loop(){
}
For other IDEs, you can clone the repo with git clone https://github.com/jake-is-ESD-protected/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_err
in 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
.
jes_err_t register_job(const char* name, uint32_t mem_size, uint8_t priority, void (*function)(void* p), uint8_t is_loop)
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.
Returns: Status. Returnse_err_no_err
in case of successful registration.
Use 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. Be sure to set this truthfully, as it may mess with the CLI, if you use that.
Brief: Start a registered job.
name
: String name of job as set inregister_job()
.
Returns: Status. Returnse_err_no_err
in case of successful launch.
To launch a job, use launch_job()
and give it the name of the job you previously registered. If you find this step redundant, see register_and_launch_job()
.
Brief: Start a registered job with arguments.
name
: String name of job as set inregister_job()
.args
: String of arguments delimited by whitespaces. Can't be longer thanMAX_JOB_ARGS_LEN_BYTE
. Returns: Status. Returnse_err_no_err
in 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 register_and_launch_job(const char* name, uint32_t mem_size, uint8_t priority, void (*function)(void* p), uint8_t is_loop)
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.
Returns: Status. Returnse_err_no_err
in 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
args
of the job.
s
: String to insert intoargs
field.
Returns: status,e_err_no_err
if 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
args
of the job.
Returns: Pointer toargs
field 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 set_args()
or the CLI.
Brief: Set the field
optional
of the job.
p
: Arbitrary reference to parameter.
Returns: status,e_err_no_err
if OK.
For any arbitrary argument you might pass to a job, the field optional
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
jescore
does 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
optional
of the job.
Returns: Pointer tooptional
field of the job.
Use this function to retrieve whatever you put into the optional
field in your job with set_param()
. Be careful, this value might be NULL
if you never called set_param()
with anything other than NULL
.
Brief: Notify a job with an optional message.
name
: Name of job which should be notified.
notification
: Optional pointer to notification value.
Call this function to notify a different job. For a job to accept notifications, it has to be waiting for one, see 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.
name
: Name of job which should be notified.
notification
: Optional pointer to notification value.
Same as notify_job()
but for interrupt service routines. Call this from within an interrupt.
Brief: Wait for an incoming message.
Returns: Optional message pointer given innotify_job()
.
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.
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
}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 orNULL
or 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.
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 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 set_args()
and get_args()
use this buffer as well as any usage of the CLI.