IO Task

The IO task manages buttons, LEDS, and the LED strip. It is also the home of several miscellaneous utility tasks.

to do all of this the task uses a scheduler to call tick functions for each task at regular configurable intervals.

the registered tasks are:

  • wdt_reset
    • kicks the watchdog timer (task needs to do this to prevent the watchdog from resetting the device)
    • called every 1 second
  • button_tick
    • reads the button state and calls the button callback
    • called every 5 milliseconds
  • state_machine_tick
    • updates the state machine
    • called every 5 milliseconds
  • battery_status_tick
    • querys the PSOC for if an OHM battery is connected
    • called every 500 milliseconds
  • resistance_update
    • updates the resistance value on the PSOC if the resistance has changed
    • called every 500 milliseconds
  • print_heap
    • miscellanious task that prints the heap size
    • called every 10 seconds
  • retreive_debug_meta
    • retreives the debug meta data from the PSOC
    • called every 500 milliseconds
  • retreive_debug_info
    • uses the debug meta data to retreive specific debug information from the PSOC
    • called every 1 second

Configuration

IO_TASK_VERBOSE_LOGGING

if set to 1 the IO task will print debug information to the console

RESISTANCE_STEP

the amount to change the resistance by for each button press

RESISTANCE_MAX

the maximum resistance value

RESISTANCE_MIN

the minimum resistance value

RESISTANCE_START

the initial resistance value

// resistance settings
#define RESISTANCE_STEP (5U)
#define RESISTANCE_MAX (100U)
#define RESISTANCE_MIN (0U)
#define RESISTANCE_START (0U)

led_ticks

how long the LED should remain in a temporary state for before reverting to the default state in state machine ticks

#define led_ticks (1000U)

NUM_LEDS

the number of LEDs in the LED strip

#define NUM_LEDS (3)

Public Variables

resistance_count_global

the current resistance value

uint16_t resistance_count_global = RESISTANCE_START;

battery_present

a flag indicating if an OHM battery is connected

bool battery_present = true;

Public Functions

io_task_init

initializes the IO task initializes the state machine initializes the button module initializes the LED initializes the LED strip set up button callbacks for the inc and dec buttons

void io_task_init(void)
{
    state = STATE_IDLE;
    resistance_count_global = 0;
    last_time = xTaskGetTickCount();
    button_init();
    led_init();

    strip.gpio = CRADEL_LED;

    strip.led_strip_buf_1 = buf_1;
    strip.led_strip_buf_2 = buf_2;
    strip.led_strip_length = NUM_LEDS;
    strip.rgb_led_type = RGB_LED_TYPE_WTF101;
    strip.rmt_channel = RMT_CHANNEL_0;
    strip.rmt_interrupt_num = 19;
    strip.showing_buf_1 = true;
    strip.access_semaphore = xSemaphoreCreateBinary();
    led_strip_init(&strip);
    led_strip_set_pixel_rgb(&strip, 0, 255, 0, 0);
    led_strip_set_pixel_rgb(&strip, 1, 255, 0, 0);
    led_strip_set_pixel_rgb(&strip, 2, 255, 0, 0);
    led_strip_show(&strip);

    button_setup(BUTTON_DEC, GPIO_INTR_POSEDGE, button_pressed_callback, (void*)&button_dec);
    button_setup(BUTTON_INC, GPIO_INTR_POSEDGE, button_pressed_callback, (void*)&button_inc);
}

io_task

main FreeRTOS task function, task is currently registered by external code. runs in loop executing the registered tasks at regular intervals.

void io_task(void* arg)
{
    while (true)
    {
        tick++;
        for (int i = 0; i < sizeof(schedule) / sizeof(schedule_entry_t); i++)
        {
            if (tick % schedule[i].ticks == 0)
            {
                schedule[i].callback();
            }
        }
        vTaskDelay(5 / portTICK_PERIOD_MS);
        gpio_set_level(GPIO_NUM_12, true);
    };
}

Private Types

State

an enum defining the possible states of the state machine

typedef enum State
{
    STATE_IDLE = 0,
    STATE_BUTTON_DEC_PRESSED = 1,
    STATE_BUTTON_INC_PRESSED = 2,
} State;

Button

a struct representing a button

typedef struct Button
{
    uint32_t id;
} Button;

Private Variables

buf_1 and buf_2

buffers used to store the LED strip state, their are two buffers so that the LED strip can be updated in the background while the current state is being displayed (double buffering)

static led_strip_color_t buf_1[NUM_LEDS] = {0};
static led_strip_color_t buf_2[NUM_LEDS] = {0};

led_strip

the LED strip object used by the led_strip library

static led_strip_t strip = {0};

button_inc and button_dec

buttons used to increase and decrease the resistance passed as user_data to the button callback

static const Button button_dec = {.id = BUTTON_DEC};

static const Button button_inc = {.id = BUTTON_INC};

state

the current state of the state machine

volatile static State state = STATE_IDLE;

last_time

the last time the state machine state changed, used to timeout temporary states back to the default state

volatile static uint32_t last_time = 0;

strip_state

the current state of the LED strip tracked seperatley from the main state machine to allow optimisation where we only update the LED strip when it needs to change

volatile static int strip_state = 0;

Private Functions

button_pressed_callback

callback function for the button module, user_arg is used to identify whether it is the inc or dec button so that same callback can be used for both buttons, in retrospect this didnt actually save any code space.

updates state machine state based on the button pressed updates the resistance value based on the button pressed

static void button_pressed_callback(void* args)
{
    Button* button = (Button*)args;
    switch (button->id)
    {
        case BUTTON_DEC:
            state = STATE_BUTTON_DEC_PRESSED;
            last_time = xTaskGetTickCountFromISR();
            if ((resistance_count_global - RESISTANCE_STEP) <= RESISTANCE_MAX)
            {
                resistance_count_global = resistance_count_global - RESISTANCE_STEP;
            }
            break;
        case BUTTON_INC:
            state = STATE_BUTTON_INC_PRESSED;
            last_time = xTaskGetTickCountFromISR();
            if ((resistance_count_global + RESISTANCE_STEP) <= RESISTANCE_MAX)
            {
                resistance_count_global = resistance_count_global + RESISTANCE_STEP;
            }
            break;
    }
}

state_machine_tick

updates the state machine, sets LEDS based on the current state, handles timeouts for temporary states, updates the LED strip when the state changes

static void state_machine_tick(void)
{
    int last_strip_state = strip_state;
    uint32_t time = xTaskGetTickCount();
    switch (state)
    {
        case STATE_IDLE:
            led_set_color(LED_COLOR_GREEN);
            if (battery_present)
            {
                if (strip_state != 0)
                {
                    strip_state = 0;
                    led_strip_set_pixel_rgb(&strip, 0, 0, 255, 0);
                    led_strip_set_pixel_rgb(&strip, 1, 0, 255, 0);
                    led_strip_set_pixel_rgb(&strip, 2, 0, 255, 0);
                }
            }
            else
            {
                if (strip_state != 1)
                {
                    strip_state = 1;
                    led_strip_set_pixel_rgb(&strip, 0, 0, 0, 0);
                    led_strip_set_pixel_rgb(&strip, 1, 0, 255, 0);
                    led_strip_set_pixel_rgb(&strip, 2, 0, 0, 0);
                }
            }
            break;
        case STATE_BUTTON_DEC_PRESSED:
            led_set_color(LED_COLOR_RED);
            if (strip_state != 2)
            {
                strip_state = 2;
                led_strip_set_pixel_rgb(&strip, 0, 255, 0, 0);
                led_strip_set_pixel_rgb(&strip, 1, 0, 255, 0);
                led_strip_set_pixel_rgb(&strip, 2, 0, 0, 0);
            }
            if (time > (last_time + led_ticks))
            {
                state = STATE_IDLE;
                last_time = time;
            }
            break;
        case STATE_BUTTON_INC_PRESSED:
            led_set_color(LED_COLOR_BLUE);
            if (strip_state != 3)
            {
                strip_state = 3;
                led_strip_set_pixel_rgb(&strip, 0, 0, 0, 0);
                led_strip_set_pixel_rgb(&strip, 1, 0, 255, 0);
                led_strip_set_pixel_rgb(&strip, 2, 0, 0, 255);
            }
            if (time > (last_time + led_ticks))
            {
                state = STATE_IDLE;
                last_time = time;
            }
            break;
    }
    if (last_strip_state != strip_state)
    {
        led_strip_show(&strip);
    }
}

wdt_reset

kicks the watchdog timer

static void wdt_reset()
{
    esp_task_wdt_reset();
}

battery_status_check

queries the PSOC for if an OHM battery is connected

static void battery_status_check()
{
    get_battery_present(UART_CHANNEL_PSOC);
}

resistance_update

updates the resistance value on the PSOC if the resistance has changed, does the same for the FTP for now, this is a temporary hack, should probably be rolled into its own function

static void resistance_update()
{
    if(last_ftp != global_ftp || metrics.resistance != resistance_count_global)
    {
        last_ftp = global_ftp;
        set_resistance(UART_CHANNEL_PSOC, resistance_count_global);
    }
}