Merge branch 'main' into cud

This commit is contained in:
Siwat Sirichai 2023-11-30 19:51:00 +07:00
commit aad6fce479
4 changed files with 448 additions and 25 deletions

35
readme.md Normal file
View File

@ -0,0 +1,35 @@
# IoT Core OS V3
This is an OS for the ESPMega PRO R3 Programable Logic Controller
## **Compatibility**
1. **CPU**
- ESPMega PRO R3.0a/b
- ESPMega PRO R3.1a
- ESPMega PRO R3.2a/b/c
2. **CPU Add-ons**
- ESPMega PRO Internal Display Module
- ESPMega PRO External Touch Display Module
3. **Add-on Cards**
- ESPMega I/O Analog Expansion Card
- ESPMega I/O IR Expansion Kit
- ESPMega I/O Card Hub
- ESPMega I/O UART Multiplexer [WIP]
- ESPMega I/O Digital Expansion Card [WIP]
## Features
- Internal Touch Display support for diagnostics and configuration
- WebUI for Configuration and OTA Update
- Allowing for reading and writing to registers from MQTT
- Provides abstraction layer to the MQTT protocol and internal components
## User Code and 3rd Party Extension
This OS allows the user to write custom program for the device to run in the OS<br/>
### *usercode.hpp* and *user_code.cpp*
### I/O Abstraction Layer
### MQTT Abstraction Layer
### RTC and Clock Abstraction Layer
### Persistent Storage Abstraction Layer
### Climate Abstraction Layer
### Energy Monitoring Abstraction Layer
### Timer Abstraction Layer
### LCD Abstraction Layer

View File

@ -78,7 +78,6 @@ Mode 0: Off, 1: Cool, 2: Fan
Fan Speed 0: Auto, 1: High, 2: Mid, 3: Low
*/
#ifdef ENABLE_CLIMATE_MODULE
#define DHT22_PIN 32
uint8_t ac_mode = 0;
uint8_t ac_fan_speed = 0;
uint8_t ac_temperature = 25;
@ -92,23 +91,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 +144,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 +164,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 +317,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
@ -534,6 +578,12 @@ void mqtt_connect()
publish_ac_state();
#endif
mqtt_connected_user_callback();
#ifdef ENABLE_ANALOG_MODULE
publish_dac_states();
publish_dac_values();
publish_adc_values();
publish_adc_states();
#endif
standalone = false;
ESPMega_updateTimeFromNTP();
}
@ -572,6 +622,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 +664,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 +709,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
}
/**
@ -924,6 +1009,12 @@ void state_request_callback()
publish_env_state();
#endif
user_state_request_callback();
#ifdef ENABLE_ANALOG_MODULE
publish_adc_states();
publish_adc_values();
publish_dac_states();
publish_dac_values();
#endif
}
#ifdef ENABLE_IR_MODULE
@ -1748,3 +1839,274 @@ void check_boot_reset()
factory_reset();
}
}
#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);
publish_adc_state(id);
}
/**
* @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);
publish_adc_state(id);
}
void publish_adc_state(int id)
{
ADC_STATE_TOPIC[base_topic_length + 4] = ((id - id % 10) / 10) + '0';
ADC_STATE_TOPIC[base_topic_length + 5] = (id % 10) + '0';
mqtt.publish(ADC_STATE_TOPIC, adc_report_enable[id] ? "on" : "off");
}
void publish_adc_states()
{
for (int i = 0; i < ADC_COUNT; i++)
{
publish_adc_state(i);
}
}
/**
* @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_adc_values();
}
/**
* Publishes the ADC value to the MQTT broker.
*
* @param id The ID of the ADC channel.
*/
void publish_adc_value(int id)
{
ADC_REPORT_TOPIC[base_topic_length + 4] = ((id - id % 10) / 10) + '0';
ADC_REPORT_TOPIC[base_topic_length + 5] = (id % 10) + '0';
char temp[8];
itoa(adc_values[id], temp, DEC);
mqtt.publish(ADC_REPORT_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_adc_values()
{
for (int i = 0; i < ADC_COUNT; i++)
{
if (adc_report_enable[i])
publish_adc_value(i);
}
}
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);
}
/**
* @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[5] - '0';
int b = topic[6] - '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]);
publish_dac_value(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]);
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);
}
}
/**
* @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[5] - '0';
int b = topic[6] - '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[5] - '0';
int b = topic[6] - '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

View File

@ -132,3 +132,24 @@ void set_mqtt_useauth(bool use_auth);
void factory_reset();
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_value(int id);
void publish_adc_values();
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);
void publish_dac_value(int id);
void publish_dac_state(int id);
void publish_dac_values();
void publish_dac_states();
void publish_adc_state(int id);
void publish_adc_states();

View File

@ -19,15 +19,17 @@
#define ENABLE_INTERNAL_LCD
#define ENABLE_IR_MODULE
#define ENABLE_CLIMATE_MODULE // Require IR Module
#define ENABLE_ANALOG_MODULE
#define ENABLE_WEBUI
// Infrared Transciever
// IR Kit Configuration
#define IR_RECIEVE_PIN 35
#define IR_SEND_PIN 5
#define MARK_EXCESS_MICROS 20
#define RAW_BUFFER_LENGTH 750
#define AC_MAX_TEMPERATURE 30
#define AC_MIN_TEMPERATURE 15
#define DHT22_PIN 32
/*
Environment Details
@ -65,6 +67,9 @@ I0-I2: Fan Switch
#define elcd ESPMega_EXTLCD
#endif
// Analog Module Configuration
#define ANALOG_REPORTING_INTERVAL 500
// User Defined Functions
void user_pre_init();
void user_init();