commit b913fca342e9ef3bd44226b6c90005330e1efc12 Author: Daan Meijer Date: Wed Jun 5 22:45:06 2024 +0200 work in progress diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..17cf7ee --- /dev/null +++ b/.gitignore @@ -0,0 +1,16 @@ +.pioenvs +.piolibdeps +.clang_complete +.gcc-flags.json +.pio + +.vscode/.browse.c_cpp.db* +.vscode/c_cpp_properties.json +.vscode/launch.json +.vscode/ipch + +src/WifiSettings.h + +vue/node_modules +vue/.vite +data/public \ No newline at end of file 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..50c5791 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,30 @@ +{ + "files.associations": { + "atomic": "cpp", + "*.tcc": "cpp", + "bitset": "cpp", + "chrono": "cpp", + "deque": "cpp", + "list": "cpp", + "string": "cpp", + "unordered_map": "cpp", + "vector": "cpp", + "iterator": "cpp", + "memory_resource": "cpp", + "optional": "cpp", + "string_view": "cpp", + "functional": "cpp", + "istream": "cpp", + "new": "cpp", + "ostream": "cpp", + "ranges": "cpp", + "sstream": "cpp", + "stdexcept": "cpp", + "streambuf": "cpp", + "system_error": "cpp", + "regex": "cpp", + "tuple": "cpp", + "variant": "cpp", + "random": "cpp" + } +} \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..01751ec --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Daan Meijer + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..1734066 --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +# Hanglamp + +## Intro + +Led strip driver for dual WS2812B strip of 46 leds \ 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/lib/README b/lib/README new file mode 100644 index 0000000..6debab1 --- /dev/null +++ b/lib/README @@ -0,0 +1,46 @@ + +This directory is intended for project specific (private) libraries. +PlatformIO will compile them to static libraries and link into executable file. + +The source code of each library should be placed in a an own separate directory +("lib/your_library_name/[here are source files]"). + +For example, see a structure of the following two libraries `Foo` and `Bar`: + +|--lib +| | +| |--Bar +| | |--docs +| | |--examples +| | |--src +| | |- Bar.c +| | |- Bar.h +| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html +| | +| |--Foo +| | |- Foo.c +| | |- Foo.h +| | +| |- README --> THIS FILE +| +|- platformio.ini +|--src + |- main.c + +and a contents of `src/main.c`: +``` +#include +#include + +int main (void) +{ + ... +} + +``` + +PlatformIO Library Dependency Finder will find automatically dependent +libraries scanning project source files. + +More information about PlatformIO Library Dependency Finder +- https://docs.platformio.org/page/librarymanager/ldf.html diff --git a/platformio.ini b/platformio.ini new file mode 100644 index 0000000..21364a4 --- /dev/null +++ b/platformio.ini @@ -0,0 +1,34 @@ +; 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:esp12e] +platform = espressif8266 +board = esp12e +framework = arduino +monitor_speed = 115200 +upload_speed = 921600 +lib_deps = + khoih-prog/ESPAsync_WiFiManager + bblanchon/ArduinoJson@^6.19.4 + vshymanskyy/Preferences@^2.0.0 + mbed-multitech/flash-fs + fastled/FastLED@^3.6.0 + arkhipenko/TaskScheduler@^3.7.0 + drk/PubSubClient@^2.8 + khoih-prog/ESPAsync_WiFiManager@^1.15.1 + devyte/ESPAsyncDNSServer@^1.0.0 + me-no-dev/ESPAsyncUDP + makuna/NeoPixelBus @ ^2.8.0 + + +lib_ldf_mode = chain+ +monitor_filters = default, esp8266_exception_decoder +board_build.filesystem = littlefs +board_build.f_cpu = 80000000L diff --git a/src/Hanglamp.cpp b/src/Hanglamp.cpp new file mode 100644 index 0000000..ffa01cb --- /dev/null +++ b/src/Hanglamp.cpp @@ -0,0 +1,152 @@ +#include "Led.h" + +#include + +#define _TASK_SLEEP_ON_IDLE_RUN // Enable 1 ms SLEEP_IDLE powerdowns between runs if no callback methods were invoked during the pass +#define _TASK_STATUS_REQUEST // Compile with support for StatusRequest functionality - triggering tasks on status change events in addition to time only + +#include + +#include "MQTT.h" +#include "Wifi.h" + +#include "FS.h" +#include + +#include "PinFinder.h" + + +void feedWatchdog(){ + ESP.wdtFeed(); +} + +bool blinkStatus = false; +void blink(){ + blinkStatus = !blinkStatus; + digitalWrite(LED_BUILTIN, blinkStatus); +} + +CRGB colorTable[] = { + CRGB::Red, + CRGB::Blue, + CRGB::Green, + CRGB::Purple, + CRGB::Turquoise, + CRGB::Yellow +}; + +uint8_t colorIndex = 0; +void colors(){ + colorIndex++; + if(colorIndex >= (sizeof(colorTable) / sizeof(CRGB))){ + colorIndex = 0; + } + Serial.printf("colorIndex: %d\n", colorIndex); + jumpTo(colorTable[colorIndex]); +} + +Scheduler runner; + +Task taskBlink(1000, TASK_FOREVER, &blink); +Task taskColors(1000, TASK_FOREVER, &colors); +// Task taskFade(1, TASK_FOREVER, &fadeTask); +// Task taskFeedWatchdog(1000, TASK_FOREVER, &feedWatchdog); + + +void setup() { + + + Serial.begin(115200); + + delay(500); + + if(!LittleFS.begin()){ + Serial.println("LittleFS Mount Failed"); + return; + } + + + // runner.init(); + Serial.println("Initialized scheduler"); + + runner.addTask(taskBlink); + runner.addTask(taskColors); + // runner.addTask(taskFade); + // runner.addTask(taskFeedWatchdog); + + // taskBlink.enable(); + taskColors.enable(); + // taskFade.enable(); + // taskFeedWatchdog.enable(); + + Serial.println("Scheduled tasks"); + + + // PinFinder_setup(); + + Led_setup(); + + jumpTo(CRGB(0xFF00FF)); + + Wifi_setup(); + + jumpTo(CRGB(0x00FFFF)); + + jumpTo(CRGB(0x000000)); + MQTT_setup(); + // OTA_setup(); + + jumpTo(CRGB(0x00FFFF)); + + ESP.wdtEnable(10000); +} + + +void loop() { + MQTT_loop(); + // OTA_loop(); + // PinFinder_loop(); + + // runner.execute(); + colors(); + + delay(100); + + String command; + + while(Serial.available()) { + + command = Serial.readString();// read the incoming data as string + + Serial.print("Have command: "); + Serial.println(command); + + int index = command.indexOf(":"); + if(index >= 0){ + String name = command.substring(0, index); + Serial.print("Have command name: "); + Serial.println(name); + if(name.equals("c")){ + //c:0xff4000 + //c:0xff0000 + //c:0x00ff00 + //c:0x0000ff + String color = command.substring(index+1); + if(color.startsWith("0x")){ + color = color.substring(2); + } + + int value = strtol(color.c_str(), NULL, 16); + + CRGB target; + target.r = (value >> 16) & 0xFF; + target.g = (value >> 8) & 0xFF; + target.b = (value >> 0) & 0xFF; + + jumpTo(target); + } + } + + } + +} diff --git a/src/Led.cpp b/src/Led.cpp new file mode 100644 index 0000000..a9d4b4c --- /dev/null +++ b/src/Led.cpp @@ -0,0 +1,117 @@ +#include "Led.h" + +CRGB led; + +CRGB source; +CRGB target; + + +CRGB strip1[STRIP_LENGTH]; +CRGB strip2[STRIP_LENGTH]; + + +int fadeStart = 0; + +void fadeTo(const CRGB & dest){ + + Serial.println("fadeTask is not implemented"); + return; + + char buffer[80]; + + if(led.r == dest.r && led.g == dest.g && led.b == dest.b){ + return; + } + + snprintf(buffer, sizeof(buffer), "Have a new target: 0x%02X%02X%02X (old color 0x%02X%02X%02X)\n", dest.r, dest.g, dest.b, led.r, led.g, led.b); + Serial.println(buffer); + + source = led; + target = dest; + + fadeStart = millis(); +} + +void displayLed(){ + for(uint8_t index = 0; index < STRIP_LENGTH; index++){ + // Serial.printf("Led 1:%02d 0x%02x%02x%02x\n", index, strip1[index].r, strip1[index].g, strip1[index].b); + } + for(uint8_t index = 0; index < STRIP_LENGTH; index++){ + // Serial.printf("Led 2:%02d 0x%02x%02x%02x\n", index, strip2[index].r, strip2[index].g, strip2[index].b); + } + FastLED.show(); +} + +void jumpTo(const CRGB & dest){ + for(uint8_t index = 0; index < STRIP_LENGTH; index++){ + strip1[index] = dest; + strip2[index] = dest; + } + Serial.printf("Set color to 0x%02x%02x%02x\n", dest.r, dest.g, dest.b); + displayLed(); +} + +void fadeTask(){ + Serial.println("fadeTask is not implemented"); + return; + if(fadeStart == 0){ + return; + } + + float fadeInMillis = millis() - fadeStart; + + float progress = (fadeInMillis / 1000) / FADE_PERIOD; + + fract8 fract = progress * 256; + + if(progress >= 1.0f){ + led = target; + source = target; + fadeStart = 0; + }else{ + led = source.lerp8(target, fract); + displayLed(); + } + +} + + + + +void Led_setup(){ + // pinMode(PIN_STRIP1, OUTPUT); + // pinMode(PIN_STRIP2, OUTPUT); + + // analogWriteRange(255); + + + // pinMode(PIN_BUILTIN_LED, OUTPUT); + // digitalWrite(PIN_BUILTIN_LED, HIGH); + + FastLED.addLeds(strip1, STRIP_LENGTH); + FastLED.addLeds(strip2, STRIP_LENGTH); +} + +void Led_loop(){ + strip2[0] = CRGB(255, 0, 0); + FastLED.show(); + delay(500); + strip2[1] = CRGB(0, 255, 0); + FastLED.show(); + delay(500); + strip2[2] = CRGB(0, 0, 255); + FastLED.show(); + delay(500); + strip2[5] = CRGB(150, 0, 255); + FastLED.show(); + delay(500); + strip2[9] = CRGB(255, 200, 20); + FastLED.show(); + delay(500); + strip2[14] = CRGB(85, 60, 180); + FastLED.show(); + delay(500); + strip2[19] = CRGB(50, 255, 20); + FastLED.show(); + delay(500); +} \ No newline at end of file diff --git a/src/Led.h b/src/Led.h new file mode 100644 index 0000000..cf689ca --- /dev/null +++ b/src/Led.h @@ -0,0 +1,26 @@ +#pragma once + +#include "stdint.h" +#include +#include + +#include + +#define PIN_BUILTIN_LED 2 + + +#define PIN_STRIP1 0 +#define PIN_STRIP2 2 + +#define STRIP_LENGTH 46 + +#define FADE_PERIOD 3.0f + + +void Led_setup(); +void Led_loop(); + +void jumpTo(const CRGB & dest); + +void fadeTo(const CRGB & dest); +void fadeTask(); \ No newline at end of file diff --git a/src/MQTT.cpp b/src/MQTT.cpp new file mode 100644 index 0000000..8390dcd --- /dev/null +++ b/src/MQTT.cpp @@ -0,0 +1,117 @@ +#include "MQTT.h" + +#include + + +#include +#include + + +WiFiClient espClient; +PubSubClient client(espClient); + + +std::vector * channels = new std::vector(); + +char mqtt_server[40] = "manus"; +char mqtt_port[6] = "1883"; + +void connect() { + + Serial.print("Attempting MQTT connection..."); + // Create a random client ID + String clientId = "Konijntje-"; + clientId += String(ESP.getChipId()); + // Attempt to connect + if (client.connect(clientId.c_str())) { + Serial.println("connected"); + + // ... and resubscribe +// if(topic != NULL){ +// client.subscribe(topic); +// } + } else { + Serial.print("failed, rc="); + Serial.println(client.state()); + + } +} + +void MQTT_publish(const char * topic, String str){ + char * buff = new char[str.length() + 1]; + str.toCharArray(buff, str.length() + 1); + MQTT_publish(topic, buff); + delete buff; +} + +void MQTT_publish(const char * topic, const char * msg){ + client.publish(topic, msg); +} + +void (*pCallback)(uint8_t *, unsigned int) = NULL; + + +void MQTT_subscribe(char * topic, void (*callback)(uint8_t *, unsigned int)){ + Serial.println("MQTT_subscribe"); + + Serial.printf("Topic: [%s]\n", topic); + + SubscribedChannel * channel = new SubscribedChannel(); + channel->topic = new String(topic); + channel->callback = callback; + client.subscribe(topic); + channels->push_back(*channel); +} + + + +void MQTT_callback(char* topic, uint8_t * payload, unsigned int length){ + + Serial.println("MQTT_callback"); + + for (auto channel = channels->begin(); channel != channels->end(); ++channel){ + if(channel->topic->equals(topic)){ + channel->callback(payload, length); + } + } + +} + + +void MQTT_setup(){ + + Serial.println("MQTT_init"); + + client.setServer(mqtt_server, String(mqtt_port).toInt()); + client.setCallback(MQTT_callback); + +// Serial.println("MQTT_init after callback"); +// char buff[256]; +// sprintf(buff, "%08x", _pCallback); +// Serial.println(buff); + + connect(); +} + +void MQTT_loop(){ + + #if DEBUG_GENERAL + Serial.println("MQTT_loop()"); + #endif + int reconnectCounter = 0; + while(!client.connected() && reconnectCounter < 3) { + Serial.println("MQTT: connecting..."); + reconnectCounter++; + connect(); + + if(client.connected()){ + Serial.println("MQTT: succes!"); + break; + }else{ + Serial.println("MQTT: connection failed, try again in 5 seconds"); + // Wait 5 seconds before retrying + delay(5000); + } + } + client.loop(); +} diff --git a/src/MQTT.h b/src/MQTT.h new file mode 100644 index 0000000..5ae11af --- /dev/null +++ b/src/MQTT.h @@ -0,0 +1,21 @@ + +#include "settings.h" + +#include "Arduino.h" + +using namespace std; + + + +struct SubscribedChannel { + String * topic; + void (*callback)(uint8_t *, unsigned int); +}; + + + + +void MQTT_publish(const char * topic, String str); +void MQTT_publish(const char * topic, const char * msg); +void MQTT_loop(); +void MQTT_setup(); diff --git a/src/PinFinder.cpp b/src/PinFinder.cpp new file mode 100644 index 0000000..583a66a --- /dev/null +++ b/src/PinFinder.cpp @@ -0,0 +1,33 @@ +#include "PinFinder.h" + +#include "Arduino.h" + +char pins[] = { + 0, + 2, + 4, + 5, + 12, + 13, + 14, + 15, + 16, +}; +void PinFinder_setup(){ + for(int index=0; index < sizeof(pins); index++){ + auto pin = pins[index]; + Serial.printf("Setting pin %d to OUTPUT\n", pin); + pinMode(pin, OUTPUT); + } +} + +void PinFinder_loop(){ + for(int index=0; index < sizeof(pins); index++){ + auto pin = pins[index]; + Serial.printf("Setting pin %d high\n", pin); + digitalWrite(pin, HIGH); + delay(1000); + Serial.printf("Setting pin %d low\n", pin); + digitalWrite(pin, LOW); + } +} diff --git a/src/PinFinder.h b/src/PinFinder.h new file mode 100644 index 0000000..68c5a39 --- /dev/null +++ b/src/PinFinder.h @@ -0,0 +1,2 @@ +void PinFinder_setup(); +void PinFinder_loop(); \ No newline at end of file diff --git a/src/Strip.h b/src/Strip.h new file mode 100644 index 0000000..e69de29 diff --git a/src/Wifi.cpp b/src/Wifi.cpp new file mode 100644 index 0000000..f18c69c --- /dev/null +++ b/src/Wifi.cpp @@ -0,0 +1,42 @@ +#include + +#include +#include + +#include "WifiSettings.h" + + + +void Wifi_setup_softap(){ + + Serial.print("Setting soft-AP ... "); + boolean result = WiFi.softAP("ESPsoftAP_01", "pass-to-soft-AP"); + if(result == true) + { + Serial.println("Ready"); + } + else + { + Serial.println("Failed!"); + } + +} + +void Wifi_setup(){ + + WiFi.mode(WIFI_STA); + //TODO: + WiFi.begin(WIFI_ESSID, WIFI_PASSWORD); + + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + + Serial.println(""); + Serial.println("WiFi connected"); + Serial.println("IP address: "); + Serial.println(WiFi.localIP()); + +} + diff --git a/src/Wifi.h b/src/Wifi.h new file mode 100644 index 0000000..8523e21 --- /dev/null +++ b/src/Wifi.h @@ -0,0 +1,3 @@ + + +void Wifi_setup(); \ No newline at end of file diff --git a/src/WifiSettings.h.example b/src/WifiSettings.h.example new file mode 100644 index 0000000..fb03893 --- /dev/null +++ b/src/WifiSettings.h.example @@ -0,0 +1,2 @@ +#define WIFI_ESSID "YOUR ESSID HERE" +#define WIFI_PASSWORD "YOUR PASSWORD HERE" diff --git a/src/settings.h b/src/settings.h new file mode 100644 index 0000000..c0553ff --- /dev/null +++ b/src/settings.h @@ -0,0 +1,7 @@ +#ifndef SETTINGS_H +#define SETTINGS_H + +extern char mqtt_server[40]; +extern char mqtt_port[6]; + +#endif \ No newline at end of file diff --git a/test/README b/test/README new file mode 100644 index 0000000..9b1e87b --- /dev/null +++ b/test/README @@ -0,0 +1,11 @@ + +This directory is intended for PlatformIO Test Runner and project tests. + +Unit Testing is a software testing method by which individual units of +source code, sets of one or more MCU program modules together with associated +control data, usage procedures, and operating procedures, are tested to +determine whether they are fit for use. Unit testing finds problems early +in the development cycle. + +More information about PlatformIO Unit Testing: +- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html