2023-11-15 09:55:43 +00:00
/**
* @ file espmega_iot_core . cpp
* @ brief Implementation of the ESPMega IoT Core library .
2023-11-16 07:51:17 +00:00
*
* 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-16 07:51:17 +00:00
*
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-11-29 19:18:44 +00:00
# ifdef ENABLE_ANALOG_MODULE
# define DAC_COUNT 4
# define ADC_COUNT 8
bool dac_states [ DAC_COUNT ] ;
uint16_t dac_values [ DAC_COUNT ] ;
uint16_t adc_values [ ADC_COUNT ] ;
bool adc_report_enable [ ADC_COUNT ] ;
char ADC_COMMAND_TOPIC [ 75 ] ;
char ADC_STATE_TOPIC [ 75 ] ;
char ADC_REPORT_TOPIC [ 75 ] ;
char DAC_SET_STATE_TOPIC [ 75 ] ;
char DAC_SET_VALUE_TOPIC [ 75 ] ;
char DAC_STATE_TOPIC [ 75 ] ;
char DAC_VALUE_TOPIC [ 75 ] ;
# endif
2023-09-11 17:37:33 +00:00
// EEPROM ADDRESS
2023-11-29 19:18:44 +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
# define EEPROM_ADDRESS_ADC_REPORT_STATE 187 // 8bytes, thru 194
# define EEPROM_ADDRESS_DAC_STATE 195 // 4bytes, thru 198
# define EEPROM_ADDRESS_DAC_VALUE 199 // 8bytes, thru 206
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 ;
2023-11-12 13:38:42 +00:00
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 ( ) ;
2023-11-29 19:18:44 +00:00
Thread analog_handler = Thread ( ) ;
StaticThreadController < 5 > thread_controller ( & mqtt_reconnector , & environment_reporter , & eeprom_pwm_updater , & user_timer_tick , & analog_handler ) ;
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-11-29 19:18:44 +00:00
# ifdef OVERCLOCK_FM2
2023-11-16 16:30:46 +00:00
Wire . setClock ( 1000000 ) ;
2023-11-29 19:18:44 +00:00
# endif
# ifdef OVERCLOCK_FM
2023-11-16 16:30:46 +00:00
Wire . setClock ( 400000 ) ;
2023-11-29 19:18:44 +00:00
# endif
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 . . . \" " ) ;
2023-11-12 13:38:42 +00:00
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 ( ) ;
2023-11-12 13:38:42 +00:00
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-16 07:51:17 +00:00
*
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 ( ) ;
2023-11-12 13:38:42 +00:00
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-11-29 19:18:44 +00:00
# ifdef ENABLE_ANALOG_MODULE
memcpy ( ADC_COMMAND_TOPIC , MQTT_BASE_TOPIC , 20 ) ;
strcat ( ADC_COMMAND_TOPIC , " /adc/00/set/state " ) ;
memcpy ( ADC_STATE_TOPIC , MQTT_BASE_TOPIC , 20 ) ;
strcat ( ADC_STATE_TOPIC , " /adc/00/state " ) ;
memcpy ( ADC_REPORT_TOPIC , MQTT_BASE_TOPIC , 20 ) ;
strcat ( ADC_REPORT_TOPIC , " /adc/00/report " ) ;
memcpy ( DAC_SET_STATE_TOPIC , MQTT_BASE_TOPIC , 20 ) ;
strcat ( DAC_SET_STATE_TOPIC , " /dac/00/set/state " ) ;
memcpy ( DAC_SET_VALUE_TOPIC , MQTT_BASE_TOPIC , 20 ) ;
strcat ( DAC_SET_VALUE_TOPIC , " /dac/00/set/value " ) ;
memcpy ( DAC_STATE_TOPIC , MQTT_BASE_TOPIC , 20 ) ;
strcat ( DAC_STATE_TOPIC , " /dac/00/state " ) ;
memcpy ( DAC_VALUE_TOPIC , MQTT_BASE_TOPIC , 20 ) ;
strcat ( DAC_VALUE_TOPIC , " /dac/00/value " ) ;
ESPMega_FRAM . read ( EEPROM_ADDRESS_ADC_REPORT_STATE , ( uint8_t * ) adc_report_enable , 8 ) ;
ESPMega_FRAM . read ( EEPROM_ADDRESS_DAC_STATE , ( uint8_t * ) dac_states , 4 ) ;
ESPMega_FRAM . read ( EEPROM_ADDRESS_DAC_VALUE , ( uint8_t * ) dac_values , 8 ) ;
for ( int i = 0 ; i < DAC_COUNT ; i + + )
{
dac_set_state ( i , dac_states [ i ] ) ;
dac_set_value ( i , dac_values [ i ] ) ;
}
# endif
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-16 07:51:17 +00:00
*
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-16 07:51:17 +00:00
*
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-16 07:51:17 +00:00
*
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 ;
2023-11-07 10:59:35 +00:00
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 ) ; } ) ;
2023-11-07 10:59:35 +00:00
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-11-07 10:59:35 +00:00
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 \n Rebooting... \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 .
2023-11-16 07:51:17 +00:00
*
* @ 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-16 07:51:17 +00:00
*
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-16 07:51:17 +00:00
*
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-16 07:51:17 +00:00
*
2023-11-15 09:55:43 +00:00
* @ return void
*/
2023-08-25 18:38:17 +00:00
void mqtt_connect ( )
{
2023-11-12 13:38:42 +00:00
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 )
2023-11-12 13:38:42 +00:00
mqtt . connect ( HOSTNAME , MQTT_USERNAME , MQTT_PASSWORD ) ;
2023-11-07 08:49:11 +00:00
else
2023-11-12 13:38:42 +00:00
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-11-30 11:53:01 +00:00
publish_dac_states ( ) ;
publish_dac_values ( ) ;
publish_all_adc ( ) ;
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-16 07:51:17 +00:00
*
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 ' ;
2023-11-12 13:38:42 +00:00
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
2023-11-12 13:38:42 +00:00
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
2023-11-12 13:38:42 +00:00
mqtt . subscribe ( STATE_REQUEST_TOPIC ) ;
2023-11-29 19:18:44 +00:00
# ifdef ENABLE_ANALOG_MODULE
for ( int i = 0 ; i < ADC_COUNT ; i + + )
{
ADC_COMMAND_TOPIC [ base_topic_length + 4 ] = ( ( i - i % 10 ) / 10 ) + ' 0 ' ;
ADC_COMMAND_TOPIC [ base_topic_length + 5 ] = ( i % 10 ) + ' 0 ' ;
mqtt . subscribe ( ADC_COMMAND_TOPIC ) ;
}
for ( int i = 0 ; i < DAC_COUNT ; i + + )
{
DAC_SET_STATE_TOPIC [ base_topic_length + 4 ] = ( ( i - i % 10 ) / 10 ) + ' 0 ' ;
DAC_SET_STATE_TOPIC [ base_topic_length + 5 ] = ( i % 10 ) + ' 0 ' ;
DAC_SET_VALUE_TOPIC [ base_topic_length + 4 ] = ( ( i - i % 10 ) / 10 ) + ' 0 ' ;
DAC_SET_VALUE_TOPIC [ base_topic_length + 5 ] = ( i % 10 ) + ' 0 ' ;
mqtt . subscribe ( DAC_SET_STATE_TOPIC ) ;
mqtt . subscribe ( DAC_SET_VALUE_TOPIC ) ;
}
# endif
2023-11-12 13:38:42 +00:00
}
2023-11-15 09:55:43 +00:00
/**
* @ brief Callback function for MQTT messages received by the device .
2023-11-16 07:51:17 +00:00
*
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 )
{
2023-11-12 13:38:42 +00:00
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 ) ;
2023-11-16 07:51:17 +00:00
}
2023-11-29 19:18:44 +00:00
# ifdef ENABLE_ANALOG_MODULE
else if ( ( ! strncmp ( topic_trim , " /adc/ " , 5 ) ) & & ! strncmp ( topic_trim + 7 , " /set/state " , 10 ) )
{
adc_set_state_callback ( topic_trim , topic_length , payload_nt , length ) ;
}
else if ( ( ! strncmp ( topic_trim , " /dac/ " , 5 ) ) & & ! strncmp ( topic_trim + 7 , " /set/state " , 10 ) )
{
dac_set_state_callback ( topic_trim , topic_length , payload_nt , length ) ;
}
else if ( ( ! strncmp ( topic_trim , " /dac/ " , 5 ) ) & & ! strncmp ( topic_trim + 7 , " /set/value " , 10 ) )
{
dac_set_value_callback ( topic_trim , topic_length , payload_nt , length ) ;
}
# endif
2023-11-16 07:51:17 +00:00
else if ( ! strcmp ( topic , STATE_REQUEST_TOPIC ) )
{
2023-11-12 14:25:08 +00:00
state_request_callback ( ) ;
2023-11-12 13:38:42 +00:00
}
2023-11-12 14:25:08 +00:00
else
{
2023-11-16 07:51:17 +00:00
# ifdef ENABLE_CLIMATE_MODULE
2023-11-12 14:25:08 +00:00
ac_state_callback ( topic , topic_length , payload_nt , length ) ;
2023-11-16 07:51:17 +00:00
# endif
2023-11-12 13:38:42 +00:00
}
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-16 07:51:17 +00:00
*
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-11-29 19:18:44 +00:00
# ifdef ENABLE_ANALOG_MODULE
analog_handler . onRun ( adc_loop ) ;
analog_handler . setInterval ( ANALOG_REPORTING_INTERVAL ) ;
# endif
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-16 07:51:17 +00:00
*
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
{
2023-11-12 13:38:42 +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-16 07:51:17 +00:00
*
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-16 07:51:17 +00:00
*
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
{
2023-11-12 13:38:42 +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 13:38:42 +00:00
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-16 07:51:17 +00:00
*
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-16 07:51:17 +00:00
*
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-16 07:51:17 +00:00
*
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-16 07:51:17 +00:00
*
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 )
{
2023-11-12 13:38:42 +00:00
mqtt . publish ( PWM_STATE_TOPIC , " on " ) ;
2023-08-25 18:38:17 +00:00
}
else if ( state = = 0 )
{
2023-11-12 13:38:42 +00:00
mqtt . publish ( PWM_STATE_TOPIC , " off " ) ;
2023-08-25 18:38:17 +00:00
}
2023-11-12 13:38:42 +00:00
char temp [ 6 ] ;
2023-11-12 14:25:08 +00:00
itoa ( value , temp , DEC ) ;
2023-11-12 13:38:42 +00:00
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-16 07:51:17 +00:00
*
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-16 07:51:17 +00:00
*
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-16 07:51:17 +00:00
*
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-16 07:51:17 +00:00
*
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-16 07:51:17 +00:00
*
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-16 07:51:17 +00:00
*
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-16 07:51:17 +00:00
*
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-16 07:51:17 +00:00
*
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 ' ;
2023-11-12 13:38:42 +00:00
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-16 07:51:17 +00:00
*
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-11-30 11:53:01 +00:00
publish_all_adc ( ) ;
publish_dac_states ( ) ;
publish_dac_values ( ) ;
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-16 07:51:17 +00:00
*
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 :
2023-11-12 13:38:42 +00:00
mqtt . publish ( AC_MODE_TOPIC , " off " ) ;
2023-08-28 17:49:11 +00:00
break ;
case 1 :
2023-11-12 13:38:42 +00:00
mqtt . publish ( AC_MODE_TOPIC , " cool " ) ;
2023-08-28 17:49:11 +00:00
break ;
case 2 :
2023-11-12 13:38:42 +00:00
mqtt . publish ( AC_MODE_TOPIC , " fan_only " ) ;
2023-08-28 17:49:11 +00:00
default :
break ;
}
2023-11-12 13:38:42 +00:00
char temp [ 5 ] ;
2023-11-12 14:25:08 +00:00
itoa ( ac_temperature , temp , DEC ) ;
2023-11-12 13:38:42 +00:00
mqtt . publish ( AC_TEMPERATURE_TOPIC , temp ) ;
2023-08-28 17:49:11 +00:00
switch ( ac_fan_speed )
{
case 0 :
2023-11-12 13:38:42 +00:00
mqtt . publish ( AC_FAN_TOPIC , " auto " ) ;
2023-08-28 17:49:11 +00:00
break ;
case 1 :
2023-11-12 13:38:42 +00:00
mqtt . publish ( AC_FAN_TOPIC , " high " ) ;
2023-08-28 17:49:11 +00:00
break ;
case 2 :
2023-11-12 16:07:16 +00:00
mqtt . publish ( AC_FAN_TOPIC , " medium " ) ;
2023-08-28 17:49:11 +00:00
break ;
case 3 :
2023-11-12 13:38:42 +00:00
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-16 07:51:17 +00:00
*
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-16 07:51:17 +00:00
*
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 ) ;
}
2023-11-12 16:07:16 +00:00
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-16 07:51:17 +00:00
*
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-16 07:51:17 +00:00
*
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-16 07:51:17 +00:00
*
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 ( ) ;
2023-11-12 13:38:42 +00:00
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 :
2023-11-12 13:38:42 +00:00
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-16 07:51:17 +00:00
*
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-16 07:51:17 +00:00
*
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-16 07:51:17 +00:00
*
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-16 07:51:17 +00:00
*
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-16 07:51:17 +00:00
*
2023-11-15 09:55:43 +00:00
* @ note This function assumes that the LCD panel has already been initialized .
2023-11-16 07:51:17 +00:00
*
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 ) ;
2023-11-07 10:59:35 +00:00
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-16 07:51:17 +00:00
*
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-16 07:51:17 +00:00
*
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-16 07:51:17 +00:00
*
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-16 07:51:17 +00:00
*
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-16 07:51:17 +00:00
*
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-16 07:51:17 +00:00
*
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-16 07:51:17 +00:00
*
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-16 07:51:17 +00:00
*
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-16 07:51:17 +00:00
*
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-16 07:51:17 +00:00
*
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-16 07:51:17 +00:00
*
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-16 07:51:17 +00:00
*
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-16 07:51:17 +00:00
*
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-16 07:51:17 +00:00
*
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-16 07:51:17 +00:00
*
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-16 07:51:17 +00:00
*
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-16 07:51:17 +00:00
*
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-16 07:51:17 +00:00
*
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-16 07:51:17 +00:00
*
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-16 07:51:17 +00:00
*
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-16 07:51:17 +00:00
*
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-16 07:51:17 +00:00
*
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-16 07:51:17 +00:00
*
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-16 07:51:17 +00:00
*
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-16 07:51:17 +00:00
*
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-16 07:51:17 +00:00
*
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-16 07:51:17 +00:00
*
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-16 07:51:17 +00:00
*
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 .
2023-11-16 07:51:17 +00:00
*
* 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-16 07:51:17 +00:00
*
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-16 07:51:17 +00:00
*
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-16 07:51:17 +00:00
*
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-11-29 19:18:44 +00:00
}
# ifdef ENABLE_ANALOG_MODULE
/**
* Enables the ADC reporting for the specified ID .
*
* @ param id The ID of the ADC to enable reporting for .
*/
void enable_adc ( int id )
{
adc_report_enable [ id ] = true ;
ESPMega_FRAM . write8 ( EEPROM_ADDRESS_ADC_REPORT_STATE + id , 1 ) ;
}
/**
* @ brief Disables the ADC reporting for the specified ID .
*
* This function sets the adc_report_enable flag to false for the specified ID and writes the state to the EEPROM .
*
* @ param id The ID of the ADC to disable reporting for .
*/
void disable_adc ( int id )
{
adc_report_enable [ id ] = false ;
ESPMega_FRAM . write8 ( EEPROM_ADDRESS_ADC_REPORT_STATE + id , 0 ) ;
}
/**
* @ brief Updates the ADC value for the specified ID if ADC reporting is enabled .
*
* @ param id The ID of the ADC channel .
*/
void adc_update ( int id )
{
if ( adc_report_enable [ id ] )
{
adc_values [ id ] = ESPMega_analogRead ( id ) ;
}
}
/**
* @ brief Updates the ADC value for the specified ID , do so even if reporting is disabled . .
*
* @ param id The ID of the ADC pin .
*/
void adc_update_force ( int id )
{
adc_values [ id ] = ESPMega_analogRead ( id ) ;
}
/**
* @ brief Updates all ADC channels .
*
* This function updates all ADC channels by calling the ` adc_update ` function for each channel .
*
* @ return void
*/
void adc_update_all ( )
{
for ( int i = 0 ; i < ADC_COUNT ; i + + )
{
adc_update ( i ) ;
}
}
/**
* @ brief Performs ADC loop operations .
*
* This function updates all ADC values and publishes them .
*/
void adc_loop ( )
{
adc_update_all ( ) ;
publish_all_adc ( ) ;
}
/**
* Publishes the ADC value to the MQTT broker .
*
* @ param id The ID of the ADC channel .
*/
void publish_adc ( int id )
{
ADC_STATE_TOPIC [ base_topic_length + 4 ] = ( ( id - id % 10 ) / 10 ) + ' 0 ' ;
ADC_STATE_TOPIC [ base_topic_length + 5 ] = ( id % 10 ) + ' 0 ' ;
char temp [ 6 ] ;
itoa ( adc_values [ id ] , temp , DEC ) ;
mqtt . publish ( ADC_STATE_TOPIC , temp ) ;
}
/**
* Publishes the values of all enabled ADC channels .
* This function iterates through all ADC channels and publishes the values
* of the enabled channels using the publish_adc ( ) function .
*/
void publish_all_adc ( )
{
for ( int i = 0 ; i < ADC_COUNT ; i + + )
{
if ( adc_report_enable [ i ] )
publish_adc ( i ) ;
}
}
2023-11-30 11:53:01 +00:00
void publish_dac_state ( int id )
{
DAC_STATE_TOPIC [ base_topic_length + 4 ] = ( ( id - id % 10 ) / 10 ) + ' 0 ' ;
DAC_STATE_TOPIC [ base_topic_length + 5 ] = ( id % 10 ) + ' 0 ' ;
mqtt . publish ( DAC_STATE_TOPIC , dac_states [ id ] ? " on " : " off " ) ;
}
void publish_dac_value ( int id ) {
DAC_VALUE_TOPIC [ base_topic_length + 4 ] = ( ( id - id % 10 ) / 10 ) + ' 0 ' ;
DAC_VALUE_TOPIC [ base_topic_length + 5 ] = ( id % 10 ) + ' 0 ' ;
char temp [ 6 ] ;
itoa ( dac_values [ id ] , temp , DEC ) ;
mqtt . publish ( DAC_VALUE_TOPIC , temp ) ;
}
2023-11-29 19:18:44 +00:00
/**
* @ brief Retrieves the ADC value for the specified ID .
*
* This function checks if the ADC report is enabled for the given ID . If not , it forces an update .
* It then returns the ADC value for the specified ID .
*
* @ param id The ID of the ADC channel .
* @ return The ADC value for the specified ID .
*/
uint16_t get_adc_value ( int id )
{
if ( ! adc_report_enable [ id ] )
adc_update_force ( id ) ;
return adc_values [ id ] ;
}
/**
* @ brief Sets the state of an ADC based on the received MQTT message .
*
* This function is called when an MQTT message is received to set the state of an ADC ( Analog - to - Digital Converter ) .
* The function extracts the ADC ID from the topic and enables or disables the ADC based on the payload value .
*
* @ param topic The topic of the MQTT message .
* @ param topic_length The length of the topic .
* @ param payload The payload of the MQTT message .
* @ param payload_length The length of the payload .
*/
void adc_set_state_callback ( char * topic , uint8_t topic_length , char * payload , unsigned int payload_length )
{
int a = topic [ 4 ] - ' 0 ' ;
int b = topic [ 5 ] - ' 0 ' ;
int id = 10 * a + b ;
if ( ! strcmp ( payload , " on " ) )
{
enable_adc ( id ) ;
}
else if ( ! strcmp ( payload , " off " ) )
{
disable_adc ( id ) ;
}
}
/**
* @ brief Sets the value of a DAC channel .
*
* This function sets the value of a DAC channel specified by the ` id ` parameter .
* The ` value ` parameter represents the desired value for the DAC channel .
* The function updates the internal DAC value array , writes the value to the DAC ,
* and also stores the value in the FRAM memory .
*
* @ param id The ID of the DAC channel .
* @ param value The desired value for the DAC channel .
*/
void dac_set_value ( int id , int value )
{
dac_values [ id ] = value ;
ESPMega_dacWrite ( id , dac_values [ id ] * dac_states [ id ] ) ;
ESPMega_FRAM . write16 ( EEPROM_ADDRESS_DAC_VALUE + id * 2 , dac_values [ id ] ) ;
2023-11-30 11:53:01 +00:00
publish_dac_value ( id ) ;
2023-11-29 19:18:44 +00:00
}
/**
* @ brief Sets the state of a DAC channel .
*
* This function updates the state of a DAC channel and writes the new state to the DAC output .
* It also saves the state to the EEPROM for persistence across power cycles .
*
* @ param id The ID of the DAC channel .
* @ param state The new state of the DAC channel .
*/
void dac_set_state ( int id , bool state )
{
dac_states [ id ] = state ;
ESPMega_dacWrite ( id , dac_values [ id ] * dac_states [ id ] ) ;
ESPMega_FRAM . write8 ( EEPROM_ADDRESS_DAC_STATE + id , dac_states [ id ] ) ;
2023-11-30 11:53:01 +00:00
publish_dac_state ( id ) ;
}
void publish_dac_states ( )
{
for ( int i = 0 ; i < DAC_COUNT ; i + + )
{
publish_dac_state ( i ) ;
}
}
void publish_dac_values ( )
{
for ( int i = 0 ; i < DAC_COUNT ; i + + )
{
publish_dac_value ( i ) ;
}
2023-11-29 19:18:44 +00:00
}
/**
* @ brief Sets the value of the DAC with a callback function .
*
* @ param topic The topic of the message .
* @ param topic_length The length of the topic string .
* @ param payload The payload of the message .
* @ param payload_length The length of the payload string .
*/
void dac_set_value_callback ( char * topic , uint8_t topic_length , char * payload , unsigned int payload_length )
{
int a = topic [ 4 ] - ' 0 ' ;
int b = topic [ 5 ] - ' 0 ' ;
int id = 10 * a + b ;
int value = atoi ( payload ) ;
dac_set_value ( id , value ) ;
}
/**
* @ brief Callback function for setting the state of the DAC .
*
* This function is called when a message is received on the specified topic .
* It takes the topic , topic length , payload , and payload length as parameters .
*
* @ param topic The topic of the received message .
* @ param topic_length The length of the topic string .
* @ param payload The payload of the received message .
* @ param payload_length The length of the payload string .
*/
void dac_set_state_callback ( char * topic , uint8_t topic_length , char * payload , unsigned int payload_length )
{
int a = topic [ 4 ] - ' 0 ' ;
int b = topic [ 5 ] - ' 0 ' ;
int id = 10 * a + b ;
if ( ! strcmp ( payload , " on " ) )
{
dac_set_state ( id , true ) ;
}
else if ( ! strcmp ( payload , " off " ) )
{
dac_set_state ( id , false ) ;
}
}
# endif