From 89234e9f121a90776d615c20ce11e12307149de9 Mon Sep 17 00:00:00 2001
From: Jochen Vothknecht <jochen3120@gmail.com>
Date: Thu, 14 Apr 2022 10:00:50 +0200
Subject: [PATCH] Integrating UDP and timer stuff into SpiderLib

---
 CLC-qthing/SpiderLib/UDP.cpp  | 135 ++++++++++++++++++++++++++++++++++
 CLC-qthing/SpiderLib/UDP.hpp  |  15 ++++
 CLC-qthing/SpiderLib/Util.cpp |  21 ++++++
 CLC-qthing/SpiderLib/Util.hpp |  22 ++++++
 4 files changed, 193 insertions(+)
 create mode 100644 CLC-qthing/SpiderLib/UDP.cpp
 create mode 100644 CLC-qthing/SpiderLib/UDP.hpp
 create mode 100644 CLC-qthing/SpiderLib/Util.cpp
 create mode 100644 CLC-qthing/SpiderLib/Util.hpp

diff --git a/CLC-qthing/SpiderLib/UDP.cpp b/CLC-qthing/SpiderLib/UDP.cpp
new file mode 100644
index 0000000..4f4e7f3
--- /dev/null
+++ b/CLC-qthing/SpiderLib/UDP.cpp
@@ -0,0 +1,135 @@
+#include "UDP.hpp"
+
+/*
+
+#include <lwip/netdb.h>
+
+#include <map>
+
+#include "esp_log.h"
+#include "lwip/err.h"
+#include "lwip/sockets.h"
+#include "lwip/sys.h"
+
+
+static const char* TAG = "udp-server";
+
+static const in_port_t PORT = 4213;
+
+// rate-limits positive udp log messages; time in milliseconds
+static const uint16_t loggingTimeout = 500;  // TODO: make configurable?!
+
+static std::map<std::string, qthing::udpPacketCallback> packetCallbackMap;
+
+void qthing::addUDPPacketCallback(std::string magicString, qthing::udpPacketCallback callback) {
+  if (packetCallbackMap.count(magicString) > 0) {
+    qthing::udpPacketCallback old_callback = packetCallbackMap.at(magicString);
+    packetCallbackMap[magicString] = [old_callback, callback](udpPacket packet) {
+      old_callback(packet);
+      callback(packet);
+    };
+  } else {
+    packetCallbackMap[magicString] = callback;
+  }
+
+  ESP_LOGI(TAG, "Registered callback for packets with magic string %s", magicString.c_str());
+}
+
+static char rx_buffer[2048];  // TODO: get MTU
+
+static void udp_server_task(void* pvParameters) {
+  char addr_str[128];
+  int addr_family;
+  int ip_protocol;
+
+  struct sockaddr_in6 destAddr;
+  bzero(&destAddr.sin6_addr.un, sizeof(destAddr.sin6_addr.un));
+  destAddr.sin6_family = AF_INET6;
+  destAddr.sin6_port = htons(PORT);
+  addr_family = AF_INET6;
+  ip_protocol = IPPROTO_IPV6;
+  inet6_ntoa_r(destAddr.sin6_addr, addr_str, sizeof(addr_str) - 1);
+
+  while (true) {
+    int sock = socket(addr_family, SOCK_DGRAM, ip_protocol);
+    if (sock < 0) {
+      ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
+      break;
+    }
+    ESP_LOGI(TAG, "Socket created");
+
+    int err = bind(sock, (struct sockaddr*)&destAddr, sizeof(destAddr));
+    if (err < 0) {
+      ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno);
+    }
+    ESP_LOGI(TAG, "Socket bound");
+    ESP_LOGI(TAG, "Waiting for data");
+
+    // This is for the rate limited logging only
+    uint32_t loggingBytes = 0;
+    uint16_t loggingPackets = 0;
+    uint32_t loggingLastMillis = qthing::millis();
+
+    while (true) {
+      struct sockaddr_in6 sourceAddr;  // Large enough for both IPv4 or IPv6
+      socklen_t socklen = sizeof(sourceAddr);
+
+      memset(rx_buffer, 0, sizeof(rx_buffer));
+      int len = recvfrom(sock, rx_buffer, sizeof(rx_buffer) - 1, 0, (struct sockaddr*)&sourceAddr, &socklen);
+
+      // Error occured during receiving
+      if (len < 0) {
+        ESP_LOGE(TAG, "recvfrom failed: errno %d", errno);
+        break;
+      }
+      // Data received
+      else {
+
+        if (loggingTimeout == 0) {
+          ESP_LOGI(TAG, "Received %d bytes from %s", len, qthing::to_string(sourceAddr).c_str());
+        } else {
+          loggingBytes   += len;
+          loggingPackets += 1;
+
+          if (qthing::millis() - loggingLastMillis > loggingTimeout) {
+
+            ESP_LOGI(TAG, "Received %d bytes in %d packets", loggingBytes, loggingPackets);
+
+            loggingBytes      = 0;
+            loggingPackets    = 0;
+            loggingLastMillis = qthing::millis();
+          }
+        }
+
+        std::string payload_str(rx_buffer);
+        int16_t lenBestMatch = -1;
+        qthing::udpPacketCallback callback = NULL;
+        for (auto it = packetCallbackMap.begin(); it != packetCallbackMap.end(); ++it) {
+          if (len >= it->first.length() && payload_str.find(it->first) == 0) {
+            lenBestMatch = len;
+            callback = it->second;
+          }
+        }
+
+        if (lenBestMatch > -1) {
+          qthing::udpPacket packet = {.sourceAddr = sourceAddr, .payload = rx_buffer, .length = (uint16_t)len};
+
+          callback(packet);
+        } else {
+          ESP_LOGW(TAG, "Packet has unknown magic string");
+        }
+      }
+    }
+
+    if (sock != -1) {
+      ESP_LOGE(TAG, "Shutting down socket and restarting...");
+      shutdown(sock, 0);
+      close(sock);
+    }
+  }
+  vTaskDelete(NULL);
+}
+
+void qthing::start_udp_server() { xTaskCreate(udp_server_task, "udp_server", 4096, NULL, 5, NULL); }
+
+*/
diff --git a/CLC-qthing/SpiderLib/UDP.hpp b/CLC-qthing/SpiderLib/UDP.hpp
new file mode 100644
index 0000000..40e3bb9
--- /dev/null
+++ b/CLC-qthing/SpiderLib/UDP.hpp
@@ -0,0 +1,15 @@
+#pragma once
+
+
+/*
+
+namespace SpiderLib {
+
+
+  void start_udp_server();
+
+
+}
+
+
+*/
\ No newline at end of file
diff --git a/CLC-qthing/SpiderLib/Util.cpp b/CLC-qthing/SpiderLib/Util.cpp
new file mode 100644
index 0000000..e563259
--- /dev/null
+++ b/CLC-qthing/SpiderLib/Util.cpp
@@ -0,0 +1,21 @@
+#include "Util.hpp"
+
+// ESP32 specific
+#include "esp_timer.h"
+
+// C++ system level
+#include <cinttypes>
+
+// project specific
+// #include "MyHeader.hpp"
+
+
+
+namespace SpiderLib {
+
+
+  uint32_t Time::s()  { return (uint64_t)(esp_timer_get_time() / 1000000ULL); }
+  uint64_t Time::ms() { return (uint64_t)(esp_timer_get_time() /    1000ULL); }
+  uint64_t Time::us() { return (uint64_t) esp_timer_get_time(); }
+
+}
diff --git a/CLC-qthing/SpiderLib/Util.hpp b/CLC-qthing/SpiderLib/Util.hpp
new file mode 100644
index 0000000..b8e049a
--- /dev/null
+++ b/CLC-qthing/SpiderLib/Util.hpp
@@ -0,0 +1,22 @@
+#pragma once
+
+// ESP32 specific
+// #include "MyHeader.h"
+
+// C++ system level
+#include <cinttypes>
+
+// project specific
+// #include "MyHeader.hpp"
+
+
+namespace SpiderLib {
+
+
+  class Time {
+    public:
+      static uint32_t  s();
+      static uint64_t ms();
+      static uint64_t us();
+  };
+}
-- 
GitLab