Dans l’article précédent tiré de l’exemple développé par Lewis Le, nous avons vu comment réveiller l’écran et les fonctions de la montre connectée ESP32 TTGO T-Watch à l’aide de FreeRTOS. Le code est clairement destiné à des Makers qui ont déjà une très bonne maitrise de la programmation. Je vous propose donc une version ultra simplifiée plus accessible qui conviendra à des projets plus simples.
Le code proposé ici peut certainement être encore amélioré et simplifié. Si vous avez encore mieux, n’hésitez pas à proposer vos améliorations sur le dépôts GitHub ou dans les commentaires 🙂
https://projetsdiy.fr/data/uploads/2020/11/ttgo-t-watch-deep-light-sleep-wakup-axp202-bma423.mp4?_=1
Eléments montrés dans le clip (dans l’ordre d’apparition) : réveil par le bouton principal (AXP202), extinction automatique après 5 secondes d’inactivité (light sleep), réveil de l’ESP32 par un tap sur l’écran, mise en deep sleep, réveil automatique par un Timer.
Sommaire
Comment réveiller la T-Watch sans FreeRTOS ?
La méthode “académique” pour réveiller l’ESP32 et les périphériques et d’utiliser le système temps réel de FreeRTOS dont voici l’architecture générale.
Bien évidemment pour des projets simples, et à fortiori lorsqu’on débute en C++, c’est un peu difficile à mettre en pratique.
Il est possible de faire quasiment la même chose en utilisant uniquement les interruptions de l’ESP32 ce qui donne une architecture plus simple.
On conserve donc les deux interruptions sur le GPIO qui vous nous permettre de sortir de veille via le bouton principal (par l’intermédiaire de l4AXP202) ou l’accéléromètre BMA423.
gpio_wakeup_enable ((gpio_num_t)BMA423_INT1, GPIO_INTR_LOW_LEVEL);
gpio_wakeup_enable ((gpio_num_t)AXP202_INT, GPIO_INTR_LOW_LEVEL);
esp_sleep_enable_gpio_wakeup();
esp_light_sleep_start();
Lorsque l’utilisateur tapote l’écran de la montre ou appuie sur le bouton principal ou active un booléen. Ici, on utilise deux booléens ce qui permettra de déclencher un traitement différent en fonction du réveil. On pourrait par exemple afficher à l’écran l’activité physique lorsqu’on tapote l’écran et l’heure lorsqu’on appuie sur le bouton principal. C’est un détail.
Réveil par l’accéléromètre BMA423 | Réveil par le bouton principal via l’AXP202 |
|
|
Ensuite, il suffit de tester un changement d’état de ces deux booléens dans la loop() pour réveiller l’ESP32 et les accessoires de la montre (écran, carte d’extension…).
Lorsque le BMA423 détecte un mouvement ou que l’utilisateur tapote l’écran :
- On passe le booléen à Faux
- On ré-initialiser le chronomètre d’extinction automatique de l’écran
- On attend la fin de l’interruption du BMA423 avant de continuer
- On exécute la fonction de réveil (identique au projet précédent)
if ( irq_bma ) {
Serial.println("irq_bma detectee");
irq_bma = false;
resetChrono();
do {
rlst = watch->bma->readInterrupt();
} while (!rlst);
low_energy();
}
Lorsqu’on appuie sur le bouton principal (qui est connecté à l’AXP202) :
- On récupère l’évènement qui a provoqué l’interruption avec la méthode watch->power->readIRQ()
- Puis on test l’origine du réveil. On pourra adapter les actions en fonction de l’événement qui a provoqué le réveil. On dispose pour cela de plusieurs fonctions
- isPEKShortPressIRQ appui simple
- isPEKLongtPressIRQ appui long
- …
- Enfin, il ne faut pas oublier de purger le registre IRQ pour la prochaine action watch->power->clearIRQ()
if (irq_axp202) {
Serial.println("irq detected");
irq_axp202 = false;
watch->power->readIRQ();
if ( watch->power->isPEKShortPressIRQ() ) {
Serial.println("Power button pressed >> wakeup / switch on light sleep");
low_energy();
}
watch->power->clearIRQ();
}
Le reste du code est inchangé par rapport au projet précédent.
Comment renvoyer l’ESP32 en deep-sleep (sommeil profond)
Ce qui est génial avec la T-Watch Touch (ou la plateforme M5Stack d’ailleurs) se sont les cartes d’extension disponibles et la batterie intégrée dans un mini boitier. Plus besoin de faire des soudures pour plein d’application.
Pour économiser au maximum la batterie, le mieux est d’activer la mise en veille profonde (deep sleep) de l’ESP32. Ensuite, on pourra réveiller l’ESP32 périodiquement avec un Timer. Tout est expliqué en détail dans cet article pour aller plus loin.
Le problème, c’est qu’il faut prévoir en amont un mécanisme pour replonger l’ESP32 dans son sommeil profond après le traitement ou une période d’inactivité.
Pour cela, il suffit de récupérer l’origine du réveil à l’aide de la méthode esp_sleep_get_wakeup_cause(). la fonction renvoie le numéro de la cause du réveil :
- RTC_IO
- RTC_CNTL
- Touch Pad touches capacitives
- Timer ce qui nous intéresse ici
- ULP co-processeur Ultra Low Power
- Inconnu généralement après une mise à jour du firmware
Connaissant le numéro du réveil, il suffit de passer un booléen à True que l’on testera à la mise en veille. Et voilà, le tour est joué !
void low_energy()
{
if ( watch->bl->isOn()) {
if ( return_to_deepsleep ) {
goToDeepSleep(); // Retour en veille profonde
} else {
mise en veille légère (light sleep)
...
}
} else {
allume l'écran et les périphériques de la montre
...
}
}
Code du projet
Voici le code source Arduino complet de l’exemple que vous pouvez également récupérer directement sur GitHub. Le projet peut être compilé à l’aide de l’IDE Arduino ou de PlatformIO.
Le code source peut servir de base pour le développement de votre propre projet de montre connectée.
/* Arduino IDE - uncomment your watch */
//#define LILYGO_WATCH_2019_WITH_TOUCH
//#define LILYGO_WATCH_2019_NO_TOUCH
//#define LILYGO_WATCH_BLOCK
//#define LILYGO_WATCH_2020_V1
/* PlatformIO -> Select your watch in platformio.ini file */
#include
#include
#include "esp_sleep.h"
/**************************/
/* Static variables */
/**************************/
TTGOClass *watch = nullptr;
BMA *sensor;
AXP20X_Class *power;
TFT_eSPI *tft = nullptr;
bool KeyPressed = false;
bool lenergy = false;
int awake = 0;
int seconde = 0;
int timeleft = 0;
static bool irq_axp202 = false;
static bool irq_bma = false;
static bool return_to_deepsleep = false;
#define DEFAULT_SCREEN_TIMEOUT 5*1000
#define uS_TO_S_FACTOR 1000000ULL /* Conversion factor for micro seconds to seconds */
#define TIME_TO_SLEEP 30 /* Time ESP32 will go to sleep (in seconds) */
/**************************/
/* STATIC PROTOTYPES */
/**************************/
void buildTFTPage(int timeleft);
void goToDeepSleep();
void resetChrono();
int get_wakeup_reason();
/**************************/
/* Switch On/Off power */
/**************************/
void low_energy()
{
if ( watch->bl->isOn()) {
if ( return_to_deepsleep ) {
goToDeepSleep();
} else {
// Si le rétro-éclairage est actif >> passe en mode light sleep. Le Core de l'ESP32 reste actif
Serial.println("BL is ON >> activate light sleep");
watch->closeBL();
watch->bma->enableStepCountInterrupt(false);
watch->displaySleep();
lenergy = true;
// Diminue la fréquence du CPU à 10MHz pour réduire la consommation
setCpuFrequencyMhz(10);
Serial.println("ENTER IN LIGHT SLEEP MODE");
delay(50);
// Réveil avec le bouton principal
gpio_wakeup_enable ((gpio_num_t)AXP202_INT, GPIO_INTR_LOW_LEVEL);
// Réveil avec l'accéléromètre
gpio_wakeup_enable ((gpio_num_t)BMA423_INT1, GPIO_INTR_LOW_LEVEL);
// Active le réveille depuis le GPIO
esp_sleep_enable_gpio_wakeup();
// Met en veille légère. Le Core du CPU continue de fonctionner
esp_light_sleep_start();
}
} else {
// The backlight is off
// Le rétro-éclairage est éteint
lenergy = false;
setCpuFrequencyMhz(160);
resetChrono();
Serial.println("Wake-up ESP32 and accessories");
watch->displayWakeup();
watch->openBL();
watch->rtc->syncToSystem();
delay(100);
}
}
void setup() {
Serial.begin(115200);
// Retourne en sommeil profond si le réveil a été causé par le timer
if ( get_wakeup_reason() == 4 ) return_to_deepsleep = true;
watch = TTGOClass::getWatch();
// Initialize the hardware
watch->begin();
power = watch->power;
// Turn on the backlight | Allume le rétro-éclairage
watch->openBL();
// T-Watch with user button only
watch->button->setClickHandler(goToDeepSleep);
// 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, [] {
irq_bma = true;
}, RISING);
sensor = watch->bma;
// Paramètre de l'accéléromètre BMA423 (optionnel)
Acfg cfg;
cfg.odr = BMA4_OUTPUT_DATA_RATE_100HZ;
cfg.range = BMA4_ACCEL_RANGE_4G;
cfg.bandwidth = BMA4_ACCEL_NORMAL_AVG4;
cfg.perf_mode = BMA4_CONTINUOUS_MODE;
sensor->accelConfig(cfg);
// Active le BMA 423
sensor->enableAccel();
// Active la fonction de réveil du BMA423
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();
// Interruption qui permet de mettre en veille légère ou réveiller l'écran
pinMode(AXP202_INT, INPUT);
attachInterrupt(AXP202_INT, [] {
irq_axp202 = true;
}, FALLING);
watch->power->enableIRQ(AXP202_PEK_SHORTPRESS_IRQ | AXP202_VBUS_REMOVED_IRQ | AXP202_VBUS_CONNECT_IRQ | AXP202_CHARGING_IRQ, true);
watch->power->clearIRQ();
// Démarre le chronomètre pour la mise en veille automatique
awake = millis();
resetChrono();
// Build landing page
//créé la page principale
buildTFTPage(timeleft);
}
void loop() {
int16_t x, y;
bool rlst;
if ( watch->getTouch(x,y) ) {
while (watch->getTouch(x, y) ) {}
// Relance le chrono lorsque l'utilisateur touche l'écran
awake = millis();
}
// Lorsqu'on dépasse le temps d'inactivité >> met en veille l'écran et les périphériques de la montre
if ( millis() - awake > DEFAULT_SCREEN_TIMEOUT) {
if ( !lenergy) {
awake = millis();
low_energy();
}
}
if ( millis() - seconde >= 1000 ) {
if ( !lenergy) {
seconde = millis();
timeleft --;
buildTFTPage(timeleft);
}
}
// L'utilisateur vient d'appuyer sur le bouton principal
if (irq_axp202) {
Serial.println("irq detected");
irq_axp202 = false;
watch->power->readIRQ();
if ( watch->power->isPEKShortPressIRQ() ) {
Serial.println("Power button pressed >> wakeup / switch on light sleep");
low_energy();
}
watch->power->clearIRQ();
}
// L'utilisateur vient de tapoter l'écran
if ( irq_bma ) {
Serial.println("irq_bma detectee");
irq_bma = false;
resetChrono();
Serial.println("Power button pressed >> wakeup / switch on light sleep");
do {
rlst = watch->bma->readInterrupt();
} while (!rlst);
low_energy();
}
// On vérifie à chaque passage dans la bouche l'état du bouton utilisateur
watch->button->loop();
}
// Reset du chronomètre pour la mise en veille automatique de l'écran (mode light sleep)
void resetChrono()
{
seconde = millis();
awake = millis();
timeleft = DEFAULT_SCREEN_TIMEOUT / 1000;
buildTFTPage(timeleft);
}
void buildTFTPage(int timeleft){
TFT_eSPI *tft = watch->tft;
tft->fillScreen(TFT_BLACK);
tft->setTextSize(2);
tft->setTextColor(TFT_WHITE);
tft->drawString("T-Watch Sleep Demo", 0,0);
tft->drawFastHLine(0,20,240,TFT_WHITE);
tft->drawString("Light sleep in", 10,60);
char buf[20];
tft->setTextSize(4);
sprintf(buf, "%u s", timeleft);
tft->drawString(buf, 50,100);
}
// Permet d'activer le mode deep sleep et le timer pour un éveil périodique (enregistreur de données par exemple)
void goToDeepSleep()
{
Serial.println("Go To 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);
// Activate Timer Wakeup. Usefull for a GPS trcker for example
// 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();
}
// Indique la raison du réveil de l'ESP32
int get_wakeup_reason(){
esp_sleep_wakeup_cause_t wakeup_reason;
wakeup_reason = esp_sleep_get_wakeup_cause();
switch(wakeup_reason)
{
case 1 : Serial.printf("Wakeup caused by external signal using RTC_IO %u \n", wakeup_reason); break;
case 2 : Serial.printf("Wakeup caused by external signal using RTC_CNTL %u \n", wakeup_reason); break;
case 3 : Serial.printf("Wakeup caused by touchpad %u \n", wakeup_reason); break;
case 4 : Serial.printf("Wakeup caused by timer %u \n", wakeup_reason); break;
case 5 : Serial.printf("Wakeup caused by ULP program %u \n", wakeup_reason); break;
default : Serial.printf("Wakeup was not caused by deep sleep %u \n", wakeup_reason); break;
}
return wakeup_reason;
}
[env:ttgo-t-watch]
platform = espressif32
board = ttgo-t-watch
framework = arduino
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
; Important, activate LVGL support
;-D LILYGO_WATCH_LVGL=1
lib_deps =
TTGO TWatch Library
upload_speed = 2000000
monitor_speed = 115200
Le code source a été testé sur la T-Watch Touch 2019
Ainsi que sur la T-Watch 2020
Mises à jour
27/11/2020 Publication de l’article
English Version
|
Avez-vous aimé cet article ?
[Total: 0 Moyenne: 0]