Closed karthidigi closed 6 months ago
This sounds like I missed another dependency. I will look into this and let you know, and update the README. I am also trying to get a CI build test that I can run on a weekly basis just to let me know the next time it needs an update.
Thanks for your patience, and thank you again for helping me spot these problems.
Ok. I looked a little closer, I don't think this is a missing dependency. I notice you are using version 3.0.1 of the esp8266 board support libraries. I updated before I started working on #9. I have no problems building with version 3.1.2 of the esp8266 board support libraries, since #10. Can you try updating you board package for the esp8266? I think that should solve the problem. 🤞🏾 (and don't forget to revert that change you made... that shouldn't be necessary )
yes thank you for finding the ESP8266 Ardunino core version, I updated it, and now the code successfully compiled
I have doubts about which ESP8266 board to select in board manager? I selected the Generic ESP8266 Module, CPU frequency max is: 40Mhz only, I have set the Flash settings as per your recommendation
and getting below warning
Variables and constants in RAM (global, static), used 33132 / 80192 bytes (41%)
║ SEGMENT BYTES DESCRIPTION
╠══ DATA 1524 initialized variables
╠══ RODATA 3944 constants
╚══ BSS 27664 zeroed variables
. Instruction RAM (IRAM_ATTR, ICACHE_RAM_ATTR), used 61259 / 65536 bytes (93%)
║ SEGMENT BYTES DESCRIPTION
╠══ ICACHE 32768 reserved space for flash instruction cache
╚══ IRAM 28491 code in IRAM
. Code in flash (default, ICACHE_FLASH_ATTR), used 348440 / 1048576 bytes (33%)
║ SEGMENT BYTES DESCRIPTION
╚══ IROM 348440 code in flash
When in doubt the generic module is probably the best choice. I personally built mine with ESP01 that plug into a 2x4 header block, but early prototypes were built with esp12E (and F) modules.
and getting below warning
Variables and constants in RAM (global, static), used 33132 / 80192 bytes (41%) ║ SEGMENT BYTES DESCRIPTION ╠══ DATA 1524 initialized variables ╠══ RODATA 3944 constants ╚══ BSS 27664 zeroed variables . Instruction RAM (IRAM_ATTR, ICACHE_RAM_ATTR), used 61259 / 65536 bytes (93%) ║ SEGMENT BYTES DESCRIPTION ╠══ ICACHE 32768 reserved space for flash instruction cache ╚══ IRAM 28491 code in IRAM . Code in flash (default, ICACHE_FLASH_ATTR), used 348440 / 1048576 bytes (33%) ║ SEGMENT BYTES DESCRIPTION ╚══ IROM 348440 code in flash
These aren't warnings, just output of the size of the memory and flash consumed... there are a few warnings from the compiler about some WebSocket callbacks that are not implemented... this is fine, those callbacks are not necessary for this application, although it would be a good idea for me to implement ping/pong, then the server could periodically make sure the client is still connected to the websocket, and close it if the client misses more than a few pings in a row...
hi, Thanks Again,
I am using ESP01 - Flashing is successful, I did some modifications in RGB gpios,
#define LED_RED 1 // 100r resistor
#define LED_GREEN 2 // 470r resistor
#define LED_BLUE 0 // 220r resistor
with my setup, GPIO 3 is not working as Output so I switched to GPIO0. I commented out batteryCheck(); Now everything works but RGB Colors are inverted, when I select a black color in the selection RGB light turns full white, and with the WHITE selection RGB light turns OFF. I have to verify the GPIOs are connected correctly, I am also planning to check this ESP8266 12F. I'll update you on the progress.
Hmmm. Now I need to open up an old build and confirm my wiring... I have a feeling I wasn't looking at the hardware when I wrote the documentation...
I normally run the LEDs with the resistor on the anode going to 3.3v and pull the pin low as a sink to ground. This is better for the GPIO pins as they can safely sink more power than they drive.
Since you have it out in front of you, would you mind rewiring it the way I just described, and let me know it that fixes the reversed behavior? That is if you have a common-anode RGB LED on hand.
Thank you again. You inspired me to add some CI workflows to help keep me up to date, and I added CodeQL analysis to spot bugs... it found 3 in the JavaScript (which are now fixed). So you might want to pull a fresh copy of the repo to get those latest fixes.
Sorry. I just realized my description was confusing and wrong. The resistors go between the colors and their "sink" pin and the common anode goes directly to 3.3v. But I still need to find a moon lamp and open it up to be sure if what I did.
I am using a common Anode LED (3 cathodes for RGB), so my wiring is similar to your description.
So what might be the difference? One more thing I directly connected the LED without resistors in the middle
Is it possible to configure ESP to station mode? I want ESP to connect to my existing Wifi network.
I definitely need to open up one of the builds and see what I actually did. I think I can just add a definition option, and invert the signal if the opposite kind of RGB LED is used.
I did not set up station mode, but it can be added. That was something I had initially planned, but it didn’t make the cut.
I gave out a bunch of these to family members for Christmas a few years ago, and several were to the same households, so I didn’t want to deal with creating unique host names and network collisions where people were controlling the wrong “moon”. I also wanted to keep them off of the internet-connected network for security reasons.
I have a backlog of other projects I’m working on, but if I find some spare time, I will add an interface to the webpage to change the network settings. If this is a feature you think you can add on your own, I would gladly accept a pull request. 😁
I got to the bottom of the wiring of the LED. I found the notes I made for that build. I did indeed use a common cathode RGB LED. I remember now that I was in a rush, and had 50 common cathode LEDs on hand, and couldn't wait for common anode LEDs to be shipped (we were at the worst part of the pandemic, and shipping speeds were slow).
Using common anode would have been my preference. I will definitely add a definition to invert the pwm signal for common anode RGBs to my todo list, but I can't make any promises how soon. For the time being I will add this to the documentation, I should definitely have included that information! Even for myself, it's funny how much you can forget in just a few years!
Thanks again for discovering important information that should have been included in the documentation.
One more thing I directly connected the LED without resistors in the middle
Without the resistors you won't get accurate colors. And it would be especially dangerous when using a common cathode LED.
I definitely need to open up one of the builds and see what I actually did. I think I can just add a definition option, and invert the signal if the opposite kind of RGB LED is used.
I did not set up station mode, but it can be added. That was something I had initially planned, but it didn’t make the cut.
I gave out a bunch of these to family members for Christmas a few years ago, and several were to the same households, so I didn’t want to deal with creating unique host names and network collisions where people were controlling the wrong “moon”. I also wanted to keep them off of the internet-connected network for security reasons.
I have a backlog of other projects I’m working on, but if I find some spare time, I will add an interface to the webpage to change the network settings. If this is a feature you think you can add on your own, I would gladly accept a pull request. 😁
Thank you for taking the time and verifying your old build 😁 I am a Hardware Engineer, I do not have much experience in SW but I will try to include _wifi provisioning, If I succeed surely will ask for a pull request.
Also, how does this ARDUINO OTA work? is it the same as given in the ESP8266 examples?
It would be good if you enable a low-power workaround. I have plans to add TTP223 with ESP8266 12F for touch functionality for a color-changing function with the moon itself. I used one commercially available module that has IR remote, and capacitive touch functions, this is my channel
The OTA is indeed the built in ArduinoOTA, I believe I used the default port, so updating the firmware wirelessly from the IDE should be possible. I've been using ESP-IDF and esp32 platform for the last several years so my memory is a little fuzzy with ArduinoIDE and esp8266 now. But I think that requires an extra plugin.
I have been looking as several off the shelf network config and credential management managers, but so far each has a flaw that is less than ideal for the use case I envisioned. I certainly don't mind supporting STA mode, but I also need it to be able to operate in AP mode without locking the user into a captive portal that forces setting STA.
The Preferences library looks like it fits most of my needs, but it stores data unencrypted in LittFS, and since this is where the webpages are served from this is a bad security flaw. I started poking at writing my own extended preferences storage using the emulated EEPROM to keep the credentials storage out of reach of the web server, and only accessible internally, but that is turning out to be more complicated than I hoped... but that might just be because I'm out of practice with C++ and the Arduino environment.
I hadn't really considered an IR remote, that is an interesting idea. I will keep that in mind when I get around to designing my next iteration of this project.
I have found a few bugs while poking around at making WiFi settings configurable in the web interface, so I will try to get those fixed, and at least make an easy way to define the led type, so common cathode can be used. Not sure how much longer the WiFi config will take, I could easily make a way to hardcode STA settings, but I would rather make that user configurable. That way I can add a CI workflow to offer ready to flash binaries that anyone can set up for their preferred network configuration.
I used one commercially available module that has IR remote, and capacitive touch functions, this is my channel
Nice videos! It just occurred to me I should invite you to an active Telegram community for ESP8266/ESP32 and other IoT microcontrollers. We're always happy to gain new members: https://t.me/esp32hax
It's a great group to ask for advice/help with firmware and hardware design. It sounds like you could help with some circuit design questions, and get help learning more about the software side.
I just pushed changes that make it easier to use either common anode (the default) or common cathode RGB LEDs. I reintroduced STA mode support, for now you will need to edit Config.h
to use it, but I also upgraded the preference storage, and that includes space for up to 3 wifi networks to join, along with storage for custom hostname, and access point name and psk. I haven't updated the web interface to allow changing the network setting yet, but that is the plan. Because of all the changed (also to the graphic elements, and organization of the data directory), you should be sure to use the "Erase all flash contents" option when you first upload the sketch, and you will need to re-upload the web files (the data directory) using the arduino-littlefs-upload
plugin.
Also see the readme about new optional async support.
I think I addressed everything you brought up in this issue. If the updates are working for you maybe we should close this issue, so it doesn't get too big and cover too many topics.😉
Thank you so much for you feedback. This application is now 90% better than it was when you found it.
If you have more suggestions, problems or feedback please open more issues. Also I opened a couple new issues, if you have any feedback or suggestions feel free to add to those issues.
Also, how does this ARDUINO OTA work? is it the same as given in the ESP8266 examples?
During the time I was away from the ArduinoIDE (going from 1.8 to 2.x) I didn't realize this feature does not seem to be supported by the IDE anymore. The OTA update can be done several ways, and I am investigating how best to do this. I am fairly certain there is at least a python script somewhere that can push an OTA update to the port 8266
opened by the OTA updater in this sketch. I would love to use something like esp_ghota, it's an ESP-IDF component for esp32, but maybe I can do a fork that will work with Arduino and the ESP8266.
I appreciate your efforts to take this project to this level. after flashing the code and verifying, I will post my feedback Thank you once again.
I just pushed changes that make it easier to use either common anode (the default) or common cathode RGB LEDs. I reintroduced STA mode support, for now you will need to edit
Config.h
to use it, but I also upgraded the preference storage, and that includes space for up to 3 wifi networks to join, along with storage for custom hostname, and access point name and psk. I haven't updated the web interface to allow changing the network setting yet, but that is the plan. Because of all the changed (also to the graphic elements, and organization of the data directory), you should be sure to use the "Erase all flash contents" option when you first upload the sketch, and you will need to re-upload the web files (the data directory) using thearduino-littlefs-upload
plugin.Also see the readme about new optional async support.
I flashed the new code with#define USE_ASYNC true & #define USE_ASYNC false,
Serial monitor log shows below error
12:31:53.204 -> File Not Found: /generate_204 12:31:55.001 -> handleFileRead: /generate_204 12:31:55.001 -> File Not Found: /generate_204 12:31:55.038 -> handleFileRead: /gen_204 12:31:55.038 -> File Not Found: /gen_204 12:31:59.061 -> handleFileRead: /generate_204 12:31:59.061 -> File Not Found: /generate_204 12:31:59.115 -> handleFileRead: /gen_204 12:31:59.115 -> File Not Found: /gen_204
I flashed with the "Erase all flash contents" option. Am I missing something please look into this.
You should not define anything for ASYNC, that get set automatically if the compiler find the ESPAscynTCP and ESPAsyncWebServer library installed. I added new brief instructions to the README for uploading the LittleFS files using the the arduino-littlefs-upload plugin.
It looks like you are 99% of the way there.
Once the LittleFS tool is installed, use [CTRL]+[SHIFT]+[P] to open the command pallete and then when the box at the top pops open with “>” type “Upload LittleFS” and you should see “Upload LittleFS to Pico/ESP8266” show up in the list. Just click on that. (Or you can type the whole thing and press enter)
I also added info about the ASYNC feature, you just install the extra libraries and they are used automatically if available.
they are completely optional, but do speed up loading the web page by small amount.
Here's a screenshot of what you are looking for after pressing [CTRL]+[SHIFT]+[P] (and I just typed "Upload"):
The LittleFS upload used to show up in the "Tools" menu with ArduinoIDE 1.x, and if I remember correctly, it might have even done it automatically if a data
directory was found in the directory with the sketch. You have to do it manually now.
forget to mention in my previous comment. I uploaded the data files exactly as described, I tried flashing code & data today also. webpage not loading fully. after few mintues RED LED starts blinks indicating low voltage. I even reduced the threshold from 2.66V to 0.6V. Getting similar error. But previous release v1.1 works perfectly I made some changes for common anode LED in code.
int r = HTMLtoAnalog(redX);
int g = HTMLtoAnalog(grnX);
int b = HTMLtoAnalog(bluX);
r = map(r, 0, 255, 255, 0);
g = map(g, 0, 255, 255, 0);
b = map(b, 0, 255, 255, 0);
analogWrite(LED_RED, r); // write it to the LED output pins
analogWrite(LED_GREEN, g);
analogWrite(LED_BLUE, b);
I am facing issues only in current relase V1.1.1.
Do you have anything connected to pin 0? If you do that is probably why it’s emitting the low voltage warning. You can use any other pins, but pin0 should be left floating.
PIN 0 left floating, I am testing it in ESP8266 12F Witty cloud module.
`LittleFS Filesystem Uploader
Building LittleFS filesystem
C:\Users\Jaswanth\AppData\Local\Arduino15\packages\esp32\tools\mklittlefs\3.0.0-gnu12-dc7f933/mklittlefs.exe -c C:\Users\Jaswanth\OneDrive\Documents\Arduino\MoonLight_esp8266\Moonlight_8266-1.2.1\Moonlight_8266_12f/data -p 256 -b 8192 -s 2072576 C:\Users\Jaswanth\AppData\Local\Temp\tmp-13100-YvU6wO8pcYUj-.littlefs.bin
/edit.html
/index.html
/success.html
/_static/main.css
/_static/manifest.json
/_static/manifest.json.license
/_static/moon.png
/_static/moon.png.license
/_static/moon_32x32.ico
/_static/moon_32x32.ico.license
/_static/moon_48x48.ico
/_static/moon_48x48.ico.license
/_static/moon_64x64.ico
/_static/moon_64x64.ico.license
/_static/RGBsocket.js
/_static/upload.js
Uploading LittleFS filesystem
C:\Users\Jaswanth\AppData\Local\Arduino15\packages\esp8266\tools\python3\3.7.2-post1/python3.exe C:\Users\Jaswanth\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\3.1.2/tools/upload.py --chip esp8266 --port COM5 --baud 115200 write_flash 2097152 C:\Users\Jaswanth\AppData\Local\Temp\tmp-13100-YvU6wO8pcYUj-.littlefs.bin
esptool.py v3.0
Serial port COM5
Connecting....
Chip is ESP8266EX
Features: WiFi
Crystal is 26MHz
MAC: 18:fe:34:ca:cd:99
Uploading stub...
Running stub...
Stub running...
Configuring flash size...
Auto-detected Flash size: 4MB
Compressed 2072576 bytes to 42327...
Writing at 0x00200000... (33 %)
Writing at 0x00204000... (66 %)
Writing at 0x00208000... (100 %)
Wrote 2072576 bytes (42327 compressed) at 0x00200000 in 6.6 seconds (effective 2508.9 kbit/s)...
Hash of data verified.
Leaving...
Hard resetting via RTS pin...
Completed upload.`
ESP8266-12F with below modified code everything works fine, I added map
function for Common Anode LED,
I am using the same arduino IDE 2.0x with littlefs
`#include <ArduinoOTA.h>
#include <DNSServer.h>
#include <EEPROM.h>
#include <ESP8266WebServer.h>
#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <Hash.h>
#include <LittleFS.h>
#include <WebSocketsServer.h>
#include <WiFiUdp.h>
#include <cmath>
IPAddress apIP(192, 168, 4, 1);
ESP8266WebServer server(80); // create a web server on port 80
WebSocketsServer webSocket(81); // create a websocket server on port 81
DNSServer dnsServer; // create an instance of the DNSServer class, called 'dnsServer'
File fsUploadFile; // a File variable to temporarily store the received file.
const char *ssid = "Moonlight"; // The name of the Wi-Fi network that will be created
const char *password =
""; // The password required to connect to it, leave blank for an open network
const char *hostName = "moon"; // A hostname for the DNS and OTA services
const char *OTAPassword =
"edca965aece0a07662f4f989947ef119"; // Change to match your OTA password md5() hash
bool rainbow; // For rainbow mode.
char savedColor[12]; // keeping track of saved color preferences.
char webColor[8]; // current color in HTML format.
char rainbowColor[8]; // To show the correct color on the moon during rainbow mode
#define TX_POW 1 // sets wifi power (0 lowest 20.5 highest)
// specify the pins with an RGB LED connected
#define LED_RED 14 // 100r resistor
#define LED_GREEN 12 // 470r resistor
#define LED_BLUE 13 // 220r resistor
// PIN 0 must be left floating for battery measurements to work!
ADC_MODE(ADC_VCC);
// EEPROM Registers where our configs are stored.
#define MODE_STO 0
#define R_STO 1
#define G_STO 2
#define B_STO 3
#define LOG_ENABLED true // Change this to true to enable console logging
/*___________________________________________________SETUP__________________________________________________________*/
void setup() {
pinMode(LED_RED, OUTPUT); // the pins with LEDs connected are outputs
pinMode(LED_GREEN, OUTPUT);
pinMode(LED_BLUE, OUTPUT);
if (LOG_ENABLED) {
Serial.begin(115200); // Start the Serial communication to send messages to the computer
delay(10);
Serial.println("\r\n");
}
EEPROM.begin(512);
analogWrite(LED_RED, 0); // Turn on moon.
analogWrite(LED_GREEN, 0);
analogWrite(LED_BLUE, 0);
colorInit(); // Restore saved color settings.
startWiFi(); // Start a Wi-Fi access point, and try to connect to some given access points. Then
// wait for either an AP or STA connection
startDNS(); // Start the DNS and mDNS responder
startLittleFS(); // Start the FS and list all contents
startWebSocket(); // Start a WebSocket server
startServer(); // Start an HTTP server with a file read handler and an upload handler
startOTA(); // Start the OTA service
}
/*____________________________________________________LOOP__________________________________________________________*/
unsigned long prevMillis = millis();
int hue = 0;
void loop() {
for (int i = 0; i <= 750000; i++) { // measure vcc voltage at approx. 60 sec intervals. varies
// slightly under load, but it's not critical.
webSocket.loop(); // constantly check for websocket events
dnsServer.processNextRequest(); // handle dns requests
server.handleClient(); // handle server requests
if (rainbow) { // if the rainbow effect is turned on
if (millis() > prevMillis + 27) {
if (++hue == 360) // Cycle through the color wheel in 10 seconds (increment by one degree
// every 27 ms)
hue = 0;
setHue(hue); // Set the RGB LED to the right color
prevMillis = millis();
}
}
ArduinoOTA.handle(); // Check for OTA update.
}
batteryCheck();
void rainbowWeb();
}
/*_________________________________________SETUP_FUNCTIONS__________________________________________________________*/
void startWiFi() { // Start a Wi-Fi access point, and try to connect to some given access points.
// Then wait for either an AP or STA connection
WiFi.setOutputPower(TX_POW); // sets wifi power (0 lowest 20.5 highest)
WiFi.softAP(ssid, password); // Start the access point
MDNS.update();
if (LOG_ENABLED) {
Serial.println("Network initalized.");
}
}
void startOTA() { // Start the OTA service
ArduinoOTA.setHostname(hostName);
ArduinoOTA.setPasswordHash(OTAPassword);
ArduinoOTA.setPort(8266);
ArduinoOTA.onStart([]() {
if (LOG_ENABLED) {
Serial.println("Starting OTA update.");
}
});
ArduinoOTA.onEnd([]() {
if (LOG_ENABLED) {
Serial.println("Update complete");
}
analogWrite(LED_RED, 255);
analogWrite(LED_GREEN, 0);
analogWrite(LED_BLUE, 255);
delay(3000);
ESP.restart();
});
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
if (LOG_ENABLED) {
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
}
// Start at full brightness and fade out until complete.
int UploadProg = 10 - ((progress / (total / 100)) * 10.2);
analogWrite(LED_RED, 0);
analogWrite(LED_GREEN, 0);
analogWrite(LED_BLUE, UploadProg);
});
ArduinoOTA.onError([](ota_error_t error) {
analogWrite(LED_BLUE, 255);
analogWrite(LED_GREEN, 255);
analogWrite(LED_RED, 0);
if (LOG_ENABLED) {
Serial.printf("Error[%u]: ", error);
if (error == OTA_AUTH_ERROR)
Serial.println("Auth Failed");
else if (error == OTA_BEGIN_ERROR)
Serial.println("Begin Failed");
else if (error == OTA_CONNECT_ERROR)
Serial.println("Connect Failed");
else if (error == OTA_RECEIVE_ERROR)
Serial.println("Receive Failed");
else if (error == OTA_END_ERROR)
Serial.println("End Failed");
}
delay(1000);
for (int m = 0; m <= 2; m++) {
analogWrite(LED_RED, 100);
delay(133);
analogWrite(LED_RED, 0);
delay(200);
}
htmlPWM(webColor);
});
ArduinoOTA.begin();
if (LOG_ENABLED) {
Serial.println("OTA service ready.");
}
}
void startLittleFS() { // Start LittleFS (and maybe list all contents)
LittleFS.begin(); // Start the File System
if (LOG_ENABLED) {
Serial.println("LittleFS started. Contents:");
Dir dir = LittleFS.openDir("/");
while (dir.next()) { // List the file system contents
String fileName = dir.fileName();
size_t fileSize = dir.fileSize();
Serial.printf("\tFS File: %s, size: %s\r\n", fileName.c_str(), formatBytes(fileSize).c_str());
}
Serial.printf("\n");
}
}
void startWebSocket() { // Start a WebSocket server
webSocket.begin(); // start the websocket server
webSocket.onEvent(
webSocketEvent); // if there's an incoming websocket message, go to function 'webSocketEvent'
MDNS.addService("ws", "tcp", 81);
if (LOG_ENABLED) {
Serial.println("WebSocket server started.");
}
}
void startDNS() { // Start the DNS and mDNS responders
MDNS.begin(hostName); // start the multicast domain name server
dnsServer.setTTL(300); // defaults ot 60 second TTL.
dnsServer.setErrorReplyCode(
DNSReplyCode::NoError); // default return code is 'DNSReplyCode::NonExistentDomain'
dnsServer.start(53, "*", apIP); // Start DNS server redirecting all requests to the UI
if (LOG_ENABLED) {
Serial.print("mDNS responder started: http://");
Serial.print(hostName);
Serial.println(".local");
Serial.println("DNS responder started on port 53.");
}
}
void startServer() { // Start a HTTP server with a file read handler and an upload handler
server.on(
"/edit.html", HTTP_POST,
[]() { // If a POST request is sent to the /edit.html address,
server.send(200, "text/plain", ""); // go to 'handleFileUpload'
},
handleFileUpload);
// On page request go to function 'handleNotFound' and check if the file exists
server.onNotFound(handleNotFound);
server.begin(); // start the HTTP server
MDNS.addService("http", "tcp", 80);
if (LOG_ENABLED) {
Serial.println("HTTP server started.");
}
}
void colorInit() {
byte raining = EEPROM.read(MODE_STO);
byte rD = EEPROM.read(R_STO);
byte gD = EEPROM.read(G_STO);
byte bD = EEPROM.read(B_STO);
if (LOG_ENABLED) {
Serial.println("Loaded preferences... ");
Serial.print("Got ");
Serial.print(raining);
Serial.println(" from EEPROM 0 (Rainbow)");
Serial.print("Got ");
Serial.print(rD);
Serial.println(" from EEPROM 1 (Red)");
Serial.print("Got ");
Serial.print(gD);
Serial.println(" from EEPROM 2 (Green)");
Serial.print("Got ");
Serial.print(bD);
Serial.println(" from EEPROM 3 (Blue)");
}
if (rD + gD + bD < 19) { // In case value is too low to light led, assume no saved prefs.
if (LOG_ENABLED) {
Serial.println("Preferences do not appear valid!");
Serial.println("Attempting to restore factory defaults to EEPROM...");
}
rD = 255;
gD = 255;
bD = 255;
raining = 0;
// write default prefs to avoid this next time.
EEPROM.write(MODE_STO, raining);
EEPROM.write(R_STO, rD);
EEPROM.write(G_STO, gD);
EEPROM.write(B_STO, bD);
EEPROM.commit();
if (LOG_ENABLED) {
Serial.println("Defaults have been restored.");
}
}
if (LOG_ENABLED) {
Serial.println("Read stored values, Converting to HTML and PWM colors...");
}
char buffer[3];
char webRGB[8];
strcpy(webRGB, "#");
itoa((int)rD, buffer, 16);
if (buffer[0] <= '9') {
strcat(webRGB, "0");
} // correct #0FF0 to #00FF00 for example...
strcat(webRGB, buffer);
itoa((int)gD, buffer, 16);
if (buffer[0] <= '9') {
strcat(webRGB, "0");
} // Hacky, I know, but it works.
strcat(webRGB, buffer);
itoa((int)bD, buffer, 16);
if (buffer[0] <= '9') {
strcat(webRGB, "0");
} // I'm open to suggestions...
strcat(webRGB, buffer);
strcat(webRGB, "\0");
if (LOG_ENABLED) {
Serial.print("updating web color to ");
Serial.println(webRGB);
}
std::copy(webRGB, webRGB + 7, webColor);
htmlPWM(webColor);
if (LOG_ENABLED) {
Serial.println("set LED color: ");
Serial.print(webColor);
Serial.print("Red : ");
Serial.print(rD);
Serial.print(", Green : ");
Serial.print(gD);
Serial.print(", Blue : ");
Serial.println(bD);
}
if (raining == 0) {
rainbow = false;
if (LOG_ENABLED) {
Serial.println("Rainbow mode off.");
}
strcat(savedColor, webColor);
strcat(savedColor, "-\0");
} else {
rainbow = true;
if (LOG_ENABLED) {
Serial.println("Rainbow mode active.");
}
strcat(savedColor, webColor);
strcat(savedColor, "+\0");
}
if (LOG_ENABLED) {
Serial.print("Saved Color string is: ");
Serial.println(savedColor);
Serial.println("Preferences loaded.");
}
}
/*______________________________________WEBSERVER_HANDLERS__________________________________________________________*/
void handleNotFound() { // if the requested file or page doesn't exist, return a 404 not found error
if (!handleFileRead(server.uri())) { // check if the file exists in the flash memory (LittleFS),
// if so, send it
server.send(404, "text/plain", "404: File not found");
}
}
void handleFileForbid() { // dont serve config files, etc...
server.send(403, "text/plain", "Access Forbidden!");
}
void handleServerError() { // A last resort server error handler
server.send(500, "text/plain", "Oops. You found a bug!");
}
bool handleFileRead(String path) { // send the right file to the client (if it exists)
if (LOG_ENABLED) {
Serial.println("handleFileRead: " + path);
}
if (path.startsWith("/cfg"))
handleFileForbid();
if (path.endsWith("/"))
path += "index.html"; // If a folder is requested, send the index file
String contentType = getContentType(path); // Get the MIME type
String pathWithGz = path + ".gz";
if (LittleFS.exists(pathWithGz) || LittleFS.exists(path)) { // If the file exists, either as a compressed archive, or normal
if (LittleFS.exists(pathWithGz)) // If there's a compressed version available
path += ".gz"; // Use the compressed version
File file = LittleFS.open(path, "r"); // Open the file
size_t sent = server.streamFile(file, contentType); // Send it to the client
file.close(); // Close the file again
if (LOG_ENABLED) {
Serial.print("Sent file: " + path);
Serial.print(", size: ");
Serial.println(sent);
}
return true;
}
if (LOG_ENABLED) {
Serial.println("File Not Found: " + path); // If the file doesn't exist, return false
}
return false;
}
void handleFileUpload() { // upload a new file to the LittleFS
HTTPUpload &upload = server.upload();
String path;
if (upload.status == UPLOAD_FILE_START) {
path = upload.filename;
if (!path.startsWith("/"))
path = "/" + path;
if (!path.endsWith(".gz")) { // The file server always prefers a compressed version of a file
String pathWithGz =
path + ".gz"; // So if an uploaded file is not compressed, the existing compressed
if (LittleFS.exists(pathWithGz)) // version of that file must be deleted (if it exists)
LittleFS.remove(pathWithGz);
}
if (LOG_ENABLED) {
Serial.print("handleFileUpload Name: ");
Serial.println(path);
}
fsUploadFile = LittleFS.open(
path, "w"); // Open the file for writing in LittleFS (create if it doesn't exist)
path = String();
} else if (upload.status == UPLOAD_FILE_WRITE) {
if (fsUploadFile)
fsUploadFile.write(upload.buf, upload.currentSize); // Write the received bytes to the file
} else if (upload.status == UPLOAD_FILE_END) {
if (fsUploadFile) { // If the file was successfully created
fsUploadFile.close(); // Close the file again
if (LOG_ENABLED) {
Serial.print("handleFileUpload Size: ");
Serial.println(upload.totalSize);
}
server.sendHeader("Location", "/success.html"); // Redirect the client to the success page
server.send(303);
} else {
server.send(500, "text/html",
"<html><body><h1>500: couldn't create file.</h1><p><a "
"href='/'>Return</a></p></body></html>");
}
}
}
/*______________________________________WEBSOCKET_HANDLERS__________________________________________________________*/
void webSocketEvent(uint8_t num, WStype_t type, uint8_t *payload,
size_t length) { // When a WebSocket message is received
switch (type) {
case WStype_DISCONNECTED: // if the websocket is disconnected
if (LOG_ENABLED) {
Serial.printf("[%u] Disconnected!\n", num);
}
break;
case WStype_CONNECTED:
{ // if a new websocket connection is established
if (LOG_ENABLED) {
IPAddress ip = webSocket.remoteIP(num);
Serial.printf("[%u] Connected from %d.%d.%d.%d url: %s\n", num, ip[0], ip[1], ip[2], ip[3],
payload);
}
webSocket.broadcastTXT(webColor);
batteryCheck();
if (rainbow == true) {
webSocket.broadcastTXT("R");
}
}
break;
case WStype_TEXT: // if new text data is received
if (LOG_ENABLED) {
Serial.printf("[%u] get Text: %s\n", num, payload);
}
if (payload[0] == '#') { // we get RGB data
// keep track of the client color.
std::copy(payload, payload + 7, webColor);
htmlPWM(webColor);
} else if (*payload == 'R') { // the browser sends an R when the rainbow effect is enabled
rainbow = true;
if (LOG_ENABLED) {
Serial.println("Client activated rainbow mode.");
}
webSocket.broadcastTXT("R");
} else if (*payload == 'N') { // the browser sends an N when the rainbow effect is disabled
rainbow = false;
if (LOG_ENABLED) {
Serial.println("Client deactivated rainbow mode.");
}
webSocket.broadcastTXT("N");
htmlPWM(webColor);
webSocket.broadcastTXT(webColor);
} else if (payload[0] == 'S') { // the browser sends a S to request saving color settings.
if (LOG_ENABLED) {
Serial.println("Client requested save color settings.");
}
saveColor(payload);
} else if (*payload == 'C') {
if (LOG_ENABLED) {
Serial.print("Client requested color settings, sending ");
Serial.println(webColor);
}
webSocket.broadcastTXT(webColor);
if (rainbow == true) {
if (LOG_ENABLED) {
Serial.println("Rainbow on.");
}
webSocket.broadcastTXT("R");
} else {
if (LOG_ENABLED) {
Serial.println("Rainbow off.");
}
webSocket.broadcastTXT("N");
}
}
break;
case WStype_ERROR:
if (LOG_ENABLED) {
IPAddress ip = webSocket.remoteIP(num);
Serial.printf("Websocket error accepting payload from %d.%d.%d.%d url: %s\n", ip[0], ip[1],
ip[2], ip[3], payload);
}
break;
}
}
/*__________________________________________TASK_FUNCTIONS__________________________________________________________*/
void htmlPWM(char *setcolor) { // Convert HTML color and set PWM
std::copy(setcolor, setcolor + 7, webColor); // keep client color in sync.
// Split RGB HEX String into individual color values
char redX[5] = { 0 };
char grnX[5] = { 0 };
char bluX[5] = { 0 };
redX[0] = grnX[0] = bluX[0] = '0';
redX[1] = grnX[1] = bluX[1] = 'X';
redX[2] = setcolor[1];
redX[3] = setcolor[2];
grnX[2] = setcolor[3];
grnX[3] = setcolor[4];
bluX[2] = setcolor[5];
bluX[3] = setcolor[6];
int r = HTMLtoAnalog(redX);
int g = HTMLtoAnalog(grnX);
int b = HTMLtoAnalog(bluX);
r = map(r, 0, 255, 255, 0);
g = map(g, 0, 255, 255, 0);
b = map(b, 0, 255, 255, 0);
analogWrite(LED_RED, r); // write it to the LED output pins
analogWrite(LED_GREEN, g);
analogWrite(LED_BLUE, b);
// Update our webColor to keep UI in sync.
webSocket.broadcastTXT(webColor);
}
int HTMLtoAnalog(char *WebColor) {
// Convert HEX String to integer
int ColorNum = strtol(WebColor, NULL, 16);
// convert 4-bit web (0-255) to 10-bit analog (0-1023) range. without floats the rounding errors
// cause large errors with small numbers.
float tenBit = ColorNum * 4.012;
// convert linear (0..512..1023) to logarithmic (0..256..1023) scale.
// This is just an approximation to compensate for the way that LEDs and eyes work.
float TrueColor = sq(tenBit) / 1023;
int pwmVal = round(TrueColor);
return pwmVal;
}
void saveColor(const uint8_t *savecolor) {
byte rain = 0;
// Split RGB HEX String into individual color values
char redX[5] = { 0 };
char grnX[5] = { 0 };
char bluX[5] = { 0 };
redX[0] = grnX[0] = bluX[0] = '0';
redX[1] = grnX[1] = bluX[1] = 'x';
// redX[5] = grnX[5] = bluX[5] = '\0';
redX[2] = savecolor[2];
redX[3] = savecolor[3];
grnX[2] = savecolor[4];
grnX[3] = savecolor[5];
bluX[2] = savecolor[6];
bluX[3] = savecolor[7];
// Convert HEX String to integer
int redW = strtol(redX, NULL, 16);
int grnW = strtol(grnX, NULL, 16);
int bluW = strtol(bluX, NULL, 16);
unsigned char redP = (unsigned char)redW;
unsigned char grnP = (unsigned char)grnW;
unsigned char bluP = (unsigned char)bluW;
if (rainbow != true) {
EEPROM.write(MODE_STO, rain);
EEPROM.write(R_STO, redP);
EEPROM.write(G_STO, grnP);
EEPROM.write(B_STO, bluP);
if (LOG_ENABLED) {
Serial.print("Wrote ");
Serial.print(rain);
Serial.println(" to EEPROM 0 (Rainbow state)");
Serial.print("Wrote ");
Serial.print(redP);
Serial.println(" to EEPROM 1 (Red 4-bit)");
Serial.print("Wrote ");
Serial.print(grnP);
Serial.println(" to EEPROM 2 (Green 4-bit)");
Serial.print("Wrote ");
Serial.print(bluP);
Serial.println(" to EEPROM 3 (Blue 4-bit)");
}
if (EEPROM.commit()) {
if (LOG_ENABLED) {
Serial.println("All data stored to EEPROM.");
}
webSocket.broadcastTXT("Sy");
webSocket.broadcastTXT(webColor);
} else {
if (LOG_ENABLED) {
Serial.println("Failed to commit data to EEPROM!");
}
webSocket.broadcastTXT("S:FAILED");
}
} else { // Save Rainbow mode active
rain = 1;
if (LOG_ENABLED) {
Serial.print("Writing ");
Serial.print(rain);
Serial.println(" to EEPROM 0 (Rainbow state)");
}
EEPROM.write(MODE_STO, rain);
if (EEPROM.commit()) {
if (LOG_ENABLED) {
Serial.println("All data stored to EEPROM.");
}
webSocket.broadcastTXT("Sy");
} else {
if (LOG_ENABLED) {
Serial.println("Failed to commit data to EEPROM!");
}
webSocket.broadcastTXT("S:FAILED");
}
}
}
void batteryCheck() {
float Batt;
Batt = ESP.getVcc() / 1000.0;
char VCC[7] = { 0 };
char Volts[8] = { 0 };
dtostrf(Batt, 6, 4, VCC);
strcat(Volts, "V");
strcat(Volts, VCC);
strcat(Volts, "\0");
webSocket.broadcastTXT(Volts);
// REAL voltage in the battery is 1/2 volt higher, before the diode voltage drop, so this value
// does not represent over-drain.
while (Batt <= 1.1) { //= 2.660
int fade = 0;
analogWrite(LED_RED, fade);
analogWrite(LED_GREEN, 255);
analogWrite(LED_BLUE, 255);
for (int i = 0; i <= 255; i++) {
analogWrite(LED_RED, i);
}
for (int i = 255; i <= 1; i--) {
analogWrite(LED_RED, i);
}
}
}
void rainbowWeb(int Ar, int Ag, int Ab) {
if (LOG_ENABLED) {
Serial.println("Got PWM values :");
Serial.println(Ar);
Serial.println(Ag);
Serial.println(Ab);
}
// GPIO pwm to 8-bit web color correction
float Rd = sqrt((Ar * 1023)) / 4.011;
float Gd = sqrt((Ag * 1023)) / 4.011;
float Bd = sqrt((Ab * 1023)) / 4.011;
int Rx = round(Rd);
int Gx = round(Gd);
int Bx = round(Bd);
if (LOG_ENABLED) {
Serial.println("Converted 8-bit web colors are:");
Serial.println(Rx);
Serial.println(Gx);
Serial.println(Bx);
}
// encode to html color
char webRGB[8];
char redStr[3];
char grnStr[3];
char bluStr[3];
itoa(Rx, redStr, 16);
itoa(Gx, grnStr, 16);
itoa(Bx, bluStr, 16);
strcpy(webRGB, "#");
if (Rx <= 15) {
strcat(webRGB, "0");
} // correct #0FF0 to #00FF00 for example...
strcat(webRGB, redStr);
if (Gx <= 15) {
strcat(webRGB, "0");
}
strcat(webRGB, grnStr);
if (Bx <= 15) {
strcat(webRGB, "0");
}
strcat(webRGB, bluStr);
strcat(webRGB, "\0");
std::copy(webRGB, webRGB + 7, rainbowColor);
// if (LOG_ENABLED) {
// Serial.print("Converted to HTML color string: ");
// Serial.println(rainbowColor);
// }
return;
}
String formatBytes(size_t bytes) { // convert sizes in bytes to KB and MB
if (bytes < 1024) {
return String(bytes) + "B";
} else if (bytes < (1024 * 1024)) {
return String(bytes / 1024.0) + "KB";
} else if (bytes < (1024 * 1024 * 1024)) {
return String(bytes / 1024.0 / 1024.0) + "MB";
} else {
return String("ERROR");
}
}
String getContentType(
String filename) { // determine the filetype of a given filename, based on the extension
if (filename.endsWith(".html"))
return "text/html";
else if (filename.endsWith(".css"))
return "text/css";
else if (filename.endsWith(".js"))
return "application/javascript";
else if (filename.endsWith(".ico"))
return "image/x-icon";
else if (filename.endsWith(".gz"))
return "application/x-gzip";
else if (filename.endsWith(".png"))
return "image/png";
else if (filename.endsWith(".svg"))
return "image/svg+xml";
else if (filename.endsWith(".bmp"))
return "image/bmp";
else if (filename.endsWith(".gif"))
return "image/gif";
else if (filename.endsWith(".jpg"))
return "image/jpeg";
else if (filename.endsWith(".jpeg"))
return "image/jpeg";
else if (filename.endsWith(".json"))
return "application/json";
return "text/plain";
}
void setHue(
int hue) { // Set the RGB LED to a given hue (color) (0° = Red, 120° = Green, 240° = Blue)
hue %= 360; // hue is an angle between 0 and 359°
float radH = hue * 3.142 / 180; // Convert degrees to radians
float rf = 0.0;
float gf = 0.0;
float bf = 0.0;
if (hue >= 0 && hue < 120) { // Convert from HSI color space to RGB
rf = cos(radH * 3 / 4);
gf = sin(radH * 3 / 4);
bf = 0;
} else if (hue >= 120 && hue < 240) {
radH -= 2.09439;
gf = cos(radH * 3 / 4);
bf = sin(radH * 3 / 4);
rf = 0;
} else if (hue >= 240 && hue < 360) {
radH -= 4.188787;
bf = cos(radH * 3 / 4);
rf = sin(radH * 3 / 4);
gf = 0;
}
int r = (rf * rf * 1023);
int g = (gf * gf * 1023);
int b = (bf * bf * 1023); // 4.011;
rainbowWeb(r, g, b);
webSocket.broadcastTXT(rainbowColor);
r = map(r, 0, 1023, 255, 0);
g = map(g, 0, 1023, 255, 0);
b = map(b, 0, 1023, 255, 0);
analogWrite(LED_RED, r); // Write the right color to the LED output pins
analogWrite(LED_GREEN, g);
analogWrite(LED_BLUE, b);
// analogWrite(LED_RED, (r/4.011)); // Write the right color to the LED output pins
// analogWrite(LED_GREEN,(g/4.011));
// analogWrite(LED_BLUE,(b/4.011));
// Serial.println("Red:");
// Serial.print(r);
// Serial.println("LED_GREEN:");
// Serial.print(g);
// Serial.println("LED_BLUE:"); /// 4.011;
// Serial.print(b);
}
`
Ok. I see now this is still the v1.1.1 code, which had some bugs in the JavaScript and other places that can cause problems for some browsers. I did a new source release of v1.2.0 that includes all of my recent changes. I thought you were pulling the code from the main branch... I didn't stop to think you might only be working from the download release files... sorry for the confusion. 😁
I also need to update the README with information about how the moon gives some feedback while connecting to the network if STA mode is enabled... while it esp8266 is connecting and acquiring an IP address it will turn blue and brighten and dim quickly... after the network connection is established the previously saved (or default white) color will be restored.
I tested latest release 1.2.0 moon graphics not loading
and LED remains in White color,
Serial monitor log:
WebSocket server started. No valid preference stored at address 128 No hostname saved, using default: moon No valid preference stored at address 164 No admin password set, using default: <*****> OTA service ready. handleFileRead: /hotspot-detect.html File Not Found: /hotspot-detect.html handleFileRead: / Sent file: /index.html size: 1528 handleFileRead: /connecttest.txt File Not Found: /connecttest.txt handleFileRead: / Sent file: /index.html size: 1528 handleFileRead: /_static/main.css Sent file: /_static/main.css size: 3221 handleFileRead: /_static/RGBsocket.js Sent file: /_static/RGBsocket.js size: 5604 handleFileRead: /_static/moon.png Sent file: /_static/moon.png size: 29805 handleFileRead: /_static/moon_32x32.ico Sent file: /_static/moon_32x32.ico size: 766 handleFileRead: /connecttest.txt File Not Found: /connecttest.txt
What happens when you try to change the color? Is there any change, or does it remain white? Are the colors inverted, does selecting white turn if off?
What happens when you try to change the color? Is there any change, or does it remain white? Are the colors inverted, does selecting white turn if off?
when selecting color no change in the LED it remains white, even in Rainbow mode, no response in the LED.
LED never goes off, so it is not inverted.
I tried changing color and captured the log
handleFileRead: /gen_204 File Not Found: /gen_204 handleFileRead: /generate_204 File Not Found: /generate_204 handleFileRead: /gen_204 File Not Found: /gen_204 handleFileRead: / Sent file: /index.html size: 1528 handleFileRead: /_static/main.css Sent file: /_static/main.css size: 3221 handleFileRead: /_static/RGBsocket.js Sent file: /_static/RGBsocket.js size: 5604 handleFileRead: /_static/moon.png Sent file: /_static/moon.png size: 29805 handleFileRead: /_static/moon_32x32.ico Sent file: /_static/moon_32x32.ico size: 766 handleFileRead: /generate_204 File Not Found: /generate_204 handleFileRead: /gen_204 File Not Found: /gen_204 handleFileRead: /generate_204 File Not Found: /generate_204 handleFileRead: /gen_204 File Not Found: /gen_204
Ok... the websocket isn't passing the color data back and forth. Is there any chance that JavaScript is being blocked?
Thank you for being so patient an following up with these issues! I discovered that I introduced a new bug, when I was using a linter to find other bugs in the JavaScript (Obviously not one of my stronger subjects). It helped me find several bugs, (all of them that I could find assume you are running nodejs, not HTML with vanilla JavaScript), but it threw some picky errors (related to use in node
apparently) that I (incorrectly) tried to appease and ended up introducing real errors... I fixed the problem in RGBSocket.js
in the main repo, but have not done a bug fix release yet, I wan to make sure it works on you end, so I don't make another buggy release.
If you don't mind would you test this change, before I do a 1.2.1 release?
Just need to delete a couple lines from the top of RGBSocket.js
...
This is the diff: https://github.com/UncleGrumpy/Moonlight_8266/pull/31/files
So, after confirming the changes in #31 fix this last problem, I think it is safe to close this issue..? If so I will make a 1.2.1 release with the bug fix included, and serial logging back off by default.
Please continue to open new issues if you run into new problems. I added one already about the rainbow mode "quirk". Any if you would like to submit some IR codes for #20, or make a PR for other added features, that would be great.
Thank you again for your patience and feedback, it really helped get this project into a much more polished state.
Thank you for being so patient an following up with these issues! I discovered that I introduced a new bug, when I was using a linter to find other bugs in the JavaScript (Obviously not one of my stronger subjects). It helped me find several bugs, (all of them that I could find assume you are running nodejs, not HTML with vanilla JavaScript), but it threw some picky errors (related to use in
node
apparently) that I (incorrectly) tried to appease and ended up introducing real errors... I fixed the problem inRGBSocket.js
in the main repo, but have not done a bug fix release yet, I wan to make sure it works on you end, so I don't make another buggy release.If you don't mind would you test this change, before I do a 1.2.1 release? Just need to delete a couple lines from the top of
RGBSocket.js
...This is the diff: https://github.com/UncleGrumpy/Moonlight_8266/pull/31/files
I will check this version, and provide my feedback on this
Hi Winford, I have tested with ESP01, code is working reliably, thank you for the efforts to make this project perfect. I am closing this issue.
Hi UncleGrumpy,
Thank you very much for your quick response, now library issue is resolved getting below complilation error.
In member function 'void WebSocketsServer::handleNewClients()': C:\Users\****\OneDrive\Documents\Arduino\libraries\WebSockets\src\WebSocketsServer.cpp:658:86: error: 'class WiFiServer' has no member named 'accept'; did you mean '_accept'? 658 | WEBSOCKETS_NETWORK_CLASS * tcpClient = new WEBSOCKETS_NETWORK_CLASS(_server->accept()); | ^~~~~~ | _accept
In file included from C:\Users**\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\3.0.1\libraries\ESP8266WiFi\src/ESP8266WiFi.h:40, from C:\Users*****\OneDrive\Documents\Arduino\libraries\WebSockets\src\WebSockets.h:172, from C:\Users\\OneDrive\Documents\Arduino\libraries\WebSockets\src\WebSocketsServer.cpp:25: C:\Users*\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\3.0.1\libraries\ESP8266WiFi\src/WiFiServer.h:101:8: note: candidate: 'long int WiFiServer::_accept(tcp_pcb, long int)' 101 | long _accept(tcp_pcb* newpcb, long err); | ^
~~ C:\Users*****\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\3.0.1\libraries\ESP8266WiFi\src/WiFiServer.h:101:8: note: candidate expects 2 arguments, 0 provided`