T-Watch. Simplified code for shutdown and wake-up with BMA423 or AXP202 of the ESP32

ttgo t-watch esp32 deep sleep wakeup bma423 axp202 no freertos
Share on facebook
Share on twitter
Share on linkedin
Share on pinterest
Share on email
Share on whatsapp
Table of Contents

In the previous article taken from the example developed by Lewis Le, we saw how to wake up the screen and functions of the ESP32 TTGO T-Watch connected watch using FreeRTOS. The code is clearly intended for Makers who already have a very good command of programming. I therefore offer you an ultra simplified version that is more accessible and suitable for simpler projects.聽

 

The code proposed here can certainly be further improved and simplified. If you have even better things, feel free to post your improvements on the GitHub repository or in the comments 馃檪

Elements shown in the clip (in order of appearance): wake-up by the main button (AXP202), automatic switch-off after 5 seconds of inactivity (light sleep), wake-up of the ESP32 by a tap on the screen, setting in deep sleep, automatic wake-up by a Timer.

How to wake up the T-Watch without FreeRTOS?

The “academic” method to wake up the ESP32 and the peripherals and use the FreeRTOS real time system, the general architecture of which is below.

esp32 wakeup bma423 interrupt t-watch freertos xeventgroup

Read Also
T-Watch. Sleep and wake-up ESP32 with BMA423 accelerometer or AXP202 button

Obviously for simple projects, and all the more so when you are new to C++, it is a little difficult to put into practice.

It is possible to do almost the same thing using only the interrupts of the ESP32 which results in a simpler architecture.

esp32 wakeup bma423 axp202 deep sleep interrupt t-watch

 

Read Also
ESP32. How to use external interrupts with Arduino code

We therefore keep the two interrupts on the GPIO which allow us to wake up via the main button (via l4AXP202) or the BMA423 accelerometer.

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();

When the user taps the watch screen or presses the home button or activates a boolean. Here, we use two booleans which will trigger a different treatment depending on the alarm clock. For example, you could display physical activity when you tap the screen and the time when you press the main button on the screen. It’s a detail.

Waking up by the BMA423 accelerometerWake-up by the main button via the AXP202
pinMode(BMA423_INT1, INPUT);
  attachInterrupt(BMA423_INT1, [] {
      irq_bma = true;
  }, RISING);
pinMode(AXP202_INT, INPUT);
  attachInterrupt(AXP202_INT, [] {
      irq_axp202 = true;
  }, FALLING);

 

Then, it suffices to test a change of state of these two booleans in the loop() to wake up the ESP32 and the watch accessories (screen, expansion card …).

When the BMA423 detects motion or the user taps the screen:

  • We pass the boolean to False
  • We reset the automatic screen shutdown timer
  • We wait for the end of the interruption of the BMA423 before continuing
  • We execute the wake-up function (identical to the previous project)
if ( irq_bma ) {
    Serial.println("irq_bma detected");
    irq_bma = false;   
    resetChrono();   
    do {
      rlst = watch->bma->readInterrupt();
    } while (!rlst);
    low_energy();
  }

When pressing the main button (which is connected to the AXP202):

  • We retrieve the event that caused the interruption with the watch->power->readIRQ() method
  • Then we test the origin of the alarm clock. We can adapt the actions according to the event that caused the awakening. Several functions are available for this
    • isPEKShortPressIRQ single press
    • isPEKLongtPressIRQ long press
  • Finally, do not forget to purge the IRQ register for the next 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();
  } 

The rest of the code is unchanged from the previous project.

Read Also
T-Watch. Sleep and wake-up ESP32 with BMA423 accelerometer or AXP202 button

How to send the ESP32 back to deep-sleep

What is great with the T-Watch Touch (or the M5Stack platform for that matter) are the available expansion boards and the battery integrated into a mini box. No more need to weld for full application.

To save the battery as much as possible, it is best to activate the deep sleep mode of the ESP32. Then, we can wake up the ESP32 periodically with a Timer. Everything is explained in detail in this article to go further.

 

The problem is that a mechanism must be provided upstream to put the ESP32 back into its deep sleep after treatment or a period of inactivity.

To do this, simply retrieve the origin of the wake-up call using the esp_sleep_get_wakeup_cause() method. the function returns the number of the cause of the alarm clock:

  1. RTC_IO
  2. RTC_CNTL
  3. Touch Pad capacitive keys
  4. Timer what interests us here
  5. ULP Ultra Low Power co-processor
  6. Usually unknown after a firmware update

Knowing the number of the alarm clock, all you have to do is pass a boolean to True which will be tested on standby. And now, voila !

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
       ...
    }
} 

Project code

Here is the full Arduino source code for the example which you can also grab directly from GitHub. The project can be compiled using the Arduino IDE or PlatformIO.

logo github

The source code can be used as a basis for the development of your own connected watch project.

  • main.cpp
  • platformio.ini

/* 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 <Arduino.h>
#include <LilyGoWatch.h>
#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 {  
        // If the backlighting is active >> switches to light sleep mode. The ESP32 Core remains active
        Serial.println("BL is ON >> activate light sleep");
        watch->closeBL();
        watch->bma->enableStepCountInterrupt(false);
        watch->displaySleep();
        lenergy = true;
        // Decrease CPU frequency to 10MHz to reduce consumption
        setCpuFrequencyMhz(10);
        Serial.println("ENTER IN LIGHT SLEEP MODE");
        delay(50);
        // Alarm clock with the home button
        gpio_wakeup_enable ((gpio_num_t)AXP202_INT, GPIO_INTR_LOW_LEVEL);  
        // Waking up with the accelerometer
        gpio_wakeup_enable ((gpio_num_t)BMA423_INT1, GPIO_INTR_LOW_LEVEL);
        // Active le r茅veille depuis le GPIO  
        esp_sleep_enable_gpio_wakeup();
        // Puts into light standby. The CPU Core keeps running
        esp_light_sleep_start();

      }  
    } else {
      // The backlight is off   
      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);

  if ( get_wakeup_reason() == 4 ) return_to_deepsleep = true;

  watch = TTGOClass::getWatch();
    // Initialize the hardware
  watch->begin();
  power = watch->power;

  // Turn on the backlight 
  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;

  // Accel parameter structure
  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 and BMA 423
  sensor->enableAccel();

  // Activates the wake-up function of the BMA423
  sensor->enableFeature(BMA423_WAKEUP, true);
  
  // Send a signal on pin 39 of the ESP32 as soon as movement is detected
  sensor->enableWakeupInterrupt();

  // Interrupt that allows you to lightly sleep or wake up the screen
  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();

  // start chronometer
  awake = millis();
  resetChrono();
  // Build landing page 
  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();
  }
  if ( millis() - awake > DEFAULT_SCREEN_TIMEOUT) {
    if ( !lenergy) {
      awake = millis();
      low_energy();
    }  
  }
  if ( millis() - seconde >= 1000 ) {
    if ( !lenergy) {
      seconde = millis();
      timeleft --;
      buildTFTPage(timeleft);
    }  
  }
  // User wake-up watch with button
  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();
  }  
  // User touch the screen
  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();
  }  
  // The state of the user button is checked at each passage through the mouth
  watch->button->loop();  
}
// Reset of the stopwatch for automatic standby of the screen (light sleep mode)
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);
}

// Allows you to activate the deep sleep mode and the timer for a periodic awakening (data logger for example)
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
    esp_sleep_enable_timer_wakeup(uS_TO_S_FACTOR * TIME_TO_SLEEP);

    esp_deep_sleep_start();
}

// Function that prints the reason by which ESP32 has been awaken from sleep
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

The source code has been tested on the T-Watch Touch 2019

As well as on the T-Watch 2020

T-Watch and expansion boards

Updates

2020/11/27聽Publication of the article

Version fran莽aise

Click to rate this post!
[Total: 1 Average: 5]

Thanks聽for your reading

Did you like this project ? Don't miss any more projects by subscribing to our weekly newsletter!

Are you having a problem with this topic?

Maybe someone has already found the solution, visit the forum before asking your question
We will be happy to hear your thoughts

      Leave a Reply

      Ads
      Read more
      Recent posts on the Forum
      DIY Projects
      DIY Projects