diff --git a/src/espmega_iot_core.cpp b/src/espmega_iot_core.cpp index af1b163..a997d5d 100644 --- a/src/espmega_iot_core.cpp +++ b/src/espmega_iot_core.cpp @@ -92,23 +92,43 @@ char AC_ROOM_TEMPERATURE_TOPIC[75]; char AC_HUMIDITY_TOPIC[75]; #endif +#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 + // EEPROM ADDRESS -#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_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 char PWM_STATE_TOPIC[75]; char PWM_VALUE_TOPIC[75]; @@ -125,7 +145,8 @@ Thread mqtt_reconnector = Thread(); Thread environment_reporter = Thread(); Thread eeprom_pwm_updater = Thread(); Thread user_timer_tick = Thread(); -StaticThreadController<4> thread_controller(&mqtt_reconnector, &environment_reporter, &eeprom_pwm_updater, &user_timer_tick); +Thread analog_handler = Thread(); +StaticThreadController<5> thread_controller(&mqtt_reconnector, &environment_reporter, &eeprom_pwm_updater, &user_timer_tick, &analog_handler); #ifdef ENABLE_INTERNAL_LCD Thread top_bar_updater = Thread(); @@ -144,12 +165,12 @@ void setup() #endif Serial.println("ESPMega R3 Initializing"); ESPMega_begin(); - #ifdef OVERCLOCK_FM2 +#ifdef OVERCLOCK_FM2 Wire.setClock(1000000); - #endif - #ifdef OVERCLOCK_FM +#endif +#ifdef OVERCLOCK_FM Wire.setClock(400000); - #endif +#endif io_begin(); eeprom_retrieve_init(); user_pre_init(); @@ -297,6 +318,30 @@ void eeprom_retrieve_init() strcat(PWM_VALUE_TOPIC, "/pwm/00/value"); memcpy(INPUTS_TOPIC, MQTT_BASE_TOPIC, 20); strcat(INPUTS_TOPIC, "/input/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 } #ifdef ENABLE_WEBUI @@ -572,6 +617,23 @@ void mqtt_subscribe() mqtt.subscribe(AC_SET_MODE_TOPIC); #endif mqtt.subscribe(STATE_REQUEST_TOPIC); +#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 } /** @@ -597,6 +659,20 @@ void mqtt_callback(char *topic, byte *payload, unsigned int length) { pwm_value_callback(topic_trim, topic_length, payload_nt, length); } +#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 else if (!strcmp(topic, STATE_REQUEST_TOPIC)) { state_request_callback(); @@ -628,6 +704,10 @@ void thread_initialization() eeprom_pwm_updater.setInterval(1000); user_timer_tick.onRun(timer_tick_callback); user_timer_tick.setInterval(15000); +#ifdef ENABLE_ANALOG_MODULE + analog_handler.onRun(adc_loop); + analog_handler.setInterval(ANALOG_REPORTING_INTERVAL); +#endif } /** @@ -1747,4 +1827,223 @@ void check_boot_reset() { factory_reset(); } -} \ No newline at end of file +} + +#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); + } +} +/** + * @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]); +} + +/** + * @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]); +} + +/** + * @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 diff --git a/src/espmega_iot_core.hpp b/src/espmega_iot_core.hpp index f542632..a2eb29f 100644 --- a/src/espmega_iot_core.hpp +++ b/src/espmega_iot_core.hpp @@ -131,4 +131,19 @@ void eeprom_mqtt_useauth_retrieve(); void set_mqtt_useauth(bool use_auth); void factory_reset(); -void check_boot_reset(); \ No newline at end of file +void check_boot_reset(); + +void enable_adc(int id); +void disable_adc(int id); +void adc_update(int id); +void adc_update_force(int id); +void adc_update_all(); +void adc_loop(); +void publish_adc(int id); +void publish_all_adc(); +uint16_t get_adc_value(int id); +void adc_set_state_callback(char *topic, uint8_t topic_length, char *payload, unsigned int payload_length); +void dac_set_value(int id, int value); +void dac_set_state(int id, bool state); +void dac_set_value_callback(char *topic, uint8_t topic_length, char *payload, unsigned int payload_length); +void dac_set_state_callback(char *topic, uint8_t topic_length, char *payload, unsigned int payload_length); diff --git a/src/user_code.hpp b/src/user_code.hpp index f14d89b..0e04793 100644 --- a/src/user_code.hpp +++ b/src/user_code.hpp @@ -16,6 +16,7 @@ #define ENABLE_INTERNAL_LCD #define ENABLE_IR_MODULE #define ENABLE_CLIMATE_MODULE // Require IR Module +#define ENABLE_ANALOG_MODULE #define ENABLE_WEBUI // Infrared Transciever @@ -35,6 +36,9 @@ #define ESPMega_EXTLCD Serial2 #endif +// Analog Module Configuration +#define ANALOG_REPORTING_INTERVAL 500 + // User Defined Functions void user_pre_init(); void user_init();