From d46849736ff5d5767b34cdffdf341066ae507169 Mon Sep 17 00:00:00 2001
From: Svante Kaiser <svantekaiser@example.com>
Date: Thu, 2 Nov 2023 13:14:34 +0300
Subject: [PATCH] initial commit

---
 .gitignore              |   5 +
 .vscode/extensions.json |  10 ++
 .vscode/settings.json   |  58 +++++++++++
 include/README          |  39 ++++++++
 platformio.ini          |  21 ++++
 src/config/config.h     |   0
 src/infra/mqtt.cpp      |  50 ++++++++++
 src/infra/mqtt.h        |  18 ++++
 src/main.cpp            | 214 ++++++++++++++++++++++++++++++++++++++++
 src/stateMachine.cpp    | 100 +++++++++++++++++++
 src/stateMachine.h      |  33 +++++++
 src/utils/print.cpp     |  77 +++++++++++++++
 src/utils/print.h       |  44 +++++++++
 13 files changed, 669 insertions(+)
 create mode 100644 .gitignore
 create mode 100644 .vscode/extensions.json
 create mode 100644 .vscode/settings.json
 create mode 100644 include/README
 create mode 100644 platformio.ini
 create mode 100644 src/config/config.h
 create mode 100644 src/infra/mqtt.cpp
 create mode 100644 src/infra/mqtt.h
 create mode 100644 src/main.cpp
 create mode 100644 src/stateMachine.cpp
 create mode 100644 src/stateMachine.h
 create mode 100644 src/utils/print.cpp
 create mode 100644 src/utils/print.h

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/.vscode/extensions.json b/.vscode/extensions.json
new file mode 100644
index 0000000..080e70d
--- /dev/null
+++ b/.vscode/extensions.json
@@ -0,0 +1,10 @@
+{
+    // See http://go.microsoft.com/fwlink/?LinkId=827846
+    // for the documentation about the extensions.json format
+    "recommendations": [
+        "platformio.platformio-ide"
+    ],
+    "unwantedRecommendations": [
+        "ms-vscode.cpptools-extension-pack"
+    ]
+}
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..9fb8bf6
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,58 @@
+{
+  "files.associations": {
+    "algorithm": "cpp",
+    "array": "cpp",
+    "atomic": "cpp",
+    "*.tcc": "cpp",
+    "cctype": "cpp",
+    "cerrno": "cpp",
+    "climits": "cpp",
+    "clocale": "cpp",
+    "cmath": "cpp",
+    "cstdarg": "cpp",
+    "cstddef": "cpp",
+    "cstdint": "cpp",
+    "cstdio": "cpp",
+    "cstdlib": "cpp",
+    "cstring": "cpp",
+    "ctime": "cpp",
+    "cwchar": "cpp",
+    "cwctype": "cpp",
+    "deque": "cpp",
+    "map": "cpp",
+    "string": "cpp",
+    "unordered_map": "cpp",
+    "unordered_set": "cpp",
+    "vector": "cpp",
+    "exception": "cpp",
+    "functional": "cpp",
+    "iterator": "cpp",
+    "memory": "cpp",
+    "memory_resource": "cpp",
+    "numeric": "cpp",
+    "optional": "cpp",
+    "random": "cpp",
+    "string_view": "cpp",
+    "system_error": "cpp",
+    "tuple": "cpp",
+    "type_traits": "cpp",
+    "utility": "cpp",
+    "fstream": "cpp",
+    "initializer_list": "cpp",
+    "iomanip": "cpp",
+    "ios": "cpp",
+    "iosfwd": "cpp",
+    "istream": "cpp",
+    "limits": "cpp",
+    "locale": "cpp",
+    "new": "cpp",
+    "ostream": "cpp",
+    "queue": "cpp",
+    "sstream": "cpp",
+    "stdexcept": "cpp",
+    "streambuf": "cpp",
+    "cinttypes": "cpp",
+    "cstdbool": "cpp",
+    "typeinfo": "cpp"
+  }
+}
\ No newline at end of file
diff --git a/include/README b/include/README
new file mode 100644
index 0000000..194dcd4
--- /dev/null
+++ b/include/README
@@ -0,0 +1,39 @@
+
+This directory is intended for project header files.
+
+A header file is a file containing C declarations and macro definitions
+to be shared between several project source files. You request the use of a
+header file in your project source file (C, C++, etc) located in `src` folder
+by including it, with the C preprocessing directive `#include'.
+
+```src/main.c
+
+#include "header.h"
+
+int main (void)
+{
+ ...
+}
+```
+
+Including a header file produces the same results as copying the header file
+into each source file that needs it. Such copying would be time-consuming
+and error-prone. With a header file, the related declarations appear
+in only one place. If they need to be changed, they can be changed in one
+place, and programs that include the header file will automatically use the
+new version when next recompiled. The header file eliminates the labor of
+finding and changing all the copies as well as the risk that a failure to
+find one copy will result in inconsistencies within a program.
+
+In C, the usual convention is to give header files names that end with `.h'.
+It is most portable to use only letters, digits, dashes, and underscores in
+header file names, and at most one dot.
+
+Read more about using header files in official GCC documentation:
+
+* Include Syntax
+* Include Operation
+* Once-Only Headers
+* Computed Includes
+
+https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html
diff --git a/platformio.ini b/platformio.ini
new file mode 100644
index 0000000..07aa070
--- /dev/null
+++ b/platformio.ini
@@ -0,0 +1,21 @@
+; 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
+
+[env:esp32dev]
+platform = espressif32
+board = esp32dev
+framework = arduino
+check_skip_packages = yes
+monitor_speed = 115200
+debug_tool = esp-prog
+board_build.partitions = no_ota.csv
+lib_deps = 
+	adafruit/Adafruit NeoPixel@^1.11.0
+	ottowinter/AsyncMqttClient-esphome@^0.8.6
diff --git a/src/config/config.h b/src/config/config.h
new file mode 100644
index 0000000..e69de29
diff --git a/src/infra/mqtt.cpp b/src/infra/mqtt.cpp
new file mode 100644
index 0000000..a93c777
--- /dev/null
+++ b/src/infra/mqtt.cpp
@@ -0,0 +1,50 @@
+#include <Arduino.h>
+#include "infra/mqtt.h"
+
+void connectToMqtt() {
+    Serial.println("Connecting to MQTT...");
+    mqttClient.connect();
+}
+
+void onMqttConnect(bool sessionPresent) {
+    Serial.println("Connected to MQTT.");
+    Serial.print("Session present: ");
+    Serial.println(sessionPresent);
+
+    uint16_t packetIdSub = mqttClient.subscribe("esp32/led", 0);
+    Serial.print("Subscribing at QoS 0, packetId: ");
+    Serial.println(packetIdSub);
+}
+
+void onMqttDisconnect(AsyncMqttClientDisconnectReason reason) {
+  Serial.println("Disconnected from MQTT.");
+  if (WiFi.isConnected()) {
+    xTimerStart(mqttReconnectTimer, 0);
+  }
+}
+
+void onMqttSubscribe(uint16_t packetId, uint8_t qos) {
+  Serial.println("Subscribe acknowledged.");
+  Serial.print("  packetId: ");
+  Serial.println(packetId);
+  Serial.print("  qos: ");
+  Serial.println(qos);
+}
+
+void onMqttUnsubscribe(uint16_t packetId) {
+  Serial.println("Unsubscribe acknowledged.");
+  Serial.print("  packetId: ");
+  Serial.println(packetId);
+}
+
+void onMqttPublish(uint16_t packetId) {
+  Serial.println("Publish acknowledged.");
+  Serial.print("  packetId: ");
+  Serial.println(packetId);
+}
+
+void sendDataToMQTT(int flatNumber) {
+  char num_char[3];
+  sprintf(num_char, "%d", flatNumber);
+  mqttClient.publish("/digitum/out/flat_number", 2, true, num_char);
+}
\ No newline at end of file
diff --git a/src/infra/mqtt.h b/src/infra/mqtt.h
new file mode 100644
index 0000000..6401e39
--- /dev/null
+++ b/src/infra/mqtt.h
@@ -0,0 +1,18 @@
+#ifndef INFRA_MQTT_H
+#define INFRA_MQTT_H
+
+#include <HTTPClient.h>
+#include <AsyncMqttClient.h>
+
+AsyncMqttClient mqttClient;
+TimerHandle_t mqttReconnectTimer;
+
+void connectToMqtt();
+void onMqttConnect(bool sessionPresent);
+void onMqttDisconnect(AsyncMqttClientDisconnectReason reason);
+void onMqttSubscribe(uint16_t packetId, uint8_t qos);
+void onMqttUnsubscribe(uint16_t packetId);
+void onMqttPublish(uint16_t packetId);
+void sendDataToMQTT(int flatNumber);
+
+#endif // INFRA_MQTT_H
\ No newline at end of file
diff --git a/src/main.cpp b/src/main.cpp
new file mode 100644
index 0000000..f34224e
--- /dev/null
+++ b/src/main.cpp
@@ -0,0 +1,214 @@
+#include <Arduino.h>
+#include <ETH.h>
+#include <SPI.h>
+#include <SD.h>
+#include <HTTPClient.h>
+#include <IPAddress.h>
+#include <AsyncMqttClient.h>
+
+#include "infra/mqtt.h"
+#include "utils/print.h"
+#include "stateMachine.h"
+
+#define LEDS_PIN 32
+#define DRY_CONT_PIN 15
+#define DOOR_SENS_PIN 114
+
+#define DATA_PIN 12
+#define DATA_PERIOD 240 // microseconds
+
+#define PRINT_RAW_SIGNAL_FLAG 0
+
+/*
+   * ETH_CLOCK_GPIO0_IN   - default: external clock from crystal oscillator
+   * ETH_CLOCK_GPIO0_OUT  - 50MHz clock from internal APLL output on GPIO0 - possibly an inverter is needed for LAN8720
+   * ETH_CLOCK_GPIO16_OUT - 50MHz clock from internal APLL output on GPIO16 - possibly an inverter is needed for LAN8720
+   * ETH_CLOCK_GPIO17_OUT - 50MHz clock from internal APLL inverted output on GPIO17 - tested with LAN8720
+*/
+#define ETH_CLK_MODE    ETH_CLOCK_GPIO17_OUT
+
+// Pin# of the enable signal for the external crystal oscillator (-1 to disable for internal APLL source)
+#define ETH_POWER_PIN   16
+
+// Type of the Ethernet PHY (LAN8720 or TLK110)
+#define ETH_TYPE        ETH_PHY_LAN8720
+
+// I²C-address of Ethernet PHY (0 or 1 for LAN8720, 31 for TLK110)
+#define ETH_ADDR        1
+
+// Pin# of the I²C clock signal for the Ethernet PHY
+#define ETH_MDC_PIN     23
+
+// Pin# of the I²C IO signal for the Ethernet PHY
+#define ETH_MDIO_PIN    18
+
+#define NRST            5
+
+#define MQTT_HOST IPAddress(192, 168, 1, 173)
+#define MQTT_PORT 1883
+
+uint32_t lastMillis;
+uint64_t lastMicros;
+
+static bool eth_connected = false;
+
+TimerHandle_t ethReconnectTimer;
+
+void WiFiEvent(WiFiEvent_t event)
+{
+#if ESP_IDF_VERSION_MAJOR > 3
+    switch (event) {
+    case ARDUINO_EVENT_ETH_START:
+        Serial.println("ETH Started");
+        // set eth hostname here
+        ETH.setHostname("esp32-ethernet");
+        break;
+    case ARDUINO_EVENT_ETH_CONNECTED:
+        Serial.println("ETH Connected");
+        break;
+    case ARDUINO_EVENT_ETH_GOT_IP:
+        Serial.print("ETH MAC: ");
+        Serial.print(ETH.macAddress());
+        Serial.print(", IPv4: ");
+        Serial.print(ETH.localIP());
+        if (ETH.fullDuplex()) {
+            Serial.print(", FULL_DUPLEX");
+        }
+        Serial.print(", ");
+        Serial.print(ETH.linkSpeed());
+        Serial.println("Mbps");
+        eth_connected = true;
+        connectToMqtt();
+        break;
+    case ARDUINO_EVENT_ETH_DISCONNECTED:
+        Serial.println("ETH Disconnected");
+        eth_connected = false;
+        break;
+    case ARDUINO_EVENT_ETH_STOP:
+        Serial.println("ETH Stopped");
+        eth_connected = false;
+        break;
+    default:
+        break;
+    }
+#elif
+    switch (event) {
+    case SYSTEM_EVENT_ETH_START:
+        Serial.println("ETH Started");
+        // set eth hostname here
+        ETH.setHostname("esp32-ethernet");
+        break;
+    case SYSTEM_EVENT_ETH_CONNECTED:
+        Serial.println("ETH Connected");
+        break;
+    case SYSTEM_EVENT_ETH_GOT_IP:
+        Serial.print("ETH MAC: ");
+        Serial.print(ETH.macAddress());
+        Serial.print(", IPv4: ");
+        Serial.print(ETH.localIP());
+        if (ETH.fullDuplex()) {
+            Serial.print(", FULL_DUPLEX");
+        }
+        Serial.print(", ");
+        Serial.print(ETH.linkSpeed());
+        Serial.println("Mbps");
+        eth_connected = true;
+        break;
+    case SYSTEM_EVENT_ETH_DISCONNECTED:
+        Serial.println("ETH Disconnected");
+        eth_connected = false;
+        break;
+    case SYSTEM_EVENT_ETH_STOP:
+        Serial.println("ETH Stopped");
+        eth_connected = false;
+        break;
+    default:
+        break;
+    }
+#endif
+}
+
+void connectEth() {
+    pinMode(NRST, OUTPUT);
+    digitalWrite(NRST, 0);
+    delay(200);
+    digitalWrite(NRST, 1);
+    delay(200);
+    digitalWrite(NRST, 0);
+    delay(200);
+    digitalWrite(NRST, 1);
+    delay(200);
+
+    ETH.begin(ETH_ADDR,
+              ETH_POWER_PIN,
+              ETH_MDC_PIN,
+              ETH_MDIO_PIN,
+              ETH_TYPE,
+              ETH_CLK_MODE);
+}
+
+void testClient(const char * host, uint16_t port)
+{
+  Serial.print("\nconnecting to ");
+  Serial.println(host);
+
+  WiFiClient client;
+  if (!client.connect(host, port)) {
+    Serial.println("connection failed");
+    return;
+  }
+  client.printf("GET / HTTP/1.1\r\nHost: %s\r\n\r\n", host);
+  while (client.connected() && !client.available());
+  while (client.available()) {
+    Serial.write(client.read());
+  }
+
+  Serial.println("closing connection\n");
+  client.stop();
+}
+
+void setup() {
+    Serial.begin(115200);
+
+    WiFi.onEvent(WiFiEvent);
+
+    connectEth();
+
+    mqttReconnectTimer = xTimerCreate("mqttTimer", pdMS_TO_TICKS(2000), pdFALSE, (void*)0, reinterpret_cast<TimerCallbackFunction_t>(connectToMqtt));
+
+    mqttClient.onConnect(onMqttConnect);
+    mqttClient.onDisconnect(onMqttDisconnect);
+    mqttClient.onSubscribe(onMqttSubscribe);
+    mqttClient.onUnsubscribe(onMqttUnsubscribe);
+    //mqttClient.onMessage(onMqttMessage);
+    mqttClient.onPublish(onMqttPublish);
+    mqttClient.setServer(MQTT_HOST, MQTT_PORT);
+
+    pinMode(DATA_PIN, INPUT);
+
+    pinMode(DRY_CONT_PIN, OUTPUT);
+    digitalWrite(DRY_CONT_PIN, 0);
+    delay(2000);
+    digitalWrite(DRY_CONT_PIN, 1);
+    delay(2000);
+    digitalWrite(DRY_CONT_PIN, 0);
+    delay(2000);
+}
+
+void loop() {
+  if (lastMicros < micros()) {
+    data = digitalRead(DATA_PIN);
+    updateStateMachine(data);
+
+    if (PRINT_RAW_SIGNAL_FLAG)
+      printf("{}", data);
+
+    lastMillis = micros() + DATA_PERIOD;
+  }
+
+  if (eth_connected) {
+    if (lastMillis < millis()) {;
+      lastMillis = millis() + 3000;
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/stateMachine.cpp b/src/stateMachine.cpp
new file mode 100644
index 0000000..406f571
--- /dev/null
+++ b/src/stateMachine.cpp
@@ -0,0 +1,100 @@
+#include "stateMachine.h"
+
+void updateStateMachine(int data) {
+  switch (currentState) {
+    case NOT_CONNECTED:
+      if (data == 0) {
+        // Stay in the NOT_CONNECTED state
+      } else if (data == 1) {
+        currentState = CONNECTED;
+        println("connected");
+      }
+      break;
+
+    case CONNECTED:
+      if (data == 0) {
+        countZeros++;
+        if (countZeros >= NOT_CONNECTED_THRESHOLD) {
+          currentState = NOT_CONNECTED;
+          println("not connected");
+          resetCounters();
+        }
+      } else if (data == 1) {
+        if (countZeros >= INITIALIZING_CALL_THRESHOLD) {
+          currentState = RECEIVING_DATA;
+          println("receiving data");
+          resetCounters();
+        }
+      }
+      break;
+
+    case RECEIVING_DATA:
+      if (data != previousData) {
+        if (previousData == HIGH) {
+          dataLength++;
+        }
+        signalDuration = 0;
+      } else {
+        signalDuration++;
+      }
+      previousData = data;
+      if (data == 0) {
+        countOnes = 0;
+        countZeros++;
+        if (countZeros >= DATA_RECEIVED_THESHOLD) {
+          println("| data length: ", dataLength);
+          println("| flat: ", dataLength/2);
+          //sendDataToMQTT(dataLength/2);
+
+          currentState = DATA_RECEIVED;
+          println("data received");
+          resetCounters();
+        }
+      } else if (data == 1) {
+        countZeros = 0;
+        countOnes++;
+        if (countOnes >= CONNECTED_THRESHOLD) {
+          currentState = CONNECTED;
+          println("connected");
+          resetCounters();
+        }
+      }
+      break;
+
+    case DATA_RECEIVED:
+      if (data == 0) {
+        countZeros++;
+        if (countZeros >= CALL_ENDED_THRESHOLD) {
+          currentState = CALL_ENDED;
+          println("call ended");
+          resetCounters();
+        }
+      } else if (data == 1) {
+        countOnes++;
+        if (countOnes >= CONNECTED_THRESHOLD) {
+          currentState = CONNECTED;
+          println("connected");
+          resetCounters();
+        }
+      break;
+      }
+
+    case CALL_ENDED:
+      if (data == 0) {
+        countZeros++;
+        if (countZeros >= NOT_CONNECTED_THRESHOLD) {
+          currentState = NOT_CONNECTED;
+          println("not connected");
+          resetCounters();
+        }
+      } else if (data == 1) {
+        countOnes++;
+        if (countOnes >= CONNECTED_THRESHOLD) {
+          currentState = CONNECTED;
+          println("connected");
+          resetCounters();
+        }
+      break;
+      }
+  }
+}
\ No newline at end of file
diff --git a/src/stateMachine.h b/src/stateMachine.h
new file mode 100644
index 0000000..88f632e
--- /dev/null
+++ b/src/stateMachine.h
@@ -0,0 +1,33 @@
+#ifndef STATE_MACHINE_H
+#define STATE_MACHINE_H
+
+#include <Arduino.h>
+#include "utils/print.cpp"
+
+#define CONNECTED_THRESHOLD 50000
+#define NOT_CONNECTED_THRESHOLD 50000
+#define INITIALIZING_CALL_THRESHOLD 15000
+#define DATA_RECEIVED_THESHOLD 30000
+#define CALL_ENDED_THRESHOLD 10000
+
+enum State {
+  NOT_CONNECTED,
+  CONNECTED,
+  RECEIVING_DATA,
+  DATA_RECEIVED,
+  CALL_ENDED
+};
+
+int data = 0;
+State currentState = NOT_CONNECTED;
+int countZeros = 0;
+int countOnes = 0;
+
+int previousData = 0;
+int dataLength = 0;
+int signalDuration = 0;
+
+void resetCounters();
+void updateStateMachine(int data);
+
+#endif // STATE_MACHINE_H
\ No newline at end of file
diff --git a/src/utils/print.cpp b/src/utils/print.cpp
new file mode 100644
index 0000000..70c3ffc
--- /dev/null
+++ b/src/utils/print.cpp
@@ -0,0 +1,77 @@
+#include <Arduino.h>
+#include "utils/print.h"
+
+void print() {
+  // Empty function to terminate recursion
+}
+
+// Overloaded function to print a single value
+template <typename T>
+void print(T value) {
+  Serial.print(value);
+}
+
+// Recursive function to print multiple values
+template <typename T, typename... Args>
+void print(T value, Args... args) {
+  Serial.print(value);
+  print(args...); // Recursively call print for the remaining arguments
+}
+
+// Function to print a newline
+void println() {
+  Serial.println();
+}
+
+// Overloaded function to print a single value and a newline
+template <typename T>
+void println(T value) {
+  Serial.println(value);
+}
+
+// Recursive function to print multiple values and a newline
+template <typename T, typename... Args>
+void println(T value, Args... args) {
+  Serial.print(value);
+  println(args...); // Recursively call println for the remaining arguments
+}
+
+// Convenience functions for easy usage
+template <typename... Args>
+void print(Args... args) {
+  print(args...);
+}
+
+template <typename... Args>
+void println(Args... args) {
+  println(args...);
+}
+
+String fstring(const char* format) {
+  String result = format;
+  return result;
+}
+
+template <typename... Args>
+String fstring(const char* format, Args... args) {
+  String result = "";
+  while (*format) {
+    if (*format == '{' && *(format + 1) == '}' && sizeof...(args) > 0) {
+      result += String(args...);
+      format += 2; // Skip the "{}" in the format string
+    } else {
+      result += *format;
+      format++;
+    }
+  }
+  return result;
+}
+
+void printf(const char* format) {
+  Serial.print(fstring(format));
+}
+
+template <typename... Args>
+void printf(const char* format, Args... args) {
+  Serial.print(fstring(format, args...));
+}
\ No newline at end of file
diff --git a/src/utils/print.h b/src/utils/print.h
new file mode 100644
index 0000000..a207ca8
--- /dev/null
+++ b/src/utils/print.h
@@ -0,0 +1,44 @@
+#ifndef UTILS_PRINT_H
+#define UTILS_PRINT_H
+
+#include <Arduino.h>
+
+void print();
+
+// Overloaded function to print a single value
+template <typename T>
+void print(T value);
+
+// Recursive function to print multiple values
+template <typename T, typename... Args>
+void print(T value, Args... args);
+
+// Function to print a newline
+void println();
+
+// Overloaded function to print a single value and a newline
+template <typename T>
+void println(T value);
+
+// Recursive function to print multiple values and a newline
+template <typename T, typename... Args>
+void println(T value, Args... args);
+
+// Convenience functions for easy usage
+template <typename... Args>
+void print(Args... args);
+
+template <typename... Args>
+void println(Args... args);
+
+String fstring(const char* format);
+
+template <typename... Args>
+String fstring(const char* format, Args... args);
+
+void printf(const char* format);
+
+template <typename... Args>
+void printf(const char* format, Args... args);
+
+#endif // UTILS_PRINT_H
\ No newline at end of file