/** * @copyright Utilitech AS 2023 * License: Fair Source * */ #include "HwTools.h" bool HwTools::applyBoardConfig(uint8_t boardType, GpioConfig& gpioConfig, MeterConfig& meterConfig, uint8_t hanPin) { #if defined(CONFIG_IDF_TARGET_ESP32S2) switch(boardType) { case 5: // Pow-K+ meterConfig.txPin = 9; case 7: // Pow-U+ case 6: // Pow-P1 meterConfig.rxPin = 16; gpioConfig.apPin = 0; gpioConfig.ledPinRed = 13; gpioConfig.ledPinGreen = 14; gpioConfig.ledRgbInverted = true; gpioConfig.vccPin = 10; gpioConfig.vccResistorGnd = 22; gpioConfig.vccResistorVcc = 33; gpioConfig.ledDisablePin = 6; return true; case 51: // Wemos S2 mini gpioConfig.ledPin = 15; gpioConfig.ledInverted = false; gpioConfig.apPin = 0; meterConfig.rxPin = hanPin > 0 ? hanPin : 18; if(meterConfig.rxPin != 18) { gpioConfig.vccPin = 18; gpioConfig.vccResistorGnd = 45; gpioConfig.vccResistorVcc = 10; } return true; case 50: // Generic ESP32-S2 meterConfig.rxPin = hanPin > 0 ? hanPin : 18; return true; } #elif defined(CONFIG_IDF_TARGET_ESP32C3) switch(boardType) { case 8: // dbeinder: HAN mosquito meterConfig.rxPin = 7; meterConfig.rxPinPullup = false; gpioConfig.apPin = 9; gpioConfig.ledRgbInverted = true; gpioConfig.ledPinRed = 5; gpioConfig.ledPinGreen = 6; gpioConfig.ledPinBlue = 4; return true; case 71: // ESP32-C3-DevKitM-1 gpioConfig.apPin = 9; case 70: // Generic ESP32-C3 meterConfig.rxPin = hanPin > 0 ? hanPin : 7; return true; } #elif defined(CONFIG_IDF_TARGET_ESP32S3) switch(boardType) { case 80: // Generic ESP32-S3 meterConfig.rxPin = hanPin > 0 ? hanPin : 18; return true; } #elif defined(ESP32) switch(boardType) { case 241: // LilyGO T-ETH-POE gpioConfig.apPin = 0; meterConfig.rxPin = hanPin > 0 ? hanPin : 39; gpioConfig.ledPin = 2; gpioConfig.ledInverted = true; return true; case 242: // M5 PoESP32 meterConfig.rxPin = hanPin > 0 ? hanPin : 16; return true; case 243: // WT32-ETH01 meterConfig.rxPin = hanPin > 0 ? hanPin : 39; return true; case 245: // wESP32 gpioConfig.apPin = 0; meterConfig.rxPin = hanPin > 0 ? hanPin : 39; case 201: // D32 meterConfig.rxPin = hanPin > 0 ? hanPin : 16; gpioConfig.apPin = 4; gpioConfig.ledPin = 5; gpioConfig.ledInverted = true; return true; case 202: // Feather case 203: // DevKitC case 200: // ESP32 meterConfig.rxPin = hanPin > 0 ? hanPin : 16; gpioConfig.ledPin = 2; gpioConfig.ledInverted = false; return true; } #elif defined(ESP8266) switch(boardType) { case 2: // spenceme gpioConfig.vccBootLimit = 32; meterConfig.rxPin = 3; gpioConfig.apPin = 0; gpioConfig.ledPin = 2; gpioConfig.ledInverted = true; gpioConfig.tempSensorPin = 5; return true; case 0: // roarfred meterConfig.rxPin = 3; gpioConfig.apPin = 0; gpioConfig.ledPin = 2; gpioConfig.ledInverted = true; gpioConfig.tempSensorPin = 5; return true; case 1: // Arnio Kamstrup case 3: // Pow-K UART0 case 4: // Pow-U UART0 meterConfig.rxPin = 3; gpioConfig.apPin = 0; gpioConfig.ledPin = 2; gpioConfig.ledInverted = true; gpioConfig.ledPinRed = 13; gpioConfig.ledPinGreen = 14; gpioConfig.ledRgbInverted = true; return true; case 5: // Pow-K GPIO12 case 7: // Pow-U GPIO12 meterConfig.rxPin = 12; gpioConfig.apPin = 0; gpioConfig.ledPin = 2; gpioConfig.ledInverted = true; gpioConfig.ledPinRed = 13; gpioConfig.ledPinGreen = 14; gpioConfig.ledRgbInverted = true; return true; case 101: // D1 meterConfig.rxPin = hanPin > 0 ? hanPin : 5; gpioConfig.apPin = 4; gpioConfig.ledPin = 2; gpioConfig.ledInverted = true; gpioConfig.vccMultiplier = 1100; return true; case 100: // ESP8266 meterConfig.rxPin = hanPin > 0 ? hanPin : 3; gpioConfig.ledPin = 2; gpioConfig.ledInverted = true; return true; } #endif return false; } void HwTools::setup(SystemConfig* sys, GpioConfig* config) { this->boardType = sys->boardType; this->tempSensorInit = false; if(sensorApi != NULL) delete sensorApi; if(oneWire != NULL) delete oneWire; if(config->tempSensorPin > 0 && config->tempSensorPin < 40) { pinMode(config->tempSensorPin, INPUT); tempPin = config->tempSensorPin; } else { tempPin = config->tempSensorPin = 0xFF; } #if defined(CONFIG_IDF_TARGET_ESP32S2) analogReadResolution(13); analogRange = 8192; analogSetAttenuation(ADC_11db); #elif defined(ESP32) analogReadResolution(12); analogRange = 4096; analogSetAttenuation(ADC_6db); #endif if(config->vccPin > 0 && config->vccPin < 40) { #if defined(CONFIG_IDF_TARGET_ESP32S2) getAdcChannel(config->vccPin, voltAdc); if(voltAdc.unit != 0xFF) { if(voltAdc.unit == ADC_UNIT_1) { voltAdcChar = (esp_adc_cal_characteristics_t*) calloc(1, sizeof(esp_adc_cal_characteristics_t)); esp_adc_cal_value_t adcVal = esp_adc_cal_characterize((adc_unit_t) voltAdc.unit, ADC_ATTEN_DB_11, ADC_WIDTH_BIT_13, 1100, voltAdcChar); adc1_config_channel_atten((adc1_channel_t) voltAdc.channel, ADC_ATTEN_DB_11); } else if(voltAdc.unit == ADC_UNIT_2) { voltAdcChar = (esp_adc_cal_characteristics_t*) calloc(1, sizeof(esp_adc_cal_characteristics_t)); esp_adc_cal_value_t adcVal = esp_adc_cal_characterize((adc_unit_t) voltAdc.unit, ADC_ATTEN_DB_11, ADC_WIDTH_BIT_13, 1100, voltAdcChar); adc2_config_channel_atten((adc2_channel_t) voltAdc.channel, ADC_ATTEN_DB_11); } } #elif defined(ESP32) getAdcChannel(config->vccPin, voltAdc); if(voltAdc.unit != 0xFF) { if(voltAdc.unit == ADC_UNIT_1) { voltAdcChar = (esp_adc_cal_characteristics_t*) calloc(1, sizeof(esp_adc_cal_characteristics_t)); esp_adc_cal_value_t adcVal = esp_adc_cal_characterize((adc_unit_t) voltAdc.unit, ADC_ATTEN_DB_6, ADC_WIDTH_BIT_12, 1100, voltAdcChar); adc1_config_channel_atten((adc1_channel_t) voltAdc.channel, ADC_ATTEN_DB_6); } else if(voltAdc.unit == ADC_UNIT_2) { voltAdcChar = (esp_adc_cal_characteristics_t*) calloc(1, sizeof(esp_adc_cal_characteristics_t)); esp_adc_cal_value_t adcVal = esp_adc_cal_characterize((adc_unit_t) voltAdc.unit, ADC_ATTEN_DB_6, ADC_WIDTH_BIT_12, 1100, voltAdcChar); adc2_config_channel_atten((adc2_channel_t) voltAdc.channel, ADC_ATTEN_DB_6); } } #else pinMode(config->vccPin, INPUT); #endif vccPin = config->vccPin; } else { voltAdc.unit = 0xFF; voltAdc.channel = 0xFF; vccPin = config->vccPin = 0xFF; } vccOffset = config->vccOffset / 100.0; vccMultiplier = config->vccMultiplier / 1000.0; vccGnd_r = config->vccResistorGnd; vccVcc_r = config->vccResistorVcc; if(config->tempAnalogSensorPin > 0 && config->tempAnalogSensorPin < 40) { pinMode(config->tempAnalogSensorPin, INPUT); atempPin = config->tempAnalogSensorPin; } else { atempPin = config->tempAnalogSensorPin = 0xFF; } if(config->ledPin > 0 && config->ledPin < 40) { pinMode(config->ledPin, OUTPUT); ledPin = config->ledPin; ledInvert = config->ledInverted; ledOff(LED_INTERNAL); } else { ledPin = config->ledPin = 0xFF; } if(config->ledPinRed > 0 && config->ledPinRed < 40) { pinMode(config->ledPinRed, OUTPUT); redPin = config->ledPinRed; ledOff(LED_RED); } else { redPin = config->ledPinRed = 0xFF; } if(config->ledPinGreen > 0 && config->ledPinGreen < 40) { pinMode(config->ledPinGreen, OUTPUT); greenPin = config->ledPinGreen; ledOff(LED_GREEN); } else { greenPin = config->ledPinGreen = 0xFF; } if(config->ledPinBlue > 0 && config->ledPinBlue < 40) { pinMode(config->ledPinBlue, OUTPUT); bluePin = config->ledPinBlue; ledOff(LED_BLUE); } else { bluePin = config->ledPinBlue = 0xFF; } rgbInvert = config->ledRgbInverted; if(config->ledDisablePin > 0 && config->ledDisablePin < 40) { pinMode(config->ledDisablePin, OUTPUT_OPEN_DRAIN); ledDisablePin = config->ledDisablePin; ledBehaviour = config->ledBehaviour; setBootSuccessful(false); } } void HwTools::getAdcChannel(uint8_t pin, AdcConfig& config) { config.unit = 0xFF; config.channel = 0xFF; #if defined(ESP32) switch(pin) { case ADC1_CHANNEL_0_GPIO_NUM: config.unit = ADC_UNIT_1; config.channel = ADC1_CHANNEL_0; break; case ADC1_CHANNEL_1_GPIO_NUM: config.unit = ADC_UNIT_1; config.channel = ADC1_CHANNEL_1; break; case ADC1_CHANNEL_2_GPIO_NUM: config.unit = ADC_UNIT_1; config.channel = ADC1_CHANNEL_2; break; case ADC1_CHANNEL_3_GPIO_NUM: config.unit = ADC_UNIT_1; config.channel = ADC1_CHANNEL_3; break; case ADC1_CHANNEL_4_GPIO_NUM: config.unit = ADC_UNIT_1; config.channel = ADC1_CHANNEL_4; break; #if defined(ADC1_CHANNEL_5_GPIO_NUM) case ADC1_CHANNEL_5_GPIO_NUM: config.unit = ADC_UNIT_1; config.channel = ADC1_CHANNEL_5; break; #endif #if defined(ADC1_CHANNEL_6_GPIO_NUM) case ADC1_CHANNEL_6_GPIO_NUM: config.unit = ADC_UNIT_1; config.channel = ADC1_CHANNEL_6; break; #endif #if defined(ADC1_CHANNEL_7_GPIO_NUM) case ADC1_CHANNEL_7_GPIO_NUM: config.unit = ADC_UNIT_1; config.channel = ADC1_CHANNEL_7; break; #endif #if defined(ADC1_CHANNEL_8_GPIO_NUM) case ADC1_CHANNEL_8_GPIO_NUM: config.unit = ADC_UNIT_1; config.channel = ADC1_CHANNEL_8; break; #endif #if defined(ADC1_CHANNEL_9_GPIO_NUM) case ADC1_CHANNEL_9_GPIO_NUM: config.unit = ADC_UNIT_1; config.channel = ADC1_CHANNEL_9; break; #endif #if defined(ADC2_CHANNEL_0_GPIO_NUM) case ADC2_CHANNEL_0_GPIO_NUM: config.unit = ADC_UNIT_2; config.channel = ADC2_CHANNEL_0; break; #endif #if defined(ADC2_CHANNEL_1_GPIO_NUM) case ADC2_CHANNEL_1_GPIO_NUM: config.unit = ADC_UNIT_2; config.channel = ADC2_CHANNEL_1; break; #endif #if defined(ADC2_CHANNEL_2_GPIO_NUM) case ADC2_CHANNEL_2_GPIO_NUM: config.unit = ADC_UNIT_2; config.channel = ADC2_CHANNEL_2; break; #endif #if defined(ADC2_CHANNEL_3_GPIO_NUM) case ADC2_CHANNEL_3_GPIO_NUM: config.unit = ADC_UNIT_2; config.channel = ADC2_CHANNEL_3; break; #endif #if defined(ADC2_CHANNEL_4_GPIO_NUM) case ADC2_CHANNEL_4_GPIO_NUM: config.unit = ADC_UNIT_2; config.channel = ADC2_CHANNEL_4; break; #endif #if defined(ADC2_CHANNEL_5_GPIO_NUM) case ADC2_CHANNEL_5_GPIO_NUM: config.unit = ADC_UNIT_2; config.channel = ADC2_CHANNEL_5; break; #endif #if defined(ADC2_CHANNEL_6_GPIO_NUM) case ADC2_CHANNEL_6_GPIO_NUM: config.unit = ADC_UNIT_2; config.channel = ADC2_CHANNEL_6; break; #endif #if defined(ADC2_CHANNEL_7_GPIO_NUM) case ADC2_CHANNEL_7_GPIO_NUM: config.unit = ADC_UNIT_2; config.channel = ADC2_CHANNEL_7; break; #endif #if defined(ADC2_CHANNEL_8_GPIO_NUM) case ADC2_CHANNEL_8_GPIO_NUM: config.unit = ADC_UNIT_2; config.channel = ADC2_CHANNEL_8; break; #endif #if defined(ADC2_CHANNEL_9_GPIO_NUM) case ADC2_CHANNEL_9_GPIO_NUM: config.unit = ADC_UNIT_2; config.channel = ADC2_CHANNEL_9; break; #endif } #endif } float HwTools::getVcc() { float volts = 0.0; if(vccPin != 0xFF) { #if defined(ESP32) if(voltAdc.unit != 0xFF) { uint32_t x = 0; for (int i = 0; i < 10; i++) { if(voltAdc.unit == ADC_UNIT_1) { x += adc1_get_raw((adc1_channel_t) voltAdc.channel); } else if(voltAdc.unit == ADC_UNIT_2) { int v = 0; #if defined(CONFIG_IDF_TARGET_ESP32S2) adc2_get_raw((adc2_channel_t) voltAdc.channel, ADC_WIDTH_BIT_13, &v); #elif defined(CONFIG_IDF_TARGET_ESP32) adc2_get_raw((adc2_channel_t) voltAdc.channel, ADC_WIDTH_BIT_12, &v); #endif x += v; } } x = x / 10; uint32_t voltage = esp_adc_cal_raw_to_voltage(x, voltAdcChar); volts = voltage / 1000.0; } else { uint32_t x = 0; for (int i = 0; i < 10; i++) { x += analogRead(vccPin); } volts = (x * 3.3) / 10.0 / analogRange; } #else uint32_t x = 0; for (int i = 0; i < 10; i++) { x += analogRead(vccPin); } volts = (x * 3.3) / 10.0 / analogRange; #endif } else { } if(volts == 0.0) { #if defined(ESP8266) volts = ESP.getVcc() / 1024.0; #else return 0.0; #endif } if(vccGnd_r > 0 && vccVcc_r > 0) volts *= ((float) (vccGnd_r + vccVcc_r) / vccGnd_r); if(vccOffset != 0.0) volts += vccOffset; if(vccMultiplier != 0.0) volts *= vccMultiplier; return volts; } uint8_t HwTools::getTempSensorCount() { return sensorCount; } TempSensorData* HwTools::getTempSensorData(uint8_t i) { if(i < sensorCount) { return tempSensors[i]; } return NULL; } bool HwTools::updateTemperatures() { if(tempPin != 0xFF) { if(!tempSensorInit) { oneWire = new OneWire(tempPin); sensorApi = new DallasTemperature(this->oneWire); sensorApi->begin(); delay(100); tempSensorInit = true; DeviceAddress addr; sensorApi->requestTemperatures(); int c = sensorApi->getDeviceCount(); if(this->tempSensors != NULL) { delete this->tempSensors; } this->tempSensors = new TempSensorData*[c]; for(int i = 0; i < c; i++) { bool found = false; sensorApi->getAddress(addr, i); float t = sensorApi->getTempC(addr); for(int x = 0; x < sensorCount; x++) { TempSensorData *data = tempSensors[x]; if(isSensorAddressEqual(data->address, addr)) { found = true; data->lastRead = t; if(t > -85) { data->changed = data->lastValidRead != t; data->lastValidRead = t; } } } if(!found) { TempSensorData *data = new TempSensorData(); memcpy(data->address, addr, 8); data->lastRead = t; if(t > -85) { data->changed = data->lastValidRead != t; data->lastValidRead = t; } tempSensors[sensorCount++] = data; } yield(); } } else { if(sensorCount > 0) { sensorApi->requestTemperatures(); for(int x = 0; x < sensorCount; x++) { TempSensorData *data = tempSensors[x]; float t = sensorApi->getTempC(data->address); data->lastRead = t; if(t > -85) { data->changed = data->lastValidRead != t; data->lastValidRead = t; } } } } return true; } return false; } bool HwTools::isSensorAddressEqual(uint8_t a[8], uint8_t b[8]) { for(int i = 0; i < 8; i++) { if(a[i] != b[i]) return false; } return true; } float HwTools::getTemperature() { uint8_t c = 0; float ret = 0; float analogTemp = getTemperatureAnalog(); if(analogTemp != DEVICE_DISCONNECTED_C) { ret += analogTemp; c++; } for(int x = 0; x < sensorCount; x++) { TempSensorData data = *tempSensors[x]; if(data.lastValidRead > -85) { ret += data.lastValidRead; c++; } } return c == 0 ? DEVICE_DISCONNECTED_C : ret/c; } float HwTools::getTemperatureAnalog() { if(atempPin != 0xFF) { float adcCalibrationFactor = 1.06587; int volts = ((float) analogRead(atempPin) / analogRange) * 3.3; return ((volts * adcCalibrationFactor) - 0.4) / 0.0195; } return DEVICE_DISCONNECTED_C; } int HwTools::getWifiRssi() { int rssi = WiFi.RSSI(); return isnan(rssi) ? -100.0 : rssi; } void HwTools::setBootSuccessful(bool value) { if(bootSuccessful && value) return; bootSuccessful = value; if(ledDisablePin > 0 && ledDisablePin < 40) { switch(ledBehaviour) { case LED_BEHAVIOUR_ERROR_ONLY: case LED_BEHAVIOUR_OFF: digitalWrite(ledDisablePin, LOW); break; case LED_BEHAVIOUR_BOOT: if(bootSuccessful) { digitalWrite(ledDisablePin, LOW); } else { digitalWrite(ledDisablePin, HIGH); } break; default: digitalWrite(ledDisablePin, HIGH); } } } bool HwTools::ledOn(uint8_t color) { if(ledBehaviour == LED_BEHAVIOUR_OFF) return false; if(ledBehaviour == LED_BEHAVIOUR_ERROR_ONLY && color != LED_RED) return false; if(ledBehaviour == LED_BEHAVIOUR_BOOT && color != LED_RED && bootSuccessful) return false; if(color == LED_INTERNAL) { return writeLedPin(color, ledInvert ? LOW : HIGH); } else { return writeLedPin(color, rgbInvert ? LOW : HIGH); } } bool HwTools::ledOff(uint8_t color) { if(color == LED_INTERNAL) { return writeLedPin(color, ledInvert ? HIGH : LOW); } else { return writeLedPin(color, rgbInvert ? HIGH : LOW); } } bool HwTools::ledBlink(uint8_t color, uint8_t blink) { for(int i = 0; i < blink; i++) { if(!ledOn(color)) return false; delay(75); ledOff(color); if(i != blink) delay(75); } return true; } bool HwTools::writeLedPin(uint8_t color, uint8_t state) { switch(color) { case LED_INTERNAL: { if(ledPin != 0xFF) { digitalWrite(ledPin, state); return true; } else { return false; } break; } case LED_RED: { if(redPin != 0xFF) { digitalWrite(redPin, state); return true; } else { return false; } break; } case LED_GREEN: { if(greenPin != 0xFF) { digitalWrite(greenPin, state); return true; } else { return false; } break; } case LED_BLUE: { if(bluePin != 0xFF) { digitalWrite(bluePin, state); return true; } else { return false; } break; } case LED_YELLOW: { if(redPin != 0xFF && greenPin != 0xFF) { digitalWrite(redPin, state); digitalWrite(greenPin, state); return true; } else { return false; } break; } } return false; } bool HwTools::isVoltageOptimal(float range) { if(boardType >= 1 && boardType <= 8 && maxVcc > 2.8) { // BUS-Power boards unsigned long now = millis(); if(now - lastVccRead > 250) { vcc = getVcc(); lastVccRead = now; } if(vcc > 3.4 || vcc < 2.8) { maxVcc = 0; // Voltage is outside the operating range, we have to assume voltage is OK } else if(vcc > maxVcc) { maxVcc = vcc; } else { float diff = min(maxVcc, (float) 3.3) - vcc; return diff < range; } } return true; } uint8_t HwTools::getBoardType() { return boardType; }