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
......@@ -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);
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment