ise-iot-oop-v2/src/main.cpp

485 lines
No EOL
17 KiB
C++

#include <main.hpp>
/***********************************************
* 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(&ETH);
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);
}