Arduino
Arduino copied to clipboard
Version 3.0.0 up break my92xx library
Basic Infos
- [x] This issue complies with the issue POLICY doc.
- [x] I have read the documentation at readthedocs and the issue is not addressed there.
- [x] I have tested that the issue is present in current master branch (aka latest git).
- [x] I have searched the issue tracker for a similar issue.
- [x] If there is a stack dump, I have decoded it.
- [x] I have filled out all fields below.
Platform
- Hardware: ESP8266
- Core Version: 3.0.2
- Development Env: Arduino IDE
- Operating System: MacOS
Settings in IDE
- Module: Generic ESP8266 Module
- Flash Mode: dout
- Flash Size: 1MB
- lwip Variant: v2 Lower Memory
- Reset Method: nodemcu
- Flash Frequency: 40Mhz
- CPU Frequency: 80Mhz
- Upload Using: OTA or serial
- Upload Speed: 115200 when serial
Problem Description
Sending code to an Itead SONOFF B1 light bulb, which uses a my9231 chip to control the 5 channels of LED. When I use ESP8266 board definitions 2.7.4, everything works as planned. If I update to 3.0.0 or above, the bulb flashes more or less at random as I adjust values. The brightnesses no longer scale smoothly with the commands, nor go to the proper channels. Seems funny, since the my92xx library isn't using much - only digitalWrites and the c code for os_delay_us. I tried replacing the os_delay_us definition with delayMicroseconds, without any better luck.
MCVE Sketch
#define PROGNAME "ESP8266_B1_bulb"
#define VERSION "1.1.0"
#define VERDATE "2021-12-06"
/*
ESP8266_B1_bulb.ino
2021-02-15
Charles B. Malloch, PhD
Program to control a Sonoff B1 Bulb
Target hardware: ESP8266 on a Sonoff B1 RGB WW CW LED light bulb on Edison base
v0.0.1 2021-02-13 cloned from ESP8266_Sonoff v1.1.1
v1.2.1 2021-02-16 cbm repaired timer
v0.2.4 2021-04-06 cbm ready for production test
v0.2.15 2021-04-27 cbm added veil of indication
v0.3.0 2021-11-29 cbm modified to alternately start up with red or white
v0.3.7 2021-12-02 cbm removed timed auto-reboot, 'cause we don't retain color over
a soft restart. Could, with work, retrieve it from MQTT "status"
v0.4.0 2021-12-02 cbm now fixed; writing and reading soft-restart file
v1.0.0 2021-12-02 cbm calling it production!
v1.1.0 2021-12-06 cbm substituting on-time for reset reason to keep old color
Sonoff B1 Bulb Pinout, CW from CCW, for wiring to an ESP-01 programming adapter:
1 Vcc (3.3) red
2 RX yellow NOTE this is the RX of the ESP, so RX header hole ( next to 3V3 )
3 TX green
4 GND black
5 GPIO0 purple
Use adapter from Sonoff box
Schematic:
https://user-images.githubusercontent.com/17343162/29202638-6b2c39ee-7e6a-11e7-93eb-727d7efaa24c.png
3-color LEDs are controlled by my9231 chip
http://www.bitfracture.com/pages/techarticles/squirrel-esp8266-esp8285-wifi-lightbulb-control-project
has picture of my9231 setup (colors not right, though)
MQTT: can send messages like home/bulb/b01/command -> {"color":[32,0,0,0,0]}
[ R, G, B, WW, CW ]
The current two bulbs are designated b01 (hall), b02 (office)
Plans:
Turn on full warm white, alternately non-max red
thus acting like a little-bit-smart bulb
MQTT changes: send timeout_ms only if testing
send reset, on, off messages
Appears to work using upstairs computer and a05 with VERBOSE set to 12
and looking at MQTT telemetry.
--BUT--
When compiled and sent from pro2, IT DOESN'T WORK RIGHT - the colors are screwed
up. When compiled and sent from Hack, it work(ed) OK.
Checked the my92xx library revisions - identical Hack and pro2.
Testing with board revisions; broken ones are at 3.0.2; office OK at 2.7.x
broken at 3.0.1, trying 3.0.0; still NG
tried using delayMicroseconds instead of os_delay_us; still NG
trying 2.7.4, just prior to 3.0.0: WORKS!!!
*/
#include <ESP8266WiFi.h>
#include <ArduinoJson.h> //https://github.com/bblanchon/ArduinoJson
#include <WiFiClient.h>
#include <PubSubClient.h>
#include <ESP8266WebServer.h>
#include <WebSocketsServer.h>
// mDNS is now furnished by ArduinoOTA
// see https://tttapa.github.io/ESP8266/Chap08%20-%20mDNS.html
// ESP8266mDNS is multicast DNS - responds to <whatever>.local
// #include <ESP8266mDNS.h>
// ESP8266WiFiMulti looks at multiple access points and chooses the strongest
// #include <ESP8266WiFiMulti.h>
#include <WiFiUDP.h>
#include <TimeLib.h>
#include <LittleFS.h>
#define ALLOW_OTA
#ifdef ALLOW_OTA
#include <ArduinoOTA.h>
#endif
#include <my92xx.h>
#include <cbmNetworkInfo.h>
#define SECOND_ms ( 1000UL )
#define MINUTE_ms ( 60UL * SECOND_ms )
#define HOUR_ms ( 60UL * MINUTE_ms )
// R, G, B, WW, CW
#define RED_color 0x80, 0x00, 0x00, 0x00, 0x00
#define WHITE_color 0x00, 0x00, 0x00, 0x80, 0x80
// ***************************************
// ***************************************
#pragma mark -> vars MAIN PARAMETERS
#define mqtt_baseTopic "home/bulb"
#define BAUDRATE 115200
#define VERBOSE 2
// auto reboot will have side effect of turning bulb off! <- now fixed
const unsigned long rebootInterval_ms = 24UL * HOUR_ms;
#define TESTING 0
// ***************************************
// ***************************************
/********************************** GPIO **************************************/
#pragma mark -> vars GPIO
/*
I2C cbm standard colors: yellow for SDA, blue for SCL
Blue onboard LED is inverse logic, connected as:
ESP-01:
GPIO0 for SDA
GPIO1 ( TX ) is blue, inverse logic
GPIO2 (next to GND) for SCL; 1K pullup on my boards
GPIO3 ( RX ) is red
Adafruit Huzzah:
GPIO0 is red
GPIO2 is blue, inverse logic
GPIO4 is SDA; GPIO5 is SCL
Amica NodeMCU
GPIO4 (D2) is SDA; GPIO5 (D1) is SCL
GPIO16 (D0) is red but this is usually used to drive reset
LOW to pin RST (CH_PD / chip_enable)
blue is GPIO1 TX conflicts with the use of Serial
WeMos-WROOM-02: D16
Sonoff
pagoda:
GPIO4 is red (after board modification) (inverse logic, hardware PWM)
GPIO13 is green (inverse logic, no PWM)
switch block:
red LED associated with relay
GPIO13 is blue (inverse logic, PWM)
GPIO0 is the pushbutton (has pullup resistor)
GPIO12 is the relay output
GPIO14 is J1 Pin 5 (not used in this program)
ITEAD Sonoff S31:
GPIO0 is the pushbutton (has pullup resistor)
GPIO12 is the relay output
GPIO13 is green
Sonoff Bulb
GPIO12 (SDA) and GPIO14 (SCL) to two cascaded MY9231
controlling Blue Red Green | Warm NC Cool
*/
#define INVERSE_LOGIC_ON 0
#define INVERSE_LOGIC_OFF 1
const int pd_LED_white = 4; // hardware PWM
/********************************* Network ************************************/
#pragma mark -> vars Network
// locales include M5, CBMIoT, CBMDATACOL, CBMDDWRT, CBMDDWRT3, CBMDDWRT3GUEST,
// CBMBELKIN, CBM_RASPI_MOSQUITTO
// need to use CBMDDWRT3 for my own network access
// can use CBMIoT or CBMDDWRT3GUEST for Sparkfun etc.
#define WIFI_LOCALE CBMDATACOL
const unsigned int port_UDP = 9250;
/*
The cbmNetworkInfo object has (at least) the following instance variables:
.ip
.gateway
.mask
.ssid
.password
.chipID ( GUID assigned by manufacturer, often the last three octets
of the MAC address, e.g. 5c:cf:7f:xx:xx:xx )
.chipName ( globally-unique name assigned by cbm and marked on PCB
*/
cbmNetworkInfo Network;
// Create an ESP8266 WiFiClient class to connect to the MQTT server.
WiFiClient conn_TCP;
WiFiUDP conn_UDP;
PubSubClient conn_MQTT ( conn_TCP );
ESP8266WebServer htmlServer ( 80 );
WebSocketsServer webSocket = WebSocketsServer(81);
/************************************ mDNS ************************************/
#pragma mark -> vars mDNS
const int mdnsOtaIdLen = 40;
char mdnsOtaId [ mdnsOtaIdLen ];
/************************************ MQTT ************************************/
#pragma mark -> vars MQTT
const int mqttClientIDLen = 50;
char mqtt_clientID [ mqttClientIDLen ] = "whatever";
const int mqttTopicLen = 50;
char mqttCmdTopic [ mqttTopicLen ];
char mqttStatusTopic [ mqttTopicLen ];
char mqttTimeoutTopic [ mqttTopicLen ];
char mqttResetReasonTopic [ mqttTopicLen ];
/********************************** NTP ***************************************/
#pragma mark -> vars NTP
IPAddress timeServer ( 17, 253, 14, 253 );
const char* NTPServerName = "time.apple.com";
const int NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 48 bytes of the message
byte NTPBuffer [NTP_PACKET_SIZE]; // buffer to hold incoming and outgoing packets
const int timeZone = 0; // GMT
// const int timeZone = -5; // Eastern Standard Time (USA)
// const int timeZone = -4; // Eastern Daylight Time (USA)
unsigned long lastNTPResponseAt_ms = millis();
// uint32_t time_unix_s = 0;
bool ntpTimeGoodP = false;
/********************************** bulb **************************************/
#pragma mark -> vars bulb
#define MY92XX_MODEL MY92XX_MODEL_MY9231
#define MY92XX_CHIPS 2
#define MY92XX_DI_PIN 12
#define MY92XX_DCKI_PIN 14
#define MY92XX_COLD 0
#define MY92XX_WARM 1
#define MY92XX_RED 4
#define MY92XX_GREEN 3
#define MY92XX_BLUE 5
my92xx * _my92xx;
byte currentR, currentG, currentB, currentW, currentC;
// output states: 0 -> off; 1 -> on; 2 -> on timer;
int outputState = 0;
const char * outputStateString [3] = { "OFF", "ON", "TIMER" };
#if TESTING
const unsigned long turnOffTimerDuration_ms = 6UL * SECOND_ms;
#else
const unsigned long turnOffTimerDuration_ms = 30UL * MINUTE_ms;
#endif
unsigned long timerStartedAt_ms = 0UL;
unsigned long timeoutValue_ms = 0UL;
unsigned long settingsDirtyAt_ms = 0UL;
/******************************* Global Vars **********************************/
#pragma mark -> vars global
char uniqueToken [ 9 ];
unsigned long lastRebootAt_nts = 0UL;
const size_t pBufLen = 128;
char pBuf [ pBufLen ];
const int htmlMessageLen = 3072;
char htmlMessage [htmlMessageLen];
const int timeStringLen = 32;
char timeString [ timeStringLen ] = "<unset>";
char bootTimeString [ timeStringLen ] = "<unset>";
const size_t jsonStrSize = 1000;
char jsonString [ jsonStrSize ]; // needs to be global to be on heap!
bool forceWSUpdate = false;
/**************************** Function Prototypes *****************************/
#pragma mark -> function prototypes
void POST ();
void initializeGPIO ();
void initializeBulb ();
void initializeLittleFS ();
void initializeLampColor ();
void lampColorInitialize_powercycle ();
void lampColorInitialize_restorePrevious ();
void initializeWebServer ();
bool connect_WiFi ();
bool connect_MQTT ();
void handleReceivedMQTTMessage ( char * topic, byte * payload, unsigned int length );
void sendSettingsToMQTT ( bool force = false );
int sendValueToMQTT ( const char * topic, int value, const char * name, bool retainP = false );
int sendValueToMQTT ( const char * topic, const char * value, const char * name, bool retainP = false );
void interpretNewCommandString ( char * theTopic, char * thePayload );
void htmlResponse_root ();
void webSocketEvent ( uint8_t num, WStype_t type, uint8_t * payload, size_t length );
void update_WebSocket ();
time_t getUnixTime();
void sendNTPpacket ( IPAddress& address );
void formatTimeString ( char * result, int resultLen, unsigned long time );
void formatIntervalString ( char * result, int resultLen, unsigned long time );
void saveSettingsToFile ();
int setOutputState ( int state, bool force = false );
void sendOutput ();
void setOutput ( byte R, byte G, byte B, byte W, byte C );
void toggleOutput ();
void setTimer ( unsigned long p_timeoutValue_ms = turnOffTimerDuration_ms );
unsigned long timeRemaining_ms ();
// magic juju to return array size
// see http://forum.arduino.cc/index.php?topic=157398.0
template< typename T, size_t N > size_t ArraySize (T (&) [N]){ return N; }
/******************************************************************************/
void setup ( void ) {
if ( VERBOSE >= 12 ) delay ( 10000 );
initializeGPIO ();
initializeBulb ();
#if TESTING
Serial.printf ( "\n\nTESTING mode %d\n", TESTING );
POST();
#endif
/*********************** LittleFS File System Setup *************************/
initializeLittleFS ();
lampColorInitialize_restorePrevious ();
/****************************** WiFi Setup **********************************/
// for security reasons, the network settings are stored in a private library
Network.init ( WIFI_LOCALE );
if ( ! strncmp ( Network.chipName, "unknown", 12 ) ) {
Network.describeESP ( Network.chipName );
}
// Connect to WiFi access point.
Serial.printf ( "\nESP8266 device '%s' connecting to %s\n", Network.chipName, Network.ssid );
yield ();
WiFi.config ( Network.ip, Network.gw, Network.mask, Network.dns );
// for exception 3 problem, use erase wifi parameters once
WiFi.begin ( Network.ssid, Network.password );
Serial.println ( F ( "about to connect_WiFi" ) );
connect_WiFi ();
// while ( WiFi.status() != WL_CONNECTED ) {
// Serial.print ( F ( "v" ) );
// delay ( 500 ); // implicitly yields but may not pet the nice doggy
// }
// Serial.println ();
if ( VERBOSE >= 15 ) WiFi.printDiag ( Serial );
if ( VERBOSE >= 4 ) {
Serial.println ( F ( "WiFi connected with IP address: " ) );
Serial.println ( WiFi.localIP() );
}
/************************** Random Token Setup ******************************/
// create a hopefully-unique string to identify this program instance
// REQUIREMENT: must have initialized the network for chipName
#ifdef cbmnetworkinfo_h
// Chuck-only
strncpy ( uniqueToken, Network.chipName, 9 );
#else
snprintf ( uniqueToken, 9, "%08x", ESP.getChipId() );
#endif
snprintf ( mdnsOtaId, mdnsOtaIdLen, "esp8266-%s", uniqueToken );
Serial.printf ( "mDNS host name: %s.local\n", mdnsOtaId );
/******************************* UDP Setup **********************************/
conn_UDP.begin ( port_UDP );
if ( VERBOSE >= 4 ) Serial.println ( F ( "UDP connected" ) );
delay ( 50 );
/****************************** MQTT Setup **********************************/
snprintf ( mqtt_clientID, mqttClientIDLen, "%s_%s", PROGNAME, uniqueToken );
snprintf ( mqttCmdTopic, mqttTopicLen, "%s/%s/%s", mqtt_baseTopic, uniqueToken, "command" );
snprintf ( mqttStatusTopic, mqttTopicLen, "%s/%s/%s", mqtt_baseTopic, uniqueToken, "status" );
snprintf ( mqttTimeoutTopic, mqttTopicLen, "%s/%s/%s", mqtt_baseTopic, uniqueToken, "timeout_ms" );
snprintf ( mqttResetReasonTopic, mqttTopicLen, "%s/%s/%s", mqtt_baseTopic, uniqueToken, "reset_reason" );
conn_MQTT.setCallback ( handleReceivedMQTTMessage );
/*
// testing because it wouldn't find the DNS -- it needs the Network.dns arg
// when we connect!!! See above:
// WiFi.config ( Network.ip, Network.gw, Network.mask, Network.dns );
IPAddress mosq;
WiFi.hostByName( CBM_MQTT_SERVER, mosq );
Serial.print ( F ( "Here's what we found for the IP of '" ) );
Serial.print ( CBM_MQTT_SERVER );
Serial.print ( F ( "': " ) );
Serial.println ( mosq );
*/
conn_MQTT.setServer ( CBM_MQTT_SERVER, CBM_MQTT_SERVERPORT );
if ( VERBOSE >= 5 ) {
Serial.print ( F ( "Connecting to MQTT at " ) );
Serial.println ( CBM_MQTT_SERVER );
}
connect_MQTT ();
yield ();
/******************************* NTP Setup **********************************/
setSyncProvider ( getUnixTime );
setSyncInterval ( 300 ); // seconds
unsigned long beganWaitingAt_ms = millis();
while ( ( timeStatus() != timeSet ) && ( ( millis () - beganWaitingAt_ms ) < 10000UL ) ) {
now ();
Serial.print ( F ( "t" ) );
delay ( 100 );
yield();
}
snprintf ( pBuf, pBufLen, "%s/%s/time/startup", PROGNAME, uniqueToken );
if ( timeStatus() == timeSet ) {
lastRebootAt_nts = now();
formatTimeString ( bootTimeString, timeStringLen, now () );
sendValueToMQTT ( pBuf, bootTimeString, "Startup Time" );
Serial.printf ( "Starting at %s\n", bootTimeString );
} else {
Serial.println ( F ( "WARNING: No NTP response within 10 seconds..." ) );
sendValueToMQTT ( pBuf, "WARNING no NTP within 10 seconds", "Startup Time" );
}
yield();
/***************************** OTA Update Setup *****************************/
#ifdef ALLOW_OTA
// Port defaults to 8266
// ArduinoOTA.setPort(8266);
// Hostname defaults to esp8266-[ChipID]
ArduinoOTA.setHostname ( (const char *) mdnsOtaId );
// No authentication by default
ArduinoOTA.setPassword ( (const char *) CBM_OTA_KEY );
ArduinoOTA.onStart([]() {
Serial.println ( F ( "Start" ) );
});
ArduinoOTA.onEnd([]() {
Serial.println ( F ( "\nEnd" ) );
});
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
Serial.printf ( "Progress: %u%%\r", ( progress / ( total / 100 ) ) );
});
ArduinoOTA.onError([](ota_error_t error) {
Serial.printf("Error[%u]: ", error);
if (error == OTA_AUTH_ERROR) Serial.println ( F ( "Auth Failed" ) );
else if (error == OTA_BEGIN_ERROR) Serial.println ( F ( "Begin Failed" ) );
else if (error == OTA_CONNECT_ERROR) Serial.println ( F ( "Connect Failed" ) );
else if (error == OTA_RECEIVE_ERROR) Serial.println ( F ( "Receive Failed" ) );
else if (error == OTA_END_ERROR) Serial.println ( F ( "End Failed" ) );
});
ArduinoOTA.begin();
Serial.println ( F ( "ArduinoOTA running" ) );
yield();
#endif
/**************************** HTML Server Setup *****************************/
initializeWebServer ();
// /**************************** mDNS Server Setup *****************************/
//
// snprintf ( mdnsOtaId, mdnsOtaIdLen, "%s_%s", PROGNAME, uniqueToken );
// if (!MDNS.begin ( mdnsOtaId ) ) { // Start the mDNS responder for <PROGNAME>.local
// Serial.println ( F ( "Error setting up MDNS responder!" ) );
// }
// Serial.printf ( "mDNS responder '%s.local' started", mdnsOtaId );
/************************* Power-on Color Setting ***************************/
initializeLampColor ();
/************************** Report successful init **************************/
Serial.printf ( "\n%s v%s %s cbm", PROGNAME, VERSION, VERDATE );
#ifdef TESTING
Serial.printf ( " TESTING level %d\n", TESTING );
#else
Serial.println ();
#endif
}
void loop ( void ) {
const unsigned long wsSendInterval_ms = 5UL * SECOND_ms;
static unsigned long lastWSSendAt_ms = 0UL;
/****************************************************************************/
if ( ! connect_WiFi () ) return;
yield();
#ifdef ALLOW_OTA
ArduinoOTA.handle();
yield ();
#endif
/****************************************************************************/
// Ensure the connection to the MQTT server is alive (this will make the first
// connection and automatically reconnect when disconnected). See the MQTT_connect
// function definition below.
if ( ! connect_MQTT () ) return;
conn_MQTT.loop();
delay ( 10 ); // fix some issues with WiFi stability
yield();
/****************************************************************************/
htmlServer.handleClient();
webSocket.loop();
yield ();
/****************************************************************************/
if ( ( forceWSUpdate )
|| ( lastWSSendAt_ms == 0UL ) // initial update
|| ( ( millis() - lastWSSendAt_ms ) > wsSendInterval_ms ) // regular update
|| ( timerStartedAt_ms && ( ( millis() - lastWSSendAt_ms ) > 1000UL ) ) // update faster while counting down
) {
if ( ( VERBOSE >= 15 ) && forceWSUpdate ) Serial.println ( F ( "forced WS update" ) );
// dtostrf ( temperature_degC, 0, 1, fBuf );
// snprintf ( pBuf, pBufLen, "%s/%s", "raw/temperature_degC/air", sensorId );
// sendValueToMQTT ( pBuf, fBuf, "Temperature" );
if ( timerStartedAt_ms ) {
// update if clock is ticking
sendValueToMQTT ( mqttTimeoutTopic, timeRemaining_ms(), "time remaining" );
}
update_WebSocket ();
lastWSSendAt_ms = millis();
}
yield ();
// if settings have changed, send them to MQTT?
// if changed by web page, yes
// if changed by power-on, yes
// if soft reset, it screws up the settings?
sendSettingsToMQTT ();
/****************************************************************************/
if ( rebootInterval_ms && ( millis() > rebootInterval_ms ) ) {
snprintf ( pBuf, pBufLen, "%s/%s/time/restart", PROGNAME, uniqueToken );
sendValueToMQTT ( pBuf, bootTimeString, "Restart Time" );
Serial.printf ( "Rebooting at %s\n", bootTimeString );
delay ( 1000 );
ESP.restart(); // soft reset; ESP.reset() is a hard reset leaving regs unknown...
}
if ( outputState ) {
// on
if ( timerStartedAt_ms != 0UL ) {
// timer is running
if ( timeRemaining_ms() == 0UL ) {
// timed out
Serial.printf ( "Timeout!\n" );
setOutputState ( 0 );
timerStartedAt_ms = 0UL;
}
}
}
/****************************************************************************/
yield ();
}
// *****************************************************************************
// ***************************** Initializations *******************************
// *****************************************************************************
void POST () {
Serial.print ( F ( "\n\nPOST\n\n" ) );
int oldVal = setOutputState ( 1 );
sendOutput ( 0x00, 0x00, 0x00, 0x00, 0x00 );
delay ( 100 );
sendOutput ( 0x80, 0x00, 0x00, 0x00, 0x00 );
delay ( 100 );
sendOutput ( 0xff, 0x00, 0x00, 0x00, 0x00 );
delay ( 250 );
sendOutput ( 0x00, 0x00, 0x00, 0x00, 0x00 );
delay ( 10 ); yield();
sendOutput ( 0x00, 0x80, 0x00, 0x00, 0x00 );
delay ( 100 );
sendOutput ( 0x00, 0xff, 0x00, 0x00, 0x00 );
delay ( 250 );
sendOutput ( 0x00, 0x00, 0x00, 0x00, 0x00 );
delay ( 10 ); yield();
sendOutput ( 0x00, 0x00, 0x80, 0x00, 0x00 );
delay ( 100 );
sendOutput ( 0x00, 0x00, 0xff, 0x00, 0x00 );
delay ( 250 );
sendOutput ( 0x00, 0x00, 0x00, 0x00, 0x00 );
delay ( 10 ); yield();
sendOutput ( 0x00, 0x00, 0x00, 0x80, 0x00 );
delay ( 100 );
sendOutput ( 0x00, 0x00, 0x00, 0xff, 0x00 );
delay ( 250 );
sendOutput ( 0x00, 0x00, 0x00, 0x00, 0x00 );
delay ( 10 ); yield();
sendOutput ( 0x00, 0x00, 0x00, 0x00, 0x80 );
delay ( 100 );
sendOutput ( 0x00, 0x00, 0x00, 0x00, 0xff );
delay ( 250 );
sendOutput ( 0x00, 0x00, 0x00, 0x00, 0x00 );
for ( int i = 0; i < 2 * 5; i++ ) {
for ( int j = 0; j < 256; j += 10 ) {
int b = ( i % 2 ) ? 255 - j : j;
sendOutput ( i * 20, 0x00, 0x00, j, j );
delay ( 20 );
}
}
sendOutput ( 0x00, 0x00, 0x00, 0x80, 0x80 );
for ( int i = 0; i < 5; i++ ) {
setOutputState ( 1 );
delay ( 100 );
setOutputState ( 0 );
delay ( 100 );
}
// restore bulb settings
setOutput ( currentR, currentG, currentB, currentW, currentC );
setOutputState ( oldVal );
yield();
}
void initializeGPIO () {
/*
pinMode ( pd_LED_green, OUTPUT ); digitalWrite ( pd_LED_green, INVERSE_LOGIC_OFF );
pinMode ( pd_LED_red, OUTPUT ); digitalWrite ( pd_LED_red, INVERSE_LOGIC_OFF );
pinMode ( pdOutput, OUTPUT ); digitalWrite ( pdOutput, LOW );
pinMode ( pdC5, OUTPUT ); digitalWrite ( pdC5, LOW );
pinMode ( pdButton, INPUT ); // has built-in pullup
*/
Serial.begin ( BAUDRATE );
while ( ! Serial && ( millis() < 2000 ) ) {
// int newStatus = 1 - digitalRead ( pd_LED_red );
// digitalWrite ( pd_LED_green, newStatus );
// digitalWrite ( pd_LED_red, newStatus );
// #ifdef TESTING
// digitalWrite ( pdOutput, newStatus );
// #endif
delay ( 200 ); // wait a little more, if necessary, for serial to come up
yield ();
}
if ( VERBOSE >= 10 ) while ( ! Serial && ( millis() < 20000 ) );
Serial.print ( F ( "\n\n" ) );
// digitalWrite ( pd_LED_green, INVERSE_LOGIC_OFF );
// digitalWrite ( pd_LED_red, INVERSE_LOGIC_OFF );
}
void initializeBulb () {
// _my92xx = new my92xx ( MY92XX_MODEL, MY92XX_CHIPS,
// MY92XX_DI_PIN, MY92XX_DCKI_PIN,
// MY92XX_COMMAND_DEFAULT );
/*
freq divide 4 breaks
one shot enable no diff
bit width 16 breaks
cmd reaction slow breaks
*/
_my92xx = new my92xx ( MY92XX_MODEL, MY92XX_CHIPS,
MY92XX_DI_PIN, MY92XX_DCKI_PIN,
{ \
.scatter = MY92XX_CMD_SCATTER_APDM, \
.frequency = MY92XX_CMD_FREQUENCY_DIVIDE_1, \
.bit_width = MY92XX_CMD_BIT_WIDTH_8, \
.reaction = MY92XX_CMD_REACTION_FAST, \
.one_shot = MY92XX_CMD_ONE_SHOT_DISABLE, \
.resv = 0 \
}
);
_my92xx->setState(true);
currentR = 0;
currentG = 0;
currentB = 0;
currentW = 0;
currentC = 0;
}
void initializeLittleFS () {
if ( ! LittleFS.begin() ) {
Serial.println ( F ( "INFO: LittleFS.begin() failed" ) );
Serial.print ( F (" formatting...") );
if ( LittleFS.format() ) {
Serial.println ( F (" ok") );
} else {
Serial.println ( F (" FAILED") );
}
}
}
void initializeLampColor () {
// enum rst_reason {
// REASON_DEFAULT_RST = 0, // normal startup by power on
// REASON_WDT_RST = 1, // hardware watch dog reset
// REASON_EXCEPTION_RST = 2, // exception reset, GPIO status won’t change
// REASON_SOFT_WDT_RST = 3, // software watch dog reset, GPIO status won’t change
// REASON_SOFT_RESTART = 4, // software restart ,system_restart , GPIO status won’t change
// REASON_DEEP_SLEEP_AWAKE = 5, // wake up from deep-sleep
// REASON_EXT_SYS_RST = 6 // external system reset
// };
//
// char * reasons [ ] = { "REASON_DEFAULT",
// "REASON_WDT",
// "REASON_EXCEPTION",
// "REASON_SOFT_WDT",
// "REASON_SOFT_RESTART",
// "REASON_DEEP_SLEEP_AWAKE",
// "REASON_EXT_SYS_RST "
// };
//
//
// // byte reset_reason_0 = rtc_get_reset_reason ( 0 );
// // Serial.print ( F ( "Reset reason 0: " ) ); Serial.println ( reset_reason_0, HEX );
// // byte reset_reason_1 = rtc_get_reset_reason ( 1 );
// // Serial.print ( F ( "Reset reason 1: " ) ); Serial.println ( reset_reason_1, HEX );
// rst_info *resetInfo;
// resetInfo = ESP.getResetInfoPtr();
// byte reset_reason = resetInfo->reason;
//
// Serial.print ( F ( "Reset reason: " ) ); Serial.println ( reasons [ reset_reason ] );
// sendValueToMQTT ( mqttResetReasonTopic, reasons [ reset_reason ], "Reset reason", true );
// calculate time since previous power-on
// read previous power-on time
// in UNIX time, seconds since 12:00 AM Jan 1, 1970 as time_t
// note: time_t is defined as unsigned long
while ( ! ntpTimeGoodP && ( millis() < 30000UL ) ) {
Serial.println ( F ( "Trying to get a goot time from NTP" ) );
getUnixTime ();
delay ( 5000 );
}
time_t interval_secs = 1000000UL;
if ( ntpTimeGoodP ) {
File f;
f = LittleFS.open ( "/start_UNIX_time.txt", "r" );
if ( VERBOSE >= 4 ) Serial.printf ( "INFO: LittleFS file%s opened\n", f ? "" : " not" );
unsigned long lastStartAt_unixtime;
if ( f ) {
// file will contain one line, with either "white" or "red"
// to identify the color it will next start with
lastStartAt_unixtime = f.parseInt ();
f.close();
if ( VERBOSE >= 5 ) {
Serial.print ( F ( " ... file contained: " ) );
Serial.println ( lastStartAt_unixtime );
}
} else {
Serial.println ( F ( "WARNING: start_UNIX_time.txt file not available" ) );
}
// now rewrite that file with the current time
f = LittleFS.open ( "/start_UNIX_time.txt", "w" );
f.print ( now() );
f.close();
interval_secs = now() - lastStartAt_unixtime;
if ( VERBOSE >= 3 ) {
Serial.print ( F ( "Seconds since last startup: " ) );
Serial.println ( interval_secs );
}
} else {
// don't have a good time from NTP
}
// if ( true || reset_reason == ESP_RST_POWERON ) {
// if ( ( reset_reason == REASON_DEFAULT_RST )
// || ( reset_reason == REASON_EXT_SYS_RST ) ) {
if ( interval_secs < 60UL ) {
lampColorInitialize_powercycle ();
} else {
// reboot interval restart -- probably leave settings to what's in MQTT
// but MQTT doesn't provide a "color" command - colors individually stored, under "status"
// so no "command"
lampColorInitialize_restorePrevious ();
}
sendSettingsToMQTT ( 1 );
}
void lampColorInitialize_powercycle () {
File f;
// power on, but not soft reset
f = LittleFS.open ( "/power_on_color.txt", "r" );
Serial.printf ( "INFO: LittleFS file%s opened\n", f ? "" : " not" );
#define colorStringLen 10
char colorString [ colorStringLen ];
if ( f && f.available() ) {
// file will contain one line, with either "white" or "red"
// to identify the color it will next start with
f.readString().toCharArray( colorString, colorStringLen );
f.close();
if ( VERBOSE >= 5 ) {
Serial.print ( F ( " ... file contained: " ) );
Serial.println ( colorString );
}
} else {
Serial.println ( F ( "WARNING: power_on_color.txt file not available" ) );
}
// regardless of read status, try to write a file
f = LittleFS.open ( "/power_on_color.txt", "w" );
if ( ! strncmp ( colorString, "red", 6 ) ) {
if ( VERBOSE >= 5 ) Serial.println ( F ( "was red; make white" ) );
f.print ( F ( "white" ) );
setOutput ( WHITE_color ); // white
} else {
if ( VERBOSE >= 5 ) Serial.println ( F ( "was white; make red" ) );
f.print ( F ( "red" ) );
setOutput ( RED_color ); // red
}
f.close();
setOutputState ( 1 );
}
void lampColorInitialize_restorePrevious () {
File f;
// soft reset
f = LittleFS.open ( "/soft_reset_color.txt", "r" );
Serial.printf ( "INFO: LittleFS file%s opened\n", f ? "" : " not" );
int stateVector [ 6 ] = { -1, -1, -1, -1, -1, -1 } ;
if ( f && f.available() ) {
/*
file will contain a comma-separated list of 6 items:
5 color values
1 0-or-1 "on" value
*/
Serial.print ( F ( " ... file contained: " ) );
for ( int i = 0; i < 6; i++ ) {
stateVector [ i ] = f.parseInt();
Serial.print ( stateVector [ i ] );
Serial.print ( ( i < 5 ) ? ", " : "\n" );
}
f.close();
// set stateVector if we got a valid line
if ( stateVector [ 5 ] >= 0 ) {
// these values should never all be zero
setOutput ( stateVector [ 0 ],
stateVector [ 1 ],
stateVector [ 2 ],
stateVector [ 3 ],
stateVector [ 4 ] );
// this value will be zero of lamp was off
setOutputState ( stateVector [ 5 ], 1 );
} else {
Serial.println ( F ( "Incomplete data from soft-restart file" ) );
}
} else {
Serial.println ( F ( "WARNING: soft_reset_color.txt file not available" ) );
}
}
void initializeWebServer () {
/**************************** HTML Server Setup *****************************/
// for html, do the on's before server begin
htmlServer.on ( "/", htmlResponse_root );
htmlServer.onNotFound([](){
htmlServer.send(404, "text/plain", "404: Not found");
});
htmlServer.begin();
webSocket.begin();
webSocket.onEvent ( webSocketEvent );
// the below are applicable to a WebSocket CLIENT...
// webSocket.setReconnectInterval ( 15000 ); // ms to reconnect after failure
// ms; <ping interval> <response timeout> <disconnect if n failures>
// webSocket.enableHeartbeat ( 30000, 3000, 2 );
}
// *****************************************************************************
// ********************************** WiFi *************************************
// *****************************************************************************
bool connect_WiFi () {
if ( WiFi.status() == WL_CONNECTED ) return true;
const unsigned long oldConnectionTimeout_ms = 10UL * SECOND_ms;
unsigned long startTime_ms = millis();
unsigned long lastDotAt_ms = millis();
unsigned long lastBlinkAt_ms = millis();
static bool newConnection = true;
const int bufLen = 25;
char strBuf [ bufLen ];
bool printed = false;
sendOutput ( 0x00, 0x00, 0x40, 0x00, 0x00 ); // blue while connecting
while ( WiFi.status() != WL_CONNECTED ) {
if ( VERBOSE > 4 && ! printed ) {
Serial.print ( F ( "\nChecking wifi..." ) );
printed = true;
newConnection = true;
}
if ( ( millis() - lastDotAt_ms ) > 1000 ) {
Serial.print ( F ( "." ) );
lastDotAt_ms = millis();
}
if ( ( millis() - startTime_ms ) > oldConnectionTimeout_ms ) {
Serial.printf ( "About to smart config...\n" );
WiFi.beginSmartConfig();
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print ( WiFi.smartConfigDone() );
}
Serial.printf ( "Done smart config.\n" );
}
delay ( 1000 );
}
if ( newConnection && VERBOSE > 4 ) {
Serial.print ( F ( "\n WiFi connected as " ) );
Serial.print ( WiFi.localIP() );
Serial.print ( F ( " in " ) );
Serial.print ( millis() - startTime_ms );
Serial.println ( F ( " ms" ) );
}
startTime_ms = millis();
printed = false;
newConnection = false;
sendOutput ( currentR, currentG, currentB, currentW, currentC ); // restore
return true;
}
bool connect_MQTT () {
if ( conn_MQTT.connected() ) return true;
unsigned long startTime_ms = millis();
unsigned long lastDotAt_ms = millis();
unsigned long lastBlinkAt_ms = millis();
static bool newConnection = true;
const int bufLen = 25;
char strBuf [ bufLen ];
bool printed = false;
sendOutput ( 0x40, 0x00, 0x40, 0x00, 0x00 ); // violet while connecting
while ( ! conn_MQTT.connected () ) {
conn_MQTT.connect ( mqtt_clientID );
if ( VERBOSE > 4 && ! printed ) {
Serial.print ( F ( " Connecting to MQTT as '" ) ); Serial.print ( mqtt_clientID );
Serial.print ( F ( "'; status: " ) );
Serial.print ( conn_MQTT.state () );
Serial.print ( F ( "..." ) );
/*
-4 : MQTT_CONNECTION_TIMEOUT - the server didn't respond within the keepalive time
-3 : MQTT_CONNECTION_LOST - the network connection was broken
-2 : MQTT_CONNECT_FAILED - the network connection failed - perhaps the IP is bad!!!
-1 : MQTT_DISCONNECTED - the client is disconnected cleanly
0 : MQTT_CONNECTED - the cient is connected
1 : MQTT_CONNECT_BAD_PROTOCOL - the server doesn't support the requested version of MQTT
2 : MQTT_CONNECT_BAD_CLIENT_ID - the server rejected the client identifier
3 : MQTT_CONNECT_UNAVAILABLE - the server was unable to accept the connection
4 : MQTT_CONNECT_BAD_CREDENTIALS - the username/password were rejected
5 : MQTT_CONNECT_UNAUTHORIZED - the client was not authorized to connect_WiFi
*/
printed = true;
newConnection = true;
}
if ( ( millis() - lastDotAt_ms ) > 1000 ) {
Serial.print ( F ( "." ) );
lastDotAt_ms = millis();
}
delay ( 50 );
}
if ( newConnection ) {
if ( VERBOSE > 4 ) {
Serial.print ( F ( "\n MQTT connected in " ) );
Serial.print ( millis() - startTime_ms );
Serial.println ( F ( " ms" ) );
}
conn_MQTT.subscribe ( mqttCmdTopic, 1 ); // topic[, QoS]
// it looks like trying to subscribe with QoS 2 silently fails!
if ( VERBOSE > 4 ) {
Serial.print ( F ( " Subscribed to MQTT feed '" ) );
Serial.print ( mqttCmdTopic );
Serial.println ( F ( "'" ) );
}
}
// client.unsubscribe("/example");
newConnection = false;
sendOutput ( currentR, currentG, currentB, currentW, currentC ); // restore
return true;
}
// *****************************************************************************
// ********************************** MQTT *************************************
// *****************************************************************************
void sendSettingsToMQTT ( bool force ) {
const unsigned long sendSettingsDelay_ms = 2UL * SECOND_ms;
static byte oldR = 0x00;
static byte oldG = 0x00;
static byte oldB = 0x00;
static byte oldW = 0x00;
static byte oldC = 0x00;
static int oldState = -99;
if ( force
|| ( settingsDirtyAt_ms
&& ( millis() - settingsDirtyAt_ms ) > sendSettingsDelay_ms
)
) {
if ( currentR != oldR ) {
snprintf ( pBuf, pBufLen, "%s/%s", mqttStatusTopic, "red" );
sendValueToMQTT ( pBuf, currentR, "red", true );
oldR = currentR;
}
if ( currentG != oldG ) {
snprintf ( pBuf, pBufLen, "%s/%s", mqttStatusTopic, "green" );
sendValueToMQTT ( pBuf, currentG, "green", true );
oldG = currentG;
}
if ( currentB != oldB ) {
snprintf ( pBuf, pBufLen, "%s/%s", mqttStatusTopic, "blue" );
sendValueToMQTT ( pBuf, currentB, "blue", true );
oldB = currentB;
}
if ( currentW != oldW ) {
snprintf ( pBuf, pBufLen, "%s/%s", mqttStatusTopic, "warm" );
sendValueToMQTT ( pBuf, currentW, "warm", true );
oldW = currentW;
}
if ( currentC != oldC ) {
snprintf ( pBuf, pBufLen, "%s/%s", mqttStatusTopic, "cool" );
sendValueToMQTT ( pBuf, currentC, "cool", true );
oldC = currentC;
}
if ( outputState != oldState ) {
snprintf ( pBuf, pBufLen, "%s/%s", mqttStatusTopic, "state" );
sendValueToMQTT ( pBuf, outputState, "Output state", true );
oldState = outputState;
}
saveSettingsToFile ();
settingsDirtyAt_ms = 0UL;
}
}
int sendValueToMQTT ( const char * topic, int value, const char * name, bool retainP ) {
const int valLen = 10;
char val [ valLen ];
snprintf ( val, valLen, "%d", value );
return sendValueToMQTT ( topic, val, name, retainP );
}
int sendValueToMQTT ( const char * topic, const char * value, const char * name, bool retainP ) {
/*
Send data via MQTT to Mosquitto
*/
int tLen = strlen ( topic );
int vLen = strlen ( value );
if ( vLen < 1 ) {
if ( VERBOSE >= 10 ) {
Serial.print ( F ( "\nNot sending null payload " ) );
Serial.println ( name );
}
return 0;
}
if ( ( tLen + vLen ) > MQTT_MAX_PACKET_SIZE ) {
if ( VERBOSE >= 10 ) {
Serial.print ( F ( "\nNot sending oversized ( " ) );
Serial.print ( tLen + vLen );
Serial.print ( F ( " > 128 ) packet " ) );
Serial.println ( name );
}
return 0;
}
if ( VERBOSE >= 10 ) {
Serial.print ( F ( "Sending " ) );
Serial.print ( topic );
Serial.print ( F ( " ( name = " ) );
Serial.print ( name );
Serial.print ( F ( ", len = " ) );
Serial.print ( tLen + vLen );
Serial.print ( F ( " ): " ) );
Serial.print ( value );
Serial.print ( F ( " ... " ) );
}
/*
Note: each publish call seems to take about a second
Even though connection remains good
But only when the MQTT server is overtaxed - probably in need of restart!
*/
bool success = conn_MQTT.publish ( topic, value, retainP );
if ( VERBOSE >= 10 ) Serial.println ( success ? "OK!" : "Failed" );
return success ? ( tLen + vLen ) : -1;
};
void handleReceivedMQTTMessage ( char * topic, byte * payload, unsigned int length ) {
const size_t pBufLen = 512;
char pBuf [ pBufLen ];
memcpy ( pBuf, payload, length );
pBuf [ length ] = '\0';
if ( VERBOSE >= 8 ) {
Serial.print ( F ( "Got: " ) );
Serial.print ( topic );
Serial.print ( F ( ": " ) );
Serial.print ( pBuf );
Serial.println ();
}
interpretNewCommandString ( topic, pBuf );
}
void interpretNewCommandString ( char * theTopic, char * thePayload ) {
DynamicJsonDocument doc ( 400 );
if ( VERBOSE >= 4 ) {
Serial.print ( F ( "thePayload: '" ) );
Serial.print ( thePayload );
Serial.println ( F ( "'" ) );
}
DeserializationError error = deserializeJson ( doc, thePayload );
// Test if parsing succeeds.
if ( error ) {
Serial.print(F("deserializeJson() failed: "));
Serial.println(error.f_str());
return;
}
// Most of the time, you can rely on the implicit casts.
// In other case, you can do doc["time"].as<long>();
if ( doc.containsKey ( "color" ) ) {
byte R, G, B, W, C;
R = doc["color"][0]; // || 0x00;
G = doc["color"][1]; // || 0x00;
B = doc["color"][2]; // || 0x00;
W = doc["color"][3]; // || 0x00;
C = doc["color"][4]; // || 0x00;
setOutput ( R, G, B, W, C );
setOutputState ( 1 );
}
if ( doc.containsKey ( "state" ) ) {
setOutputState ( doc["state"].as<int>() );
}
if ( doc.containsKey ( "timer" ) ) {
setTimer ();
}
}
// *****************************************************************************
// ********************************* output *************************************
// *****************************************************************************
void toggleOutput () {
setOutputState ( ! outputState );
}
int setOutputState ( int newSetting, bool force ) {
// turn on/off without losing settings...
// force resets the output state and resends the retained brightness values
if ( VERBOSE >= 4 ) {
Serial.printf ( "State: %s\n", newSetting ? "ON" : "OFF" );
}
int oldVal = outputState;
if ( ( newSetting != outputState ) || force ) {
// if not force, then newSetting must be unequal to outputState
// if force, can't assume anything about the equality
if ( newSetting >= 0 ) outputState = newSetting;
if ( outputState ) {
// turn on; could be timer or just "on"
if ( ! currentR && ! currentG && ! currentB && ! currentW && ! currentC ) {
// everything's set to zero brightness
if ( VERBOSE >= 12 ) Serial.println ( F ( "Setting default nonzero values" ) );
currentW = 0x80;
currentC = 0x80;
}
sendOutput ( currentR, currentG, currentB, currentW, currentC );
} else {
// turn off, leaving settings alone
if ( VERBOSE >= 12 ) Serial.println ( F ( "Turning off, leaving settings" ) );
sendOutput ( 0x00, 0x00, 0x00, 0x00, 0x00 );
}
if ( VERBOSE > 10 ) Serial.printf ( " -> output is %s\n", outputStateString [ outputState ] );
settingsDirtyAt_ms = millis();
timerStartedAt_ms = 0UL; // cancel timer on any interaction
}
forceWSUpdate = true;
return ( oldVal );
}
void setOutput ( byte R, byte G, byte B, byte W, byte C ) {
// turning off doesn't call this routine, so current values should never be 0
currentR = R;
currentG = G;
currentB = B;
currentW = W;
currentC = C;
sendOutput ( currentR, currentG, currentB, currentW, currentC );
settingsDirtyAt_ms = millis();
forceWSUpdate = true;
}
void sendOutput ( byte R, byte G, byte B, byte W, byte C ) {
// lowest level send - just send these values, without side effects
if ( VERBOSE >= 4 ) {
Serial.printf ( "Color settings: 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x\n",
R, G, B, W, C );
}
_my92xx->setChannel ( MY92XX_RED, (unsigned int) R );
_my92xx->setChannel ( MY92XX_GREEN, (unsigned int) G );
_my92xx->setChannel ( MY92XX_BLUE , (unsigned int) B );
_my92xx->setChannel ( MY92XX_WARM, (unsigned int) W );
_my92xx->setChannel ( MY92XX_COLD, (unsigned int) C );
_my92xx->update();
}
void setTimer ( unsigned long p_timeoutValue_ms ) {
if ( VERBOSE >= 4 ) {
Serial.printf ( "Timer enabled\n" );
}
setOutputState ( 2 );
timerStartedAt_ms = millis();
timeoutValue_ms = p_timeoutValue_ms;
// Serial.printf ( "setTimer: setting %ul ms; turnoff at: %ul ms\n", timeoutValue_ms, turnOffAt_ms );
sendValueToMQTT ( mqttTimeoutTopic, timeoutValue_ms, "Timeout value" );
}
unsigned long timeRemaining_ms () {
if ( timerStartedAt_ms == 0 ) return 0UL;
bool timedOut = ( millis() - timerStartedAt_ms ) > timeoutValue_ms;
return ( timedOut ? 0UL : ( timerStartedAt_ms + timeoutValue_ms ) - millis() );
}
// *****************************************************************************
// ********************************** HTML *************************************
// *****************************************************************************
// { "color": [ 0, 0, 0, 128, 255 ] }
// { "timer": 1 }
// { "state": 1 }
void htmlResponse_root () {
sendOutput ( 0x40, 0x40, 0x00, 0x00, 0x00 );
const char * htmlMessage = R"HTML(
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<link referrerpolicy="no-referrer" rel="icon" href="http://miniTunes.cbmHome/favicon.ico">
<style>
p{} .data { font-weight: bold; }
.right { text-align: right; }
.myButton {
-moz-box-shadow: 3px 4px 0px 0px #899599;
-webkit-box-shadow: 3px 4px 0px 0px #899599;
box-shadow: 3px 4px 0px 0px #899599;
background:-webkit-gradient(linear, left top, left bottom, color-stop(0.05, #ededed), color-stop(1, #bab1ba));
background:-moz-linear-gradient(top, #ededed 5%, #bab1ba 100%);
background:-webkit-linear-gradient(top, #ededed 5%, #bab1ba 100%);
background:-o-linear-gradient(top, #ededed 5%, #bab1ba 100%);
background:-ms-linear-gradient(top, #ededed 5%, #bab1ba 100%);
background:linear-gradient(to bottom, #ededed 5%, #bab1ba 100%);
filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ededed', endColorstr='#bab1ba',GradientType=0);
background-color:#ededed;
-moz-border-radius:15px;
-webkit-border-radius:15px;
border-radius:15px;
border:1px solid #d6bcd6;
display:inline-block;
cursor:pointer;
color:#3a8a9e;
font-family:Arial;
font-size:17px;
padding:7px 25px;
text-decoration:none;
text-shadow:0px 1px 0px #e1e2ed;
}
.myButton .willBe { display:none; }
.myButton:hover {
background:-webkit-gradient(linear, left top, left bottom, color-stop(0.05, #bab1ba), color-stop(1, #ededed));
background:-moz-linear-gradient(top, #bab1ba 5%, #ededed 100%);
background:-webkit-linear-gradient(top, #bab1ba 5%, #ededed 100%);
background:-o-linear-gradient(top, #bab1ba 5%, #ededed 100%);
background:-ms-linear-gradient(top, #bab1ba 5%, #ededed 100%);
background:linear-gradient(to bottom, #bab1ba 5%, #ededed 100%);
filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#bab1ba', endColorstr='#ededed',GradientType=0);
background-color:#bab1ba;
}
.myButton:hover .is { display:none; }
.myButton:hover .willBe { display:inline-block; }
.myButton:active {
position:relative;
top:1px;
}
.timerSliderContainer {
position: relative;
top: 20px;
width: 50%;
}
.timerSlider,.rSlider,.gSlider,.bSlider,.wSlider,.cSlider {
-webkit-appearance: none;
width: 300px;
height: 25px;
background: #d3d3d3;
outline: none;
opacity: 0.7;
-webkit-transition: .2s;
transition: opacity .2s;
}
.timerSlider {
width: 500px;
}
.timerSlider:hover,
.rSlider:hover,
.gSlider:hover,
.bSlider:hover,
.wSlider:hover,
.cSlider:hover {
opacity: 1;
}
.timerSlider::-moz-range-thumb,
.rSlider::-moz-range-thumb,
.gSlider::-moz-range-thumb,
.bSlider::-moz-range-thumb,
.wSlider::-moz-range-thumb,
.cSlider::-moz-range-thumb {
width: 25px;
height: 25px;
cursor: pointer;
}
.timerSlider::-webkit-slider-thumb,
.rSlider::-webkit-slider-thumb,
.gSlider::-webkit-slider-thumb,
.bSlider::-webkit-slider-thumb,
.wSlider::-webkit-slider-thumb,
.cSlider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 25px;
height: 25px;
cursor: pointer;
}
.timerSlider::-moz-range-thumb,
.timerSlider::-webkit-slider-thumb {
background: #4CAF50;
}
.rSlider::-moz-range-thumb,
.rSlider::-webkit-slider-thumb {
background: #FF0000;
}
.gSlider::-moz-range-thumb,
.gSlider::-webkit-slider-thumb {
background: #00FF00;
}
.bSlider::-moz-range-thumb,
.bSlider::-webkit-slider-thumb {
background: #0000FF;
}
.wSlider::-moz-range-thumb,
.wSlider::-webkit-slider-thumb {
background: #ffc0c0;
}
.cSlider::-moz-range-thumb,
.cSlider::-webkit-slider-thumb {
background: #c0c0ff;
}
.timerSliderinfo {
position: absolute;
top: -45px;
left: 150px;
background: #80d880;
}
</style>
<script type="text/javascript">
var ws;
var wsUri = "ws:";
var loc = window.location;
if (loc.protocol === "https:") { wsUri = "wss:"; }
// wsUri += "//" + loc.host + loc.pathname.replace("simple","ws/simple");
wsUri += "//" + loc.host + ":81/";
ws = new WebSocket ( wsUri );
window.addEventListener('load', onLoad);
function wsConnect() {
ws.onerror = function (error) {
document.getElementById ( 'STATUS' ).innerHTML = "ERROR!!";
document.getElementById ( 'STATUS' ).style = "background:pink; color:red";
document.getElementById ("overlay").style.display = "block";
};
ws.onclose = function() {
document.getElementById ( 'STATUS' ).innerHTML = "<b>not</b> connected";
document.getElementById ( 'STATUS' ).style = "background:none; color:red";
document.getElementById ("overlay").style.display = "block";
// in case of lost connection tries to reconnect every 3 secs
setTimeout ( wsConnect, 3000 );
}
ws.onopen = function () {
document.getElementById ( 'STATUS' ).innerHTML = "connected";
document.getElementById ( 'STATUS' ).style = "background:none; color:black";
ws.send ( "Open for data" );
document.getElementById ( 'OUTPUT_CURRENT' ).innerHTML = "..yet unknown..";
document.getElementById ( 'OUTPUT_ONCHANGE' ).innerHTML = "..yet unknown tobe..";
}
ws.onmessage = function ( msg ) {
var data = msg.data;
document.getElementById ("overlay").style.display = "none";
var jsonObj = JSON.parse(data);
document.getElementById ( 'PROGNAME' ).innerHTML = jsonObj.PROGNAME;
document.getElementById ( 'VERSION' ).innerHTML = jsonObj.VERSION;
document.getElementById ( 'VERDATE' ).innerHTML = jsonObj.VERDATE;
document.getElementById ( 'UNIQUE_TOKEN' ).innerHTML = jsonObj.UNIQUE_TOKEN;
document.getElementById ( 'IP_STRING' ).innerHTML = jsonObj.IP_STRING;
var temp = jsonObj.MDNS_ID;
document.getElementById("MDNS_ID").innerHTML =
( temp == "<failure>" ) ? "<em>mDns failed</em>" : temp.concat ( ".local" );
document.getElementById ( 'TIME' ).innerHTML = jsonObj.TIME;
document.getElementById ( 'MQTT_CMD_TOPIC' ).innerHTML = jsonObj.MQTT_CMD_TOPIC;
document.getElementById ( 'MQTT_STATUS_TOPIC' ).innerHTML = jsonObj.MQTT_STATUS_TOPIC;
document.getElementById ( 'MQTT_TIMEOUT_TOPIC' ).innerHTML = jsonObj.MQTT_TIMEOUT_TOPIC;
document.getElementById ( 'R_VALUE' ).innerHTML = jsonObj.R_VALUE;
document.getElementById ( 'G_VALUE' ).innerHTML = jsonObj.G_VALUE;
document.getElementById ( 'B_VALUE' ).innerHTML = jsonObj.B_VALUE;
document.getElementById ( 'W_VALUE' ).innerHTML = jsonObj.W_VALUE;
document.getElementById ( 'C_VALUE' ).innerHTML = jsonObj.C_VALUE;
document.getElementById ( 'R_SLIDER' ).value = jsonObj.R_VALUE;
document.getElementById ( 'G_SLIDER' ).value = jsonObj.G_VALUE;
document.getElementById ( 'B_SLIDER' ).value = jsonObj.B_VALUE;
document.getElementById ( 'W_SLIDER' ).value = jsonObj.W_VALUE;
document.getElementById ( 'C_SLIDER' ).value = jsonObj.C_VALUE;
document.getElementById ( 'OUTPUT_CURRENT' ).innerHTML = jsonObj.OUTPUT_STATUS;
document.getElementById ( 'OUTPUT_ONCHANGE' ).innerHTML = jsonObj.OUTPUT_CHANGE_STATUS;
var secs = jsonObj.TIMER_SECONDS_REMAINING;
document.getElementById ( "TIMER_SLIDER" ).value = secs;
// var mins = Math.round ( secs / 6 ) / 10;
// document.getElementById ( "TIMER_SLIDER_VALUE" ).innerHTML = ""; // jsonObj.TIMER_SECONDS_REMAINING ? ( mins + " minutes" ) : "";
document.getElementById ( "TIMER_REMAINING" ).innerHTML = jsonObj.TIMER_REMAINING_STRING;
document.getElementById ( 'LAST_BOOT_AT' ).innerHTML = jsonObj.LAST_BOOT_AT;
}
}
function onLoad(event) {
initButtons();
initSliders();
}
function initButtons() {
document.getElementById('MAIN_BUTTON').addEventListener('click', toggle);
}
function toggle(){
ws.send('toggle');
}
function pageUnload () {
document.getElementById ( 'STATUS' ).innerHTML = "page unloaded";
document.getElementById ( 'STATUS' ).style = "background:none; color:red";
document.getElementById ("overlay").style.display = "block";
}
function initSliders() {
var timerSlider = document.getElementById("TIMER_SLIDER");
// document.getElementById("TIMER_SLIDER_VALUE").innerHTML = timerSlider.value;
timerSlider.oninput = function() {
// document.getElementById("TIMER_SLIDER_VALUE").innerHTML = this.value;
ws.send ( 'timer_value:' + this.value );
}
var rSlider = document.getElementById("R_SLIDER");
rSlider.oninput = function() {
ws.send ( 'red_value:' + this.value );
}
var gSlider = document.getElementById("G_SLIDER");
gSlider.oninput = function() {
ws.send ( 'green_value:' + this.value );
}
var bSlider = document.getElementById("B_SLIDER");
bSlider.oninput = function() {
ws.send ( 'blue_value:' + this.value );
}
var wSlider = document.getElementById("W_SLIDER");
wSlider.oninput = function() {
ws.send ( 'warm_value:' + this.value );
}
var cSlider = document.getElementById("C_SLIDER");
cSlider.oninput = function() {
ws.send ( 'cool_value:' + this.value );
}
}
</script>
<title>Sonoff LED Bulb</title>
</head>
<body onload="wsConnect()" onunload="pageUnload()">
<div id="overlay" style="
position: fixed; /* Sit on top of the page content */
display: block; /* shown by default */
width: 100%; /* Full width (cover the whole page) */
height: 100%; /* Full height (cover the whole page) */
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0,0,0,0.25); /* background color, with opacity */
z-index: 2; /* Specify a stack order in case you're using a different order for other elements */
cursor: pointer; /* Add a pointer on hover */
">
<span style="
position: absolute;
top: 50%;
left: 50%;
font-size: 50px;
background: none;
color: red;
transform: translate(-50%,-50%);
-ms-transform: translate(-50%,-50%);
">
<span id="STATUS">uninitialized</span>
</span>
</div>
<h2><span id="PROGNAME">..progname..
</span> v<span id="VERSION">..version..
</span> <span id="VERDATE">..verdate..</span> cbm</h2>
<h4> Running on
<span id="UNIQUE_TOKEN">..unique_token..</span> at
<span id="IP_STRING">..ipstring..</span>
<span id="MDNS_ID">..mdns..</span>
</h4>
<h4> MQTT command topic:
<span id="MQTT_CMD_TOPIC">..mqtt_cmd_topic..</span> <br/>
MQTT status topic:
<span id="MQTT_STATUS_TOPIC">..mqtt_status_topic..</span> <br/>
MQTT timeout topic:
<span id="MQTT_TIMEOUT_TOPIC">..mqtt_timeout_topic..</span> <br/>
</h4>
<br>
<h4>As of <time id="TIME">..time..:</time></h4>
<table id="COLOR_TABLE" border="1" style="width: 500px">
<thead>
<tr>
<th colspan="3">Color Values</th>
</tr>
</thead>
<tbody>
<tr>
<td style="width: 150px">Red</td>
<td class="right" id="R_VALUE" style="width: 50px">..R..</td>
<td><input type="range" min="0" max="255" value="0" class="rSlider" id="R_SLIDER"></td>
</tr>
<tr>
<tr>
<td>Green</td>
<td class="right" id="G_VALUE">..G..</td>
<td><input type="range" min="0" max="255" value="0" class="gSlider" id="G_SLIDER"></td>
</tr>
<tr>
<tr>
<td>Blue</td>
<td class="right" id="B_VALUE">..B..</td>
<td><input type="range" min="0" max="255" value="0" class="bSlider" id="B_SLIDER"></td>
</tr>
<tr>
<tr>
<td>Warm White</td>
<td class="right" id="W_VALUE">..W..</td>
<td><input type="range" min="0" max="255" value="0" class="wSlider" id="W_SLIDER"></td>
</tr>
<tr>
<tr>
<td>Cool White</td>
<td class="right" id="C_VALUE">..C..</td>
<td><input type="range" min="0" max="255" value="0" class="cSlider" id="C_SLIDER"></td>
</tr>
</tbody>
</table>
<br><br>
<button id="MAIN_BUTTON" class="myButton">
<span id="OUTPUT_CURRENT" class="is">..currently..</span>
<span id="OUTPUT_ONCHANGE" class="willBe">..willbe..</span>
</button>
<div class="timerSliderContainer">
<input type="range" min="0" max="7200" value="0" class="timerSlider" id="TIMER_SLIDER">
<span class="timerSliderinfo" id="TIMER_REMAINING"></span>
</div>
<br><br>Last boot at <span id="LAST_BOOT_AT">..last_boot..</span><br>
</body>
</html>
)HTML";
yield ();
htmlServer.send ( 200, "text/html", htmlMessage );
if ( VERBOSE >= 20 ) {
Serial.print ( F ( "done\n" ) );
}
setOutputState ( -1, true ); // revert output, canceling indicator
forceWSUpdate = true;
}
void webSocketEvent ( uint8_t num, WStype_t type, uint8_t * payload, size_t length ) {
// Serial.printf ( "Event %d received\n", type );
switch ( type ) {
case WStype_ERROR: // 0
if ( VERBOSE >= 10 ) Serial.printf("[WSc] Error!\n");
break;
case WStype_DISCONNECTED: // 1
if ( VERBOSE >= 10 ) Serial.printf("[WSc] Disconnected!\n");
break;
case WStype_CONNECTED: // 2
if ( length > 1 ) {
if ( VERBOSE >= 10 ) Serial.printf ( "[WSc] %s: Connected to url: %s\n", num, payload );
// bool sendTXT ( uint8_t num, uint8_t * payload, size_t length = 0, bool headerToPayload = false );
webSocket.sendTXT ( num, "Connected", 9 );
} else {
if ( VERBOSE >= 10 ) Serial.printf ( "[WSc] CONNECTED message length 1 (empty!)\n" );
}
break;
case WStype_TEXT: // 3
if ( length > 1 ) {
if ( VERBOSE >= 10 ) Serial.printf ( "[WSc] get text: %s\n", payload );
if ( ! strcmp ( (const char *) payload, "toggle" ) ) {
toggleOutput();
}
// if ( ! strcmp ( (const char *) payload, "initiate_timer" ) ) {
// setTimer();
// }
if ( strstr ( (const char *) payload, "timer_value:" ) ) {
if ( VERBOSE >= 10 ) Serial.println ( (const char *) payload );
// strstr returns NULL if string not found
char * pch;
pch = strtok ( (char *) payload, ":" );
pch = strtok ( NULL, ":"); // skip the first token "timer_value"
int seconds = atoi ( pch );
setTimer ( seconds * 1000UL );
} else {
bool change = false;
if ( strstr ( (const char *) payload, "red_value:" ) ) {
if ( VERBOSE >= 10 ) Serial.println ( (const char *) payload );
// strstr returns NULL if string not found
char * pch;
pch = strtok ( (char *) payload, ":" );
pch = strtok ( NULL, ":"); // skip the first token
currentR = atoi ( pch );
change = true;
}
if ( strstr ( (const char *) payload, "green_value:" ) ) {
if ( VERBOSE >= 10 ) Serial.println ( (const char *) payload );
// strstr returns NULL if string not found
char * pch;
pch = strtok ( (char *) payload, ":" );
pch = strtok ( NULL, ":"); // skip the first token
currentG = atoi ( pch );
change = true;
}
if ( strstr ( (const char *) payload, "blue_value:" ) ) {
if ( VERBOSE >= 10 ) Serial.println ( (const char *) payload );
// strstr returns NULL if string not found
char * pch;
pch = strtok ( (char *) payload, ":" );
pch = strtok ( NULL, ":"); // skip the first token
currentB = atoi ( pch );
change = true;
}
if ( strstr ( (const char *) payload, "warm_value:" ) ) {
if ( VERBOSE >= 10 ) Serial.println ( (const char *) payload );
// strstr returns NULL if string not found
char * pch;
pch = strtok ( (char *) payload, ":" );
pch = strtok ( NULL, ":"); // skip the first token
currentW = atoi ( pch );
change = true;
}
if ( strstr ( (const char *) payload, "cool_value:" ) ) {
if ( VERBOSE >= 10 ) Serial.println ( (const char *) payload );
// strstr returns NULL if string not found
char * pch;
pch = strtok ( (char *) payload, ":" );
pch = strtok ( NULL, ":"); // skip the first token
currentC = atoi ( pch );
change = true;
}
if ( change && outputState ) {
setOutput ( currentR, currentG, currentB, currentW, currentC );
forceWSUpdate = true;
}
}
// send message to server
// webSocket.sendTXT ( num, "message here", 12 );
} else {
if ( VERBOSE >= 10 ) Serial.printf ( "[WSc] text message length 1 (empty!)\n" );
}
break;
case WStype_BIN: // 4
if ( VERBOSE >= 10 ) Serial.printf("[WSc] get binary length: %u\n", length);
// hexdump(payload, length);
// send data to server
// webSocket.sendBIN(payload, length);
break;
case WStype_PING: // 9?
// pong will be send automatically
if ( VERBOSE >= 10 ) Serial.printf("[WSc] get ping\n");
break;
case WStype_PONG: // 10
// answer to a ping we send
if ( VERBOSE >= 10 ) Serial.printf("[WSc] get pong\n");
break;
default:
Serial.printf ( "[WSc] got unexpected event type %d\n", type );
break;
}
// Serial.println ( F ( "Switch exited" ) );
}
void update_WebSocket () {
// ArduinoJSON v.6.x
DynamicJsonDocument doc ( 600 );
#if TESTING
doc["PROGNAME"] = PROGNAME " TESTING";
#else
doc["PROGNAME"] = PROGNAME;
#endif
doc["VERSION"] = VERSION;
doc["VERDATE"] = VERDATE;
doc["UNIQUE_TOKEN"] = uniqueToken;
char ipString[25];
snprintf ( ipString, 25, "%u.%u.%u.%u", Network.ip[0], Network.ip[1], Network.ip[2], Network.ip[3] );
doc["IP_STRING"] = ipString;
doc["MDNS_ID"] = mdnsOtaId;
if ( timeStatus() == timeSet ) {
formatTimeString ( timeString, timeStringLen, now() );
} else {
snprintf ( timeString, timeStringLen, "<time not set>" );
}
doc["TIME"] = timeString;
doc["MQTT_CMD_TOPIC"] = mqttCmdTopic;
doc["MQTT_STATUS_TOPIC"] = mqttStatusTopic;
doc["MQTT_TIMEOUT_TOPIC"] = mqttTimeoutTopic;
doc["LAST_BOOT_AT"] = bootTimeString;
// JsonArray dataArray = doc.createNestedArray("data");
// const size_t pBufLen = 32;
// char pBuf [ pBufLen ];
doc["R_VALUE"] = currentR;
doc["G_VALUE"] = currentG;
doc["B_VALUE"] = currentB;
doc["W_VALUE"] = currentW;
doc["C_VALUE"] = currentC;
doc["OUTPUT_STATUS"] = outputStateString [ outputState ];
doc["OUTPUT_CHANGE_STATUS"] = outputState ? "turn OFF" : "turn ON";
if ( timerStartedAt_ms == 0UL ) {
doc["TIMER_SECONDS_REMAINING"] = 0;
doc["TIMER_REMAINING_STRING"] = "";
} else {
unsigned long timeRemaining_s = timeRemaining_ms() / SECOND_ms;
formatIntervalString ( timeString, timeStringLen, timeRemaining_s );
snprintf ( pBuf, pBufLen, "Time remaining %s\n", timeString );
doc["TIMER_SECONDS_REMAINING"] = timeRemaining_s;
doc["TIMER_REMAINING_STRING"] = pBuf;
// Serial.printf ( "Timer pBuf: %s\n", pBuf );
}
serializeJson ( doc, jsonString );
#if 0 && defined ( TELEMETRY_ON )
{
snprintf ( pBuf, pBufLen, "%s/%s/debug/update_Websocket_JSON_string_len", PROGNAME, uniqueToken );
sendValueToMQTT ( pBuf, strlen ( jsonString ), "update_Websocket JSON string len", MQTT_RETAIN );
}
#endif
if ( VERBOSE >= 20 )
Serial.printf ( "doc size %d and JSON string length %d\n", doc.size(), measureJson ( doc ) );
if ( VERBOSE >= 15 ) Serial.println ( jsonString );
// webSocket.sendTXT ( 0, jsonString, strlen ( jsonString ) ); // both work
webSocket.broadcastTXT ( jsonString, strlen ( jsonString ) ); // both work
if ( VERBOSE >= 15 ) Serial.print ( F (" ... sent\n" ) );
if ( ( VERBOSE >= 15 ) && forceWSUpdate ) Serial.println ( F ( "forced WS update completing" ) );
forceWSUpdate = false;
}
// *****************************************************************************
// ********************************** time *************************************
// *****************************************************************************
time_t getUnixTime() {
while (conn_UDP.parsePacket() > 0) ; // discard any previously received packets
if ( VERBOSE >= 16 ) Serial.println ( F ( "Transmit NTP Request" ) );
sendNTPpacket(timeServer);
uint32_t beginWait = millis();
while (millis() - beginWait < 1500) {
int size = conn_UDP.parsePacket();
if (size >= NTP_PACKET_SIZE) {
if ( VERBOSE >= 16 ) Serial.println ( F ( "Receive NTP Response" ) );
ntpTimeGoodP = true;
conn_UDP.read(NTPBuffer, NTP_PACKET_SIZE); // read packet into the buffer
unsigned long secsSince1900;
// convert four bytes starting at location 40 to a long integer
secsSince1900 = (unsigned long)NTPBuffer[40] << 24;
secsSince1900 |= (unsigned long)NTPBuffer[41] << 16;
secsSince1900 |= (unsigned long)NTPBuffer[42] << 8;
secsSince1900 |= (unsigned long)NTPBuffer[43];
return secsSince1900 - 2208988800UL + timeZone * 1UL * 60UL * 60UL;
}
}
Serial.println ( F ( "No NTP Response :-(" ) );
return 0; // return 0 if unable to get the time
}
void sendNTPpacket ( IPAddress& address ) {
if ( VERBOSE >= 16 ) Serial.printf ( "Sending NTP request to %2u.%2u.%2u.%2u\n",
address[0], address[1], address[2], address[3] );
memset(NTPBuffer, 0, NTP_PACKET_SIZE); // set all bytes in the buffer to 0
// Initialize values needed to form NTP request
NTPBuffer[0] = 0b11100011; // LI, Version, Mode
NTPBuffer[1] = 0; // Stratum, or type of clock
NTPBuffer[2] = 6; // Polling Interval
NTPBuffer[3] = 0xEC; // Peer Clock Precision
// 8 bytes of zero for Root Delay & Root Dispersion
NTPBuffer[12] = 49;
NTPBuffer[13] = 0x4E;
NTPBuffer[14] = 49;
NTPBuffer[15] = 52;
// all NTP fields have been given values, now
// you can send a packet requesting a timestamp:
// send a packet requesting a timestamp:
conn_UDP.beginPacket ( address, 123 ); // NTP requests are to port 123
conn_UDP.write ( NTPBuffer, NTP_PACKET_SIZE );
conn_UDP.endPacket();
}
void formatTimeString ( char * result, int resultLen, unsigned long time ) {
snprintf ( result, resultLen, "%04d-%02d-%02d %02d:%02d:%02dZ",
year ( time ), month ( time ), day ( time ), hour ( time ), minute ( time ), second ( time ) );
}
void formatIntervalString ( char * result, int resultLen, unsigned long time ) {
snprintf ( result, resultLen, "%02d:%02d:%02d",
hour ( time ), minute ( time ), second ( time ) );
}
// *****************************************************************************
// ********************************** util *************************************
// *****************************************************************************
void saveSettingsToFile () {
// try to write status to LittleFS
// unsigned long s = millis();
File f;
f = LittleFS.open ( "/soft_reset_color.txt", "w" );
f.printf ( "%d,%d,%d, %d,%d, %d", currentR, currentG, currentB,
currentW, currentC,
outputState );
f.close();
// unsigned long interval = millis() - s;
// Serial.printf ( "LittleFS file write took %lu ms\n", interval );
}
// *****************************************************************************
// *****************************************************************************
// *****************************************************************************
Debug Messages
Debug messages go here
You sure we are better served with the issue here instead of https://github.com/xoseperez/my92xx/issues?
Is this the actual MCVE, and channels don't properly adjust from 0 to 255?
#include <Arduino.h>
#include <my92xx.h>
#define MY92XX_MODEL MY92XX_MODEL_MY9231
#define MY92XX_CHIPS 2
#define MY92XX_DI_PIN 12
#define MY92XX_DCKI_PIN 14
#define MY92XX_COLD 0
#define MY92XX_WARM 1
#define MY92XX_RED 4
#define MY92XX_GREEN 3
#define MY92XX_BLUE 5
static constexpr unsigned char MY92XX_CHANNELS[] {
MY92XX_RED,
MY92XX_GREEN,
MY92XX_BLUE,
MY92XX_WARM,
MY92XX_COLD
};
static my92xx* _my92xx { nullptr };
void setup() {
_my92xx = new my92xx ( MY92XX_MODEL, MY92XX_CHIPS,
MY92XX_DI_PIN, MY92XX_DCKI_PIN,
{ \
.scatter = MY92XX_CMD_SCATTER_APDM, \
.frequency = MY92XX_CMD_FREQUENCY_DIVIDE_1, \
.bit_width = MY92XX_CMD_BIT_WIDTH_8, \
.reaction = MY92XX_CMD_REACTION_FAST, \
.one_shot = MY92XX_CMD_ONE_SHOT_DISABLE, \
.resv = 0 \
}
);
_my92xx->setState(true);
for (;;) {
for (unsigned char value = 0; value < 255; ++value) {
for (auto channel : MY92XX_CHANNELS) {
_my92xx->setChannel(channel, value);
}
_my92xx->update();
delay(100);
}
}
}
void loop() { }
delayMicroseconds(...)
is os_delay_us(...)
so no surprise there :)
https://github.com/esp8266/Arduino/blob/c312a2eaf1356ceaafad7c4935fa850e087c84fe/cores/esp8266/core_esp8266_wiring.cpp#L189-L191
Since we are expected to digitalWrite in a tight sequence... Have you tried locking interrupts before doing _my92xx->update()
in the sendOutput()
, like the library source have tried originally (but commented out)? Something like
ets_intr_lock();
_my92xx->update();
ets_intr_unlock();
I just tried locking interrupts before the update call. No change. :(