Skip to content

Commit

Permalink
add buttons/rotary/ir/BT sink in sleep + battery low-voltage
Browse files Browse the repository at this point in the history
  • Loading branch information
philippe44 committed Sep 17, 2023
1 parent f4c0a91 commit bb185d7
Show file tree
Hide file tree
Showing 10 changed files with 199 additions and 71 deletions.
2 changes: 1 addition & 1 deletion components/display/display.c
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ void display_init(char *welcome) {
}

// and finally register ourselves to power off upon deep sleep
services_sleep_sethook(display_sleep);
services_sleep_setsuspend(display_sleep);
}

free(config);
Expand Down
4 changes: 4 additions & 0 deletions components/driver_bt/bt_app_source.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ static const char * TAG = "bt_app_source";
static const char * BT_RC_CT_TAG="RCCT";
extern int32_t output_bt_data(uint8_t *data, int32_t len);
extern void output_bt_tick(void);
extern void output_bt_stop(void);
extern void output_bt_start(void);
extern char* output_state_str(void);
extern bool output_stopped(void);
extern bool is_recovery_running;
Expand Down Expand Up @@ -803,6 +805,7 @@ static void bt_app_av_media_proc(uint16_t event, void *param)
if (a2d->media_ctrl_stat.cmd == ESP_A2D_MEDIA_CTRL_START &&
a2d->media_ctrl_stat.status == ESP_A2D_MEDIA_CTRL_ACK_SUCCESS) {
ESP_LOGI(TAG,"a2dp media started successfully.");
output_bt_start();
set_a2dp_media_state(APP_AV_MEDIA_STATE_STARTED);
} else {
// not started succesfully, transfer to idle state
Expand Down Expand Up @@ -831,6 +834,7 @@ static void bt_app_av_media_proc(uint16_t event, void *param)
if (a2d->media_ctrl_stat.cmd == ESP_A2D_MEDIA_CTRL_STOP &&
a2d->media_ctrl_stat.status == ESP_A2D_MEDIA_CTRL_ACK_SUCCESS) {
ESP_LOGI(TAG,"a2dp media stopped successfully...");
output_bt_stop();
set_a2dp_media_state(APP_AV_MEDIA_STATE_IDLE);
} else {
ESP_LOGI(TAG,"a2dp media stopping...");
Expand Down
4 changes: 2 additions & 2 deletions components/services/battery.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ static struct {
.cells = 2,
};

void (*battery_handler_svc)(float value);
void (*battery_handler_svc)(float value, int cells);

/****************************************************************************************
*
Expand All @@ -66,7 +66,7 @@ static void battery_callback(TimerHandle_t xTimer) {
if (++battery.count == 30) {
battery.avg = battery.sum / battery.count;
battery.sum = battery.count = 0;
if (battery_handler_svc) (battery_handler_svc)(battery.avg);
if (battery_handler_svc) (battery_handler_svc)(battery.avg, battery.cells);
ESP_LOGI(TAG, "Voltage %.2fV", battery.avg);
}
}
Expand Down
17 changes: 16 additions & 1 deletion components/services/buttons.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,14 @@
#include "driver/rmt.h"
#include "gpio_exp.h"
#include "buttons.h"
#include "services.h"
#include "rotary_encoder.h"
#include "globdefs.h"

static const char * TAG = "buttons";

static EXT_RAM_ATTR int n_buttons;
static EXT_RAM_ATTR uint32_t buttons_idle_since;

#define BUTTON_STACK_SIZE 4096
#define MAX_BUTTONS 32
Expand Down Expand Up @@ -156,17 +158,30 @@ static void buttons_handler(struct button_s *button, int level) {
}
}

/****************************************************************************************
* Get inactivity callback
*/
static uint32_t buttons_idle_callback(void) {
return pdTICKS_TO_MS(xTaskGetTickCount()) - buttons_idle_since;
}

/****************************************************************************************
* Tasks that calls the appropriate functions when buttons are pressed
*/
static void buttons_task(void* arg) {
ESP_LOGI(TAG, "starting button tasks");
ESP_LOGI(TAG, "starting button tasks");

buttons_idle_since = pdTICKS_TO_MS(xTaskGetTickCount());
services_sleep_setsleeper(buttons_idle_callback);

while (1) {
QueueSetMemberHandle_t xActivatedMember;

// wait on button, rotary and infrared queues
if ((xActivatedMember = xQueueSelectFromSet( common_queue_set, portMAX_DELAY )) == NULL) continue;

// mark the last activity
buttons_idle_since = pdTICKS_TO_MS(xTaskGetTickCount());

if (xActivatedMember == button_queue) {
struct button_s button;
Expand Down
2 changes: 1 addition & 1 deletion components/services/monitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ extern bool jack_inserted_svc(void);
extern void (*spkfault_handler_svc)(bool inserted);
extern bool spkfault_svc(void);

extern void (*battery_handler_svc)(float value);
extern void (*battery_handler_svc)(float value, int cells);
extern float battery_value_svc(void);
extern uint16_t battery_level_svc(void);

Expand Down
137 changes: 96 additions & 41 deletions components/services/services.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,14 @@ static EXT_RAM_ATTR struct {
uint64_t wake_gpio, wake_level;
uint64_t rtc_gpio, rtc_level;
uint32_t delay;
} sleep_config;
float battery_level;
int battery_count;
void (*idle_chain)(uint32_t now);
void (*battery_chain)(float level, int cells);
void (*suspend[10])(void);
uint32_t (*sleeper[10])(void);
} sleep_context;

static EXT_RAM_ATTR void (*sleep_hooks[16])(void);

static const char *TAG = "services";

/****************************************************************************************
Expand Down Expand Up @@ -102,6 +106,43 @@ static void sleep_gpio_handler(void *id, button_event_e event, button_press_e mo
if (event == BUTTON_PRESSED) services_sleep_activate(SLEEP_ONGPIO);
}

/****************************************************************************************
*
*/
static void sleep_timer(uint32_t now) {
static uint32_t last;

// first chain the calls to psudo_idle function
if (sleep_context.idle_chain) sleep_context.idle_chain(now);

// only query callbacks every 30s if we have at least one sleeper
if (!*sleep_context.sleeper || now < last + 30*1000) return;
last = now;

// call all sleep hooks that might want to do something
for (uint32_t (**sleeper)(void) = sleep_context.sleeper; *sleeper; sleeper++) {
if ((*sleeper)() < sleep_context.delay) return;
}

// if we are here, we are ready to sleep;
services_sleep_activate(SLEEP_ONTIMER);
}

/****************************************************************************************
*
*/
static void sleep_battery(float level, int cells) {
// chain if any
if (sleep_context.battery_chain) sleep_context.battery_chain(level, cells);

// then assess if we have to stop because of low batt
if (level < sleep_context.battery_level) {
if (sleep_context.battery_count++ == 2) services_sleep_activate(SLEEP_ONBATTERY);
} else {
sleep_context.battery_count = 0;
}
}

/****************************************************************************************
*
*/
Expand All @@ -110,10 +151,20 @@ static void sleep_init(void) {
char *p;

// do we want delay sleep
PARSE_PARAM(config, "delay", '=', sleep_config.delay);
sleep_config.delay *= 60*1000;
if (sleep_config.delay) {
ESP_LOGI(TAG, "Sleep inactivity of %d minute(s)", sleep_config.delay / (60*1000));
PARSE_PARAM(config, "delay", '=', sleep_context.delay);
sleep_context.delay *= 60*1000;
if (sleep_context.delay) {
sleep_context.idle_chain = pseudo_idle_svc;
pseudo_idle_svc = sleep_timer;
ESP_LOGI(TAG, "Sleep inactivity of %d minute(s)", sleep_context.delay / (60*1000));
}

// do we want battery safety
PARSE_PARAM_FLOAT(config, "batt", '=', sleep_context.battery_level);
if (sleep_context.battery_level != 0.0) {
sleep_context.battery_chain = battery_handler_svc;
battery_handler_svc = sleep_battery;
ESP_LOGI(TAG, "Sleep on battery level of %.2f", sleep_context.battery_level);
}

// get the wake criteria
Expand All @@ -126,15 +177,15 @@ static void sleep_init(void) {
if (!rtc_gpio_is_valid_gpio(gpio)) {
ESP_LOGE(TAG, "invalid wake GPIO %d (not in RTC domain)", gpio);
} else {
sleep_config.wake_gpio |= 1LL << gpio;
sleep_context.wake_gpio |= 1LL << gpio;
}
if (sscanf(item, "%*[^:]:%d", &level)) sleep_config.wake_level |= level << gpio;
if (sscanf(item, "%*[^:]:%d", &level)) sleep_context.wake_level |= level << gpio;
p = strchr(p, '|');
}

// when moving to esp-idf more recent than 4.4.x, multiple gpio wake-up with level specific can be done
if (sleep_config.wake_gpio) {
ESP_LOGI(TAG, "Sleep wake-up gpio bitmap 0x%llx (active 0x%llx)", sleep_config.wake_gpio, sleep_config.wake_level);
if (sleep_context.wake_gpio) {
ESP_LOGI(TAG, "Sleep wake-up gpio bitmap 0x%llx (active 0x%llx)", sleep_context.wake_gpio, sleep_context.wake_level);
}
}

Expand All @@ -148,15 +199,15 @@ static void sleep_init(void) {
if (!rtc_gpio_is_valid_gpio(gpio)) {
ESP_LOGE(TAG, "invalid rtc GPIO %d", gpio);
} else {
sleep_config.rtc_gpio |= 1LL << gpio;
sleep_context.rtc_gpio |= 1LL << gpio;
}
if (sscanf(item, "%*[^:]:%d", &level)) sleep_config.rtc_level |= level << gpio;
if (sscanf(item, "%*[^:]:%d", &level)) sleep_context.rtc_level |= level << gpio;
p = strchr(p, '|');
}

// when moving to esp-idf more recent than 4.4.x, multiple gpio wake-up with level specific can be done
if (sleep_config.rtc_gpio) {
ESP_LOGI(TAG, "RTC forced gpio bitmap 0x%llx (active 0x%llx)", sleep_config.rtc_gpio, sleep_config.rtc_level);
if (sleep_context.rtc_gpio) {
ESP_LOGI(TAG, "RTC forced gpio bitmap 0x%llx (active 0x%llx)", sleep_context.rtc_gpio, sleep_context.rtc_level);
}
}

Expand All @@ -172,49 +223,40 @@ static void sleep_init(void) {
}
}

/****************************************************************************************
*
*/
void services_sleep_callback(uint32_t elapsed) {
if (sleep_config.delay && elapsed >= sleep_config.delay) {
services_sleep_activate(SLEEP_ONTIMER);
}
}

/****************************************************************************************
*
*/
void services_sleep_activate(sleep_cause_e cause) {
// call all sleep hooks that might want to do something
for (void (**hook)(void) = sleep_hooks; *hook; hook++) (*hook)();
for (void (**suspend)(void) = sleep_context.suspend; *suspend; suspend++) (*suspend)();

// isolate all possible GPIOs, except the wake-up and RTC-maintaines ones
esp_sleep_config_gpio_isolate();

// keep RTC domain up if we need to maintain pull-up/down of some GPIO from RTC
if (sleep_config.rtc_gpio) esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
if (sleep_context.rtc_gpio) esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);

for (int i = 0; i < GPIO_NUM_MAX; i++) {
// must be a RTC GPIO
if (!rtc_gpio_is_valid_gpio(i)) continue;

// do we need to maintain a pull-up or down of that GPIO
if ((1LL << i) & sleep_config.rtc_gpio) {
if ((sleep_config.rtc_level >> i) & 0x01) rtc_gpio_pullup_en(i);
if ((1LL << i) & sleep_context.rtc_gpio) {
if ((sleep_context.rtc_level >> i) & 0x01) rtc_gpio_pullup_en(i);
else rtc_gpio_pulldown_en(i);
// or is this not wake-up GPIO, just isolate it
} else if (!((1LL << i) & sleep_config.wake_gpio)) {
} else if (!((1LL << i) & sleep_context.wake_gpio)) {
rtc_gpio_isolate(i);
}
}

// is there just one GPIO
if (sleep_config.wake_gpio & (sleep_config.wake_gpio - 1)) {
ESP_LOGI(TAG, "going to sleep cause %d, wake-up on multiple GPIO, any '1' wakes up 0x%llx", cause, sleep_config.wake_gpio);
esp_sleep_enable_ext1_wakeup(sleep_config.wake_gpio, ESP_EXT1_WAKEUP_ANY_HIGH);
} else if (sleep_config.wake_gpio) {
int gpio = __builtin_ctz(sleep_config.wake_gpio);
int level = (sleep_config.wake_level >> gpio) & 0x01;
if (sleep_context.wake_gpio & (sleep_context.wake_gpio - 1)) {
ESP_LOGI(TAG, "going to sleep cause %d, wake-up on multiple GPIO, any '1' wakes up 0x%llx", cause, sleep_context.wake_gpio);
esp_sleep_enable_ext1_wakeup(sleep_context.wake_gpio, ESP_EXT1_WAKEUP_ANY_HIGH);
} else if (sleep_context.wake_gpio) {
int gpio = __builtin_ctz(sleep_context.wake_gpio);
int level = (sleep_context.wake_level >> gpio) & 0x01;
ESP_LOGI(TAG, "going to sleep cause %d, wake-up on GPIO %d level %d", cause, gpio, level);
esp_sleep_enable_ext0_wakeup(gpio, level);
} else {
Expand All @@ -226,18 +268,31 @@ void services_sleep_activate(sleep_cause_e cause) {
else esp_deep_sleep_start();
}


/****************************************************************************************
*
*/
void services_sleep_sethook(void (*hook)(void)) {
for (int i = 0; i < sizeof(sleep_hooks)/sizeof(void(*)(void)); i++) {
if (!sleep_hooks[i]) {
sleep_hooks[i] = hook;
return;
}
static void register_method(void **store, size_t size, void *method) {
for (int i = 0; i < size; i++, *store++) if (!*store) {
*store = method;
return;
}
}

/****************************************************************************************
*
*/
void services_sleep_setsuspend(void (*hook)(void)) {
register_method((void**) sleep_context.suspend, sizeof(sleep_context.suspend)/sizeof(*sleep_context.suspend), (void*) hook);
}

/****************************************************************************************
*
*/
void services_sleep_setsleeper(uint32_t (*sleeper)(void)) {
register_method((void**) sleep_context.sleeper, sizeof(sleep_context.sleeper)/sizeof(*sleep_context.sleeper), (void*) sleeper);
}

/****************************************************************************************
*
*/
Expand Down
6 changes: 3 additions & 3 deletions components/services/services.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

#pragma once

typedef enum { SLEEP_ONTIMER, SLEEP_ONKEY, SLEEP_ONGPIO, SLEEP_ONIR } sleep_cause_e;
typedef enum { SLEEP_ONTIMER, SLEEP_ONKEY, SLEEP_ONGPIO, SLEEP_ONIR, SLEEP_ONBATTERY } sleep_cause_e;
void services_sleep_activate(sleep_cause_e cause);
void services_sleep_sethook(void (*hook)(void));
void services_sleep_callback(uint32_t elapsed);
void services_sleep_setsuspend(void (*hook)(void));
void services_sleep_setsleeper(uint32_t (*sleeper)(void));
Loading

0 comments on commit bb185d7

Please sign in to comment.