From 1b3c17c55d46cfbc6c8dc8c73d2bfbc6c80b9cbd Mon Sep 17 00:00:00 2001 From: CommanderRedYT Date: Sun, 21 Nov 2021 01:37:51 +0100 Subject: [PATCH] Added udp cloud --- main/CMakeLists.txt | 2 + main/displays/lockscreen.cpp | 2 + main/globals.cpp | 1 + main/globals.h | 1 + main/main.cpp | 2 + main/presets.cpp | 1 + main/presets.h | 3 +- main/settings.h | 2 + main/stringsettings.h | 2 + main/udpcloud.cpp | 176 +++++++++++++++++++++++++++++++++++ main/udpcloud.h | 6 ++ 11 files changed, 197 insertions(+), 1 deletion(-) create mode 100644 main/udpcloud.cpp create mode 100644 main/udpcloud.h diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 7047a3e..9399c61 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -49,6 +49,7 @@ set(headers texts.h time_bobbycar.h types.h + udpcloud.h unifiedmodelmode.h utils.h webserver_displaycontrol.h @@ -239,6 +240,7 @@ set(sources texts.cpp time_bobbycar.cpp types.cpp + udpcloud.cpp unifiedmodelmode.cpp utils.cpp webserver.cpp diff --git a/main/displays/lockscreen.cpp b/main/displays/lockscreen.cpp index fe909d7..beb8d5f 100644 --- a/main/displays/lockscreen.cpp +++ b/main/displays/lockscreen.cpp @@ -22,6 +22,7 @@ void Lockscreen::start() currentMode = &m_mode; profileButtonDisabled = !settings.lockscreen.allowPresetSwitch; + isLocked = true; } void Lockscreen::initScreen() @@ -122,6 +123,7 @@ void Lockscreen::stop() } profileButtonDisabled = false; + isLocked = false; } void Lockscreen::confirm() diff --git a/main/globals.cpp b/main/globals.cpp index 40e4ec5..ef85c42 100644 --- a/main/globals.cpp +++ b/main/globals.cpp @@ -14,6 +14,7 @@ float gametrakDist; float avgSpeed{}; float avgSpeedKmh{}; float sumCurrent{}; +bool isLocked{}; char deviceName[32] = STRING(DEVICE_PREFIX) "_ERR"; diff --git a/main/globals.h b/main/globals.h index 69d6512..cb93b34 100644 --- a/main/globals.h +++ b/main/globals.h @@ -43,6 +43,7 @@ extern float avgSpeedKmh; extern float sumCurrent; extern char deviceName[32]; +extern bool isLocked; #ifdef GLOBALS_PLUGIN #include GLOBALS_PLUGIN diff --git a/main/main.cpp b/main/main.cpp index fc20e46..b827121 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -62,6 +62,7 @@ using namespace std::chrono_literals; #endif #ifdef FEATURE_CLOUD #include "cloud.h" +#include "udpcloud.h" #endif #include "wifi_bobbycar.h" #include "time_bobbycar.h" @@ -447,5 +448,6 @@ extern "C" void app_main() handle_dns_announce(); #endif calculateStatistics(); + sendUdpCloudPacket(); } } diff --git a/main/presets.cpp b/main/presets.cpp index abeeae5..a6a3fac 100644 --- a/main/presets.cpp +++ b/main/presets.cpp @@ -23,6 +23,7 @@ StringSettings makeDefaultStringSettings() }, #ifdef FEATURE_CLOUD .cloudUrl = {}, + .udpCloudUrl = {}, #endif #ifdef FEATURE_OTA .otaUrl = {}, diff --git a/main/presets.h b/main/presets.h index 5644db6..cfd3fc2 100644 --- a/main/presets.h +++ b/main/presets.h @@ -173,7 +173,8 @@ constexpr Settings::BoardcomputerHardware defaultBoardcomputerHardware { #ifdef FEATURE_CLOUD constexpr Settings::CloudSettings defaultCloudSettings { .cloudEnabled = false, - .cloudTransmitTimeout = 10 + .cloudTransmitTimeout = 10, + .udpUid = 0, }; #endif diff --git a/main/settings.h b/main/settings.h index 7f3a714..2bfa4ee 100644 --- a/main/settings.h +++ b/main/settings.h @@ -121,6 +121,7 @@ struct Settings struct CloudSettings { bool cloudEnabled; int16_t cloudTransmitTimeout; // in ms + uint32_t udpUid; } cloudSettings; #endif @@ -286,6 +287,7 @@ void Settings::executeForEveryCommonSetting(T &&callable) #ifdef FEATURE_CLOUD callable("cloudEnabled", cloudSettings.cloudEnabled); callable("clodTransmTmout", cloudSettings.cloudTransmitTimeout); + callable("cloudUDPUid", cloudSettings.udpUid); #endif #ifdef FEATURE_LEDSTRIP diff --git a/main/stringsettings.h b/main/stringsettings.h index 7745a38..ac88ee4 100644 --- a/main/stringsettings.h +++ b/main/stringsettings.h @@ -15,6 +15,7 @@ struct StringSettings #ifdef FEATURE_CLOUD std::string cloudUrl; + std::string udpCloudUrl; #endif #ifdef FEATURE_OTA @@ -75,6 +76,7 @@ void StringSettings::executeForEveryCommonSetting(T &&callable) #ifdef FEATURE_CLOUD callable("cloudUrl", cloudUrl); + callable("udpUrl", udpCloudUrl); #endif #ifdef FEATURE_OTA diff --git a/main/udpcloud.cpp b/main/udpcloud.cpp new file mode 100644 index 0000000..5ce4a39 --- /dev/null +++ b/main/udpcloud.cpp @@ -0,0 +1,176 @@ +constexpr const char * const TAG = "bobbycloud"; + +// 3rd party includes +#include +#include + +// local includes +#include "udpcloud.h" +#include "udpsender.h" +#include "espwifistack.h" +#include "esp_log.h" +#include "fmt/format.h" +#include "globals.h" +#include "utils.h" +#include "lwip/dns.h" +#include "espchrono.h" +#include "battery.h" +#include "drivingstatistics.h" + +using namespace std::chrono_literals; + +espchrono::millis_clock::time_point timestampLastFailed; + +void spamUdpBroadcast() +{ + wifi_stack::UdpSender sender; + + if (!sender.ready()) + { + ESP_LOGE(TAG, "could not init udp sender!"); + return; + } + + std::string buf; + const auto uptime = espchrono::millis_clock::now().time_since_epoch().count(); + + buf = fmt::format("uptime: {}", uptime); + + if (const auto result = sender.send(ESP_IF_WIFI_STA, 187, buf); !result) + { + ESP_LOGE(TAG, "broadcast failed"); + } +} + +std::string buildUdpCloudJson() +{ + StaticJsonDocument<1024> doc; + std::string buf; + const auto uptime = espchrono::millis_clock::now().time_since_epoch().count(); + + float avgVoltage = 0; + for (auto &controller : controllers) + { + avgVoltage += controller.getCalibratedVoltage(); + } + avgVoltage = avgVoltage / controllers.size(); + + const auto watt = sumCurrent * avgVoltage; + // const auto w_per_kmh = watt / avgSpeedKmh; + + // User ID + doc["uid"] = settings.cloudSettings.udpUid; + doc["upt"] = uptime; + + const auto addController = [&](const Controller &controller, const bool isBack) { + if (controller.feedbackValid) + { + auto arr = doc.createNestedObject(!isBack ? "f":"b"); + // Voltage + arr["V"] = controller.getCalibratedVoltage(); + + // Amperes + arr["lA"] = fixCurrent(controller.feedback.left.dcLink); + arr["rA"] = fixCurrent(controller.feedback.right.dcLink); + + // Temperature + arr[!isBack ? "fT":"bT"] = fixBoardTemp(controller.feedback.boardTemp); + + // Errors + arr[!isBack ? "flE":"blE"] = controller.feedback.left.error; + arr[!isBack ? "frE":"brE"] = controller.feedback.right.error; + + // Speed + arr[!isBack ? "flS":"blS"] = convertToKmh(controller.feedback.left.speed); + arr[!isBack ? "frS":"brS"] = convertToKmh(controller.feedback.right.speed); + } + }; + + addController(controllers.front, false); + addController(controllers.back, true); + + // Potis + { + auto arr = doc.createNestedObject("p"); + if (gas) + arr["g"] = *gas; + if (raw_gas) + arr["rg"] = *raw_gas; + if (brems) + arr["b"] = *brems; + if (raw_brems) + arr["rb"] = *raw_brems; + } + + // Statistics + doc["bP"] = getBatteryPercentage(avgVoltage, BatteryCellType(settings.battery.cellType)); + doc["bV"] = avgVoltage; + doc["l"] = isLocked; + // doc["mM"] = currentMode->displayName(); + doc["mN"] = drivingStatistics.meters_driven; + doc["mT"] = drivingStatistics.totalMeters; + doc["dT"] = drivingStatistics.currentDrivingTime; + doc["cW"] = watt; + doc["wN"] = drivingStatistics.wh_used; + doc["wL"] = getRemainingWattHours(); + doc["kmL"] = getRemainingWattHours() / settings.battery.watthoursPerKilometer; + + serializeJson(doc, buf); + return buf; +} + +void sendUdpCloudPacket() +{ + EVERY_N_MILLIS(30) { + if (espchrono::ago(timestampLastFailed) < 3s) + return; + + if (stringSettings.udpCloudUrl.empty()) + { + return; + } + + if (wifi_stack::get_sta_status() != wifi_stack::WiFiStaStatus::CONNECTED) + { + return; + } + + ip_addr_t udpCloudIp; + + if (const auto res = dns_gethostbyname(stringSettings.udpCloudUrl.c_str(), &udpCloudIp, nullptr, nullptr); res != ERR_OK) + { + if (res == ERR_INPROGRESS) + { + ESP_LOGD(TAG, "dns_gethostbyname() failed because: %i", res); + } + else + { + ESP_LOGE(TAG, "dns_gethostbyname() failed because: %i", res); + } + timestampLastFailed = espchrono::millis_clock::now(); + return; + } + + if (udpCloudIp.type != IPADDR_TYPE_V4) + { + ESP_LOGE(TAG, "unsupported ip type: %hhu", udpCloudIp.type); + return; + } + + sockaddr_in receipient; + receipient.sin_port = htons(24242); + receipient.sin_addr.s_addr = udpCloudIp.u_addr.ip4.addr; + receipient.sin_family = AF_INET; + + wifi_stack::UdpSender udpCloudSender; + std::string buf = buildUdpCloudJson(); + + + if (const auto result = udpCloudSender.send(receipient, buf); !result) + { + ESP_LOGE(TAG, "send to cloud failed"); + } + + ESP_LOGD(TAG, "now: %s", buf.c_str()); + } +} diff --git a/main/udpcloud.h b/main/udpcloud.h new file mode 100644 index 0000000..e4e5f50 --- /dev/null +++ b/main/udpcloud.h @@ -0,0 +1,6 @@ +#pragma once +#include + +void spamUdpBroadcast(); +std::string buildUdpCloudJson(); +void sendUdpCloudPacket();