diff --git a/.github/workflows/release3_beta.yml b/.github/workflows/release3_beta.yml new file mode 100644 index 00000000..41ba84c4 --- /dev/null +++ b/.github/workflows/release3_beta.yml @@ -0,0 +1,187 @@ +name: Beta Release V2.91 + +# # # # # # # # # # # +# To create new Beta version and upload so web server, just commit with a message with the format "Beta v*.*.*" (Beta v1.0.1, Beta v1.1.12, Beta v2.1.123, etc) +# # # # # # # # # # # + +on: + push: + branches: + - development + paths-ignore: + - '.github/workflows/release3_beta.yml' # Ignore changes in the workflow file itself + # Only run when commit message contains "Beta" + # Use 'contains' with 'github.event.head_commit.message' + if: contains(github.event.head_commit.message, 'Beta') || github.event_name == 'workflow_dispatch' + workflow_dispatch: + +jobs: + build_beta: + name: Create Beta Release + + runs-on: ubuntu-latest + + strategy: + matrix: + environment: + # - esp32dev + # - esp32dev_OLED + - TTGO_TDISPLAY + - TTGO_TDISPLAY_SANDWICH + - TDISPLAY_S3 + # - esp32dev_ST7789_240x320 + + env: + CHIP_FAMILY: ${{ matrix.environment == 'TDISPLAY_S3_beta' && 'ESP32-S3' || 'ESP32' }} + + timeout-minutes: 30 + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Show environments + run: | + echo + + + - name: Check if version exists + id: check_version + run: | + commit_message=$(git log --format=%B -n 1 $GITHUB_SHA) + version_regex="Beta v([0-9]+\.[0-9]+\.[0-9]+)" + if [[ $commit_message =~ $version_regex ]]; then + echo "Version found: ${BASH_REMATCH[1]}" + echo "::set-output name=VERSION::${BASH_REMATCH[1]}" + else + echo "No valid version found in commit message. Exiting..." + exit 1 + fi + + - name: Determine chipFamily + id: determine_chip_family + run: | + case "${{ matrix.environment }}" in + "esp32dev_beta") + CHIP_FAMILY="ESP32";; + "TDISPLAY_S3_beta") + CHIP_FAMILY="ESP32-S3";; + # Add more cases for other environments as needed + *) + CHIP_FAMILY="UNKNOWN";; + esac + echo "::set-output name=chipFamily::${CHIP_FAMILY}" + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v1 + with: + python-version: ${{ matrix.python-version }} + + - name: Install dependencies + if: steps.check_version.outputs.VERSION != '' + run: | + python -m pip install --upgrade pip + pip install -U platformio + platformio update + + - name: Get current date + id: date + run: | + echo "::set-output name=date::$(date +'%d-%m-%Y')" + echo "::set-output name=time::$(date +'%H:%M:%S')" + + - name: Create beta manifest file + if: steps.check_version.outputs.VERSION != '' + id: create_betamanifest + run: | + # Define offset values based on env.CHIP_FAMILY + if [[ "${{ env.CHIP_FAMILY }}" == "ESP32-S3" ]]; then + bootloader_offset=0 + partitions_offset=32768 + app0_offset=57344 + firmware_offset=65536 + spiffs_offset=13172736 + else + # Default values for ESP32 or other environments + bootloader_offset=4096 + partitions_offset=32768 + app0_offset=57344 + firmware_offset=65536 + spiffs_offset=3997696 + fi + + # Create beta manifest + echo "{" > ${{ github.event.repository.name }}-${{ matrix.environment }}.beta.manifest.json + echo " \"name\": \"${{ github.event.repository.name }}-${{ matrix.environment }}\"," >> ${{ github.event.repository.name }}-${{ matrix.environment }}.beta.manifest.json + echo " \"flavour\": \"${{ matrix.environment }}\"," >> ${{ github.event.repository.name }}-${{ matrix.environment }}.beta.manifest.json + echo " \"version\": \"${{ steps.check_version.outputs.VERSION }}-beta\"," >> ${{ github.event.repository.name }}-${{ matrix.environment }}.beta.manifest.json + echo " \"compilation_date\": \"${{ steps.date.outputs.date }}\"," >> ${{ github.event.repository.name }}-${{ matrix.environment }}.beta.manifest.json + echo " \"compilation_time\": \"${{ steps.date.outputs.time }}\"," >> ${{ github.event.repository.name }}-${{ matrix.environment }}.beta.manifest.json + echo " \"new_install_prompt_erase\": true," >> ${{ github.event.repository.name }}-${{ matrix.environment }}.beta.manifest.json + echo " \"new_install_improv_wait_time\": 20," >> ${{ github.event.repository.name }}-${{ matrix.environment }}.beta.manifest.json + echo " \"builds\": [" >> ${{ github.event.repository.name }}-${{ matrix.environment }}.beta.manifest.json + echo " {" >> ${{ github.event.repository.name }}-${{ matrix.environment }}.beta.manifest.json + echo " \"chipFamily\": \"${{ env.CHIP_FAMILY }}\"," >> ${{ github.event.repository.name }}-${{ matrix.environment }}.beta.manifest.json + echo " \"improv\": true," >> ${{ github.event.repository.name }}-${{ matrix.environment }}.beta.manifest.json + echo " \"parts\": [" >> ${{ github.event.repository.name }}-${{ matrix.environment }}.beta.manifest.json + echo " { \"path\": \"${{ github.event.repository.name }}-${{ matrix.environment }}-${{ steps.check_version.outputs.VERSION }}-bootloader.bin\", \"offset\": $bootloader_offset }," >> ${{ github.event.repository.name }}-${{ matrix.environment }}.beta.manifest.json + echo " { \"path\": \"${{ github.event.repository.name }}-${{ matrix.environment }}-${{ steps.check_version.outputs.VERSION }}-partitions.bin\", \"offset\": $partitions_offset }," >> ${{ github.event.repository.name }}-${{ matrix.environment }}.beta.manifest.json + echo " { \"path\": \"${{ github.event.repository.name }}-${{ matrix.environment }}-${{ steps.check_version.outputs.VERSION }}-firmware.bin\", \"offset\": $firmware_offset }," >> ${{ github.event.repository.name }}-${{ matrix.environment }}.beta.manifest.json + echo " { \"path\": \"${{ github.event.repository.name }}-${{ matrix.environment }}-${{ steps.check_version.outputs.VERSION }}-spiffs.bin\", \"offset\": $spiffs_offset }" >> ${{ github.event.repository.name }}-${{ matrix.environment }}.beta.manifest.json + echo " ]" >> ${{ github.event.repository.name }}-${{ matrix.environment }}.beta.manifest.json + echo " }" >> ${{ github.event.repository.name }}-${{ matrix.environment }}.beta.manifest.json + echo " ]" >> ${{ github.event.repository.name }}-${{ matrix.environment }}.beta.manifest.json + echo "}" >> ${{ github.event.repository.name }}-${{ matrix.environment }}.beta.manifest.json + echo "::set-output name=betamanifest::$(cat ${{ github.event.repository.name }}-${{ matrix.environment }}.beta.manifest.json)" + + - name: Read beta manifest files + run: | + echo "Beta Manifest:" + echo ${{ github.event.repository.name }}-${{ matrix.environment }}.beta.manifest.json + echo "Beta Manifest file contents read with cat:" + cat ${{ github.event.repository.name }}-${{ matrix.environment }}.beta.manifest.json + + - name: Copy beta manifest files + run: | + mkdir ./beta_firmware + cp ${{ github.event.repository.name }}-${{ matrix.environment }}.beta.manifest.json ./beta_firmware/${{ github.event.repository.name }}-${{ matrix.environment }}.beta.manifest.json + + - name: Build beta firmware file + if: steps.check_version.outputs.VERSION != '' + run: | + pio run -e ${{ matrix.environment }} + + - name: Copy beta firmware files + if: steps.check_version.outputs.VERSION != '' + run: | + cp .pio/build/${{ matrix.environment }}/bootloader.bin ./beta_firmware/${{ github.event.repository.name }}-${{ matrix.environment }}-${{ steps.check_version.outputs.VERSION }}-bootloader.bin + cp .pio/build/${{ matrix.environment }}/partitions.bin ./beta_firmware/${{ github.event.repository.name }}-${{ matrix.environment }}-${{ steps.check_version.outputs.VERSION }}-partitions.bin + cp .pio/build/${{ matrix.environment }}/firmware.bin ./beta_firmware/${{ github.event.repository.name }}-${{ matrix.environment }}-${{ steps.check_version.outputs.VERSION }}-firmware.bin + + - name: Build beta spiffs file + if: steps.check_version.outputs.VERSION != '' + run: | + pio run -e ${{ matrix.environment }} -t buildfs + + - name: Copy beta spiffs files + if: steps.check_version.outputs.VERSION != '' + run: | + ls -la ./beta_firmware + cp .pio/build/${{ matrix.environment }}/spiffs.bin ./beta_firmware/${{ github.event.repository.name }}-${{ matrix.environment }}-${{ steps.check_version.outputs.VERSION }}-spiffs.bin + + - name: 📂 Sync beta files - FTP-Deploy-Action + if: steps.check_version.outputs.VERSION != '' + uses: SamKirkland/FTP-Deploy-Action@2.0.0 + env: + FTP_SERVER: ${{ secrets.FTP_SERVER }} + FTP_USERNAME: ${{ secrets.FTP_USER }} + FTP_PASSWORD: ${{ secrets.FTP_PASSWORD }} + LOCAL_DIR: ./beta_firmware/ + REMOTE_DIR: /${{ github.event.repository.name }}/beta/ + METHOD: ftp + PORT: 21 + ARGS: --verbose + + - name: Clean eMariete.com Cache + if: steps.check_version.outputs.VERSION != '' + run: curl https://emariete.com/clean_cache_litespeed.php diff --git a/CO2_Gadget.ino b/CO2_Gadget.ino index 5e262cde..03c743fc 100644 --- a/CO2_Gadget.ino +++ b/CO2_Gadget.ino @@ -18,6 +18,8 @@ // Functions and enum definitions void reverseButtons(bool reversed); void outputsLoop(); +void publishMQTTLogData(String logData); +void putPreferences(); // Define enum for toneBuzzerBeep enum ToneBuzzerBeep { @@ -54,6 +56,7 @@ bool activeESPNOW = false; bool activeOTA = false; bool troubledWIFI = false; // There are problems connecting to WIFI. Temporary suspend WIFI bool troubledMQTT = false; // There are problems connecting to MQTT. Temporary suspend MQTT +bool troubledESPNOW = false; // There are problems connecting to ESP-NOW. Temporary suspend ESP-NOW uint64_t timeTroubledWIFI = 0; // Time since WIFI is troubled uint64_t timeTroubledMQTT = 0; // Time since MQTT is troubled uint64_t timeToRetryTroubledWIFI = 300; // Time in seconds to retry WIFI connection after it is troubled @@ -70,6 +73,7 @@ bool showFahrenheit = false; bool displayShowTemperature = true; bool displayShowHumidity = true; bool displayShowBattery = true; +bool displayShowBatteryVoltage = false; bool displayShowCO2 = true; bool displayShowPM25 = true; bool debugSensors = false; @@ -91,17 +95,18 @@ uint16_t boardIdESPNow = 0; uint64_t timeInitializationCompleted = 0; // Variables for Battery reading -float battery_voltage = 0; -uint8_t battery_level = 0; -uint16_t timeBetweenBatteryRead = 15; -uint64_t lastTimeBatteryRead = 0; // Time of last battery reading +float batteryVoltage = 0; +uint8_t batteryLevel = 100; +uint16_t vRef = 960; +uint16_t batteryDischargedMillivolts = 3200; // Voltage of battery when we consider it discharged (0%). +uint16_t batteryFullyChargedMillivolts = 4200; // Voltage of battery when it is considered fully charged (100%). // Variables to control automatic display off to save power bool workingOnExternalPower = true; // True if working on external power (USB connected) uint32_t actualDisplayBrightness = 100; // To know if it's on or off bool displayOffOnExternalPower = false; uint16_t timeToDisplayOff = 0; // Time in seconds to turn off the display to save power. -volatile uint64_t lastTimeButtonPressed = 0; // Last time stamp a button was pressed +volatile uint64_t lastTimeButtonPressed = 0; // Last time stamp button up was pressed // Variables for MQTT timming uint16_t timeBetweenMQTTPublish = 60; // Time in seconds between MQTT transmissions @@ -152,7 +157,7 @@ uint16_t co2RedRange = 1000; #include #include -Stream& miSerialPort = Serial; +// Stream& miSerialPort = Serial; enum notificationTypes { notifyNothing, notifyInfo, @@ -177,9 +182,6 @@ bool displayNotification(String notificationText, notificationTypes notification /********* INCLUDE BATTERY FUNCTIONALITY *********/ /********* *********/ /*****************************************************************************************************/ -uint16_t vRef = 1100; -uint16_t batteryDischargedMillivolts = 3500; // Voltage of battery when we consider it discharged (0%). -uint16_t batteryFullyChargedMillivolts = 4200; // Voltage of battery when it is considered fully charged (100%). #include "CO2_Gadget_Battery.h" /*****************************************************************************************************/ @@ -289,6 +291,7 @@ void wakeUpDisplay() { if (actualDisplayBrightness == 0) { #if defined(SUPPORT_OLED) || defined(SUPPORT_TFT) setDisplayBrightness(DisplayBrightness); + publishMQTTLogData("Display woken up. Setting display brightness to " + String(DisplayBrightness)); #endif lastTimeButtonPressed = millis(); } @@ -323,10 +326,10 @@ void processPendingCommands() { } void initGPIO() { - #ifdef GREEN_PIN +#ifdef GREEN_PIN pinMode(GREEN_PIN, OUTPUT); digitalWrite(GREEN_PIN, LOW); - #endif +#endif pinMode(BLUE_PIN, OUTPUT); digitalWrite(BLUE_PIN, LOW); pinMode(RED_PIN, OUTPUT); @@ -335,14 +338,14 @@ void initGPIO() { void outputsRelays() { if ((!outputsModeRelay) || (co2 == 0)) return; // Don't turn on relays until there is CO2 Data - #ifdef GREEN_PIN +#ifdef GREEN_PIN if (co2 >= co2OrangeRange) { digitalWrite(GREEN_PIN, GREEN_PIN_LOW); } if (co2 < co2OrangeRange) { digitalWrite(GREEN_PIN, GREEN_PIN_HIGH); } - #endif +#endif if (co2 >= co2OrangeRange) { digitalWrite(BLUE_PIN, BLUE_PIN_HIGH); } @@ -360,24 +363,24 @@ void outputsRelays() { void outputsRGBLeds() { if ((outputsModeRelay) || (co2 == 0)) return; // Don't turn on led until there is CO2 Data if (co2 > co2RedRange) { - #ifdef GREEN_PIN +#ifdef GREEN_PIN digitalWrite(GREEN_PIN, GREEN_PIN_LOW); - #endif +#endif digitalWrite(RED_PIN, RED_PIN_HIGH); digitalWrite(BLUE_PIN, BLUE_PIN_LOW); return; } if (co2 >= co2OrangeRange) { - #ifdef GREEN_PIN +#ifdef GREEN_PIN digitalWrite(GREEN_PIN, GREEN_PIN_HIGH); - #endif +#endif digitalWrite(BLUE_PIN, BLUE_PIN_LOW); digitalWrite(RED_PIN, RED_PIN_HIGH); return; } - #ifdef GREEN_PIN +#ifdef GREEN_PIN digitalWrite(GREEN_PIN, GREEN_PIN_HIGH); - #endif +#endif digitalWrite(BLUE_PIN, BLUE_PIN_LOW); digitalWrite(RED_PIN, RED_PIN_LOW); } @@ -422,7 +425,7 @@ void adjustBrightnessLoop() { } // If battery pin not connected, assume it's working on external power - if (battery_voltage < 1) { + if (batteryVoltage < 1) { workingOnExternalPower = true; } @@ -436,40 +439,42 @@ void adjustBrightnessLoop() { if ((!displayOffOnExternalPower) && (workingOnExternalPower)) { setDisplayBrightness(DisplayBrightness); } + if (timeToDisplayOff==0) { + setDisplayBrightness(DisplayBrightness); + } return; } // Display backlight is NOT sleeping and brightness change detected if ((actualDisplayBrightness > 0) && (actualDisplayBrightness != DisplayBrightness)) { setDisplayBrightness(DisplayBrightness); + publishMQTTLogData("Setting display brightness to " + String(DisplayBrightness)); } - // If configured not to turn off the display on external power and it's working on external power, do nothing and return (except if DisplayBrightness is 0)) - if ((!displayOffOnExternalPower) && (workingOnExternalPower)) { + // If configured not to turn off the display on external power and it's working on external power, do nothing and return (except if DisplayBrightness is 0, then turn on display)) + if ((workingOnExternalPower) && (!displayOffOnExternalPower)) { if (actualDisplayBrightness == 0) { setDisplayBrightness(DisplayBrightness); // Exception: When USB connected (just connected) & TFT is OFF -> Turn Display ON + publishMQTTLogData("Turning on display on external power. Actual brightness: " + String(actualDisplayBrightness)); } return; } - if ((actualDisplayBrightness != 0) && (millis() - lastTimeButtonPressed >= timeToDisplayOff * 1000) && DisplayBrightness > 0) { - Serial.println("-->[MAIN] Turning off display to save power. Actual brightness: " + String(actualDisplayBrightness)); - turnOffDisplay(); - } -#endif -} + if (timeToDisplayOff == 0) return; // If timeToDisplayOff is 0, don't turn off the display -void batteryLoop() { - const float lastBatteryVoltage = battery_voltage; - readBatteryVoltage(); - if (!inMenu) { - if (abs(lastBatteryVoltage - battery_voltage) >= 0.1) { // If battery voltage changed by at least 0.1, update battery level - battery_level = getBatteryPercentage(); - // Serial.printf("-->[BATT] Battery Level: %d%%\n", battery.level()); + if ((actualDisplayBrightness != 0) && (millis() - lastTimeButtonPressed >= timeToDisplayOff * 1000) && DisplayBrightness > 0) { + if ((workingOnExternalPower) && (displayOffOnExternalPower)) { + Serial.println("-->[MAIN] Turning off display on external power to save power. Actual brightness: " + String(actualDisplayBrightness)); + turnOffDisplay(); + publishMQTTLogData("[MAIN] Turning off display on external power to save power. Actual brightness: " + String(actualDisplayBrightness)); + } + if (!workingOnExternalPower) { + Serial.println("-->[MAIN] Turning off display on battery to save power. Actual brightness: " + String(actualDisplayBrightness)); + turnOffDisplay(); + publishMQTTLogData("[MAIN] Turning off display on battery to save power. Actual brightness: " + String(actualDisplayBrightness)); } } - // If battery voltage is more than 5% of the fully charged battery voltage, asume it's working on external power - workingOnExternalPower = (battery_voltage * 1000 > batteryFullyChargedMillivolts + (batteryFullyChargedMillivolts * 5 / 100)); +#endif } void setCpuFrequencyAndReinitSerial(int16_t newCpuFrequency) { @@ -493,10 +498,10 @@ void utilityLoop() { const int16_t lowCpuFrequency = 80; if (workingOnExternalPower && actualCPUFrequency != highCpuFrequency) { - Serial.printf("-->[BATT] Battery voltage: %.2fV. Increasing CPU frequency to %dMHz\n", battery_voltage, highCpuFrequency); + Serial.printf("-->[BATT] Battery voltage: %.2fV. Increasing CPU frequency to %dMHz\n", batteryVoltage, highCpuFrequency); setCpuFrequencyAndReinitSerial(highCpuFrequency); } else if (!workingOnExternalPower && actualCPUFrequency != lowCpuFrequency) { - Serial.printf("-->[BATT] Battery voltage: %.2fV. Decreasing CPU frequency to %dMHz\n", battery_voltage, lowCpuFrequency); + Serial.printf("-->[BATT] Battery voltage: %.2fV. Decreasing CPU frequency to %dMHz\n", batteryVoltage, lowCpuFrequency); setCpuFrequencyAndReinitSerial(lowCpuFrequency); } } @@ -505,7 +510,6 @@ void utilityLoop() { void setup() { uint32_t brown_reg_temp = READ_PERI_REG(RTC_CNTL_BROWN_OUT_REG); // save WatchDog register WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); // disable brownout detector - setCpuFrequencyMhz(80); // Lower CPU frecuency to reduce power consumption Serial.begin(115200); delay(50); #ifdef AUTO_VERSION @@ -520,10 +524,9 @@ void setup() { Serial.printf("-->[STUP] Free PSRAM: %d\n", ESP.getFreePsram()); // Get the size of the flash memory - uint32_t flash_size = ESP.getFlashChipSize(); - Serial.printf("-->[STUP] Flash size: %d\n", flash_size); - Serial.printf("-->[STUP] Flash speed: %d\n", ESP.getFlashChipSpeed()); - Serial.printf("-->[STUP] Flash mode: %d\n", ESP.getFlashChipMode()); + // Serial.printf("-->[STUP] Flash size: %d\n", ESP.getFlashChipSize()); + // Serial.printf("-->[STUP] Flash speed: %d\n", ESP.getFlashChipSpeed()); + // Serial.printf("-->[STUP] Flash mode: %d\n", ESP.getFlashChipMode()); Serial.printf("-->[STUP] Starting up...\n\n"); @@ -575,4 +578,4 @@ void loop() { buttonsLoop(); menuLoop(); BLELoop(); -} +} \ No newline at end of file diff --git a/CO2_Gadget_Battery.h b/CO2_Gadget_Battery.h index 64bb45b9..92fd48e0 100644 --- a/CO2_Gadget_Battery.h +++ b/CO2_Gadget_Battery.h @@ -5,43 +5,76 @@ /*****************************************************************************************************/ /********* *********/ /********* SETUP BATTERY FUNCTIONALITY *********/ -/********* Uses Library https://github.com/rlogiacco/BatterySense/blob/master/Battery.cpp *********/ +/********* Uses Library https://github.com/rlogiacco/BatterySense *********/ /********* *********/ /*****************************************************************************************************/ // clang-format on +float lastBatteryVoltage = 0; +uint16_t timeBetweenBatteryRead = 1; +uint64_t lastTimeBatteryRead = 0; // Time of last battery reading +const uint8_t batterySamples = 3; // Number of samples to average for battery voltage. + // Battery info (percent charge). const uint32_t voltageDividerR1Ohms = 100000; // Resistance of "R1" for voltage divider. const uint32_t voltageDividerR2Ohms = 100000; // Resistance of "R2" for voltage divider. -// Voltage divider ratio for battery sense must be (R1 + R2) / R2 - see https://github.com/rlogiacco/BatterySense. +// Voltage divider ratio for battery sense must be (R1 + R2) / R2 const float voltageDividerRatio = (voltageDividerR1Ohms + voltageDividerR2Ohms) / voltageDividerR2Ohms; #include Battery battery(batteryDischargedMillivolts, batteryFullyChargedMillivolts, ADC_BATTERY_PIN); -/** - * 1 cell li-ion/li-poly battery wired to A0, continuous sensing, sigmoidal mapping function, cut off at 3000mV - * https://github.com/rlogiacco/BatterySense#lesser-than-5v-with-voltage-booster - **/ + void initBattery() { battery.onDemand(battery.ON_DEMAND_DISABLE, LOW); - battery.begin(vRef, voltageDividerRatio, &sigmoidal); + battery.begin(vRef, voltageDividerRatio, &asigmoidal); + // Serial.println("-->[BATT***] Battery initialized with vRef: " + String(vRef) + " and voltage divider ratio: " + String(voltageDividerRatio)); } -float readBatteryVoltage() { +void readBatteryVoltage() { + float batteryVoltageNow = 0; if ((millis() - lastTimeBatteryRead >= timeBetweenBatteryRead * 1000) || (lastTimeBatteryRead == 0)) { - battery_voltage = (float)battery.voltage() / 1000; - // Serial.print("-->[BATT] Battery read: "); - // Serial.print(battery_voltage); - // Serial.println("V"); + for (uint8_t i = 0; i < batterySamples; i++) { + batteryVoltageNow += float(battery.voltage()) / 1000; + delay(10); + } + batteryVoltageNow /= 3; + batteryVoltage = batteryVoltageNow; + batteryLevel = battery.level(batteryVoltage*1000); lastTimeBatteryRead = millis(); + + // If battery voltage is more than 9% of the fully charged battery voltage (~4.58V), assume it's working on external power + workingOnExternalPower = (batteryVoltageNow * 1000 > batteryFullyChargedMillivolts + (batteryFullyChargedMillivolts * 9 / 100)); + + // publishMQTTLogData("Battery Level: " + String(batteryLevel) + "% Battery voltage changed from: " + String(lastBatteryVoltage) + "V to " + String(batteryVoltage) + "V"); } - return (battery_voltage); } -uint8_t getBatteryPercentage() { - return battery.level(); +// #include + +// void readEfuse() { +// esp_adc_cal_characteristics_t chars; +// auto val_type = esp_adc_cal_characterize(ADC_UNIT_1, ADC_ATTEN_DB_11, ADC_WIDTH_BIT_12, 1100, &chars); +// if (val_type == ESP_ADC_CAL_VAL_EFUSE_VREF) { +// Serial.println("eFuse Vref"); +// } else if (val_type == ESP_ADC_CAL_VAL_EFUSE_TP) { +// Serial.println("Two Point"); +// } else { +// Serial.println("Default"); +// } +// } + +void batteryLoop() { + float batteryVoltageNow = 0; + readBatteryVoltage(); + // Serial.printf("-->[BATT] Battery Level: %d%%. Battery voltage: %.4fV\n", batteryLevel, batteryVoltageNow); + if (!inMenu) { + if (abs(lastBatteryVoltage - batteryVoltage) >= 0.1) { // If battery voltage changed by at least 0.1V, update battery level + // Serial.printf("-->[BATT] Battery Level: %d%%. Battery voltage changed from: %.4fV to %.4fV\n", batteryLevel, lastBatteryVoltage, batteryVoltage); + lastBatteryVoltage = batteryVoltage; + } + } } #endif // CO2_Gadget_Battery_h \ No newline at end of file diff --git a/CO2_Gadget_ESP-NOW.h b/CO2_Gadget_ESP-NOW.h index 50bd90ad..f18c03c4 100644 --- a/CO2_Gadget_ESP-NOW.h +++ b/CO2_Gadget_ESP-NOW.h @@ -185,7 +185,7 @@ void publishESPNow() { outgoingReadings.co2 = co2; outgoingReadings.temp = temp; outgoingReadings.hum = hum; - outgoingReadings.battery = battery_voltage; + outgoingReadings.battery = batteryVoltage; outgoingReadings.readingId++; // Send message via ESP-NOW diff --git a/CO2_Gadget_MQTT.h b/CO2_Gadget_MQTT.h index d7606968..63c9ce2d 100644 --- a/CO2_Gadget_MQTT.h +++ b/CO2_Gadget_MQTT.h @@ -251,6 +251,14 @@ void initMQTT() { #endif } +void publishMQTTLogData(String logData) { +#ifdef SUPPORT_MQTT + // if (activeMQTT && !troubledMQTT && !troubledWIFI && (WiFi.status() == WL_CONNECTED) && mqttClient.connected()) { + // publishStrMQTT("/log", logData); + // } +#endif +} + void publishMQTTAlarms() { static bool MQTTGreenAlarm, MQTTOrangeAlarm, MQTTRedAlarm = false; @@ -282,8 +290,8 @@ void publishMQTTAlarms() { void publishMQTTSystemData() { publishIntMQTT("/uptime", millis() / 1000); - publishFloatMQTT("/voltage", battery_voltage); - publishIntMQTT("/battery", battery_level); + publishFloatMQTT("/voltage", batteryVoltage); + publishIntMQTT("/battery", batteryLevel); publishIntMQTT("/freeMem", ESP.getFreeHeap()); publishIntMQTT("/wifiRSSI", WiFi.RSSI()); publishStrMQTT("/IP", WiFi.localIP().toString()); diff --git a/CO2_Gadget_Menu.h b/CO2_Gadget_Menu.h index 41a3af70..bbeaf86e 100644 --- a/CO2_Gadget_Menu.h +++ b/CO2_Gadget_Menu.h @@ -590,15 +590,15 @@ MENU(espnowConfigMenu, "ESP-NOW Config", doNothing, noEvent, wrapStyle #endif // SUPPORT_ESPNOW result doSetvRef(eventMask e, navNode &nav, prompt &item) { - battery.begin(vRef, voltageDividerRatio, &sigmoidal); + battery.begin(vRef, voltageDividerRatio, &asigmoidal); delay(10); - battery_voltage = (float)battery.voltage() / 1000; + batteryVoltage = (float)battery.voltage() / 1000; nav.target-> dirty = true; return proceed; } MENU(batteryConfigMenu, "Battery Config", doNothing, noEvent, wrapStyle - ,FIELD(battery_voltage, "Battery:", "V", 0, 9, 0, 0, doNothing, noEvent, noStyle) + ,FIELD(batteryVoltage, "Battery:", "V", 0, 9, 0, 0, doNothing, noEvent, noStyle) ,FIELD(vRef, "Voltage ref:", "", 0, 2000, 10, 10, doSetvRef, anyEvent, noStyle) ,FIELD(batteryFullyChargedMillivolts, "Bat Full (mV):", "", 0, 4200, 10, 10, doNothing, noEvent, noStyle) ,FIELD(batteryDischargedMillivolts, "Bat Empty (mV):", "", 2700, 3700, 10, 10, doNothing, noEvent, noStyle) @@ -817,7 +817,7 @@ class altPromptUptime:public prompt { MENU(informationMenu, "Information", doNothing, noEvent, wrapStyle - ,FIELD(battery_voltage, "Battery", "V", 0, 9, 0, 0, doNothing, noEvent, noStyle) + ,FIELD(batteryVoltage, "Battery", "V", 0, 9, 0, 0, doNothing, noEvent, noStyle) ,OP("Comp " BUILD_GIT, doNothing, noEvent) ,OP("Version " CO2_GADGET_VERSION CO2_GADGET_REV, doNothing, noEvent) ,OP("" FLAVOUR, doNothing, noEvent) @@ -834,7 +834,7 @@ result enterMainMenu(menuOut &o, idleEvent e) { return proceed; } -// ,FIELD(battery_voltage, "Battery", "Volts", 0, 9, 0, 0, doNothing, noEvent, noStyle) // It was removed from menu as updates avoids timeout to work +// ,FIELD(batteryVoltage, "Battery", "Volts", 0, 9, 0, 0, doNothing, noEvent, noStyle) // It was removed from menu as updates avoids timeout to work MENU(mainMenu, "CO2 Gadget", doNothing, noEvent, wrapStyle ,SUBMENU(informationMenu) ,SUBMENU(configMenu) @@ -1060,10 +1060,10 @@ result idle(menuOut &o, idleEvent e) { void menuLoop() { uint16_t timeToWaitForImprov = 5; // Time to wait for Improv-WiFi to connect on startup if (Serial.available() && Serial.peek() == 0x2A) { // 0x2A is the '*' character. - inMenu = true; - if (inMenu) { + // inMenu = true; + // if (inMenu) { nav.doInput(); // Do input, even if no display, as serial menu needs this - } + // } } if (millis() < timeInitializationCompleted + timeToWaitForImprov * 1000) { // Wait before starting the menu to avoid issues with Improv-WiFi diff --git a/CO2_Gadget_Preferences.h b/CO2_Gadget_Preferences.h index cd04b3f2..586967d9 100644 --- a/CO2_Gadget_Preferences.h +++ b/CO2_Gadget_Preferences.h @@ -5,9 +5,23 @@ #include Preferences preferences; +uint8_t prefVersion = 0; +uint8_t prefRevision = 0; + +void upgradePreferences() { + if ((batteryDischargedMillivolts == 3500) && (prefVersion == 0) && (prefRevision == 0)) { + batteryDischargedMillivolts = 3200; + Serial.printf("-->[PREF] Upgrading preferences batteryDischargedMillivolts to %d\n", batteryDischargedMillivolts); + prefRevision = 1; + putPreferences(); + } +} + void printPreferences() { Serial.printf("-->[PREF] \n"); Serial.printf("-->[PREF] LOADED PREFERENCES FROM NVR:\n"); + Serial.printf("-->[PREF] prefVersion:\t #%d#\n", prefVersion); + Serial.printf("-->[PREF] prefRevision:\t #%d#\n", prefRevision); Serial.printf("-->[PREF] customCalValue: #%d#\n", customCalibrationValue); Serial.printf("-->[PREF] tempOffset:\t #%.1f#\n", tempOffset); Serial.printf("-->[PREF] altitudeMeters:\t #%d#\n", altitudeMeters); @@ -57,6 +71,7 @@ void printPreferences() { Serial.printf("-->[PREF] showTemp:\t #%s#\n", ((displayShowTemperature) ? "Show" : "Hide")); Serial.printf("-->[PREF] showHumidity:\t #%s#\n", ((displayShowHumidity) ? "Show" : "Hide")); Serial.printf("-->[PREF] showBattery:\t #%s#\n", ((displayShowBattery) ? "Show" : "Hide")); + Serial.printf("-->[PREF] showBatteryVolt:\t #%s#\n", ((displayShowBatteryVoltage) ? "Show" : "Hide")); Serial.printf("-->[PREF] showCO2:\t #%s#\n", ((displayShowCO2) ? "Show" : "Hide")); Serial.printf("-->[PREF] showPM25:\t #%s#\n", ((displayShowPM25) ? "Show" : "Hide")); @@ -76,9 +91,10 @@ void initPreferences() { // Serial.printf("-->[PREF] Preferences NOT cleared\n"); // } // preferences.end(); - // delay(1000); + // delay(000); preferences.begin("CO2-Gadget", false); - // preferences.clear(); // Remove all preferences + prefVersion = preferences.getUInt("prefVersion", 0); + prefRevision = preferences.getUInt("prefRevision", 0); customCalibrationValue = preferences.getUInt("customCalValue", 415); tempOffset = float(preferences.getFloat("tempOffset", 0)); altitudeMeters = preferences.getUInt("altitudeMeters", 0); @@ -102,7 +118,7 @@ void initPreferences() { activeMQTT = false; preferences.putBool("activeMQTT", activeMQTT); } - batteryDischargedMillivolts = preferences.getUInt("batDischgd", 3500); + batteryDischargedMillivolts = preferences.getUInt("batDischgd", 3200); batteryFullyChargedMillivolts = preferences.getUInt("batChargd", 4200); vRef = preferences.getUInt("vRef", 930); // Looks like, due to a bug, 930 is a goos starting number for vRef timeToDisplayOff = preferences.getUInt("tToDispOff", 60); @@ -148,6 +164,7 @@ void initPreferences() { displayShowTemperature = preferences.getBool("showTemp", true); displayShowHumidity = preferences.getBool("showHumidity", true); displayShowBattery = preferences.getBool("showBattery", true); + displayShowBatteryVoltage = preferences.getBool("showBattVolt", false); displayShowCO2 = preferences.getBool("showCO2", true); displayShowPM25 = preferences.getBool("showPM25", true); @@ -165,10 +182,11 @@ void initPreferences() { wifiPass.trim(); hostName.trim(); preferences.end(); -#define DEBUG_PREFERENCES +// #define DEBUG_PREFERENCES #ifdef DEBUG_PREFERENCES printPreferences(); #endif +upgradePreferences(); } void putPreferences() { @@ -228,6 +246,7 @@ void putPreferences() { preferences.putBool("showTemp", displayShowTemperature); preferences.putBool("showHumidity", displayShowHumidity); preferences.putBool("showBattery", displayShowBattery); + preferences.putBool("showBattVolt", displayShowBatteryVoltage); preferences.putBool("showCO2", displayShowCO2); preferences.putBool("showPM25", displayShowPM25); @@ -263,7 +282,7 @@ String getPreferencesAsJson() { doc["activeESPNOW"] = preferences.getBool("activeESPNOW", false); doc["activeOTA"] = preferences.getBool("activeOTA", false); doc["rootTopic"] = preferences.getString("rootTopic", rootTopic); - doc["batDischgd"] = preferences.getInt("batDischgd", 3500); + doc["batDischgd"] = preferences.getInt("batDischgd", 3200); doc["batChargd"] = preferences.getInt("batChargd", 4200); doc["vRef"] = preferences.getInt("vRef", 930); doc["mqttClientId"] = preferences.getString("mqttClientId", mqttClientId); @@ -363,6 +382,7 @@ String getActualSettingsAsJson() { doc["showTemp"] = displayShowTemperature; doc["showHumidity"] = displayShowHumidity; doc["showBattery"] = displayShowBattery; + doc["showBattVolt"] = displayShowBatteryVoltage; doc["showCO2"] = displayShowCO2; doc["showPM25"] = displayShowPM25; doc["measInterval"] = measurementInterval; @@ -420,12 +440,12 @@ bool handleSavePreferencesfromJSON(String jsonPreferences) { rootTopic = JsonDocument["rootTopic"].as().c_str(); batteryDischargedMillivolts = JsonDocument["batDischgd"]; batteryFullyChargedMillivolts = JsonDocument["batChargd"]; - if (vRef != JsonDocument["vRef"]) { + if (vRef != JsonDocument["vRef"]) { // If battery reference changed, apply it vRef = JsonDocument["vRef"]; - battery.begin(vRef, voltageDividerRatio, &sigmoidal); - delay(10); - battery_voltage = (float)battery.voltage() / 1000; + battery.begin(vRef, voltageDividerRatio, &asigmoidal); + readBatteryVoltage(); } + vRef = JsonDocument["vRef"]; mqttClientId = JsonDocument["mqttClientId"].as().c_str(); mqttBroker = JsonDocument["mqttBroker"].as().c_str(); mqttUser = JsonDocument["mqttUser"].as().c_str(); @@ -458,6 +478,7 @@ bool handleSavePreferencesfromJSON(String jsonPreferences) { displayShowTemperature = JsonDocument["showTemp"]; displayShowHumidity = JsonDocument["showHumidity"]; displayShowBattery = JsonDocument["showBattery"]; + displayShowBatteryVoltage = JsonDocument["showBattVolt"]; displayShowCO2 = JsonDocument["showCO2"]; displayShowPM25 = JsonDocument["showPM25"]; diff --git a/CO2_Gadget_Sensors.h b/CO2_Gadget_Sensors.h index 878b145f..d8dedf6f 100644 --- a/CO2_Gadget_Sensors.h +++ b/CO2_Gadget_Sensors.h @@ -65,17 +65,7 @@ uint16_t getSCD4xFeatureSet() { Serial.println("-->[SENS] SCD4x getFeatures error: " + String(error)); } else { uint8_t typeOfSensor = ((featureSet & 0x1000) >> 12); - switch (typeOfSensor) { - case 0: - Serial.println("-->[SENS] SCD4x Sensor Type: SCD40"); - break; - case 1: - Serial.println("-->[SENS] SCD4x Sensor Type: SCD41"); - break; - default: - Serial.println("-->[SENS] SCD4x Sensor Type: Unknown (probably unsupported SCD42 obsolete sensor)"); - break; - } + Serial.println("-->[SENS] SCD4x Sensor Type: SCD4" + String(typeOfSensor)); } sensors.scd4x.startPeriodicMeasurement(); return featureSet; diff --git a/CO2_Gadget_TFT.h b/CO2_Gadget_TFT.h index f5fcfaeb..1c1efd2d 100644 --- a/CO2_Gadget_TFT.h +++ b/CO2_Gadget_TFT.h @@ -123,16 +123,16 @@ void setElementLocations() { elementPosition.tempY = displayHeight - 25; elementPosition.humidityX = displayWidth - 60; elementPosition.humidityY = displayHeight - 25; - elementPosition.batteryIconX = displayWidth - 36; - elementPosition.batteryIconY = 4; + elementPosition.batteryIconX = displayWidth - 34; + elementPosition.batteryIconY = 2; elementPosition.batteryVoltageX = displayWidth - 92; elementPosition.batteryVoltageY = 2; elementPosition.bleIconX = 2; - elementPosition.bleIconY = 1; + elementPosition.bleIconY = 2; elementPosition.wifiIconX = 26; - elementPosition.wifiIconY = 1; + elementPosition.wifiIconY = 2; elementPosition.mqttIconX = 50; - elementPosition.mqttIconY = 1; + elementPosition.mqttIconY = 2; elementPosition.espNowIconX = 74; elementPosition.espNowIconY = 1; } @@ -148,8 +148,8 @@ void setElementLocations() { elementPosition.tempY = displayHeight - 25; elementPosition.humidityX = displayWidth - 60; elementPosition.humidityY = displayHeight - 25; - elementPosition.batteryIconX = displayWidth - 36; - elementPosition.batteryIconY = 4; + elementPosition.batteryIconX = displayWidth - 34; + elementPosition.batteryIconY = 2; elementPosition.batteryVoltageX = displayWidth - 92; elementPosition.batteryVoltageY = 2; elementPosition.bleIconX = 2; @@ -173,8 +173,8 @@ void setElementLocations() { elementPosition.tempY = displayHeight - 25; elementPosition.humidityX = displayWidth - 60; elementPosition.humidityY = displayHeight - 25; - elementPosition.batteryIconX = displayWidth - 36; - elementPosition.batteryIconY = 4; + elementPosition.batteryIconX = displayWidth - 34; + elementPosition.batteryIconY = 2; elementPosition.batteryVoltageX = displayWidth - 92; elementPosition.batteryVoltageY = 2; elementPosition.bleIconX = 2; @@ -194,26 +194,26 @@ void setDisplayBrightness(uint16_t newBrightness) { // Serial.printf("\n-->[TFT ] DisplayBrightness value at %d\n", DisplayBrightness); // Serial.printf("-->[TFT ] actualDisplayBrightness value at %d\n", actualDisplayBrightness); // Serial.printf("-->[TFT ] New display brightness value at %d\n", newBrightness); - analogWrite(TFT_BL, newBrightness); + analogWrite(TFT_BACKLIGHT, newBrightness); actualDisplayBrightness = newBrightness; } #endif -#ifdef TDISPLAY_S3 +#ifdef ARDUINO_LILYGO_T_DISPLAY_S3 if (actualDisplayBrightness != newBrightness) { // Serial.printf("\n-->[TFT ] DisplayBrightness value at %d\n", DisplayBrightness); // Serial.printf("-->[TFT ] Old actualDisplayBrightness value at %d\n", actualDisplayBrightness); // Serial.printf("-->[TFT ] New actualDisplayBrightness value at %d\n", newBrightness); if (newBrightness == 0) { - digitalWrite(TFT_BL, LOW); + digitalWrite(TFT_BACKLIGHT, LOW); } else { - digitalWrite(TFT_BL, HIGH); + digitalWrite(TFT_BACKLIGHT, HIGH); } actualDisplayBrightness = newBrightness; } #endif #ifdef ST7789_240x320 if (actualDisplayBrightness != newBrightness) { - analogWrite(TFT_BL, newBrightness); + analogWrite(TFT_BACKLIGHT, newBrightness); actualDisplayBrightness = newBrightness; } #endif @@ -264,14 +264,14 @@ void displaySplashScreen() { void initBacklight() { #if defined(TTGO_TDISPLAY) || defined(ST7789_240x320) - pinMode(TFT_BL, OUTPUT); + pinMode(TFT_BACKLIGHT, OUTPUT); setDisplayBrightness(DisplayBrightness); #endif -#ifdef TDISPLAY_S3 - pinMode(TFT_BL, OUTPUT); +#ifdef ARDUINO_LILYGO_T_DISPLAY_S3 + pinMode(TFT_BACKLIGHT, OUTPUT); pinMode(TFT_POWER_ON_BATTERY, OUTPUT); - delay(50); - digitalWrite(TFT_BL, HIGH); + delay(20); + digitalWrite(TFT_BACKLIGHT, HIGH); digitalWrite(TFT_POWER_ON_BATTERY, HIGH); #endif } @@ -356,13 +356,13 @@ bool displayNotification(String notificationText, String notificationText2, noti return true; } -uint16_t getBatteryColor(uint16_t battery_voltage) { +uint16_t getBatteryColor(float batteryVoltage) { uint16_t color; - if (battery_voltage <= 3.6) { + if (batteryVoltage <= 3.6) { color = TFT_RED; - } else if (battery_voltage <= 3.8) { + } else if (batteryVoltage <= 3.8) { color = TFT_ORANGE; - } else if (battery_voltage <= 4.5) { + } else if (!workingOnExternalPower) { color = TFT_GREEN; } else { color = TFT_SKYBLUE; @@ -371,41 +371,65 @@ uint16_t getBatteryColor(uint16_t battery_voltage) { } void showBatteryVoltage(int32_t posX, int32_t posY) { - if ((!displayShowBattery) || (battery_voltage < 1)) return; - String batteryVoltageString = " " + String(battery_voltage, 1) + "V "; + if ((!displayShowBatteryVoltage) || (!displayShowBattery) || (batteryVoltage < 1)) return; + String batteryVoltageString = " " + String(batteryVoltage, 1) + "V "; tft.setTextDatum(TL_DATUM); tft.setCursor(posX, posY); spr.loadFont(SMALL_FONT); - spr.setTextColor(getBatteryColor(battery.voltage()), TFT_BLACK); + spr.setTextColor(getBatteryColor(batteryVoltage), TFT_BLACK); spr.printToSprite(batteryVoltageString); // Space padding helps over-write old numbers spr.unloadFont(); } void showBatteryIcon(int32_t posX, int32_t posY) { // For TTGO T-Display posX=tft.width() - 32, posY=4 - if ((!displayShowBattery) || (battery_voltage < 1)) return; - uint8_t batteryLevel = battery.level(); uint16_t color; + if ((!displayShowBattery) || (batteryVoltage < 1)) return; + if (batteryLevel < 20) { color = TFT_RED; } else { color = TFT_SILVER; } - if (battery_voltage > 4.5) { // Charging... + if (batteryVoltage > 4.5) { // Charging... color = iconDefaultColor; } - tft.drawRoundRect(posX, posY, 32, 14, 2, color); // Battery outter rectangle - tft.drawLine(posX + 33, posY + 4, posX + 33, posY + 10, color); + if (spr.createSprite(34, 20) == nullptr) { + Serial.printf("-->[TFT ] Error: sprite not created, not enough free RAM! Free RAM: %d\n", ESP.getFreeHeap()); + spr.deleteSprite(); + return; + } + + publishMQTTLogData("-->[TFT ] Battery Level: " + String(batteryLevel) + "% Battery voltage: " + String(batteryVoltage) + "V"); + + spr.fillSprite(TFT_BLACK); - if (batteryLevel > 20) tft.fillRect(posX + 4, posY + 2, 4, 10, color); - if (batteryLevel > 40) tft.fillRect(posX + 11, posY + 2, 4, 10, color); - if (batteryLevel > 60) tft.fillRect(posX + 18, posY + 2, 4, 10, color); - if (batteryLevel > 80) tft.fillRect(posX + 25, posY + 2, 4, 10, color); + if (workingOnExternalPower) { + spr.drawRoundRect(12, 0, 16 + 4, 16 + 4, 2, TFT_DARKGREY); + spr.setSwapBytes(true); + spr.drawBitmap(14, 2, iconUSB, 16, 16, TFT_BLACK, iconDefaultColor); + } else { + uint32_t upperLine = 4; + spr.drawRoundRect(0, upperLine, 32, 14, 2, color); // Battery outter rectangle + spr.drawLine(33, upperLine + 4, 33, upperLine + 10, color); + if (batteryLevel > 20) spr.fillRect(4, upperLine + 2, 4, 10, color); + if (batteryLevel > 40) spr.fillRect(11, upperLine + 2, 4, 10, color); + if (batteryLevel > 60) spr.fillRect(18, upperLine + 2, 4, 10, color); + if (batteryLevel > 80) spr.fillRect(25, upperLine + 2, 4, 10, color); + } + + spr.pushSprite(posX, posY); + spr.deleteSprite(); } void showWiFiIcon(int32_t posX, int32_t posY) { int8_t rssi = WiFi.RSSI(); + if (troubledWIFI) { + tft.drawRoundRect(posX - 2, posY - 2, 16 + 4, 16 + 4, 2, TFT_RED); + tft.drawBitmap(posX, posY, iconWiFi, 16, 16, TFT_BLACK, iconDefaultColor); + return; + } tft.drawRoundRect(posX - 2, posY - 2, 16 + 4, 16 + 4, 2, TFT_DARKGREY); if (!activeWIFI) { tft.drawBitmap(posX, posY, iconWiFi, 16, 16, TFT_BLACK, TFT_DARKGREY); @@ -415,10 +439,10 @@ void showWiFiIcon(int32_t posX, int32_t posY) { tft.drawBitmap(posX, posY, iconWiFi, 16, 16, TFT_BLACK, iconDefaultColor); else if (rssi < 70) tft.drawBitmap(posX, posY, iconWiFiMed, 16, 16, TFT_BLACK, TFT_ORANGE); - else if (rssi < 90) + else if (rssi < 80) tft.drawBitmap(posX, posY, iconWiFiMed, 16, 16, TFT_BLACK, TFT_YELLOW); } else { - tft.drawBitmap(posX, posY, iconWiFiLow, 16, 16, TFT_BLACK, TFT_RED); + tft.drawBitmap(posX, posY, iconWiFiLow, 16, 16, TFT_BLACK, TFT_BLUE); } } } @@ -433,6 +457,11 @@ void showBLEIcon(int32_t posX, int32_t posY) { } void showMQTTIcon(int32_t posX, int32_t posY) { + if (troubledMQTT) { + tft.drawRoundRect(posX - 2, posY - 2, 16 + 4, 16 + 4, 2, TFT_RED); + tft.drawBitmap(posX, posY, iconMQTT, 16, 16, TFT_BLACK, iconDefaultColor); + return; + } tft.drawRoundRect(posX - 2, posY - 2, 16 + 4, 16 + 4, 2, TFT_DARKGREY); if (!activeMQTT) { tft.drawBitmap(posX, posY, iconMQTT, 16, 16, TFT_BLACK, TFT_DARKGREY); @@ -442,12 +471,19 @@ void showMQTTIcon(int32_t posX, int32_t posY) { } void showEspNowIcon(int32_t posX, int32_t posY) { +#ifdef SUPPORT_ESPNOW + if (troubledESPNOW) { + tft.drawRoundRect(posX - 2, posY - 2, 16 + 4, 16 + 4, 2, TFT_RED); + tft.drawBitmap(posX, posY, iconEspNow, 16, 16, TFT_BLACK, iconDefaultColor); + return; + } tft.drawRoundRect(posX - 2, posY - 2, 16 + 4, 16 + 4, 2, TFT_DARKGREY); if (!activeESPNOW) { tft.drawBitmap(posX, posY, iconEspNow, 16, 16, TFT_BLACK, TFT_DARKGREY); } else { tft.drawBitmap(posX, posY, iconEspNow, 16, 16, TFT_BLACK, iconDefaultColor); } +#endif } void showTemperatureIcon(int32_t posX, int32_t posY) { @@ -522,31 +558,6 @@ uint16_t getCO2Color(uint16_t co2) { return color; } -void OLDshowCO2(uint16_t co2, int32_t posX, int32_t posY, uint16_t pixelsToBaseline) { - if (co2 > 9999) co2 = 9999; - - spr.loadFont(BIG_FONT); - uint16_t width = spr.textWidth("0000"); - uint16_t height = elementPosition.co2FontDigitsHeight; - uint16_t posSpriteX = posX - width; - uint16_t posSpriteY = posY - height; - if (posSpriteX < 0) posSpriteX = 0; - if (posSpriteY < 0) posSpriteY = 0; - if (spr.createSprite(width, height) == nullptr) { - Serial.printf("-->[TFT ] Error: sprite not created, not enough free RAM! Free RAM: %d\n", ESP.getFreeHeap()); - spr.unloadFont(); - spr.deleteSprite(); - return; - } - // spr.drawRect(0, 0, width, height, TFT_WHITE); - spr.setTextColor(getCO2Color(co2), TFT_BLACK); - spr.setTextDatum(TR_DATUM); - spr.drawNumber(co2, width, 0); - spr.pushSprite(posSpriteX, posSpriteY); - spr.unloadFont(); - spr.deleteSprite(); -} - void showCO2(uint16_t co2, int32_t posX, int32_t posY, uint16_t pixelsToBaseline) { if ((co2 == previousCO2Value) || (co2 == 0) || (co2 > 9999)) return; @@ -557,7 +568,7 @@ void showCO2(uint16_t co2, int32_t posX, int32_t posY, uint16_t pixelsToBaseline uint16_t posSpriteY = posY - height; uint16_t color = getCO2Color(co2); if (posSpriteY < 0) posSpriteY = 0; - spr.createSprite(digitWidth, height); + // spr.createSprite(digitWidth, height); if (spr.createSprite(digitWidth, height) == nullptr) { // Serial.printf("-->[TFT ] Error: sprite not created, not enough free RAM! Free RAM: %d\n", ESP.getFreeHeap()); spr.unloadFont(); diff --git a/CO2_Gadget_WIFI.h b/CO2_Gadget_WIFI.h index d38daa5d..1e5bb504 100644 --- a/CO2_Gadget_WIFI.h +++ b/CO2_Gadget_WIFI.h @@ -560,17 +560,19 @@ void initWebServer() { server.on("/settings", HTTP_GET, [](AsyncWebServerRequest *request) { String inputString; // /settings?MeasurementInterval=100 - if (request->hasParam(PARAM_INPUT_1)) { - inputString = request->getParam(PARAM_INPUT_1)->value(); + if (request->hasParam("MeasurementInterval")) { + inputString = request->getParam("MeasurementInterval")->value(); if (checkStringIsNumerical(inputString)) { Serial.printf("-->[WiFi] Received /settings command MeasurementInterval with parameter %s\n", inputString); measurementInterval = inputString.toInt(); request->send(200, "text/plain", "OK. Setting MeasurementInterval to " + inputString + ", please re-calibrate your sensor."); + } else { + request->send(400, "text/plain", "Error. MeasurementInterval must have a number as parameter."); } }; // /settings?CalibrateCO2=400 - if (request->hasParam(PARAM_INPUT_2)) { - inputString = request->getParam(PARAM_INPUT_2)->value(); + if (request->hasParam("CalibrateCO2")) { + inputString = request->getParam("CalibrateCO2")->value(); if (checkStringIsNumerical(inputString)) { Serial.printf("-->[WiFi] Received /settings command CalibrateCO2 with parameter %s\n", inputString); if ((inputString.toInt() >= 400) && (inputString.toInt() <= 2000)) { @@ -578,10 +580,22 @@ void initWebServer() { pendingCalibration = true; request->send(200, "text/plain", "OK. Recalibrating CO2 sensor to " + inputString); } else { - request->send(200, "text/plain", "Error. CO2 calibration value must be between 400 and 2000"); + request->send(400, "text/plain", "Error. CO2 calibration value must be between 400 and 2000"); } } }; + // /settings?displayShowBatteryVoltage=true + if (request->hasParam("displayShowBatteryVoltage")) { + String inputString = request->getParam("displayShowBatteryVoltage")->value(); + if (inputString.equalsIgnoreCase("true") || inputString.equalsIgnoreCase("false")) { + bool newValue = inputString.equalsIgnoreCase("true"); + displayShowBatteryVoltage = newValue; + Serial.printf("-->[WiFi] Received /settings command displayShowBatteryVoltage with parameter: %s\n", newValue ? "true" : "false"); + request->send(200, "text/plain", "OK. Setting displayShowBatteryVoltage to " + inputString); + } else { + request->send(400, "text/plain", "Error. Invalid parameter. Use 'true' or 'false'"); + } + } }); server.on("/getPreferences", HTTP_GET, [](AsyncWebServerRequest *request) { @@ -713,9 +727,8 @@ void initWifi() { activeWIFI = false; } if (activeWIFI) { - wifiChanged = true; - if (!connectToWiFi()) { + wifiChanged = false; return; } @@ -729,6 +742,8 @@ void initWifi() { // Try to connect to MQTT broker on next loop if needed troubledMQTT = false; + + wifiChanged = true; } } @@ -745,9 +760,10 @@ void wifiClientLoop() { Serial.println("-->[WiFi] RSSI: " + String(WiFi.RSSI()) + " dBm"); wifiSSID = WiFi.SSID(); activeWIFI = true; - putPreferences(); - // initWifi(); - wifiChanged = true; + if (wifiSSID != "") { + putPreferences(); + wifiChanged = true; + } } if ((wifiChanged) && (!inMenu)) { diff --git a/README.md b/README.md index 2ab72f90..be42de95 100644 --- a/README.md +++ b/README.md @@ -66,11 +66,11 @@ Supporting any other ESP32 board is very easy. Yoy just have to setup the pines These are the GPIOs used by each predefined board: -| Flavor | Display | RX/TX | I2C | UP/DWN | GPIO Orange | GPIO Red | GPIO Battery | GPIO Neopixel | GPIO Buzzer +| Flavor | Display | RX/TX | I2C SDA/SCL | UP/DWN | GPIO Orange | GPIO Red | GPIO Battery | GPIO Neopixel | GPIO Buzzer |:-----------------------|:----------------:|:--------:|:--------:|:--------:|:--------:|:--------:|:--------:|:--------:|:--------:| | TTGO_TDISPLAY TFT | TFT 240×135 | 13/12 | 21/22 | 35/0 | 32 | 33 | 34 | 26 | 2 | TTGO_TDISPLAY_SANDWICH | TFT 240×135 | 13/12 | 22/21 | 35/0 | 32 | 33 | 34 | 26 | 2 -| TDISPLAY_S3 | TFT 320x170 | 18/17 | 42/43 | 14/0 | 03 | 01 | 04 | 16 | 2 +| TDISPLAY_S3 | TFT 320x170 | 18/17 | 43/44 | 14/0 | 03 | 01 | 04 | 16 | 2 | esp32dev_OLED SSH1106 | SSH1106 128×64 | 17/16 | 21/22 | 15/0 | 32 | 33 | 34 | 26 | 2 | esp32dev | No display | 17/16 | 21/22 | 15/0 | 32 | 33 | 34 | 26 | 2 | esp32dev-sandwich | No display | 17/16 | 22/21 | 15/0 | 32 | 33 | 34 | 26 | 2 diff --git a/data/preferences.html b/data/preferences.html index e06a644d..5b22176a 100644 --- a/data/preferences.html +++ b/data/preferences.html @@ -618,7 +618,7 @@

CO2 Gadget Preferences

// If the user confirms, proceed with the restart if (isConfirmed) { // Send a request to the ESP32 endpoint to trigger a reset - fetch('/restart-esp32', { + fetch('/restart', { method: 'POST', headers: { 'Content-Type': 'application/json', diff --git a/icons.h b/icons.h index 018f32e2..56bbcab1 100644 --- a/icons.h +++ b/icons.h @@ -1,3 +1,5 @@ +// https://sourceforge.net/projects/lcd-image-converter/ +// https://notisrac.github.io/FileToCArray/ Drag and drop file on "Choose file..." button. Tick box "Treat as binary". Click "Convert". Click "Save as file" and move the header file to sketch folder. Open the sketch in IDE. Include the header file containing the array. // https://iconarchive.com/show/farm-fresh-icons-by-fatcow/temperature-5-icon.html // https://www.flaticon.com/search?word=bluetooth&order_by=4&type=icon @@ -44,6 +46,11 @@ const unsigned char iconEspNow[32] PROGMEM = { 0xff, 0xff, 0xff, 0xff, 0xc0, 0x03, 0xc0, 0x03, 0xe6, 0x67, 0xe2, 0x47, 0xf0, 0x0f, 0xf8, 0x1f, 0xfc, 0x3f, 0xfe, 0x7f, 0xfe, 0x7f, 0xfe, 0x7f, 0xfe, 0x7f, 0xfe, 0x7f, 0xff, 0xff, 0xff, 0xff}; +// 'iconUSB', 16x16px +static unsigned char iconUSB[32] PROGMEM = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xfc, 0x1f, 0xfd, 0xbf, 0x9b, 0xff, 0x03, 0x41, + 0x04, 0x00, 0x9e, 0xff, 0xfe, 0x4f, 0xff, 0x07, 0xff, 0xcf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + // 'iconHumidity', 16x16px const uint16_t iconHumidity[256] PROGMEM = { 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xc79f, 0xc79f, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, diff --git a/platformio.ini b/platformio.ini index 0037ad52..552dcbaf 100644 --- a/platformio.ini +++ b/platformio.ini @@ -1,13 +1,3 @@ -; PlatformIO Project Configuration File -; -; Build options: build flags, source filter -; Upload options: custom upload port, speed and extra flags -; Library options: dependencies, extra library storages -; Advanced options: extra scripting -; -; Please visit documentation for the other options and examples -; https://docs.platformio.org/page/projectconf.html - [platformio] src_dir = . data_dir = data @@ -60,15 +50,13 @@ build_flags = '-DWIFI_PW_CREDENTIALS=""' -D MQTT_BROKER_SERVER="\"192.168.1.145"\" - -D CO2_GADGET_VERSION="\"0.11."\" + -D CO2_GADGET_VERSION="\"0.12."\" -D CO2_GADGET_REV="\"000"\" -D CORE_DEBUG_LEVEL=0 -DCACHE_DIR=".pio/build" -DBUZZER_PIN=2 ; ESP32 pin GPIO13 connected to piezo buzzer -DNEOPIXEL_PIN=26 ; Pinnumber for button for down/next and back / exit actions -DNEOPIXEL_COUNT=16 ; How many neopixels to control - ; -DENABLE_PIN=27 ; Reserved for the future to enable the sensor - ; -DENABLE_PIN_HIGH=1 ; Should be ENABLE_PIN high or low to enable the sensor? -DADC_BATTERY_PIN=34 ; ADC GPIO PIN to read battery voltage -DBLUE_PIN=32 ; GPIO to go HIGH on orange color range -DBLUE_PIN_LOW=0 @@ -82,7 +70,7 @@ build_flags = -DSUPPORT_BLE ; Comment to dissable Bluetooth (makes more memory available) -DSUPPORT_BUZZER ; -DSUPPORT_ESPNOW - -USUPPORT_OTA ; + -DSUPPORT_OTA ; -DSUPPORT_MDNS ; -DSUPPORT_MQTT ; -DSUPPORT_MQTT_DISCOVERY @@ -147,7 +135,7 @@ build_flags = -DTFT_CS=5 -DTFT_DC=16 -DTFT_RST=23 - -DTFT_BL=4 + -DTFT_BACKLIGHT=4 -DTFT_BACKLIGHT_ON=HIGH -DLOAD_GLCD=1 -DLOAD_FONT2=1 @@ -197,7 +185,7 @@ build_flags = -DTFT_CS=5 -DTFT_DC=16 -DTFT_RST=23 - -DTFT_BL=4 + -DTFT_BACKLIGHT=4 -DTFT_BACKLIGHT_ON=HIGH -DLOAD_GLCD=1 -DLOAD_FONT2=1 @@ -253,7 +241,6 @@ build_flags = ${common_env_data.build_flags} -DSUPPORT_OTA -DARDUINO_ESP32_DEV=1 - -DTDISPLAY_S3 -DBTN_UP=14 ; Pinnumber for button for up/previous and select / enter actions -DBTN_DWN=0 ; Pinnum -DADC_BATTERY_PIN=4 ; ADC GPIO PIN to read battery voltage @@ -266,8 +253,8 @@ build_flags = -DBLUE_PIN=03 ; GPIO to go HIGH on orange color range -DSUPPORT_TFT -DLV_LVGL_H_INCLUDE_SIMPLE - -DARDUINO_USB_CDC_ON_BOOT=1 - -DARDUINO_USB_MODE=1 + ; -DARDUINO_USB_CDC_ON_BOOT=1 + ; -DARDUINO_USB_MODE=1 -DTOUCH_MODULES_CST_MUTUAL -DUSER_SETUP_LOADED=1 -DBOARD_HAS_PSRAM @@ -294,7 +281,7 @@ build_flags = -DTFT_D5=46 -DTFT_D6=47 -DTFT_D7=48 - -DTFT_BL=38 + -DTFT_BACKLIGHT=38 -DTFT_POWER_ON_BATTERY=15 ; Pin must be high to power on display when on battery -DTFT_BACKLIGHT_ON=HIGH -DLOAD_GLCD @@ -347,7 +334,7 @@ build_flags = -DTFT_CS=5 -DTFT_DC=16 -DTFT_RST=-1 - -DTFT_BL=17 + -DTFT_BACKLIGHT=17 -DLOAD_GLCD=1 -DLOAD_FONT2=1 -DLOAD_FONT4=1