diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..fc0ebae --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,85 @@ +name: Build + +on: + push: + branches: + - master + # tags: + # - "v*" + # pull_request: + # types: [ open, synchronize, edited, reopened, closed ] + +jobs: + build: + name: Build bins for ${{ matrix.pio_env }} + strategy: + # Ensure that a wheel builder finishes even if another fails + fail-fast: false + matrix: + pio_env: ['lcd_ssd1306', 'd32_pro_tft', 'tft_espi', 'm5stickc_plus'] + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3.3.0 + - run: git fetch --prune --unshallow + + - name: Cache pip + uses: actions/cache@v3.2.2 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} + restore-keys: | + ${{ runner.os }}-pip- + + - name: Cache PlatformIO + uses: actions/cache@v3.2.2 + with: + path: ~/.platformio + key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }} + + - name: Set up Python + uses: actions/setup-python@v4.4.0 + + - name: Install PlatformIO + run: | + python -m pip install --upgrade pip + pip install --upgrade platformio + + - name: Build all environments + run: | + pio run -e ${{ matrix.pio_env }} + pio run -e ${{ matrix.pio_env }} --target buildfs + # sh ./copy_bins.sh + + - name: Copy files + run: | + cp .pio/build/${{ matrix.pio_env }}/firmware.bin bin/${{ matrix.pio_env }}_firmware.bin + cp .pio/build/${{ matrix.pio_env }}/partitions.bin bin/${{ matrix.pio_env }}_partitions.bin + cp .pio/build/${{ matrix.pio_env }}/spiffs.bin bin/${{ matrix.pio_env }}_spiffs.bin + + + # - name: "Create Prerelease" + # uses: "marvinpinto/action-automatic-releases@latest" + # with: + # repo_token: "${{ secrets.GITHUB_TOKEN }}" + # prerelease: true + # files: | + # LICENSE + # bin/*.bin + + - name: Create Draft Release + uses: softprops/action-gh-release@v1 + # if: startsWith(github.ref, 'refs/tags/') + with: + body: "Draft release" + # note you'll typically need to create a personal access token + # with permissions to create releases in the other repo + token: ${{ secrets.GITHUB_TOKEN }} + draft: true + prerelease: true + files: | + LICENSE + bin/*.bin + env: + GITHUB_REPOSITORY: thorrak/tiltbridge \ No newline at end of file diff --git a/data/settings.htm b/data/settings.htm index 76a0d0a..93a1c7f 100644 --- a/data/settings.htm +++ b/data/settings.htm @@ -112,6 +112,7 @@ Brewstatus Taplist.io MQTT + Generic JSON Target @@ -536,6 +537,36 @@
Brewfather Settings
+
+ +
+
User Target Settings
+
+ +
+

+ These settings control how TiltBridge talks to a user-defined target. Specify a url + here and TiltBridge will send a +

+
+
+ +
+ +
+
+
+ +
+
+
+ +
+
diff --git a/data/settings.js b/data/settings.js index 2ca49df..546496f 100644 --- a/data/settings.js +++ b/data/settings.js @@ -182,6 +182,9 @@ function populateConfig(callback = null) { // Get configuration settings, popula // Brewfather Tab $('input[name="brewfatherKey"]').val(config.brewfatherKey); + // Brewfather Tab + $('input[name="userTargetURL"]').val(config.userTargetURL); + // Grainfather Tab $('input[name="grainfatherURL_red"]').val(config.Red.grainfatherURL); $('input[name="grainfatherURL_green"]').val(config.Green.grainfatherURL); @@ -306,6 +309,9 @@ function processPost(obj) { // Disable buttons and call POST handler for form case "#brewfather": processBrewfatherPost(url, obj); break; + case "#usertarget": + processUserTargetPost(url, obj); + break; case "#grainfather": processGrainfatherPost(url, obj); break; @@ -475,6 +481,19 @@ function processBrewfatherPost(url, obj) { // Handle Brewfather posts postData(url, data); } +function processUserTargetPost(url, obj) { // Handle User Target posts + // Get form data + var $form = $(obj.form), + userTargetKeyVal = $form.find("input[name='userTargetURL']").val(); + + // Process post + data = { + userTargetURL: userTargetKeyVal + }; + postData(url, data); +} + + function processGrainfatherPost(url, obj) { // Handle Grainfather posts // Get form data var $form = $(obj.form), diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index 10134d8..1786916 100644 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -3,6 +3,16 @@ Changelog +v1.2.0 - Jan 9, 2023 - Fix OLED & Refactor LCD Code +--------------------------------------------------- + +- Fix OLED firmware +- Refactor LCD code to combine "D32 TFT" and "ESPI TFT" code +- Replace existing M5 libraries with (existing) ESPI TFT library +- Add new "User Target" data destination +- Add support for OLED display at pins 17 & 18 + + v1.1.3 - Aug 18, 2022 - Fix WiFi Manager ---------------------------------------- diff --git a/platformio.ini b/platformio.ini index eb30781..579ebed 100644 --- a/platformio.ini +++ b/platformio.ini @@ -66,7 +66,7 @@ lib_deps = https://github.com/thorrak/Arduino-Log.git ; // Need this until ArduinoLog merges https://github.com/thijse/Arduino-Log/pull/23 https://github.com/lbussy/esptelnet.git https://github.com/me-no-dev/ESPAsyncWebServer.git - https://github.com/tzapu/WiFiManager.git#e0c5fb7daf4c44f2753fdc7325b9d47ad154ed30 ;#feature_asyncwebserver + https://github.com/thorrak/WiFiManager.git#feature_asyncwebserver h2zero/NimBLE-Arduino @ 1.3.4 ; https://github.com/h2zero/NimBLE-Arduino.git 256dpi/MQTT @ 2.4.8 lbussy/LCBUrl @ ^1.1.7 @@ -196,26 +196,49 @@ lib_deps = bodmer/TFT_eSPI @ 2.4.72 ; https://github.com/Bodmer/TFT_eSPI.git build_type = ${common.build_type} -[env:m5stickc] ; M5Stack M5Stick-C -board = m5stick-c -board_build.f_cpu = 240000000L + +[env:m5stickc_plus] +board = esp32dev platform = ${common.platform} ; platform_packages = ${common.platform_packages} framework = ${common.framework} +; The M5StickC Plus has 4MB of flash. Use the custom 4MB partition to allow enough space +; for both OTA and SPIFFS. board_build.partitions = ${common.board_build.partitions} upload_speed = 1500000 monitor_speed = ${common.monitor_speed} monitor_filters = ${common.monitor_filters} -; This can/will be set in tools/get_port.py -; upload_port = {common.upload_port} -; monitor_port = ${common.monitor_port} monitor_dtr = ${common.monitor_dtr} monitor_rts = ${common.monitor_rts} build_flags = ${common.build_flags} + ; -DM5STICKC_PLUS -DLCD_TFT_M5STICKC=1 + -DLCD_TFT_ESPI=1 + -DDISABLE_OTA_UPDATES +; -DBUTTON_NO_PULLUP=1 + -DBUTTON_INVERT + -DLCD_TFT_ESPI=1 + -DAXP192=1 + ; -DDISABLE_OTA_UPDATES=1 + -DUSER_SETUP_LOADED=1 + -DST7789_DRIVER=1 + -DTFT_WIDTH=135 + -DTFT_HEIGHT=240 + -DCGRAM_OFFSET=1 + -DTFT_MISO=-1 + -DTFT_MOSI=15 + -DTFT_SCLK=13 + -DTFT_CS=5 + -DTFT_DC=23 + -DTFT_RST=18 + -DLOAD_GFXFF=1 + -DWIFI_RESET_BUTTON_GPIO=37 -DDISABLE_OTA_UPDATES +; -DBOARD_RESET_BUTTON_GPIO=39 lib_deps = ${common.lib_deps} - m5stack/M5StickC @ 0.2.0 + bodmer/TFT_eSPI @ 2.4.79 ; https://github.com/Bodmer/TFT_eSPI.git + tanakamasayuki/I2C AXP192 Power management@^1.0.4 build_type = ${common.build_type} +check_skip_packages = yes diff --git a/src/bridge_lcd.cpp b/src/bridge_lcd.cpp index f3335be..398ab7d 100644 --- a/src/bridge_lcd.cpp +++ b/src/bridge_lcd.cpp @@ -1,6 +1,7 @@ #include "bridge_lcd.h" #include "jsonconfig.h" #include +#include #ifdef LCD_SSD1306 #include @@ -8,12 +9,17 @@ bridge_lcd lcd; -#if defined(LCD_SSD1306) || defined(LCD_TFT_ESPI) || defined(LCD_TFT_M5STICKC) +#if defined(LCD_SSD1306) || defined(LCD_TFT_ESPI) #include "img/oled_logo.h" // Small logo -#elif defined LCD_TFT +#elif defined(LCD_TFT) #include "img/tft_logo.h" // Large logo #endif +#if defined(AXP192) +#include // This mostly applies to m5 Stick so we can control the TFT backlight +I2C_AXP192 axp192(I2C_AXP192_DEFAULT_ADDRESS, Wire1); +#endif + bool onResetScreen = false; @@ -33,6 +39,27 @@ bridge_lcd::bridge_lcd() { #if HAVE_LCD void bridge_lcd::init() { +#ifdef AXP192 + // For m5 stick and whatnot, the LCD backlight AND the controller both are powered off the AXP192, so we need to initialize that first + Wire1.begin(21, 22); + + I2C_AXP192_InitDef initDef = { + .EXTEN = true, + .BACKUP = true, + .DCDC1 = 3300, + .DCDC2 = 0, + .DCDC3 = 0, + .LDO2 = 3000, + .LDO3 = 3000, + .GPIO0 = 2800, + .GPIO1 = -1, + .GPIO2 = -1, + .GPIO3 = -1, + .GPIO4 = -1, + }; + axp192.begin(initDef); +#endif + #ifdef LCD_SSD1306 // We're currently supporting three sets of hardware - The ESP32 "OLED" // board, TTGO Boards, and the TiltBridge sleeve @@ -52,7 +79,17 @@ void bridge_lcd::init() { oled_display = new SSD1306Wire(0x3c, 4, 15); } else { digitalWrite(16, LOW); // We weren't able to find the TTGO board, so reset the pin - oled_display = new SSD1306Wire(0x3c, 21, 22); // ... and just default to the "sleeve" configuration + + pinMode(21, OUTPUT); + digitalWrite(21, LOW); // Set GPIO16 low to reset OLED + delay(50); + digitalWrite(21, HIGH); // While OLED is running, must set GPIO16 in high + if (i2c_device_at_address(0x3c, 17, 18)) { + oled_display = new SSD1306Wire(0x3c, 17, 18); + } else { + digitalWrite(21, LOW); // We weren't able to find the TTGO board, so reset the pin + oled_display = new SSD1306Wire(0x3c, 21, 22); // ... and just default to the "sleeve" configuration + } } } @@ -66,59 +103,27 @@ void bridge_lcd::init() { oled_display->resetOrientation(); } oled_display->setFont(ArialMT_Plain_10); -#elif defined(LCD_TFT) - tft = new TFT_eSPI(); - tft->begin(); - tft->setFreeFont(&FreeSans12pt7b); - tft->setSwapBytes(true); - tft->initDMA(); - -#ifdef LCD_TFT_M5_STACK - // +4 "mirrors" the text (supposedly) - tft->setRotation(0); -#else // ! LCD_TFT_M5_STACK - if (config.invertTFT) - { - tft->setRotation(1); - } - else - { - tft->setRotation(3); - } -#endif // ! LCD_TFT_M5_STACK - tft->fillScreen(TFT_BLACK); - tft->setTextColor(TFT_WHITE, TFT_BLACK); + +#elif defined(LCD_TFT_ESPI) || defined(LCD_TFT) + tft = new TFT_eSPI(TFT_WIDTH, TFT_HEIGHT); + tft->init(); + reinit(); + +#if defined(LCD_TFT) + tft->setFreeFont(&FreeSans12pt7b); +#endif // LCD_TFT #ifdef TFT_BACKLIGHT pinMode(TFT_BACKLIGHT, OUTPUT); digitalWrite(TFT_BACKLIGHT, HIGH); #endif // TFT_BACKLIGHT -#elif defined(LCD_TFT_ESPI) - tft = new TFT_eSPI(TFT_WIDTH, TFT_HEIGHT); - tft->init(); - if (config.invertTFT) { - tft->setRotation(1); - } else { - tft->setRotation(3); - } - tft->fillScreen(TFT_BLACK); - -#elif defined(LCD_TFT_M5STICKC) - M5.begin(); - tft = &M5.Lcd; - if (config.invertTFT) { - tft->setRotation(1); - } else { - tft->setRotation(3); - } - tft->fillScreen(TFT_BLACK); #endif // LCD_TFT_ESPI } void bridge_lcd::reinit() { -#if defined (LCD_TFT) || defined (LCD_TFT_ESPI) || defined (LCD_TFT_M5STICKC) +#if defined(LCD_TFT) || defined(LCD_TFT_ESPI) clear(); if (config.invertTFT) { tft->setRotation(1); @@ -161,7 +166,7 @@ void bridge_lcd::display_logo(bool fromReset) { gimp_image.width, gimp_image.height, gimp_image.pixel_data); -#elif defined(LCD_TFT_ESPI) || defined (LCD_TFT_M5STICKC) +#elif defined(LCD_TFT_ESPI) tft->drawXBitmap( (tft->width() - oled_logo_width) / 2, (tft->height() - oled_logo_height) / 2, @@ -220,7 +225,7 @@ void bridge_lcd::display_wifi_success_screen(const char *mdns_url, const char *i // Displayed at startup when the TiltBridge is configured to connect to WiFi clear(); -#if defined(LCD_TFT_ESPI) || defined(LCD_TFT_M5STICKC) +#if defined(LCD_TFT_ESPI) print_line("Access TiltBridge at:", "", 1); #else print_line("Access your TiltBridge at:", "", 1); @@ -239,7 +244,7 @@ void bridge_lcd::display_wifi_reset_screen() { clear(); onResetScreen = true; -#if defined(LCD_SSD1306) || defined(LCD_TFT_ESPI) || defined(LCD_TFT_M5STICKC) +#if defined(LCD_SSD1306) || defined(LCD_TFT_ESPI) print_line("Press the button again to", "", 1); print_line("disable autoconnection", "", 2); print_line("and start the WiFi ", "", 3); @@ -271,7 +276,7 @@ void bridge_lcd::display_ota_update_screen() void bridge_lcd::print_line(const char *left_text, const char *right_text, uint8_t line) { -#if defined(LCD_TFT_ESPI) || defined(LCD_TFT_M5STICKC) +#if defined(LCD_TFT_ESPI) print_line("", left_text, right_text, line); #else print_line(left_text, "", right_text, line); @@ -322,11 +327,6 @@ void bridge_lcd::print_line(const char *left_text, const char *middle_text, cons // TFT_eSPI::drawString(const char *string, int32_t poX, int32_t poY, uint8_t font_number) tft->setFreeFont(FF17); - tft->drawString(middle_text, 0, starting_pixel_row, GFXFF); - tft->drawString(right_text, tft->width() / 2, starting_pixel_row, GFXFF); -#elif defined(LCD_TFT_M5STICKC) - int16_t starting_pixel_row = (TFT_M5STICKC_LINE_CLEARANCE + tft->fontHeight(GFXFF)) * (line - 1) + TFT_M5STICKC_LINE_CLEARANCE; - tft->drawString(middle_text, 0, starting_pixel_row, GFXFF); tft->drawString(right_text, tft->width() / 2, starting_pixel_row, GFXFF); #endif @@ -342,7 +342,7 @@ void bridge_lcd::clear() { #ifdef LCD_SSD1306 oled_display->clear(); oled_display->setFont(SSD1306_FONT); -#elif defined (LCD_TFT) || defined (LCD_TFT_ESPI) || defined(LCD_TFT_M5STICKC) +#elif defined(LCD_TFT) || defined(LCD_TFT_ESPI) tft->fillScreen(TFT_BLACK); #endif @@ -491,7 +491,7 @@ void bridge_lcd::print_tilt_to_line(tiltHydrometer *tilt, uint8_t line) { sprintf(gravity, "%s", tilt->converted_gravity(false).c_str()); sprintf(temp, "%s %s", tilt->converted_temp(false).c_str(), tilt->is_celsius() ? "C" : "F"); -#if defined (LCD_TFT_ESPI) || defined (LCD_TFT_M5STICKC) +#if defined(LCD_TFT_ESPI) tft->setTextColor(tilt_text_colors[tilt->m_color]); #endif @@ -522,7 +522,7 @@ void bridge_lcd::print_tilt_to_line(tiltHydrometer *tilt, uint8_t line) { fHeight - 8, tilt_text_colors[tilt->m_color]); } -#elif defined(LCD_TFT_ESPI) || defined(LCD_TFT_M5STICKC) +#elif defined(LCD_TFT_ESPI) tft->setTextColor(TFT_WHITE); #endif } @@ -533,9 +533,13 @@ bool bridge_lcd::i2c_device_at_address(byte address, int sda_pin, int scl_pin) { // multiple OLED ESP32 boards byte error; - Wire.begin(sda_pin, scl_pin); + if(!Wire.begin(sda_pin, scl_pin)) { + Log.error(F("Failed to initialize Wire on pin %d/%d\r\n"), sda_pin, scl_pin); + return false; // Failed to initialize twowire on selected sda/scl + } Wire.beginTransmission(address); error = Wire.endTransmission(); + Wire.end(); if (error == 0) // No error means that a device responded return true; diff --git a/src/bridge_lcd.h b/src/bridge_lcd.h index af72355..9de3c94 100644 --- a/src/bridge_lcd.h +++ b/src/bridge_lcd.h @@ -14,6 +14,7 @@ #define SSD_LINE_CLEARANCE 2 #define SSD1306_FONT ArialMT_Plain_10 #define TILTS_PER_PAGE 5 // The actual number is one fewer than this - the first row is used for headers +#define HAVE_LCD 1 #elif defined(LCD_TFT) @@ -24,6 +25,7 @@ #define TILTS_PER_PAGE 15 // The actual number is one fewer than this - the first row is used for headers #define TILT_FONT_SIZE 2 #define MIN_PRESSURE 2000 +#define HAVE_LCD 1 #elif defined(LCD_TFT_ESPI) @@ -36,22 +38,16 @@ #define FF17 &FreeSans9pt7b #define GFXFF 1 #define TILTS_PER_PAGE 5 // The actual number is one fewer than this - the first row is used for headers +#define HAVE_LCD 1 -#elif defined(LCD_TFT_M5STICKC) - -#include - -#define TFT_M5STICKC_LINE_CLEARANCE 0 -#define GFXFF 2 -#define TILTS_PER_PAGE 5 // The actual number is one fewer than this - the first row is used for headers #endif // LCD_SSD1306 -#ifdef TILTS_PER_PAGE -#define HAVE_LCD 1 -#else -#define HAVE_LCD 0 -#endif +// #ifdef TILTS_PER_PAGE +// #define HAVE_LCD 1 +// #else +// #define HAVE_LCD 0 +// #endif #define SCREEN_TILT 0 #define SCREEN_LOGO 1 @@ -91,7 +87,7 @@ class bridge_lcd { #ifdef LCD_SSD1306 SSD1306Wire *oled_display; -#elif defined(LCD_TFT) || defined(LCD_TFT_ESPI) || defined(LCD_TFT_M5STICKC) +#elif defined(LCD_TFT) || defined(LCD_TFT_ESPI) TFT_eSPI *tft; #endif // LCD_SSD1306 diff --git a/src/http_server.cpp b/src/http_server.cpp index 94f0ad5..6b75863 100644 --- a/src/http_server.cpp +++ b/src/http_server.cpp @@ -8,6 +8,7 @@ Ticker sendNowTicker; extern bool send_cloudTarget; extern bool send_brewersFriend; extern bool send_brewfather; +extern bool send_userTarget; extern bool send_grainfather; extern bool send_localTarget; extern bool send_brewStatus; @@ -467,6 +468,51 @@ bool processBrewfatherSettings(AsyncWebServerRequest *request) } } +bool processUserTargetSettings(AsyncWebServerRequest *request) +{ + int failCount = 0; + // Loop through all parameters + int params = request->params(); + for (int i = 0; i < params; i++) { + AsyncWebParameter *p = request->getParam(i); + if (p->isPost()) { + // Process any p->name().c_str() / p->value().c_str() pairs + const char *name = p->name().c_str(); + const char *value = p->value().c_str(); + Log.verbose(F("Processing [%s]:(%s) pair.\r\n"), name, value); + + // Brewfather settings + // + if (strcmp(name, "userTargetURL") == 0) { + // Set Brewfather Key + if (strlen(value) > USER_TARGET_MIN_URL_LENGTH && strlen(value) < 128 ) { + strlcpy(config.userTargetURL, value, 65); + Log.notice(F("Settings update, [%s]:(%s) applied.\r\n"), name, value); + // Trigger a send to the user target in 5 seconds using the updated key + sendNowTicker.once(5, [](){send_userTarget = true;}); + } else if (strcmp(value, "") == 0 || strlen(value) == 0) { + strlcpy(config.userTargetURL, value, 128); + Log.notice(F("Settings update, [%s]:(%s) cleared.\r\n"), name, value); + } else { + failCount++; + Log.warning(F("Settings update error, [%s]:(%s) not valid.\r\n"), name, value); + } + } + } + } + if (failCount) { + Log.error(F("Error: Invalid User Target configuration.\r\n")); + return false; + } else { + if (config.save()) { + return true; + } else { + Log.error(F("Error: Unable to save User Target configuration data.\r\n")); + return false; + } + } +} + bool processGrainfatherSettings(AsyncWebServerRequest *request) { int failCount = 0; @@ -985,6 +1031,14 @@ void setPostPages() { request->send(500, F("text/plain"), F("Unable to process data")); } }); + server.on("/settings/usertarget/", HTTP_POST, [](AsyncWebServerRequest *request) { + Log.verbose(F("Processing post to /settings/usertarget/.\r\n")); + if (processUserTargetSettings(request)) { + request->send(200, F("text/plain"), F("Ok")); + } else { + request->send(500, F("text/plain"), F("Unable to process data")); + } + }); server.on("/settings/brewstatus/", HTTP_POST, [](AsyncWebServerRequest *request) { Log.verbose(F("Processing post to /settings/brewstatus/.\r\n")); if (processBrewstatusSettings(request)) { diff --git a/src/http_server.h b/src/http_server.h index 2aac99d..c594cdb 100644 --- a/src/http_server.h +++ b/src/http_server.h @@ -27,7 +27,8 @@ #define BREWFATHER_MIN_KEY_LENGTH 5 #define BREWERS_FRIEND_MIN_KEY_LENGTH 12 #define BREWSTATUS_MIN_KEY_LENGTH 12 -#define GRAINFATHER_MIN_URL_LENGTH 44 +#define GRAINFATHER_MIN_URL_LENGTH 44 +#define USER_TARGET_MIN_URL_LENGTH 12 class httpServer { public: diff --git a/src/jsonconfig.cpp b/src/jsonconfig.cpp index 3f7427b..48a20cc 100644 --- a/src/jsonconfig.cpp +++ b/src/jsonconfig.cpp @@ -223,6 +223,7 @@ DynamicJsonDocument Config::to_json() { obj["scriptsEmail"] = scriptsEmail; obj["brewersFriendKey"] = brewersFriendKey; obj["brewfatherKey"] = brewfatherKey; + obj["userTargetURL"] = userTargetURL; obj["mqttBrokerHost"] = mqttBrokerHost; obj["mqttBrokerPort"] = mqttBrokerPort; obj["mqttUsername"] = mqttUsername; @@ -375,6 +376,11 @@ void Config::load_from_json(DynamicJsonDocument obj) { strlcpy(brewfatherKey, bk, 65); } + if (!obj["userTargetURL"].isNull()) { + const char *uturl = obj["userTargetURL"]; + strlcpy(userTargetURL, uturl, 128); + } + if (!obj["mqttBrokerHost"].isNull()) { const char *mi = obj["mqttBrokerHost"]; strlcpy(mqttBrokerHost, mi, 256); diff --git a/src/jsonconfig.h b/src/jsonconfig.h index 47577d3..115df5d 100644 --- a/src/jsonconfig.h +++ b/src/jsonconfig.h @@ -79,6 +79,7 @@ class Config: public ConfigFile { char scriptsEmail[256] = ""; char brewersFriendKey[65] = ""; char brewfatherKey[65] = ""; + char userTargetURL[129] = ""; char mqttBrokerHost[256] = ""; uint16_t mqttBrokerPort = 1883; char mqttUsername[51] = ""; diff --git a/src/sendData.cpp b/src/sendData.cpp index 89fd361..506e095 100644 --- a/src/sendData.cpp +++ b/src/sendData.cpp @@ -10,6 +10,7 @@ Ticker cloudTargetTicker; Ticker localTargetTicker; Ticker brewersFriendTicker; Ticker brewfatherTicker; +Ticker userTargetTicker; Ticker grainfatherTicker; Ticker brewStatusTicker; //Ticker taplistioTicker; // Now inside dataSendHandler object @@ -21,6 +22,7 @@ bool send_cloudTarget = false; bool send_localTarget = false; bool send_brewersFriend = false; bool send_brewfather = false; +bool send_userTarget = false; bool send_grainfather = false; bool send_brewStatus = false; //bool send_taplistio = false; // Now inside dataSendHandler object @@ -43,7 +45,8 @@ void dataSendHandler::init() brewStatusTicker.once(30, [](){send_brewStatus = true;}); // Schedule first send to Brew Status brewfatherTicker.once(40, [](){send_brewfather = true;}); // Schedule first send to Brewfather brewersFriendTicker.once(50, [](){send_brewersFriend = true;}); // Schedule first send to Brewer's Friend - mqttTicker.once(60, [](){send_mqtt = true;}); // Schedule first send to MQTT + userTargetTicker.once(60, [](){send_userTarget = true;}); // Schedule first send to User-defined JSON target + mqttTicker.once(65, [](){send_mqtt = true;}); // Schedule first send to MQTT gSheetsTicker.once(70, [](){send_gSheets = true;}); // Schedule first send to Google Sheets grainfatherTicker.once(80, [](){send_grainfather = true;}); // Schedule first send to Grainfather taplistioTicker.once(90, [](){data_sender.send_taplistio = true;}); // Schedule first send to Taplist.io @@ -136,6 +139,29 @@ bool send_to_bf_and_bf() brewfatherTicker.once(BREWFATHER_DELAY, [](){send_brewfather = true;}); // Set up subsequent send to Brewfather send_lock = false; } + + + if (send_userTarget && ! send_lock) + { + send_lock = true; + // User Target + send_userTarget = false; + if (WiFiClass::status() == WL_CONNECTED && strlen(config.userTargetURL) > USER_TARGET_MIN_URL_LENGTH) + { + Log.verbose(F("Calling send to User Target.\r\n")); + retval = data_sender.send_to_bf_and_bf(BF_MEANS_USER_TARGET); + if (retval) + { + Log.notice(F("Completed send to User Target.\r\n")); + } + else + { + Log.verbose(F("Error sending to User Target.\r\n")); + } + } + userTargetTicker.once(USER_TARGET_DELAY, [](){send_userTarget = true;}); // Set up subsequent send to User Target + send_lock = false; + } return retval; } @@ -182,6 +208,15 @@ bool dataSendHandler::send_to_bf_and_bf(const uint8_t which_bf) strcpy(url, "http://log.brewersfriend.com/stream/"); strcat(url, config.brewersFriendKey); } + else if (which_bf == BF_MEANS_USER_TARGET) + { + if (strlen(config.userTargetURL) <= USER_TARGET_MIN_URL_LENGTH) + { + Log.verbose(F("User target URL not populated. Returning.\r\n")); + return false; + } + strcpy(url, config.userTargetURL); + } else { Log.error(F("Invalid value of which_bf passed to send_to_bf_and_bf.\r\n")); diff --git a/src/sendData.h b/src/sendData.h index 04f423f..37f5421 100644 --- a/src/sendData.h +++ b/src/sendData.h @@ -26,6 +26,7 @@ #define BREWFATHER_DELAY (15 * 60) // 15 minute delay between pushes to Brewfather #define GRAINFATHER_DELAY (15 * 60) // 15 minute delay between pushes to Grainfather #define CLOUD_DELAY (30 * 60) // 30 minute delay between pushes to Parse Cloud +#define USER_TARGET_DELAY (10 * 60) // 10 minute delay between pushes to user specified send target #define BREWFATHER_MIN_KEY_LENGTH 5 #define BREWERS_FRIEND_MIN_KEY_LENGTH 12 @@ -39,9 +40,11 @@ // This is me being lazy and simplifying the reuse of code. The formats for Brewer's // Friend and Brewfather are basically the same so I'm combining them together -// in one function +// in one function. I'm being even lazier by adding a user defined "send target" +// (user specified URL) to the same code block. #define BF_MEANS_BREWFATHER 1 #define BF_MEANS_BREWERS_FRIEND 2 +#define BF_MEANS_USER_TARGET 3 class dataSendHandler {