From d113c1b1f77adb0fd0d7964c7db2ffa91d7e2d37 Mon Sep 17 00:00:00 2001 From: jenoack Date: Tue, 14 Feb 2023 09:33:00 +0100 Subject: [PATCH] Latest Version with all features enabled --- .../.vscode/settings.json | 33 ++ .../include/capportal.hpp | 27 ++ .../Unendlichkeitsmaschine/include/eeprom.hpp | 55 +++ .../Unendlichkeitsmaschine/include/json.hpp | 29 ++ .../Unendlichkeitsmaschine/include/main.hpp | 106 +++++ .../Unendlichkeitsmaschine/include/mqtt.hpp | 75 ++++ .../Unendlichkeitsmaschine/include/oled.hpp | 43 ++ vscode/Unendlichkeitsmaschine/platformio.ini | 7 + .../Unendlichkeitsmaschine/src/capportal.cpp | 85 ++++ vscode/Unendlichkeitsmaschine/src/json.cpp | 28 ++ vscode/Unendlichkeitsmaschine/src/main.cpp | 425 ++++++++++++++---- vscode/Unendlichkeitsmaschine/src/mqtt.cpp | 131 ++++++ vscode/Unendlichkeitsmaschine/src/oled.cpp | 237 ++++++++++ 13 files changed, 1206 insertions(+), 75 deletions(-) create mode 100644 vscode/Unendlichkeitsmaschine/.vscode/settings.json create mode 100644 vscode/Unendlichkeitsmaschine/include/capportal.hpp create mode 100644 vscode/Unendlichkeitsmaschine/include/eeprom.hpp create mode 100644 vscode/Unendlichkeitsmaschine/include/json.hpp create mode 100644 vscode/Unendlichkeitsmaschine/include/main.hpp create mode 100644 vscode/Unendlichkeitsmaschine/include/mqtt.hpp create mode 100644 vscode/Unendlichkeitsmaschine/include/oled.hpp create mode 100644 vscode/Unendlichkeitsmaschine/src/capportal.cpp create mode 100644 vscode/Unendlichkeitsmaschine/src/json.cpp create mode 100644 vscode/Unendlichkeitsmaschine/src/mqtt.cpp create mode 100644 vscode/Unendlichkeitsmaschine/src/oled.cpp diff --git a/vscode/Unendlichkeitsmaschine/.vscode/settings.json b/vscode/Unendlichkeitsmaschine/.vscode/settings.json new file mode 100644 index 0000000..eacdacb --- /dev/null +++ b/vscode/Unendlichkeitsmaschine/.vscode/settings.json @@ -0,0 +1,33 @@ +{ + "svn.ignoreMissingSvnWarning": true, + "files.associations": { + "typeinfo": "cpp", + "*.tcc": "cpp", + "chrono": "cpp", + "ctime": "cpp", + "iomanip": "cpp", + "limits": "cpp", + "numeric": "cpp", + "ratio": "cpp", + "streambuf": "cpp", + "tuple": "cpp", + "type_traits": "cpp", + "utility": "cpp", + "array": "cpp", + "string": "cpp", + "unordered_map": "cpp", + "algorithm": "cpp", + "functional": "cpp", + "memory": "cpp", + "memory_resource": "cpp", + "optional": "cpp", + "random": "cpp", + "system_error": "cpp", + "cmath": "cpp", + "fstream": "cpp", + "istream": "cpp", + "ostream": "cpp", + "thread": "cpp", + "regex": "cpp" + } +} \ No newline at end of file diff --git a/vscode/Unendlichkeitsmaschine/include/capportal.hpp b/vscode/Unendlichkeitsmaschine/include/capportal.hpp new file mode 100644 index 0000000..3333977 --- /dev/null +++ b/vscode/Unendlichkeitsmaschine/include/capportal.hpp @@ -0,0 +1,27 @@ +#ifndef CAPPORTAL_HPP +#define CAPPORTAL_HPP + +#include // Replace with WiFi.h for ESP32 +#include // Replace with WebServer.h for ESP32 +#include +#include "time.h" +extern String html_content; + +class Cap{ +private: + const char* ntpServer = "pool.ntp.org"; + const long gmtOffset_sec = 3600; + const int daylightOffset_sec = 3600; + AutoConnect *Portal; + AutoConnectConfig *Config; +public: + void begin(); + void handle(); + unsigned long epochTime(); + String localTime(); + String localTime(time_t epochtime); + String localIP(); +}; + + +#endif //CAPPORTAL_HPP \ No newline at end of file diff --git a/vscode/Unendlichkeitsmaschine/include/eeprom.hpp b/vscode/Unendlichkeitsmaschine/include/eeprom.hpp new file mode 100644 index 0000000..60b3579 --- /dev/null +++ b/vscode/Unendlichkeitsmaschine/include/eeprom.hpp @@ -0,0 +1,55 @@ +#ifndef __eeprom_H__ +#define __eeprom_H__ +#include + +class Eeprom +{ +private: + +public: + Eeprom(); + ~Eeprom(); + void set_int(String name , uint64_t val ); + void set_int_period(String name , uint64_t val , time_t period_ms); + uint64_t get_int(String name ); + void begin(); +}; + +Eeprom::Eeprom() +{ +} + +Eeprom::~Eeprom() +{ +} + +void Eeprom::begin() +{ + Serial.println("Init EEPROM ..."); + NVS.begin(); +} + +void Eeprom::set_int_period(String name , uint64_t val , time_t period_ms) +{ + static time_t last_stored_ms = 0; + if((last_stored_ms == 0) || ((millis() - last_stored_ms) > period_ms)) + { + last_stored_ms = millis(); + set_int(name , val ); + } +} + +void Eeprom::set_int(String name , uint64_t val ) +{ + if(NVS.getInt(name) != val) + { + NVS.setInt(name, val); + } +} + +uint64_t Eeprom::get_int(String name ) +{ + return NVS.getInt(name); +} + +#endif \ No newline at end of file diff --git a/vscode/Unendlichkeitsmaschine/include/json.hpp b/vscode/Unendlichkeitsmaschine/include/json.hpp new file mode 100644 index 0000000..47d7f0d --- /dev/null +++ b/vscode/Unendlichkeitsmaschine/include/json.hpp @@ -0,0 +1,29 @@ +#ifndef JSON_HPP +#define JSON_HPP + +#include +#include +#include + +typedef struct +{ + const char * name; + unsigned long ticks; + unsigned long period_ticks; + const char * unit; + double value; + float unit_factor; + unsigned long last_update_ms; + unsigned long timestamp; +} DataStruct; + +const uint8_t NR_JSON_SECTION_OBJECTS = 8; + +class Myjson{ + +private: +public: + String create_json_string(volatile DataStruct *data_struct, size_t nr_dataobjects, time_t runtime_s); +}; + +#endif //JSON_HPP \ No newline at end of file diff --git a/vscode/Unendlichkeitsmaschine/include/main.hpp b/vscode/Unendlichkeitsmaschine/include/main.hpp new file mode 100644 index 0000000..6ae7982 --- /dev/null +++ b/vscode/Unendlichkeitsmaschine/include/main.hpp @@ -0,0 +1,106 @@ +#ifndef MAIN_HPP +#define MAIN_HPP + +#define MACHINE_RESTART +//#define DUMMY_DATA + +#define EEPROM_NAME_TICK "hall_tick" +#define EEPROM_NAME_TIMESTAMP "timestamp" +#define EEPROM_NAME_RUN_TIME "runtime" +#define EEPROM_NAME_START_EPOCHTIME "starttime" +#define EEPROM_STORE_RUNTIME_ALL_MS 1000 * 60 //* 5 + +#include +#include +#include +#include // Only needed for Arduino 1.6.5 and earlier +#include "eeprom.hpp" +#include "oled.hpp" +#include "capportal.hpp" + +#define HAS_MQTT +#ifdef HAS_MQTT +#include "mqtt.hpp" +#include "json.hpp" +Mymqtt mqtt; +Myjson json; + + +/*makerlab*/ +const char* mqttServer = "mqtt.makerlab-murnau.de"; +const int mqttPort = 8883; +const char* mqttUser = ""; +const char* mqttPassword = ""; + +/*hivemq*/ +/* +const char* mqttServer = "ccb0edef424149d4b3d2ee2ee503b87d.s1.eu.hivemq.cloud"; +const int mqttPort = 8883; +const char* mqttUser = "infinity"; +const char* mqttPassword = "12345678"; +*/ +#endif + +WiFiUDP ntpUDP; +//NTPClient timeClient(ntpUDP); +NTPClient timeClient( ntpUDP,"pool.ntp.org", 3600); + +// Variables to save date and time +String timeStamp; + +Eeprom Store; +Cap capportal; +Servo ESC; // Der ESC-Controller (Electronic Speed Controller bzw. elektronischer Fahrtregler) wird als Objekt mit dem Namen "ESC" festgelegt. +Oled display; + +const unsigned long UPDATE_TURN_VALUES_EVERY_MS = 1000; + +const uint8_t HALL_SENSORS_COUNT = 8; // input, sec, min, hour, day, week, month, year +const uint8_t ALL_DATA_COUNT = HALL_SENSORS_COUNT; + +const uint8_t HALL_TICKS_PER_TURN = 4; +const uint8_t HALL_MIN_PULSE_MS = 50; + +volatile DataStruct AllData[ALL_DATA_COUNT]; +volatile DataStruct *HallData = AllData; + +const char *Data_names[ALL_DATA_COUNT] = +{ + // Hall sensors + "cascade_input", "cascade_sec", "cascade_min", "cascade_hour", "cascade_day", "cascade_week", "cascade_month", "cascade_year", +}; +const char *Data_units[ALL_DATA_COUNT] = +{ + // Hall sensors + "U/min", "sec", "min", "hour", "day", "week", "month", "year", +}; +const float Data_units_factor[ALL_DATA_COUNT] = +{ + //Hall sensors + (60000.0/UPDATE_TURN_VALUES_EVERY_MS)/HALL_TICKS_PER_TURN, 1.0/HALL_TICKS_PER_TURN, 1.0/HALL_TICKS_PER_TURN, 1.0/HALL_TICKS_PER_TURN, 1.0/HALL_TICKS_PER_TURN, 1.0/HALL_TICKS_PER_TURN, 1.0/HALL_TICKS_PER_TURN, 1.0/HALL_TICKS_PER_TURN +}; + +const char ** Hall_names = Data_names; +const char ** Hall_units = Data_units; +const float * Hall_units_factor = Data_units_factor; + +const uint8_t LED_BUILTIN = 2; +const uint8_t POTI_PIN = 36; // never use ADC2 (GPIO0, 2, 4, 12, 13, 14, 15, 25, 26 and 27) if Wifi is active ... use ADC1 instead (GPIO32-GPIO39) +const uint8_t ESC_PIN = 16; + + +const uint8_t HALL1_PIN = 34; +const uint8_t HALL2_PIN = 35; +const uint8_t HALL3_PIN = 32; +const uint8_t HALL4_PIN = 33; +const uint8_t HALL5_PIN = 25; +const uint8_t HALL6_PIN = 26; +const uint8_t HALL7_PIN = 27; +const uint8_t HALL8_PIN = 14; + +const uint8_t MAX_ESC_SPEED = 60; +const uint8_t MIN_ESC_SPEED = 35; +const uint16_t MAX_POTI_VALUE = 4095; +const uint16_t JITTER_POTI_PERCENT = 10; + +#endif //MAIN_HPP \ No newline at end of file diff --git a/vscode/Unendlichkeitsmaschine/include/mqtt.hpp b/vscode/Unendlichkeitsmaschine/include/mqtt.hpp new file mode 100644 index 0000000..44e85d7 --- /dev/null +++ b/vscode/Unendlichkeitsmaschine/include/mqtt.hpp @@ -0,0 +1,75 @@ +#ifndef MQTT_HPP +#define MQTT_HPP + + + +#include // Replace with WiFi.h for ESP32 +//#include +#include +#include + +static const char *root_ca PROGMEM = R"EOF( +-----BEGIN CERTIFICATE----- +MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw +TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh +cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4 +WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu +ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY +MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc +h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+ +0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U +A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW +T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH +B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC +B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv +KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn +OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn +jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw +qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI +rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV +HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq +hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL +ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ +3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK +NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5 +ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur +TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC +jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc +oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq +4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA +mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d +emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc= +-----END CERTIFICATE----- +)EOF"; + + +#define MQTT_TRY_TO_CONNECT_MS 2000 +#define MQTT_CHANNEL "infinitymachine" +#define MQTT_BUFFER_SIZE 1024 +#define SEND_MQTT_EVERY_MS 1000 + +class Mymqtt +{ + +private: + WiFiClientSecure *espClient; + PubSubClient *client; + String _server = ""; + uint16_t _port = 0; + String _mqttUser = ""; + String _mqttPassword = ""; + String _data_sent = ""; + +public: + + void begin(const char * server, uint16_t mqttPort, const char* mqttUser, const char* mqttPassword); + bool connect(); + bool is_connected(); + bool publish(String topic,String data); + bool send_and_loop(String topic,String data); + bool loop(); + //void callback(char* topic, byte* payload, unsigned int length); + +}; + +#endif //MQTT_HPP \ No newline at end of file diff --git a/vscode/Unendlichkeitsmaschine/include/oled.hpp b/vscode/Unendlichkeitsmaschine/include/oled.hpp new file mode 100644 index 0000000..d638b03 --- /dev/null +++ b/vscode/Unendlichkeitsmaschine/include/oled.hpp @@ -0,0 +1,43 @@ +#ifndef OLED_HPP +#define OLED_HPP + +#include "SSD1306Wire.h" // legacy: #include "SSD1306.h" +#include "json.hpp" +#include + + +class Oled +{ + private: + const unsigned long PAGE_VIEW_TIME_MS = 10000; + const uint8_t HALLS_PER_PAGE = 4; + const uint8_t MAX_PAGE_NR = HALL2; + const uint8_t MIN_PAGE_NR = SPEED; + + enum pages {SPEED, HALL1, HALL2}; + + //SSD1306Wire display(0x3c, SDA, SCL); + SSD1306Wire *display; + + uint8_t xpos = 0; + uint8_t ypos = 0; + + pages page = SPEED; + uint8_t page_nr = MIN_PAGE_NR; + + void next_page(bool speedchange); + void show_page_header( String ipaddr, pages page); + void show_speed(int speed, int min_speed, int max_speed, double u_min, unsigned long runtime, pages page); + void show_halls(volatile DataStruct *HallData, uint8_t hall_sensor_count, pages page); + void show_page_footer(pages page); + + + public: + void begin(); + void progressBar(uint8_t i); + void show_values(int speed, int min_speed, int max_speed, volatile DataStruct *HallData, uint8_t hall_sensors_count, unsigned long runtime, String ipaddr); + +}; + + +#endif //OLED_HPP \ No newline at end of file diff --git a/vscode/Unendlichkeitsmaschine/platformio.ini b/vscode/Unendlichkeitsmaschine/platformio.ini index 23bbf19..fbb23ff 100644 --- a/vscode/Unendlichkeitsmaschine/platformio.ini +++ b/vscode/Unendlichkeitsmaschine/platformio.ini @@ -9,9 +9,16 @@ ; https://docs.platformio.org/page/projectconf.html [env:esp32] +monitor_speed = 115200 platform = espressif32 board = esp32dev framework = arduino lib_deps = thingpulse/ESP8266 and ESP32 OLED driver for SSD1306 displays@^4.2.1 madhephaestus/ESP32Servo@^0.11.0 + tridenttd/TridentTD_ESP32NVS@^1.0 + hieromon/AutoConnect@^1.4.0 + knolleary/PubSubClient@^2.8 + bblanchon/ArduinoJson@^6.19.4 + arduino-libraries/NTPClient@^3.2.1 + paulstoffregen/Time@^1.6.1 diff --git a/vscode/Unendlichkeitsmaschine/src/capportal.cpp b/vscode/Unendlichkeitsmaschine/src/capportal.cpp new file mode 100644 index 0000000..cc1c784 --- /dev/null +++ b/vscode/Unendlichkeitsmaschine/src/capportal.cpp @@ -0,0 +1,85 @@ +#include "capportal.hpp" +#include + +WebServer CapServer; +String html_content = ""; + +static void rootPage() { + CapServer.send(302, "text/html", html_content); +} + +void Cap::begin() { + + Portal = new AutoConnect(CapServer); + Config = new AutoConnectConfig(); + + Config->autoReset = false; // Not reset the module even by intentional disconnection using AutoConnect menu. + Config->portalTimeout = 1000; // Do not block the sketch and exit after 1000ms + //Config->immediateStart = true; + Config->autoReconnect = true; // Reconnect to known access points. + Config->reconnectInterval = 6; // Reconnection attempting interval is 3[min]. + Config->retainPortal = true; // Keep the captive portal open. + Config->apid = "Unendlichkeit_AP"; + Config->menuItems = Config->menuItems | AC_MENUITEM_UPDATE; + Config->hostName = "Unendlichkeit"; + Config->ota = AC_OTA_BUILTIN; + Portal->config(*Config); + + configTime(gmtOffset_sec, daylightOffset_sec, ntpServer); + + //content = ""; + CapServer.on("/", rootPage); + if (Portal->begin()) { + Serial.println("WiFi connected: " + WiFi.localIP().toString()); + } + else{ + Serial.println("No configured Wifi connection. Connect to '" + Config->apid + "' and configure network connections."); + } +} + +String Cap::localIP() +{ + return WiFi.localIP().toString(); +} + +void Cap::handle(){ + Portal->handleClient(); +} + +// Function that gets current epoch time +unsigned long Cap::epochTime() { + time_t now; + struct tm timeinfo; + if ( !getLocalTime(&timeinfo)) { + //Serial.println("Failed to obtain time"); + return(0); + } + time(&now); + //Serial.printf("Epochtime: %lu\n", now); + return now; +} + +String Cap::localTime(){ + String dateTime = ""; + struct tm timeinfo; + if(!getLocalTime(&timeinfo)){ + //Serial.println("Failed to obtain time"); + return(dateTime); + } + + //Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S"); + dateTime = String(timeinfo.tm_year + 1900) + "-" + String(timeinfo.tm_mon + 1) + "-" + String(timeinfo.tm_mday) + "+" + String(timeinfo.tm_hour) + ":" + String(timeinfo.tm_min) + ":" + String(timeinfo.tm_sec); + //Serial.printf("%s\n", dateTime.c_str()); + + return(dateTime); +} + +String Cap::localTime(time_t epochtime){ + String dateTime = ""; + dateTime = String(year(epochtime)) + "-" + String(month(epochtime)) + "-" + String(day(epochtime)) + "+" + String(hour(epochtime)) + ":" + String(minute(epochtime)) + ":" + String(second(epochtime)); + Serial.printf("%s\n", dateTime.c_str()); + + return(dateTime); +} + + diff --git a/vscode/Unendlichkeitsmaschine/src/json.cpp b/vscode/Unendlichkeitsmaschine/src/json.cpp new file mode 100644 index 0000000..82c0881 --- /dev/null +++ b/vscode/Unendlichkeitsmaschine/src/json.cpp @@ -0,0 +1,28 @@ +#include "json.hpp" + +String Myjson::create_json_string(volatile DataStruct *data_struct, size_t nr_dataobjects, time_t runtime_sec) +{ + static unsigned long last_send_ms = 0; + unsigned int _sections = nr_dataobjects; //number of different values + const unsigned int _sec_objects = NR_JSON_SECTION_OBJECTS; //number of objects per value + const unsigned int _obj_members = 5; //number of members per object + String json_str = ""; + + int _capacity = JSON_ARRAY_SIZE(_sections) + _sec_objects*JSON_OBJECT_SIZE(_obj_members); + DynamicJsonDocument _doc(_capacity + 100); + _doc["runtime_s"] = runtime_sec; + JsonObject _doc_object[_sections]; + for(unsigned int section_nr = 0; section_nr < _sections; section_nr++) + { + _doc_object[section_nr] = _doc.createNestedObject(data_struct[section_nr].name); + _doc_object[section_nr]["ticks"] = data_struct[section_nr].ticks; + _doc_object[section_nr]["value"] = data_struct[section_nr].value; + _doc_object[section_nr]["unit"] = data_struct[section_nr].unit; + _doc_object[section_nr]["timestamp"] = data_struct[section_nr].timestamp; + } + + int nr_bytes = serializeJson(_doc, json_str); + + return(json_str); + +} \ No newline at end of file diff --git a/vscode/Unendlichkeitsmaschine/src/main.cpp b/vscode/Unendlichkeitsmaschine/src/main.cpp index 7fe1bdd..303b0e0 100644 --- a/vscode/Unendlichkeitsmaschine/src/main.cpp +++ b/vscode/Unendlichkeitsmaschine/src/main.cpp @@ -1,115 +1,390 @@ -#include -#include -#include // Only needed for Arduino 1.6.5 and earlier -#include "SSD1306Wire.h" // legacy: #include "SSD1306.h" +#include "main.hpp" -SSD1306Wire display(0x3c, SDA, SCL); -Servo ESC; // Der ESC-Controller (Electronic Speed Controller bzw. elektronischer Fahrtregler) wird als Objekt mit dem Namen "ESC" festgelegt. int Drehregler; // Ausgabewert des Drehreglers -int speed, last_speed; // Das Wort "Geschwindigkeit" steht als Variable für den Ansteuerungswert am ESC. -unsigned long turn = 0; -unsigned long speedturns = 0; -unsigned long speedchanged_ms = 0; -unsigned long speed_u_per_min = 0; +int speed; // Das Wort "Geschwindigkeit" steht als Variable für den Ansteuerungswert am ESC. +int last_speed; -const uint8_t LED_BUILTIN = 2; -const uint8_t POTI_PIN = 4; -const uint8_t ESC_PIN = 16; -const uint8_t LS1_PIN = 14; +time_t run_time = 0; +time_t start_time = 0; -const uint8_t MAX_ESC_SPEED = 60; -const uint8_t MIN_ESC_SPEED = 35; -const uint16_t MAX_POTI_VALUE = 4095; +void ISR_HALL1(); +void ISR_HALL2(); +void ISR_HALL3(); +void ISR_HALL4(); +void ISR_HALL5(); +void ISR_HALL6(); +void ISR_HALL7(); +void ISR_HALL8(); -void show_values(); -void ISR(); +void data_generate(); +void data_store(); +void data_check(); +void data_init(); +void speed_set(); +void count_secs(time_t* run_time); + +#ifdef HAS_MQTT +void json_init(); +#endif void setup() { - Serial.begin(9600); - // Initialising the UI will init the display too. - display.init(); - display.clear(); - display.flipScreenVertically(); - //display.setFont(ArialMT_Plain_10); - display.setTextAlignment(TEXT_ALIGN_CENTER); - display.setFont(ArialMT_Plain_24); - display.setFont(ArialMT_Plain_16); - display.drawString(64, 2, "Init controller"); - display.setFont(ArialMT_Plain_10); - display.drawString(64, 40, "You may hear some beeps."); - display.drawString(64, 52, "That's OK! ;-)"); - display.display(); - display.setTextAlignment(TEXT_ALIGN_LEFT); - display.setFont(ArialMT_Plain_16); + delay(1000); + Serial.begin(115200); + Serial.println("Init capture portal ..."); + + randomSeed(micros()); + + display.begin(); + capportal.begin(); + html_content = ""; + + #ifdef HAS_MQTT + mqtt.begin(mqttServer, mqttPort, mqttUser, mqttPassword); + #endif + + Store.begin(); + data_init(); + + Serial.println("Init ETC ..."); ESC.attach(ESC_PIN,1000,2000); for(uint8_t i=0;i<=100;i=i+10) { - display.drawProgressBar(6, 28, 116, 6, i); + display.progressBar(i); delay(500); ESC.write(0); - display.display(); } - attachInterrupt(LS1_PIN, ISR, RISING); - - last_speed = 0; - speed = 0; + pinMode(39, INPUT_PULLDOWN); + pinMode(HALL1_PIN, INPUT); + pinMode(HALL2_PIN, INPUT); + pinMode(HALL3_PIN, INPUT); + pinMode(HALL4_PIN, INPUT); + pinMode(HALL5_PIN, INPUT); + pinMode(HALL6_PIN, INPUT); + pinMode(HALL7_PIN, INPUT); + pinMode(HALL8_PIN, INPUT); + //Serial.println("Init light sensor interrupt ..."); + //attachInterrupt(LS1_PIN, ISR_LS1, RISING); + + Serial.println("Init hall sensor interrupts ..."); + attachInterrupt(digitalPinToInterrupt(HALL1_PIN), ISR_HALL1, FALLING); + attachInterrupt(digitalPinToInterrupt(HALL2_PIN), ISR_HALL2, FALLING); + attachInterrupt(digitalPinToInterrupt(HALL3_PIN), ISR_HALL3, FALLING); + attachInterrupt(digitalPinToInterrupt(HALL4_PIN), ISR_HALL4, FALLING); + attachInterrupt(digitalPinToInterrupt(HALL5_PIN), ISR_HALL5, FALLING); + attachInterrupt(digitalPinToInterrupt(HALL6_PIN), ISR_HALL6, FALLING); + attachInterrupt(digitalPinToInterrupt(HALL7_PIN), ISR_HALL7, FALLING); + attachInterrupt(digitalPinToInterrupt(HALL8_PIN), ISR_HALL8, FALLING); + + Serial.println(" Auf gehts ... "); + } void loop() { + + capportal.handle(); + + data_generate(); + + count_secs(&run_time); + speed_set(); + data_check(); + data_store(); + + #ifdef HAS_MQTT + mqtt.send_and_loop("infinity/data", json.create_json_string(AllData, ALL_DATA_COUNT, run_time)); + #endif - Drehregler = analogRead(POTI_PIN); // Dieser Befehl liest den Wert des Potentiometers am analogen Pin A0 aus und speichert ihn unter der Variablen "Drehregler". Der Wert liegt zwischen 0 und 1023. - speed = map(Drehregler, 0, MAX_POTI_VALUE, MIN_ESC_SPEED, MAX_ESC_SPEED); // Der "MAP-" Befehl wandelt den Messwert aus der Variablen "Drehregler" um, damit er am ESC verarbeitet werden kann. Der Zahlenbereich 0 bis 1023 wird dabei in einen Zahlenwert zwischen 0 und 180 umgewandelt. - if(speed != last_speed) + display.show_values(speed, MIN_ESC_SPEED, MAX_ESC_SPEED, HallData, HALL_SENSORS_COUNT, run_time, capportal.localIP()); + +} + +void count_secs(time_t* run_time) +{ + static time_t last_update_ms = 0; + if(last_update_ms == 0) { - last_speed = speed; - speedchanged_ms = millis(); - speedturns = 0; + last_update_ms = millis(); + } + if(millis() - last_update_ms >= 1000) + { + last_update_ms = millis(); + *run_time = *run_time + 1; + } +} + +void speed_set() +{ + static uint8_t _count_meas = 0; + static int _Drehregler = 0; + static unsigned long _last_read_ms = 0; + + if( millis() - _last_read_ms > 25) + { + _last_read_ms = millis(); + int maxDrehregler = 0; + int minDrehregler = 0; + int sumDrehregler = 0; + for(uint8_t i = 0; i<6;i++) + { + Drehregler = analogRead(POTI_PIN); // Dieser Befehl liest den Wert des Potentiometers am analogen Pin A0 aus und speichert ihn unter der Variablen "Drehregler". Der Wert liegt zwischen 0 und 1023. + if(i==0) + { + maxDrehregler = Drehregler; + minDrehregler = Drehregler; + } + else + { + if(Drehregler < minDrehregler) + { + minDrehregler = Drehregler; + } + if(Drehregler > maxDrehregler) + { + maxDrehregler = Drehregler; + } + } + sumDrehregler = sumDrehregler + Drehregler; + } + sumDrehregler = sumDrehregler - maxDrehregler - minDrehregler; + sumDrehregler = sumDrehregler/4; + speed = map(sumDrehregler, 0, MAX_POTI_VALUE, MIN_ESC_SPEED, MAX_ESC_SPEED); // Der "MAP-" Befehl wandelt den Messwert aus der Variablen "Drehregler" um, damit er am ESC verarbeitet werden kann. Der Zahlenbereich 0 bis 1023 wird dabei in einen Zahlenwert zwischen 0 und 180 umgewandelt. + /* + if(abs(Drehregler - _Drehregler) < ((_Drehregler * JITTER_POTI_PERCENT)/100) ) + { + _count_meas++; + } + else + { + _count_meas = 0; + } + _Drehregler = Drehregler; + + if(_count_meas >= 4) + { + _count_meas >= 0; + speed = map(Drehregler, 0, MAX_POTI_VALUE, MIN_ESC_SPEED, MAX_ESC_SPEED); // Der "MAP-" Befehl wandelt den Messwert aus der Variablen "Drehregler" um, damit er am ESC verarbeitet werden kann. Der Zahlenbereich 0 bis 1023 wird dabei in einen Zahlenwert zwischen 0 und 180 umgewandelt. + } + */ } - if(millis() - speedchanged_ms > 10000) - { - speed_u_per_min = (speedturns*6); - speedturns = 0; - speedchanged_ms = millis(); - } - - //Serial.print("Drehregler:"); Serial.print(Drehregler); Serial.print(", Speed:");Serial.println(speed); if(MIN_ESC_SPEED < speed) ESC.write(speed); // Der endgültige Wert für den ESC wird an den ESC gesendet. Der ESC nimmt das Signal an dieser Stelle auf und steuert den Motor entsprechend der gesendeten Werte an. else ESC.write(0); - show_values(); } -void show_values() +void data_init() { - display.clear(); - display.setTextAlignment(TEXT_ALIGN_LEFT); - display.setFont(ArialMT_Plain_10); - display.drawString(1, 1, "Speed:"); - display.drawProgressBar(40, 4, 80, 6, map(speed, MIN_ESC_SPEED,MAX_ESC_SPEED,0,100)); - display.setFont(ArialMT_Plain_10); - display.drawString(1, 15, "Turns:"); - display.drawString(35, 15, String(turn)); - display.setFont(ArialMT_Plain_24); - display.setTextAlignment(TEXT_ALIGN_RIGHT); - display.drawString(64, 37, String(speed_u_per_min)); - display.setTextAlignment(TEXT_ALIGN_LEFT); - display.drawString(68,37, "U/min"); - display.display(); + Serial.println("Init internal data ..."); + #ifdef MACHINE_RESTART + run_time = 0; + start_time = capportal.epochTime(); + Store.set_int(EEPROM_NAME_RUN_TIME , run_time ); + Store.set_int(EEPROM_NAME_START_EPOCHTIME, start_time ); + Serial.printf("Machine restarted. Set runtime to 0. Stored this value and the epoch start time %lu to EEPROM.\n", start_time); + #else + run_time = Store.get_int(EEPROM_NAME_RUN_TIME ); + start_time = Store.get_int(EEPROM_NAME_START_EPOCHTIME ); + Serial.printf("Machine runtime loaded from EPPROM: %lu seconds. Machine was started %s\n", run_time, capportal.localTime(start_time)); + #endif + last_speed = 0; + speed = 0; + for (uint8_t i = 0; i < ALL_DATA_COUNT; i ++) + { + AllData[i].name = Data_names[i]; + AllData[i].unit = Data_units[i]; + AllData[i].value = 0; + AllData[i].unit_factor = Data_units_factor[i]; + AllData[i].period_ticks = 0; + #ifdef MACHINE_RESTART + AllData[i].ticks = 0; + #else + AllData[i].ticks = Store.get_int(EEPROM_NAME_TICK+String(i)); + AllData[i].timestamp = Store.get_int(EEPROM_NAME_TIMESTAMP+String(i)); + #endif + AllData[i].last_update_ms = 0; + AllData[i].timestamp = 0; + } + delay(500); } -void IRAM_ATTR ISR() +void data_check() { - turn++; - speedturns++; + static unsigned long speedchanged_ms ; + unsigned long current_millis = millis(); + + Serial.printf("H1(pin %d): %d, H2(pin %d): %d, H3(pin %d): %d, H4(pin %d): %d, H5(pin %d) : %d, H6(pin %d) : %d, H7(pin %d) : %d, H8(pin %d) : %d.\n", + HALL1_PIN, digitalRead(HALL1_PIN), + HALL2_PIN, digitalRead(HALL2_PIN), + HALL3_PIN, digitalRead(HALL3_PIN), + HALL4_PIN, digitalRead(HALL4_PIN), + HALL5_PIN, digitalRead(HALL5_PIN), + HALL6_PIN, digitalRead(HALL6_PIN), + HALL7_PIN, digitalRead(HALL7_PIN), + HALL8_PIN, digitalRead(HALL8_PIN) + ); + + + if(speed != last_speed) + { + last_speed = speed; + speedchanged_ms = current_millis; + HallData[0].period_ticks = 0; + } + else + { + if(current_millis - speedchanged_ms > UPDATE_TURN_VALUES_EVERY_MS) + { + speedchanged_ms = current_millis; + HallData[0].value = (HallData[0].period_ticks * HallData[0].unit_factor); + HallData[0].period_ticks = 0; + } + } + + for(uint8_t nr = 1; nr < HALL_SENSORS_COUNT; nr++) + { + HallData[nr].value = (HallData[nr].ticks * HallData[nr].unit_factor); + HallData[nr].period_ticks = 0; + } } +void data_store() +{ + static unsigned long _last_stored_ms = 0; + if(_last_stored_ms == 0) + { + _last_stored_ms = millis(); + } + if(millis() - _last_stored_ms >= EEPROM_STORE_RUNTIME_ALL_MS) + { + _last_stored_ms = millis(); + Store.set_int(EEPROM_NAME_RUN_TIME, run_time); + for(uint8_t nr = 0; nr < HALL_SENSORS_COUNT; nr++) + { + Store.set_int(EEPROM_NAME_TICK+String(nr), HallData[nr].ticks); + Store.set_int(EEPROM_NAME_TIMESTAMP+String(nr), HallData[nr].timestamp); + } + } +} +void IRAM_ATTR ISR_HALL1() +{ + const uint8_t hallnr = 0; + HallData[hallnr].ticks++; + HallData[hallnr].period_ticks++; + HallData[hallnr].timestamp = run_time; + HallData[hallnr].last_update_ms = millis(); +} + +void IRAM_ATTR ISR_HALL2() +{ + const uint8_t hallnr = 1; + HallData[hallnr].ticks++; + HallData[hallnr].period_ticks++; + HallData[hallnr].timestamp = run_time; + HallData[hallnr].last_update_ms = millis(); +} + +void IRAM_ATTR ISR_HALL3() +{ + const uint8_t hallnr = 2; + HallData[hallnr].ticks++; + HallData[hallnr].period_ticks++; + HallData[hallnr].timestamp = run_time; + HallData[hallnr].last_update_ms = millis(); +} + +void IRAM_ATTR ISR_HALL4() +{ + const uint8_t hallnr = 3; + HallData[hallnr].ticks++; + HallData[hallnr].period_ticks++; + HallData[hallnr].timestamp = run_time; + HallData[hallnr].last_update_ms = millis(); +} + +void IRAM_ATTR ISR_HALL5() +{ + const uint8_t hallnr = 4; + HallData[hallnr].ticks++; + HallData[hallnr].period_ticks++; + HallData[hallnr].timestamp = run_time; + HallData[hallnr].last_update_ms = millis(); +} + +void IRAM_ATTR ISR_HALL6() +{ + const uint8_t hallnr = 5; + HallData[hallnr].ticks++; + HallData[hallnr].period_ticks++; + HallData[hallnr].timestamp = run_time; + HallData[hallnr].last_update_ms = millis(); +} + +void IRAM_ATTR ISR_HALL7() +{ + const uint8_t hallnr = 6; + HallData[hallnr].ticks++; + HallData[hallnr].period_ticks++; + HallData[hallnr].timestamp = run_time; + HallData[hallnr].last_update_ms = millis(); +} + +void IRAM_ATTR ISR_HALL8() +{ + const uint8_t hallnr = 7; + HallData[hallnr].ticks++; + HallData[hallnr].period_ticks++; + HallData[hallnr].timestamp = run_time; + HallData[hallnr].last_update_ms = millis(); +} + +//this code is just for generating dummy data - in normal use not needed +void data_generate() +{ + #ifdef DUMMY_DATA + + static unsigned long _last_updated_ms = 0; + unsigned long _wait_ms = map(speed, MIN_ESC_SPEED, MAX_ESC_SPEED, 60000/500/4, 60000/1500/4); + //Serial.printf("Speed is : %lu and _wait_ms is %lu\n", speed, _wait_ms); + + if(_last_updated_ms == 0) + { + _last_updated_ms = millis(); + } + + if(millis() - _last_updated_ms >= _wait_ms ) + { + _last_updated_ms = millis(); + + for(uint8_t i = 0; i < HALL_SENSORS_COUNT; i++) + { + if(i == 0) + { + HallData[i].ticks++; + HallData[i].period_ticks++; + HallData[i].last_update_ms = millis(); + HallData[i].timestamp = run_time; + } + else + { + //Serial.printf("i=%d HallData[i-1].ticks=%lu %4 = %d\n" , i, HallData[i-1].ticks , HallData[i-1].ticks % 4); + if (HallData[i].last_update_ms != HallData[i-1].last_update_ms && HallData[i-1].ticks % 4 == 0) + { + HallData[i].ticks++; + HallData[i].period_ticks++; + HallData[i].last_update_ms = HallData[i-1].last_update_ms; + HallData[i].timestamp = run_time; + } + } + } + } + #endif +} diff --git a/vscode/Unendlichkeitsmaschine/src/mqtt.cpp b/vscode/Unendlichkeitsmaschine/src/mqtt.cpp new file mode 100644 index 0000000..5b825a8 --- /dev/null +++ b/vscode/Unendlichkeitsmaschine/src/mqtt.cpp @@ -0,0 +1,131 @@ +#include "mqtt.hpp" + + +void Mymqtt::begin(const char * server, uint16_t mqttPort, const char* mqttUser, const char* mqttPassword) +{ + _server = server; + _port = mqttPort; + _mqttPassword = mqttPassword; + _mqttUser = mqttUser; + espClient = new WiFiClientSecure();//new WiFiClient(); + client = new PubSubClient(*espClient); + client->setBufferSize(MQTT_BUFFER_SIZE); +} + +void callback(char* topic, byte* payload, unsigned int length) { + String incommingMessage = ""; + for (int i = 0; i < length; i++) incommingMessage+=(char)payload[i]; + Serial.println("Message arrived ["+String(topic)+"]"+incommingMessage); + // check for other commands + /* else if( strcmp(topic,command2_topic) == 0){ + if (incommingMessage.equals(“1”)) { } // do something else + } + */ +} + +bool Mymqtt::connect() +{ + static unsigned long last_connection_try_ms = 0; + unsigned long start_ms = 0; + if(true == is_connected()) + { + return true; + } + if((last_connection_try_ms == 0) || (millis() - last_connection_try_ms > MQTT_TRY_TO_CONNECT_MS)) + { + last_connection_try_ms = millis(); + if (WiFi.status() == WL_CONNECTED) + { + Serial.printf("(Re)connecting to MQTT ... server '%s' port: %d \n", _server.c_str(), _port); + espClient->setCACert(root_ca); + client->setServer(_server.c_str(), _port); + client->setCallback(callback); + start_ms = millis(); + String clientId = MQTT_CHANNEL "-"; // Create a random client ID + clientId += String(random(0xffff), HEX); + if (client->connect(MQTT_CHANNEL, _mqttUser.c_str(), _mqttPassword.c_str())) + { + Serial.println(" -> MQTT connected"); + String connect_msg = "Connected from Infinitymachine of MakerLab Murnau e.V at " + String(millis()); + if(client->publish("infinity/connected", connect_msg.c_str())) + { + Serial.println("Connection and initial publish passed."); + } + else + { + Serial.println("Connection and initial publish FAILED."); + } + } + else + { + Serial.print(" -> MQTT connection failed with state "); + Serial.println(client->state()); + } + } + else + { + Serial.println("Not connected to the WiFi network"); + } + } + return(is_connected()); +} + +bool Mymqtt::is_connected() +{ + return(client->connected()); +} + +bool Mymqtt::publish(String topic,String data) +{ + if(true == connect()) + { + client->publish(topic.c_str(), data.c_str(), true); + return(true); + } + return(false); +} + +bool Mymqtt::loop() +{ + if(true == connect()) + { + client->loop(); + return(true); + } + return(false); +} + +bool Mymqtt::send_and_loop(String topic,String data) +{ + static unsigned long _last_sent_ms = 0; + if(millis() - _last_sent_ms >= SEND_MQTT_EVERY_MS) + { + _last_sent_ms = millis(); + if(true == connect()) + { + client->loop(); + if(data != _data_sent) + { + if((MQTT_BUFFER_SIZE - 20) > data.length()) + { + if( client->publish(topic.c_str(), data.c_str() , data.length()) ) + { + _data_sent = data; + } + else + { + //Serial.printf("Sending data (topic: %s): FAILED!!!\n -> '%s'\n", topic.c_str(), data.c_str()); + return(false); + } + } + else + { + //Serial.printf("Data length (%d) is larger than MQTT buffer(%d + 20bytes header)\n", data.length(), MQTT_BUFFER_SIZE); + return(false); + } + } + return(true); + } + } + return(false); +} diff --git a/vscode/Unendlichkeitsmaschine/src/oled.cpp b/vscode/Unendlichkeitsmaschine/src/oled.cpp new file mode 100644 index 0000000..31a12a7 --- /dev/null +++ b/vscode/Unendlichkeitsmaschine/src/oled.cpp @@ -0,0 +1,237 @@ +#include "oled.hpp" +//#include "main.hpp" + +void Oled::begin() +{ + display = new SSD1306Wire(0x3c, SDA, SCL); + Serial.println("Init OLED display ..."); + display->init(); + display->clear(); + display->flipScreenVertically(); + //display->setFont(ArialMT_Plain_10); + display->setTextAlignment(TEXT_ALIGN_CENTER); + display->setFont(ArialMT_Plain_24); + display->setFont(ArialMT_Plain_16); + display->drawString(64, 2, "Init controller"); + display->setFont(ArialMT_Plain_10); + display->drawString(64, 40, "You may hear some beeps."); + display->drawString(64, 52, "That's OK! ;-)"); + display->display(); + display->setTextAlignment(TEXT_ALIGN_LEFT); + display->setFont(ArialMT_Plain_16); +} + +void Oled::progressBar(uint8_t i) +{ + display->drawProgressBar(6, 28, 116, 6, i); + display->display(); +} + +void Oled::next_page(bool speedchange) +{ + static unsigned long last_switch_page_ms = 0; + + if(last_switch_page_ms == 0) + { + page_nr = MIN_PAGE_NR; + last_switch_page_ms = millis(); + } + if(speedchange == true) + { + last_switch_page_ms = millis(); + page_nr = SPEED; + } + else{ + if(millis() - last_switch_page_ms > PAGE_VIEW_TIME_MS) + { + last_switch_page_ms = millis(); + if(page_nr >= MAX_PAGE_NR) + { + page_nr = MIN_PAGE_NR; + } + else + { + page_nr++; + } + } + } + page = (pages)page_nr; +} + +void Oled::show_values(int speed, int min_speed, int max_speed, volatile DataStruct *HallData, uint8_t hall_sensors_count, unsigned long runtime, String ipaddr) +{ + const uint8_t local_hall_count = 8; + static int _speed = 0; + static double _hall[local_hall_count] = {0, 0, 0, 0, 0, 0, 0, 0}; + static unsigned long _runtime = 0; + static String _ipaddr = "-"; + + bool hall_has_changed = false; + for(uint8_t i = 0; i < min(local_hall_count,hall_sensors_count); i++) + { + if(_hall[i] != HallData[i].value) + { + hall_has_changed = true; + break; + } + } + + if( _speed != speed || hall_has_changed == true || _runtime != runtime || _ipaddr != ipaddr) + { + + next_page(_speed != speed); + + _speed = speed; + for(uint8_t i = 0; i < min(local_hall_count,hall_sensors_count); i++) + { + _hall[i] = HallData[i].value; + } + _runtime = runtime; //runtime comes in in secs + _ipaddr = ipaddr; + + show_page_header(ipaddr, page); + show_speed(speed, min_speed, max_speed, _hall[0], _runtime, page ); + show_halls(HallData, min(local_hall_count,hall_sensors_count), page); + show_page_footer(page); + display->display(); + + + } + +} + +void Oled::show_page_footer(pages page) +{ + const int16_t distance = 1; + int16_t _xpos = 0; + uint8_t nr_pages = 1+(MAX_PAGE_NR - MIN_PAGE_NR); + const int16_t radius = 3; + const int16_t _ypos = 66 - radius; + uint8_t space_all_circles = nr_pages*(2*radius) + (nr_pages-1)*distance; + uint8_t start_xpos = (128-space_all_circles)/2; + for(uint8_t i = 0; i < nr_pages; i++) + { + _xpos = start_xpos + i*(radius*2+distance); + if((pages)i == page) + { + display->fillCircle(_xpos, _ypos, radius); + + } + display->drawCircle(_xpos, _ypos, radius); + } + display->drawHorizontalLine(0,63,start_xpos-3); + display->drawHorizontalLine(_xpos+radius*2-2,63,128 - (_xpos+radius*2)); +} + +void Oled::show_page_header(String ipaddr, pages page) +{ + xpos = 0; + ypos = 0; + display->clear(); + display->setTextAlignment(TEXT_ALIGN_LEFT); + display->setFont(ArialMT_Plain_10); + switch(page) + { + case HALL1: + case HALL2: + display->drawString(0, ypos, "Measured numbers:"); + display->setTextAlignment(TEXT_ALIGN_RIGHT); + break; + case SPEED: + default: + if(ipaddr == "0.0.0.0") + { + display->drawString(0, ypos, "No WiFi."); + display->setTextAlignment(TEXT_ALIGN_RIGHT); + display->drawString(128,ypos, "x"); + } + else + { + display->drawString(0, ypos, "IP:" + ipaddr); + display->setTextAlignment(TEXT_ALIGN_RIGHT); + display->drawString(128,ypos, "Y"); + display->drawString(124,ypos-2, "--"); + } + break; + } + display->drawHorizontalLine(0,ypos+12, 128); + ypos = ypos + 13; +} + +void Oled::show_speed(int speed, int min_speed, int max_speed, double u_min, unsigned long runtime, pages page) +{ + if(page == SPEED) + { + static char _runtime_str[26]; + static unsigned long _runtime = 0; + if( _runtime != runtime || _runtime == 0) + { + _runtime = runtime; + uint8_t _runtime_years = _runtime / (365*24*3600 + 6*3600); // this includes the leap year as an offset of 6hours each year -> all for years this is a full day + _runtime = _runtime - _runtime_years * (365*24*3600 + 6*3600); + uint8_t _runtime_month = _runtime / (30*24*3600); //assumes a month has 30 days ... + _runtime = _runtime - _runtime_month * (30*24*3600); + uint8_t _runtime_week = _runtime / (7*24*3600); + _runtime = _runtime - _runtime_week * (7*24*3600); + uint8_t _runtime_days = _runtime / (24*3600); // time in days + _runtime = _runtime - _runtime_days * (24*3600); + uint8_t _runtime_hour = _runtime / 3600 ; + _runtime = _runtime - _runtime_hour * 3600; + uint8_t _runtime_min = _runtime / 60; + _runtime = _runtime - _runtime_min * 60; + uint8_t _runtime_sec = _runtime; + sprintf(_runtime_str, "%01dy %02dm %01dw %01dd %02dh %02dm %02ds", _runtime_years, _runtime_month, _runtime_week, _runtime_days, _runtime_hour, _runtime_min, _runtime_sec); + } + + uint8_t _ypos = ypos; + display->setTextAlignment(TEXT_ALIGN_LEFT); + display->setFont(ArialMT_Plain_10); + display->drawString(1, _ypos, "Speed:"); + display->drawProgressBar(38, _ypos+4, 87, 5 , map(speed, min_speed, max_speed,0,100)); + display->setFont(ArialMT_Plain_10); + _ypos=_ypos+9; + display->setFont(ArialMT_Plain_24); + display->setTextAlignment(TEXT_ALIGN_RIGHT); + display->drawString(83, _ypos, String(u_min)); + display->setFont(ArialMT_Plain_16); + display->setTextAlignment(TEXT_ALIGN_LEFT); + display->drawString(85,_ypos+8, "U/min"); + _ypos=_ypos+24; + display->setFont(ArialMT_Plain_10); + display->setTextAlignment(TEXT_ALIGN_CENTER); + display->drawString(64, _ypos, _runtime_str); + } +} + +void Oled::show_halls(volatile DataStruct *HallData, uint8_t hall_sensor_count, pages page) +{ + if(page == HALL1 || page == HALL2) + { + uint8_t hall_start = (page_nr-HALL1)*HALLS_PER_PAGE; + uint8_t hall_count = min(HALLS_PER_PAGE, (uint8_t)(hall_sensor_count-hall_start)); + uint8_t _ypos = ypos; + uint8_t _xpos = 1; + display->setFont(ArialMT_Plain_10); + _ypos = _ypos + 1; + _xpos = _xpos + 1; + for(uint8_t i=hall_start; i<(hall_count+hall_start);i++) + { + String text = "H" + String(i+1) + " [" + String(HallData[i].unit) + "] "; + String value = String(HallData[i].value); + String point = "."; + uint16_t width_text = display->getStringWidth(text); + uint16_t width_value = display->getStringWidth(value); + uint16_t width_point = display->getStringWidth(point); + uint8_t nr_points = (128-_xpos-width_text-width_value)/width_point; + display->setTextAlignment(TEXT_ALIGN_LEFT); + display->drawString(_xpos, _ypos, text); + for(uint8_t i=0;idrawString(_xpos+width_text+i*width_point, _ypos, point); + } + display->setTextAlignment(TEXT_ALIGN_RIGHT); + display->drawString(128,_ypos, value); + _ypos = _ypos + 11; + } + } +} \ No newline at end of file