24 favourite slots and a Power off menu entry
Power off drops the peripheral rail (PWR_ON low), sleeps the panel and puts the chip into deep sleep with ext1 wake armed on the encoder pins, choosing any-low or any-high from the knob's resting state so a single detent wakes it. The button (GPIO0) is not a wake source on purpose.
This commit is contained in:
1 parent
bd05fca812
commit
89dfdf09a6
9 files changed
+98
-10
No files matched your search
@@ -60,15 +60,21 @@ click a number to edit it (turn changes it, click confirms), hold to go back.
|
||||
Entries:
|
||||
|
||||
* **Save as favourite / Remove from favourites** — the camera that was playing;
|
||||
saving asks for one of the 12 slots (overwrites it).
|
||||
* **Favourites** — the 12 slots; click one to play it. A saved camera shows a
|
||||
saving asks for one of the 24 slots (overwrites it).
|
||||
* **Favourites** — the 24 slots; click one to play it. A saved camera shows a
|
||||
yellow *FAV n* marker while it plays.
|
||||
* **Source** — *Random* draws from the sites, *Favourites* cycles the saved slots.
|
||||
* **Provider** — *insecam*, *opentopia* or *Mixed* (a random site per batch).
|
||||
* **Time per camera**, **Listing** (mixed / top rated / newest), **Screen
|
||||
brightness**, **Ring brightness**, **Caption**.
|
||||
* **Forget a favourite**, **Wi-Fi setup** (reboots into the hotspot), the board
|
||||
name with its IP, and the power source.
|
||||
* **Forget a favourite**, **Wi-Fi setup** (reboots into the hotspot), **Power
|
||||
off**, the board name with its IP, and the power source.
|
||||
|
||||
*Power off* switches the peripheral rail off (screen, ring, codec) and puts the
|
||||
ESP32-S3 into deep sleep; turning the knob wakes it and it boots as normal. The
|
||||
encoder's push button is deliberately not a wake source: it is GPIO 0, and a
|
||||
press that is still held when the chip resets would land it in the ROM
|
||||
bootloader instead.
|
||||
|
||||
The menu closes by itself after 45 s without input.
|
||||
|
||||
@@ -114,8 +120,9 @@ it stays out of the way. The CC1101 board's BQ27220 gauge is not supported.
|
||||
elapses or the knob moves, skip dead cameras, keep history for "previous".
|
||||
* `ui.c` is LVGL 9 via `esp_lvgl_port`: a canvas for the frame, a translucent
|
||||
caption bar, a LIVE/PAUSED badge, a hairline progress bar and a six-row menu page.
|
||||
* `settings.c` keeps the settings blob and the 12 favourite slots in NVS
|
||||
(namespace `widget`); `menu.c` is the knob-driven menu that edits them.
|
||||
* `settings.c` keeps the settings blob and the 24 favourite slots in NVS
|
||||
(namespace `widget`); `menu.c` is the knob-driven menu that edits them;
|
||||
`power.c` is the deep-sleep power-off with ext1 wake on the encoder pins.
|
||||
|
||||
## License
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
idf_component_register(
|
||||
SRCS "main.c" "board.c" "display.c" "ui.c" "leds.c" "input.c" "net.c"
|
||||
"httpstream.c" "jpegframe.c" "jpegdec.c" "insecam.c" "slideshow.c" "settings.c" "menu.c" "battery.c" "webpage.c" "opentopia.c"
|
||||
"httpstream.c" "jpegframe.c" "jpegdec.c" "insecam.c" "slideshow.c" "settings.c" "menu.c" "battery.c" "webpage.c" "opentopia.c" "power.c"
|
||||
INCLUDE_DIRS "."
|
||||
PRIV_REQUIRES esp_lcd esp_wifi nvs_flash esp_netif esp_event esp_http_server driver esp_timer esp_adc esp_http_client mbedtls
|
||||
)
|
||||
+8
-1
@@ -15,6 +15,8 @@ static const char *TAG = "display";
|
||||
#define BL_TIMER LEDC_TIMER_0
|
||||
#define BL_CHANNEL LEDC_CHANNEL_0
|
||||
|
||||
static esp_lcd_panel_handle_t panel;
|
||||
|
||||
static void backlight_init(void)
|
||||
{
|
||||
ledc_timer_config_t timer = {
|
||||
@@ -57,7 +59,6 @@ void display_init(void)
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)LCD_SPI_HOST, &io_cfg, &io));
|
||||
|
||||
esp_lcd_panel_handle_t panel = NULL;
|
||||
esp_lcd_panel_dev_config_t panel_cfg = {
|
||||
.reset_gpio_num = board->lcd_rst,
|
||||
.rgb_ele_order = board->lcd_bgr ? LCD_RGB_ELEMENT_ORDER_BGR : LCD_RGB_ELEMENT_ORDER_RGB,
|
||||
@@ -90,3 +91,9 @@ void display_init(void)
|
||||
if (!disp) { ESP_LOGE(TAG, "lvgl_port_add_disp failed"); abort(); }
|
||||
ESP_LOGI(TAG, "ST7789 %dx%d up at %d MHz", LCD_H_RES, LCD_V_RES, CONFIG_WIDGET_LCD_PCLK_MHZ);
|
||||
}
|
||||
|
||||
void display_off(void)
|
||||
{
|
||||
display_set_backlight(0);
|
||||
if (panel) esp_lcd_panel_disp_on_off(panel, false);
|
||||
}
|
||||
@@ -8,3 +8,5 @@
|
||||
void display_init(void);
|
||||
// 0..100
|
||||
void display_set_backlight(uint8_t percent);
|
||||
// Panel sleep-in (used before powering down).
|
||||
void display_off(void);
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "jpegdec.h"
|
||||
#include "leds.h"
|
||||
#include "net.h"
|
||||
#include "power.h"
|
||||
#include "settings.h"
|
||||
#include "slideshow.h"
|
||||
#include "ui.h"
|
||||
@@ -29,6 +30,7 @@ static void run_portal(void)
|
||||
void app_main(void)
|
||||
{
|
||||
board_init();
|
||||
power_report_wakeup();
|
||||
settings_init();
|
||||
leds_init();
|
||||
leds_set_brightness(settings.led_brightness);
|
||||
|
||||
+6
-1
@@ -5,6 +5,7 @@
|
||||
#include "input.h"
|
||||
#include "leds.h"
|
||||
#include "net.h"
|
||||
#include "power.h"
|
||||
#include "settings.h"
|
||||
#include "ui.h"
|
||||
#include "esp_log.h"
|
||||
@@ -19,7 +20,7 @@ static const char *TAG = "menu";
|
||||
|
||||
typedef enum { VIEW_TOP, VIEW_FAV_PLAY, VIEW_FAV_SAVE, VIEW_FAV_FORGET } view_t;
|
||||
enum { ROW_FAV_TOGGLE, ROW_FAVS, ROW_SOURCE, ROW_PROVIDER, ROW_INTERVAL, ROW_LISTING, ROW_LCD, ROW_RING,
|
||||
ROW_CAPTION, ROW_FORGET, ROW_WIFI, ROW_IP, ROW_BATTERY, ROW_CLOSE, TOP_ROWS };
|
||||
ROW_CAPTION, ROW_FORGET, ROW_WIFI, ROW_POWER, ROW_IP, ROW_BATTERY, ROW_CLOSE, TOP_ROWS };
|
||||
|
||||
static ui_menu_row_t rows[TOP_ROWS > FAV_SLOTS + 1 ? TOP_ROWS : FAV_SLOTS + 1];
|
||||
static char values[TOP_ROWS][20];
|
||||
@@ -52,6 +53,7 @@ static int build_top(const cam_info_t *current, int fav_slot)
|
||||
rows[ROW_CAPTION] = (ui_menu_row_t){"Caption", settings.caption ? "Shown" : "Hidden", false};
|
||||
rows[ROW_FORGET] = (ui_menu_row_t){"Forget a favourite", NULL, fav_count() == 0};
|
||||
rows[ROW_WIFI] = (ui_menu_row_t){"Wi-Fi setup", NULL, false};
|
||||
rows[ROW_POWER] = (ui_menu_row_t){"Power off", "knob wakes", false};
|
||||
rows[ROW_IP] = (ui_menu_row_t){board->name, net_ip_str(), true};
|
||||
int mv, pct;
|
||||
bool usb;
|
||||
@@ -189,6 +191,9 @@ menu_result_t menu_run(const cam_info_t *current, int *fav_slot_out)
|
||||
vTaskDelay(pdMS_TO_TICKS(800));
|
||||
esp_restart();
|
||||
break;
|
||||
case ROW_POWER:
|
||||
ui_menu_hide();
|
||||
power_off();
|
||||
case ROW_CLOSE:
|
||||
goto out;
|
||||
default:
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
#include "power.h"
|
||||
#include "board.h"
|
||||
#include "display.h"
|
||||
#include "leds.h"
|
||||
#include "ui.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/rtc_io.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_sleep.h"
|
||||
#include "esp_wifi.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
static const char *TAG = "power";
|
||||
|
||||
// Arm ext1 wake on the encoder pins. The S3 has one level mode for all selected
|
||||
// pins, so pick it from the knob's resting state: pins that are high wake on
|
||||
// low; if both rest low, wake on high. A single detent toggles both pins.
|
||||
static void arm_knob_wakeup(void)
|
||||
{
|
||||
int pins[2] = {board->enc_a, board->enc_b};
|
||||
uint64_t high = 0, low = 0;
|
||||
for (int i = 0; i < 2; i++) {
|
||||
rtc_gpio_init(pins[i]);
|
||||
rtc_gpio_set_direction(pins[i], RTC_GPIO_MODE_INPUT_ONLY);
|
||||
rtc_gpio_pulldown_dis(pins[i]);
|
||||
rtc_gpio_pullup_en(pins[i]);
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(5));
|
||||
for (int i = 0; i < 2; i++) {
|
||||
if (rtc_gpio_get_level(pins[i])) high |= 1ULL << pins[i];
|
||||
else low |= 1ULL << pins[i];
|
||||
}
|
||||
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); // keeps the RTC pull-ups alive
|
||||
if (high) esp_sleep_enable_ext1_wakeup_io(high, ESP_EXT1_WAKEUP_ANY_LOW);
|
||||
else esp_sleep_enable_ext1_wakeup_io(low, ESP_EXT1_WAKEUP_ANY_HIGH);
|
||||
ESP_LOGI(TAG, "wake on knob: A%d=%d B%d=%d", board->enc_a, !!(high & (1ULL << board->enc_a)),
|
||||
board->enc_b, !!(high & (1ULL << board->enc_b)));
|
||||
}
|
||||
|
||||
void power_off(void)
|
||||
{
|
||||
ESP_LOGI(TAG, "powering off");
|
||||
ui_status("Off", "turn the knob to wake");
|
||||
vTaskDelay(pdMS_TO_TICKS(700));
|
||||
leds_set_mode(LED_OFF, 0);
|
||||
vTaskDelay(pdMS_TO_TICKS(80)); // let the ring task push the dark frame
|
||||
display_off();
|
||||
esp_wifi_stop();
|
||||
gpio_set_level(board->pwr_en, 0); // peripheral rail off (screen, ring, codec)
|
||||
arm_knob_wakeup();
|
||||
esp_deep_sleep_start();
|
||||
}
|
||||
|
||||
void power_report_wakeup(void)
|
||||
{
|
||||
esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause();
|
||||
if (cause == ESP_SLEEP_WAKEUP_EXT1) ESP_LOGI(TAG, "woken by the knob");
|
||||
else if (cause != ESP_SLEEP_WAKEUP_UNDEFINED) ESP_LOGI(TAG, "wakeup cause %d", cause);
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
// Cuts the peripheral rail and puts the ESP32 into deep sleep; a turn of the knob wakes it.
|
||||
void power_off(void) __attribute__((noreturn));
|
||||
// Logs how we came up (cold boot vs. knob wake).
|
||||
void power_report_wakeup(void);
|
||||
+1
-1
@@ -29,7 +29,7 @@ void settings_save(void);
|
||||
void settings_request_portal(void);
|
||||
bool settings_take_portal_request(void);
|
||||
|
||||
#define FAV_SLOTS 12
|
||||
#define FAV_SLOTS 24
|
||||
const cam_info_t *fav_get(int slot); // NULL when empty
|
||||
void fav_set(int slot, const cam_info_t *cam);
|
||||
void fav_clear(int slot);
|
||||
|
||||
Reference in new issue
Block a user