/*
Adaptation du code source original de Lewis Le : deep sleep, réveil par bouton
ou accéléromètre BMA423
This is just a demonstration. Most of the functions are not implemented.
The main implementation is low-power standby.
The off-screen standby (not deep sleep) current is about 4mA.
Select standard motherboard and standard backplane for testing.
Created by Lewis he on October 10, 2019.
*/
#include
#include
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/timers.h"
#include "freertos/queue.h"
#include
#include "esp_wifi.h"
#include "esp_sleep.h"
#include
#include "gui.h"
#define G_EVENT_VBUS_PLUGIN _BV(0)
#define G_EVENT_VBUS_REMOVE _BV(1)
#define G_EVENT_CHARGE_DONE _BV(2)
#define G_EVENT_WIFI_SCAN_START _BV(3)
#define G_EVENT_WIFI_SCAN_DONE _BV(4)
#define G_EVENT_WIFI_CONNECTED _BV(5)
#define G_EVENT_WIFI_BEGIN _BV(6)
#define G_EVENT_WIFI_OFF _BV(7)
enum {
Q_EVENT_WIFI_SCAN_DONE,
Q_EVENT_WIFI_CONNECT,
Q_EVENT_BMA_INT,
Q_EVENT_AXP_INT,
} ;
#define DEFAULT_SCREEN_TIMEOUT 5*1000
#define uS_TO_S_FACTOR 1000000ULL /* Conversion factor for micro seconds to seconds */
#define TIME_TO_SLEEP 20 /* Time ESP32 will go to sleep (in seconds) */
#define WATCH_FLAG_SLEEP_MODE _BV(1)
#define WATCH_FLAG_SLEEP_EXIT _BV(2)
#define WATCH_FLAG_BMA_IRQ _BV(3)
#define WATCH_FLAG_AXP_IRQ _BV(4)
/* Pointers */
TTGOClass *watch;
BMA *sensor;
AXP20X_Class *power;
TFT_eSPI *tft = nullptr;
QueueHandle_t g_event_queue_handle = NULL;
EventGroupHandle_t g_event_group = NULL;
EventGroupHandle_t isr_group = NULL;
bool lenergy = false;
/* PROTOTYPES */
void print_wakeup_reason();
void buttonClicked();
void setupNetwork()
{
WiFi.mode(WIFI_STA);
WiFi.onEvent([](WiFiEvent_t event, WiFiEventInfo_t info) {
xEventGroupClearBits(g_event_group, G_EVENT_WIFI_CONNECTED);
}, WiFiEvent_t::SYSTEM_EVENT_STA_DISCONNECTED);
WiFi.onEvent([](WiFiEvent_t event, WiFiEventInfo_t info) {
uint8_t data = Q_EVENT_WIFI_SCAN_DONE;
xQueueSend(g_event_queue_handle, &data, portMAX_DELAY);
}, WiFiEvent_t::SYSTEM_EVENT_SCAN_DONE);
WiFi.onEvent([](WiFiEvent_t event, WiFiEventInfo_t info) {
xEventGroupSetBits(g_event_group, G_EVENT_WIFI_CONNECTED);
}, WiFiEvent_t::SYSTEM_EVENT_STA_CONNECTED);
WiFi.onEvent([](WiFiEvent_t event, WiFiEventInfo_t info) {
//wifi_connect_status(true);
}, WiFiEvent_t::SYSTEM_EVENT_STA_GOT_IP);
}
/*******************************************/
/* Bascule en mode basse consommation */
/*******************************************/
void low_energy()
{
// Le rétro-éclairage est actif
if (watch->bl->isOn()) {
xEventGroupSetBits(isr_group, WATCH_FLAG_SLEEP_MODE);
watch->closeBL();
watch->bma->enableStepCountInterrupt(false);
watch->displaySleep();
watch->stopLvglTick();
if (!WiFi.isConnected()) {
lenergy = true;
WiFi.mode(WIFI_OFF);
// Diminue la fréquence du CPU à 20MHz pour réduire la consommation
setCpuFrequencyMhz(20);
Serial.println("ENTER IN LIGHT SLEEEP MODE");
delay(50);
// Réveil avec le bouton principal
// Réveil avec le bouton principal
gpio_wakeup_enable ((gpio_num_t)AXP202_INT, GPIO_INTR_LOW_LEVEL);
esp_sleep_enable_gpio_wakeup();
esp_light_sleep_start();
}
// Le rétro-éclairage est éteint
} else {
watch->displayWakeup();
watch->openBL();
watch->startLvglTick();
lv_disp_trig_activity(NULL);
updateStepCounter(watch->bma->getCounter());
updateBatteryLevel();
updateBatteryIcon(LV_ICON_CALCULATION);
watch->rtc->syncToSystem();
watch->bma->enableStepCountInterrupt();
}
}
void setup()
{
Serial.begin(115200);
print_wakeup_reason();
//Create a program that allows the required message objects and group flags
g_event_queue_handle = xQueueCreate(20, sizeof(uint8_t));
g_event_group = xEventGroupCreate();
isr_group = xEventGroupCreate();
watch = TTGOClass::getWatch();
tft = watch->tft;
watch->begin();
// Enregistre les pointeurs
// Enregistre les pointeurs
power = watch->power;
// User button handler | Superviseur bouton utilisateur
watch->button->setClickHandler(buttonClicked);
// Turn on the IRQ used
watch->power->adc1Enable(AXP202_BATT_VOL_ADC1 | AXP202_BATT_CUR_ADC1 | AXP202_VBUS_VOL_ADC1 | AXP202_VBUS_CUR_ADC1, AXP202_ON);
watch->power->enableIRQ(AXP202_VBUS_REMOVED_IRQ | AXP202_VBUS_CONNECT_IRQ | AXP202_CHARGING_FINISHED_IRQ, AXP202_ON);
watch->power->clearIRQ();
// Turn off unused power
watch->power->setPowerOutPut(AXP202_EXTEN, AXP202_OFF);
watch->power->setPowerOutPut(AXP202_DCDC2, AXP202_OFF);
watch->power->setPowerOutPut(AXP202_LDO3, AXP202_OFF);
watch->power->setPowerOutPut(AXP202_LDO4, AXP202_OFF);
//Connection interrupted to the specified pin
pinMode(BMA423_INT1, INPUT);
attachInterrupt(BMA423_INT1, [] {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
EventBits_t bits = xEventGroupGetBitsFromISR(isr_group);
if (bits & WATCH_FLAG_SLEEP_MODE)
{
//! For quick wake up, use the group flag
xEventGroupSetBitsFromISR(isr_group, WATCH_FLAG_SLEEP_EXIT | WATCH_FLAG_BMA_IRQ, &xHigherPriorityTaskWoken);
} else
{
uint8_t data = Q_EVENT_BMA_INT;
xQueueSendFromISR(g_event_queue_handle, &data, &xHigherPriorityTaskWoken);
}
if (xHigherPriorityTaskWoken)
{
portYIELD_FROM_ISR ();
}
}, RISING);
sensor = watch->bma;
// Optionnel
// // Accel parameter structure
// Acfg cfg;
// /*!
// Output data rate in Hz, Optional parameters:
// - BMA4_OUTPUT_DATA_RATE_0_78HZ
// - BMA4_OUTPUT_DATA_RATE_1_56HZ
// - BMA4_OUTPUT_DATA_RATE_3_12HZ
// - BMA4_OUTPUT_DATA_RATE_6_25HZ
// - BMA4_OUTPUT_DATA_RATE_12_5HZ
// - BMA4_OUTPUT_DATA_RATE_25HZ
// - BMA4_OUTPUT_DATA_RATE_50HZ
// - BMA4_OUTPUT_DATA_RATE_100HZ
// - BMA4_OUTPUT_DATA_RATE_200HZ
// - BMA4_OUTPUT_DATA_RATE_400HZ
// - BMA4_OUTPUT_DATA_RATE_800HZ
// - BMA4_OUTPUT_DATA_RATE_1600HZ
// */
// cfg.odr = BMA4_OUTPUT_DATA_RATE_100HZ;
// /*!
// G-range, Optional parameters:
// - BMA4_ACCEL_RANGE_2G
// - BMA4_ACCEL_RANGE_4G
// - BMA4_ACCEL_RANGE_8G
// - BMA4_ACCEL_RANGE_16G
// */
// cfg.range = BMA4_ACCEL_RANGE_4G;
// /*!
// Bandwidth parameter, determines filter configuration, Optional parameters:
// - BMA4_ACCEL_OSR4_AVG1
// - BMA4_ACCEL_OSR2_AVG2
// - BMA4_ACCEL_NORMAL_AVG4
// - BMA4_ACCEL_CIC_AVG8
// - BMA4_ACCEL_RES_AVG16
// - BMA4_ACCEL_RES_AVG32
// - BMA4_ACCEL_RES_AVG64
// - BMA4_ACCEL_RES_AVG128
// */
// cfg.bandwidth = BMA4_ACCEL_NORMAL_AVG4;
//
// /*! Filter performance mode , Optional parameters:
// - BMA4_CIC_AVG_MODE
// - BMA4_CONTINUOUS_MODE
// */
// cfg.perf_mode = BMA4_CONTINUOUS_MODE;
//
// // Configure the BMA423 accelerometer
// sensor->accelConfig(cfg);
//
// Enable BMA423 accelerometer
// Active le BMA 423
sensor->enableAccel();
// Enable BMA423 isDoubleClick feature
sensor->enableFeature(BMA423_WAKEUP, true);
// Envoi un signal sur la broche 39 de l'ESP32 dès qu'un mouvement est détecté
sensor->enableWakeupInterrupt();
// Récupère les interruptions du contrôleur d'alimentation AXP202
pinMode(AXP202_INT, INPUT);
attachInterrupt(AXP202_INT, [] {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
EventBits_t bits = xEventGroupGetBitsFromISR(isr_group);
if (bits & WATCH_FLAG_SLEEP_MODE)
{
//! For quick wake up, use the group flag
xEventGroupSetBitsFromISR(isr_group, WATCH_FLAG_SLEEP_EXIT | WATCH_FLAG_AXP_IRQ, &xHigherPriorityTaskWoken);
} else
{
uint8_t data = Q_EVENT_AXP_INT;
xQueueSendFromISR(g_event_queue_handle, &data, &xHigherPriorityTaskWoken);
}
if (xHigherPriorityTaskWoken)
{
portYIELD_FROM_ISR ();
}
}, FALLING);
//Check if the RTC clock matches, if not, use compile time
watch->rtc->check();
//Synchronize time to system time
watch->rtc->syncToSystem();
#ifdef LILYGO_WATCH_HAS_BUTTON
//Set the user button long press to restart
watch->button->setLongClickHandler([]() {
Serial.println("Pressed Restart Button,Restart now ...");
delay(1000);
esp_restart();
});
#endif
//Setting up the network
setupNetwork();
//Initialize lvgl
watch->lvgl_begin();
//Execute your own GUI interface
setupGui();
//Clear lvgl counter
lv_disp_trig_activity(NULL);
#ifdef LILYGO_WATCH_HAS_BUTTON
//In lvgl we call the button processing regularly
lv_task_create([](lv_task_t *args) {
watch->button->loop();
}, 30, 1, nullptr);
#endif
//When the initialization is complete, turn on the backlight
watch->openBL();
}
void loop()
{
bool rlst;
uint8_t data;
//! Fast response wake-up interrupt
EventBits_t bits = xEventGroupGetBits(isr_group);
if (bits & WATCH_FLAG_SLEEP_EXIT) {
Serial.println("EXIT SLEEP MODE");
if (lenergy) {
lenergy = false;
// rtc_clk_cpu_freq_set(RTC_CPU_FREQ_160M);
setCpuFrequencyMhz(160);
}
low_energy();
// Détruit le message WATCH_FLAG_BMA_IRQ (réveil avec le BMA 423)
if (bits & WATCH_FLAG_BMA_IRQ) {
Serial.printf("WATCH_FLAG_BMA_IRQ bits=%u\n", bits);
do {
rlst = watch->bma->readInterrupt();
} while (!rlst);
xEventGroupClearBits(isr_group, WATCH_FLAG_BMA_IRQ);
}
if (bits & WATCH_FLAG_AXP_IRQ) {
Serial.printf("WATCH_FLAG_AXP_IRQ bits=%u\n", bits);
watch->power->readIRQ();
watch->power->clearIRQ();
//TODO: Only accept axp power pek key short press
xEventGroupClearBits(isr_group, WATCH_FLAG_AXP_IRQ);
}
xEventGroupClearBits(isr_group, WATCH_FLAG_SLEEP_EXIT);
xEventGroupClearBits(isr_group, WATCH_FLAG_SLEEP_MODE);
}
if ((bits & WATCH_FLAG_SLEEP_MODE)) {
//! No event processing after entering the information screen
return;
}
//! Normal polling
if (xQueueReceive(g_event_queue_handle, &data, 5 / portTICK_RATE_MS) == pdPASS) {
switch (data) {
case Q_EVENT_BMA_INT:
do {
rlst = watch->bma->readInterrupt();
} while (!rlst);
break;
case Q_EVENT_AXP_INT:
watch->power->readIRQ();
if (watch->power->isPEKShortPressIRQ()) {
// Bascule en mode basse consommation lorsqu'on appuie sur le bouton principal
watch->power->clearIRQ();
low_energy();
return;
}
watch->power->clearIRQ();
break;
case Q_EVENT_WIFI_SCAN_DONE: {
int16_t len = WiFi.scanComplete();
for (int i = 0; i < len; ++i) {
wifi_list_add(WiFi.SSID(i).c_str());
}
break;
}
default:
break;
}
}
if (lv_disp_get_inactive_time(NULL) < DEFAULT_SCREEN_TIMEOUT) {
lv_task_handler();
} else {
low_energy();
}
}
void buttonClicked(){
Serial.println("User button clicked, enter in deep sleep mode");
// Set screen and touch to sleep mode
watch->displaySleep();
/*
When using T - Watch2020V1, you can directly call power->powerOff(),
if you use the 2019 version of TWatch, choose to turn off
according to the power you need to turn off
*/
#ifdef LILYGO_WATCH_2020_V1
watch->powerOff();
// LDO2 is used to power the display, and LDO2 can be turned off if needed
// power->setPowerOutPut(AXP202_LDO2, false);
#else
power->setPowerOutPut(AXP202_LDO3, false);
power->setPowerOutPut(AXP202_LDO4, false);
power->setPowerOutPut(AXP202_LDO2, false);
// The following power channels are not used
power->setPowerOutPut(AXP202_EXTEN, false);
power->setPowerOutPut(AXP202_DCDC2, false);
#endif
esp_sleep_enable_ext0_wakeup((gpio_num_t)AXP202_INT, LOW);
// Active le réveil automatique. Utile pour un tracker GPS
esp_sleep_enable_timer_wakeup(uS_TO_S_FACTOR * TIME_TO_SLEEP);
esp_deep_sleep_start();
}
// Affiche la cause du réveil de l'ESP32
void print_wakeup_reason(){
esp_sleep_wakeup_cause_t wakeup_reason;
wakeup_reason = esp_sleep_get_wakeup_cause();
switch(wakeup_reason)
{
case 1 : Serial.println("Wakeup caused by external signal using RTC_IO"); break;
case 2 : Serial.println("Wakeup caused by external signal using RTC_CNTL"); break;
case 3 : Serial.println("Wakeup caused by touchpad"); break;
case 4 : Serial.println("Wakeup caused by timer"); break;
case 5 : Serial.println("Wakeup caused by ULP program"); break;
default : Serial.println("Wakeup was not caused by deep sleep"); break;
}
}
/*
Copyright (c) 2019 lewis he
This is just a demonstration. Most of the functions are not implemented.
The main implementation is low-power standby.
The off-screen standby (not deep sleep) current is about 4mA.
Select standard motherboard and standard backplane for testing.
Created by Lewis he on October 10, 2019.
*/
// Please select the model you want to use in config.h
//#include "config.h"
#include
#include
#include "gui.h"
#include
#include "string.h"
#include
#include "FS.h"
#include "SD.h"
#include
#define RTC_TIME_ZONE "CST-8"
LV_FONT_DECLARE(Geometr);
LV_FONT_DECLARE(Ubuntu);
LV_IMG_DECLARE(bg);
LV_IMG_DECLARE(bg1);
LV_IMG_DECLARE(bg2);
LV_IMG_DECLARE(bg3);
LV_IMG_DECLARE(WALLPAPER_1_IMG);
LV_IMG_DECLARE(WALLPAPER_2_IMG);
LV_IMG_DECLARE(WALLPAPER_3_IMG);
LV_IMG_DECLARE(step);
LV_IMG_DECLARE(menu);
LV_IMG_DECLARE(wifi);
LV_IMG_DECLARE(light);
LV_IMG_DECLARE(bluetooth);
LV_IMG_DECLARE(sd);
LV_IMG_DECLARE(setting);
LV_IMG_DECLARE(on);
LV_IMG_DECLARE(off);
LV_IMG_DECLARE(level1);
LV_IMG_DECLARE(level2);
LV_IMG_DECLARE(level3);
LV_IMG_DECLARE(iexit);
LV_IMG_DECLARE(modules);
LV_IMG_DECLARE(CAMERA_PNG);
extern EventGroupHandle_t g_event_group;
extern QueueHandle_t g_event_queue_handle;
static lv_style_t settingStyle;
static lv_obj_t *mainBar = nullptr;
static lv_obj_t *timeLabel = nullptr;
static lv_obj_t *menuBtn = nullptr;
static uint8_t globalIndex = 0;
static void lv_update_task(struct _lv_task_t *);
static void lv_battery_task(struct _lv_task_t *);
static void updateTime();
static void view_event_handler(lv_obj_t *obj, lv_event_t event);
static void wifi_event_cb();
static void sd_event_cb();
static void setting_event_cb();
static void light_event_cb();
static void modules_event_cb();
static void camera_event_cb();
static void wifi_destory();
class StatusBar
{
typedef struct {
bool vaild;
lv_obj_t *icon;
} lv_status_bar_t;
public:
StatusBar()
{
memset(_array, 0, sizeof(_array));
}
void createIcons(lv_obj_t *par)
{
_par = par;
static lv_style_t barStyle;
lv_style_init(&barStyle);
lv_style_set_radius(&barStyle, LV_OBJ_PART_MAIN, 0);
lv_style_set_bg_color(&barStyle, LV_OBJ_PART_MAIN, LV_COLOR_GRAY);
lv_style_set_bg_opa(&barStyle, LV_OBJ_PART_MAIN, LV_OPA_20);
lv_style_set_border_width(&barStyle, LV_OBJ_PART_MAIN, 0);
lv_style_set_text_color(&barStyle, LV_OBJ_PART_MAIN, LV_COLOR_WHITE);
lv_style_set_image_recolor(&barStyle, LV_OBJ_PART_MAIN, LV_COLOR_WHITE);
_bar = lv_cont_create(_par, NULL);
lv_obj_set_size(_bar, LV_HOR_RES, _barHeight);
lv_obj_add_style(_bar, LV_OBJ_PART_MAIN, &barStyle);
_array[0].icon = lv_label_create(_bar, NULL);
lv_label_set_text(_array[0].icon, "100%");
_array[1].icon = lv_img_create(_bar, NULL);
lv_img_set_src(_array[1].icon, LV_SYMBOL_BATTERY_FULL);
_array[2].icon = lv_img_create(_bar, NULL);
lv_img_set_src(_array[2].icon, LV_SYMBOL_WIFI);
lv_obj_set_hidden(_array[2].icon, true);
_array[3].icon = lv_img_create(_bar, NULL);
lv_img_set_src(_array[3].icon, LV_SYMBOL_BLUETOOTH);
lv_obj_set_hidden(_array[3].icon, true);
//step counter
_array[4].icon = lv_img_create(_bar, NULL);
lv_img_set_src(_array[4].icon, &step);
lv_obj_align(_array[4].icon, _bar, LV_ALIGN_IN_LEFT_MID, 10, 0);
_array[5].icon = lv_label_create(_bar, NULL);
lv_label_set_text(_array[5].icon, "0");
lv_obj_align(_array[5].icon, _array[4].icon, LV_ALIGN_OUT_RIGHT_MID, 5, 0);
refresh();
}
void setStepCounter(uint32_t counter)
{
lv_label_set_text(_array[5].icon, String(counter).c_str());
lv_obj_align(_array[5].icon, _array[4].icon, LV_ALIGN_OUT_RIGHT_MID, 5, 0);
}
void updateLevel(int level)
{
lv_label_set_text(_array[0].icon, (String(level) + "%").c_str());
refresh();
}
void updateBatteryIcon(lv_icon_battery_t icon)
{
const char *icons[6] = {LV_SYMBOL_BATTERY_EMPTY, LV_SYMBOL_BATTERY_1, LV_SYMBOL_BATTERY_2, LV_SYMBOL_BATTERY_3, LV_SYMBOL_BATTERY_FULL, LV_SYMBOL_CHARGE};
lv_img_set_src(_array[1].icon, icons[icon]);
refresh();
}
void show(lv_icon_status_bar_t icon)
{
lv_obj_set_hidden(_array[icon].icon, false);
refresh();
}
void hidden(lv_icon_status_bar_t icon)
{
lv_obj_set_hidden(_array[icon].icon, true);
refresh();
}
uint8_t height()
{
return _barHeight;
}
lv_obj_t *self()
{
return _bar;
}
private:
void refresh()
{
int prev;
for (int i = 0; i < 4; i++) {
if (!lv_obj_get_hidden(_array[i].icon)) {
if (i == LV_STATUS_BAR_BATTERY_LEVEL) {
lv_obj_align(_array[i].icon, NULL, LV_ALIGN_IN_RIGHT_MID, 0, 0);
} else {
lv_obj_align(_array[i].icon, _array[prev].icon, LV_ALIGN_OUT_LEFT_MID, iconOffset, 0);
}
prev = i;
}
}
};
lv_obj_t *_bar = nullptr;
lv_obj_t *_par = nullptr;
uint8_t _barHeight = 30;
lv_status_bar_t _array[6];
const int8_t iconOffset = -5;
};
class MenuBar
{
public:
typedef struct {
const char *name;
void *img;
void (*event_cb)();
} lv_menu_config_t;
MenuBar()
{
_cont = nullptr;
_view = nullptr;
_exit = nullptr;
_obj = nullptr;
_vp = nullptr;
};
~MenuBar() {};
void createMenu(lv_menu_config_t *config, int count, lv_event_cb_t event_cb, int direction = 1)
{
static lv_style_t menuStyle;
lv_style_init(&menuStyle);
lv_style_set_radius(&menuStyle, LV_OBJ_PART_MAIN, 0);
lv_style_set_bg_color(&menuStyle, LV_OBJ_PART_MAIN, LV_COLOR_GRAY);
lv_style_set_bg_opa(&menuStyle, LV_OBJ_PART_MAIN, LV_OPA_0);
lv_style_set_border_width(&menuStyle, LV_OBJ_PART_MAIN, 0);
lv_style_set_text_color(&menuStyle, LV_OBJ_PART_MAIN, LV_COLOR_WHITE);
lv_style_set_image_recolor(&menuStyle, LV_OBJ_PART_MAIN, LV_COLOR_WHITE);
_count = count;
_vp = new lv_point_t [count];
_obj = new lv_obj_t *[count];
for (int i = 0; i < count; i++) {
if (direction) {
_vp[i].x = 0;
_vp[i].y = i;
} else {
_vp[i].x = i;
_vp[i].y = 0;
}
}
_cont = lv_cont_create(lv_scr_act(), NULL);
lv_obj_set_size(_cont, LV_HOR_RES, LV_VER_RES - 30);
lv_obj_align(_cont, NULL, LV_ALIGN_OUT_BOTTOM_MID, 0, 0);
lv_obj_add_style(_cont, LV_OBJ_PART_MAIN, &menuStyle);
_view = lv_tileview_create(_cont, NULL);
lv_tileview_set_valid_positions(_view, _vp, count );
lv_tileview_set_edge_flash(_view, false);
lv_obj_align(_view, NULL, LV_ALIGN_CENTER, 0, 0);
lv_page_set_scrlbar_mode(_view, LV_SCRLBAR_MODE_OFF);
lv_obj_add_style(_view, LV_OBJ_PART_MAIN, &menuStyle);
lv_coord_t _w = lv_obj_get_width(_view) ;
lv_coord_t _h = lv_obj_get_height(_view);
for (int i = 0; i < count; i++) {
_obj[i] = lv_cont_create(_view, _view);
lv_obj_set_size(_obj[i], _w, _h);
lv_obj_t *img = lv_img_create(_obj[i], NULL);
lv_img_set_src(img, config[i].img);
lv_obj_align(img, _obj[i], LV_ALIGN_CENTER, 0, 0);
lv_obj_t *label = lv_label_create(_obj[i], NULL);
lv_label_set_text(label, config[i].name);
lv_obj_align(label, img, LV_ALIGN_OUT_BOTTOM_MID, 0, 0);
i == 0 ? lv_obj_align(_obj[i], NULL, LV_ALIGN_CENTER, 0, 0) : lv_obj_align(_obj[i], _obj[i - 1], direction ? LV_ALIGN_OUT_BOTTOM_MID : LV_ALIGN_OUT_RIGHT_MID, 0, 0);
lv_tileview_add_element(_view, _obj[i]);
lv_obj_set_click(_obj[i], true);
lv_obj_set_event_cb(_obj[i], event_cb);
}
_exit = lv_imgbtn_create(lv_scr_act(), NULL);
lv_imgbtn_set_src(_exit, LV_BTN_STATE_RELEASED, &menu);
lv_imgbtn_set_src(_exit, LV_BTN_STATE_PRESSED, &menu);
lv_imgbtn_set_src(_exit, LV_BTN_STATE_CHECKED_PRESSED, &menu);
lv_imgbtn_set_src(_exit, LV_BTN_STATE_CHECKED_RELEASED, &menu);
lv_obj_align(_exit, NULL, LV_ALIGN_IN_BOTTOM_RIGHT, -20, -20);
lv_obj_set_event_cb(_exit, event_cb);
lv_obj_set_top(_exit, true);
}
lv_obj_t *exitBtn() const
{
return _exit;
}
lv_obj_t *self() const
{
return _cont;
}
void hidden(bool en = true)
{
lv_obj_set_hidden(_cont, en);
lv_obj_set_hidden(_exit, en);
}
lv_obj_t *obj(int index) const
{
if (index > _count)return nullptr;
return _obj[index];
}
private:
lv_obj_t *_cont, *_view, *_exit, * *_obj;
lv_point_t *_vp ;
int _count = 0;
};
MenuBar::lv_menu_config_t _cfg[7] = {
{.name = "WiFi", .img = (void *) &wifi, .event_cb = wifi_event_cb},
{.name = "Bluetooth", .img = (void *) &bluetooth, /*.event_cb = bluetooth_event_cb*/},
{.name = "SD Card", .img = (void *) &sd, /*.event_cb =sd_event_cb*/},
{.name = "Light", .img = (void *) &light, /*.event_cb = light_event_cb*/},
{.name = "Setting", .img = (void *) &setting, /*.event_cb = setting_event_cb */},
{.name = "Modules", .img = (void *) &modules, /*.event_cb = modules_event_cb */},
{.name = "Camera", .img = (void *) &CAMERA_PNG, /*.event_cb = camera_event_cb*/ }
};
MenuBar menuBars;
StatusBar bar;
static void event_handler(lv_obj_t *obj, lv_event_t event)
{
if (event == LV_EVENT_SHORT_CLICKED) { //! Event callback Is in here
if (obj == menuBtn) {
lv_obj_set_hidden(mainBar, true);
if (menuBars.self() == nullptr) {
menuBars.createMenu(_cfg, sizeof(_cfg) / sizeof(_cfg[0]), view_event_handler);
lv_obj_align(menuBars.self(), bar.self(), LV_ALIGN_OUT_BOTTOM_MID, 0, 0);
} else {
menuBars.hidden(false);
}
}
}
}
void setupGui()
{
lv_style_init(&settingStyle);
lv_style_set_radius(&settingStyle, LV_OBJ_PART_MAIN, 0);
lv_style_set_bg_color(&settingStyle, LV_OBJ_PART_MAIN, LV_COLOR_GRAY);
lv_style_set_bg_opa(&settingStyle, LV_OBJ_PART_MAIN, LV_OPA_0);
lv_style_set_border_width(&settingStyle, LV_OBJ_PART_MAIN, 0);
lv_style_set_text_color(&settingStyle, LV_OBJ_PART_MAIN, LV_COLOR_WHITE);
lv_style_set_image_recolor(&settingStyle, LV_OBJ_PART_MAIN, LV_COLOR_WHITE);
//Create wallpaper
void *images[] = {(void *) &bg, (void *) &bg1, (void *) &bg2, (void *) &bg3 };
lv_obj_t *scr = lv_scr_act();
lv_obj_t *img_bin = lv_img_create(scr, NULL); /*Create an image object*/
srand((int)time(0));
int r = rand() % 4;
lv_img_set_src(img_bin, images[r]);
lv_obj_align(img_bin, NULL, LV_ALIGN_CENTER, 0, 0);
//! bar
bar.createIcons(scr);
updateBatteryLevel();
lv_icon_battery_t icon = LV_ICON_CALCULATION;
TTGOClass *ttgo = TTGOClass::getWatch();
if (ttgo->power->isChargeing()) {
icon = LV_ICON_CHARGE;
}
updateBatteryIcon(icon);
//! main
static lv_style_t mainStyle;
lv_style_init(&mainStyle);
lv_style_set_radius(&mainStyle, LV_OBJ_PART_MAIN, 0);
lv_style_set_bg_color(&mainStyle, LV_OBJ_PART_MAIN, LV_COLOR_GRAY);
lv_style_set_bg_opa(&mainStyle, LV_OBJ_PART_MAIN, LV_OPA_0);
lv_style_set_border_width(&mainStyle, LV_OBJ_PART_MAIN, 0);
lv_style_set_text_color(&mainStyle, LV_OBJ_PART_MAIN, LV_COLOR_WHITE);
lv_style_set_image_recolor(&mainStyle, LV_OBJ_PART_MAIN, LV_COLOR_WHITE);
mainBar = lv_cont_create(scr, NULL);
lv_obj_set_size(mainBar, LV_HOR_RES, LV_VER_RES - bar.height());
lv_obj_add_style(mainBar, LV_OBJ_PART_MAIN, &mainStyle);
lv_obj_align(mainBar, bar.self(), LV_ALIGN_OUT_BOTTOM_MID, 0, 0);
//! Time
static lv_style_t timeStyle;
lv_style_copy(&timeStyle, &mainStyle);
lv_style_set_text_font(&timeStyle, LV_STATE_DEFAULT, &Ubuntu);
timeLabel = lv_label_create(mainBar, NULL);
lv_obj_add_style(timeLabel, LV_OBJ_PART_MAIN, &timeStyle);
updateTime();
//! menu
static lv_style_t style_pr;
lv_style_init(&style_pr);
lv_style_set_image_recolor(&style_pr, LV_OBJ_PART_MAIN, LV_COLOR_BLACK);
lv_style_set_text_color(&style_pr, LV_OBJ_PART_MAIN, lv_color_hex3(0xaaa));
menuBtn = lv_imgbtn_create(mainBar, NULL);
lv_imgbtn_set_src(menuBtn, LV_BTN_STATE_RELEASED, &menu);
lv_imgbtn_set_src(menuBtn, LV_BTN_STATE_PRESSED, &menu);
lv_imgbtn_set_src(menuBtn, LV_BTN_STATE_CHECKED_RELEASED, &menu);
lv_imgbtn_set_src(menuBtn, LV_BTN_STATE_CHECKED_PRESSED, &menu);
lv_obj_add_style(menuBtn, LV_OBJ_PART_MAIN, &style_pr);
lv_obj_align(menuBtn, mainBar, LV_ALIGN_OUT_BOTTOM_MID, 0, -70);
lv_obj_set_event_cb(menuBtn, event_handler);
lv_task_create(lv_update_task, 1000, LV_TASK_PRIO_LOWEST, NULL);
lv_task_create(lv_battery_task, 30000, LV_TASK_PRIO_LOWEST, NULL);
}
void updateStepCounter(uint32_t counter)
{
bar.setStepCounter(counter);
}
static void updateTime()
{
time_t now;
struct tm info;
char buf[64];
time(&now);
localtime_r(&now, &info);
strftime(buf, sizeof(buf), "%H:%M", &info);
lv_label_set_text(timeLabel, buf);
lv_obj_align(timeLabel, NULL, LV_ALIGN_IN_TOP_MID, 0, 20);
TTGOClass *ttgo = TTGOClass::getWatch();
ttgo->rtc->syncToRtc();
}
void updateBatteryLevel()
{
TTGOClass *ttgo = TTGOClass::getWatch();
int p = ttgo->power->getBattPercentage();
bar.updateLevel(p);
}
void updateBatteryIcon(lv_icon_battery_t icon)
{
if (icon >= LV_ICON_CALCULATION) {
TTGOClass *ttgo = TTGOClass::getWatch();
int level = ttgo->power->getBattPercentage();
if (level > 95)icon = LV_ICON_BAT_FULL;
else if (level > 80)icon = LV_ICON_BAT_3;
else if (level > 45)icon = LV_ICON_BAT_2;
else if (level > 20)icon = LV_ICON_BAT_1;
else icon = LV_ICON_BAT_EMPTY;
}
bar.updateBatteryIcon(icon);
}
static void lv_update_task(struct _lv_task_t *data)
{
updateTime();
}
static void lv_battery_task(struct _lv_task_t *data)
{
updateBatteryLevel();
}
static void view_event_handler(lv_obj_t *obj, lv_event_t event)
{
int size = sizeof(_cfg) / sizeof(_cfg[0]);
if (event == LV_EVENT_SHORT_CLICKED) {
if (obj == menuBars.exitBtn()) {
menuBars.hidden();
lv_obj_set_hidden(mainBar, false);
return;
}
for (int i = 0; i < size; i++) {
if (obj == menuBars.obj(i)) {
if (_cfg[i].event_cb != nullptr) {
menuBars.hidden();
_cfg[i].event_cb();
}
return;
}
}
}
}
/*****************************************************************
*
* ! Keyboard Class
*
*/
class Keyboard
{
public:
typedef enum {
KB_EVENT_OK,
KB_EVENT_EXIT,
} kb_event_t;
typedef void (*kb_event_cb)(kb_event_t event);
Keyboard()
{
_kbCont = nullptr;
};
~Keyboard()
{
if (_kbCont)
lv_obj_del(_kbCont);
_kbCont = nullptr;
};
void create(lv_obj_t *parent = nullptr)
{
static lv_style_t kbStyle;
lv_style_init(&kbStyle);
lv_style_set_radius(&kbStyle, LV_OBJ_PART_MAIN, 0);
lv_style_set_bg_color(&kbStyle, LV_OBJ_PART_MAIN, LV_COLOR_GRAY);
lv_style_set_bg_opa(&kbStyle, LV_OBJ_PART_MAIN, LV_OPA_0);
lv_style_set_border_width(&kbStyle, LV_OBJ_PART_MAIN, 0);
lv_style_set_text_color(&kbStyle, LV_OBJ_PART_MAIN, LV_COLOR_WHITE);
lv_style_set_image_recolor(&kbStyle, LV_OBJ_PART_MAIN, LV_COLOR_WHITE);
if (parent == nullptr) {
parent = lv_scr_act();
}
_kbCont = lv_cont_create(parent, NULL);
lv_obj_set_size(_kbCont, LV_HOR_RES, LV_VER_RES - 30);
lv_obj_align(_kbCont, NULL, LV_ALIGN_CENTER, 0, 0);
lv_obj_add_style(_kbCont, LV_OBJ_PART_MAIN, &kbStyle);
lv_obj_t *ta = lv_textarea_create(_kbCont, NULL);
lv_obj_set_height(ta, 40);
lv_textarea_set_one_line(ta, true);
lv_textarea_set_pwd_mode(ta, false);
lv_textarea_set_text(ta, "");
lv_obj_align(ta, _kbCont, LV_ALIGN_IN_TOP_MID, 10, 10);
lv_obj_t *kb = lv_keyboard_create(_kbCont, NULL);
lv_keyboard_set_map(kb, LV_KEYBOARD_MODE_TEXT_LOWER, btnm_mapplus[0]);
lv_obj_set_height(kb, LV_VER_RES / 3 * 2);
lv_obj_set_width(kb, 240);
lv_obj_align(kb, _kbCont, LV_ALIGN_IN_BOTTOM_MID, 0, 0);
lv_keyboard_set_textarea(kb, ta);
lv_obj_add_style(kb, LV_OBJ_PART_MAIN, &kbStyle);
lv_obj_add_style(ta, LV_OBJ_PART_MAIN, &kbStyle);
lv_obj_set_event_cb(kb, __kb_event_cb);
_kb = this;
}
void align(const lv_obj_t *base, lv_align_t align, lv_coord_t x = 0, lv_coord_t y = 0)
{
lv_obj_align(_kbCont, base, align, x, y);
}
static void __kb_event_cb(lv_obj_t *kb, lv_event_t event)
{
if (event != LV_EVENT_VALUE_CHANGED && event != LV_EVENT_LONG_PRESSED_REPEAT) return;
lv_keyboard_ext_t *ext = (lv_keyboard_ext_t *)lv_obj_get_ext_attr(kb);
const char *txt = lv_btnmatrix_get_active_btn_text(kb);
if (txt == NULL) return;
static int index = 0;
if (strcmp(txt, LV_SYMBOL_OK) == 0) {
strcpy(__buf, lv_textarea_get_text(ext->ta));
if (_kb->_cb != nullptr) {
_kb->_cb(KB_EVENT_OK);
}
return;
} else if (strcmp(txt, "Exit") == 0) {
if (_kb->_cb != nullptr) {
_kb->_cb(KB_EVENT_EXIT);
}
return;
} else if (strcmp(txt, LV_SYMBOL_RIGHT) == 0) {
index = index + 1 >= sizeof(btnm_mapplus) / sizeof(btnm_mapplus[0]) ? 0 : index + 1;
lv_keyboard_set_map(kb, LV_KEYBOARD_MODE_TEXT_LOWER, btnm_mapplus[index]);
return;
} else if (strcmp(txt, "Del") == 0) {
lv_textarea_del_char(ext->ta);
} else {
lv_textarea_add_text(ext->ta, txt);
}
}
void setKeyboardEvent(kb_event_cb cb)
{
_cb = cb;
}
const char *getText()
{
return (const char *)__buf;
}
void hidden(bool en = true)
{
lv_obj_set_hidden(_kbCont, en);
}
private:
lv_obj_t *_kbCont = nullptr;
kb_event_cb _cb = nullptr;
static const char *btnm_mapplus[10][23];
static Keyboard *_kb;
static char __buf[128];
};
char Keyboard::__buf[128];
Keyboard *Keyboard::_kb = nullptr;
const char *Keyboard::btnm_mapplus[10][23] = {
{
"a", "b", "c", "\n",
"d", "e", "f", "\n",
"g", "h", "i", "\n",
LV_SYMBOL_OK, "Del", "Exit", LV_SYMBOL_RIGHT, ""
},
{
"j", "k", "l", "\n",
"n", "m", "o", "\n",
"p", "q", "r", "\n",
LV_SYMBOL_OK, "Del", "Exit", LV_SYMBOL_RIGHT, ""
},
{
"s", "t", "u", "\n",
"v", "w", "x", "\n",
"y", "z", " ", "\n",
LV_SYMBOL_OK, "Del", "Exit", LV_SYMBOL_RIGHT, ""
},
{
"A", "B", "C", "\n",
"D", "E", "F", "\n",
"G", "H", "I", "\n",
LV_SYMBOL_OK, "Del", "Exit", LV_SYMBOL_RIGHT, ""
},
{
"J", "K", "L", "\n",
"N", "M", "O", "\n",
"P", "Q", "R", "\n",
LV_SYMBOL_OK, "Del", "Exit", LV_SYMBOL_RIGHT, ""
},
{
"S", "T", "U", "\n",
"V", "W", "X", "\n",
"Y", "Z", " ", "\n",
LV_SYMBOL_OK, "Del", "Exit", LV_SYMBOL_RIGHT, ""
},
{
"1", "2", "3", "\n",
"4", "5", "6", "\n",
"7", "8", "9", "\n",
LV_SYMBOL_OK, "Del", "Exit", LV_SYMBOL_RIGHT, ""
},
{
"0", "+", "-", "\n",
"/", "*", "=", "\n",
"!", "?", "#", "\n",
LV_SYMBOL_OK, "Del", "Exit", LV_SYMBOL_RIGHT, ""
},
{
"", "@", "\n",
"%", "$", "(", "\n",
")", "{", "}", "\n",
LV_SYMBOL_OK, "Del", "Exit", LV_SYMBOL_RIGHT, ""
},
{
"[", "]", ";", "\n",
"\"", "'", ".", "\n",
",", ":", " ", "\n",
LV_SYMBOL_OK, "Del", "Exit", LV_SYMBOL_RIGHT, ""
}
};
/*****************************************************************
*
* ! Switch Class
*
*/
class Switch
{
public:
typedef struct {
const char *name;
void (*cb)(uint8_t, bool);
} switch_cfg_t;
typedef void (*exit_cb)();
Switch()
{
_swCont = nullptr;
}
~Switch()
{
if (_swCont)
lv_obj_del(_swCont);
_swCont = nullptr;
}
void create(switch_cfg_t *cfg, uint8_t count, exit_cb cb, lv_obj_t *parent = nullptr)
{
static lv_style_t swlStyle;
lv_style_init(&swlStyle);
lv_style_set_radius(&swlStyle, LV_OBJ_PART_MAIN, 0);
lv_style_set_bg_color(&swlStyle, LV_OBJ_PART_MAIN, LV_COLOR_GRAY);
lv_style_set_bg_opa(&swlStyle, LV_OBJ_PART_MAIN, LV_OPA_0);
lv_style_set_border_width(&swlStyle, LV_OBJ_PART_MAIN, 0);
lv_style_set_border_opa(&swlStyle, LV_OBJ_PART_MAIN, LV_OPA_50);
lv_style_set_text_color(&swlStyle, LV_OBJ_PART_MAIN, LV_COLOR_WHITE);
lv_style_set_image_recolor(&swlStyle, LV_OBJ_PART_MAIN, LV_COLOR_WHITE);
if (parent == nullptr) {
parent = lv_scr_act();
}
_exit_cb = cb;
_swCont = lv_cont_create(parent, NULL);
lv_obj_set_size(_swCont, LV_HOR_RES, LV_VER_RES - 30);
lv_obj_align(_swCont, NULL, LV_ALIGN_CENTER, 0, 0);
lv_obj_add_style(_swCont, LV_OBJ_PART_MAIN, &swlStyle);
_count = count;
_sw = new lv_obj_t *[count];
_cfg = new switch_cfg_t [count];
memcpy(_cfg, cfg, sizeof(switch_cfg_t) * count);
lv_obj_t *prev = nullptr;
for (int i = 0; i < count; i++) {
lv_obj_t *la1 = lv_label_create(_swCont, NULL);
lv_label_set_text(la1, cfg[i].name);
i == 0 ? lv_obj_align(la1, NULL, LV_ALIGN_IN_TOP_LEFT, 30, 20) : lv_obj_align(la1, prev, LV_ALIGN_OUT_BOTTOM_MID, 0, 20);
_sw[i] = lv_imgbtn_create(_swCont, NULL);
lv_imgbtn_set_src(_sw[i], LV_BTN_STATE_RELEASED, &off);
lv_imgbtn_set_src(_sw[i], LV_BTN_STATE_PRESSED, &off);
lv_imgbtn_set_src(_sw[i], LV_BTN_STATE_CHECKED_RELEASED, &off);
lv_imgbtn_set_src(_sw[i], LV_BTN_STATE_CHECKED_PRESSED, &off);
lv_obj_set_click(_sw[i], true);
lv_obj_align(_sw[i], la1, LV_ALIGN_OUT_RIGHT_MID, 80, 0);
lv_obj_set_event_cb(_sw[i], __switch_event_cb);
prev = la1;
}
_exitBtn = lv_imgbtn_create(_swCont, NULL);
lv_imgbtn_set_src(_exitBtn, LV_BTN_STATE_RELEASED, &iexit);
lv_imgbtn_set_src(_exitBtn, LV_BTN_STATE_PRESSED, &iexit);
lv_imgbtn_set_src(_exitBtn, LV_BTN_STATE_CHECKED_RELEASED, &iexit);
lv_imgbtn_set_src(_exitBtn, LV_BTN_STATE_CHECKED_PRESSED, &iexit);
lv_obj_set_click(_exitBtn, true);
lv_obj_align(_exitBtn, _swCont, LV_ALIGN_IN_BOTTOM_MID, 0, -5);
lv_obj_set_event_cb(_exitBtn, __switch_event_cb);
_switch = this;
}
void align(const lv_obj_t *base, lv_align_t align, lv_coord_t x = 0, lv_coord_t y = 0)
{
lv_obj_align(_swCont, base, align, x, y);
}
void hidden(bool en = true)
{
lv_obj_set_hidden(_swCont, en);
}
static void __switch_event_cb(lv_obj_t *obj, lv_event_t event)
{
if (event == LV_EVENT_SHORT_CLICKED) {
Serial.println("LV_EVENT_SHORT_CLICKED");
if (obj == _switch->_exitBtn) {
if ( _switch->_exit_cb != nullptr) {
_switch->_exit_cb();
return;
}
}
}
if (event == LV_EVENT_SHORT_CLICKED) {
Serial.println("LV_EVENT_VALUE_CHANGED");
for (int i = 0; i < _switch->_count ; i++) {
lv_obj_t *sw = _switch->_sw[i];
if (obj == sw) {
const void *src = lv_imgbtn_get_src(sw, LV_BTN_STATE_RELEASED);
const void *dst = src == &off ? &on : &off;
bool en = src == &off;
lv_imgbtn_set_src(sw, LV_BTN_STATE_RELEASED, dst);
lv_imgbtn_set_src(sw, LV_BTN_STATE_PRESSED, dst);
lv_imgbtn_set_src(sw, LV_BTN_STATE_CHECKED_RELEASED, dst);
lv_imgbtn_set_src(sw, LV_BTN_STATE_CHECKED_PRESSED, dst);
if (_switch->_cfg[i].cb != nullptr) {
_switch->_cfg[i].cb(i, en);
}
return;
}
}
}
}
void setStatus(uint8_t index, bool en)
{
if (index > _count)return;
lv_obj_t *sw = _sw[index];
const void *dst = en ? &on : &off;
lv_imgbtn_set_src(sw, LV_BTN_STATE_RELEASED, dst);
lv_imgbtn_set_src(sw, LV_BTN_STATE_PRESSED, dst);
lv_imgbtn_set_src(sw, LV_BTN_STATE_CHECKED_RELEASED, dst);
lv_imgbtn_set_src(sw, LV_BTN_STATE_CHECKED_PRESSED, dst);
}
private:
static Switch *_switch;
lv_obj_t *_swCont = nullptr;
uint8_t _count;
lv_obj_t **_sw = nullptr;
switch_cfg_t *_cfg = nullptr;
lv_obj_t *_exitBtn = nullptr;
exit_cb _exit_cb = nullptr;
};
Switch *Switch::_switch = nullptr;
/*****************************************************************
*
* ! Preload Class
*
*/
class Preload
{
public:
Preload()
{
_preloadCont = nullptr;
}
~Preload()
{
if (_preloadCont == nullptr) return;
lv_obj_del(_preloadCont);
_preloadCont = nullptr;
}
void create(lv_obj_t *parent = nullptr)
{
if (parent == nullptr) {
parent = lv_scr_act();
}
if (_preloadCont == nullptr) {
static lv_style_t plStyle;
lv_style_init(&plStyle);
lv_style_set_radius(&plStyle, LV_OBJ_PART_MAIN, 0);
lv_style_set_bg_color(&plStyle, LV_OBJ_PART_MAIN, LV_COLOR_GRAY);
lv_style_set_bg_opa(&plStyle, LV_OBJ_PART_MAIN, LV_OPA_0);
lv_style_set_border_width(&plStyle, LV_OBJ_PART_MAIN, 0);
lv_style_set_text_color(&plStyle, LV_OBJ_PART_MAIN, LV_COLOR_WHITE);
lv_style_set_image_recolor(&plStyle, LV_OBJ_PART_MAIN, LV_COLOR_WHITE);
static lv_style_t style;
lv_style_init(&style);
lv_style_set_radius(&style, LV_OBJ_PART_MAIN, 0);
lv_style_set_bg_color(&style, LV_OBJ_PART_MAIN, LV_COLOR_GRAY);
lv_style_set_bg_opa(&style, LV_OBJ_PART_MAIN, LV_OPA_0);
lv_style_set_border_width(&style, LV_OBJ_PART_MAIN, 0);
lv_style_set_text_color(&style, LV_OBJ_PART_MAIN, LV_COLOR_WHITE);
lv_style_set_image_recolor(&style, LV_OBJ_PART_MAIN, LV_COLOR_WHITE);
_preloadCont = lv_cont_create(parent, NULL);
lv_obj_set_size(_preloadCont, LV_HOR_RES, LV_VER_RES - 30);
lv_obj_align(_preloadCont, NULL, LV_ALIGN_OUT_BOTTOM_MID, 0, 0);
lv_obj_add_style(_preloadCont, LV_OBJ_PART_MAIN, &plStyle);
lv_obj_t *preload = lv_spinner_create(_preloadCont, NULL);
lv_obj_set_size(preload, lv_obj_get_width(_preloadCont) / 2, lv_obj_get_height(_preloadCont) / 2);
lv_obj_add_style(preload, LV_OBJ_PART_MAIN, &style);
lv_obj_align(preload, _preloadCont, LV_ALIGN_CENTER, 0, 0);
}
}
void align(const lv_obj_t *base, lv_align_t align, lv_coord_t x = 0, lv_coord_t y = 0)
{
lv_obj_align(_preloadCont, base, align, x, y);
}
void hidden(bool en = true)
{
lv_obj_set_hidden(_preloadCont, en);
}
private:
lv_obj_t *_preloadCont = nullptr;
};
/*****************************************************************
*
* ! List Class
*
*/
class List
{
public:
typedef void(*list_event_cb)(const char *);
List()
{
}
~List()
{
if (_listCont == nullptr) return;
lv_obj_del(_listCont);
_listCont = nullptr;
}
void create(lv_obj_t *parent = nullptr)
{
if (parent == nullptr) {
parent = lv_scr_act();
}
if (_listCont == nullptr) {
static lv_style_t listStyle;
lv_style_init(&listStyle);
lv_style_set_radius(&listStyle, LV_OBJ_PART_MAIN, 0);
lv_style_set_bg_color(&listStyle, LV_OBJ_PART_MAIN, LV_COLOR_GRAY);
lv_style_set_bg_opa(&listStyle, LV_OBJ_PART_MAIN, LV_OPA_0);
lv_style_set_border_width(&listStyle, LV_OBJ_PART_MAIN, 0);
lv_style_set_text_color(&listStyle, LV_OBJ_PART_MAIN, LV_COLOR_WHITE);
lv_style_set_image_recolor(&listStyle, LV_OBJ_PART_MAIN, LV_COLOR_WHITE);
_listCont = lv_list_create(lv_scr_act(), NULL);
lv_list_set_scrollbar_mode(_listCont, LV_SCROLLBAR_MODE_OFF);
lv_obj_set_size(_listCont, LV_HOR_RES, LV_VER_RES - 30);
lv_obj_add_style(_listCont, LV_OBJ_PART_MAIN, &listStyle);
lv_obj_align(_listCont, NULL, LV_ALIGN_CENTER, 0, 0);
}
_list = this;
}
void add(const char *txt, void *imgsrc = (void *)LV_SYMBOL_WIFI)
{
lv_obj_t *btn = lv_list_add_btn(_listCont, imgsrc, txt);
lv_obj_set_event_cb(btn, __list_event_cb);
}
void align(const lv_obj_t *base, lv_align_t align, lv_coord_t x = 0, lv_coord_t y = 0)
{
lv_obj_align(_listCont, base, align, x, y);
}
void hidden(bool en = true)
{
lv_obj_set_hidden(_listCont, en);
}
static void __list_event_cb(lv_obj_t *obj, lv_event_t event)
{
if (event == LV_EVENT_SHORT_CLICKED) {
const char *txt = lv_list_get_btn_text(obj);
if (_list->_cb != nullptr) {
_list->_cb(txt);
}
}
}
void setListCb(list_event_cb cb)
{
_cb = cb;
}
private:
lv_obj_t *_listCont = nullptr;
static List *_list ;
list_event_cb _cb = nullptr;
};
List *List::_list = nullptr;
/*****************************************************************
*
* ! Task Class
*
*/
class Task
{
public:
Task()
{
_handler = nullptr;
_cb = nullptr;
}
~Task()
{
if ( _handler == nullptr)return;
Serial.println("Free Task Func");
lv_task_del(_handler);
_handler = nullptr;
_cb = nullptr;
}
void create(lv_task_cb_t cb, uint32_t period = 1000, lv_task_prio_t prio = LV_TASK_PRIO_LOW)
{
_handler = lv_task_create(cb, period, prio, NULL);
};
private:
lv_task_t *_handler = nullptr;
lv_task_cb_t _cb = nullptr;
};
/*****************************************************************
*
* ! MesBox Class
*
*/
class MBox
{
public:
MBox()
{
_mbox = nullptr;
}
~MBox()
{
if (_mbox == nullptr)return;
lv_obj_del(_mbox);
_mbox = nullptr;
}
void create(const char *text, lv_event_cb_t event_cb, const char **btns = nullptr, lv_obj_t *par = nullptr)
{
if (_mbox != nullptr)return;
lv_obj_t *p = par == nullptr ? lv_scr_act() : par;
_mbox = lv_msgbox_create(p, NULL);
lv_msgbox_set_text(_mbox, text);
if (btns == nullptr) {
static const char *defBtns[] = {"Ok", ""};
lv_msgbox_add_btns(_mbox, defBtns);
} else {
lv_msgbox_add_btns(_mbox, btns);
}
lv_obj_set_width(_mbox, LV_HOR_RES - 40);
lv_obj_set_event_cb(_mbox, event_cb);
lv_obj_align(_mbox, NULL, LV_ALIGN_CENTER, 0, 0);
}
void setData(void *data)
{
lv_obj_set_user_data(_mbox, data);
}
void *getData()
{
return lv_obj_get_user_data(_mbox);
}
void setBtn(const char **btns)
{
lv_msgbox_add_btns(_mbox, btns);
}
private:
lv_obj_t *_mbox = nullptr;
};
/*****************************************************************
*
* ! GLOBAL VALUE
*
*/
static Keyboard *kb = nullptr;
static Switch *sw = nullptr;
static Preload *pl = nullptr;
static List *list = nullptr;
static Task *task = nullptr;
static Ticker *gTicker = nullptr;
static MBox *mbox = nullptr;
static char ssid[64], password[64];
/*****************************************************************
*
* !WIFI EVENT
*
*/
void wifi_connect_status(bool result)
{
if (gTicker != nullptr) {
delete gTicker;
gTicker = nullptr;
}
if (kb != nullptr) {
delete kb;
kb = nullptr;
}
if (sw != nullptr) {
delete sw;
sw = nullptr;
}
if (pl != nullptr) {
delete pl;
pl = nullptr;
}
if (result) {
bar.show(LV_STATUS_BAR_WIFI);
} else {
bar.hidden(LV_STATUS_BAR_WIFI);
}
menuBars.hidden(false);
}
void wifi_kb_event_cb(Keyboard::kb_event_t event)
{
if (event == 0) {
kb->hidden();
Serial.println(kb->getText());
strlcpy(password, kb->getText(), sizeof(password));
pl->hidden(false);
WiFi.mode(WIFI_STA);
WiFi.disconnect();
WiFi.begin(ssid, password);
gTicker = new Ticker;
gTicker->once_ms(5 * 1000, []() {
wifi_connect_status(false);
});
} else if (event == 1) {
delete kb;
delete sw;
delete pl;
pl = nullptr;
kb = nullptr;
sw = nullptr;
menuBars.hidden(false);
}
}
void wifi_sw_event_cb(uint8_t index, bool en)
{
switch (index) {
case 0:
if (en) {
WiFi.begin();
} else {
WiFi.disconnect();
bar.hidden(LV_STATUS_BAR_WIFI);
}
break;
case 1:
sw->hidden();
pl = new Preload;
pl->create();
pl->align(bar.self(), LV_ALIGN_OUT_BOTTOM_MID);
WiFi.disconnect();
WiFi.scanNetworks(true);
break;
case 2:
if (!WiFi.isConnected()) {
//TODO pop-up window
Serial.println("WiFi is no connect");
return;
} else {
configTzTime(RTC_TIME_ZONE, "pool.ntp.org");
sw->hidden(false);
}
break;
default:
break;
}
}
void wifi_list_cb(const char *txt)
{
strlcpy(ssid, txt, sizeof(ssid));
delete list;
list = nullptr;
kb = new Keyboard;
kb->create();
kb->align(bar.self(), LV_ALIGN_OUT_BOTTOM_MID);
kb->setKeyboardEvent(wifi_kb_event_cb);
}
void wifi_list_add(const char *ssid)
{
if (list == nullptr) {
pl->hidden();
list = new List;
list->create();
list->align(bar.self(), LV_ALIGN_OUT_BOTTOM_MID);
list->setListCb(wifi_list_cb);
}
list->add(ssid);
}
static void wifi_event_cb()
{
Switch::switch_cfg_t cfg[3] = {{"Switch", wifi_sw_event_cb}, {"Scan", wifi_sw_event_cb}, {"NTP Sync", wifi_sw_event_cb}};
sw = new Switch;
sw->create(cfg, 3, []() {
delete sw;
sw = nullptr;
menuBars.hidden(false);
});
sw->align(bar.self(), LV_ALIGN_OUT_BOTTOM_MID);
sw->setStatus(0, WiFi.isConnected());
}
static void wifi_destory()
{
Serial.printf("globalIndex:%d\n", globalIndex);
switch (globalIndex) {
//! wifi management main
case 0:
menuBars.hidden(false);
delete sw;
sw = nullptr;
break;
//! wifi ap list
case 1:
if (list != nullptr) {
delete list;
list = nullptr;
}
if (gTicker != nullptr) {
delete gTicker;
gTicker = nullptr;
}
if (kb != nullptr) {
delete kb;
kb = nullptr;
}
if (pl != nullptr) {
delete pl;
pl = nullptr;
}
sw->hidden(false);
break;
//! wifi keyboard
case 2:
if (gTicker != nullptr) {
delete gTicker;
gTicker = nullptr;
}
if (kb != nullptr) {
delete kb;
kb = nullptr;
}
if (pl != nullptr) {
delete pl;
pl = nullptr;
}
sw->hidden(false);
break;
case 3:
break;
default:
break;
}
globalIndex--;
}
/*****************************************************************
*
* !SETTING EVENT
*
*/
static void setting_event_cb()
{
}
/*****************************************************************
*
* ! LIGHT EVENT
*
*/
static void light_sw_event_cb(uint8_t index, bool en)
{
//Add lights that need to be controlled
}
static void light_event_cb()
{
const uint8_t cfg_count = 4;
Switch::switch_cfg_t cfg[cfg_count] = {
{"light1", light_sw_event_cb},
{"light2", light_sw_event_cb},
{"light3", light_sw_event_cb},
{"light4", light_sw_event_cb},
};
sw = new Switch;
sw->create(cfg, cfg_count, []() {
delete sw;
sw = nullptr;
menuBars.hidden(false);
});
sw->align(bar.self(), LV_ALIGN_OUT_BOTTOM_MID);
//Initialize switch status
for (int i = 0; i < cfg_count; i++) {
sw->setStatus(i, 0);
}
}
/*****************************************************************
*
* ! MBOX EVENT
*
*/
static lv_obj_t *mbox1 = nullptr;
static void create_mbox(const char *txt, lv_event_cb_t event_cb)
{
if (mbox1 != nullptr)return;
static const char *btns[] = {"Ok", ""};
mbox1 = lv_msgbox_create(lv_scr_act(), NULL);
lv_msgbox_set_text(mbox1, txt);
lv_msgbox_add_btns(mbox1, btns);
lv_obj_set_width(mbox1, LV_HOR_RES - 40);
lv_obj_set_event_cb(mbox1, event_cb);
lv_obj_align(mbox1, NULL, LV_ALIGN_CENTER, 0, 0);
}
static void destory_mbox()
{
if (pl != nullptr) {
delete pl;
pl = nullptr;
}
if (list != nullptr) {
delete list;
list = nullptr;
}
if (mbox1 != nullptr) {
lv_obj_del(mbox1);
mbox1 = nullptr;
}
}
/*****************************************************************
*
* ! SD CARD EVENT
*
*/
static void sd_event_cb()
{
}
/*****************************************************************
*
* ! Modules EVENT
*
*/
static void modules_event_cb()
{
}
/*****************************************************************
*
* ! Camera EVENT
*
*/
static void camera_event_cb()
{
}
/*
Copyright (c) 2019 lewis he
This is just a demonstration. Most of the functions are not implemented.
The main implementation is low-power standby.
The off-screen standby (not deep sleep) current is about 4mA.
Select standard motherboard and standard backplane for testing.
Created by Lewis he on October 10, 2019.
*/
#ifndef __GUI_H
#define __GUI_H
typedef enum {
LV_ICON_BAT_EMPTY,
LV_ICON_BAT_1,
LV_ICON_BAT_2,
LV_ICON_BAT_3,
LV_ICON_BAT_FULL,
LV_ICON_CHARGE,
LV_ICON_CALCULATION
} lv_icon_battery_t;
typedef enum {
LV_STATUS_BAR_BATTERY_LEVEL = 0,
LV_STATUS_BAR_BATTERY_ICON = 1,
LV_STATUS_BAR_WIFI = 2,
LV_STATUS_BAR_BLUETOOTH = 3,
} lv_icon_status_bar_t;
void setupGui();
void updateStepCounter(uint32_t counter);
void updateBatteryIcon(lv_icon_battery_t index);
void wifi_list_add(const char *ssid);
void wifi_connect_status(bool result);
void updateBatteryLevel();
#endif /*__GUI_H */
[env:ttgo-t-watch]
platform = espressif32
board = ttgo-t-watch
framework = arduino
monitor_speed = 115200
build_flags =
-D LILYGO_WATCH_2019_WITH_TOUCH=1
;-D LILYGO_WATCH_2019_NO_TOUCH=1
;-D LILYGO_WATCH_BLOCK=1
;-D LILYGO_WATCH_2020_V1=1
-D LILYGO_WATCH_LVGL=1
lib_deps =
TTGO TWatch Library