iot-firmware/src/espmega_iot_core.cpp

1744 lines
49 KiB
C++
Raw Normal View History

2023-11-15 09:55:43 +00:00
/**
* @file espmega_iot_core.cpp
* @brief Implementation of the ESPMega IoT Core library.
*
* This library provides the core functionality for the ESPMega IoT platform, including network connectivity,
* input/output control, and MQTT communication. It also includes support for an internal LCD display and
2023-11-15 09:55:43 +00:00
* air conditioner control.
*
2023-11-15 09:55:43 +00:00
* @author Siwat Sirichai
* @date 15 Nov 2023
*/
2023-09-29 16:18:21 +00:00
#include <espmega_iot_core.hpp>
2023-09-28 07:55:02 +00:00
// OS Configuration
2023-09-30 14:09:45 +00:00
// #define FASTBOOT
2023-11-07 12:42:43 +00:00
#ifndef ESPMEGA_REV
2023-09-30 14:09:45 +00:00
#define ESPMEGA_REV "ESPMega PRO R3.3b"
2023-11-07 12:42:43 +00:00
#endif
2023-09-28 07:55:02 +00:00
2023-08-25 18:38:17 +00:00
// Network Connectivity
2023-09-11 17:37:33 +00:00
char HOSTNAME[15];
IPAddress IP(0, 0, 0, 0);
IPAddress SUBNET(0, 0, 0, 0);
IPAddress GATEWAY(0, 0, 0, 0);
IPAddress DNS(0, 0, 0, 0);
IPAddress MQTT_SERVER(0, 0, 0, 0);
uint16_t MQTT_PORT = 0;
2023-11-07 13:43:20 +00:00
#ifdef ENABLE_WEBUI
2023-09-29 19:54:38 +00:00
WebServer otaserver(80);
2023-11-07 13:43:20 +00:00
#endif
2023-09-03 09:28:01 +00:00
bool standalone = true;
2023-09-11 18:11:49 +00:00
char MQTT_BASE_TOPIC[20];
uint8_t base_topic_length = 0;
char STATE_REQUEST_TOPIC[40];
2023-11-07 05:19:35 +00:00
bool MQTT_USE_AUTH = false;
char MQTT_USERNAME[32];
char MQTT_PASSWORD[32];
2023-09-16 18:12:29 +00:00
uint8_t utc_offset = 7;
2023-11-07 13:43:20 +00:00
#ifdef ENABLE_CLIMATE_MODULE
2023-10-03 20:14:33 +00:00
float current_room_temp = 0;
float current_room_humid = 0;
2023-11-07 13:43:20 +00:00
#endif
2023-08-25 18:38:17 +00:00
// Inputs
2023-08-26 08:22:59 +00:00
#define VINT_COUNT 16
2023-08-25 18:38:17 +00:00
const int DEBOUNCE_TIME_MS = 50;
2023-08-26 08:22:59 +00:00
const int virtual_interrupt_pins[VINT_COUNT] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
int virtual_interupt_state[VINT_COUNT];
unsigned long virtual_interupt_timer[VINT_COUNT];
2023-08-25 18:38:17 +00:00
// Outputs
#define PWM_COUNT 16
2023-09-11 09:13:09 +00:00
const uint8_t pwm_pins[PWM_COUNT] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
bool pwm_states[PWM_COUNT];
uint8_t pwm_states_eeprom[PWM_COUNT];
uint16_t pwm_values[PWM_COUNT];
2023-09-11 17:37:33 +00:00
uint8_t pwm_values_eeprom[PWM_COUNT * 2];
2023-09-10 07:24:27 +00:00
// output = m*input+c
const float pwm_linear_scaling_m[PWM_COUNT] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
const float pwm_linear_scaling_c[PWM_COUNT] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
2023-08-25 18:38:17 +00:00
#define PWM_CYCLE_VALUES_COUNT 3
const int PWM_CYCLE_VALUES[PWM_CYCLE_VALUES_COUNT] = {50, 125, 255};
2023-09-11 18:11:49 +00:00
char PWM_SET_STATE_TOPIC[70];
char PWM_SET_VALUE_TOPIC[70];
2023-08-25 18:38:17 +00:00
2023-11-07 13:43:20 +00:00
#ifdef ENABLE_INTERNAL_LCD
2023-09-03 09:28:01 +00:00
// LCD
int lcd_current_page = 1;
2023-09-03 10:41:31 +00:00
int lcd_pwmAdj_id = 0;
2023-09-03 09:28:01 +00:00
EasyNex panel(Serial);
2023-11-07 13:43:20 +00:00
#endif
2023-09-03 09:28:01 +00:00
2023-08-28 17:49:11 +00:00
// Air Conditioner Control
/*
Mode 0: Off, 1: Cool, 2: Fan
Fan Speed 0: Auto, 1: High, 2: Mid, 3: Low
*/
2023-11-07 13:43:20 +00:00
#ifdef ENABLE_CLIMATE_MODULE
2023-09-29 17:06:49 +00:00
#define DHT22_PIN 32
2023-09-11 09:13:09 +00:00
uint8_t ac_mode = 0;
uint8_t ac_fan_speed = 0;
uint8_t ac_temperature = 25;
2023-09-11 18:11:49 +00:00
char AC_SET_MODE_TOPIC[75];
char AC_SET_FAN_TOPIC[75];
char AC_SET_TEMPERATURE_TOPIC[75];
char AC_MODE_TOPIC[75];
char AC_FAN_TOPIC[75];
char AC_TEMPERATURE_TOPIC[75];
char AC_ROOM_TEMPERATURE_TOPIC[75];
char AC_HUMIDITY_TOPIC[75];
2023-11-07 13:43:20 +00:00
#endif
2023-08-27 19:07:04 +00:00
2023-09-11 17:37:33 +00:00
// EEPROM ADDRESS
2023-11-07 08:49:11 +00:00
#define EEPROM_ADDRESS_AC_MODE 0 // 01bytes
#define EEPROM_ADDRESS_AC_TEMPERATURE 1 // 01bytes
#define EEPROM_ADDRESS_AC_FAN_SPEED 2 // 01bytes
#define EEPROM_ADDRESS_PWM_STATE 3 // 16bytes, thru 18
#define EEPROM_ADDRESS_PWM_VALUE 19 // 32bytes, thru 50
#define EEPROM_ADDRESS_HOSTNAME 65 // 15bytes, thru 79
#define EEPROM_ADDRESS_TOPIC 80 // 20bytes, thru 99
#define EEPROM_ADDRESS_IP 100 // 04bytes, thru 103
#define EEPROM_ADDRESS_SUBNET 104 // 04bytes, thru 107
#define EEPROM_ADDRESS_GATEWAY 108 // 04bytes, thru 111
#define EEPROM_ADDRESS_DNS 112 // 04bytes, thru 115
#define EEPROM_ADDRESS_MQTT_SERVER 116 // 04bytes, thru 119
#define EEPROM_ADDRESS_MQTT_PORT 120 // 02bytes, thru 121
#define EEPROM_ADDRESS_MQTT_USERNAME 122 // 32bytes, thru 153
#define EEPROM_ADDRESS_MQTT_PASSWORD 154 // 32bytes, thru 185
#define EEPROM_ADDRESS_MQTT_USEAUTH 186 // 1bytes
2023-09-11 17:37:33 +00:00
2023-09-11 18:11:49 +00:00
char PWM_STATE_TOPIC[75];
char PWM_VALUE_TOPIC[75];
char INPUTS_TOPIC[75];
2023-08-26 08:22:59 +00:00
2023-08-25 18:38:17 +00:00
WiFiClient eth;
PubSubClient mqtt(MQTT_SERVER, 1883, eth);
2023-08-25 18:38:17 +00:00
2023-11-07 13:43:20 +00:00
#ifdef ENABLE_CLIMATE_MODULE
2023-08-28 17:49:11 +00:00
DHTNEW env_sensor(DHT22_PIN);
2023-11-07 13:43:20 +00:00
#endif
2023-08-28 17:49:11 +00:00
2023-08-25 18:38:17 +00:00
Thread mqtt_reconnector = Thread();
2023-08-28 17:49:11 +00:00
Thread environment_reporter = Thread();
2023-09-11 09:13:09 +00:00
Thread eeprom_pwm_updater = Thread();
2023-09-29 17:06:49 +00:00
Thread user_timer_tick = Thread();
StaticThreadController<4> thread_controller(&mqtt_reconnector, &environment_reporter, &eeprom_pwm_updater, &user_timer_tick);
2023-08-25 18:38:17 +00:00
2023-11-07 13:43:20 +00:00
#ifdef ENABLE_INTERNAL_LCD
2023-09-03 09:28:01 +00:00
Thread top_bar_updater = Thread();
Thread page_updater = Thread();
StaticThreadController<2> lcd_thread_controller(&top_bar_updater, &page_updater);
2023-11-07 13:43:20 +00:00
#endif
2023-09-03 09:28:01 +00:00
2023-08-25 18:38:17 +00:00
void setup()
{
Serial.begin(115200);
2023-09-30 14:09:45 +00:00
#ifdef ENABLE_EXTERNAL_LCD
Serial2.begin(115200, SERIAL_8N1, RXD2, TXD2);
#endif
2023-11-07 13:43:20 +00:00
#ifdef ENABLE_INTERNAL_LCD
2023-09-03 09:28:01 +00:00
panel.begin(115200);
2023-11-07 13:43:20 +00:00
#endif
2023-09-11 17:37:33 +00:00
Serial.println("ESPMega R3 Initializing");
2023-08-26 05:53:25 +00:00
ESPMega_begin();
2023-08-26 10:28:21 +00:00
io_begin();
2023-09-11 09:13:09 +00:00
eeprom_retrieve_init();
2023-10-02 20:47:12 +00:00
user_pre_init();
2023-11-07 13:43:20 +00:00
#ifdef ENABLE_INTERNAL_LCD
2023-09-11 17:37:33 +00:00
lcd_send_stop_bit();
lcd_init();
lcd_begin();
2023-11-07 13:43:20 +00:00
#endif
2023-11-07 12:16:30 +00:00
check_boot_reset();
2023-09-30 14:09:45 +00:00
#ifdef ENABLE_EXTERNAL_LCD
2023-09-30 07:28:21 +00:00
Serial2.print("rest");
Serial2.write(0xFF);
Serial2.write(0xFF);
Serial2.write(0xFF);
2023-09-30 14:09:45 +00:00
#endif
2023-09-11 17:37:33 +00:00
lcd_send_command("boot_state.txt=\"Core Initializing . . .\"");
2023-08-27 19:07:04 +00:00
Serial.println("Initializing Infrared . . .");
2023-11-07 13:43:20 +00:00
#ifdef ENABLE_IR_MODULE
2023-09-03 09:28:01 +00:00
lcd_send_command("boot_state.txt=\"Infrared Initializing . . .\"");
2023-08-28 17:49:11 +00:00
IrReceiver.begin(IR_RECIEVE_PIN);
IrSender.begin(IR_SEND_PIN);
2023-11-07 13:43:20 +00:00
#endif
2023-09-03 09:28:01 +00:00
lcd_send_command("boot_state.txt=\"Network Initializing . . .\"");
2023-08-25 18:38:17 +00:00
network_begin();
2023-09-03 09:28:01 +00:00
lcd_send_command("boot_state.txt=\"IoT Core Initializing . . .\"");
mqtt.setSocketTimeout(1000);
2023-11-07 12:28:21 +00:00
eth.setTimeout(1);
2023-11-12 14:25:08 +00:00
mqtt.setCallback(&mqtt_callback);
2023-08-25 18:38:17 +00:00
mqtt_connect();
2023-09-03 09:28:01 +00:00
lcd_send_command("boot_state.txt=\"Threads Initializing . . .\"");
2023-08-25 18:38:17 +00:00
thread_initialization();
2023-11-07 13:43:20 +00:00
#ifdef ENABLE_WEBUI
2023-09-29 19:54:38 +00:00
ota_begin();
2023-11-07 13:43:20 +00:00
#endif
2023-08-26 08:22:59 +00:00
Serial.println("Initialization Completed.");
Serial.println("Jumping to User Code.");
2023-09-29 16:18:21 +00:00
user_init();
2023-09-03 09:28:01 +00:00
lcd_send_command("page dashboard");
2023-08-25 18:38:17 +00:00
}
void loop()
{
virtual_interrupt_loop();
mqtt.loop();
2023-08-25 18:38:17 +00:00
ESPMega_loop();
2023-11-07 13:43:20 +00:00
#ifdef ENABLE_IR_MODULE
2023-08-27 19:07:04 +00:00
ir_loop();
2023-11-07 13:43:20 +00:00
#endif
2023-08-26 05:53:25 +00:00
thread_controller.run();
2023-11-07 13:43:20 +00:00
#ifdef ENABLE_INTERNAL_LCD
2023-09-03 09:28:01 +00:00
lcd_loop();
2023-11-07 13:43:20 +00:00
#endif
2023-09-29 16:18:21 +00:00
user_loop();
2023-11-07 13:43:20 +00:00
#ifdef ENABLE_WEBUI
2023-09-29 19:54:38 +00:00
otaserver.handleClient();
2023-11-07 13:43:20 +00:00
#endif
2023-08-26 05:53:25 +00:00
}
2023-11-15 09:55:43 +00:00
/**
* @brief Retrieves data from EEPROM and initializes various variables and topics.
*
2023-11-15 09:55:43 +00:00
*/
2023-09-11 09:13:09 +00:00
void eeprom_retrieve_init()
{
2023-11-07 13:43:20 +00:00
// EEPROM Data Retrival
#ifdef ENABLE_CLIMATE_MODULE
2023-09-28 07:55:02 +00:00
ac_mode = ESPMega_FRAM.read8(EEPROM_ADDRESS_AC_MODE);
ac_temperature = ESPMega_FRAM.read8(EEPROM_ADDRESS_AC_TEMPERATURE);
ac_fan_speed = ESPMega_FRAM.read8(EEPROM_ADDRESS_AC_FAN_SPEED);
2023-11-07 13:43:20 +00:00
#endif
// EEPROM Data Retrival Validation
#ifdef ENABLE_CLIMATE_MODULE
2023-09-11 09:13:09 +00:00
if (ac_mode > 2)
{
ac_mode = 0;
2023-09-28 07:55:02 +00:00
ESPMega_FRAM.write8(EEPROM_ADDRESS_AC_MODE, ac_mode);
2023-09-11 09:13:09 +00:00
}
if (ac_temperature > AC_MAX_TEMPERATURE || ac_temperature < AC_MIN_TEMPERATURE)
{
ac_temperature = AC_MAX_TEMPERATURE;
2023-09-28 07:55:02 +00:00
ESPMega_FRAM.write8(EEPROM_ADDRESS_AC_TEMPERATURE, ac_temperature);
2023-09-11 09:13:09 +00:00
}
if (ac_fan_speed > 3)
{
ac_fan_speed = 0;
2023-09-28 07:55:02 +00:00
ESPMega_FRAM.write8(EEPROM_ADDRESS_AC_TEMPERATURE, ac_fan_speed);
2023-09-11 09:13:09 +00:00
}
2023-09-11 17:37:33 +00:00
ac_set_state(ac_mode, ac_temperature, ac_fan_speed);
2023-11-07 13:43:20 +00:00
#endif
2023-09-28 07:55:02 +00:00
ESPMega_FRAM.read(EEPROM_ADDRESS_PWM_STATE, pwm_states_eeprom, 16);
2023-09-11 09:13:09 +00:00
memcpy(pwm_states, pwm_states_eeprom, 16);
2023-09-28 07:55:02 +00:00
ESPMega_FRAM.read(EEPROM_ADDRESS_PWM_VALUE, pwm_values_eeprom, 32);
2023-09-11 09:13:09 +00:00
memcpy(pwm_values, pwm_values_eeprom, 32);
2023-09-11 17:37:33 +00:00
for (int i = 0; i < 15; i++)
{
if (pwm_states[i] <= 1)
pwm_set_state(i, pwm_states[i]);
else
pwm_set_state(i, 0);
if (pwm_values[i] <= 4095)
pwm_set_value(i, pwm_values[i]);
else
pwm_set_value(i, 0);
2023-09-11 09:13:09 +00:00
}
2023-09-11 17:37:33 +00:00
IP = eeprom_ip_retrieve(EEPROM_ADDRESS_IP);
SUBNET = eeprom_ip_retrieve(EEPROM_ADDRESS_SUBNET);
GATEWAY = eeprom_ip_retrieve(EEPROM_ADDRESS_GATEWAY);
DNS = eeprom_ip_retrieve(EEPROM_ADDRESS_DNS);
MQTT_SERVER = eeprom_ip_retrieve(EEPROM_ADDRESS_MQTT_SERVER);
eeprom_hostname_retrieve();
eeprom_mqtt_port_retrieve();
2023-11-07 05:19:35 +00:00
eeprom_mqtt_useauth_retrieve();
eeprom_mqtt_username_retrieve();
eeprom_mqtt_password_retrieve();
mqtt.setServer(MQTT_SERVER, MQTT_PORT);
2023-09-11 18:11:49 +00:00
eeprom_basetopic_retrieve();
2023-09-30 14:09:45 +00:00
base_topic_length = strlen(MQTT_BASE_TOPIC) + 1;
2023-09-11 18:11:49 +00:00
memcpy(STATE_REQUEST_TOPIC, MQTT_BASE_TOPIC, 20);
strcat(STATE_REQUEST_TOPIC, "/requeststate");
memcpy(PWM_SET_STATE_TOPIC, MQTT_BASE_TOPIC, 20);
strcat(PWM_SET_STATE_TOPIC, "/pwm/00/set/state");
memcpy(PWM_SET_VALUE_TOPIC, MQTT_BASE_TOPIC, 20);
strcat(PWM_SET_VALUE_TOPIC, "/pwm/00/set/value");
2023-11-07 13:43:20 +00:00
#ifdef ENABLE_CLIMATE_MODULE
2023-09-11 18:11:49 +00:00
memcpy(AC_SET_MODE_TOPIC, MQTT_BASE_TOPIC, 20);
strcat(AC_SET_MODE_TOPIC, "/ac/set/mode");
memcpy(AC_SET_FAN_TOPIC, MQTT_BASE_TOPIC, 20);
strcat(AC_SET_FAN_TOPIC, "/ac/set/fan_speed");
memcpy(AC_SET_TEMPERATURE_TOPIC, MQTT_BASE_TOPIC, 20);
strcat(AC_SET_TEMPERATURE_TOPIC, "/ac/set/temperature");
memcpy(AC_MODE_TOPIC, MQTT_BASE_TOPIC, 20);
strcat(AC_MODE_TOPIC, "/ac/mode");
memcpy(AC_FAN_TOPIC, MQTT_BASE_TOPIC, 20);
strcat(AC_FAN_TOPIC, "/ac/fan_speed");
memcpy(AC_TEMPERATURE_TOPIC, MQTT_BASE_TOPIC, 20);
strcat(AC_TEMPERATURE_TOPIC, "/ac/temperature");
memcpy(AC_ROOM_TEMPERATURE_TOPIC, MQTT_BASE_TOPIC, 20);
strcat(AC_ROOM_TEMPERATURE_TOPIC, "/ac/room_temperature");
memcpy(AC_HUMIDITY_TOPIC, MQTT_BASE_TOPIC, 20);
strcat(AC_HUMIDITY_TOPIC, "/ac/humidity");
2023-11-07 13:43:20 +00:00
#endif
2023-09-11 18:11:49 +00:00
memcpy(PWM_STATE_TOPIC, MQTT_BASE_TOPIC, 20);
strcat(PWM_STATE_TOPIC, "/pwm/00/state");
memcpy(PWM_VALUE_TOPIC, MQTT_BASE_TOPIC, 20);
strcat(PWM_VALUE_TOPIC, "/pwm/00/value");
memcpy(INPUTS_TOPIC, MQTT_BASE_TOPIC, 20);
strcat(INPUTS_TOPIC, "/input/00");
2023-09-11 09:13:09 +00:00
}
2023-11-07 13:43:20 +00:00
#ifdef ENABLE_WEBUI
2023-11-15 09:55:43 +00:00
/**
* Initializes the OTA (Over-The-Air) update server and sets up the necessary routes for configuration and firmware updates.
*
2023-11-15 09:55:43 +00:00
* The OTA server can be accessed through the root URL ("/") and the configuration page can be accessed through "/config".
* The firmware update can be initiated through the "/update" route using HTTP POST method.
*
2023-11-15 09:55:43 +00:00
* The configuration page allows the user to set the device IP address, network mask, gateway, DNS server, hostname, BMS server IP address, BMS server port, BMS server endpoint, and BMS server authentication credentials.
*
2023-11-15 09:55:43 +00:00
* @return void
*/
2023-09-30 14:09:45 +00:00
void ota_begin()
{
otaserver.on("/", HTTP_GET, []()
{
2023-09-29 19:54:38 +00:00
otaserver.sendHeader("Connection", "close");
2023-11-07 09:03:46 +00:00
String otabuffer = ota_part1;
otabuffer+=ota_part2_1+"Hostname"+ota_part2_2+String(HOSTNAME)+ota_part2_3;
otabuffer+=ota_part2_1+"IP Address"+ota_part2_2+IP.toString()+ota_part2_3;
otabuffer+=ota_part2_1+"MAC Address"+ota_part2_2+ETH.macAddress()+ota_part2_3;
otabuffer+=ota_part2_1+"Device"+ota_part2_2+ESPMEGA_REV+ota_part2_3;
otabuffer+=ota_part2_1+"BMS Server"+ota_part2_2+MQTT_SERVER.toString()+ota_part2_3;
otabuffer+=ota_part2_1+"BMS Endpoint"+ota_part2_2+String(MQTT_BASE_TOPIC)+ota_part2_3;
otabuffer+=ota_part2_1+"Centrally Managed"+ota_part2_2;
2023-09-30 14:09:45 +00:00
if(standalone)
otabuffer+=String("No");
else
otabuffer+=String("Yes");
2023-11-07 09:03:46 +00:00
otabuffer+=ota_part2_3+ota_part3;
2023-09-30 14:09:45 +00:00
otaserver.send(200, "text/html", otabuffer); });
2023-11-07 09:46:47 +00:00
otaserver.on("/config", HTTP_GET, []()
{
otaserver.sendHeader("Connection", "close");
String configbuffer = config_part1;
configbuffer+=config_txt_part1+"IP Address"+config_txt_part2+"text"+config_txt_part3+"dev_ip"+config_txt_part4+"dev_ip"+config_txt_part5+IP.toString()+config_txt_part6;
configbuffer+=config_txt_part1+"Network Mask"+config_txt_part2+"text"+config_txt_part3+"netmask"+config_txt_part4+"netmask"+config_txt_part5+SUBNET.toString()+config_txt_part6;
2023-11-07 09:46:47 +00:00
configbuffer+=config_txt_part1+"Gateway"+config_txt_part2+"text"+config_txt_part3+"gateway"+config_txt_part4+"gateway"+config_txt_part5+GATEWAY.toString()+config_txt_part6;
configbuffer+=config_txt_part1+"DNS Server"+config_txt_part2+"text"+config_txt_part3+"dns"+config_txt_part4+"dns"+config_txt_part5+DNS.toString()+config_txt_part6;
configbuffer+=config_txt_part1+"Hostname"+config_txt_part2+"text"+config_txt_part3+"hostname"+config_txt_part4+"hostname"+config_txt_part5+String(HOSTNAME)+config_txt_part6;
configbuffer+=config_txt_part1+"BMS Server - IP Address"+config_txt_part2+"text"+config_txt_part3+"bms_ip"+config_txt_part4+"bms_ip"+config_txt_part5+MQTT_SERVER.toString()+config_txt_part6;
configbuffer+=config_txt_part1+"BMS Server - Port"+config_txt_part2+"text"+config_txt_part3+"bms_port"+config_txt_part4+"bms_port"+config_txt_part5+String(MQTT_PORT)+config_txt_part6;
2023-11-07 11:09:12 +00:00
configbuffer+=config_auth_part1+(MQTT_USE_AUTH?"checked=\"checked\"":"")+config_auth_part2;
2023-11-07 09:46:47 +00:00
configbuffer+=config_txt_part1+"BMS Server - Username"+config_txt_part2+"text"+config_txt_part3+"bms_username"+config_txt_part4+"bms_username"+config_txt_part5+String(MQTT_USERNAME)+config_txt_part6;
configbuffer+=config_txt_part1+"BMS Server - Password"+config_txt_part2+"password"+config_txt_part3+"bms_password"+config_txt_part4+"bms_password"+config_txt_part5+String(MQTT_PASSWORD)+config_txt_part6;
configbuffer+=config_txt_part1+"BMS Server - Endpoint"+config_txt_part2+"text"+config_txt_part3+"bms_endpoint"+config_txt_part4+"bms_endpoint"+config_txt_part5+String(MQTT_BASE_TOPIC)+config_txt_part6;
configbuffer+=config_part2;
otaserver.send(200, "text/html", configbuffer); });
otaserver.on("/save_config", HTTP_GET, []()
{
otaserver.sendHeader("Connection", "close");
String configbuffer = "Configuration Saved. Rebooting . . .";
otaserver.send(200, "text/html", configbuffer);
bool use_auth = false;
for(int i=0;i<otaserver.args();i++) {
String arg = otaserver.argName(i);
String value = otaserver.arg(i);
if(!arg.compareTo("dev_ip")) {
set_ip(value);
} else if(!arg.compareTo("netmask")) {
set_netmask(value);
} else if(!arg.compareTo("gateway")) {
set_gw(value);
} else if(!arg.compareTo("dns")) {
set_dns(value);
} else if(!arg.compareTo("hostname")) {
set_hostname(value);
} else if(!arg.compareTo("bms_ip")) {
set_mqtt_server(value);
} else if(!arg.compareTo("bms_username")) {
set_mqtt_username(value);
}else if(!arg.compareTo("bms_port")) {
uint16_t port = value.toInt();
mqtt_port_set(port);
}else if(!arg.compareTo("bms_endpoint")) {
set_basetopic(value);
}
else if(!arg.compareTo("bms_password")) {
set_mqtt_password(value);
} else if(!arg.compareTo("bms_useauth")) {
if(!value.compareTo("yes"))
use_auth = true;
}
}
set_mqtt_useauth(use_auth);
otaserver.send(200, "text/html", configbuffer);
delay(500);
2023-11-07 13:43:20 +00:00
ESP.restart(); });
2023-09-30 14:09:45 +00:00
otaserver.on(
"/update", HTTP_POST, []()
{
2023-09-29 19:54:38 +00:00
otaserver.sendHeader("Connection", "close");
otaserver.send(200, "text/plain", (Update.hasError()) ? "FAIL" : "OK");
2023-09-30 14:09:45 +00:00
ESP.restart(); },
[]()
{
HTTPUpload &upload = otaserver.upload();
if (upload.status == UPLOAD_FILE_START)
{
2023-10-01 05:32:12 +00:00
lcd_send_command("page ota");
2023-10-01 06:48:02 +00:00
Serial.println(upload.currentSize);
2023-10-01 05:32:12 +00:00
String otafiletxt = "Downloading File : " + upload.filename;
2023-11-07 13:43:20 +00:00
#ifdef ENABLE_INTERNAL_LCD
2023-10-01 06:48:02 +00:00
lcd_send_stop_bit();
panel.writeStr("otatxt.txt", otafiletxt);
2023-11-07 13:43:20 +00:00
#endif
2023-09-30 14:09:45 +00:00
Serial.printf("Update: %s\n", upload.filename.c_str());
if (!Update.begin(UPDATE_SIZE_UNKNOWN))
{
2023-11-07 13:43:20 +00:00
#ifdef ENABLE_INTERNAL_LCD
2023-10-01 06:48:02 +00:00
panel.writeStr("otatxt.txt", "Update Failed, Rebooting . . .");
2023-11-07 13:43:20 +00:00
#endif
2023-09-30 14:09:45 +00:00
Update.printError(Serial);
}
}
else if (upload.status == UPLOAD_FILE_WRITE)
{
if (Update.write(upload.buf, upload.currentSize) != upload.currentSize)
{
2023-11-07 13:43:20 +00:00
#ifdef ENABLE_INTERNAL_LCD
2023-10-01 06:48:02 +00:00
panel.writeStr("otatxt.txt", "Update Failed, Rebooting . . .");
2023-11-07 13:43:20 +00:00
#endif
2023-09-30 14:09:45 +00:00
Update.printError(Serial);
}
2023-10-01 06:48:02 +00:00
if (upload.currentSize != 0 && upload.totalSize != 0)
{
lcd_send_stop_bit();
uint32_t totalsize_kb = upload.totalSize / 1000;
2023-10-02 11:02:17 +00:00
uint32_t upload_pct = 100 * upload.totalSize / 1000000;
String otafiletxt = "Downloading File : " + upload.filename + " (" + String(totalsize_kb) + "KB)";
2023-11-07 13:43:20 +00:00
#ifdef ENABLE_INTERNAL_LCD
2023-10-01 06:48:02 +00:00
panel.writeNum("prog.val", upload_pct);
panel.writeStr("otatxt.txt", otafiletxt);
2023-11-07 13:43:20 +00:00
#endif
2023-10-01 06:48:02 +00:00
}
2023-09-30 14:09:45 +00:00
}
else if (upload.status == UPLOAD_FILE_END)
{
if (Update.end(true))
{
2023-11-07 13:43:20 +00:00
#ifdef ENABLE_INTERNAL_LCD
2023-10-01 06:48:02 +00:00
panel.writeStr("otatxt.txt", "Update Completed, Rebooting . . .");
2023-11-07 13:43:20 +00:00
#endif
2023-09-30 14:09:45 +00:00
Serial.printf("Update Success: %u\nRebooting...\n", upload.totalSize);
}
else
{
2023-11-07 13:43:20 +00:00
#ifdef ENABLE_INTERNAL_LCD
2023-10-01 06:48:02 +00:00
panel.writeStr("otatxt.txt", "Update Failed, Rebooting . . .");
2023-11-07 13:43:20 +00:00
#endif
2023-09-30 14:09:45 +00:00
Update.printError(Serial);
}
}
});
2023-09-29 19:54:38 +00:00
otaserver.begin();
}
2023-11-07 13:43:20 +00:00
#endif
2023-09-29 19:54:38 +00:00
2023-08-26 05:53:25 +00:00
void io_begin()
{
Serial.println("Initializing I/O . . .");
2023-11-07 13:43:20 +00:00
#ifdef ENABLE_IR_MODULE
2023-08-28 17:49:11 +00:00
pinMode(IR_RECIEVE_PIN, INPUT_PULLUP);
pinMode(IR_SEND_PIN, OUTPUT);
2023-11-07 13:43:20 +00:00
#endif
2023-08-25 18:38:17 +00:00
}
2023-11-15 09:55:43 +00:00
/**
* @brief Initializes the network and sets the hostname and IP configuration.
*
* @details This function initializes the Ethernet connection and sets the hostname, IP address, gateway, subnet, and DNS server.
* It also initializes the NTP core and updates the time from the NTP server.
*
2023-11-15 09:55:43 +00:00
* @return void
*/
2023-08-25 18:38:17 +00:00
void network_begin()
{
2023-08-26 05:53:25 +00:00
Serial.print("Initializing Network ");
2023-08-25 18:38:17 +00:00
ETH.begin();
2023-08-26 05:53:25 +00:00
ETH.setHostname(HOSTNAME);
ETH.config(IP, GATEWAY, SUBNET, DNS, DNS);
2023-09-30 14:09:45 +00:00
#ifndef FASTBOOT
2023-09-16 08:55:34 +00:00
delay(1000);
2023-09-16 18:12:29 +00:00
lcd_send_command("boot_state.txt=\"Ethernet Core Initializing \"");
delay(500);
lcd_send_command("boot_state.txt=\"Ethernet Core Initializing . \"");
delay(500);
lcd_send_command("boot_state.txt=\"Ethernet Core Initializing . . \"");
delay(500);
2023-09-16 08:55:34 +00:00
lcd_send_command("boot_state.txt=\"Ethernet Core Initializing . . .\"");
delay(500);
2023-09-16 18:12:29 +00:00
lcd_send_command("boot_state.txt=\"NTP Core Initializing . . .\"");
2023-09-16 08:55:34 +00:00
delay(500);
2023-09-30 14:09:45 +00:00
#endif
2023-09-16 18:12:29 +00:00
char ntp[19];
2023-09-30 14:09:45 +00:00
MQTT_SERVER.toString().toCharArray(ntp, 19);
ESPMega_configNTP(utc_offset * 3600, 0, ntp);
2023-09-16 18:12:29 +00:00
ESPMega_updateTimeFromNTP();
2023-08-26 05:53:25 +00:00
Serial.println();
2023-08-25 18:38:17 +00:00
}
2023-11-15 09:55:43 +00:00
/**
* @brief Connects to the MQTT broker and subscribes to topics.
*
2023-11-15 09:55:43 +00:00
* If MQTT_USE_AUTH is true, it connects to the broker using the provided username and password.
* If the connection is successful, it subscribes to topics and publishes PWM and input states.
* If ENABLE_CLIMATE_MODULE is defined, it also publishes AC state.
*
2023-11-15 09:55:43 +00:00
* @note This function assumes that the MQTT client has been initialized and WiFi is connected.
*
2023-11-15 09:55:43 +00:00
* @return void
*/
2023-08-25 18:38:17 +00:00
void mqtt_connect()
{
if (!mqtt.connected())
2023-08-25 18:38:17 +00:00
{
Serial.print("MQTT not connected, connecting . . .\n");
2023-09-03 09:28:01 +00:00
lcd_send_stop_bit();
2023-11-07 08:49:11 +00:00
if (MQTT_USE_AUTH)
mqtt.connect(HOSTNAME, MQTT_USERNAME, MQTT_PASSWORD);
2023-11-07 08:49:11 +00:00
else
mqtt.connect(HOSTNAME);
if (mqtt.connected())
2023-08-25 18:38:17 +00:00
{
mqtt_subscribe();
Serial.print("MQTT connected\n");
2023-09-03 09:28:01 +00:00
lcd_send_stop_bit();
2023-08-26 08:22:59 +00:00
publish_pwm_states();
publish_input_states();
2023-11-07 13:43:20 +00:00
#ifdef ENABLE_CLIMATE_MODULE
2023-08-28 17:49:11 +00:00
publish_ac_state();
2023-11-07 13:43:20 +00:00
#endif
2023-10-02 19:34:56 +00:00
mqtt_connected_user_callback();
2023-09-03 09:28:01 +00:00
standalone = false;
2023-11-07 12:29:48 +00:00
ESPMega_updateTimeFromNTP();
2023-08-25 18:38:17 +00:00
}
else
{
2023-09-03 09:28:01 +00:00
standalone = true;
2023-08-25 18:38:17 +00:00
Serial.print("MQTT not connected, continuing in standalone mode\n");
2023-09-03 09:28:01 +00:00
lcd_send_stop_bit();
2023-08-25 18:38:17 +00:00
}
2023-09-03 09:28:01 +00:00
lcd_send_stop_bit();
2023-11-07 13:43:20 +00:00
#ifdef ENABLE_INTERNAL_LCD
2023-09-03 09:28:01 +00:00
lcd_refresh();
lcd_top_bar_update();
2023-11-07 13:43:20 +00:00
#endif
2023-08-25 18:38:17 +00:00
}
}
2023-11-15 09:55:43 +00:00
/**
* @brief Subscribes to MQTT topics for controlling PWM, climate module and requesting device state.
*
2023-11-15 09:55:43 +00:00
*/
2023-08-25 18:38:17 +00:00
void mqtt_subscribe()
{
2023-08-26 08:22:59 +00:00
for (int i = 0; i < PWM_COUNT; i++)
{
2023-09-11 18:11:49 +00:00
PWM_SET_VALUE_TOPIC[base_topic_length + 4] = ((i - i % 10) / 10) + '0';
PWM_SET_VALUE_TOPIC[base_topic_length + 5] = (i % 10) + '0';
PWM_SET_STATE_TOPIC[base_topic_length + 4] = ((i - i % 10) / 10) + '0';
PWM_SET_STATE_TOPIC[base_topic_length + 5] = (i % 10) + '0';
mqtt.subscribe(PWM_SET_STATE_TOPIC);
mqtt.subscribe(PWM_SET_VALUE_TOPIC);
2023-08-26 08:22:59 +00:00
}
2023-11-07 13:43:20 +00:00
#ifdef ENABLE_CLIMATE_MODULE
mqtt.subscribe(AC_SET_FAN_TOPIC);
mqtt.subscribe(AC_SET_TEMPERATURE_TOPIC);
mqtt.subscribe(AC_SET_MODE_TOPIC);
2023-11-07 13:43:20 +00:00
#endif
mqtt.subscribe(STATE_REQUEST_TOPIC);
}
2023-11-15 09:55:43 +00:00
/**
* @brief Callback function for MQTT messages received by the device.
*
2023-11-15 09:55:43 +00:00
* @param topic The topic of the received message.
* @param payload The payload of the received message.
* @param length The length of the payload.
*/
2023-11-12 14:25:08 +00:00
void mqtt_callback(char *topic, byte *payload, unsigned int length)
{
uint8_t topic_length = strlen(topic);
char topic_trim[50];
2023-11-12 14:25:08 +00:00
char payload_nt[length + 1];
memcpy(payload_nt, payload, length);
payload_nt[length] = NULL;
strncpy(topic_trim, topic + base_topic_length - 1, topic_length);
if ((!strncmp(topic_trim, "/pwm/", 5)) && !strncmp(topic_trim + 7, "/set/state", 10))
{
pwm_state_callback(topic_trim, topic_length, payload_nt, length);
}
else if ((!strncmp(topic_trim, "/pwm/", 5)) && !strncmp(topic_trim + 7, "/set/value", 10))
{
pwm_value_callback(topic_trim, topic_length, payload_nt, length);
}
else if (!strcmp(topic, STATE_REQUEST_TOPIC))
{
2023-11-12 14:25:08 +00:00
state_request_callback();
}
2023-11-12 14:25:08 +00:00
else
{
#ifdef ENABLE_CLIMATE_MODULE
2023-11-12 14:25:08 +00:00
ac_state_callback(topic, topic_length, payload_nt, length);
#endif
}
2023-11-12 14:51:19 +00:00
user_mqtt_callback(topic, topic_length, payload_nt, length);
2023-08-25 18:38:17 +00:00
}
2023-11-15 09:55:43 +00:00
/**
* @brief Initializes the threads for various tasks such as MQTT connection, environment reporting, EEPROM PWM update, and user timer tick.
*
2023-11-15 09:55:43 +00:00
*/
2023-08-25 18:38:17 +00:00
void thread_initialization()
{
2023-08-26 05:53:25 +00:00
Serial.println("Initializing Threads . . .");
Serial.println("Initializing MQTT Thread . . .");
2023-08-25 18:38:17 +00:00
mqtt_reconnector.onRun(mqtt_connect);
2023-09-03 09:28:01 +00:00
mqtt_reconnector.setInterval(15000);
2023-11-07 13:43:20 +00:00
#ifdef ENABLE_CLIMATE_MODULE
2023-08-28 17:49:11 +00:00
environment_reporter.onRun(publish_env_state);
environment_reporter.setInterval(5000);
2023-11-07 13:43:20 +00:00
#endif
2023-09-11 09:13:09 +00:00
eeprom_pwm_updater.onRun(eeprom_pwm_update);
2023-09-28 07:55:02 +00:00
eeprom_pwm_updater.setInterval(1000);
2023-09-29 17:06:49 +00:00
user_timer_tick.onRun(timer_tick_callback);
2023-09-29 17:37:31 +00:00
user_timer_tick.setInterval(15000);
2023-08-25 18:38:17 +00:00
}
2023-11-15 09:55:43 +00:00
/**
* @brief Callback function for handling PWM state changes.
*
2023-11-15 09:55:43 +00:00
* @param topic The MQTT topic on which the message was received.
* @param topic_length The length of the MQTT topic.
* @param payload The message payload.
* @param payload_length The length of the message payload.
*/
2023-11-12 14:25:08 +00:00
void pwm_state_callback(char *topic, uint8_t topic_length, char *payload, unsigned int payload_length)
2023-08-25 18:38:17 +00:00
{
int a = topic[5] - '0';
int b = topic[6] - '0';
2023-08-26 08:22:59 +00:00
int id = 10 * a + b;
2023-11-12 14:25:08 +00:00
if (!strcmp(payload, "on"))
2023-08-26 08:22:59 +00:00
{
pwm_set_state(id, true);
}
2023-11-12 14:25:08 +00:00
else if (!strcmp(payload, "off"))
2023-08-26 08:22:59 +00:00
{
pwm_set_state(id, false);
}
}
2023-11-15 09:55:43 +00:00
/**
* @brief Callback function for PWM value updates.
*
2023-11-15 09:55:43 +00:00
* This function is called whenever a message is received on the PWM value update topic.
* It extracts the PWM ID and value from the topic and payload, respectively, and sets the PWM value.
*
2023-11-15 09:55:43 +00:00
* @param topic The topic on which the message was received.
* @param topic_length The length of the topic.
* @param payload The message payload.
* @param payload_length The length of the message payload.
*/
2023-11-12 14:25:08 +00:00
void pwm_value_callback(char *topic, uint8_t topic_length, char *payload, unsigned int payload_length)
2023-08-26 08:22:59 +00:00
{
int a = topic[5] - '0';
int b = topic[6] - '0';
2023-08-26 08:22:59 +00:00
int id = 10 * a + b;
int value = atoi(payload);
2023-08-26 08:22:59 +00:00
pwm_set_value(id, value);
2023-08-25 18:38:17 +00:00
}
2023-11-15 09:55:43 +00:00
/**
* @brief Callback function for virtual interrupts.
*
2023-11-15 09:55:43 +00:00
* This function publishes the input state of the virtual interrupt pin and calls the user-defined callback function.
* If ENABLE_INTERNAL_LCD is defined, it also updates the LCD display with the input state.
*
2023-11-15 09:55:43 +00:00
* @param pin The virtual interrupt pin number.
* @param state The state of the virtual interrupt pin (HIGH or LOW).
*/
2023-08-25 18:38:17 +00:00
void virtual_interrupt_callback(int pin, int state)
{
2023-09-03 09:28:01 +00:00
2023-08-28 17:49:11 +00:00
publish_input_state(pin, state);
2023-11-07 13:43:20 +00:00
#ifdef ENABLE_INTERNAL_LCD
2023-09-03 09:28:01 +00:00
if (lcd_current_page == 2)
panel.writeNum("I" + String(pin) + ".val", state);
2023-11-07 13:43:20 +00:00
#endif
2023-09-29 16:18:21 +00:00
virtual_interrupt_user_callback(pin, state);
2023-08-25 18:38:17 +00:00
}
void virtual_interrupt_loop()
{
for (int i = 0; i < 16; i++)
{
int current_pin_value = ESPMega_digitalRead(virtual_interrupt_pins[i]);
if (virtual_interupt_state[i] != current_pin_value)
{
if (millis() - virtual_interupt_timer[i] > DEBOUNCE_TIME_MS)
{
virtual_interupt_state[i] = current_pin_value;
virtual_interrupt_callback(i, current_pin_value);
}
}
else
{
virtual_interupt_timer[i] = millis();
}
yield();
}
}
2023-11-15 09:55:43 +00:00
/**
* @brief Publishes the current state of all PWM channels.
*
2023-11-15 09:55:43 +00:00
*/
2023-08-25 18:38:17 +00:00
void publish_pwm_states()
{
for (int i = 0; i < PWM_COUNT; i++)
{
publish_pwm_state(i);
}
}
2023-11-15 09:55:43 +00:00
/**
* @brief Publishes the state and value of a PWM signal to MQTT broker.
*
2023-11-15 09:55:43 +00:00
* @param id The ID of the PWM signal.
*/
2023-08-25 18:38:17 +00:00
void publish_pwm_state(int id)
{
2023-08-26 08:22:59 +00:00
int state = pwm_states[id];
int value = pwm_values[id];
2023-09-11 18:11:49 +00:00
PWM_STATE_TOPIC[base_topic_length + 4] = ((id - id % 10) / 10) + '0';
PWM_STATE_TOPIC[base_topic_length + 5] = (id % 10) + '0';
PWM_VALUE_TOPIC[base_topic_length + 4] = ((id - id % 10) / 10) + '0';
PWM_VALUE_TOPIC[base_topic_length + 5] = (id % 10) + '0';
2023-08-25 18:38:17 +00:00
if (state == 1)
{
mqtt.publish(PWM_STATE_TOPIC, "on");
2023-08-25 18:38:17 +00:00
}
else if (state == 0)
{
mqtt.publish(PWM_STATE_TOPIC, "off");
2023-08-25 18:38:17 +00:00
}
char temp[6];
2023-11-12 14:25:08 +00:00
itoa(value, temp, DEC);
mqtt.publish(PWM_VALUE_TOPIC, temp);
2023-08-25 18:38:17 +00:00
}
2023-11-15 09:55:43 +00:00
/**
* @brief Sets the state of a PWM pin and publishes the new state.
*
2023-11-15 09:55:43 +00:00
* @param id The ID of the PWM pin.
* @param state The new state of the PWM pin.
*/
2023-08-25 18:38:17 +00:00
void pwm_set_state(int id, int state)
{
2023-08-26 08:22:59 +00:00
if (state != pwm_states[id])
2023-08-25 18:38:17 +00:00
{
2023-08-26 08:22:59 +00:00
pwm_states[id] = state;
int pwm_value = pwm_values[id];
ESPMega_analogWrite(pwm_pins[id], state * (int)(pwm_linear_scaling_m[id] * pwm_value + pwm_linear_scaling_c[id]));
2023-11-07 13:43:20 +00:00
#ifdef ENABLE_INTERNAL_LCD
2023-09-03 09:28:01 +00:00
if (lcd_current_page == 3)
2023-10-03 19:36:27 +00:00
panel.writeNum("j" + String(id) + ".ppic", pwm_states[id] ? 33 : 48);
2023-09-03 10:41:31 +00:00
else if (lcd_current_page == 5 && id == lcd_pwmAdj_id)
panel.writeStr("pwm_state.txt", pwm_states[lcd_pwmAdj_id] ? "ON" : "OFF");
2023-11-07 13:43:20 +00:00
#endif
2023-08-25 18:38:17 +00:00
publish_pwm_state(id);
2023-09-30 08:42:44 +00:00
pwm_changed_user_callback(id);
2023-08-25 18:38:17 +00:00
}
}
2023-11-15 09:55:43 +00:00
/**
* @brief Sets the PWM value for a given id.
*
2023-11-15 09:55:43 +00:00
* @param id The id of the PWM pin.
* @param value The value to set the PWM pin to.
*/
2023-08-25 18:38:17 +00:00
void pwm_set_value(int id, int value)
{
2023-08-26 08:22:59 +00:00
pwm_values[id] = value;
int pwm_state = pwm_states[id];
ESPMega_analogWrite(pwm_pins[id], pwm_state * (int)(pwm_linear_scaling_m[id] * value + pwm_linear_scaling_c[id]));
2023-11-07 13:43:20 +00:00
#ifdef ENABLE_INTERNAL_LCD
2023-09-03 09:28:01 +00:00
if (lcd_current_page == 3)
panel.writeNum("j" + String(id) + ".val", int(value / 4095.0 * 100.0));
2023-09-03 10:41:31 +00:00
else if (lcd_current_page == 5 && id == lcd_pwmAdj_id)
panel.writeNum("pwm_value.val", pwm_values[lcd_pwmAdj_id]);
2023-11-07 13:43:20 +00:00
#endif
2023-08-25 18:38:17 +00:00
publish_pwm_state(id);
2023-09-30 08:42:44 +00:00
pwm_changed_user_callback(id);
2023-08-25 18:38:17 +00:00
}
2023-11-15 09:55:43 +00:00
/**
* @brief Toggles the state of a PWM pin.
*
2023-11-15 09:55:43 +00:00
* @param id The ID of the PWM pin to toggle.
*/
2023-08-25 18:38:17 +00:00
void pwm_toggle(int id)
{
2023-08-26 08:22:59 +00:00
int state = !pwm_states[id];
2023-08-25 18:38:17 +00:00
pwm_set_state(id, state);
}
2023-11-15 09:55:43 +00:00
/**
* Toggles the state of two PWM pins.
* @param id1 The first PWM pin ID.
* @param id2 The second PWM pin ID.
*/
2023-08-25 18:38:17 +00:00
void pwm_toggle(int id1, int id2)
{
boolean state = pwm_group_state(id1, id2);
if (state)
{
pwm_set_state(id1, 0);
pwm_set_state(id2, 0);
}
else
{
pwm_set_state(id1, 1);
pwm_set_state(id2, 1);
}
}
2023-11-15 09:55:43 +00:00
/**
* @brief Returns a boolean value indicating whether either of the two specified PWM states is true.
*
2023-11-15 09:55:43 +00:00
* @param id1 The first PWM state ID.
* @param id2 The second PWM state ID.
* @return true if either of the two specified PWM states is true, false otherwise.
*/
2023-08-25 18:38:17 +00:00
boolean pwm_group_state(int id1, int id2)
{
int state1 = pwm_states[id1 - 1], state2 = pwm_states[id2 - 1];
if (state1 || state2)
return true;
return false;
}
2023-11-15 09:55:43 +00:00
/**
* @brief Cycles the PWM value for the given id.
*
2023-11-15 09:55:43 +00:00
* @param id The id of the PWM pin.
*/
2023-08-25 18:38:17 +00:00
void pwm_cycle_value(int id)
{
2023-08-26 08:22:59 +00:00
int state = pwm_states[id];
int value = pwm_values[id];
2023-08-25 18:38:17 +00:00
if (state == 1)
for (int i = 0; i < PWM_CYCLE_VALUES_COUNT; i++)
{
if (PWM_CYCLE_VALUES[i] == value)
{
if (i > 0)
{
pwm_set_value(id, PWM_CYCLE_VALUES[i - 1]);
return;
}
else
{
pwm_set_state(id, 0);
return;
}
}
}
pwm_set_state(id, 1);
pwm_set_value(id, PWM_CYCLE_VALUES[PWM_CYCLE_VALUES_COUNT - 1]);
2023-08-26 08:22:59 +00:00
}
2023-11-15 09:55:43 +00:00
/**
* @brief Publishes the input states for all virtual input pins.
*
2023-11-15 09:55:43 +00:00
*/
2023-08-26 08:22:59 +00:00
void publish_input_states()
{
for (int i = 0; i < VINT_COUNT; i++)
{
publish_input_state(i);
}
}
2023-11-15 09:55:43 +00:00
/**
* @brief Publishes the input state of a specific pin.
*
2023-11-15 09:55:43 +00:00
* @param id The ID of the pin to publish the state of.
*/
2023-08-26 08:22:59 +00:00
void publish_input_state(int id)
{
int state = ESPMega_digitalRead(virtual_interrupt_pins[id]);
publish_input_state(id, state);
}
2023-11-15 09:55:43 +00:00
/**
* @brief Publishes the state of an input to the MQTT broker.
*
2023-11-15 09:55:43 +00:00
* @param id The ID of the input.
* @param state The state of the input (0 or 1).
*/
2023-08-26 08:22:59 +00:00
void publish_input_state(int id, int state)
{
2023-09-11 18:11:49 +00:00
INPUTS_TOPIC[base_topic_length + 6] = ((id - id % 10) / 10) + '0';
INPUTS_TOPIC[base_topic_length + 7] = (id % 10) + '0';
mqtt.publish(INPUTS_TOPIC, state ? "1" : "0");
2023-08-26 08:22:59 +00:00
}
2023-11-15 09:55:43 +00:00
/**
* @brief Callback function to request the current state of the device.
*
2023-11-15 09:55:43 +00:00
* This function publishes the input and PWM states of the device. If the climate module is enabled, it also publishes the AC and environment states.
* Finally, it calls the user-defined state request callback function.
*/
2023-11-12 14:25:08 +00:00
void state_request_callback()
2023-08-26 08:22:59 +00:00
{
publish_input_states();
publish_pwm_states();
2023-11-07 13:43:20 +00:00
#ifdef ENABLE_CLIMATE_MODULE
2023-08-28 17:49:11 +00:00
publish_ac_state();
2023-11-14 10:39:36 +00:00
publish_env_state();
2023-11-07 13:43:20 +00:00
#endif
2023-10-02 19:24:30 +00:00
user_state_request_callback();
2023-08-27 19:07:04 +00:00
}
2023-11-07 13:43:20 +00:00
#ifdef ENABLE_IR_MODULE
2023-08-28 17:49:11 +00:00
void ir_loop()
{
if (IrReceiver.decode())
{
IrReceiver.resume();
}
}
2023-11-07 13:43:20 +00:00
#endif
2023-08-28 17:49:11 +00:00
2023-11-07 13:43:20 +00:00
#ifdef ENABLE_CLIMATE_MODULE
2023-11-15 09:55:43 +00:00
/**
* @brief Publishes the current state of the air conditioner to MQTT topics.
*
2023-11-15 09:55:43 +00:00
*/
2023-08-28 17:49:11 +00:00
void publish_ac_state()
{
switch (ac_mode)
{
case 0:
mqtt.publish(AC_MODE_TOPIC, "off");
2023-08-28 17:49:11 +00:00
break;
case 1:
mqtt.publish(AC_MODE_TOPIC, "cool");
2023-08-28 17:49:11 +00:00
break;
case 2:
mqtt.publish(AC_MODE_TOPIC, "fan_only");
2023-08-28 17:49:11 +00:00
default:
break;
}
char temp[5];
2023-11-12 14:25:08 +00:00
itoa(ac_temperature, temp, DEC);
mqtt.publish(AC_TEMPERATURE_TOPIC, temp);
2023-08-28 17:49:11 +00:00
switch (ac_fan_speed)
{
case 0:
mqtt.publish(AC_FAN_TOPIC, "auto");
2023-08-28 17:49:11 +00:00
break;
case 1:
mqtt.publish(AC_FAN_TOPIC, "high");
2023-08-28 17:49:11 +00:00
break;
case 2:
mqtt.publish(AC_FAN_TOPIC, "medium");
2023-08-28 17:49:11 +00:00
break;
case 3:
mqtt.publish(AC_FAN_TOPIC, "low");
2023-08-28 17:49:11 +00:00
break;
}
}
2023-11-15 09:55:43 +00:00
/**
* @brief Callback function for handling AC state changes.
*
2023-11-15 09:55:43 +00:00
* This function is called whenever a message is received on the AC_SET_TEMPERATURE_TOPIC, AC_SET_MODE_TOPIC, or AC_SET_FAN_TOPIC.
* It parses the payload of the message and updates the AC state accordingly.
*
2023-11-15 09:55:43 +00:00
* @param topic The topic of the message.
* @param topic_length The length of the topic.
* @param payload The payload of the message.
* @param payload_length The length of the payload.
*/
2023-11-12 14:25:08 +00:00
void ac_state_callback(char *topic, uint8_t topic_length, char *payload, unsigned int payload_length)
2023-08-28 17:49:11 +00:00
{
2023-11-12 14:25:08 +00:00
if (!strcmp(topic, AC_SET_TEMPERATURE_TOPIC))
2023-08-28 17:49:11 +00:00
{
2023-11-12 14:25:08 +00:00
int new_temp = atoi(payload);
2023-08-28 17:49:11 +00:00
if (new_temp >= AC_MIN_TEMPERATURE && new_temp <= AC_MAX_TEMPERATURE)
{
ac_set_state(ac_mode, new_temp, ac_fan_speed);
}
}
2023-11-12 14:25:08 +00:00
else if (!strcmp(topic, AC_SET_MODE_TOPIC))
2023-08-28 17:49:11 +00:00
{
2023-11-12 14:25:08 +00:00
if (!strcmp(payload, "off"))
2023-08-28 17:49:11 +00:00
{
ac_set_state(0, ac_temperature, ac_fan_speed);
2023-08-27 19:07:04 +00:00
}
2023-11-12 14:25:08 +00:00
else if (!strcmp(payload, "cool"))
2023-08-28 17:49:11 +00:00
{
ac_set_state(1, ac_temperature, ac_fan_speed);
}
2023-11-12 14:25:08 +00:00
else if (!strcmp(payload, "fan_only"))
2023-08-28 17:49:11 +00:00
{
ac_set_state(2, ac_temperature, ac_fan_speed);
}
}
2023-11-12 14:25:08 +00:00
else if (!strcmp(topic, AC_SET_FAN_TOPIC))
2023-08-28 17:49:11 +00:00
{
2023-11-12 14:25:08 +00:00
if (!strcmp(payload, "auto"))
2023-08-28 17:49:11 +00:00
{
ac_set_state(ac_mode, ac_temperature, 0);
}
2023-11-12 15:44:50 +00:00
else if (!strcmp(payload, "high"))
2023-08-28 17:49:11 +00:00
{
ac_set_state(ac_mode, ac_temperature, 1);
}
else if (!strcmp(payload, "medium"))
2023-08-28 17:49:11 +00:00
{
ac_set_state(ac_mode, ac_temperature, 2);
}
2023-11-12 15:44:50 +00:00
else if (!strcmp(payload, "low"))
2023-08-28 17:49:11 +00:00
{
ac_set_state(ac_mode, ac_temperature, 3);
}
}
}
2023-11-15 09:55:43 +00:00
/**
* @brief Sets the state of the air conditioner.
*
2023-11-15 09:55:43 +00:00
* @param mode The mode of the air conditioner (0 = off, 1 = cool, 2 = fan).
* @param temperature The temperature to set the air conditioner to (in Celsius).
* @param fan_speed The fan speed of the air conditioner (0 = low, 1 = medium, 2 = high).
*/
2023-08-28 17:49:11 +00:00
void ac_set_state(int mode, int temperature, int fan_speed)
{
ac_mode = mode;
ac_temperature = temperature;
2023-10-02 11:47:51 +00:00
if (ac_temperature < AC_MIN_TEMPERATURE)
ac_temperature = AC_MIN_TEMPERATURE;
else if (ac_temperature > AC_MAX_TEMPERATURE)
ac_temperature = AC_MAX_TEMPERATURE;
2023-08-28 17:49:11 +00:00
ac_fan_speed = fan_speed;
temperature -= AC_MIN_TEMPERATURE;
2023-11-07 13:43:20 +00:00
#ifdef ENABLE_INTERNAL_LCD
2023-09-03 15:55:07 +00:00
if (lcd_current_page == 4)
{
lcd_ac_refresh_fan();
lcd_ac_refresh_mode();
if (ac_mode != 2)
panel.writeStr("temp.txt", String(ac_temperature) + "C");
else
panel.writeStr("temp.txt", "--C");
}
2023-11-07 13:43:20 +00:00
#endif
2023-08-28 17:49:11 +00:00
publish_ac_state();
2023-09-11 09:13:09 +00:00
uint8_t ac_datablock[3] = {ac_mode, ac_temperature, ac_fan_speed};
2023-09-28 07:55:02 +00:00
ESPMega_FRAM.write(0, ac_datablock, 3);
2023-08-28 17:49:11 +00:00
switch (mode)
{
case 0:
IrSender.sendRaw(ir_code_off, sizeof(ir_code_off) / sizeof(ir_code_off[0]), NEC_KHZ);
break;
case 1:
2023-11-08 05:49:45 +00:00
IrSender.sendRaw(ir_code_cool[fan_speed][temperature], sizeof(ir_code_cool[fan_speed][temperature]) / sizeof(ir_code_cool[fan_speed][temperature][0]), NEC_KHZ);
2023-08-28 17:49:11 +00:00
break;
case 2:
IrSender.sendRaw(ir_code_fan[fan_speed], sizeof(ir_code_fan[fan_speed]) / sizeof(ir_code_fan[fan_speed][0]), NEC_KHZ);
break;
}
2023-10-02 16:20:06 +00:00
ac_changed_user_callback(mode, ac_temperature, fan_speed);
2023-08-28 17:49:11 +00:00
}
2023-11-15 09:55:43 +00:00
/**
* @brief Reads the environment sensor and publishes the temperature and humidity to MQTT topics.
*
2023-11-15 09:55:43 +00:00
* @details Reads the environment sensor and publishes the temperature and humidity to MQTT topics.
* If the internal LCD is enabled, it also updates the temperature and humidity values on the LCD screen.
*
2023-11-15 09:55:43 +00:00
* @return void
*/
2023-08-28 17:49:11 +00:00
void publish_env_state()
{
int errorCode = env_sensor.read();
yield();
switch (errorCode)
{
case DHTLIB_OK:
2023-10-03 20:14:33 +00:00
current_room_humid = env_sensor.getHumidity();
current_room_temp = env_sensor.getTemperature();
char temp[5];
dtostrf(current_room_temp, 0, 2, temp);
mqtt.publish(AC_ROOM_TEMPERATURE_TOPIC, temp);
dtostrf(current_room_humid, 0, 2, temp);
mqtt.publish(AC_HUMIDITY_TOPIC, temp);
mqtt.loop();
2023-11-07 13:43:20 +00:00
#ifdef ENABLE_INTERNAL_LCD
2023-10-03 20:14:33 +00:00
if (lcd_current_page == 4)
{
2023-10-04 06:47:24 +00:00
Serial.printf("roomtemp.txt=\"%.01fC\"", current_room_temp);
2023-10-03 20:06:44 +00:00
lcd_send_stop_bit();
2023-10-04 06:47:24 +00:00
Serial.printf("roomhumid.txt=\"%d%%\"", (int)current_room_humid);
2023-10-03 20:06:44 +00:00
lcd_send_stop_bit();
}
2023-11-07 13:43:20 +00:00
#endif
2023-08-28 17:49:11 +00:00
break;
default:
mqtt.publish(AC_ROOM_TEMPERATURE_TOPIC, "ERROR");
mqtt.loop();
mqtt.publish(AC_HUMIDITY_TOPIC, "ERROR");
mqtt.loop();
2023-08-28 17:49:11 +00:00
}
2023-09-03 09:28:01 +00:00
}
2023-11-07 13:43:20 +00:00
#endif
2023-09-03 09:28:01 +00:00
2023-11-07 13:43:20 +00:00
#ifdef ENABLE_INTERNAL_LCD
2023-11-15 09:55:43 +00:00
/**
* @brief Initializes the LCD display and sets up the refresh intervals for the top bar and page.
*
2023-11-15 09:55:43 +00:00
*/
2023-09-03 09:28:01 +00:00
void lcd_begin()
{
top_bar_updater.onRun(lcd_top_bar_update);
top_bar_updater.setInterval(10000);
page_updater.onRun(lcd_refresh_pd);
page_updater.setInterval(5000);
lcd_refresh();
}
2023-11-15 09:55:43 +00:00
/**
* @brief Runs the LCD loop, updating the display and listening for input.
*
2023-11-15 09:55:43 +00:00
*/
2023-09-03 09:28:01 +00:00
void lcd_loop()
{
lcd_thread_controller.run();
panel.NextionListen();
if (panel.currentPageId != lcd_current_page)
{
lcd_current_page = panel.currentPageId;
lcd_refresh();
lcd_top_bar_update();
}
}
2023-11-15 09:55:43 +00:00
/**
* @brief Refreshes the LCD display if the current page is 1.
*
2023-11-15 09:55:43 +00:00
*/
2023-09-03 09:28:01 +00:00
void lcd_refresh_pd()
{
if (lcd_current_page == 1)
{
lcd_refresh();
}
}
2023-11-15 09:55:43 +00:00
/**
* @brief Refreshes the LCD display based on the current page.
*
2023-11-15 09:55:43 +00:00
* @details The function writes the appropriate values to the LCD display based on the current page.
*
2023-11-15 09:55:43 +00:00
* @note This function assumes that the LCD panel has already been initialized.
*
2023-11-15 09:55:43 +00:00
*/
2023-09-03 09:28:01 +00:00
void lcd_refresh()
{
switch (lcd_current_page)
{
case 1:
panel.writeStr("hostname.txt", HOSTNAME);
panel.writeStr("server_address.txt", MQTT_SERVER.toString());
panel.writeStr("ip_address.txt", IP.toString());
panel.writeStr("status_txt.txt", standalone ? "Standalone" : "BMS Managed");
break;
case 2:
for (int i = 0; i <= 15; i++)
{
panel.writeNum("I" + String(i) + ".val", virtual_interupt_state[i]);
}
break;
case 3:
for (int i = 0; i <= 15; i++)
{
panel.writeNum("j" + String(i) + ".val", int(pwm_values[i] / 4095.0 * 100.0));
2023-10-03 19:28:09 +00:00
panel.writeNum("j" + String(i) + ".ppic", pwm_states[i] ? 33 : 48);
2023-09-03 09:28:01 +00:00
}
break;
2023-09-03 15:55:07 +00:00
case 4:
2023-11-07 13:43:20 +00:00
#ifdef ENABLE_CLIMATE_MODULE
2023-10-04 06:47:24 +00:00
Serial.printf("roomtemp.txt=\"%.01fC\"", current_room_temp);
lcd_send_stop_bit();
Serial.printf("roomhumid.txt=\"%d%%\"", (int)current_room_humid);
lcd_send_stop_bit();
2023-09-03 15:55:07 +00:00
if (ac_mode != 2)
panel.writeStr("temp.txt", String(ac_temperature) + "C");
else
panel.writeStr("temp.txt", "--C");
lcd_ac_refresh_fan();
lcd_ac_refresh_mode();
2023-11-07 13:43:20 +00:00
#else
lcd_send_command("page climate_nomod");
#endif
2023-09-03 10:41:31 +00:00
case 5:
panel.writeStr("pwm_id.txt", String("P") + String(lcd_pwmAdj_id));
panel.writeStr("pwm_state.txt", pwm_states[lcd_pwmAdj_id] ? "ON" : "OFF");
panel.writeNum("pwm_value.val", pwm_values[lcd_pwmAdj_id]);
break;
2023-09-11 17:37:33 +00:00
case 6:
panel.writeStr("ip_set.txt", IP.toString());
panel.writeStr("netmask_set.txt", SUBNET.toString());
panel.writeStr("gateway_set.txt", GATEWAY.toString());
panel.writeStr("dns_set.txt", DNS.toString());
panel.writeStr("host_set.txt", HOSTNAME);
2023-11-07 08:49:11 +00:00
break;
case 11:
panel.writeStr("mqttsv_set.txt", MQTT_SERVER.toString());
2023-09-11 17:37:33 +00:00
panel.writeNum("port_set.val", MQTT_PORT);
2023-09-11 18:11:49 +00:00
panel.writeStr("topic_set.txt", MQTT_BASE_TOPIC);
2023-11-07 08:49:11 +00:00
panel.writeStr("user_set.txt", MQTT_USERNAME);
panel.writeStr("password_set.txt", MQTT_PASSWORD);
panel.writeNum("use_auth.val", MQTT_USE_AUTH);
2023-09-11 17:37:33 +00:00
break;
2023-09-03 09:28:01 +00:00
default:
break;
}
}
2023-11-15 09:55:43 +00:00
/**
* @brief Updates the top bar of the LCD display with the current time, server status, and LAN status.
*
2023-11-15 09:55:43 +00:00
* @details This function gets the current time using ESPMega_getTime() function and formats it to display on the LCD.
* It also updates the server and LAN status icons based on the standalone and ETH.linkUp() values respectively.
*
2023-11-15 09:55:43 +00:00
* @return void
*/
2023-09-03 09:28:01 +00:00
void lcd_top_bar_update()
{
2023-09-16 11:02:44 +00:00
char time_buffer[15];
2023-09-16 17:06:00 +00:00
rtctime_t time = ESPMega_getTime();
2023-09-30 14:09:45 +00:00
sprintf(time_buffer, "%02d:%02d", time.hours, time.minutes);
2023-09-16 17:06:00 +00:00
panel.writeStr("time.txt", time_buffer);
2023-09-03 09:28:01 +00:00
panel.writeNum("server.pic", standalone ? 4 : 5);
panel.writeNum("lan.pic", ETH.linkUp() ? 3 : 2);
2023-09-03 10:41:31 +00:00
}
2023-11-15 09:55:43 +00:00
2023-11-07 13:43:20 +00:00
#ifdef ENABLE_CLIMATE_MODULE
2023-11-15 09:55:43 +00:00
/**
* @brief Refreshes the LCD display with the current AC mode.
*
2023-11-15 09:55:43 +00:00
* This function updates the LCD display with the current AC mode, which can be one of the following:
* - 0: Off
* - 1: Cool
* - 2: Fan
*
2023-11-15 09:55:43 +00:00
* The function uses the panel object to write the appropriate image to the display for each mode.
*/
2023-09-03 15:55:07 +00:00
void lcd_ac_refresh_mode()
{
// auto high mid low
panel.writeNum("mode_cool.pic", ac_mode == 1 ? 12 : 13);
panel.writeNum("mode_fan.pic", ac_mode == 2 ? 22 : 23);
2023-09-28 07:55:02 +00:00
panel.writeNum("mode_off.pic", ac_mode == 0 ? 24 : 25);
2023-09-03 15:55:07 +00:00
}
2023-11-15 09:55:43 +00:00
/**
* @brief Refreshes the fan speed icons on the LCD panel based on the current AC fan speed.
*
2023-11-15 09:55:43 +00:00
* This function updates the fan speed icons on the LCD panel based on the current AC fan speed.
* It uses the panel.writeNum() function to write the appropriate fan speed icon to the panel.
*
2023-11-15 09:55:43 +00:00
*/
2023-09-03 15:55:07 +00:00
void lcd_ac_refresh_fan()
{
panel.writeNum("fan_auto.pic", ac_fan_speed == 0 ? 14 : 15);
panel.writeNum("fan_low.pic", ac_fan_speed == 3 ? 18 : 19);
panel.writeNum("fan_mid.pic", ac_fan_speed == 2 ? 20 : 21);
panel.writeNum("fan_high.pic", ac_fan_speed == 1 ? 16 : 17);
}
2023-11-07 13:43:20 +00:00
#endif
2023-09-03 15:55:07 +00:00
2023-09-03 10:41:31 +00:00
void trigger0()
{
if (lcd_pwmAdj_id >= 15)
lcd_pwmAdj_id = 0;
else
lcd_pwmAdj_id++;
lcd_refresh();
}
void trigger1()
{
if (lcd_pwmAdj_id <= 0)
lcd_pwmAdj_id = 15;
else
lcd_pwmAdj_id--;
lcd_refresh();
}
void trigger2()
{
pwm_toggle(lcd_pwmAdj_id);
}
void trigger3()
{
int value = panel.readNumber("pwm_value.val");
lcd_send_stop_bit();
2023-09-03 15:55:07 +00:00
pwm_set_value(lcd_pwmAdj_id, value);
}
void trigger4()
{
2023-11-07 13:43:20 +00:00
#ifdef ENABLE_CLIMATE_MODULE
2023-09-03 15:55:07 +00:00
if (ac_temperature < AC_MAX_TEMPERATURE && ac_mode != 2)
ac_set_state(ac_mode, ac_temperature + 1, ac_fan_speed);
2023-11-07 13:43:20 +00:00
#endif
2023-09-03 15:55:07 +00:00
}
void trigger5()
{
2023-11-07 13:43:20 +00:00
#ifdef ENABLE_CLIMATE_MODULE
2023-09-03 15:55:07 +00:00
if (ac_temperature > AC_MIN_TEMPERATURE && ac_mode != 2)
ac_set_state(ac_mode, ac_temperature - 1, ac_fan_speed);
2023-11-07 13:43:20 +00:00
#endif
2023-09-03 15:55:07 +00:00
}
void trigger6()
{
2023-11-07 13:43:20 +00:00
#ifdef ENABLE_CLIMATE_MODULE
2023-09-03 15:55:07 +00:00
ac_set_state(ac_mode, ac_temperature, 0);
2023-11-07 13:43:20 +00:00
#endif
2023-09-03 15:55:07 +00:00
}
void trigger7()
{
2023-11-07 13:43:20 +00:00
#ifdef ENABLE_CLIMATE_MODULE
2023-09-03 15:55:07 +00:00
ac_set_state(ac_mode, ac_temperature, 3);
2023-11-07 13:43:20 +00:00
#endif
2023-09-03 15:55:07 +00:00
}
void trigger8()
{
2023-11-07 13:43:20 +00:00
#ifdef ENABLE_CLIMATE_MODULE
2023-09-03 15:55:07 +00:00
ac_set_state(ac_mode, ac_temperature, 2);
2023-11-07 13:43:20 +00:00
#endif
2023-09-03 15:55:07 +00:00
}
void trigger9()
{
2023-11-07 13:43:20 +00:00
#ifdef ENABLE_CLIMATE_MODULE
2023-09-03 15:55:07 +00:00
ac_set_state(ac_mode, ac_temperature, 1);
2023-11-07 13:43:20 +00:00
#endif
2023-09-03 15:55:07 +00:00
}
void trigger10()
{
2023-11-07 13:43:20 +00:00
#ifdef ENABLE_CLIMATE_MODULE
2023-09-03 15:55:07 +00:00
ac_set_state(1, ac_temperature, ac_fan_speed);
2023-11-07 13:43:20 +00:00
#endif
2023-09-03 15:55:07 +00:00
}
void trigger11()
{
2023-11-07 13:43:20 +00:00
#ifdef ENABLE_CLIMATE_MODULE
2023-09-03 15:55:07 +00:00
ac_set_state(2, ac_temperature, ac_fan_speed);
2023-11-07 13:43:20 +00:00
#endif
2023-09-03 15:55:07 +00:00
}
void trigger12()
{
2023-11-07 13:43:20 +00:00
#ifdef ENABLE_CLIMATE_MODULE
2023-09-03 15:55:07 +00:00
ac_set_state(0, ac_temperature, ac_fan_speed);
2023-11-07 13:43:20 +00:00
#endif
2023-09-03 15:55:07 +00:00
}
void trigger13()
{
2023-09-11 17:37:33 +00:00
set_ip(panel.readStr("ip_set.txt"));
set_netmask(panel.readStr("netmask_set.txt"));
set_gw(panel.readStr("gateway_set.txt"));
set_dns(panel.readStr("dns_set.txt"));
set_hostname(panel.readStr("host_set.txt"));
delay(100);
ESP.restart();
2023-09-11 09:13:09 +00:00
}
2023-11-07 08:49:11 +00:00
void trigger14()
{
2023-11-06 16:24:06 +00:00
Serial.print("page dashboard");
lcd_send_stop_bit();
}
2023-11-07 08:49:11 +00:00
void trigger15()
{
set_mqtt_server(panel.readStr("mqttsv_set.txt"));
set_basetopic(panel.readStr("topic_set.txt"));
uint16_t port = panel.readNumber("port_set.val");
mqtt_port_set(port);
set_mqtt_useauth(panel.readNumber("use_auth.val"));
set_mqtt_username(panel.readStr("user_set.txt"));
set_mqtt_password(panel.readStr("password_set.txt"));
delay(100);
ESP.restart();
}
2023-11-07 13:43:20 +00:00
#endif // End Internal LCD Code Block
2023-11-07 08:49:11 +00:00
2023-11-15 09:55:43 +00:00
/**
* @brief Updates the PWM states and values in EEPROM if they have changed.
*
2023-11-15 09:55:43 +00:00
* If the current PWM states or values are different from the ones stored in EEPROM,
* this function updates the EEPROM with the current values.
*/
2023-09-11 09:13:09 +00:00
void eeprom_pwm_update()
{
if (memcmp(pwm_states, pwm_states_eeprom, 16))
{
memcpy(pwm_states_eeprom, pwm_states, 16);
2023-09-28 07:55:02 +00:00
ESPMega_FRAM.write(3, pwm_states_eeprom, 16);
2023-09-11 09:13:09 +00:00
}
if (memcmp(pwm_values, pwm_values_eeprom, 32))
{
memcpy(pwm_values_eeprom, pwm_values, 32);
2023-09-28 07:55:02 +00:00
ESPMega_FRAM.write(19, pwm_values_eeprom, 32);
2023-09-11 09:13:09 +00:00
}
2023-09-11 17:37:33 +00:00
}
2023-11-15 09:55:43 +00:00
/**
* @brief Sets the IP address and updates it in EEPROM.
*
2023-11-15 09:55:43 +00:00
* @param address The IP address to set.
*/
2023-09-11 17:37:33 +00:00
void set_ip(String address)
{
IP.fromString(address);
eeprom_ip_update(EEPROM_ADDRESS_IP, IP[0], IP[1], IP[2], IP[3]);
}
2023-11-15 09:55:43 +00:00
/**
* @brief Sets the subnet mask for the device.
*
2023-11-15 09:55:43 +00:00
* @param address The subnet mask address to set.
*/
2023-09-11 17:37:33 +00:00
void set_netmask(String address)
{
SUBNET.fromString(address);
eeprom_ip_update(EEPROM_ADDRESS_SUBNET, SUBNET[0], SUBNET[1], SUBNET[2], SUBNET[3]);
}
2023-11-15 09:55:43 +00:00
/**
* Sets the DNS server address.
* @param address The IP address of the DNS server.
*/
2023-09-11 17:37:33 +00:00
void set_dns(String address)
{
DNS.fromString(address);
eeprom_ip_update(EEPROM_ADDRESS_DNS, DNS[0], DNS[1], DNS[2], DNS[3]);
}
2023-11-15 09:55:43 +00:00
/**
* @brief Sets the gateway IP address and updates the EEPROM.
*
2023-11-15 09:55:43 +00:00
* @param address The gateway IP address as a String.
*/
2023-09-11 17:37:33 +00:00
void set_gw(String address)
{
GATEWAY.fromString(address);
eeprom_ip_update(EEPROM_ADDRESS_GATEWAY, GATEWAY[0], GATEWAY[1], GATEWAY[2], GATEWAY[3]);
}
2023-11-15 09:55:43 +00:00
/**
* @brief Sets the MQTT server address and updates the EEPROM.
*
2023-11-15 09:55:43 +00:00
* @param address The MQTT server address as a String.
*/
2023-09-11 17:37:33 +00:00
void set_mqtt_server(String address)
{
MQTT_SERVER.fromString(address);
eeprom_ip_update(EEPROM_ADDRESS_MQTT_SERVER, MQTT_SERVER[0], MQTT_SERVER[1], MQTT_SERVER[2], MQTT_SERVER[3]);
}
2023-11-15 09:55:43 +00:00
/**
* @brief Updates the IP address in EEPROM at the specified ROM address.
*
2023-11-15 09:55:43 +00:00
* @param rom_address The ROM address where the IP address is stored.
* @param byte1 The first byte of the IP address.
* @param byte2 The second byte of the IP address.
* @param byte3 The third byte of the IP address.
* @param byte4 The fourth byte of the IP address.
*/
2023-09-11 17:37:33 +00:00
void eeprom_ip_update(uint16_t rom_address, uint8_t byte1, uint8_t byte2, uint8_t byte3, uint8_t byte4)
{
uint8_t addressblock[4] = {byte1, byte2, byte3, byte4};
2023-09-28 07:55:02 +00:00
ESPMega_FRAM.write(rom_address, addressblock, 4);
2023-09-11 17:37:33 +00:00
}
2023-11-15 09:55:43 +00:00
/**
* @brief Retrieves an IP address from EEPROM memory.
*
2023-11-15 09:55:43 +00:00
* @param rom_address The address in EEPROM memory where the IP address is stored.
* @return The retrieved IP address.
*/
2023-09-11 17:37:33 +00:00
IPAddress eeprom_ip_retrieve(uint16_t rom_address)
{
uint8_t addressblock[4];
2023-09-28 07:55:02 +00:00
ESPMega_FRAM.read(rom_address, addressblock, 4);
2023-09-11 17:37:33 +00:00
return IPAddress(addressblock[0], addressblock[1], addressblock[2], addressblock[3]);
}
2023-11-15 09:55:43 +00:00
/**
* @brief Sets the hostname for the device and writes it to non-volatile memory.
*
2023-11-15 09:55:43 +00:00
* @param hostname The hostname to set.
*/
2023-09-11 17:37:33 +00:00
void set_hostname(String hostname)
{
hostname.toCharArray(HOSTNAME, 15);
2023-09-28 07:55:02 +00:00
ESPMega_FRAM.write(EEPROM_ADDRESS_HOSTNAME, (uint8_t *)HOSTNAME, 15);
2023-09-11 17:37:33 +00:00
}
2023-11-15 09:55:43 +00:00
/**
* @brief Retrieves the hostname from EEPROM and stores it in the HOSTNAME variable.
*
2023-11-15 09:55:43 +00:00
*/
2023-09-11 17:37:33 +00:00
void eeprom_hostname_retrieve()
{
2023-09-28 07:55:02 +00:00
ESPMega_FRAM.read(EEPROM_ADDRESS_HOSTNAME, (uint8_t *)HOSTNAME, 15);
2023-09-11 18:11:49 +00:00
}
2023-11-15 09:55:43 +00:00
/**
* @brief Sets the base topic for MQTT communication and writes it to EEPROM.
*
2023-11-15 09:55:43 +00:00
* @param topic The base topic to set.
*/
2023-09-11 18:11:49 +00:00
void set_basetopic(String topic)
{
topic.toCharArray(MQTT_BASE_TOPIC, 20);
2023-09-28 07:55:02 +00:00
ESPMega_FRAM.write(EEPROM_ADDRESS_TOPIC, (uint8_t *)MQTT_BASE_TOPIC, 20);
2023-09-11 18:11:49 +00:00
}
2023-11-15 09:55:43 +00:00
/**
* @brief Retrieves the MQTT base topic from EEPROM and stores it in MQTT_BASE_TOPIC array.
*
2023-11-15 09:55:43 +00:00
*/
2023-09-11 18:11:49 +00:00
void eeprom_basetopic_retrieve()
{
2023-09-28 07:55:02 +00:00
ESPMega_FRAM.read(EEPROM_ADDRESS_TOPIC, (uint8_t *)MQTT_BASE_TOPIC, 20);
2023-09-11 17:37:33 +00:00
}
2023-11-15 09:55:43 +00:00
/**
* @brief Sets the MQTT port in the EEPROM.
*
2023-11-15 09:55:43 +00:00
* @param port The MQTT port to be set.
*/
2023-09-11 17:37:33 +00:00
void mqtt_port_set(uint16_t port)
{
uint8_t port_arr[2];
memcpy(port_arr, &port, 2);
2023-09-28 07:55:02 +00:00
ESPMega_FRAM.write(EEPROM_ADDRESS_MQTT_PORT, port_arr, 2);
2023-09-11 17:37:33 +00:00
}
2023-11-15 09:55:43 +00:00
/**
* @brief Retrieves the MQTT port from EEPROM and stores it in the MQTT_PORT variable.
*
2023-11-15 09:55:43 +00:00
*/
2023-09-11 17:37:33 +00:00
void eeprom_mqtt_port_retrieve()
{
uint8_t port_arr[2];
2023-09-28 07:55:02 +00:00
ESPMega_FRAM.read(EEPROM_ADDRESS_MQTT_PORT, port_arr, 2);
2023-09-11 17:37:33 +00:00
memcpy(&MQTT_PORT, port_arr, 2);
2023-09-29 17:06:49 +00:00
}
2023-11-15 09:55:43 +00:00
/**
* @brief Returns the state of the PWM with the given ID.
*
2023-11-15 09:55:43 +00:00
* @param id The ID of the PWM.
* @return true if the PWM is on, false otherwise.
*/
2023-09-30 14:09:45 +00:00
boolean pwm_get_state(int id)
{
2023-09-29 17:06:49 +00:00
return pwm_states[id];
}
2023-11-15 09:55:43 +00:00
/**
* Returns the current PWM value for the specified ID.
*
* @param id The ID of the PWM channel to get the value for.
* @return The current PWM value for the specified ID.
*/
2023-09-30 14:09:45 +00:00
uint16_t pwm_get_value(int id)
{
2023-09-29 17:06:49 +00:00
return pwm_values[id];
}
2023-11-15 09:55:43 +00:00
/**
* @brief Returns the state of the virtual interrupt with the given ID.
*
2023-11-15 09:55:43 +00:00
* @param id The ID of the virtual interrupt.
* @return The state of the virtual interrupt.
*/
2023-09-30 14:09:45 +00:00
boolean input_get_state(int id)
{
2023-09-29 17:06:49 +00:00
return virtual_interupt_state[id];
}
2023-11-07 13:43:20 +00:00
#ifdef ENABLE_CLIMATE_MODULE
2023-11-15 09:55:43 +00:00
/**
* @brief Returns the current temperature value.
*
* @return The current temperature value.
*/
2023-09-30 14:09:45 +00:00
uint8_t ac_get_temperature()
{
2023-09-29 17:06:49 +00:00
return ac_temperature;
}
2023-11-15 09:55:43 +00:00
/**
* @brief Get the current mode of the AC system.
*
2023-11-15 09:55:43 +00:00
* @return uint8_t The current mode of the AC system.
*/
2023-09-30 14:09:45 +00:00
uint8_t ac_get_mode()
{
2023-09-29 17:06:49 +00:00
return ac_mode;
}
2023-11-15 09:55:43 +00:00
/**
* @brief Get the current fan speed.
*
2023-11-15 09:55:43 +00:00
* @return uint8_t The current fan speed.
*/
2023-09-30 14:09:45 +00:00
uint8_t ac_get_fan_speed()
{
2023-09-29 17:06:49 +00:00
return ac_fan_speed;
2023-11-07 05:19:35 +00:00
}
2023-11-07 13:43:20 +00:00
#endif
2023-11-07 05:19:35 +00:00
2023-11-15 09:55:43 +00:00
/**
* @brief Retrieves the MQTT username from EEPROM and stores it in the MQTT_USERNAME global variable.
*
2023-11-15 09:55:43 +00:00
*/
2023-11-07 08:49:11 +00:00
void eeprom_mqtt_username_retrieve()
{
ESPMega_FRAM.read(EEPROM_ADDRESS_MQTT_USERNAME, (uint8_t *)MQTT_USERNAME, 32);
2023-11-07 05:19:35 +00:00
}
2023-11-15 09:55:43 +00:00
/**
* @brief Retrieves the MQTT password from EEPROM and stores it in the MQTT_PASSWORD global variable.
*
2023-11-15 09:55:43 +00:00
*/
2023-11-07 08:49:11 +00:00
void eeprom_mqtt_password_retrieve()
{
2023-11-07 05:19:35 +00:00
ESPMega_FRAM.read(EEPROM_ADDRESS_MQTT_PASSWORD, (uint8_t *)MQTT_PASSWORD, 32);
}
2023-11-15 09:55:43 +00:00
/**
* @brief Sets the MQTT username and writes it to the EEPROM.
*
2023-11-15 09:55:43 +00:00
* @param username The MQTT username to set.
*/
2023-11-07 08:49:11 +00:00
void set_mqtt_username(String username)
{
username.toCharArray(MQTT_USERNAME, 32);
2023-11-07 05:19:35 +00:00
ESPMega_FRAM.write(EEPROM_ADDRESS_MQTT_USERNAME, (uint8_t *)MQTT_USERNAME, 20);
}
2023-11-15 09:55:43 +00:00
/**
* @brief Sets the MQTT password and writes it to the EEPROM.
*
2023-11-15 09:55:43 +00:00
* @param password The MQTT password to set.
*/
2023-11-07 08:49:11 +00:00
void set_mqtt_password(String password)
{
password.toCharArray(MQTT_PASSWORD, 32);
ESPMega_FRAM.write(EEPROM_ADDRESS_MQTT_PASSWORD, (uint8_t *)MQTT_PASSWORD, 20);
2023-11-07 05:19:35 +00:00
}
2023-11-15 09:55:43 +00:00
2023-11-07 08:49:11 +00:00
void eeprom_mqtt_useauth_retrieve()
{
2023-11-07 05:19:35 +00:00
MQTT_USE_AUTH = ESPMega_FRAM.read8(EEPROM_ADDRESS_MQTT_USEAUTH);
}
2023-11-15 09:55:43 +00:00
/**
* @brief Sets the MQTT_USE_AUTH flag and writes it to the EEPROM.
*
2023-11-15 09:55:43 +00:00
* @param use_auth A boolean value indicating whether to use authentication for MQTT.
*/
2023-11-07 08:49:11 +00:00
void set_mqtt_useauth(bool use_auth)
{
2023-11-07 05:19:35 +00:00
MQTT_USE_AUTH = use_auth;
ESPMega_FRAM.write8(EEPROM_ADDRESS_MQTT_USEAUTH, MQTT_USE_AUTH);
2023-11-07 12:16:30 +00:00
}
2023-11-15 09:55:43 +00:00
/**
* @brief Resets the device to factory default settings.
*
* This function resets the device to its factory default settings by formatting the FRAM,
2023-11-15 09:55:43 +00:00
* setting default IP address, gateway, and netmask values, and restarting the device.
*
2023-11-15 09:55:43 +00:00
*/
2023-11-07 13:43:20 +00:00
void factory_reset()
{
for (int i = 5; i > 0; i--)
{
if (digitalRead(2) == HIGH)
{
2023-11-07 12:16:30 +00:00
lcd_send_command("boot_state.txt=\"Factory Reset Canceled, Restarting\"");
delay(5000);
ESP.restart();
}
2023-11-07 13:43:20 +00:00
#ifdef ENABLE_INTERNAL_LCD
Serial.printf("boot_state.txt=\"Factory Reset in %d\"", i);
2023-11-07 12:16:30 +00:00
lcd_send_stop_bit();
2023-11-07 13:43:20 +00:00
#endif
2023-11-07 12:16:30 +00:00
delay(1000);
2023-11-07 13:43:20 +00:00
}
#ifdef ENABLE_INTERNAL_LCD
2023-11-07 12:16:30 +00:00
lcd_send_command("boot_state.txt=\"Factory Reseting . . .\"");
2023-11-07 13:43:20 +00:00
#endif
2023-11-07 12:16:30 +00:00
// Format FRAM
2023-11-07 13:43:20 +00:00
for (int i = 0; i < 32768; i++)
{
ESPMega_FRAM.write8(i, 0);
2023-11-07 12:16:30 +00:00
}
// Load Default Values
set_ip("192.168.0.10");
set_gw("192.168.0.1");
2023-11-07 13:43:20 +00:00
set_netmask("255.255.255.0");
2023-11-07 12:16:30 +00:00
// Reboot
2023-11-07 13:43:20 +00:00
#ifdef ENABLE_INTERNAL_LCD
2023-11-07 12:16:30 +00:00
lcd_send_stop_bit();
lcd_send_command("boot_state.txt=\"Factory Reset OK. Release Button.\"");
delay(3000);
2023-11-07 13:43:20 +00:00
#endif
2023-11-07 12:16:30 +00:00
ESP.restart();
}
2023-11-15 09:55:43 +00:00
/**
* @brief Checks if the device should perform a factory reset on boot.
*
2023-11-15 09:55:43 +00:00
* This function checks if pin 2 is pulled low on boot. If it is, the device will perform a factory reset.
*
2023-11-15 09:55:43 +00:00
*/
2023-11-07 13:43:20 +00:00
void check_boot_reset()
{
2023-11-07 12:16:30 +00:00
pinMode(2, INPUT_PULLUP);
2023-11-07 13:43:20 +00:00
if (digitalRead(2) == LOW)
{
2023-11-07 12:16:30 +00:00
factory_reset();
}
2023-08-25 18:38:17 +00:00
}