Initial commit.
This commit is contained in:
commit
8f4ca71f72
21 changed files with 1683 additions and 0 deletions
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
.pio
|
||||
.vscode/.browse.c_cpp.db*
|
||||
.vscode/c_cpp_properties.json
|
||||
.vscode/launch.json
|
||||
.vscode/ipch
|
10
.vscode/extensions.json
vendored
Normal file
10
.vscode/extensions.json
vendored
Normal file
|
@ -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"
|
||||
]
|
||||
}
|
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"C_Cpp.errorSquiggles": "disabled"
|
||||
}
|
1
fw.bin
Symbolic link
1
fw.bin
Symbolic link
|
@ -0,0 +1 @@
|
|||
.pio/build/d1_mini_lite/firmware.bin
|
39
include/README
Normal file
39
include/README
Normal file
|
@ -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
|
46
lib/README
Normal file
46
lib/README
Normal file
|
@ -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 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 <Foo.h>
|
||||
#include <Bar.h>
|
||||
|
||||
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
|
21
platformio.ini
Normal file
21
platformio.ini
Normal file
|
@ -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:d1_mini_lite]
|
||||
platform = espressif8266
|
||||
board = d1_mini_lite
|
||||
framework = arduino
|
||||
monitor_speed = 115200
|
||||
upload_speed = 460800
|
||||
lib_deps =
|
||||
fastled/FastLED@^3.7.1
|
||||
plerup/EspSoftwareSerial@^8.2.0
|
||||
links2004/WebSockets@^2.5.3
|
||||
vshymanskyy/Preferences@^2.0.0
|
0
src/FIS.c
Normal file
0
src/FIS.c
Normal file
15
src/FIS.h
Normal file
15
src/FIS.h
Normal file
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
#ifndef FIS_H
|
||||
#define FIS_H
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <SoftwareSerial.h>
|
||||
|
||||
|
||||
void begin();
|
||||
void writeText(char text[]);
|
||||
|
||||
#include <FIS.c>
|
||||
|
||||
#endif // FIS_H
|
||||
*/
|
56
src/RGBW.h
Normal file
56
src/RGBW.h
Normal file
|
@ -0,0 +1,56 @@
|
|||
/* FastLED_RGBW
|
||||
*
|
||||
* Hack to enable SK6812 RGBW strips to work with FastLED.
|
||||
*
|
||||
* Original code by Jim Bumgardner (http://krazydad.com).
|
||||
* Modified by David Madison (http://partsnotincluded.com).
|
||||
*
|
||||
*/
|
||||
#ifndef FastLED_RGBW_h
|
||||
#define FastLED_RGBW_h
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <FastLED.h>
|
||||
|
||||
struct CRGBW {
|
||||
union {
|
||||
struct {
|
||||
union {
|
||||
uint8_t g;
|
||||
uint8_t green;
|
||||
};
|
||||
union {
|
||||
uint8_t r;
|
||||
uint8_t red;
|
||||
};
|
||||
union {
|
||||
uint8_t b;
|
||||
uint8_t blue;
|
||||
};
|
||||
union {
|
||||
uint8_t w;
|
||||
uint8_t white;
|
||||
};
|
||||
};
|
||||
uint8_t raw[4];
|
||||
};
|
||||
CRGBW(){}
|
||||
CRGBW(uint8_t rd, uint8_t grn, uint8_t blu, uint8_t wht){
|
||||
r = rd;
|
||||
g = grn;
|
||||
b = blu;
|
||||
w = wht;
|
||||
}
|
||||
inline void operator = (const CRGB c) __attribute__((always_inline)){
|
||||
this->r = c.r;
|
||||
this->g = c.g;
|
||||
this->b = c.b;
|
||||
this->white = 0;
|
||||
}
|
||||
};
|
||||
inline uint16_t getRGBWsize(uint16_t nleds){
|
||||
uint16_t nbytes = nleds * 4;
|
||||
if(nbytes % 3 > 0) return nbytes / 3 + 1;
|
||||
else return nbytes / 3;
|
||||
}
|
||||
#endif
|
499
src/Server.cpp
Normal file
499
src/Server.cpp
Normal file
|
@ -0,0 +1,499 @@
|
|||
#include "config.h"
|
||||
#include "main.h"
|
||||
#include "pages.h"
|
||||
|
||||
#include <Updater.h>
|
||||
#include "StoreCSV.h"
|
||||
#include "RGBW.h"
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESP8266WebServer.h>
|
||||
#include <ESP8266mDNS.h>
|
||||
#include <WiFiUdp.h>
|
||||
#include <WebSocketsServer.h>
|
||||
#include <Preferences.h>
|
||||
|
||||
String convert_utf8_to_iso8859_1(String utf8) {
|
||||
String iso8859_1 = "";
|
||||
for (uint i = 0; i < utf8.length(); i++){
|
||||
char c = utf8[i]; // get the character
|
||||
if ((c & 0x80) == 0) { // is it a single-byte character?
|
||||
iso8859_1 += c; // if so, just append it
|
||||
} else if ((c & 0xE0) == 0xC0) { // is it a two-byte sequence?
|
||||
// extract the Unicode codepoint represented by the sequence
|
||||
int codepoint = ((c & 0x1F) << 6) | (utf8[++i] & 0x3F);
|
||||
if (codepoint <= 0xFF) { // is it within the ISO-8859-1 range?
|
||||
iso8859_1 += (char) codepoint; // if so, append the equivalent ISO-8859-1 character
|
||||
}
|
||||
// if not, we simply omit it
|
||||
} else {
|
||||
// it's a more-than-two-byte sequence, which is definitely outside the ISO-8859-1 range,
|
||||
// so we just skip the rest of the sequence
|
||||
if ((c & 0xF0) == 0xE0) i++;
|
||||
else if ((c & 0xF8) == 0xF0) i += 2;
|
||||
// if it's neither, it's a malformed sequence, so we skip it altogether
|
||||
}
|
||||
}
|
||||
return iso8859_1;
|
||||
}
|
||||
|
||||
ESP8266WebServer server(80);
|
||||
WebSocketsServer webSocket(81);
|
||||
|
||||
WiFiUDP UDP;
|
||||
char UDP_packet[90 * 4];
|
||||
|
||||
WiFiUDP UDP2;
|
||||
char UDP2_packet[60];
|
||||
|
||||
String ota_pw;
|
||||
|
||||
bool enableFallbackAP;
|
||||
|
||||
void handleWebClient() {
|
||||
server.handleClient();
|
||||
}
|
||||
|
||||
void handleRoot() {
|
||||
server.send(200, "text/html", displayForm);
|
||||
}
|
||||
|
||||
void tickerPage() {
|
||||
server.send(200, "text/html", tickerForm);
|
||||
}
|
||||
|
||||
void pickerPage() {
|
||||
server.send(200, "text/html", colorPickerIndex);
|
||||
}
|
||||
|
||||
void handleRainbow() {
|
||||
Serial.print("handleRainbow:");
|
||||
if (server.arg("rainbow") == "On") {
|
||||
rainbow = true;
|
||||
Serial.println("On");
|
||||
server.send(200, "text/html", "Turn Rainbow On");
|
||||
} else if (server.arg("rainbow") == "Off") {
|
||||
rainbow = false;
|
||||
Serial.println("Off");
|
||||
server.send(200, "text/html", "Turn Rainbow Off");
|
||||
}
|
||||
server.send(400, "text/html", "Error in handleRainbow, no value found " + server.arg("rainbow"));
|
||||
}
|
||||
|
||||
void handleScroller() {
|
||||
Serial.print("handleScroller:");
|
||||
if (server.arg("scroller") == "On") {
|
||||
scroller = true;
|
||||
Serial.println("On");
|
||||
server.send(200, "text/html", "Turn Scroller On");
|
||||
} else if (server.arg("scroller") == "Off") {
|
||||
scroller = false;
|
||||
Serial.println("Off");
|
||||
server.send(200, "text/html", "Turn Scroller Off");
|
||||
}
|
||||
server.send(400, "text/html", "Error in handeScroller, no value found " + server.arg("scroller"));
|
||||
}
|
||||
|
||||
char lookup_hex(char c) {
|
||||
switch (c & 0b00011111) {
|
||||
case '0':
|
||||
return 0x0;
|
||||
case '1':
|
||||
return 0x1;
|
||||
case '2':
|
||||
return 0x2;
|
||||
case '3':
|
||||
return 0x3;
|
||||
case '4':
|
||||
return 0x4;
|
||||
case '5':
|
||||
return 0x5;
|
||||
case '6':
|
||||
return 0x6;
|
||||
case '7':
|
||||
return 0x7;
|
||||
case '8':
|
||||
return 0x8;
|
||||
case '9':
|
||||
return 0x9;
|
||||
|
||||
case 'a':
|
||||
return 0xA;
|
||||
case 'b':
|
||||
return 0xB;
|
||||
case 'c':
|
||||
return 0xC;
|
||||
case 'd':
|
||||
return 0xD;
|
||||
case 'e':
|
||||
return 0xE;
|
||||
case 'f':
|
||||
return 0xF;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void handleSetPixels() {
|
||||
char hexbuf[720]; // NUM_PIX * bytes * 2
|
||||
|
||||
Serial.print("handleSetPixels:");
|
||||
if (server.hasArg("pix")) {
|
||||
server.arg("pix").toCharArray(hexbuf, 720);
|
||||
|
||||
uint8_t buf[360];
|
||||
uint8_t toggle = 0;
|
||||
for(int i = 0 ; i < 720 ; i++) {
|
||||
buf[i/2] = lookup_hex(hexbuf[i]) << (toggle * 8);
|
||||
|
||||
toggle ^= 1;
|
||||
}
|
||||
|
||||
Serial.println(hexbuf);
|
||||
server.send(200, "text/html", "Color");
|
||||
} else {
|
||||
server.send(400, "text/html", "Invalid request, no text found");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void handleDisplayString() {
|
||||
String message;
|
||||
Serial.print("handleDisplayString:");
|
||||
if (server.hasArg("text")) {
|
||||
message = server.arg("text");
|
||||
|
||||
writeText(message.c_str());
|
||||
|
||||
Serial.println(message);
|
||||
server.send(200, "text/html", "Display: '" + message +"'");
|
||||
} else {
|
||||
server.send(400, "text/html", "Invalid request, no text found");
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<String> ticker_lines;
|
||||
uint32_t ticker_timeout;
|
||||
|
||||
/*
|
||||
void handleTickerEvent() {
|
||||
time_t now;
|
||||
|
||||
if (ticker_lines.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
static unsigned long lastLoopTime = millis();
|
||||
static int idx = 0;
|
||||
if(millis() - lastLoopTime < ticker_timeout) {
|
||||
return;
|
||||
}
|
||||
lastLoopTime = millis();
|
||||
|
||||
if (idx < ticker_lines.size())
|
||||
{
|
||||
SoftSerial.write(0x0A);
|
||||
SoftSerial.write(0x09);
|
||||
SoftSerial.write(0x0D);
|
||||
SoftSerial.print(ticker_lines[idx]);
|
||||
idx++;
|
||||
|
||||
} else {
|
||||
idx = 0;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
void handleOTA() {
|
||||
server.sendHeader("Connection", "close");
|
||||
server.send(200, "text/html", loginIndex);
|
||||
}
|
||||
|
||||
void handleServerIndex() {
|
||||
server.sendHeader("Connection", "close");
|
||||
if(server.arg("k") != ota_pw)
|
||||
server.send(400, "text/html", "invalid credentials.");
|
||||
else
|
||||
server.send(200, "text/html", serverIndex);
|
||||
}
|
||||
|
||||
void handleUpdate() {
|
||||
server.sendHeader("Connection", "close");
|
||||
if(server.arg("k") != ota_pw) {
|
||||
server.send(400, "text/html", "invalid credentials.");
|
||||
} else {
|
||||
server.send(200, "text/html", serverIndex);
|
||||
server.send(200, "text/plain", (Update.hasError()) ? "FAIL" : "OK");
|
||||
ESP.restart();
|
||||
}
|
||||
}
|
||||
|
||||
void handleUpload() {
|
||||
HTTPUpload& upload = server.upload();
|
||||
if (upload.status == UPLOAD_FILE_START) {
|
||||
Serial.printf("Update: %s\n", upload.filename.c_str());
|
||||
if (!Update.begin(upload.contentLength)) {
|
||||
Update.printError(Serial);
|
||||
}
|
||||
} else if (upload.status == UPLOAD_FILE_WRITE) {
|
||||
if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) {
|
||||
Update.printError(Serial);
|
||||
}
|
||||
} else if (upload.status == UPLOAD_FILE_END) {
|
||||
if (Update.end(true)) {
|
||||
Serial.printf("Update Success: %u\nRebooting...\n", upload.totalSize);
|
||||
} else {
|
||||
Update.printError(Serial);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void handleCredentials() {
|
||||
String ssid;
|
||||
String wifi_pw;
|
||||
if (server.hasArg("ssid") && server.hasArg("wifi_pw")) {
|
||||
ssid = server.arg("ssid");
|
||||
wifi_pw = server.arg("wifi_pw");
|
||||
ota_pw = server.arg("ota_pw");
|
||||
|
||||
prefs.begin("wifi", false);
|
||||
prefs.putString("ssid", ssid);
|
||||
prefs.putString("wifi_pw", wifi_pw);
|
||||
prefs.putString("ota_pw", ota_pw);
|
||||
prefs.end();
|
||||
|
||||
server.send(200, "text/html", "Saved credentials!");
|
||||
} else {
|
||||
server.send(400, "text/plain", "Bad Request");
|
||||
}
|
||||
}
|
||||
|
||||
void CSVUploadPage() {
|
||||
server.sendHeader("Connection", "close");
|
||||
server.send(200, "text/html", csvUploadPage);
|
||||
}
|
||||
|
||||
void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length){
|
||||
if(type == WStype_TEXT){
|
||||
String message = String((char*) payload);
|
||||
Serial.print("WS:");
|
||||
Serial.println(message);
|
||||
if (message.startsWith("{RGB}=")) {
|
||||
uint32_t rgb;
|
||||
rgb = strtol(message.substring(7,13).c_str(), 0, 16);
|
||||
fillColor(rgb>>16, rgb >> 8, rgb >> 0);
|
||||
return;
|
||||
}
|
||||
|
||||
Serial.println(message);
|
||||
writeText(message.c_str());
|
||||
} else if(type == WStype_BIN) {
|
||||
for(size_t i = 0 ; i < length ; i+=4 ) {
|
||||
set_pixelw(i/4, CRGBW(payload[i], payload[i+1], payload[i+2], payload[i+3]));
|
||||
}
|
||||
|
||||
show_pixel();
|
||||
}
|
||||
}
|
||||
|
||||
void handleFallbackRoot() {
|
||||
Serial.print("handleFallbackRoot");
|
||||
prefs.begin("wifi", true);
|
||||
String ssid = prefs.getString("ssid", "");
|
||||
String wifi_pw = prefs.getString("wifi_pw", "");
|
||||
String ota_pw = prefs.getString("ota_pw", "");
|
||||
prefs.end();
|
||||
|
||||
char wifiCredentialsPage[1024];
|
||||
sprintf(wifiCredentialsPage, wifiCredentialsTemplate, ssid.c_str(), wifi_pw.c_str(), ota_pw.c_str());
|
||||
|
||||
server.send(200, "text/html", wifiCredentialsPage);
|
||||
}
|
||||
|
||||
void startAPMode() {
|
||||
progress(4, SETUP_PROGRESS_MAX, 255, 255, 0);
|
||||
|
||||
WiFi.disconnect();
|
||||
WiFi.softAPConfig(FALLBACK_IP, GATEWAY_IP, SUBNET_IP);
|
||||
|
||||
add_progress();
|
||||
WiFi.mode(WIFI_AP);
|
||||
WiFi.softAP(FALLBACK_SSID, FALLBACK_PASSWORD); // Using default credentials
|
||||
|
||||
IPAddress myIP = WiFi.softAPIP();
|
||||
Serial.print("AP IP address: ");
|
||||
Serial.println(myIP);
|
||||
|
||||
server.on("/", handleFallbackRoot);
|
||||
server.on("/saveCredentials", HTTP_POST, handleCredentials);
|
||||
|
||||
enableFallbackAP = true;
|
||||
|
||||
server.begin();
|
||||
add_progress();
|
||||
}
|
||||
|
||||
void connectToWiFi() {
|
||||
add_progress();
|
||||
writeText("Connecting To Wifi");
|
||||
esp_delay(1000);
|
||||
|
||||
enableFallbackAP = false;
|
||||
|
||||
// Attempt to connect to Wi-Fi with saved credentials
|
||||
prefs.begin("wifi", true); // Begin preferences in read-only mode
|
||||
String ssid = prefs.getString("ssid", "");
|
||||
const char *cstrssid = ssid.c_str();
|
||||
String wifi_pw = prefs.getString("wifi_pw", "");
|
||||
ota_pw = prefs.getString("ota_pw", "");
|
||||
prefs.end();
|
||||
|
||||
if (ssid != "" && wifi_pw != "") {
|
||||
writeText(cstrssid);
|
||||
|
||||
WiFi.begin(cstrssid, wifi_pw.c_str());
|
||||
|
||||
for(int i = 0 ; i < 5 ; i++ ) {
|
||||
if(WiFi.isConnected()) {
|
||||
break;
|
||||
}
|
||||
|
||||
esp_delay(500);
|
||||
|
||||
add_progress();
|
||||
show_progress(); // hopefully fixes some shit
|
||||
}
|
||||
|
||||
if (WiFi.waitForConnectResult() != WL_CONNECTED) {
|
||||
Serial.println("Failed to connect to Wi-Fi with saved credentials. Starting AP mode.");
|
||||
enableFallbackAP = true;
|
||||
|
||||
writeText("invalid credentials");
|
||||
esp_delay(5000);
|
||||
|
||||
startAPMode();
|
||||
}
|
||||
} else {
|
||||
Serial.println("No saved credentials found. Starting AP mode.");
|
||||
enableFallbackAP = true;
|
||||
|
||||
fillColor(255, 0, 0);
|
||||
writeText("no credentails");
|
||||
esp_delay(5000);
|
||||
|
||||
startAPMode();
|
||||
}
|
||||
|
||||
esp_delay(1000);
|
||||
|
||||
if(enableFallbackAP) {
|
||||
esp_delay(1000);
|
||||
}
|
||||
|
||||
set_progress(6, SETUP_PROGRESS_MAX);
|
||||
|
||||
IPAddress myIP = enableFallbackAP ? FALLBACK_IP : WiFi.localIP();
|
||||
String ip_adress_s = myIP.toString();
|
||||
|
||||
Serial.print("\nConnected to ");
|
||||
Serial.println(ssid);
|
||||
Serial.print("IP address: ");
|
||||
Serial.println(ip_adress_s.c_str());
|
||||
|
||||
/*use mdns for host name resolution*/
|
||||
if (!MDNS.begin(HOST))
|
||||
Serial.println("Error setting up MDNS responder, MDNS will not be used");
|
||||
else
|
||||
Serial.println("mDNS responder started");
|
||||
|
||||
add_progress();
|
||||
|
||||
#define CONNECTED "Connected:"
|
||||
#define FALLBACK "Fallback: "
|
||||
|
||||
if(enableFallbackAP) {
|
||||
writeText(FALLBACK FALLBACK_SSID);
|
||||
} else {
|
||||
char buf[strlen(CONNECTED) + strlen(cstrssid)] = CONNECTED;
|
||||
strcat(buf, cstrssid);
|
||||
|
||||
writeText(buf);
|
||||
}
|
||||
esp_delay(2000);
|
||||
const char* cstrip = ip_adress_s.c_str();
|
||||
|
||||
if(enableFallbackAP) {
|
||||
char buf[strlen(FALLBACK) + strlen(cstrip)] = FALLBACK;
|
||||
strcat(buf, cstrip);
|
||||
|
||||
writeText(buf);
|
||||
} else {
|
||||
char buf[strlen(CONNECTED) + strlen(cstrip)] = CONNECTED;
|
||||
strcat(buf, cstrip);
|
||||
|
||||
writeText(buf);
|
||||
}
|
||||
add_progress();
|
||||
|
||||
esp_delay(2000);
|
||||
|
||||
if(enableFallbackAP) {
|
||||
writeText(FALLBACK FALLBACK_SSID);
|
||||
}
|
||||
}
|
||||
|
||||
void setupWebServer() {
|
||||
server.on("/", handleRoot);
|
||||
server.on("/ticker", HTTP_GET, tickerPage);
|
||||
server.on("/pickerForm", HTTP_GET, pickerPage);
|
||||
// server.on("/tickerForm", HTTP_POST, handleTicker);
|
||||
server.on("/Scroller", HTTP_POST, handleScroller);
|
||||
server.on("/Rainbow", HTTP_POST, handleRainbow);
|
||||
server.on("/StringForm", HTTP_POST, handleDisplayString);
|
||||
server.on("/SetPixels", HTTP_POST, handleSetPixels);
|
||||
server.on("/OTA", HTTP_GET, handleOTA);
|
||||
server.on("/serverIndex", HTTP_GET, handleServerIndex);
|
||||
server.on("/uploadCSV", HTTP_GET, CSVUploadPage);
|
||||
server.on("/uploadCSV", HTTP_POST, []() {
|
||||
server.send(200, "text/plain", "CSV Uploaded Successfully");
|
||||
}, handleCSVUpload);
|
||||
|
||||
server.on("/update", HTTP_POST, []() {
|
||||
handleUpdate();
|
||||
}, handleUpload);
|
||||
server.begin();
|
||||
|
||||
UDP.begin(UDP_PORT);
|
||||
UDP2.begin(UDP_PORT + 1);
|
||||
Serial.print("Listening on UDP port ");
|
||||
Serial.println(UDP_PORT);
|
||||
|
||||
webSocket.onEvent(webSocketEvent);
|
||||
webSocket.begin();
|
||||
|
||||
}
|
||||
|
||||
void handleUDP() {
|
||||
int packetSize = UDP.parsePacket();
|
||||
if (packetSize) {
|
||||
Serial.print("Received packet! Size: ");
|
||||
Serial.println(packetSize);
|
||||
int len = UDP.read(UDP_packet, 90 * 4);
|
||||
|
||||
for(int i = 0 ; i < len ; i += 4 )
|
||||
set_pixelw(i/4, CRGBW(UDP_packet[i],UDP_packet[i+1],UDP_packet[i+2],UDP_packet[i+3]));
|
||||
|
||||
show_pixel();
|
||||
}
|
||||
|
||||
packetSize = UDP2.parsePacket();
|
||||
if (packetSize) {
|
||||
Serial.print("Received packet2! ize: ");
|
||||
Serial.println(packetSize);
|
||||
int len = UDP2.read(UDP2_packet, 60);
|
||||
|
||||
UDP2_packet[len] = '\0';
|
||||
|
||||
Serial.println("Lol nein");
|
||||
writeText(UDP2_packet);
|
||||
}
|
||||
}
|
25
src/Server.h
Normal file
25
src/Server.h
Normal file
|
@ -0,0 +1,25 @@
|
|||
// SERVER.h
|
||||
#ifndef SERVER_H
|
||||
#define SERVER_H
|
||||
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESP8266WebServer.h>
|
||||
#include <WiFiClientSecure.h> // websocket needs and doesnt include itself :/
|
||||
#include <ESP8266mDNS.h>
|
||||
#include <Updater.h>
|
||||
#include <WebSocketsServer.h>
|
||||
|
||||
// TODO: #include <Update.h>
|
||||
|
||||
void connectToWiFi();
|
||||
void setupWebServer();
|
||||
void handleWebClient();
|
||||
void handleTickerEvent();
|
||||
void handleUDP();
|
||||
|
||||
extern ESP8266WebServer server;
|
||||
extern WebSocketsServer webSocket;
|
||||
|
||||
extern bool enableFallbackAP;
|
||||
|
||||
#endif // SERVER_H
|
132
src/StoreCSV.cpp
Normal file
132
src/StoreCSV.cpp
Normal file
|
@ -0,0 +1,132 @@
|
|||
#include "StoreCSV.h"
|
||||
#include "Server.h"
|
||||
|
||||
std::vector<DataEntry> entries;
|
||||
int current_index;
|
||||
|
||||
void handleCSVUpload() {
|
||||
HTTPUpload& upload = server.upload();
|
||||
static File file;
|
||||
|
||||
if (upload.status == UPLOAD_FILE_START) {
|
||||
Serial.printf("Upload Start: %s\n", upload.filename.c_str());
|
||||
|
||||
// Initialize LittleFS
|
||||
if (!LittleFS.begin()) {
|
||||
Serial.println("An Error has occurred while mounting LittleFS. Formatting...");
|
||||
if (LittleFS.format()) {
|
||||
Serial.println("Format successful. Please try again.");
|
||||
} else {
|
||||
Serial.println("Format failed. Please check your hardware or partition scheme.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Open the file on LittleFS
|
||||
file = LittleFS.open("/uploaded.csv", "w");
|
||||
|
||||
if (!file) {
|
||||
Serial.println("Error opening file for writing");
|
||||
return;
|
||||
}
|
||||
} else if (upload.status == UPLOAD_FILE_WRITE) {
|
||||
Serial.printf("Upload Data: %s\n", upload.filename.c_str());
|
||||
|
||||
// Write the current chunk of data to the file
|
||||
file.write(upload.buf, upload.currentSize);
|
||||
} else if (upload.status == UPLOAD_FILE_END) {
|
||||
Serial.printf("Upload End: %s, %u B\n", upload.filename.c_str(), upload.totalSize);
|
||||
|
||||
// Close the file
|
||||
file.close();
|
||||
|
||||
ESP.restart();
|
||||
}
|
||||
}
|
||||
|
||||
void readCSVfromFS() {
|
||||
// Initialize LittleFS
|
||||
if (!LittleFS.begin()) {
|
||||
Serial.println("An Error has occurred while mounting LittleFS");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the file exists in the filesystem
|
||||
if (!LittleFS.exists("/uploaded.csv")) {
|
||||
Serial.println("No CSV file found. Returning.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Open file for reading
|
||||
File file = LittleFS.open("/uploaded.csv", "r");
|
||||
|
||||
if (!file) {
|
||||
Serial.println("Failed to open CSV file for reading.");
|
||||
return;
|
||||
}
|
||||
|
||||
Serial.println("Reading CSV file...");
|
||||
|
||||
// Clear previous entries
|
||||
entries.clear();
|
||||
|
||||
// Read line by line
|
||||
bool isHeader = true;
|
||||
while (file.available()) {
|
||||
String line = file.readStringUntil('\n');
|
||||
line.trim(); // remove potential CR/LF
|
||||
|
||||
// Ignore the header row
|
||||
if (isHeader) {
|
||||
isHeader = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Split line
|
||||
int col1pos = line.indexOf(";");
|
||||
int col2pos = line.indexOf(";", col1pos + 1);
|
||||
int col3pos = line.indexOf(";", col2pos + 1);
|
||||
int col4pos = line.indexOf(";", col3pos + 1);
|
||||
|
||||
if (col3pos != -1) {
|
||||
String dateStr = line.substring(0, col1pos);
|
||||
String timeStr = line.substring(col1pos + 1, col2pos);
|
||||
String name = line.substring(col3pos + 1);
|
||||
if (col4pos > col3pos)
|
||||
name = line.substring(col3pos + 1, col4pos);
|
||||
|
||||
struct tm timeinfo;
|
||||
strptime(dateStr.c_str(), "%Y-%m-%d ", &timeinfo);
|
||||
int hour = timeStr.substring(0,2).toInt();
|
||||
int min = timeStr.substring(2).toInt();
|
||||
|
||||
timeinfo.tm_hour = hour;
|
||||
timeinfo.tm_min = min;
|
||||
timeinfo.tm_sec = 0;
|
||||
time_t timestamp = mktime(&timeinfo);
|
||||
|
||||
entries.push_back(DataEntry(timestamp, name.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
// Dump entries to Serial
|
||||
for (const auto& entry : entries) {
|
||||
struct tm *tm_info = localtime(&entry.timestamp);
|
||||
char buffer[13];
|
||||
strftime(buffer, sizeof(buffer), "%d.%m. %H:%M", tm_info);
|
||||
Serial.printf("%s (%lld): %s\n",
|
||||
buffer, entry.timestamp, entry.name.c_str());
|
||||
}
|
||||
|
||||
file.close();
|
||||
|
||||
current_index = 0;
|
||||
time_t next_event_ts = 0;
|
||||
time_t now;
|
||||
time(&now);
|
||||
while (entries[current_index].timestamp < now) {
|
||||
current_index++;
|
||||
}
|
||||
Serial.printf("Current TS: %lld, Current index: %i, next event @ %lld: %s\n",
|
||||
now, current_index, entries[current_index].timestamp, entries[current_index].name.c_str());
|
||||
}
|
23
src/StoreCSV.h
Normal file
23
src/StoreCSV.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
// StoreCSV.h
|
||||
#ifndef STORE_CSV_H
|
||||
#define STORE_CSV_H
|
||||
|
||||
#include <LittleFS.h>
|
||||
#include <vector>
|
||||
#include <sstream>
|
||||
#include "main.h"
|
||||
|
||||
struct DataEntry {
|
||||
time_t timestamp;
|
||||
std::string name;
|
||||
|
||||
DataEntry(time_t t, std::string n): timestamp(t), name(n) {}
|
||||
};
|
||||
|
||||
extern std::vector<DataEntry> entries;
|
||||
extern int current_index;
|
||||
|
||||
void handleCSVUpload();
|
||||
void readCSVfromFS();
|
||||
|
||||
#endif
|
100
src/config.c
Normal file
100
src/config.c
Normal file
|
@ -0,0 +1,100 @@
|
|||
#include <config.h>
|
||||
#include <Arduino.h>
|
||||
|
||||
char *lines[] = {
|
||||
"Gay Space Piracy",
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAA",
|
||||
"AAAAAAAAAAAAAAAAAAAAAARM",
|
||||
"Temperatur: zu warm",
|
||||
"circle line: barking",
|
||||
"u fuckin' wat m8?",
|
||||
"Zug h{{{{{{{{{{lt",
|
||||
"F1nn5sterLIVE!!1!",
|
||||
"a bit shit - innit?",
|
||||
"uff", // 10
|
||||
"Toilette Besetzt",
|
||||
"fucks shit",
|
||||
"shits fucked",
|
||||
"u fuckin' twat!",
|
||||
"Schaffenburg e.V.",
|
||||
"treffen - schaffen - teilen",
|
||||
"AKPAC n.e.V.",
|
||||
"AB\\TEK n.e.V",
|
||||
"leagilize awoo",
|
||||
"heute auf gleis dr|lf", // 20
|
||||
"be gay do crime",
|
||||
"be trans be crime",
|
||||
"do gay be crime",
|
||||
"do trans be crime",
|
||||
"be trans do crime", // 25
|
||||
"TRAAAAAAAAAAAAAAAAAAAANS",
|
||||
"Unpolitischer Verein",
|
||||
"werden sie jetzt Mitglied!",
|
||||
"No awoo - $350 fine",
|
||||
"comfii :3",
|
||||
"Hermit Kingdom", // 30
|
||||
"Subscribe To Technoblade!",
|
||||
"Technoblade Never Dies!",
|
||||
"TRAAAAAAAAAAAAAAAAAAAIINS",
|
||||
"Join the Querrdom!",
|
||||
"Join Ab\\TEK!", // 35
|
||||
"Join AKPAC!",
|
||||
"Toiletten Besetzen!", // plural oder so
|
||||
"Enjoyer or Consumer?",
|
||||
"be gay do gay or something",
|
||||
"Explodierende Ratte",
|
||||
"Air Compressor Failure Day",
|
||||
"birds arent real",
|
||||
// "the gayer the furry, the more a threat to national security they are - SiegedSec,
|
||||
"daily noodles UwU",
|
||||
"wear your safety cat ears!",
|
||||
"All Cats Are Beautiful",
|
||||
"Schaffenburg e.V. AD DS",
|
||||
"Mind the Blahaj quota!",
|
||||
"Do not feed the Google",
|
||||
// "110010100 days without an accident,
|
||||
"LED the planet",
|
||||
"DECT the planet",
|
||||
"I use arch btw",
|
||||
"Temple OS, the third temple",
|
||||
"Ich nutz Bogen bei dem Weg",
|
||||
// "Everybody loves Systemd...",
|
||||
// "Plug in EVERY USB u can on wifi",
|
||||
// "SAMSUNG smart grenade AI",
|
||||
// "Apple in garden of eden",
|
||||
// "AI fuck off",
|
||||
// "Parallel universes everywhere",
|
||||
// "We need more tops",
|
||||
// "Gift us irl catgirls",
|
||||
// "We need more flummox :3",
|
||||
"Otter, Otter! Space",
|
||||
// "Satzzeichen = kein Rudeltier",
|
||||
// "Our labelprinter our propaganda",
|
||||
// "We need more flags UwU",
|
||||
"UwUntu OwO OwO :33",
|
||||
// "ADHS antifa was here",
|
||||
// "We need only fans to finance this shit",
|
||||
"Taste the rainbow",
|
||||
// "Methylphenidat, Methylphenidat",
|
||||
// "Only max 5 furries per flight",
|
||||
// "I identify as an apache",
|
||||
// "nginx / docker ?"
|
||||
// "It's the V that kills, not A",
|
||||
// "Bondage after midnight (perhaps)",
|
||||
"MEEEOOOOWWW :33",
|
||||
// "oooo u like kissing boys",
|
||||
"powered by hamter",
|
||||
"this sign is gay",
|
||||
// "a cat has programmed this",
|
||||
// "a fox has programmed this",
|
||||
// "a fox has written over 60 of this",
|
||||
// "take your HRT",
|
||||
// "Linux install: 1. Sticker",
|
||||
// "Ukraine needs more tops",
|
||||
// "this escalated quickly... again",
|
||||
"rm -rf / --no-preserve-root",
|
||||
"::1 sweet ::1",
|
||||
};
|
||||
|
||||
const size_t num_lines = sizeof(lines)/sizeof(*lines);
|
||||
//*/
|
28
src/config.h
Normal file
28
src/config.h
Normal file
|
@ -0,0 +1,28 @@
|
|||
#ifndef CONFIG_H
|
||||
#define CONFIG_H
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#define lenlines 38
|
||||
|
||||
extern char *lines[];
|
||||
extern const size_t num_lines;
|
||||
|
||||
#define NUM_LEDS 90
|
||||
#define DATA_PIN D4
|
||||
|
||||
// HSV Modifier between leds
|
||||
#define HSVM 10
|
||||
|
||||
#define FALLBACK_SSID "zugdingens"
|
||||
#define FALLBACK_PASSWORD "immernochbesseralspeter"
|
||||
|
||||
#define HOST "zugdingens"
|
||||
|
||||
#define FALLBACK_IP IPAddress(10, 0, 0, 254)
|
||||
#define GATEWAY_IP IPAddress(10, 0, 0, 254)
|
||||
#define SUBNET_IP IPAddress(255, 255, 255, 0)
|
||||
|
||||
#define UDP_PORT 4210
|
||||
|
||||
#endif // CONFIG_H
|
141
src/index.h
Normal file
141
src/index.h
Normal file
|
@ -0,0 +1,141 @@
|
|||
/*
|
||||
* This ESP8266 NodeMCU code was developed by newbiely.com
|
||||
*
|
||||
* This ESP8266 NodeMCU code is made available for public use without any restriction
|
||||
*
|
||||
* For comprehensive instructions and wiring diagrams, please visit:
|
||||
* https://newbiely.com/tutorials/esp8266/esp8266-websocket
|
||||
*/
|
||||
|
||||
const char *HTML_CONTENT = R"=====(
|
||||
<!DOCTYPE html>
|
||||
<!-- saved from url=(0019)http://192.168.0.2/ -->
|
||||
<html><head><meta http-equiv="Content-Type" content="text/html; charset=windows-1252">
|
||||
<title>ESP8266 WebSocket</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=0.7">
|
||||
<link rel="icon" href="https://diyables.io/images/page/diyables.svg">
|
||||
<style>
|
||||
/* Add some basic styling for the chat window */
|
||||
body {
|
||||
font-size: 16px;
|
||||
}
|
||||
.chat-container {
|
||||
width: 400px;
|
||||
margin: 0 auto;
|
||||
padding: 10px;
|
||||
}
|
||||
.chat-messages {
|
||||
height: 250px;
|
||||
overflow-y: auto;
|
||||
border: 1px solid #444;
|
||||
padding: 5px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.user-input {
|
||||
display: flex;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.user-input input {
|
||||
flex: 1;
|
||||
border: 1px solid #444;
|
||||
padding: 5px;
|
||||
}
|
||||
.user-input button {
|
||||
margin-left: 5px;
|
||||
background-color: #007bff;
|
||||
color: #fff;
|
||||
border: none;
|
||||
padding: 5px 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.websocket {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.websocket button {
|
||||
background-color: #007bff;
|
||||
color: #fff;
|
||||
border: none;
|
||||
padding: 5px 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.websocket .label {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
</style>
|
||||
<script>
|
||||
var ws;
|
||||
var wsm_max_len = 4096; /* bigger length causes uart0 buffer overflow with low speed smart device */
|
||||
|
||||
function update_text(text) {
|
||||
var chat_messages = document.getElementById("chat-messages");
|
||||
chat_messages.innerHTML += text + '<br>';
|
||||
chat_messages.scrollTop = chat_messages.scrollHeight;
|
||||
}
|
||||
|
||||
function send_onclick() {
|
||||
if(ws != null) {
|
||||
var message = document.getElementById("message").value;
|
||||
|
||||
if (message) {
|
||||
document.getElementById("message").value = "";
|
||||
ws.send(message + "\n");
|
||||
update_text('<span style="color:navy">' + message + '</span>');
|
||||
// You can send the message to the server or process it as needed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function connect_onclick() {
|
||||
if(ws == null) {
|
||||
ws = new WebSocket("ws://" + window.location.host + ":81");
|
||||
document.getElementById("ws_state").innerHTML = "CONNECTING";
|
||||
ws.onopen = ws_onopen;
|
||||
ws.onclose = ws_onclose;
|
||||
ws.onmessage = ws_onmessage;
|
||||
} else
|
||||
ws.close();
|
||||
}
|
||||
|
||||
function ws_onopen() {
|
||||
document.getElementById("ws_state").innerHTML = "<span style='color:blue'>CONNECTED</span>";
|
||||
document.getElementById("bt_connect").innerHTML = "Disconnect";
|
||||
document.getElementById("chat-messages").innerHTML = "";
|
||||
}
|
||||
|
||||
function ws_onclose() {
|
||||
document.getElementById("ws_state").innerHTML = "<span style='color:gray'>CLOSED</span>";
|
||||
document.getElementById("bt_connect").innerHTML = "Connect";
|
||||
ws.onopen = null;
|
||||
ws.onclose = null;
|
||||
ws.onmessage = null;
|
||||
ws = null;
|
||||
}
|
||||
|
||||
function ws_onmessage(e_msg) {
|
||||
e_msg = e_msg || window.event; // MessageEvent
|
||||
console.log(e_msg.data);
|
||||
update_text('<span style="color:blue">' + e_msg.data + '</span>');
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="chat-container">
|
||||
<h2>ESP8266 WebSocket</h2>
|
||||
<div class="websocket">
|
||||
<button class="connect-button" id="bt_connect" onclick="connect_onclick()">Connect</button>
|
||||
<span class="label">WebSocket: <span id="ws_state"><span style="color:blue">CLOSED</span></span></span>
|
||||
</div>
|
||||
<div class="chat-messages" id="chat-messages"></div>
|
||||
<div class="user-input">
|
||||
<input type="text" id="message" placeholder="Type your message...">
|
||||
<button onclick="send_onclick()">Send</button>
|
||||
</div>
|
||||
|
||||
<div class="sponsor">Sponsored by <a href="https://amazon.com/diyables">DIYables</a></div>
|
||||
</div>
|
||||
|
||||
</body></html>
|
||||
)=====";
|
291
src/main.cpp
Normal file
291
src/main.cpp
Normal file
|
@ -0,0 +1,291 @@
|
|||
#include "main.h"
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <FastLED.h>
|
||||
#include <RGBW.h>
|
||||
#include <SoftwareSerial.h>
|
||||
|
||||
#include <config.h>
|
||||
#include <Server.h>
|
||||
|
||||
#include <Preferences.h>
|
||||
Preferences prefs = Preferences();
|
||||
|
||||
SoftwareSerial fis(D2, D1, false);
|
||||
#include <FIS.c>
|
||||
|
||||
// toggle for the build-in text scroller
|
||||
bool scroller = true;
|
||||
bool rainbow = true;
|
||||
|
||||
CRGBW leds[NUM_LEDS];
|
||||
CRGB *ledsRGB = (CRGB *) &leds[0];
|
||||
|
||||
void sleep(uint mils) {
|
||||
Serial.print("sleep");
|
||||
Serial.println(mils);
|
||||
|
||||
uint end = millis() + mils;
|
||||
|
||||
while(end > millis()) {
|
||||
delay(10);
|
||||
}
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
// databits 5 + (x & 07) -> x = 2
|
||||
// docu would be nice
|
||||
// 7 bits
|
||||
// even parity
|
||||
// 2 stopbits?
|
||||
|
||||
Serial.println("FastLED");
|
||||
FastLED.addLeds<WS2812B, DATA_PIN, RGB>(ledsRGB, getRGBWsize(NUM_LEDS));
|
||||
FastLED.setBrightness(255);
|
||||
|
||||
fis.begin(1200, SWSERIAL_7E2);
|
||||
|
||||
fillColorW(255, 255, 255, 127);
|
||||
FastLED.show();
|
||||
|
||||
sleep(100);
|
||||
|
||||
writeText("booting, hi :3");
|
||||
Serial.println("boot!");
|
||||
|
||||
sleep(200);
|
||||
|
||||
progress(0, SETUP_PROGRESS_MAX, 0, 254, 0);
|
||||
|
||||
// wifi stuff
|
||||
connectToWiFi();
|
||||
|
||||
setupWebServer();
|
||||
}
|
||||
|
||||
time_t next = 0;
|
||||
time_t now;
|
||||
uint counter = 0;
|
||||
uint8_t hue;
|
||||
|
||||
int analogVal = 0;
|
||||
|
||||
void loop() {
|
||||
handleUDP();
|
||||
|
||||
handleWebClient();
|
||||
if (enableFallbackAP) {
|
||||
return;
|
||||
}
|
||||
|
||||
webSocket.loop();
|
||||
|
||||
now = millis();
|
||||
|
||||
if ( now > next) {
|
||||
next = now + 5;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
hue = uint8_t(counter);
|
||||
|
||||
// rainbow right to left
|
||||
if(rainbow) {
|
||||
for(int i = 0 ; i < NUM_LEDS/2 ; i++ ) {
|
||||
uint8_t chur = hue + ( HSVM * i );
|
||||
|
||||
leds[i] = CHSV(chur, 255, 255);
|
||||
leds[NUM_LEDS-i] = CHSV(chur, 255, 255);
|
||||
} //*/
|
||||
|
||||
FastLED.show();
|
||||
}
|
||||
// rainbow center out btoked
|
||||
/*
|
||||
int li;
|
||||
CRGB c;
|
||||
for(int i = 0 ; i < NUM_LEDS/4 ; i++ ) {
|
||||
uint8_t chur = hue + ( HSVM * i );
|
||||
li = NUM_LEDS/4 - li;
|
||||
c = CHSV(chur, 255, 255);
|
||||
|
||||
leds[li] = c;
|
||||
leds[NUM_LEDS/2 - li] = c;
|
||||
|
||||
leds[NUM_LEDS/2 + li] = c;
|
||||
leds[NUM_LEDS-li] = c;
|
||||
}
|
||||
*/
|
||||
// loop di loop
|
||||
/*
|
||||
for(int i = 0 ; i < NUM_LEDS ; i++ )
|
||||
leds[i] = CRGBW(0,0,0,255);
|
||||
|
||||
for(int i = counter%NUM_LEDS ; i < counter%NUM_LEDS + 7 ; i++ ) {
|
||||
leds[i%NUM_LEDS] = CHSV(hue, 255,255);
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
fill_stripes(CRGB(91, 206, 250), CRGB(245, 169, 184), CRGB(255, 255, 255),
|
||||
CRGB(245, 169, 184), CRGB(91, 206, 250));
|
||||
*/
|
||||
|
||||
// 1000ms / 5ms -> 10s
|
||||
if (scroller && ((counter % (10000/5)) == 0)) {
|
||||
char *a = lines[random(0, num_lines)];
|
||||
|
||||
Serial.print("@");
|
||||
Serial.print(counter);
|
||||
Serial.print(">");
|
||||
Serial.println(a);
|
||||
|
||||
writeText(a);
|
||||
}
|
||||
|
||||
counter ++;
|
||||
}
|
||||
|
||||
void writeText(const char text[]) {
|
||||
// Example string to be sent
|
||||
// header + terminator + checksum
|
||||
int strl = strlen(text);
|
||||
|
||||
int len_t = 1 + 1 + 1;
|
||||
len_t += 16; // at least for padding thingy
|
||||
|
||||
if ( strl > 16 )
|
||||
len_t += strl - 16;
|
||||
|
||||
// Create a buffer with a size of 16 bytes
|
||||
char b[len_t];
|
||||
|
||||
// Append 'v' to the beginning
|
||||
b[0] = 'v';
|
||||
|
||||
// Append the string t to the buffer
|
||||
memcpy(b+1, text, len_t - 1);
|
||||
|
||||
// Fill the remaining buffer with spaces (0x20)
|
||||
// from after start til after string before checksum
|
||||
for (int i = 1 + strl; i < len_t-2; i++) {
|
||||
b[i] = 0x20;
|
||||
}
|
||||
|
||||
b[len_t-2] = 0x0D; // trailer
|
||||
|
||||
char checksum = 0x7F;
|
||||
|
||||
// except checksum itself lel
|
||||
for ( int i = 0 ; i < len_t-1 ; i++ )
|
||||
checksum ^= b[i];
|
||||
|
||||
b[len_t-1] = checksum; // checkum
|
||||
|
||||
// Send the buffer over SoftwareSerial
|
||||
fis.write(b, len_t);
|
||||
//for ( uint8 i = 0 ; i < len_t ; i++ )
|
||||
// Serial.printf("d %d %x %c\n", i, b[i], b[i]);
|
||||
}
|
||||
|
||||
/*
|
||||
=======
|
||||
------- Fast LED stuff
|
||||
=======
|
||||
*/
|
||||
|
||||
void fill_stripes(CRGB a, CRGB b, CRGB c, CRGB d, CRGB e) {
|
||||
#define stripewidth (NUM_LEDS / 5 / 2)
|
||||
|
||||
#define STRIPE(I, VARIABLE) do { \
|
||||
fillColorFT(stripewidth * I, stripewidth * (I+1), VARIABLE);\
|
||||
fillColorFT(NUM_LEDS - (stripewidth * (I+1)), NUM_LEDS - (stripewidth * I), VARIABLE);\
|
||||
} while(0);
|
||||
|
||||
STRIPE(0, a)
|
||||
STRIPE(1, b)
|
||||
STRIPE(2, c)
|
||||
STRIPE(3, d)
|
||||
STRIPE(4, e)
|
||||
}
|
||||
|
||||
void set_pixelw(int i, CRGBW color) {
|
||||
leds[i] = color;
|
||||
}
|
||||
|
||||
void show_pixel() {
|
||||
FastLED.show();
|
||||
}
|
||||
|
||||
void fillColorFT(int from, int to, CRGB color) {
|
||||
for ( int i = from ; i < to ; i++ )
|
||||
leds[i] = color;
|
||||
}
|
||||
|
||||
void fillColorW(uint8_t r, uint8_t g, uint8_t b, uint8_t w) {
|
||||
for ( int i = 0 ; i < NUM_LEDS ; i++ )
|
||||
leds[i] = CRGBW(r, g, b, w);
|
||||
|
||||
FastLED.show();
|
||||
}
|
||||
|
||||
void fillColor(uint8_t r, uint8_t g, uint8_t b) {
|
||||
for(int i = 0 ; i < NUM_LEDS ; i++ )
|
||||
leds[i] = CRGB(r,g,b);
|
||||
|
||||
FastLED.show();
|
||||
}
|
||||
|
||||
uint8_t progress_status = 0;
|
||||
uint8_t progress_total = 1;
|
||||
uint8_t progress_r = 0;
|
||||
uint8_t progress_g = 0;
|
||||
uint8_t progress_b = 0;
|
||||
|
||||
#define PROGRESS_BACKGROUND CRGBW(0,0,0,254);
|
||||
|
||||
// sets progress thingy
|
||||
void progress_color(uint8 r, uint8 g, uint8 b) {
|
||||
progress_r = r; progress_g = g; progress_b = b;
|
||||
}
|
||||
|
||||
void add_progress() {
|
||||
progress(progress_status+1, progress_total,
|
||||
progress_r, progress_g, progress_b);
|
||||
}
|
||||
|
||||
void set_progress(uint8 x, uint8 of) {
|
||||
progress(x, of, progress_r, progress_g, progress_b);
|
||||
}
|
||||
|
||||
void show_progress() {
|
||||
progress(progress_status, progress_total,
|
||||
progress_r, progress_g, progress_b);
|
||||
}
|
||||
|
||||
void progress(uint8 progress, uint8 total, uint8 r, uint8 g, uint8 b) {
|
||||
Serial.print(progress);
|
||||
Serial.print("/");
|
||||
Serial.println(total);
|
||||
|
||||
progress_status = progress;
|
||||
progress_total = total;
|
||||
|
||||
progress_r = r; progress_g = g; progress_b = b;
|
||||
|
||||
CRGB c = CRGB(r, g, b);
|
||||
uint8_t lastled = progress * NUM_LEDS/2 / total;
|
||||
|
||||
for(int i = 0 ; i < NUM_LEDS ; i++ )
|
||||
leds[i] = PROGRESS_BACKGROUND;
|
||||
|
||||
for(int i = 0 ; i < lastled ; i++ ) {
|
||||
leds[i] = c;
|
||||
leds[NUM_LEDS - i] = c;
|
||||
}
|
||||
|
||||
FastLED.show();
|
||||
}
|
30
src/main.h
Normal file
30
src/main.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
#include <Arduino.h>
|
||||
|
||||
#ifndef MAIN_H
|
||||
#define MAIN_H
|
||||
|
||||
#define SETUP_PROGRESS_MAX 8
|
||||
|
||||
#include <Preferences.h>
|
||||
extern Preferences prefs;
|
||||
extern bool scroller;
|
||||
extern bool rainbow;
|
||||
|
||||
#include <FastLED.h>
|
||||
#include <RGBW.h>
|
||||
|
||||
void writeText(const char[]);
|
||||
void fillColorW(uint8_t r, uint8_t g, uint8_t b, uint8_t w);
|
||||
void fillColor(uint8_t r, uint8_t g, uint8_t b);
|
||||
|
||||
void progress(uint8 progress, uint8 total, uint8 r, uint8 g, uint8 b);
|
||||
void add_progress();
|
||||
void progress_color(uint8 r, uint8 g, uint8 b);
|
||||
void set_progress(uint8 x, uint8 of);
|
||||
void show_progress();
|
||||
void fill_stripes(CRGB, CRGB, CRGB, CRGB, CRGB);
|
||||
void fillColorFT(int from, int to, CRGB color);
|
||||
void set_pixelw(int p, CRGBW);
|
||||
void show_pixel();
|
||||
|
||||
#endif // MAIN_H
|
207
src/pages.h
Normal file
207
src/pages.h
Normal file
|
@ -0,0 +1,207 @@
|
|||
// Pages.h
|
||||
#ifndef PAGES_H
|
||||
#define PAGES_H
|
||||
|
||||
/*
|
||||
* Form to display strings page
|
||||
*/
|
||||
const char* displayForm =
|
||||
"<form id='StringForm' method='post'>"
|
||||
"<textarea name='text' rows='2' cols='40'></textarea><br>"
|
||||
"<input type='submit' value='Senden'> "
|
||||
"<label for='liveMode'>Live Mode:</label>"
|
||||
"<input type='checkbox' id='liveMode'><br>"
|
||||
"</form>"
|
||||
"<form id='Scroller' method='post'>"
|
||||
"<input type='hidden' id='scroller' name='scroller'>"
|
||||
"<label for='scroller'>Enable Scroller:</label>"
|
||||
"<input type='submit' value='Off' onclick='document.getElementById(\"scroller\").value=\"Off\";'>"
|
||||
"<input type='submit' value='On' onclick='document.getElementById(\"scroller\").value=\"On\";'>"
|
||||
"</form>"
|
||||
"<form id='Rainbow' method='post'>"
|
||||
"<input type='hidden' id='rainbow' name='rainbow'>"
|
||||
"<label for='scroller'>Enable Scroller:</label>"
|
||||
"<input type='submit' value='Off' onclick='document.getElementById(\"rainbow\").value=\"Off\";'>"
|
||||
"<input type='submit' value='On' onclick='document.getElementById(\"rainbow\").value=\"On\";'>"
|
||||
"</form>"
|
||||
"<div id='message'>Status</div>"
|
||||
"<br><a href='/ticker'>Ticker</a>"
|
||||
"<script>"
|
||||
"function handleSubmit(formId) {"
|
||||
"document.getElementById(formId).addEventListener('submit', function(event) {"
|
||||
"event.preventDefault();"
|
||||
"var xhr = new XMLHttpRequest();"
|
||||
"xhr.open('POST', '/'+formId, true);"
|
||||
"xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');"
|
||||
"var data = new FormData(event.target);"
|
||||
"xhr.send([...data.entries()].map(e => encodeURIComponent(e[0])+'='+encodeURIComponent(e[1])).join('&'));"
|
||||
"xhr.onloadend = function() {"
|
||||
"document.getElementById('message').textContent = 'Response (Status ' + xhr.status + '): ' + xhr.responseText;"
|
||||
"}"
|
||||
"});"
|
||||
"}"
|
||||
|
||||
"['StringForm', 'Scroller', 'Rainbow'].forEach(handleSubmit);"
|
||||
"</script>"
|
||||
"<script>"
|
||||
"var liveModeCheckbox = document.getElementById('liveMode');"
|
||||
"var textArea = document.getElementsByName('text')[0];"
|
||||
"var ws = new WebSocket('ws://' + location.hostname + ':81/');"
|
||||
"textArea.addEventListener('input', function(e) {"
|
||||
"if (liveModeCheckbox.checked) ws.send(e.target.value);"
|
||||
"});"
|
||||
"</script>";
|
||||
|
||||
const char* tickerForm =
|
||||
"<form id='tickerForm' action='/tickerForm' method='post'>"
|
||||
"<label for='Line1'>Line 1:</label>"
|
||||
"<input type='text' id='Line1' name='Line1'><br>"
|
||||
"<label for='Line2'>Line 2:</label>"
|
||||
"<input type='text' id='Line2' name='Line2'><br>"
|
||||
"<label for='Line3'>Line 3:</label>"
|
||||
"<input type='text' id='Line3' name='Line3'><br>"
|
||||
"<label for='timeout_ms'>Timeout (ms):</label>"
|
||||
"<input type='number' name='timeout_ms' step='250' default='2000'></input><br>"
|
||||
"<label for='toggle'>Enable:</label>"
|
||||
"<input type='checkbox' id='toggle' name='toggle'><br>"
|
||||
"<input type='submit' value='Senden'>"
|
||||
"</form>"
|
||||
"<script>"
|
||||
"handleSubmit('tickerForm');"
|
||||
"</script>";
|
||||
|
||||
/*
|
||||
* Login page
|
||||
*/
|
||||
const char* colorPickerIndex =
|
||||
"<head>"
|
||||
"<script src='https://cdnjs.cloudflare.com/ajax/libs/jscolor/2.5.1/jscolor.min.js' integrity='sha512-/e+XGe8oSD9M1t0NKNCrUlRsiyeFTiZw4+pmf0g8wTbc8IfiLwJsjTODc/pq3hKhKAdsehJs7STPvX7SkFSpOQ==' crossorigin='anonymous' referrerpolicy='no-referrer'></script>"
|
||||
"</head><body><p>Color: <input data-jscolor=\"{onInput:\'update(this)\'}\"></p>"
|
||||
"<script>"
|
||||
"var ws = new WebSocket('ws://' + location.hostname + ':81/');"
|
||||
"ws.onopen = function () {"
|
||||
"console.log('Connected via WebSocket');"
|
||||
"};"
|
||||
"ws.onerror = function (error) {"
|
||||
"console.log('WebSocket Error: ', error);"
|
||||
"};"
|
||||
"function update(picker) {"
|
||||
"console.log('{RGB}='+picker.toHEXString());"
|
||||
"ws.send('{RGB}='+picker.toHEXString());"
|
||||
"}"
|
||||
"</script></body></html>";
|
||||
|
||||
/*
|
||||
* Login page
|
||||
*/
|
||||
const char* loginIndex =
|
||||
"<form action='/serverIndex' method='get'>"
|
||||
"<table width='20%' bgcolor='A09F9F' align='center'>"
|
||||
"<tr>"
|
||||
"<td colspan=2>"
|
||||
"<center><font size=4><b>ESP32 OTA Login Page</b></font></center>"
|
||||
"<br>"
|
||||
"</td>"
|
||||
"<br>"
|
||||
"<br>"
|
||||
"</tr>"
|
||||
"<td>Username:</td>"
|
||||
"<td><input size=25><br></td>"
|
||||
"</tr>"
|
||||
"<br>"
|
||||
"<br>"
|
||||
"<tr>"
|
||||
"<td>Key:</td>"
|
||||
"<td><input type='Password' size=25 name='k'><br></td>"
|
||||
"<br>"
|
||||
"<br>"
|
||||
"</tr>"
|
||||
"<tr>"
|
||||
"<td><input type='submit' value='Login'></td>"
|
||||
"</tr>"
|
||||
"</table>"
|
||||
"</form>";
|
||||
|
||||
/*
|
||||
* Server Index Page
|
||||
*/
|
||||
|
||||
const char* serverIndex =
|
||||
"<script src='https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js'></script>"
|
||||
"<form method='POST' action='#' enctype='multipart/form-data' id='upload_form'>"
|
||||
"<input type='file' name='update'>"
|
||||
"<input type='submit' value='Update'>"
|
||||
"</form>"
|
||||
"<div id='prg'>progress: 0%</div>"
|
||||
"<script>"
|
||||
"$('form').submit(function(e){"
|
||||
"e.preventDefault();"
|
||||
"var form = $('#upload_form')[0];"
|
||||
"var data = new FormData(form);"
|
||||
" $.ajax({"
|
||||
"url: `/update?k=${(new URLSearchParams(window.location.search)).get('k')}`,"
|
||||
"type: 'POST',"
|
||||
"data: data,"
|
||||
"contentType: false,"
|
||||
"processData:false,"
|
||||
"xhr: function() {"
|
||||
"var xhr = new window.XMLHttpRequest();"
|
||||
"xhr.upload.addEventListener('progress', function(evt) {"
|
||||
"if (evt.lengthComputable) {"
|
||||
"var per = evt.loaded / evt.total;"
|
||||
"$('#prg').html('progress: ' + Math.round(per*100) + '%');"
|
||||
"}"
|
||||
"}, false);"
|
||||
"return xhr;"
|
||||
"},"
|
||||
"success:function(d, s) {"
|
||||
"console.log('success!')"
|
||||
"},"
|
||||
"error: function (a, b, c) {"
|
||||
"}"
|
||||
"});"
|
||||
"});"
|
||||
"</script>";
|
||||
|
||||
/*
|
||||
* CSV Upload page
|
||||
*/
|
||||
const char* csvUploadPage =
|
||||
"<form method='POST' action='/uploadCSV' enctype='multipart/form-data' id='upload_csv_form'>"
|
||||
"<label for='csvFile'>Upload CSV File:</label>"
|
||||
"<input type='file' name='csvFile' accept='.csv'><br>"
|
||||
"<input type='submit' value='Upload'>"
|
||||
"</form>"
|
||||
"<script>"
|
||||
"$('#upload_csv_form').submit(function(e){"
|
||||
"e.preventDefault();"
|
||||
"var form = $('#upload_csv_form')[0];"
|
||||
"var data = new FormData(form);"
|
||||
" $.ajax({"
|
||||
"url: '/uploadCSV',"
|
||||
"type: 'POST',"
|
||||
"data: data,"
|
||||
"contentType: false,"
|
||||
"processData:false,"
|
||||
"success:function(d, s) {"
|
||||
"console.log('File uploaded successfully!')"
|
||||
"},"
|
||||
"error: function (a, b, c) {"
|
||||
"console.log('Error during file upload.');"
|
||||
"}"
|
||||
"});"
|
||||
"});"
|
||||
"</script>";
|
||||
|
||||
/*
|
||||
* Wifi credentials form
|
||||
*/
|
||||
const char* wifiCredentialsTemplate =
|
||||
"<html><body>"
|
||||
"<form method='post' action='/saveCredentials'>"
|
||||
"SSID:<br><input type='text' name='ssid' text='%s'><br>"
|
||||
"WiFi Password:<br><input type='text' name='wifi_pw' text='%s'><br><br>"
|
||||
"OTA Password:<br><input type='text' name='ota_pw' text='%s'><br><br>"
|
||||
"<input type='submit' value='Save'></form></body></html>";
|
||||
|
||||
#endif
|
11
test/README
Normal file
11
test/README
Normal file
|
@ -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
|
Loading…
Reference in a new issue