Files
lilygo-sensor-clock/lilygo-sensor-clock.ino
2025-10-18 17:18:21 +02:00

291 lines
9.4 KiB
C++

#include "lvgl.h"
#include "pins_config.h"
#include "AXS15231B.h"
#include "ui-util.h"
#include "WiFi.h"
#include "sntp.h"
#include "time.h"
#include <Arduino.h>
#include <XPowersLib.h>
#include <HTTPClient.h>
#include "freertos/semphr.h"
// Serial
#define BAUD 921600
// Sensor pins
#define RX2_PIN TOUCH_IICSCL
#define TX2_PIN TOUCH_IICSDA
// Sensor configuration
#define COMMAND "FDFCFBFA0800120000000200000004030203" //Normal mode command, see documentation waveshare sensor
#define PRESENCE_DISTANCE 200 // in cm
#define DELAY_STATUS_CHECK 1000 // delay (in ms) between two checks of the presence
#define SLEEP_DELAY 10000 // delay (in ms) until sleep mode
#define POWER_DELAY 30000 // delay (in ms) until turning off the screen
byte configCommand[] = {
0xFD, 0xFC, 0xFB, 0xFA, 0x08, 0x00, 0x12, 0x00,
0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x03,
0x02, 0x03
};
SemaphoreHandle_t xSemaphore = NULL;
PowersSY6970 PMU;
// Sensor variables
uint16_t state = 1; // 0 = presence detected, 1 = no presence detected for the past SLEEP_DELAY ms, 2 = no presence detected for the past POWER_DELAY ms
unsigned long lastMotionCheckTime = millis();
unsigned long lastMotionDetected = 0;
// Time variables
static uint32_t last_tick;
struct tm timeinfo;
uint32_t cycleInterval = 0;
// Display variables
static lv_disp_draw_buf_t draw_buf;
static lv_color_t *buf;
static lv_color_t *buf1;
// REST API Home assistant request
static String BASE_URL_HA = "http://192.168.1.2:8123/api/services";
static String HA_TOKEN = "abc";
void setup() {
xSemaphore = xSemaphoreCreateBinary();
xSemaphoreGive(xSemaphore);
initSerial();
initializeWifi();
initializeTime();
initializeUI();
}
void loop() {
delay(1);
if (transfer_num <= 0 && lcd_PushColors_len <= 0)
lv_timer_handler();
if (transfer_num <= 1 && lcd_PushColors_len > 0) {
lcd_PushColors(0, 0, 0, 0, NULL);
}
static int flag_bl = 0;
static unsigned long cnt = 0;
printLocalTime();
readAndProcessSensorLines();
cnt++;
if (cnt >= 100) {
if (flag_bl == 0) {
pinMode(TFT_BL, OUTPUT);
digitalWrite(TFT_BL, HIGH);
flag_bl = 1;
lv_delay_ms(500);
ui_begin();
}
}
}
void initSerial() {
// Start the primary serial communication (USB Monitor)
Serial.begin(BAUD);
unsigned long startAttemptTime = millis();
while (!Serial && millis() - startAttemptTime < 2000) {
delay(100);
}
Serial.println("Serial Monitor Initialized.");
pinMode(PIN_BAT_VOLT, ANALOG);
// Screen
pinMode(TOUCH_RES, OUTPUT);
digitalWrite(TOUCH_RES, HIGH);
delay(2);
digitalWrite(TOUCH_RES, LOW);
delay(10);
digitalWrite(TOUCH_RES, HIGH);
delay(2);
// Start Serial2 for the HMMD Sensor
Serial2.begin(BAUD, SERIAL_8N1, RX2_PIN, TX2_PIN);
Serial.println("Serial2 Initialized on RX:" + String(RX2_PIN) + ", TX:" + String(TX2_PIN));
Serial2.write(configCommand, sizeof(configCommand));
}
void initializeWifi() {
WiFi.persistent(true);
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
Serial.println("Connecting to WiFi...");
int connectTime = millis();
while(WiFi.status() != WL_CONNECTED) {
delay(200);
Serial.print(".");
if (millis() - connectTime >= WIFI_CONNECT_WAIT_MAX) {
ESP.restart();
}
}
Serial.println();
Serial.println("Connection succesful!");
Serial.printf("SSID: %s\r\n", WiFi.SSID().c_str());
String IP = WiFi.localIP().toString();
Serial.printf("IP address: %s\r\n", IP.c_str());
}
void initializeTime() {
Serial.println("Initializing time...");
configTime(GMT_OFFSET_SEC, DAY_LIGHT_OFFSET_SEC, NTP_SERVER1, NTP_SERVER2);
}
void initializeUI() {
Serial.println("Initializing UI....");
axs15231_init();
lv_init();
size_t buffer_size =
sizeof(lv_color_t) * EXAMPLE_LCD_H_RES * EXAMPLE_LCD_V_RES;
buf = (lv_color_t *)ps_malloc(buffer_size);
if (buf == NULL) {
while (1) {
Serial.println("buf NULL");
delay(500);
}
}
buf1 = (lv_color_t *)ps_malloc(buffer_size);
if (buf1 == NULL) {
while (1) {
Serial.println("buf NULL");
delay(500);
}
}
lv_disp_draw_buf_init(&draw_buf, buf, buf1, buffer_size);
/*Initialize the display*/
static lv_disp_drv_t disp_drv;
lv_disp_drv_init(&disp_drv);
/*Change the following line to your display resolution*/
disp_drv.hor_res = EXAMPLE_LCD_H_RES;
disp_drv.ver_res = EXAMPLE_LCD_V_RES;
disp_drv.flush_cb = my_disp_flush;
disp_drv.draw_buf = &draw_buf;
disp_drv.sw_rotate = 1; // If you turn on software rotation, Do not update or replace LVGL
disp_drv.rotated = LV_DISP_ROT_90;
disp_drv.full_refresh = 1; // full_refresh must be 1
lv_disp_drv_register(&disp_drv);
static lv_indev_drv_t indev_drv;
lv_indev_drv_init(&indev_drv);
indev_drv.type = LV_INDEV_TYPE_POINTER;
lv_indev_drv_register(&indev_drv);
Serial.println("UI initialized");
}
void printLocalTime() {
if (millis() - last_tick > 100) {
struct tm timeInfo;
if(!getLocalTime(&timeInfo)){
Serial.println("Failed to obtain time");
} else {
lv_msg_send(MSG_NEW_HOUR, &timeInfo.tm_hour);
lv_msg_send(MSG_NEW_MIN, &timeInfo.tm_min);
lv_msg_send(MSG_NEW_SEC, &timeInfo.tm_sec);
}
last_tick = millis();
}
}
void readAndProcessSensorLines() {
if (millis() - lastMotionCheckTime >= DELAY_STATUS_CHECK) {
lastMotionCheckTime = millis();
// Check if data is available on Serial2
while (Serial2.available() > 0) {
// Read a line of text until a newline character (\n) is received
// The timeout helps prevent blocking forever if a line ending is missed
String line = Serial2.readStringUntil('\n');
// Clean up the line: remove potential carriage return (\r) and leading/trailing whitespace
line.trim();
// Check if the line contains the "Range" information
if (line.startsWith("Range ")) {
// Extract the substring after "Range "
String distanceStr = line.substring(6);
int distance = distanceStr.toInt();
bool currentStatus = distance <= PRESENCE_DISTANCE;
if (currentStatus) {
lastMotionDetected = millis();
}
//Serial.print("Radar: ");
//Serial.println(distance);
if (currentStatus && state != 0) {
state = 0;
Serial.println("Motion detected, turning screen on.");
update_display_state(state);
//restApiAction(0);
} else if (!currentStatus && state == 1 && (millis() - lastMotionDetected >= POWER_DELAY)) {
state = 2;
Serial.printf("No motion detected for %d seconds, turning screen off.\n", POWER_DELAY);
update_display_state(state);
//restApiAction(2);
} else if (!currentStatus && state == 0 && (millis() - lastMotionDetected >= SLEEP_DELAY)) {
state = 1;
Serial.printf("No motion detected for %d seconds, turning screen screensaver on.\n", SLEEP_DELAY);
update_display_state(state);
//restApiAction(1);
}
}
}
}
}
// action: 0 => Screen on, => 1 Screen off, => 2 Power off
void restApiAction(int action) {
WiFiClient client;
HTTPClient http;
int httpResponseCode;
String url;
if (WiFi.status() != WL_CONNECTED){
Serial.println("WiFi Disconnected. No request sent");
}
switch (action) {
case 0: // Screen on
Serial.println("Sending Screen ON Request");
url = BASE_URL_HA + "/webostv/command";
http.begin(client, url);
http.addHeader("Authorization", "Bearer " + HA_TOKEN);
http.addHeader("Content-Type", "application/json");
httpResponseCode = http.POST("{\"entity_id\":\"media_player.lg_webos_smart_tv\",\"command\":\"com.webos.service.tvpower/power/turnOnScreen\"}");
break;
case 1: // Screen off
Serial.println("Sending Screen OFF Request");
url = BASE_URL_HA + "/webostv/command";
http.begin(client, url);
http.addHeader("Authorization", "Bearer " + HA_TOKEN);
http.addHeader("Content-Type", "application/json");
httpResponseCode = http.POST("{\"entity_id\":\"media_player.lg_webos_smart_tv\",\"command\":\"com.webos.service.tvpower/power/turnOffScreen\"}");
break;
case 2: // Power off
Serial.println("Sending Power Off Request");
url = BASE_URL_HA + "/webostv/command";
http.begin(client, url);
http.addHeader("Authorization", "Bearer " + HA_TOKEN);
http.addHeader("Content-Type", "application/json");
httpResponseCode = http.POST("{\"entity_id\":\"media_player.lg_webos_smart_tv\",\"command\":\"com.webos.service.tvpower/power/powerOffScreen\"}");
break;
}
Serial.print("HTTP Response code: ");
Serial.println(httpResponseCode);
http.end();
}