# Use-cases `jescore` can be used for a variety of applications for and on embedded systems. Since multitasking and CLI are native, it opens up some possibilities for cool cross-platform applications. ### Finite state machine Does your project involve user-triggered tasks with sleep time in between? Perfect for `jescore`! Use interrupts to launch jobs that process your requests before going to sleep again. Since `jescore` deallocates memory for jobs which are done, you can expect that your sleep-overhead is close to **zero**. You won't have to worry about being fast because of the interrupt, because the job is quickly started there and then acts outside of the interrupt routine within the task scheduler. ### Sampler with user input Many devices in the realm of media streaming or measurement require a constant stream of data but also have to react to user input during runtime. Think of a digital oscilloscope which has to adjust its trigger value according to the users input while data is being sampled. `jescore` can take care of that. Define a sampler-job with a `while(1)` and UI-handler job which gets triggered by an interrupt, adjust some values within your trigger-job and exit. ### Unit testing Native CLI support enables easy cross-platform unit-testing. Using `pyserial` for python you can send commands to `jescore` via serial. This way, you can easily test your embedded systems internal calculation processes without being physically present. ### Hardware virtualization Ordered an expensive touchpad with a shipment time of 3 weeks? Would be a shame if you had to wait that long to resume programming your UI. With `jescore` you can define a job that reacts to an input call with arguments that abstract any information that your physical UI would later send. For example `press icon 1` could envoke a job of name `press` that executes exactly the same thing your display would do later. When it finally arrives, you just have to envoke `press icon 1` in the interrupt routine of your touch sensor. This makes UI-to-system coupling very shallow and simple and you can focus on building a cool looking UI without worrying about the boring backend stuff. # Blinky Classic blinky example powered by `jescore`. Only this time, you can actually toggle the LED without any hardware. Simply [install the `jescore-cli`](CLI-Documentation.md#setup) and type `jescore blink` to toggle your controller from your PC like a big-boy OS via your keyboard. This example is also available at `example_blink.cpp`. ```c #include #include "jescore.h" #define LED_PIN 4 void blink(void* p){ static uint8_t act = 0; act = !act; while(act){ digitalWrite(LED_PIN, HIGH); delay(1000); digitalWrite(LED_PIN, LOW); delay(1000); } } void setup() { jes_init(); pinMode(LED_PIN, OUTPUT); jes_register_and_launch_job("blink", 2048, 1, blink, 1, 1); } void loop() { } ``` Now you can control your embedded system from your PC! ![jescore_blink](https://github.com/user-attachments/assets/d180d1a6-2823-451f-9092-45aa2639c013) # CLI-driven FSM Some systems operate purely on asynchronous input, such as a user action. Polling for an input that may occur once per day but is sampled every second is very inefficient. For this reason, interrupts can help to determine an asynchronous action and to activate the system whenever it is accessed and only then. `jescore` is no different. In this example, using the CLI triggers an UART interrupt which is then piped to `jescore`. This is a simple version of the classic "garage door" example, whereby the user requests one of two actions that co-depend on each other. Here, the action performed by a motor is represented by a fading LED. While the LED is changing its status, the process is blocked. Call `jescore lights on` to see the LED fade on. This example is also available at `example_fsm_cli.cpp`. ```c++ #include #include "jescore.h" #include #define LED_PIN 4 static bool is_on = false; static bool processing = false; void lights(void* p){ /* This function will be executed when the first word of your CLI command is "lights", as registered in line 60 below. All other words that follow, such as "on" or "off" will be passed as args to this job and can be retrieved as full string with jes_job_get_args(). */ char* args = jes_job_get_args(); char* arg = strtok(args, " "); if (!arg){ uart_unif_write("No argument specified! Use or \n\r"); return; } if (strcmp(arg, "on") == 0){ if(is_on) return; // nothing to do if(processing) return; // blocked by lights_off processing = true; for(uint8_t i = 0; i < 255; i++){ analogWrite(LED_PIN, i); jes_delay_job_ms(5); } is_on = true; processing = false; } else if (strcmp(arg, "off") == 0){ if(!is_on) return; // nothing to do if(processing) return; // blocked by lights_off processing = true; for(uint8_t i = 255; i > 0; i--){ analogWrite(LED_PIN, i); jes_delay_job_ms(5); } is_on = false; processing = false; } else if (strcmp(arg, "state") == 0){ uart_unif_writef("lights are %s\n\r",(is_on ? "on" : "off")); } else{ uart_unif_write("unknown argument!\n\r"); } } void setup() { jes_init(); pinMode(LED_PIN, OUTPUT); jes_register_job("lights", 2048, 1, lights, 0, 1); } void loop() { // nothing to do here! // jescore is now purely controlled by the CLI // call `jescore lights on` to see the LED fade on // call `jescore lights status` to get a print // of the current status of the LED FSM } ``` # Synchronous job with asynchronous user input More common on embedded systems, a synchronous conversion job of analog input or output data with equal time steps between them requires strict timing. This timing usually means that the job performing this action can't "wait" or poll for user input, as that would violate its timing restrictions. In such a case, `jescore` can be used to steer a **synchronous** job with an **asynchronous** one that interact via a shared resource. This example demonstrates how the classic **blinky** sketch can be modified in terms of its blinking speed by the user. Use `jescore switch 500` to set the blinking rate to 500 ms. This example is also available at `example_sync_async.cpp`. ```c++ #include #include "jescore.h" #include #define LED_PIN 5 static uint32_t blink_pause = 1000; void blink_forever(void* p){ /* This function is a placeholder for a "time critical" loop that does not have the capability of sampling user input. This is the "sync"-job that we want to steer with the "async"- job. This job is an infinite, synchronous loop. */ while(1){ digitalWrite(LED_PIN, 1); jes_delay_job_ms(blink_pause); digitalWrite(LED_PIN, 0); jes_delay_job_ms(blink_pause); } } void blink_switch(void* p){ /* This function handles "async" user input and applies it to the time critical job. In this very simple example, the shared resource is a single uint32_t variable "blink_pause". This job handles asynchronous user input and then exits. */ char* arg = jes_job_arg_next(); if(!arg){ uart_unif_write("Usage: switch