commit eb174182143f7e7c2aa5b25a3cacbafa1740b89a Author: Cole Deck Date: Fri Jan 19 12:07:54 2024 -0600 PicoLighter Wifi Test 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..89f6be4 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,13 @@ +{ + "files.associations": { + "array": "cpp", + "deque": "cpp", + "list": "cpp", + "string": "cpp", + "unordered_map": "cpp", + "vector": "cpp", + "string_view": "cpp", + "initializer_list": "cpp", + "ranges": "cpp" + } +} \ No newline at end of file diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..37007ef --- /dev/null +++ b/TODO.md @@ -0,0 +1,12 @@ +# TODO List + +1. Add basic HTML web UI to pass xLights check (strip data, pins, basic color picker ...) +2. Add support for other protocols (not super important, because everyone uses E1.31) +3. Control software or master node - Switch between software (LedFX vs xLights, OpenRGB, etc), basic lighting controls +4. Determine when to run FastLED.show() - on E1.31 sync? does all software support that? does this lib support that? +5. Test with real lights +6. Optocoupler vs level shifter +7. Design miniRIB PCB +8. Pick 24V PSU, enclosure, 24V strip choice +9. Placement - alu profiles, diffusers (13-20mm), heat sinking +10. Waterproofing - connectors 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..3ae4a79 --- /dev/null +++ b/platformio.ini @@ -0,0 +1,22 @@ +; 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:pico] +board = rpipicow +framework = arduino +platform = https://github.com/maxgerhardt/platform-raspberrypi.git +board_build.core = earlephilhower +upload_port = /run/media/amelia/RPI-RP2/ +monitor_speed = 115200 +board_build.filesystem_size = 1m +board_build.f_cpu = 133000000L +board_flags = -DWIFICC=CYW43_COUNTRY_USA +lib_deps = + fastled/FastLED@^3.6.0 diff --git a/src/e131.cpp b/src/e131.cpp new file mode 100644 index 0000000..1f90856 --- /dev/null +++ b/src/e131.cpp @@ -0,0 +1,275 @@ +/* +* e131.cpp +* +* Project: E131 - E.131 (sACN) library for Arduino +* Copyright (c) 2015 Shelby Merrick +* http://www.forkineye.com +* +* This program is provided free for you to use in any way that you wish, +* subject to the laws and regulations where you are using it. Due diligence +* is strongly suggested before using this code. Please give credit where due. +* +* The Author makes no warranty of any kind, express or implied, with regard +* to this program or the documentation contained in this document. The +* Author shall not be liable in any event for incidental or consequential +* damages in connection with, or arising out of, the furnishing, performance +* or use of these programs. +* +* Modified to work on RP2040 by Amelia Deck +* This version will not run anymore on ESP32 due to changes to the includes. +* +*/ + +#include "e131.h" +#include + +/* E1.17 ACN Packet Identifier */ +#ifdef ARDUINO_ARCH_AVR +const PROGMEM byte E131::ACN_ID[12] = { 0x41, 0x53, 0x43, 0x2d, 0x45, 0x31, 0x2e, 0x31, 0x37, 0x00, 0x00, 0x00 }; +#else +const byte E131::ACN_ID[12] = { 0x41, 0x53, 0x43, 0x2d, 0x45, 0x31, 0x2e, 0x31, 0x37, 0x00, 0x00, 0x00 }; +#endif + +/* Constructor */ +E131::E131() { +#ifdef NO_DOUBLE_BUFFER + memset(pbuff1.raw, 0, sizeof(pbuff1.raw)); + packet = &pbuff1; + pwbuff = packet; +#else + memset(pbuff1.raw, 0, sizeof(pbuff1.raw)); + memset(pbuff2.raw, 0, sizeof(pbuff2.raw)); + packet = &pbuff1; + pwbuff = &pbuff2; +#endif + + stats.num_packets = 0; + stats.packet_errors = 0; +} + +void E131::initUnicast() { + delay(100); + udp.begin(E131_DEFAULT_PORT); + if (Serial) { + Serial.print(F("- Unicast port: ")); + Serial.println(E131_DEFAULT_PORT); + } +} + +void E131::initMulticast(uint16_t universe, uint8_t n) { + delay(100); + IPAddress address = IPAddress(239, 255, ((universe >> 8) & 0xff), + ((universe >> 0) & 0xff)); +#ifdef INT_ESP8266 + ip_addr_t ifaddr; + ip_addr_t multicast_addr; + + ifaddr.addr = static_cast(WiFi.localIP()); + for (uint8_t i = 1; i < n; i++) { + multicast_addr.addr = static_cast(IPAddress(239, 255, + (((universe + i) >> 8) & 0xff), (((universe + i) >> 0) + & 0xff))); + igmp_joingroup(&ifaddr, &multicast_addr); + } + + udp.beginMulticast(WiFi.localIP(), address, E131_DEFAULT_PORT); +#endif + if (Serial) { + Serial.println(F("- Multicast Enabled")); + } +} + +/****** START - Wireless ifdef block ******/ +#if defined (INT_ESP8266) || defined (INT_WIFI) + +/* WiFi Initialization */ +int E131::initWiFi(const char *ssid, const char *passphrase) { + /* Switch to station mode and disconnect just in case */ + WiFi.mode(WIFI_STA); + WiFi.disconnect(); + delay(100); + + if (Serial) { + Serial.println(""); + Serial.print(F("Connecting to ")); + Serial.print(ssid); + } + + if (passphrase != NULL) + WiFi.begin(ssid, passphrase); + else + WiFi.begin(ssid); + + uint32_t timeout = millis(); + uint8_t retval = 1; + while (WiFi.status() != WL_CONNECTED) { + delay(250); + if (Serial) + Serial.print("."); + if (millis() - timeout > WIFI_CONNECT_TIMEOUT) { + retval = 0; + if (Serial) { + Serial.println(""); + Serial.println(F("*** Failed to connect ***")); + } + break; + } + } + + return retval; +} + +void E131::begin(e131_listen_t type, uint16_t universe, uint8_t n) { + if (type == E131_UNICAST) + initUnicast(); + if (type == E131_MULTICAST) + initMulticast(universe, n); +} + +int E131::begin(const char *ssid, const char *passphrase) { + if (initWiFi(ssid, passphrase)) { + if (Serial) { + Serial.println(""); + Serial.print(F("Connected DHCP with IP: ")); + Serial.println(WiFi.localIP()); + } + initUnicast(); + } + return WiFi.status(); +} + +int E131::begin(const char *ssid, const char *passphrase, + IPAddress ip, IPAddress netmask, IPAddress gateway, IPAddress dns) { + if (initWiFi(ssid, passphrase)) { + WiFi.config(ip, gateway, netmask, dns); + if (Serial) { + Serial.println(""); + Serial.print(F("Connected with Static IP: ")); + Serial.println(WiFi.localIP()); + } + initUnicast(); + } + return WiFi.status(); +} + +#endif +/****** END - Wireless ifdef block ******/ + +/****** START - ESP8266 ifdef block ******/ +#if defined (INT_ESP8266) +int E131::beginMulticast(const char *ssid, const char *passphrase, + uint16_t universe, uint8_t n) { + if (initWiFi(ssid, passphrase)) { + if (Serial) { + Serial.println(""); + Serial.print(F("Connected DHCP with IP: ")); + Serial.println(WiFi.localIP()); + } + initMulticast(universe, n); + } + return WiFi.status(); +} + +int E131::beginMulticast(const char *ssid, const char *passphrase, + uint16_t universe, IPAddress ip, IPAddress netmask, + IPAddress gateway, IPAddress dns, uint8_t n) { + if (initWiFi(ssid, passphrase)) { + WiFi.config(ip, dns, gateway, netmask); + if (Serial) { + Serial.println(""); + Serial.print(F("Connected with Static IP: ")); + Serial.println(WiFi.localIP()); + } + initMulticast(universe, n); + } + return WiFi.status(); +} +#endif +/****** END - ESP8266 ifdef block ******/ + +/****** START - Ethernet ifdef block ******/ +#if defined (INT_ETHERNET) + +/* Unicast Ethernet Initializers */ +int E131::begin(uint8_t *mac) { + int retval = 0; + + if (Serial) { + Serial.println(""); + Serial.println(F("Requesting Address via DHCP")); + Serial.print(F("- MAC: ")); + for (int i = 0; i < 6; i++) + Serial.print(mac[i], HEX); + Serial.println(""); + } + + retval = Ethernet.begin(mac); + + if (Serial) { + if (retval) { + Serial.print(F("- IP Address: ")); + Serial.println(Ethernet.localIP()); + } else { + Serial.println(F("** DHCP FAILED")); + } + } + + if (retval) + initUnicast(); + + return retval; +} + +void E131::begin(uint8_t *mac, IPAddress ip, IPAddress netmask, + IPAddress gateway, IPAddress dns) { + Ethernet.begin(mac, ip, dns, gateway, netmask); + if (Serial) { + Serial.println(""); + Serial.println(F("Static Configuration")); + Serial.println(F("- MAC: ")); + for (int i = 0; i < 6; i++) + Serial.print(mac[i], HEX); + Serial.print(F("- IP Address: ")); + Serial.println(Ethernet.localIP()); + } + + initUnicast(); +} + +/* Multicast Ethernet Initializers */ +int E131::beginMulticast(uint8_t *mac, uint16_t universe, uint8_t n) { + //TODO: Add ethernet multicast support +} + +void E131::beginMulticast(uint8_t *mac, uint16_t universe, + IPAddress ip, IPAddress netmask, IPAddress gateway, + IPAddress dns, uint8_t n) { + //TODO: Add ethernet multicast support +} +#endif +/****** END - Ethernet ifdef block ******/ + +void E131::dumpError(e131_error_t error) { + switch (error) { + case ERROR_ACN_ID: + Serial.print(F("INVALID PACKET ID: ")); + for (int i = 0; i < sizeof(ACN_ID); i++) + Serial.print(pwbuff->acn_id[i], HEX); + Serial.println(""); + break; + case ERROR_PACKET_SIZE: + Serial.println(F("INVALID PACKET SIZE: ")); + break; + case ERROR_VECTOR_ROOT: + Serial.print(F("INVALID ROOT VECTOR: 0x")); + Serial.println(htonl(pwbuff->root_vector), HEX); + break; + case ERROR_VECTOR_FRAME: + Serial.print(F("INVALID FRAME VECTOR: 0x")); + Serial.println(htonl(pwbuff->frame_vector), HEX); + break; + case ERROR_VECTOR_DMP: + Serial.print(F("INVALID DMP VECTOR: 0x")); + Serial.println(pwbuff->dmp_vector, HEX); + } +} \ No newline at end of file diff --git a/src/e131.h b/src/e131.h new file mode 100644 index 0000000..4dc22d5 --- /dev/null +++ b/src/e131.h @@ -0,0 +1,249 @@ +/* +* e131.h +* +* Project: E131 - E.131 (sACN) library for Arduino +* Copyright (c) 2015 Shelby Merrick +* http://www.forkineye.com +* +* This program is provided free for you to use in any way that you wish, +* subject to the laws and regulations where you are using it. Due diligence +* is strongly suggested before using this code. Please give credit where due. +* +* The Author makes no warranty of any kind, express or implied, with regard +* to this program or the documentation contained in this document. The +* Author shall not be liable in any event for incidental or consequential +* damages in connection with, or arising out of, the furnishing, performance +* or use of these programs. +* +* Modified to work on RP2040 by Amelia Deck +* This version will not run anymore on ESP32 due to changes to the includes. +* +*/ + +#ifndef E131_H_ +#define E131_H_ + +#include "Arduino.h" + +/* Network interface detection. WiFi for ESP8266 and Ethernet for AVR */ +# include +//# include +//# include +# include +# include +# define _UDP WiFiUDP +# define INT_WIFI + +#define NO_DOUBLE_BUFFER 1 +/* Defaults */ +#define E131_DEFAULT_PORT 5568 +#define WIFI_CONNECT_TIMEOUT 15000 /* 15 seconds */ + +/* E1.31 Packet Offsets */ +#define E131_ROOT_PREAMBLE_SIZE 0 +#define E131_ROOT_POSTAMBLE_SIZE 2 +#define E131_ROOT_ID 4 +#define E131_ROOT_FLENGTH 16 +#define E131_ROOT_VECTOR 18 +#define E131_ROOT_CID 22 + +#define E131_FRAME_FLENGTH 38 +#define E131_FRAME_VECTOR 40 +#define E131_FRAME_SOURCE 44 +#define E131_FRAME_PRIORITY 108 +#define E131_FRAME_RESERVED 109 +#define E131_FRAME_SEQ 111 +#define E131_FRAME_OPT 112 +#define E131_FRAME_UNIVERSE 113 + +#define E131_DMP_FLENGTH 115 +#define E131_DMP_VECTOR 117 +#define E131_DMP_TYPE 118 +#define E131_DMP_ADDR_FIRST 119 +#define E131_DMP_ADDR_INC 121 +#define E131_DMP_COUNT 123 +#define E131_DMP_DATA 125 + +/* E1.31 Packet Structure */ +typedef union { + struct { + /* Root Layer */ + uint16_t preamble_size; + uint16_t postamble_size; + uint8_t acn_id[12]; + uint16_t root_flength; + uint32_t root_vector; + uint8_t cid[16]; + + /* Frame Layer */ + uint16_t frame_flength; + uint32_t frame_vector; + uint8_t source_name[64]; + uint8_t priority; + uint16_t reserved; + uint8_t sequence_number; + uint8_t options; + uint16_t universe; + + /* DMP Layer */ + uint16_t dmp_flength; + uint8_t dmp_vector; + uint8_t type; + uint16_t first_address; + uint16_t address_increment; + uint16_t property_value_count; + uint8_t property_values[513]; + } __attribute__((packed)); + + uint8_t raw[638]; +} e131_packet_t; + +/* Error Types */ +typedef enum { + ERROR_NONE, + ERROR_IGNORE, + ERROR_ACN_ID, + ERROR_PACKET_SIZE, + ERROR_VECTOR_ROOT, + ERROR_VECTOR_FRAME, + ERROR_VECTOR_DMP +} e131_error_t; + +/* E1.31 Listener Types */ +typedef enum { + E131_UNICAST, + E131_MULTICAST +} e131_listen_t; + +/* Status structure */ +typedef struct { + uint32_t num_packets; + uint32_t packet_errors; + IPAddress last_clientIP; + uint16_t last_clientPort; +} e131_stats_t; + +class E131 { + private: + /* Constants for packet validation */ + static const uint8_t ACN_ID[]; + static const uint32_t VECTOR_ROOT = 4; + static const uint32_t VECTOR_FRAME = 2; + static const uint8_t VECTOR_DMP = 2; + + e131_packet_t pbuff1; /* Packet buffer */ +#ifndef NO_DOUBLE_BUFFER + e131_packet_t pbuff2; /* Double buffer */ +#endif + e131_packet_t *pwbuff; /* Pointer to working packet buffer */ + _UDP udp; /* UDP handle */ + + /* Internal Initializers */ + int initWiFi(const char *ssid, const char *passphrase); + int initEthernet(uint8_t *mac, IPAddress ip, IPAddress netmask, + IPAddress gateway, IPAddress dns); + void initUnicast(); + void initMulticast(uint16_t universe, uint8_t n = 1); + + public: + uint8_t *data; /* Pointer to DMX channel data */ + uint16_t universe; /* DMX Universe of last valid packet */ + e131_packet_t *packet; /* Pointer to last valid packet */ + e131_stats_t stats; /* Statistics tracker */ + + E131(); + + /* Generic UDP listener, no physical or IP configuration */ + void begin(e131_listen_t type, uint16_t universe = 1, uint8_t n = 1); + +/****** START - Wireless ifdef block ******/ +#if defined (INT_WIFI) || defined (INT_ESP8266) + /* Unicast WiFi Initializers */ + int begin(const char *ssid, const char *passphrase); + int begin(const char *ssid, const char *passphrase, + IPAddress ip, IPAddress netmask, IPAddress gateway, IPAddress dns); +#endif +/****** END - Wireless ifdef block ******/ + +/****** START - ESP8266 ifdef block ******/ +#if defined (INT_ESP8266) + /* Multicast WiFi Initializers -- ESP8266 Only */ + int beginMulticast(const char *ssid, const char *passphrase, + uint16_t universe, uint8_t n = 1); + int beginMulticast(const char *ssid, const char *passphrase, + uint16_t universe, IPAddress ip, IPAddress netmask, + IPAddress gateway, IPAddress dns, uint8_t n = 1); +#endif +/****** END - ESP8266 ifdef block ******/ + +/****** START - Ethernet ifdef block ******/ +#if defined (INT_ETHERNET) + /* Unicast Ethernet Initializers */ + int begin(uint8_t *mac); + void begin(uint8_t *mac, + IPAddress ip, IPAddress netmask, IPAddress gateway, IPAddress dns); + + /* Multicast Ethernet Initializers */ + int beginMulticast(uint8_t *mac, uint16_t universe, uint8_t n = 1); + void beginMulticast(uint8_t *mac, uint16_t universe, + IPAddress ip, IPAddress netmask, IPAddress gateway, + IPAddress dns, uint8_t n = 1); +#endif +/****** END - Ethernet ifdef block ******/ + + /* Diag functions */ + void dumpError(e131_error_t error); + + /* Main packet parser */ + inline uint16_t parsePacket() { + e131_error_t error; + uint16_t retval = 0; + + int size = udp.parsePacket(); + if (size) { + udp.readBytes(pwbuff->raw, size); + error = validate(); + if (!error) { +#ifndef NO_DOUBLE_BUFFER + e131_packet_t *swap = packet; + packet = pwbuff; + pwbuff = swap; +#endif + universe = htons(packet->universe); + data = packet->property_values + 1; + retval = htons(packet->property_value_count) - 1; + stats.num_packets++; + stats.last_clientIP = udp.remoteIP(); + stats.last_clientPort = udp.remotePort(); + } else if (error == ERROR_IGNORE) { + // Do nothing + } else { + if (Serial) + dumpError(error); + stats.packet_errors++; + } + } + return retval; + } + + /* Packet validater */ + inline e131_error_t validate() { +#ifdef ARDUINO_ARCH_AVR + if (memcmp_P(pwbuff->acn_id, ACN_ID, sizeof(pwbuff->acn_id))) +#else + if (memcmp(pwbuff->acn_id, ACN_ID, sizeof(pwbuff->acn_id))) +#endif + return ERROR_ACN_ID; + if (htonl(pwbuff->root_vector) != VECTOR_ROOT) + return ERROR_VECTOR_ROOT; + if (htonl(pwbuff->frame_vector) != VECTOR_FRAME) + return ERROR_VECTOR_FRAME; + if (pwbuff->dmp_vector != VECTOR_DMP) + return ERROR_VECTOR_DMP; + if (pwbuff->property_values[0] != 0) + return ERROR_IGNORE; + return ERROR_NONE; + } +}; + +#endif /* E131_H_ */ \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..2685f7e --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,362 @@ +// Includes +#include +//#include +#include +#include +#include +#include "e131.h" +#include + +// User configurable + +const char* ssid = "iPhone 14"; // WiFi SSID +const char* password = "givemewifi"; // WiFi Password +const char* ntpserver = "pool.ntp.org"; // Address of NTP server. Example: pool.ntp.org +const char* HOSTNAME = "lighttest"; + +const char* update_path = "/firmware"; +const char* update_username = "admin"; +const char* update_password = "pico-stripper"; + +//#define DEBUG +// Total LED count +#define MAX_LEDS 50 + +#define LED_TYPE WS2812B +// Amount of color channels per pixel - i.e. RGB = 3, RGBW = 4 +#define PIXEL_SIZE 3 + +// Number of LED strips connected +#define LED_STRIPS 1 + +// Define the data pin connection to each strip +// Only uncomment the strips you use +#define STRIP1 28 +//#define STRIP2 0 +//#define STRIP3 2 +//#define STRIP4 3 +//#define STRIP5 4 +//#define STRIP6 5 +//#define STRIP7 6 +//#define STRIP8 7 + +#define RGB_ORDER RGB + +// define how many LEDs / zones are in each strip +int strips[LED_STRIPS] = {50}; + +// Begin code + +#ifdef DEBUG + #define PRINTFUNC print + #define PRINTLNFUNC println +#else + #define PRINTFUNC + #define PRINTLNFUNC +#endif + +int calculate[LED_STRIPS * 4]; +int universes[LED_STRIPS * 4]; +CRGB ledstrip[MAX_LEDS]; +int pins[8]; + +// Networking + +WebServer httpServer(80); +HTTPUpdateServer httpUpdater; +bool status = 0; +int blankcount = 0; +bool ready = 0; +struct tm timeinfo; +WiFiServer server(8000); +String clientbuffer = ""; +String initinfo = ""; +bool debug = 0; +bool printer = 1; +// Colors (RGB) + +const uint8_t RED[PIXEL_SIZE]= {0x20, 0x00, 0x00}; +const uint8_t ORANGE[PIXEL_SIZE]= {0x20, 0x10, 0x00}; +const uint8_t YELLOW[PIXEL_SIZE]= {0x20, 0x20, 0x00}; +const uint8_t GREEN[PIXEL_SIZE]= {0x00, 0x20, 0x00}; +const uint8_t CYAN[PIXEL_SIZE]= {0x00, 0x20, 0x20}; +const uint8_t BLUE[PIXEL_SIZE]= {0x00, 0x00, 0x20}; +const uint8_t PURPLE[PIXEL_SIZE]= {0x20, 0x00, 0x20}; +const uint8_t BLACK[PIXEL_SIZE]= {0x00, 0x00, 0x00}; +const uint8_t WHITE[PIXEL_SIZE]= {0x20, 0x20, 0x20}; +#define MAX_PIXELS_PER_UNIVERSE 512 / PIXEL_SIZE /* Number of pixels */ +#define CHANNEL_START 1 /* Channel to start listening at */ + + + +E131 e131; + +template T print(T in) { + Serial.print(String(in)); + if(printer) clientbuffer += String(in); + status = 1; + return (T)true; +} + +template T println(T in) { + Serial.println(String(in)); + if(printer) { + clientbuffer += String(in); + clientbuffer += "\n"; + } + status = 1; + return (T)true; +} + +void write_universe(int universe, uint8_t data[]) { + // universe starts at 0 + + PRINTFUNC("Universe: "); + PRINTLNFUNC(universe); + int offset = calculate[universe]; + PRINTFUNC("Offset: "); + PRINTLNFUNC(offset); + int write_size = universes[universe]; + PRINTFUNC("Length: "); + PRINTLNFUNC(write_size); + status = 0; + for (int i = 0; i < write_size; i++) { + int j = i * PIXEL_SIZE + (CHANNEL_START - 1); + if(debug) { + PRINTFUNC(data[j]); + PRINTFUNC(" "); + PRINTFUNC(data[j+1]); + PRINTFUNC(" "); + PRINTFUNC(data[j+2]); + PRINTFUNC(" "); + } + ledstrip[offset + i] = CRGB(data[j], data[j+1], data[j+2]); + //ledstrip[strip].setPixelColor(i + offset, data[j], data[j+1], data[j+2]); + } + status = 1; + //FastLED.show(); + PRINTLNFUNC("Done writing."); + + +} + +void setup() { + pinMode(22, OUTPUT); + digitalWrite(22, LOW); // Enable buffer output! + //pinMode(0, OUTPUT); + //digitalWrite(0, HIGH); + Serial.begin(115200); + delay(3000); + PRINTLNFUNC("========= PicoLighter v1.0 Initializing ========="); + while (ready == 0) { + delay(100); + } + + + // Populate universes and offsets + int offsetcount = 0; + int currentsize = 0; + for (int i = 0; i < LED_STRIPS; i++) { + int tmp = strips[i]; + + PRINTFUNC("Strip "); + PRINTFUNC(i); + PRINTFUNC(", Pin "); + PRINTFUNC(pins[i]); + PRINTFUNC(", Light count "); + PRINTLNFUNC(tmp); + + while(tmp > MAX_PIXELS_PER_UNIVERSE) { + universes[currentsize] = MAX_PIXELS_PER_UNIVERSE; + calculate[currentsize] = offsetcount; + + PRINTFUNC(" Universe "); + PRINTFUNC(currentsize + 1); + PRINTFUNC(", Light count "); + PRINTFUNC(MAX_PIXELS_PER_UNIVERSE); + PRINTFUNC(", Size "); + PRINTLNFUNC(MAX_PIXELS_PER_UNIVERSE * PIXEL_SIZE); + offsetcount += MAX_PIXELS_PER_UNIVERSE; + currentsize += 1; + tmp -= MAX_PIXELS_PER_UNIVERSE; + } + universes[currentsize] = tmp; + calculate[currentsize] = offsetcount; + PRINTFUNC(" Universe "); + PRINTFUNC(currentsize + 1); + PRINTFUNC(", Light count "); + PRINTFUNC(tmp); + PRINTFUNC(", Size "); + PRINTLNFUNC(tmp * PIXEL_SIZE); + offsetcount += tmp; + currentsize += 1; + } + PRINTLNFUNC("========= PicoLighter v1.0 Initialized ========="); + initinfo += clientbuffer; + //e131.beginMulticast(ssid, passphrase, UNIVERSE); + ready = 0; + printer = 0; +} + +void setup1() { + pinMode(LED_BUILTIN, OUTPUT); + pinMode(32+1, OUTPUT); + digitalWrite(LED_BUILTIN, HIGH); + #ifdef STRIP1 + FastLED.addLeds(ledstrip, calculate[0], strips[0]); + pins[0] = STRIP1; + #endif + #ifdef STRIP2 + FastLED.addLeds(ledstrip, calculate[1], strips[1]); + pins[1] = STRIP2; + #endif + #ifdef STRIP3 + FastLED.addLeds(ledstrip, calculate[2], strips[2]); + pins[2] = STRIP3; + #endif + #ifdef STRIP4 + FastLED.addLeds(ledstrip, calculate[3], strips[3]); + pins[3] = STRIP4; + #endif + #ifdef STRIP5 + FastLED.addLeds(ledstrip, calculate[4], strips[4]); + pins[4] = STRIP5; + #endif + #ifdef STRIP6 + FastLED.addLeds(ledstrip, calculate[5], strips[5]); + pins[5] = STRIP6; + #endif + #ifdef STRIP7 + FastLED.addLeds(ledstrip, calculate[6], strips[6]); + pins[6] = STRIP7; + #endif + #ifdef STRIP8 + FastLED.addLeds(ledstrip, calculate[7], strips[7]); + pins[7] = STRIP8; + #endif + // Test all lights + for (int i = 0; i < MAX_LEDS; i++) { + ledstrip[i] = CRGB(130, 130, 130); + FastLED.show(); + delay(30); + ledstrip[i] = CRGB(0, 0, 0); + } + FastLED.show(); + delay(3000); + WiFi.noLowPowerMode(); + if (e131.begin(ssid, password) != WL_CONNECTED) { + PRINTFUNC("Connection failed. Retrying."); + rp2040.reboot(); + } + PRINTLNFUNC("Starting mDNS client."); + MDNS.begin(HOSTNAME); + httpUpdater.setup(&httpServer, update_path, update_username, update_password); + httpServer.begin(); + MDNS.addService("http", "tcp", 80); + PRINTFUNC("OTA Updates enabled. Open http://"); + PRINTFUNC(HOSTNAME); + PRINTFUNC(update_path); + PRINTFUNC(" in your browser and login with username "); + PRINTFUNC(update_username); + PRINTFUNC(" and password "); + PRINTLNFUNC(update_password); + server.begin(); + ready = 1; + while (ready == 1) { + delay(100); + } + // If we get here, then WiFi is good to go + PRINTFUNC("Starting NTP client."); + NTP.begin(ntpserver); + NTP.waitSet([]() { PRINTFUNC("."); }, 15000); + time_t now = time(nullptr); + PRINTLNFUNC(""); + gmtime_r(&now, &timeinfo); + PRINTFUNC("Current time: "); + PRINTFUNC(asctime(&timeinfo)); + + //rp2040.wdt_begin(8000); + +} + + +void loop() { + /* Parse a packet and update pixels */ + if(e131.parsePacket()) { + write_universe(e131.universe - 1, e131.data); + } + else if (blankcount > 1000) { + status = 0; + blankcount = 0; + } else { + blankcount ++; + } +} + +void loop1() { + if(status == 1) { + FastLED.show(); + } + httpServer.handleClient(); + MDNS.update(); + if (millis() % 100 > 50) { // reset LED + digitalWrite(LED_BUILTIN, HIGH); + } + else if (millis() % 100 < 50 && status == 1) { + //status = 0; + digitalWrite(LED_BUILTIN, LOW); + } + //status = 0; + //delay(50); + float temp = analogReadTemp(); + if (temp > 50.0) { + PRINTLNFUNC("ERROR: Overtemperature triggered!"); + rp2040.reboot(); + } + WiFiClient client = server.available(); + if (client) { + PRINTLNFUNC("Client connected"); + clientbuffer = ""; + printer = 1; + client.println(initinfo); + client.println("Press d + ENTER to toggle RGB debug."); + while (client.connected()) { + if(status == 1) { + FastLED.show(); + } + httpServer.handleClient(); + MDNS.update(); + if (millis() % 100 > 50) { // reset LED + digitalWrite(LED_BUILTIN, HIGH); + } + else if (millis() % 100 < 50 && status == 1) { + //status = 0; + digitalWrite(LED_BUILTIN, LOW); + } + //status = 0; + //delay(50); + float temp = analogReadTemp(); // read temp in celsius + if (temp > 50.0) { + PRINTLNFUNC("ERROR: Overtemperature triggered!"); + rp2040.reboot(); + } + if(clientbuffer != (String) "") { + client.print(clientbuffer); + clientbuffer = ""; + } + while(client.available()) { + char c = client.read(); + if (c == 'd') { + if (debug == 1) { + debug = 0; + } else { + debug = 1; + } + } + } + } + client.stop(); + printer = 0; + } +} + diff --git a/src/util.h b/src/util.h new file mode 100644 index 0000000..098f818 --- /dev/null +++ b/src/util.h @@ -0,0 +1,13 @@ +#ifndef UTIL_H_ +#define UTIL_H_ + +#define htons(x) ( ((x) << 8) | (((x) >> 8)&0xFF) ) +#define ntohs(x) htons(x) + +#define htonl(x) ( ((x) << 24 & 0xFF000000UL) | \ + ((x) << 8 & 0x00FF0000UL) | \ + ((x) >> 8 & 0x0000FF00UL) | \ + ((x) >> 24 & 0x000000FFUL) ) +#define ntohl(x) htonl(x) + +#endif /* UTIL_H_ */ \ 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