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