252 lines
7.7 KiB
C++
252 lines
7.7 KiB
C++
|
#include <Arduino.h>
|
||
|
#include "SparkFunLIS3DH.h"
|
||
|
#include "sleep_hal_esp32.h"
|
||
|
// before going to sleep, some tasks have to be done
|
||
|
// save settings
|
||
|
#include "preferencesStorage_hal_esp32.h"
|
||
|
// turn off power of IR receiver
|
||
|
#include "infrared_receiver_hal_esp32.h"
|
||
|
// turn off tft
|
||
|
#include "tft_hal_esp32.h"
|
||
|
// disconnect WiFi
|
||
|
#include "mqtt_hal_esp32.h"
|
||
|
// disconnect BLE keyboard
|
||
|
#include "keyboard_ble_hal_esp32.h"
|
||
|
// prepare keypad keys to wakeup
|
||
|
#include "keypad_keys_hal_esp32.h"
|
||
|
|
||
|
// 20 (doesn't exist?) and 13 both work.
|
||
|
// It even works when "pinMode(ACC_INT_GPIO, INPUT);" is not set
|
||
|
uint8_t ACC_INT_GPIO = 20;
|
||
|
|
||
|
int MOTION_THRESHOLD = 50; // motion above threshold keeps device awake
|
||
|
int DEFAULT_SLEEP_TIMEOUT = 20000; // default time until device enters sleep mode in milliseconds. Can be overridden.
|
||
|
|
||
|
// is "lift to wake" enabled
|
||
|
bool wakeupByIMUEnabled = true;
|
||
|
// timeout before going to sleep
|
||
|
uint32_t sleepTimeout;
|
||
|
// Timestamp of the last activity. Go to sleep if (millis() - lastActivityTimestamp > sleepTimeout)
|
||
|
uint32_t lastActivityTimestamp;
|
||
|
|
||
|
LIS3DH IMU(I2C_MODE, 0x19);
|
||
|
Wakeup_reasons wakeup_reason;
|
||
|
|
||
|
void setLastActivityTimestamp_HAL() {
|
||
|
// There was motion, touchpad or key hit.
|
||
|
// Set the time where this happens.
|
||
|
lastActivityTimestamp = millis();
|
||
|
}
|
||
|
|
||
|
void activityDetection() {
|
||
|
// if there is any motion, setLastActivityTimestamp_HAL() is called
|
||
|
int motion = 0;
|
||
|
|
||
|
// A variable declared static inside a function is visible only inside that function, exists only once (not created/destroyed for each call) and is permanent. It is in a sense a private global variable.
|
||
|
static int accXold;
|
||
|
static int accYold;
|
||
|
static int accZold;
|
||
|
int accX = IMU.readFloatAccelX()*1000;
|
||
|
int accY = IMU.readFloatAccelY()*1000;
|
||
|
int accZ = IMU.readFloatAccelZ()*1000;
|
||
|
|
||
|
// determine motion value as da/dt
|
||
|
motion = (abs(accXold - accX) + abs(accYold - accY) + abs(accZold - accZ));
|
||
|
// If the motion exceeds the threshold, the lastActivityTimestamp is updated
|
||
|
if(motion > MOTION_THRESHOLD) {
|
||
|
setLastActivityTimestamp_HAL();
|
||
|
}
|
||
|
|
||
|
// Store the current acceleration and time
|
||
|
accXold = accX;
|
||
|
accYold = accY;
|
||
|
accZold = accZ;
|
||
|
}
|
||
|
|
||
|
void configIMUInterruptsBeforeGoingToSleep()
|
||
|
{
|
||
|
uint8_t dataToWrite = 0;
|
||
|
|
||
|
//LIS3DH_INT1_CFG
|
||
|
//dataToWrite |= 0x80;//AOI, 0 = OR 1 = AND
|
||
|
//dataToWrite |= 0x40;//6D, 0 = interrupt source, 1 = 6 direction source
|
||
|
//Set these to enable individual axes of generation source (or direction)
|
||
|
// -- high and low are used generically
|
||
|
dataToWrite |= 0x20;//Z high
|
||
|
//dataToWrite |= 0x10;//Z low
|
||
|
dataToWrite |= 0x08;//Y high
|
||
|
//dataToWrite |= 0x04;//Y low
|
||
|
dataToWrite |= 0x02;//X high
|
||
|
//dataToWrite |= 0x01;//X low
|
||
|
if (wakeupByIMUEnabled) {
|
||
|
IMU.writeRegister(LIS3DH_INT1_CFG, 0b00101010);
|
||
|
} else {
|
||
|
IMU.writeRegister(LIS3DH_INT1_CFG, 0b00000000);
|
||
|
}
|
||
|
|
||
|
//LIS3DH_INT1_THS
|
||
|
dataToWrite = 0;
|
||
|
//Provide 7 bit value, 0x7F always equals max range by accelRange setting
|
||
|
dataToWrite |= 0x45;
|
||
|
IMU.writeRegister(LIS3DH_INT1_THS, dataToWrite);
|
||
|
|
||
|
//LIS3DH_INT1_DURATION
|
||
|
dataToWrite = 0;
|
||
|
//minimum duration of the interrupt
|
||
|
//LSB equals 1/(sample rate)
|
||
|
dataToWrite |= 0x00; // 1 * 1/50 s = 20ms
|
||
|
IMU.writeRegister(LIS3DH_INT1_DURATION, dataToWrite);
|
||
|
|
||
|
//LIS3DH_CTRL_REG5
|
||
|
//Int1 latch interrupt and 4D on int1 (preserve fifo en)
|
||
|
IMU.readRegister(&dataToWrite, LIS3DH_CTRL_REG5);
|
||
|
dataToWrite &= 0xF3; //Clear bits of interest
|
||
|
dataToWrite |= 0x08; //Latch interrupt (Cleared by reading int1_src)
|
||
|
//dataToWrite |= 0x04; //Pipe 4D detection from 6D recognition to int1?
|
||
|
IMU.writeRegister(LIS3DH_CTRL_REG5, dataToWrite);
|
||
|
|
||
|
//LIS3DH_CTRL_REG3
|
||
|
//Choose source for pin 1
|
||
|
dataToWrite = 0;
|
||
|
//dataToWrite |= 0x80; //Click detect on pin 1
|
||
|
dataToWrite |= 0x40; //AOI1 event (Generator 1 interrupt on pin 1)
|
||
|
dataToWrite |= 0x20; //AOI2 event ()
|
||
|
//dataToWrite |= 0x10; //Data ready
|
||
|
//dataToWrite |= 0x04; //FIFO watermark
|
||
|
//dataToWrite |= 0x02; //FIFO overrun
|
||
|
IMU.writeRegister(LIS3DH_CTRL_REG3, dataToWrite);
|
||
|
|
||
|
}
|
||
|
|
||
|
// Enter Sleep Mode
|
||
|
void enterSleep(){
|
||
|
// Save settings to internal flash memory
|
||
|
save_preferences_HAL();
|
||
|
|
||
|
// Configure IMU
|
||
|
uint8_t intDataRead;
|
||
|
// clear interrupt
|
||
|
IMU.readRegister(&intDataRead, LIS3DH_INT1_SRC);
|
||
|
configIMUInterruptsBeforeGoingToSleep();
|
||
|
// really clear interrupt
|
||
|
IMU.readRegister(&intDataRead, LIS3DH_INT1_SRC);
|
||
|
|
||
|
#if (ENABLE_WIFI_AND_MQTT == 1)
|
||
|
// Power down modem
|
||
|
wifiStop_HAL();
|
||
|
#endif
|
||
|
|
||
|
#if (ENABLE_KEYBOARD_BLE == 1)
|
||
|
keyboardBLE_end_HAL();
|
||
|
#endif
|
||
|
|
||
|
// Prepare IO states
|
||
|
digitalWrite(TFT_DC, LOW); // LCD control signals off
|
||
|
digitalWrite(TFT_CS, LOW);
|
||
|
digitalWrite(TFT_MOSI, LOW);
|
||
|
digitalWrite(TFT_SCLK, LOW);
|
||
|
digitalWrite(LCD_EN_GPIO, HIGH); // LCD logic off
|
||
|
digitalWrite(LCD_BL_GPIO, HIGH); // LCD backlight off
|
||
|
// pinMode(CRG_STAT, INPUT); // Disable Pull-Up
|
||
|
digitalWrite(IR_VCC_GPIO, LOW); // IR Receiver off
|
||
|
|
||
|
// Configure button matrix for ext1 interrupt
|
||
|
pinMode(SW_1_GPIO, OUTPUT);
|
||
|
pinMode(SW_2_GPIO, OUTPUT);
|
||
|
pinMode(SW_3_GPIO, OUTPUT);
|
||
|
pinMode(SW_4_GPIO, OUTPUT);
|
||
|
pinMode(SW_5_GPIO, OUTPUT);
|
||
|
digitalWrite(SW_1_GPIO, HIGH);
|
||
|
digitalWrite(SW_2_GPIO, HIGH);
|
||
|
digitalWrite(SW_3_GPIO, HIGH);
|
||
|
digitalWrite(SW_4_GPIO, HIGH);
|
||
|
digitalWrite(SW_5_GPIO, HIGH);
|
||
|
gpio_hold_en((gpio_num_t)SW_1_GPIO);
|
||
|
gpio_hold_en((gpio_num_t)SW_2_GPIO);
|
||
|
gpio_hold_en((gpio_num_t)SW_3_GPIO);
|
||
|
gpio_hold_en((gpio_num_t)SW_4_GPIO);
|
||
|
gpio_hold_en((gpio_num_t)SW_5_GPIO);
|
||
|
// Force display pins to high impedance
|
||
|
// Without this the display might not wake up from sleep
|
||
|
pinMode(LCD_BL_GPIO, INPUT);
|
||
|
pinMode(LCD_EN_GPIO, INPUT);
|
||
|
gpio_hold_en((gpio_num_t)LCD_BL_GPIO);
|
||
|
gpio_hold_en((gpio_num_t)LCD_EN_GPIO);
|
||
|
gpio_deep_sleep_hold_en();
|
||
|
|
||
|
esp_sleep_enable_ext1_wakeup(BUTTON_PIN_BITMASK, ESP_EXT1_WAKEUP_ANY_HIGH);
|
||
|
|
||
|
delay(100);
|
||
|
// Sleep
|
||
|
esp_deep_sleep_start();
|
||
|
}
|
||
|
|
||
|
void init_sleep_HAL() {
|
||
|
// will be called after boot or wakeup. Releases GPIO hold and sets wakeup_reason
|
||
|
if (sleepTimeout == 0){
|
||
|
sleepTimeout = DEFAULT_SLEEP_TIMEOUT;
|
||
|
}
|
||
|
|
||
|
// Find out wakeup cause
|
||
|
if (esp_sleep_get_wakeup_cause() == ESP_SLEEP_WAKEUP_EXT1) {
|
||
|
if (log(esp_sleep_get_ext1_wakeup_status())/log(2) == 13) {
|
||
|
wakeup_reason = WAKEUP_BY_IMU;
|
||
|
} else {
|
||
|
wakeup_reason = WAKEUP_BY_KEYPAD;
|
||
|
}
|
||
|
} else {
|
||
|
wakeup_reason = WAKEUP_BY_RESET;
|
||
|
}
|
||
|
|
||
|
pinMode(ACC_INT_GPIO, INPUT);
|
||
|
|
||
|
// Release GPIO hold in case we are coming out of standby
|
||
|
gpio_hold_dis((gpio_num_t)SW_1_GPIO);
|
||
|
gpio_hold_dis((gpio_num_t)SW_2_GPIO);
|
||
|
gpio_hold_dis((gpio_num_t)SW_3_GPIO);
|
||
|
gpio_hold_dis((gpio_num_t)SW_4_GPIO);
|
||
|
gpio_hold_dis((gpio_num_t)SW_5_GPIO);
|
||
|
gpio_hold_dis((gpio_num_t)LCD_EN_GPIO);
|
||
|
gpio_hold_dis((gpio_num_t)LCD_BL_GPIO);
|
||
|
gpio_deep_sleep_hold_dis();
|
||
|
}
|
||
|
|
||
|
void init_IMU_HAL(void) {
|
||
|
// setup IMU to recognize motion
|
||
|
IMU.settings.accelSampleRate = 50; //Hz. Can be: 0,1,10,25,50,100,200,400,1600,5000 Hz
|
||
|
IMU.settings.accelRange = 2; //Max G force readable. Can be: 2, 4, 8, 16
|
||
|
IMU.settings.adcEnabled = 0;
|
||
|
IMU.settings.tempEnabled = 0;
|
||
|
IMU.settings.xAccelEnabled = 1;
|
||
|
IMU.settings.yAccelEnabled = 1;
|
||
|
IMU.settings.zAccelEnabled = 1;
|
||
|
IMU.begin();
|
||
|
uint8_t intDataRead;
|
||
|
IMU.readRegister(&intDataRead, LIS3DH_INT1_SRC);//clear interrupt
|
||
|
|
||
|
}
|
||
|
|
||
|
void check_activity_HAL() {
|
||
|
activityDetection();
|
||
|
if(millis() - lastActivityTimestamp > sleepTimeout){
|
||
|
Serial.println("Entering Sleep Mode. Goodbye.");
|
||
|
enterSleep();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
uint32_t get_sleepTimeout_HAL() {
|
||
|
return sleepTimeout;
|
||
|
}
|
||
|
void set_sleepTimeout_HAL(uint32_t aSleepTimeout) {
|
||
|
sleepTimeout = aSleepTimeout;
|
||
|
}
|
||
|
bool get_wakeupByIMUEnabled_HAL() {
|
||
|
return wakeupByIMUEnabled;
|
||
|
}
|
||
|
void set_wakeupByIMUEnabled_HAL(bool aWakeupByIMUEnabled) {
|
||
|
wakeupByIMUEnabled = aWakeupByIMUEnabled;
|
||
|
}
|
||
|
uint32_t get_lastActivityTimestamp() {
|
||
|
return lastActivityTimestamp;
|
||
|
}
|