#include /*********************************************** * Begin Configuration * ***********************************************/ // Analog Card & Current Transformer Configuration bool analogCardAvailable = false; AnalogCard analogCard = AnalogCard(); float voltage = CT_RMS_VOLTAGE; CurrentTransformerCard ct1 = CurrentTransformerCard(&analogCard, 0, &voltage, &adc2current, 5000); CurrentTransformerCard ct2 = CurrentTransformerCard(&analogCard, 1, &voltage, &adc2current, 5000); CurrentTransformerCard ct3 = CurrentTransformerCard(&analogCard, 2, &voltage, &adc2current, 5000); CurrentTransformerCard ct4 = CurrentTransformerCard(&analogCard, 3, &voltage, &adc2current, 5000); CurrentTransformerCard ct5 = CurrentTransformerCard(&analogCard, 4, &voltage, &adc2current, 5000); CurrentTransformerCard ct6 = CurrentTransformerCard(&analogCard, 5, &voltage, &adc2current, 5000); // Remote Variables RemoteVariable pm25_in = RemoteVariable(); RemoteVariable pm25_out = RemoteVariable(); RemoteVariable temp_out = RemoteVariable(); RemoteVariable weather = RemoteVariable(); RemoteVariable pm_switch = RemoteVariable(); RemoteVariable pm_fan_speed = RemoteVariable(); float adc2current(uint16_t adc_val) { return adc_val * 1.0; } #define CT_INTERVAL 5000 // Light Configuration uint8_t row = 4; uint8_t column = 2; const uint8_t light_array[4][2] = { {LIGHT_ROW1_COLUMN1, LIGHT_ROW1_COLUMN2}, {LIGHT_ROW2_COLUMN1, LIGHT_ROW2_COLUMN2}, {LIGHT_ROW3_COLUMN1, LIGHT_ROW3_COLUMN2}, {LIGHT_ROW4_COLUMN1, LIGHT_ROW4_COLUMN2}}; // Air Conditioner Configuration const char *mode_names_daikin[] = {"off", "cool", "fan_only", "dry"}; const char *mode_names_york[] = {"off", "cool", "fan_only"}; const char *fan_speed_names[] = {"auto", "high", "medium", "low"}; AirConditioner ac_daikin = { .max_temperature = 30, .min_temperature = 18, .modes = 4, .mode_names = mode_names_daikin, .fan_speeds = 4, .fan_speed_names = fan_speed_names, .getInfraredCode = &getInfraredCode_daikin}; AirConditioner ac_york = { .max_temperature = 30, .min_temperature = 18, .modes = 3, .mode_names = mode_names_york, .fan_speeds = 4, .fan_speed_names = fan_speed_names, .getInfraredCode = &getInfraredCode_york}; /*********************************************** * End Configuration * ***********************************************/ ESPMegaPRO espmega = ESPMegaPRO(); ISEDisplay iseDisplay = ISEDisplay(&iseDisplayAdapter, &light_array[0][0], row, column); ClimateCard climateCard_daikin = ClimateCard(AIR_CONDITIONER_DAIKIN_IR_PIN, ac_daikin, AIR_CONDITIONER_SENSOR_TYPE, AIR_CONDITIONER_SENSOR_PIN, AIR_CONDITIONER_RMT_CHANNEL0); ClimateCard climateCard_york = ClimateCard(AIR_CONDITIONER_YORK_IR_PIN, ac_york, AC_SENSOR_TYPE_NONE, 0, AIR_CONDITIONER_RMT_CHANNEL1); void handleMqttMessage(char *topic, char *payload) { // Unused for now } void setup() { // ------------ GPIO 2 Factory Reset Check ------------ gpio_config_t gpio_2_conf; gpio_2_conf.intr_type = GPIO_INTR_DISABLE; gpio_2_conf.mode = GPIO_MODE_INPUT; gpio_2_conf.pull_up_en = GPIO_PULLUP_ENABLE; gpio_config(&gpio_2_conf); // If GPIO 2 is pulled low, clear the FRAM then reboot (Reset the device to factory defaults) bool clear_fram = !gpio_get_level(GPIO_NUM_2); // ------------ End GPIO 2 Factory Reset Check ------------ // ------------ Display Pre Initialization Routine ------------ Serial.begin(115200); iseDisplayAdapter.begin(ISE_DISPLAY_BAUD_RATE, SERIAL_8N1, ISE_DISPLAY_RX_PIN, ISE_DISPLAY_TX_PIN); sendExtStopBytes(); iseDisplayAdapter.print("rest"); sendExtStopBytes(); sendStopBytes(); Serial.print("rest"); sendStopBytes(); Serial.print("boot_state.txt=\"Core Initializing . . .\""); sendStopBytes(); // ------------ End Display Pre Initialization Routine ------------ // Give flow of control to OS and scheduler espmega.begin(); // // ------------ Factory Reset Routine ------------ // Disable factory reset for now espmega.inputs.loop(); //set debounce time to 200 for pin 0-7 for (uint16_t i = 0; i < 8; i++){ espmega.inputs.setDebounceTime(i,200); } // if (clear_fram) // { // Serial.print("boot_state.txt=\"Factory Resetting . . .\""); // sendStopBytes(); // for (uint16_t i = 0; i < 32768; i++) // { // espmega.fram.write8(i, 0); // } // esp_restart(); // } // // ------------ End Factory Reset Routine ------------ // ------------ IoT Module Initialization Routine ------------ Serial.print("boot_state.txt=\"IoT Initializing . . .\""); sendStopBytes(); espmega.enableIotModule(); ETH.begin(); espmega.iot->bindEthernetInterface(Ð); Serial.print("boot_state.txt=\"Network Initializing . . .\""); sendStopBytes(); espmega.iot->loadNetworkConfig(); espmega.iot->connectNetwork(); Serial.print("boot_state.txt=\"MQTT Initializing . . .\""); sendStopBytes(); espmega.iot->loadMqttConfig(); espmega.iot->connectToMqtt(); Serial.print("boot_state.txt=\"Display Initializing . . .\""); espmega.enableWebServer(80); // ------------ End IoT Module Initialization Routine ------------ // ------------ Display Post Initialization Routine ------------ espmega.enableInternalDisplay(&Serial); // ------------ End Display Post Initialization Routine ------------ // ------------ Inputs and Outputs Initialization Routine ------------ espmega.inputs.registerCallback(on_pin_change); espmega.outputs.setAutoSaveToFRAM(true); // Set value of pin 0-12 to 4095 for (uint8_t i = 0; i < 13; i++) { espmega.outputs.setValue(i, 4095); } // ------------ End Inputs and Outputs Initialization Routine ------------ espmega.outputs.setState(12, true); espmega.outputs.setValue(12,4095); espmega.installCard(2, &climateCard_daikin); climateCard_daikin.bindFRAM(&espmega.fram, 5000); climateCard_daikin.loadStateFromFRAM(); climateCard_daikin.setFRAMAutoSave(true); espmega.display->bindClimateCard(&climateCard_daikin); // Current Transformers espmega.installCard(4, &analogCard); espmega.installCard(5, &ct1); ct1.bindFRAM(&espmega.fram, 6000); espmega.iot->registerCard(5); espmega.installCard(6, &ct2); ct2.bindFRAM(&espmega.fram, 6100); espmega.iot->registerCard(6); espmega.installCard(7, &ct3); ct3.bindFRAM(&espmega.fram, 6200); espmega.iot->registerCard(7); espmega.installCard(8, &ct4); ct4.bindFRAM(&espmega.fram, 6300); espmega.iot->registerCard(8); espmega.installCard(9, &ct5); ct5.bindFRAM(&espmega.fram, 6400); espmega.iot->registerCard(9); espmega.installCard(10, &ct6); ct6.bindFRAM(&espmega.fram, 6500); espmega.iot->registerCard(10); // auto binded_display_update_on_pm25_out = std::bind(&display_update,0,std::placeholders::_1); // auto binded_display_update_on_pm25_in = std::bind(&display_update,1,std::placeholders::_1); // auto binded_display_update_on_temp_out = std::bind(&display_update,2,std::placeholders::_1); // auto binded_display_update_on_weather = std::bind(&display_update,3,std::placeholders::_1); // auto binded_display_update_on_pm_switch = std::bind(&display_update,4,std::placeholders::_1); // auto binded_display_update_on_pm_fan_speed = std::bind(&display_update,5,std::placeholders::_1); // ------------ Climate Cards Initialization Routine ------------ ESP_LOGD("ISE OS", "Setting up climate cards"); // Daikin Climate Card ESP_LOGD("ISE OS", "Installing daikin climate card"); espmega.installCard(2, &climateCard_daikin); climateCard_daikin.bindFRAM(&espmega.fram, 5000); climateCard_daikin.loadStateFromFRAM(); climateCard_daikin.setFRAMAutoSave(true); // Bind daikin climate card to the internal display espmega.display->bindClimateCard(&climateCard_daikin); // York Climate Card ESP_LOGD("ISE OS", "Installing york climate card"); espmega.installCard(10, &climateCard_york); climateCard_york.bindFRAM(&espmega.fram, 5005); climateCard_york.loadStateFromFRAM(); climateCard_york.setFRAMAutoSave(true); // ------------ End Climate Cards Initialization Routine ------------ // ------------ Current Transformer Cards Initialization Routine ------------ ESP_LOGD("ISE OS", "Installing current transformer cards"); // First try to install the analog card analogCardAvailable = espmega.installCard(4, &analogCard); // If the analog card is available, install the current transformer cards // If the analog card is not available, current transformer cards will not be installed // Unless CT_FORCE_ENABLE is set to true // This is to prevent soft locking the device when the device tries to read from an ADC channel that does not exist if (analogCardAvailable || CT_FORCE_ENABLE) { ESP_LOGV("ISE OS", "Analog card available, installing current transformer cards"); espmega.installCard(5, &ct1); ct1.bindFRAM(&espmega.fram, 5010); espmega.installCard(6, &ct2); ct2.bindFRAM(&espmega.fram, 5020); espmega.installCard(7, &ct3); ct3.bindFRAM(&espmega.fram, 5030); espmega.installCard(8, &ct4); ct4.bindFRAM(&espmega.fram, 5040); espmega.installCard(9, &ct5); ct5.bindFRAM(&espmega.fram, 5050); espmega.installCard(10, &ct5); ct6.bindFRAM(&espmega.fram, 5060); espmega.iot->registerCard(5); espmega.iot->registerCard(6); espmega.iot->registerCard(7); espmega.iot->registerCard(8); espmega.iot->registerCard(9); espmega.iot->registerCard(10); } else { ESP_LOGE("ISE OS", "Analog card not available, current transformer cards cannot and will not be installed."); } ESP_LOGD("ISE OS", "Registering Current Transformer Cards with IoT Module"); // ------------ End Current Transformer Cards Initialization Routine ------------ // ------------ Remote Variables Initialization Routine ------------ pm25_out.registerCallback(&pm25outupdatedisplay); pm25_in.registerCallback(&pm25inupdatedisplay); temp_out.registerCallback(&tempoutupdatedisplay); weather.registerCallback(&weatherupdatedisplay); pm_switch.registerCallback(&pmswitchupdatedisplay); pm_fan_speed.registerCallback(&pmfanspeedupdatedisplay); // PM2.5 PPM Remote Variable pm25_out.begin(6, "/aqi/value", espmega.iot, true, "/aqi/request_value"); // Temperature Remote Variable temp_out.begin(6, "/temp/value", espmega.iot, true, "/temp/request_value"); // Weather Remote Variable weather.begin(45, "/weather/value", espmega.iot, true, "/weather/request_value"); // PM2.5 PPM Remote Variable pm25_in.begin(6, "/pm/value", espmega.iot, true, "/pm/request_value"); // Air Purifier Switch Remote Variable pm_switch.begin(6, "/pm/switch_state", espmega.iot, true, "/pm/request_switch_state"); pm_switch.enableSetValue("/pm/set_switch_state"); // Air Purifier Fan Speed Remote Variable pm_fan_speed.begin(6, "/pm/fan_speed", espmega.iot, true, "/pm/request_fan_speed"); pm_fan_speed.enableSetValue("/pm/set_fan_speed"); // ------------ End Remote Variables Initialization Routine ------------ // ------------ IoT Card Registration Routine ------------ espmega.iot->registerCard(0); // Register the Input Card espmega.iot->registerCard(1); // Register the Output Card espmega.iot->registerCard(2); // Register the Climate Card Daikin espmega.iot->registerCard(10); // Register the Climate Card York // ------------ End IoT Card Registration Routine ------------ // ------------ External Display Initialization Routine ------------ auto bindedGetTime = std::bind(&ESPMegaPRO::getTime, &espmega); iseDisplay.begin(&espmega.inputs, &espmega.outputs, &climateCard_daikin, &climateCard_york, &pm_switch, &pm_fan_speed); espmega.iot->registerRelativeMqttCallback(&handleMqttMessage); iseDisplay.registerPageChangeCallback(&handlePageChange); // ------------ End External Display Initialization Routine ------------ } void pm25outupdatedisplay(char *value) { iseDisplay.updatePMoutside(get_pm25_out()); } void pm25inupdatedisplay(char *value) { iseDisplay.updatePMinside(get_pm25_in()); } void tempoutupdatedisplay(char *value) { iseDisplay.updateTempOutside(get_temp_out()); } void weatherupdatedisplay(char *value) { iseDisplay.updateWeather(weather.getValue()); } void pmfanspeedupdatedisplay(char *value) { iseDisplay.updateAirPurifierState(); } void pmswitchupdatedisplay(char *value) { ESP_LOGI("PM switch", "getting PM switch state from MQTT: %d", pm_switch.getValue()); iseDisplay.updateAirPurifierState(); ESP_LOGI("PM switch", "toggling PM switch state from: %d to %d", pm_switch.getValue(), !pm_switch.getValue()); } void loop() { espmega.loop(); iseDisplay.loop(); // Update the time every 15 seconds static uint32_t last_time_updated = 0; if (millis() - last_time_updated > 15000) { rtctime_t time = espmega.getTime(); iseDisplay.updateDateTimeText(time); last_time_updated = millis(); } } void on_pin_change(uint8_t pin, uint8_t value) { switch (pin) { case LIGHT_ROW1_COLUMN1_IN: espmega.outputs.setState(LIGHT_ROW1_COLUMN1, !espmega.outputs.getState(LIGHT_ROW1_COLUMN1)); break; case LIGHT_ROW1_COLUMN2_IN: espmega.outputs.setState(LIGHT_ROW1_COLUMN2, !espmega.outputs.getState(LIGHT_ROW1_COLUMN2)); break; case LIGHT_ROW2_COLUMN1_IN: espmega.outputs.setState(LIGHT_ROW2_COLUMN1, !espmega.outputs.getState(LIGHT_ROW2_COLUMN1)); break; case LIGHT_ROW2_COLUMN2_IN: espmega.outputs.setState(LIGHT_ROW2_COLUMN2, !espmega.outputs.getState(LIGHT_ROW2_COLUMN2)); break; case LIGHT_ROW3_COLUMN1_IN: espmega.outputs.setState(LIGHT_ROW3_COLUMN1, !espmega.outputs.getState(LIGHT_ROW3_COLUMN1)); break; case LIGHT_ROW3_COLUMN2_IN: espmega.outputs.setState(LIGHT_ROW3_COLUMN2, !espmega.outputs.getState(LIGHT_ROW3_COLUMN2)); break; case LIGHT_ROW4_COLUMN1_IN: espmega.outputs.setState(LIGHT_ROW4_COLUMN1, !espmega.outputs.getState(LIGHT_ROW4_COLUMN1)); break; case LIGHT_ROW4_COLUMN2_IN: espmega.outputs.setState(LIGHT_ROW4_COLUMN2, !espmega.outputs.getState(LIGHT_ROW4_COLUMN2)); break; case COMPUTER_DESK_SWITCH_IN: espmega.outputs.setState(COMPUTER_DESK_SWITCH_OUT, !espmega.outputs.getState(COMPUTER_DESK_SWITCH_OUT)); break; } } uint16_t get_pm25_out() { uint16_t pm25_out_value = 0; // Read PM2.5 PPM from home assistant pm25_out_value = atoi(pm25_out.getValue()); ESP_LOGI("PM2.5", "getting PM2.5 PPM from MQTT: %d", pm25_out_value); return pm25_out_value; } uint16_t get_pm25_in() { uint16_t pm25_in_value = 0; // Read PM2.5 PPM from home assistant pm25_in_value = atoi(pm25_in.getValue()); ESP_LOGI("PM2.5", "getting PM2.5 PPM from MQTT: %d", pm25_in_value); return pm25_in_value; } float get_temp_out() { float temp_out_value = 0; // Read temperature from home assistant temp_out_value = atof(temp_out.getValue()); ESP_LOGI("Temperature", "getting Temperature from MQTT: %f", temp_out_value); return temp_out_value; } uint8_t get_pm_fanspeed() { uint8_t pm_fan_speed_value = 0; // Read PM2.5 fan speed from home assistant pm_fan_speed_value = (int)atof(pm_fan_speed.getValue()); ESP_LOGI("PM fan speed", "getting PM2.5 PPM from MQTT: %d", pm_fan_speed_value); return pm_fan_speed_value; } /** * @brief Get the PM switch state from home assistant * @return true if the PM switch is on, false if the PM switch is off */ bool get_pm_switch() { ESP_LOGI("PM switch", "getting PM switch state from MQTT: %d", pm_switch.getValue()); bool is_pm_switch_on = strcmp(pm_switch.getValue(), "on") == 0; return is_pm_switch_on; } void toggle_pm_switch() { bool is_pm_switch_on = get_pm_switch(); ESP_LOGI("PM switch", "toggling PM switch state from: %d to %d", is_pm_switch_on, !is_pm_switch_on); pm_switch.setValue(is_pm_switch_on ? "0" : "1"); } void set_pm_fanspeed(uint8_t speed) { ESP_LOGI("PM fan speed", "setting PM fan speed to: %d", speed); char buffer[4]; itoa(speed, buffer, DEC); pm_fan_speed.setValue(buffer); } void handlePageChange(uint8_t page) { ESP_LOGI("Page", "Page change to: %d", page); rtctime_t time = espmega.getTime(); iseDisplay.updateDateTimeText(time); switch (page) { case PAGE_STANDBY: iseDisplay.updateLightGroupStatePageStandby(); iseDisplay.updateAirPurifierStateStandby(); break; case PAGE_DASHBOARD: iseDisplay.updateLightGroupStatePageDashboard(); iseDisplay.updateAirPurifierState(); break; default: break; } iseDisplay.updatePMoutside(get_pm25_out()); iseDisplay.updatePMinside(get_pm25_in()); iseDisplay.updateWeather(weather.getValue()); iseDisplay.updateTempOutside(get_temp_out()); iseDisplay.updateACState(); } void sendStopBytes() { Serial.write(0xFF); Serial.write(0xFF); Serial.write(0xFF); } void sendExtStopBytes() { iseDisplayAdapter.write(0xFF); iseDisplayAdapter.write(0xFF); iseDisplayAdapter.write(0xFF); }