Skip to content
Snippets Groups Projects
Commit dd841b5a authored by trilader's avatar trilader
Browse files

feat(epicardium): Add personal state support

This includes a new RTOS task to animate the personal state LED
independently of pycardium. While the animation is running pycardium
can't control the personal state LED.
parent ddebc93c
No related branches found
No related tags found
No related merge requests found
......@@ -102,6 +102,10 @@ typedef _Bool bool;
#define API_TRNG_READ 0xB0
#define API_PERSONAL_STATE_SET 0xc0
#define API_PERSONAL_STATE_GET 0xc1
#define API_PERSONAL_STATE_IS_PERSISTENT 0xc2
/* clang-format on */
typedef uint32_t api_int_id_t;
......@@ -658,6 +662,66 @@ API(API_LEDS_SET_GAMMA_TABLE, void epic_leds_set_gamma_table(
*/
API(API_LEDS_CLEAR_ALL, void epic_leds_clear_all(uint8_t r, uint8_t g, uint8_t b));
/**
* Personal State
* ==============
* Card10 can display your personal state.
*
* If a personal state is set the top-left LED on the bottom side of the
* harmonics board is directly controlled by epicardium and it can't be
* controlled by pycardium.
*
* To re-enable pycardium control the personal state has to be cleared. To do
* that simply set it to ``STATE_NONE``.
*
* The personal state can be set to be persistent which means it won't get reset
* on pycardium application change/restart.
*/
/** Possible personal states. */
enum personal_state {
/** ``0``, No personal state - LED is under regular application control. */
STATE_NONE = 0,
/** ``1``, "no contact, please!" - I am overloaded. Please leave me be - red led, continuously on. */
STATE_NO_CONTACT = 1,
/** ``2``, "chaos" - Adventure time - blue led, short blink, long blink. */
STATE_CHAOS = 2,
/** ``3``, "communication" - want to learn something or have a nice conversation - green led, long blinks. */
STATE_COMMUNICATION = 3,
/** ``4``, "camp" - I am focussed on self-, camp-, or community maintenance - yellow led, fade on and off. */
STATE_CAMP = 4,
};
/**
* Set the users personal state.
*
* Using :c:func:`epic_personal_state_set` an application can set the users personal state.
*
* :param uint8_t state: The users personal state. Must be one of :c:type:`personal_state`.
* :param bool persistent: Indicates whether the configured personal state will remain set and active on pycardium application restart/change.
* :returns: ``0`` on success, ``-EINVAL`` if an invalid state was requested.
*/
API(API_PERSONAL_STATE_SET, int epic_personal_state_set(uint8_t state,
bool persistent));
/**
* Get the users personal state.
*
* Using :c:func:`epic_personal_state_get` an application can get the currently set personal state of the user.
*
* :returns: A value with exactly one value of :c:type:`personal_state` set.
*/
API(API_PERSONAL_STATE_GET, int epic_personal_state_get());
/**
* Get whether the users personal state is persistent.
*
* Using :c:func:`epic_personal_state_is_persistent` an app can find out whether the users personal state is persistent or transient.
*
* :returns: ``1`` if the state is persistent, ``0`` otherwise.
*/
API(API_PERSONAL_STATE_IS_PERSISTENT, int epic_personal_state_is_persistent());
/**
* Sensor Data Streams
* ===================
......
......@@ -73,6 +73,18 @@ int main(void)
}
}
/* LEDs */
if (xTaskCreate(
vLedTask,
(const char *)"LED",
configMINIMAL_STACK_SIZE,
NULL,
tskIDLE_PRIORITY + 1,
NULL) != pdPASS) {
LOG_CRIT("startup", "Failed to create %s task!", "LED");
abort();
}
/* Lifecycle */
if (xTaskCreate(
vLifecycleTask,
......
......@@ -190,10 +190,22 @@ int hardware_reset(void)
api_interrupt_init();
api_dispatcher_init();
/* Personal State */
const int personal_state_is_persistent =
epic_personal_state_is_persistent();
if (personal_state_is_persistent == 0) {
epic_personal_state_set(STATE_NONE, 0);
}
/*
* LEDs
*/
leds_init();
if (personal_state_is_persistent) {
epic_leds_clear_all(0, 0, 0);
} else {
leds_init();
}
epic_leds_set_rocket(0, 0);
epic_leds_set_rocket(1, 0);
epic_leds_set_rocket(2, 0);
......
#include "leds.h"
#include "pmic.h"
//#include "FreeRTOS.h"
//#include "task.h"
#include "FreeRTOS.h"
#include "task.h"
#include "epicardium.h"
#include "modules.h"
#include <stdbool.h>
//TODO: create smth like vTaskDelay(pdMS_TO_TICKS(//put ms here)) for us, remove blocking delay from /lib/leds.c to avoid process blocking
#define NUM_LEDS 15 /* Take from lib/card10/leds.c */
static void update_if_needed()
{
if (personal_state_enabled() == 0) {
leds_update_power();
leds_update();
}
}
void epic_leds_set(int led, uint8_t r, uint8_t g, uint8_t b)
{
if (led == PERSONAL_STATE_LED && personal_state_enabled())
return;
leds_prep(led, r, g, b);
leds_update_power();
leds_update();
......@@ -14,18 +31,26 @@ void epic_leds_set(int led, uint8_t r, uint8_t g, uint8_t b)
void epic_leds_set_hsv(int led, float h, float s, float v)
{
if (led == PERSONAL_STATE_LED && personal_state_enabled())
return;
leds_prep_hsv(led, h, s, v);
leds_update_power();
leds_update();
update_if_needed();
}
void epic_leds_prep(int led, uint8_t r, uint8_t g, uint8_t b)
{
if (led == PERSONAL_STATE_LED && personal_state_enabled())
return;
leds_prep(led, r, g, b);
}
void epic_leds_prep_hsv(int led, float h, float s, float v)
{
if (led == PERSONAL_STATE_LED && personal_state_enabled())
return;
leds_prep_hsv(led, h, s, v);
}
......@@ -33,32 +58,38 @@ void epic_leds_set_all(uint8_t *pattern_ptr, uint8_t len)
{
uint8_t(*pattern)[3] = (uint8_t(*)[3])pattern_ptr;
for (int i = 0; i < len; i++) {
if (i == PERSONAL_STATE_LED && personal_state_enabled())
continue;
leds_prep(i, pattern[i][0], pattern[i][1], pattern[i][2]);
}
leds_update_power();
leds_update();
update_if_needed();
}
void epic_leds_set_all_hsv(float *pattern_ptr, uint8_t len)
{
float(*pattern)[3] = (float(*)[3])pattern_ptr;
for (int i = 0; i < len; i++) {
if (i == PERSONAL_STATE_LED && personal_state_enabled())
continue;
leds_prep_hsv(i, pattern[i][0], pattern[i][1], pattern[i][2]);
}
leds_update_power();
leds_update();
update_if_needed();
}
void epic_leds_dim_top(uint8_t value)
{
leds_set_dim_top(value);
leds_update();
if (personal_state_enabled() == 0)
leds_update();
}
void epic_leds_dim_bottom(uint8_t value)
{
leds_set_dim_bottom(value);
leds_update();
if (personal_state_enabled() == 0)
leds_update();
}
void epic_leds_set_rocket(int led, uint8_t value)
......@@ -74,8 +105,7 @@ void epic_set_flashlight(bool power)
void epic_leds_update(void)
{
leds_update_power();
leds_update();
update_if_needed();
}
void epic_leds_set_powersave(bool eco)
......@@ -91,7 +121,7 @@ void epic_leds_set_gamma_table(uint8_t rgb_channel, uint8_t gamma_table[256])
void epic_leds_clear_all(uint8_t r, uint8_t g, uint8_t b)
{
for (int i = 0; i < NUM_LEDS; i++) {
if (i == PERSONAL_STATE_LED && personal_state_enabled)
if (i == PERSONAL_STATE_LED && personal_state_enabled())
continue;
leds_prep(i, r, g, b);
......
......@@ -10,6 +10,7 @@ module_sources = files(
'lifecycle.c',
'light_sensor.c',
'log.c',
'personal_state.c',
'pmic.c',
'rtc.c',
'serial.c',
......
......@@ -26,6 +26,11 @@ void return_to_menu(void);
void vSerialTask(void *pvParameters);
void serial_enqueue_char(char chr);
/* ---------- LED Animation / Personal States ------------------------------ */
#define PERSONAL_STATE_LED 14
void vLedTask(void *pvParameters);
int personal_state_enabled();
/* ---------- PMIC --------------------------------------------------------- */
/* In 1/10s */
#define PMIC_PRESS_SLEEP 20
......
#include "epicardium.h"
#include "leds.h"
#include "modules.h"
#include <math.h>
uint8_t _personal_state_enabled = 0;
uint8_t personal_state = STATE_NONE;
uint8_t personal_state_persistent = 0;
int led_animation_ticks = 0;
int led_animation_state = 0;
int personal_state_enabled()
{
return _personal_state_enabled;
}
int epic_personal_state_set(uint8_t state, bool persistent)
{
if (state < STATE_NONE || state > STATE_CAMP)
return -EINVAL;
led_animation_state = 0;
led_animation_ticks = 0;
personal_state = state;
uint8_t was_enabled = _personal_state_enabled;
_personal_state_enabled = (state != STATE_NONE);
personal_state_persistent = persistent;
if (was_enabled && !_personal_state_enabled) {
leds_prep(PERSONAL_STATE_LED, 0, 0, 0);
leds_update_power();
leds_update();
}
return 0;
}
int epic_personal_state_get()
{
return personal_state;
}
int epic_personal_state_is_persistent()
{
return personal_state_persistent;
}
void vLedTask(void *pvParameters)
{
const int led_animation_rate = 1000 / 25; /* 25Hz -> 40ms*/
while (1) {
if (_personal_state_enabled) {
led_animation_ticks++;
if (personal_state == STATE_NO_CONTACT) {
leds_prep(PERSONAL_STATE_LED, 255, 0, 0);
} else if (personal_state == STATE_CHAOS) {
if (led_animation_state == 0) {
leds_prep(
PERSONAL_STATE_LED, 0, 0, 255
);
if (led_animation_ticks >
(200 / led_animation_rate)) {
led_animation_ticks = 0;
led_animation_state = 1;
}
} else if (led_animation_state == 1) {
leds_prep(PERSONAL_STATE_LED, 0, 0, 0);
if (led_animation_ticks >
(300 / led_animation_rate)) {
led_animation_ticks = 0;
led_animation_state = 2;
}
} else if (led_animation_state == 2) {
leds_prep(
PERSONAL_STATE_LED, 0, 0, 255
);
if (led_animation_ticks >
(1000 / led_animation_rate)) {
led_animation_ticks = 0;
led_animation_state = 3;
}
} else if (led_animation_state == 3) {
leds_prep(PERSONAL_STATE_LED, 0, 0, 0);
if (led_animation_ticks >
(300 / led_animation_rate)) {
led_animation_ticks = 0;
led_animation_state = 0;
}
}
} else if (personal_state == STATE_COMMUNICATION) {
if (led_animation_state == 0) {
leds_prep(
PERSONAL_STATE_LED, 255, 255, 0
);
if (led_animation_ticks >
(1000 / led_animation_rate)) {
led_animation_ticks = 0;
led_animation_state = 1;
}
} else if (led_animation_state == 1) {
leds_prep(PERSONAL_STATE_LED, 0, 0, 0);
if (led_animation_ticks >
(300 / led_animation_rate)) {
led_animation_ticks = 0;
led_animation_state = 0;
}
}
} else if (personal_state == STATE_CAMP) {
leds_prep_hsv(
PERSONAL_STATE_LED,
120.0f,
1.0f,
fabs(sin(
led_animation_ticks /
(float)(1000 /
led_animation_rate))));
}
leds_update_power();
leds_update();
}
vTaskDelay(led_animation_rate / portTICK_PERIOD_MS);
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment