diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..89cc49c --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.pio +.vscode/.browse.c_cpp.db* +.vscode/c_cpp_properties.json +.vscode/launch.json +.vscode/ipch diff --git a/.gitpod.Dockerfile b/.gitpod.Dockerfile new file mode 100644 index 0000000..7fc22ba --- /dev/null +++ b/.gitpod.Dockerfile @@ -0,0 +1,5 @@ +FROM gitpod/workspace-full + +USER gitpod + +RUN pip3 install -U platformio && npm install html-minifier-terser -g \ No newline at end of file diff --git a/.gitpod.yml b/.gitpod.yml new file mode 100644 index 0000000..dd4cc5d --- /dev/null +++ b/.gitpod.yml @@ -0,0 +1,11 @@ +vscode: + extensions: + - vscode.cpp + +tasks: + - before: platformio upgrade + - init: platformio test -e native + - command: platformio run -e lolin32 + +image: + file: .gitpod.Dockerfile diff --git a/Boot.gif b/Boot.gif deleted file mode 100644 index 9fac7e4..0000000 Binary files a/Boot.gif and /dev/null differ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..bb53b93 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 lolwheel + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 31838f1..27a331f 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,11 @@ # OwieWatcher -A simple ESP8266 based sketch to connect to an [owie](https://github.com/lolwheel/Owie) device and pull the battery info from the status webpage for output to an OLED display. The current code configuration outputs the Voltage, BMS SOC & OVERRIDEN SOC. +[![Gitpod Ready-to-Code](https://img.shields.io/badge/Gitpod-Ready--to--Code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/tonyt321/OWIE-OLED) + +todo +- get gitpod working idk how to save from gitpod to here +- use the u8g2 library better display support + +A simple ESP8266 based sketch to connect to an [owie](https://github.com/tonyt321/OWIE-OLED) device and pull the battery info from the status webpage for output to an OLED display. The current code configuration outputs the Voltage, BMS SOC & OVERRIDEN SOC. ## Demo
OwieWatcher bootup

diff --git a/lib/bms/desktop.ini b/lib/bms/desktop.ini new file mode 100644 index 0000000..694dfa4 --- /dev/null +++ b/lib/bms/desktop.ini @@ -0,0 +1 @@ +[LocalizedFileNames] diff --git a/owie-watcher-logo_EXAMPLE.bmp b/owie-watcher-logo_EXAMPLE.bmp deleted file mode 100644 index 2765794..0000000 Binary files a/owie-watcher-logo_EXAMPLE.bmp and /dev/null differ diff --git a/owie-watcher.ino b/owie-watcher.ino deleted file mode 100644 index b8b2bb7..0000000 --- a/owie-watcher.ino +++ /dev/null @@ -1,192 +0,0 @@ -#include -#include -#include -#include -#include -#include - -ESP8266WiFiMulti WiFiMulti; - -// REPLACE WITH YOUR NETWORK CREDENTIALS -char* ssid = "owie-ssid"; -char* password = "owie-wifi-password"; -#define OLED_RESET 0 // GPIO0 -Adafruit_SSD1306 display(OLED_RESET); - -//Boot Logo, change if you dont like it -static const unsigned char PROGMEM logo16_glcd_bmp[] = -{ 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, - 0b11000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000011, - 0b10000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000001, - 0b10000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000001, - 0b10000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000001, - 0b10000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000001, - 0b10000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000001, - 0b10000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000001, - 0b10000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000001, - 0b10000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000001, - 0b10000000, 0b00000000, 0b01000000, 0b00000000, 0b00000000, 0b10000001, 0b00000000, 0b00000001, - 0b10000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b10000001, 0b00000000, 0b00000001, - 0b10000110, 0b01001001, 0b01001100, 0b00010010, 0b01011101, 0b11011001, 0b01000110, 0b01010001, - 0b10001001, 0b01010101, 0b01010010, 0b00010101, 0b01000010, 0b10100101, 0b10101001, 0b01100001, - 0b10001001, 0b01010101, 0b01011110, 0b00010101, 0b01001110, 0b10100001, 0b00101111, 0b01000001, - 0b10001001, 0b01010101, 0b01010000, 0b00010101, 0b01010010, 0b10100001, 0b00101000, 0b01000001, - 0b10001001, 0b01010101, 0b01010010, 0b00010101, 0b01010010, 0b10100101, 0b00101001, 0b01000001, - 0b10000110, 0b00100010, 0b01001100, 0b00001000, 0b10001110, 0b11011001, 0b00100110, 0b01000001, - 0b10000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000001, - 0b10000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000001, - 0b10000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000001, - 0b10000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000001, - 0b10000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000001, - 0b10000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000001, - 0b10000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000001, - 0b10000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000001, - 0b10000000, 0b00000000, 0b00000000, 0b00000011, 0b11000000, 0b00000000, 0b00000000, 0b00000001, - 0b10000000, 0b00000000, 0b00000000, 0b00001111, 0b11110000, 0b00000000, 0b00000000, 0b00000001, - 0b10000000, 0b00000000, 0b00000000, 0b00011100, 0b00111000, 0b00000000, 0b00000000, 0b00000001, - 0b10000000, 0b00000000, 0b00000000, 0b00111000, 0b00011100, 0b00000000, 0b00000000, 0b00000001, - 0b10000000, 0b00000000, 0b00000000, 0b00110000, 0b00001100, 0b00000000, 0b00000000, 0b00000001, - 0b10000000, 0b00011111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111000, 0b00000001, - 0b10000000, 0b00001000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00010000, 0b00000001, - 0b10000000, 0b00000100, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00100000, 0b00000001, - 0b10000000, 0b00000011, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11000000, 0b00000001, - 0b10000000, 0b00000000, 0b00000000, 0b00110000, 0b00001100, 0b00000000, 0b00000000, 0b00000001, - 0b10000000, 0b00000000, 0b00000000, 0b00111000, 0b00011100, 0b00000000, 0b00000000, 0b00000001, - 0b10000000, 0b00000000, 0b00000000, 0b00011100, 0b00111000, 0b00000000, 0b00000000, 0b00000001, - 0b10000000, 0b00000000, 0b00000000, 0b00001111, 0b11110000, 0b00000000, 0b00000000, 0b00000001, - 0b10000000, 0b00000000, 0b00000000, 0b00000011, 0b11000000, 0b00000000, 0b00000000, 0b00000001, - 0b10000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000001, - 0b10000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000001, - 0b10000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000001, - 0b10000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000001, - 0b10000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000001, - 0b10000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000001, - 0b11000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000011, - 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111, 0b11111111 }; - -void setup() { - - Serial.begin(115200); - display.begin(SSD1306_SWITCHCAPVCC, 0x3C); - display.clearDisplay(); - display.drawBitmap(0, 0, logo16_glcd_bmp, 64, 48, 1); - display.display(); - delay(2000); - display.clearDisplay(); - - Serial.println(); - Serial.println(); - Serial.println(); - - for (uint8_t t = 4; t > 0; t--) { - Serial.printf("[SETUP] WAIT %d...\n", t); - Serial.flush(); - delay(1000); - } - - WiFi.mode(WIFI_STA); - WiFiMulti.addAP(ssid,password); -} - -void loop() { - // wait for WiFi connection - if ((WiFiMulti.run() == WL_CONNECTED)) { - - WiFiClient client; - HTTPClient http; - - display.begin(SSD1306_SWITCHCAPVCC, 0x3C); - display.setTextSize(1); - display.setTextColor(WHITE,BLACK); - - display.setCursor(0, 0); - Serial.print("[HTTP] begin...\n"); - - http.begin(client, "http://192.168.4.1"); - - Serial.print("[HTTP] GET...\n"); - int httpCode = http.GET(); - if (httpCode > 0) { - Serial.printf("[HTTP] GET... code: %d\n", httpCode); - - if (httpCode == HTTP_CODE_OK) { - int len = http.getSize(); - -#if 0 - // with API - Serial.println(http.getString()); -#else - WiFiClient* stream = &client; - - while (http.connected() && (len > 0 || len == -1)) { - char vbuff[5] = {0}; - char socbuff[4] = {0}; - char ovrbuff[4] = {0}; - display.setCursor(0, 0); - stream->find("Voltage"); - while(stream->find("TOTAL_VOLTAGE\">")){ - stream->readBytesUntil('<',vbuff,5); - Serial.print(vbuff); - display.print("VOL:"); - display.print(vbuff); - display.print("v"); - display.println(); - display.display(); - stream->find("BMS_SOC\">"); - stream->readBytesUntil('<',socbuff,4); - display.println(); - display.print("BMS:"); - Serial.println(); - Serial.print(socbuff); - display.print(socbuff); - display.println(); - display.display(); - stream->find("OVERRIDDEN_SOC\">"); - stream->readBytesUntil('<',ovrbuff,4); - Serial.println(); - Serial.print(ovrbuff); - display.println(); - display.print("OVR:"); - display.print(ovrbuff); - display.println(); - display.display(); - display.print(" "); - display.display(); - display.setCursor(0, 41); - delay(5 * 1000); - display.print("REFRESH"); - display.display(); - Serial.print("REFRESH"); - delay(1 * 1000); - display.print("."); - Serial.print("."); - display.display(); - delay(1 * 1000); - display.print("."); - Serial.print("."); - display.display(); - delay(1 * 1000); - display.print("."); - Serial.print("."); - display.display(); - delay(1 * 1000); - return; - } - } -#endif - Serial.println(); - Serial.print("[HTTP] connection closed or file end.\n"); - } - } else { - Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str()); - } - - http.end(); - } - - Serial.print("RETRYING..."); - display.setCursor(0, 41); - display.print("RETRYING "); - display.display(); - delay(30 * 1000); -} diff --git a/pio_tools/gen_data.py b/pio_tools/gen_data.py new file mode 100644 index 0000000..5e55c4b --- /dev/null +++ b/pio_tools/gen_data.py @@ -0,0 +1,74 @@ +Import("env") +import os +import subprocess + +from SCons.Script import COMMAND_LINE_TARGETS + +if "idedata" in COMMAND_LINE_TARGETS: + env.Exit(0) + + +def ReadAndMaybeMinifyFiles(fullPath): + _, extension = os.path.splitext(fullPath) + if not extension in ['.html', '.js', '.css']: + with open(fullPath, "rb") as f: + return f.read() + originalSize = os.stat(fullPath).st_size + result = subprocess.run(['html-minifier-terser', + '--collapse-whitespace', + '--remove-comments', + '--minify-js', + 'true', + '--minify-css', + 'true', + fullPath], stdout=subprocess.PIPE) + minifiedContent = result.stdout + print("Minified '%s' with from %d to %d bytes" % (fullPath, originalSize, len(minifiedContent))) + return minifiedContent + + +def GenData(): + dataDir = os.path.join(env["PROJECT_DIR"], "data") + print("dataDir = %s" % dataDir) + genDir = os.path.join(env.subst("$BUILD_DIR"), 'inline_data') + print("genDir = %s" % genDir) + if not os.path.exists(dataDir): + return + if not os.path.exists(genDir): + os.mkdir(genDir) + env.Append(CPPPATH=[genDir]) + + files = sorted(file for file in os.listdir(dataDir) + if os.path.isfile(os.path.join(dataDir, file))) + + out = "// WARNING: Autogenerated by pio_tools/gen_data.py, don't edit manually.\n" + out += "#ifndef OWIE_GENERATED_DATA_H\n" + out +="#define OWIE_GENERATED_DATA_H\n\n" + for name in files: + varName = name.upper().replace(".", "_") + sizeName = varName + "_SIZE" + storageArrayName = varName + "_PROGMEM_ARRAY" + out += ( + "static const unsigned char %s[] PROGMEM = {\n " % storageArrayName) + firstByte = True + fileContent = ReadAndMaybeMinifyFiles(os.path.join(dataDir, name)) + column = 0 + for b in fileContent: + if not firstByte: + out += "," + else: + firstByte = False + column = column + 1 + if column > 20: + column = 0 + out += "\n " + out += str(b) + out += "};\n" + out += "#define %s FPSTR(%s)\n" % (varName, storageArrayName) + out += "#define %s sizeof(%s)\n\n" % (sizeName, storageArrayName) + out += "\n#endif // OWIE_GENERATED_DATA_H\n" + with open(os.path.join(genDir, "data.h"), 'w') as f: + f.write(out) + print("Wrote data.h\n") + +GenData() diff --git a/pio_tools/platformio_upload.py b/pio_tools/platformio_upload.py new file mode 100644 index 0000000..02e735d --- /dev/null +++ b/pio_tools/platformio_upload.py @@ -0,0 +1,53 @@ +# Allows PlatformIO to upload directly to AsyncElegantOTA +# +# To use: +# - copy this script into the same folder as your platformio.ini +# - set the following for your project in platformio.ini: +# +# extra_scripts = platformio_upload.py +# upload_protocol = custom +# upload_url = +# +# An example of an upload URL: +# upload_URL = http://192.168.1.123/update + +import requests +import hashlib +Import('env') + +try: + from requests_toolbelt import MultipartEncoder, MultipartEncoderMonitor + from tqdm import tqdm +except ImportError: + env.Execute("$PYTHONEXE -m pip install requests_toolbelt") + env.Execute("$PYTHONEXE -m pip install tqdm") + from requests_toolbelt import MultipartEncoder, MultipartEncoderMonitor + from tqdm import tqdm + +def on_upload(source, target, env): + firmware_path = str(source[0]) + upload_url = env.GetProjectOption('upload_url') + + with open(firmware_path, 'rb') as firmware: + md5 = hashlib.md5(firmware.read()).hexdigest() + firmware.seek(0) + encoder = MultipartEncoder(fields={ + 'MD5': md5, + 'firmware': ('firmware', firmware, 'application/octet-stream')} + ) + + bar = tqdm(desc='Upload Progress', + total=encoder.len, + dynamic_ncols=True, + unit='B', + unit_scale=True, + unit_divisor=1024 + ) + + monitor = MultipartEncoderMonitor(encoder, lambda monitor: bar.update(monitor.bytes_read - bar.n)) + + response = requests.post(upload_url, data=monitor, headers={'Content-Type': monitor.content_type}) + bar.close() + print(response,response.text) + +env.Replace(UPLOADCMD=on_upload) \ No newline at end of file diff --git a/platformio.ini b/platformio.ini new file mode 100644 index 0000000..f4bd7a5 --- /dev/null +++ b/platformio.ini @@ -0,0 +1,92 @@ +[platformio] +; point data_dir to nonexistent directory so that PIO doesn't bother building SPIFFS +data_dir = nonexistent + +[env:d1_mini_lite_clone] +platform = espressif8266 +upload_speed = 524288 +monitor_speed = 115200 +board = d1_mini +# Following is necessary for cheap Wemos D1 Lite clones. +# Without this line, flashing succeeds but programs simply don't run on the chip. +board_build.flash_mode = dout +framework = arduino +board_build.ldscript = eagle.flash.1m.ld + +custom_nanopb_protos = + + +custom_nanopb_options = + --error-on-unmatched + +extra_scripts = + pre:pio_tools/gen_data.py + +build_flags = + ; Disable global instances to save space + -DNO_GLOBAL_INSTANCES + ;-DDEBUG_ESP_PORT=Serial + ;-DDEBUG_EEPROM_ROTATE_PORT=Serial + ;-DDEBUG_ESP_CORE + ;-DDEBUG_ESP_WIFI + ;-DDEBUG_ESP_UPDATER + ;-DDEBUG_ESP_PORT=Serial + ;-DDEBUG_UPDATER=Serial + + +lib_deps = + xoseperez/EEPROM_Rotate@^0.9.2 + ottowinter/ESPAsyncWebServer-esphome@^2.1.0 + nanopb/Nanopb@^0.4.6 + bblanchon/ArduinoJson@^6.19.4 + +[env:ota] +extends = env:d1_mini_lite_clone +extra_scripts = + pre:pio_tools/gen_data.py + pio_tools/platformio_upload.py +upload_url = http://owie-c024.lan/update +upload_protocol = custom +;board_build.gzip_fw = true + +[env:native] +platform = native +debug_test = test_bms_relay + + + +[env:lolin32] +platform = espressif32 +upload_speed = 524288 +monitor_speed = 115200 +board = lolin32 +# Following is necessary for cheap Wemos D1 Lite clones. +# Without this line, flashing succeeds but programs simply don't run on the chip. +board_build.flash_mode = dout +framework = arduino +#board_build.ldscript = eagle.flash.1m.ld + +#custom_nanopb_protos = +# + +#custom_nanopb_options = +# --error-on-unmatched + +extra_scripts = + pre:pio_tools/gen_data.py + +build_flags = + ; Disable global instances to save space + ;-DNO_GLOBAL_INSTANCES #enable this to disable serial port + ;-DDEBUG_ESP_PORT=Serial + ;-DDEBUG_EEPROM_ROTATE_PORT=Serial + ;-DDEBUG_ESP_CORE + ;-DDEBUG_ESP_WIFI + ;-DDEBUG_ESP_UPDATER + ;-DDEBUG_ESP_PORT=Serial + ;-DDEBUG_UPDATER=Serial + + +#lib_deps = +# xoseperez/EEPROM_Rotate@^0.9.2 +# ottowinter/ESPAsyncWebServer-esphome@^2.1.0 +# nanopb/Nanopb@^0.4.6 +# bblanchon/ArduinoJson@^6.19.4 \ No newline at end of file diff --git a/src/owie-watcher.ino b/src/owie-watcher.ino new file mode 100644 index 0000000..b757cfd --- /dev/null +++ b/src/owie-watcher.ino @@ -0,0 +1,48 @@ + +#include +#include "time.h" + +const char* ssid = "YOUR_SSID"; +const char* password = "YOUR_PASS"; + +const char* ntpServer = "pool.ntp.org"; +const long gmtOffset_sec = 3600; +const int daylightOffset_sec = 3600; + +void printLocalTime() +{ + struct tm timeinfo; + if(!getLocalTime(&timeinfo)){ + // Serial.println("Failed to obtain time"); + return; + } + // Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S"); +} + +void setup() +{ + Serial.begin(115200); + + //connect to WiFi + //Serial.printf("Connecting to %s ", ssid); + WiFi.begin(ssid, password); + while (WiFi.status() != WL_CONNECTED) { + delay(500); + // Serial.print("."); + } + //Serial.println(" CONNECTED"); + + //init and get the time + configTime(gmtOffset_sec, daylightOffset_sec, ntpServer); + printLocalTime(); + + //disconnect WiFi as it's no longer needed + WiFi.disconnect(true); + WiFi.mode(WIFI_OFF); +} + +void loop() +{ + delay(1000); + printLocalTime(); +} \ No newline at end of file