// #define USE_INTERRUPT #include #include #include #include #include #include #include #include #include #include "EasyNextionLibrary.h" #include // Network Connectivity #define HOSTNAME "espmega-pro-r3" const IPAddress IP(192, 168, 0, 210); const IPAddress SUBNET(255, 255, 255, 0); const IPAddress GATEWAY(192, 168, 0, 1); const IPAddress DNS(10, 192, 1, 1); const IPAddress MQTT_SERVER(192, 168, 0, 26); bool standalone = true; const int MQTT_PORT = 1883; #define MQTT_BASE_TOPIC "/espmega/ProR3" char STATE_REQUEST_TOPIC[75] = MQTT_BASE_TOPIC "/requeststate"; // #define MQTT_USE_AUTH #ifdef MQTT_USE_AUTH const char MQTT_USERNAME[] = "username"; const char MQTT_PASSWORD[] = "password"; #endif // Inputs #define VINT_COUNT 16 const int DEBOUNCE_TIME_MS = 50; 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]; // Outputs #define PWM_COUNT 16 const int pwm_pins[PWM_COUNT] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; int pwm_states[PWM_COUNT]; int pwm_values[PWM_COUNT]; const float pwm_linear_scaling_m[PWM_COUNT] = {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}; #define PWM_CYCLE_VALUES_COUNT 3 const int PWM_CYCLE_VALUES[PWM_CYCLE_VALUES_COUNT] = {50, 125, 255}; char PWM_SET_STATE_TOPIC[75] = MQTT_BASE_TOPIC "/pwm/00/set/state"; char PWM_SET_VALUE_TOPIC[75] = MQTT_BASE_TOPIC "/pwm/00/set/value"; // Infrared Transciever #define IR_RECIEVE_PIN 32 #define IR_SEND_PIN 15 #define MARK_EXCESS_MICROS 20 #define RAW_BUFFER_LENGTH 750 // LCD int lcd_current_page = 1; EasyNex panel(Serial); // Air Conditioner Control /* Mode 0: Off, 1: Cool, 2: Fan Fan Speed 0: Auto, 1: High, 2: Mid, 3: Low */ #define DHT22_PIN 17 int ac_mode = 0; int ac_fan_speed = 0; int ac_temperature = 25; #define AC_MAX_TEMPERATURE 30 #define AC_MIN_TEMPERATURE 18 char AC_SET_MODE_TOPIC[75] = MQTT_BASE_TOPIC "/ac/set/mode"; char AC_SET_FAN_TOPIC[75] = MQTT_BASE_TOPIC "/ac/set/fan_speed"; char AC_SET_TEMPERATURE_TOPIC[75] = MQTT_BASE_TOPIC "/ac/set/temperature"; char AC_MODE_TOPIC[75] = MQTT_BASE_TOPIC "/ac/mode"; char AC_FAN_TOPIC[75] = MQTT_BASE_TOPIC "/ac/fan_speed"; char AC_TEMPERATURE_TOPIC[75] = MQTT_BASE_TOPIC "/ac/temperature"; char AC_ROOM_TEMPERATURE_TOPIC[75] = MQTT_BASE_TOPIC "/ac/room_temperature"; char AC_HUMIDITY_TOPIC[75] = MQTT_BASE_TOPIC "/ac/humidity"; // Forward declaration void virtual_interrupt_loop(); void virtual_interrupt_callback(int pin, int state); void network_begin(); void mqtt_connect(); void mqtt_subscribe(); void thread_initialization(); void pwm_state_callback(String topic, String message); void pwm_value_callback(String topic, String message); void state_request_callback(String topic, String message); void io_begin(); void ir_loop(); void publish_pwm_states(); void publish_pwm_state(int id); void pwm_set_state(int id, int state); void pwm_set_value(int id, int value); void pwm_toggle(int id); void pwm_toggle(int id1, int id2); void pwm_cycle_value(int id); boolean pwm_group_state(int id1, int id2); void publish_ac_state(); void publish_env_state(); void ac_state_callback(String topic, String message); void ac_set_state(int mode, int temperature, int fan_speed); void publish_input_states(); void publish_input_state(int id); void publish_input_state(int id, int state); void lcd_begin(); void lcd_loop(); void lcd_refresh(); void lcd_refresh_pd(); void lcd_top_bar_update(); char PWM_STATE_TOPIC[75] = MQTT_BASE_TOPIC "/pwm/00/state"; char PWM_VALUE_TOPIC[75] = MQTT_BASE_TOPIC "/pwm/00/value"; char INPUTS_TOPIC[75] = MQTT_BASE_TOPIC "/input/00"; WiFiClient eth; PubSubClient mqtt_client(MQTT_SERVER, 1883, eth); PubSubClientTools mqtt(mqtt_client); DHTNEW env_sensor(DHT22_PIN); Thread mqtt_reconnector = Thread(); Thread environment_reporter = Thread(); StaticThreadController<2> thread_controller(&mqtt_reconnector, &environment_reporter); Thread top_bar_updater = Thread(); Thread page_updater = Thread(); StaticThreadController<2> lcd_thread_controller(&top_bar_updater, &page_updater); void setup() { Serial.begin(115200); panel.begin(115200); lcd_init(); lcd_begin(); lcd_send_command("boot_state.txt=\"Core Initializing . . .\""); Serial.println("ESPMega R3 Initializing . . ."); ESPMega_begin(); io_begin(); Serial.println("Initializing Infrared . . ."); lcd_send_command("boot_state.txt=\"Infrared Initializing . . .\""); IrReceiver.begin(IR_RECIEVE_PIN); IrSender.begin(IR_SEND_PIN); lcd_send_command("boot_state.txt=\"Network Initializing . . .\""); network_begin(); Serial.println("Initializing MQTT . . ."); lcd_send_command("boot_state.txt=\"IoT Core Initializing . . .\""); mqtt_connect(); lcd_send_command("boot_state.txt=\"Threads Initializing . . .\""); thread_initialization(); Serial.println("Initialization Completed."); Serial.println("Jumping to User Code."); lcd_send_command("page dashboard"); } void loop() { virtual_interrupt_loop(); mqtt_client.loop(); ESPMega_loop(); ir_loop(); thread_controller.run(); lcd_loop(); } void io_begin() { Serial.println("Initializing I/O . . ."); pinMode(IR_RECIEVE_PIN, INPUT_PULLUP); pinMode(IR_SEND_PIN, OUTPUT); memset(pwm_states, 0, PWM_COUNT); for (int i = 0; i < PWM_COUNT; i++) { pwm_values[i] = 4095; } } void network_begin() { Serial.print("Initializing Network "); ETH.begin(); ETH.setHostname(HOSTNAME); ETH.config(IP, GATEWAY, SUBNET, DNS, DNS); for (int i = 0; i < 3; i++) { delay(2500); Serial.print(" ."); } Serial.println(); } void mqtt_connect() { if (!mqtt_client.connected()) { Serial.print("MQTT not connected, connecting . . .\n"); lcd_send_stop_bit(); #ifdef MQTT_USE_AUTH mqtt_client.connect(HOSTNAME, MQTT_USERNAME, MQTT_PASSWORD); #else mqtt_client.connect(HOSTNAME); #endif if (mqtt_client.connected()) { mqtt_subscribe(); Serial.print("MQTT connected\n"); lcd_send_stop_bit(); publish_pwm_states(); publish_input_states(); publish_ac_state(); standalone = false; } else { standalone = true; Serial.print("MQTT not connected, continuing in standalone mode\n"); lcd_send_stop_bit(); } lcd_send_stop_bit(); lcd_refresh(); lcd_top_bar_update(); } } void mqtt_subscribe() { for (int i = 0; i < PWM_COUNT; i++) { PWM_SET_VALUE_TOPIC[sizeof(MQTT_BASE_TOPIC) + 4] = ((i - i % 10) / 10) + '0'; PWM_SET_VALUE_TOPIC[sizeof(MQTT_BASE_TOPIC) + 5] = (i % 10) + '0'; PWM_SET_STATE_TOPIC[sizeof(MQTT_BASE_TOPIC) + 4] = ((i - i % 10) / 10) + '0'; PWM_SET_STATE_TOPIC[sizeof(MQTT_BASE_TOPIC) + 5] = (i % 10) + '0'; mqtt.subscribe(PWM_SET_STATE_TOPIC, pwm_state_callback); mqtt.subscribe(PWM_SET_VALUE_TOPIC, pwm_value_callback); } mqtt.subscribe(AC_SET_FAN_TOPIC, ac_state_callback); mqtt.subscribe(AC_SET_TEMPERATURE_TOPIC, ac_state_callback); mqtt.subscribe(AC_SET_MODE_TOPIC, ac_state_callback); mqtt.subscribe(STATE_REQUEST_TOPIC, state_request_callback); } void thread_initialization() { Serial.println("Initializing Threads . . ."); Serial.println("Initializing MQTT Thread . . ."); mqtt_reconnector.onRun(mqtt_connect); mqtt_reconnector.setInterval(15000); environment_reporter.onRun(publish_env_state); environment_reporter.setInterval(5000); } void pwm_state_callback(String topic, String message) { int a = topic.charAt(sizeof(MQTT_BASE_TOPIC) + 4) - '0'; int b = topic.charAt(sizeof(MQTT_BASE_TOPIC) + 5) - '0'; int id = 10 * a + b; if (message.compareTo("on") == 0) { pwm_set_state(id, true); } else if (message.compareTo("off") == 0) { pwm_set_state(id, false); } } void pwm_value_callback(String topic, String message) { int a = topic.charAt(sizeof(MQTT_BASE_TOPIC) + 4) - '0'; int b = topic.charAt(sizeof(MQTT_BASE_TOPIC) + 5) - '0'; int id = 10 * a + b; int value = message.toInt(); pwm_set_value(id, value); } void virtual_interrupt_callback(int pin, int state) { publish_input_state(pin, state); // Serial.printf("Pin %d changed to %d\n", pin, state); if (lcd_current_page == 2) panel.writeNum("I" + String(pin) + ".val", state); if (pin == 15) { pwm_toggle(4); } } 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(); } } void publish_pwm_states() { for (int i = 0; i < PWM_COUNT; i++) { publish_pwm_state(i); } } void publish_pwm_state(int id) { int state = pwm_states[id]; int value = pwm_values[id]; PWM_STATE_TOPIC[sizeof(MQTT_BASE_TOPIC) + 4] = ((id - id % 10) / 10) + '0'; PWM_STATE_TOPIC[sizeof(MQTT_BASE_TOPIC) + 5] = (id % 10) + '0'; if (state == 1) { mqtt_client.publish(PWM_STATE_TOPIC, "on"); } else if (state == 0) { mqtt_client.publish(PWM_STATE_TOPIC, "off"); } mqtt.publish(String(PWM_VALUE_TOPIC), String(value)); } void pwm_set_state(int id, int state) { if (state != pwm_states[id]) { 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])); if (lcd_current_page == 3) panel.writeNum("j" + String(id) + ".pco", state?47829:12710); publish_pwm_state(id); } } void pwm_set_value(int id, int value) { 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])); if (lcd_current_page == 3) panel.writeNum("j" + String(id) + ".val", int(value / 4095.0 * 100.0)); publish_pwm_state(id); } void pwm_toggle(int id) { int state = !pwm_states[id]; pwm_set_state(id, state); } 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); } } 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; } void pwm_cycle_value(int id) { int state = pwm_states[id]; int value = pwm_values[id]; 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]); } void publish_input_states() { for (int i = 0; i < VINT_COUNT; i++) { publish_input_state(i); } } void publish_input_state(int id) { int state = ESPMega_digitalRead(virtual_interrupt_pins[id]); publish_input_state(id, state); } void publish_input_state(int id, int state) { char INPUTS_TOPIC[75] = MQTT_BASE_TOPIC "/input/00"; INPUTS_TOPIC[sizeof(MQTT_BASE_TOPIC) + 6] = ((id - id % 10) / 10) + '0'; INPUTS_TOPIC[sizeof(MQTT_BASE_TOPIC) + 7] = (id % 10) + '0'; mqtt.publish(String(INPUTS_TOPIC), state ? "1" : "0"); } void state_request_callback(String topic, String message) { publish_input_states(); publish_pwm_states(); publish_ac_state(); } void ir_loop() { if (IrReceiver.decode()) { // Serial.println(); // IrReceiver.compensateAndPrintIRResultAsCArray(&Serial, false); // Serial.println(); // Serial.println(); IrReceiver.resume(); } } void publish_ac_state() { String temp = ""; switch (ac_mode) { case 0: temp = "off"; break; case 1: temp = "cool"; break; case 2: temp = "fan"; default: break; } mqtt.publish(String(AC_MODE_TOPIC), temp); mqtt.publish(String(AC_TEMPERATURE_TOPIC), String(ac_temperature)); switch (ac_fan_speed) { case 0: temp = "auto"; break; case 1: temp = "high"; break; case 2: temp = "med"; break; case 3: temp = "low"; break; } mqtt.publish(String(AC_FAN_TOPIC), temp); Serial.println("AC State Published."); } void ac_state_callback(String topic, String message) { if (topic.compareTo(String(AC_SET_TEMPERATURE_TOPIC)) == 0) { int new_temp = message.toInt(); if (new_temp >= AC_MIN_TEMPERATURE && new_temp <= AC_MAX_TEMPERATURE) { ac_set_state(ac_mode, new_temp, ac_fan_speed); } } else if (topic.compareTo(String(AC_SET_MODE_TOPIC)) == 0) { if (message.compareTo("off") == 0) { ac_set_state(0, ac_temperature, ac_fan_speed); } else if (message.compareTo("cool") == 0) { ac_set_state(1, ac_temperature, ac_fan_speed); } else if (message.compareTo("fan") == 0) { ac_set_state(2, ac_temperature, ac_fan_speed); } } else if (topic.compareTo(String(AC_SET_FAN_TOPIC)) == 0) { if (message.compareTo("auto") == 0) { ac_set_state(ac_mode, ac_temperature, 0); } else if (message.compareTo("low") == 0) { ac_set_state(ac_mode, ac_temperature, 1); } else if (message.compareTo("med") == 0) { ac_set_state(ac_mode, ac_temperature, 2); } else if (message.compareTo("high") == 0) { ac_set_state(ac_mode, ac_temperature, 3); } } } void ac_set_state(int mode, int temperature, int fan_speed) { ac_mode = mode; ac_temperature = temperature; ac_fan_speed = fan_speed; temperature -= AC_MIN_TEMPERATURE; publish_ac_state(); switch (mode) { case 0: IrSender.sendRaw(ir_code_off, sizeof(ir_code_off) / sizeof(ir_code_off[0]), NEC_KHZ); break; case 1: IrSender.sendRaw(ir_code_cool[fan_speed][temperature], sizeof(ir_code_cool[fan_speed][temperature]) / sizeof(ir_code_cool[fan_speed][0]), NEC_KHZ); 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; } } void publish_env_state() { int errorCode = env_sensor.read(); yield(); switch (errorCode) { case DHTLIB_OK: mqtt.publish(String(AC_ROOM_TEMPERATURE_TOPIC), String(env_sensor.getTemperature())); mqtt_client.loop(); mqtt.publish(String(AC_HUMIDITY_TOPIC), String(env_sensor.getHumidity())); mqtt_client.loop(); break; default: mqtt.publish(String(AC_ROOM_TEMPERATURE_TOPIC), "ERROR"); mqtt_client.loop(); mqtt.publish(String(AC_HUMIDITY_TOPIC), "ERROR"); mqtt_client.loop(); } } 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(); } 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(); } } void lcd_refresh_pd() { if (lcd_current_page == 1) { lcd_refresh(); } } 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)); panel.writeNum("j" + String(i) + ".pco", pwm_states[i]?47829:12710); } break; default: break; } } void lcd_top_bar_update() { panel.writeStr("time.txt", "NO RTC!"); panel.writeNum("server.pic", standalone ? 4 : 5); panel.writeNum("lan.pic", ETH.linkUp() ? 3 : 2); }