Fixed pin reservations. (#2214)

* Fixed pin reservations.
Added ethernet pin reservations.
Minor tweaks in usermods.

* Optional ADA compile (not default, free GPIO3 use)

* Move ethernet definitions

Remove pin 3 used check

Co-authored-by: cschwinne <dev.aircoookie@gmail.com>
This commit is contained in:
Blaž Kristan 2021-09-30 16:30:44 +02:00 committed by GitHub
parent 7d929dcde6
commit dc9dedf220
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 593 additions and 523 deletions

View File

@ -1,33 +1,33 @@
# JSON IR remote # JSON IR remote
## Purpose ## Purpose
The JSON IR remote allows users to customize IR remote behavior without writing custom code and compiling. The JSON IR remote allows users to customize IR remote behavior without writing custom code and compiling.
It also enables using any remote that is compatible with your IR receiver. Using the JSON IR remote, you can It also enables using any remote that is compatible with your IR receiver. Using the JSON IR remote, you can
map buttons from any remote to any HTTP request API or JSON API command. map buttons from any remote to any HTTP request API or JSON API command.
## Usage ## Usage
* Upload the IR config file, named _ir.json_ to your board using the [ip address]/edit url. Pick from one of the included files or create your own. * Upload the IR config file, named _ir.json_ to your board using the [ip address]/edit url. Pick from one of the included files or create your own.
* On the config > LED settings page, set the correct IR pin. * On the config > LED settings page, set the correct IR pin.
* On the config > Sync Interfaces page, select "JSON Remote" as the Infrared remote. * On the config > Sync Interfaces page, select "JSON Remote" as the Infrared remote.
## Modification ## Modification
* See if there is a json file with the same number of buttons as your remote. Many remotes will have the same internals and emit the same codes but have different labels. * See if there is a json file with the same number of buttons as your remote. Many remotes will have the same internals and emit the same codes but have different labels.
* In the ir.json file, each key will be the hex encoded IR code. * In the ir.json file, each key will be the hex encoded IR code.
* The "cmd" property will be the HTTP Request API or JSON API to execute when that button is pressed. * The "cmd" property will be the HTTP Request API or JSON API to execute when that button is pressed.
* A limited number of c functions are supported (!incBrightness, !decBrightness, !presetFallback) * A limited number of c functions are supported (!incBrightness, !decBrightness, !presetFallback)
* When using !presetFallback, include properties PL (preset to load), FX (effect to fall back to) and FP (palette to fall back to) * When using !presetFallback, include properties PL (preset to load), FX (effect to fall back to) and FP (palette to fall back to)
* If the command is _repeatable_ and does not contain the "~" character, add a "rpt": true property. * If the command is _repeatable_ and does not contain the "~" character, add a "rpt": true property.
* Other properties are ignored, but having a label property may help when editing. * Other properties are ignored, but having a label property may help when editing.
Sample: Sample:
{ {
"0xFF629D": {"cmd": "T=2", "rpt": true, "label": "Toggle on/off"}, // HTTP command "0xFF629D": {"cmd": "T=2", "rpt": true, "label": "Toggle on/off"}, // HTTP command
"0xFF9867": {"cmd": "A=~16", "label": "Inc brightness"}, // HTTP command with incrementing "0xFF9867": {"cmd": "A=~16", "label": "Inc brightness"}, // HTTP command with incrementing
"0xFF38C7": {"cmd": {"bri": 10}, "label": "Dim to 10"}, // JSON command "0xFF38C7": {"cmd": {"bri": 10}, "label": "Dim to 10"}, // JSON command
"0xFF22DD": {"cmd": "!presetFallback", "PL": 1, "FX": 16, "FP": 6, "0xFF22DD": {"cmd": "!presetFallback", "PL": 1, "FX": 16, "FP": 6,
"label": "Preset 1 or fallback to Saw - Party"}, // c function "label": "Preset 1 or fallback to Saw - Party"}, // c function
} }

View File

@ -1,409 +1,409 @@
#pragma once #pragma once
#include "wled.h" #include "wled.h"
#ifndef PIR_SENSOR_PIN #ifndef PIR_SENSOR_PIN
// compatible with QuinLED-Dig-Uno // compatible with QuinLED-Dig-Uno
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
#define PIR_SENSOR_PIN 23 // Q4 #define PIR_SENSOR_PIN 23 // Q4
#else //ESP8266 boards #else //ESP8266 boards
#define PIR_SENSOR_PIN 13 // Q4 (D7 on D1 mini) #define PIR_SENSOR_PIN 13 // Q4 (D7 on D1 mini)
#endif #endif
#endif #endif
/* /*
* This usermod handles PIR sensor states. * This usermod handles PIR sensor states.
* The strip will be switched on and the off timer will be resetted when the sensor goes HIGH. * The strip will be switched on and the off timer will be resetted when the sensor goes HIGH.
* When the sensor state goes LOW, the off timer is started and when it expires, the strip is switched off. * When the sensor state goes LOW, the off timer is started and when it expires, the strip is switched off.
* *
* *
* Usermods allow you to add own functionality to WLED more easily * Usermods allow you to add own functionality to WLED more easily
* See: https://github.com/Aircoookie/WLED/wiki/Add-own-functionality * See: https://github.com/Aircoookie/WLED/wiki/Add-own-functionality
* *
* v2 usermods are class inheritance based and can (but don't have to) implement more functions, each of them is shown in this example. * v2 usermods are class inheritance based and can (but don't have to) implement more functions, each of them is shown in this example.
* Multiple v2 usermods can be added to one compilation easily. * Multiple v2 usermods can be added to one compilation easily.
* *
* Creating a usermod: * Creating a usermod:
* This file serves as an example. If you want to create a usermod, it is recommended to use usermod_v2_empty.h from the usermods folder as a template. * This file serves as an example. If you want to create a usermod, it is recommended to use usermod_v2_empty.h from the usermods folder as a template.
* Please remember to rename the class and file to a descriptive name. * Please remember to rename the class and file to a descriptive name.
* You may also use multiple .h and .cpp files. * You may also use multiple .h and .cpp files.
* *
* Using a usermod: * Using a usermod:
* 1. Copy the usermod into the sketch folder (same folder as wled00.ino) * 1. Copy the usermod into the sketch folder (same folder as wled00.ino)
* 2. Register the usermod by adding #include "usermod_filename.h" in the top and registerUsermod(new MyUsermodClass()) in the bottom of usermods_list.cpp * 2. Register the usermod by adding #include "usermod_filename.h" in the top and registerUsermod(new MyUsermodClass()) in the bottom of usermods_list.cpp
*/ */
class PIRsensorSwitch : public Usermod class PIRsensorSwitch : public Usermod
{ {
public: public:
/** /**
* constructor * constructor
*/ */
PIRsensorSwitch() {} PIRsensorSwitch() {}
/** /**
* desctructor * desctructor
*/ */
~PIRsensorSwitch() {} ~PIRsensorSwitch() {}
/** /**
* Enable/Disable the PIR sensor * Enable/Disable the PIR sensor
*/ */
void EnablePIRsensor(bool en) { enabled = en; } void EnablePIRsensor(bool en) { enabled = en; }
/** /**
* Get PIR sensor enabled/disabled state * Get PIR sensor enabled/disabled state
*/ */
bool PIRsensorEnabled() { return enabled; } bool PIRsensorEnabled() { return enabled; }
private: private:
// PIR sensor pin // PIR sensor pin
int8_t PIRsensorPin = PIR_SENSOR_PIN; int8_t PIRsensorPin = PIR_SENSOR_PIN;
// notification mode for colorUpdated() // notification mode for colorUpdated()
const byte NotifyUpdateMode = CALL_MODE_NO_NOTIFY; // CALL_MODE_DIRECT_CHANGE const byte NotifyUpdateMode = CALL_MODE_NO_NOTIFY; // CALL_MODE_DIRECT_CHANGE
// delay before switch off after the sensor state goes LOW // delay before switch off after the sensor state goes LOW
uint32_t m_switchOffDelay = 600000; // 10min uint32_t m_switchOffDelay = 600000; // 10min
// off timer start time // off timer start time
uint32_t m_offTimerStart = 0; uint32_t m_offTimerStart = 0;
// current PIR sensor pin state // current PIR sensor pin state
byte sensorPinState = LOW; byte sensorPinState = LOW;
// PIR sensor enabled // PIR sensor enabled
bool enabled = true; bool enabled = true;
// status of initialisation // status of initialisation
bool initDone = false; bool initDone = false;
// on and off presets // on and off presets
uint8_t m_onPreset = 0; uint8_t m_onPreset = 0;
uint8_t m_offPreset = 0; uint8_t m_offPreset = 0;
// flag to indicate that PIR sensor should activate WLED during nighttime only // flag to indicate that PIR sensor should activate WLED during nighttime only
bool m_nightTimeOnly = false; bool m_nightTimeOnly = false;
// flag to send MQTT message only (assuming it is enabled) // flag to send MQTT message only (assuming it is enabled)
bool m_mqttOnly = false; bool m_mqttOnly = false;
// flag to enable triggering only if WLED is initially off (LEDs are not on, preventing running effect being overwritten by PIR) // flag to enable triggering only if WLED is initially off (LEDs are not on, preventing running effect being overwritten by PIR)
bool m_offOnly = false; bool m_offOnly = false;
bool PIRtriggered = false; bool PIRtriggered = false;
unsigned long lastLoop = 0; unsigned long lastLoop = 0;
// strings to reduce flash memory usage (used more than twice) // strings to reduce flash memory usage (used more than twice)
static const char _name[]; static const char _name[];
static const char _switchOffDelay[]; static const char _switchOffDelay[];
static const char _enabled[]; static const char _enabled[];
static const char _onPreset[]; static const char _onPreset[];
static const char _offPreset[]; static const char _offPreset[];
static const char _nightTime[]; static const char _nightTime[];
static const char _mqttOnly[]; static const char _mqttOnly[];
static const char _offOnly[]; static const char _offOnly[];
/** /**
* check if it is daytime * check if it is daytime
* if sunrise/sunset is not defined (no NTP or lat/lon) default to nighttime * if sunrise/sunset is not defined (no NTP or lat/lon) default to nighttime
*/ */
bool isDayTime() { bool isDayTime() {
bool isDayTime = false; bool isDayTime = false;
updateLocalTime(); updateLocalTime();
uint8_t hr = hour(localTime); uint8_t hr = hour(localTime);
uint8_t mi = minute(localTime); uint8_t mi = minute(localTime);
if (sunrise && sunset) { if (sunrise && sunset) {
if (hour(sunrise)<hr && hour(sunset)>hr) { if (hour(sunrise)<hr && hour(sunset)>hr) {
isDayTime = true; isDayTime = true;
} else { } else {
if (hour(sunrise)==hr && minute(sunrise)<mi) { if (hour(sunrise)==hr && minute(sunrise)<mi) {
isDayTime = true; isDayTime = true;
} }
if (hour(sunset)==hr && minute(sunset)>mi) { if (hour(sunset)==hr && minute(sunset)>mi) {
isDayTime = true; isDayTime = true;
} }
} }
} }
return isDayTime; return isDayTime;
} }
/** /**
* switch strip on/off * switch strip on/off
*/ */
void switchStrip(bool switchOn) void switchStrip(bool switchOn)
{ {
if (m_offOnly && bri && (switchOn || (!PIRtriggered && !switchOn))) return; if (m_offOnly && bri && (switchOn || (!PIRtriggered && !switchOn))) return;
PIRtriggered = switchOn; PIRtriggered = switchOn;
if (switchOn && m_onPreset) { if (switchOn && m_onPreset) {
applyPreset(m_onPreset); applyPreset(m_onPreset);
} else if (!switchOn && m_offPreset) { } else if (!switchOn && m_offPreset) {
applyPreset(m_offPreset); applyPreset(m_offPreset);
} else if (switchOn && bri == 0) { } else if (switchOn && bri == 0) {
bri = briLast; bri = briLast;
colorUpdated(NotifyUpdateMode); colorUpdated(NotifyUpdateMode);
} else if (!switchOn && bri != 0) { } else if (!switchOn && bri != 0) {
briLast = bri; briLast = bri;
bri = 0; bri = 0;
colorUpdated(NotifyUpdateMode); colorUpdated(NotifyUpdateMode);
} }
} }
void publishMqtt(const char* state) void publishMqtt(const char* state)
{ {
//Check if MQTT Connected, otherwise it will crash the 8266 //Check if MQTT Connected, otherwise it will crash the 8266
if (WLED_MQTT_CONNECTED){ if (WLED_MQTT_CONNECTED){
char subuf[64]; char subuf[64];
strcpy(subuf, mqttDeviceTopic); strcpy(subuf, mqttDeviceTopic);
strcat_P(subuf, PSTR("/motion")); strcat_P(subuf, PSTR("/motion"));
mqtt->publish(subuf, 0, false, state); mqtt->publish(subuf, 0, false, state);
} }
} }
/** /**
* Read and update PIR sensor state. * Read and update PIR sensor state.
* Initilize/reset switch off timer * Initilize/reset switch off timer
*/ */
bool updatePIRsensorState() bool updatePIRsensorState()
{ {
bool pinState = digitalRead(PIRsensorPin); bool pinState = digitalRead(PIRsensorPin);
if (pinState != sensorPinState) { if (pinState != sensorPinState) {
sensorPinState = pinState; // change previous state sensorPinState = pinState; // change previous state
if (sensorPinState == HIGH) { if (sensorPinState == HIGH) {
m_offTimerStart = 0; m_offTimerStart = 0;
if (!m_mqttOnly && (!m_nightTimeOnly || (m_nightTimeOnly && !isDayTime()))) switchStrip(true); if (!m_mqttOnly && (!m_nightTimeOnly || (m_nightTimeOnly && !isDayTime()))) switchStrip(true);
publishMqtt("on"); publishMqtt("on");
} else /*if (bri != 0)*/ { } else /*if (bri != 0)*/ {
// start switch off timer // start switch off timer
m_offTimerStart = millis(); m_offTimerStart = millis();
} }
return true; return true;
} }
return false; return false;
} }
/** /**
* switch off the strip if the delay has elapsed * switch off the strip if the delay has elapsed
*/ */
bool handleOffTimer() bool handleOffTimer()
{ {
if (m_offTimerStart > 0 && millis() - m_offTimerStart > m_switchOffDelay) if (m_offTimerStart > 0 && millis() - m_offTimerStart > m_switchOffDelay)
{ {
if (enabled == true) if (enabled == true)
{ {
if (!m_mqttOnly && (!m_nightTimeOnly || (m_nightTimeOnly && !isDayTime()))) switchStrip(false); if (!m_mqttOnly && (!m_nightTimeOnly || (m_nightTimeOnly && !isDayTime()))) switchStrip(false);
publishMqtt("off"); publishMqtt("off");
} }
m_offTimerStart = 0; m_offTimerStart = 0;
return true; return true;
} }
return false; return false;
} }
public: public:
//Functions called by WLED //Functions called by WLED
/** /**
* setup() is called once at boot. WiFi is not yet connected at this point. * setup() is called once at boot. WiFi is not yet connected at this point.
* You can use it to initialize variables, sensors or similar. * You can use it to initialize variables, sensors or similar.
*/ */
void setup() void setup()
{ {
if (enabled) { if (enabled) {
// pin retrieved from cfg.json (readFromConfig()) prior to running setup() // pin retrieved from cfg.json (readFromConfig()) prior to running setup()
if (PIRsensorPin >= 0 && pinManager.allocatePin(PIRsensorPin, false, PinOwner::UM_PIR)) { if (PIRsensorPin >= 0 && pinManager.allocatePin(PIRsensorPin, false, PinOwner::UM_PIR)) {
// PIR Sensor mode INPUT_PULLUP // PIR Sensor mode INPUT_PULLUP
pinMode(PIRsensorPin, INPUT_PULLUP); pinMode(PIRsensorPin, INPUT_PULLUP);
sensorPinState = digitalRead(PIRsensorPin); sensorPinState = digitalRead(PIRsensorPin);
} else { } else {
if (PIRsensorPin >= 0) { if (PIRsensorPin >= 0) {
DEBUG_PRINTLN(F("PIRSensorSwitch pin allocation failed.")); DEBUG_PRINTLN(F("PIRSensorSwitch pin allocation failed."));
} }
PIRsensorPin = -1; // allocation failed PIRsensorPin = -1; // allocation failed
enabled = false; enabled = false;
} }
} }
initDone = true; initDone = true;
} }
/** /**
* connected() is called every time the WiFi is (re)connected * connected() is called every time the WiFi is (re)connected
* Use it to initialize network interfaces * Use it to initialize network interfaces
*/ */
void connected() void connected()
{ {
} }
/** /**
* loop() is called continuously. Here you can check for events, read sensors, etc. * loop() is called continuously. Here you can check for events, read sensors, etc.
*/ */
void loop() void loop()
{ {
// only check sensors 4x/s // only check sensors 4x/s
if (!enabled || millis() - lastLoop < 250 || strip.isUpdating()) return; if (!enabled || millis() - lastLoop < 250 || strip.isUpdating()) return;
lastLoop = millis(); lastLoop = millis();
if (!updatePIRsensorState()) { if (!updatePIRsensorState()) {
handleOffTimer(); handleOffTimer();
} }
} }
/** /**
* addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API. * addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API.
* *
* Add PIR sensor state and switch off timer duration to jsoninfo * Add PIR sensor state and switch off timer duration to jsoninfo
*/ */
void addToJsonInfo(JsonObject &root) void addToJsonInfo(JsonObject &root)
{ {
JsonObject user = root["u"]; JsonObject user = root["u"];
if (user.isNull()) user = root.createNestedObject("u"); if (user.isNull()) user = root.createNestedObject("u");
if (enabled) if (enabled)
{ {
// off timer // off timer
String uiDomString = F("PIR <i class=\"icons\">&#xe325;</i>"); String uiDomString = F("PIR <i class=\"icons\">&#xe325;</i>");
JsonArray infoArr = user.createNestedArray(uiDomString); // timer value JsonArray infoArr = user.createNestedArray(uiDomString); // timer value
if (m_offTimerStart > 0) if (m_offTimerStart > 0)
{ {
uiDomString = ""; uiDomString = "";
unsigned int offSeconds = (m_switchOffDelay - (millis() - m_offTimerStart)) / 1000; unsigned int offSeconds = (m_switchOffDelay - (millis() - m_offTimerStart)) / 1000;
if (offSeconds >= 3600) if (offSeconds >= 3600)
{ {
uiDomString += (offSeconds / 3600); uiDomString += (offSeconds / 3600);
uiDomString += F("h "); uiDomString += F("h ");
offSeconds %= 3600; offSeconds %= 3600;
} }
if (offSeconds >= 60) if (offSeconds >= 60)
{ {
uiDomString += (offSeconds / 60); uiDomString += (offSeconds / 60);
offSeconds %= 60; offSeconds %= 60;
} }
else if (uiDomString.length() > 0) else if (uiDomString.length() > 0)
{ {
uiDomString += 0; uiDomString += 0;
} }
if (uiDomString.length() > 0) if (uiDomString.length() > 0)
{ {
uiDomString += F("min "); uiDomString += F("min ");
} }
uiDomString += (offSeconds); uiDomString += (offSeconds);
infoArr.add(uiDomString + F("s")); infoArr.add(uiDomString + F("s"));
} else { } else {
infoArr.add(sensorPinState ? F("sensor on") : F("inactive")); infoArr.add(sensorPinState ? F("sensor on") : F("inactive"));
} }
} else { } else {
String uiDomString = F("PIR sensor"); String uiDomString = F("PIR sensor");
JsonArray infoArr = user.createNestedArray(uiDomString); JsonArray infoArr = user.createNestedArray(uiDomString);
infoArr.add(F("disabled")); infoArr.add(F("disabled"));
} }
} }
/** /**
* addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object). * addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object).
* Values in the state object may be modified by connected clients * Values in the state object may be modified by connected clients
*/ */
/* /*
void addToJsonState(JsonObject &root) void addToJsonState(JsonObject &root)
{ {
} }
*/ */
/** /**
* readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object). * readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object).
* Values in the state object may be modified by connected clients * Values in the state object may be modified by connected clients
*/ */
/* /*
void readFromJsonState(JsonObject &root) void readFromJsonState(JsonObject &root)
{ {
} }
*/ */
/** /**
* provide the changeable values * provide the changeable values
*/ */
void addToConfig(JsonObject &root) void addToConfig(JsonObject &root)
{ {
JsonObject top = root.createNestedObject(FPSTR(_name)); JsonObject top = root.createNestedObject(FPSTR(_name));
top[FPSTR(_enabled)] = enabled; top[FPSTR(_enabled)] = enabled;
top[FPSTR(_switchOffDelay)] = m_switchOffDelay / 1000; top[FPSTR(_switchOffDelay)] = m_switchOffDelay / 1000;
top["pin"] = PIRsensorPin; top["pin"] = PIRsensorPin;
top[FPSTR(_onPreset)] = m_onPreset; top[FPSTR(_onPreset)] = m_onPreset;
top[FPSTR(_offPreset)] = m_offPreset; top[FPSTR(_offPreset)] = m_offPreset;
top[FPSTR(_nightTime)] = m_nightTimeOnly; top[FPSTR(_nightTime)] = m_nightTimeOnly;
top[FPSTR(_mqttOnly)] = m_mqttOnly; top[FPSTR(_mqttOnly)] = m_mqttOnly;
top[FPSTR(_offOnly)] = m_offOnly; top[FPSTR(_offOnly)] = m_offOnly;
DEBUG_PRINTLN(F("PIR config saved.")); DEBUG_PRINTLN(F("PIR config saved."));
} }
/** /**
* restore the changeable values * restore the changeable values
* readFromConfig() is called before setup() to populate properties from values stored in cfg.json * readFromConfig() is called before setup() to populate properties from values stored in cfg.json
* *
* The function should return true if configuration was successfully loaded or false if there was no configuration. * The function should return true if configuration was successfully loaded or false if there was no configuration.
*/ */
bool readFromConfig(JsonObject &root) bool readFromConfig(JsonObject &root)
{ {
bool oldEnabled = enabled; bool oldEnabled = enabled;
int8_t oldPin = PIRsensorPin; int8_t oldPin = PIRsensorPin;
JsonObject top = root[FPSTR(_name)]; JsonObject top = root[FPSTR(_name)];
if (top.isNull()) { if (top.isNull()) {
DEBUG_PRINT(FPSTR(_name)); DEBUG_PRINT(FPSTR(_name));
DEBUG_PRINTLN(F(": No config found. (Using defaults.)")); DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
return false; return false;
} }
PIRsensorPin = top["pin"] | PIRsensorPin; PIRsensorPin = top["pin"] | PIRsensorPin;
enabled = top[FPSTR(_enabled)] | enabled; enabled = top[FPSTR(_enabled)] | enabled;
m_switchOffDelay = (top[FPSTR(_switchOffDelay)] | m_switchOffDelay/1000) * 1000; m_switchOffDelay = (top[FPSTR(_switchOffDelay)] | m_switchOffDelay/1000) * 1000;
m_onPreset = top[FPSTR(_onPreset)] | m_onPreset; m_onPreset = top[FPSTR(_onPreset)] | m_onPreset;
m_onPreset = max(0,min(250,(int)m_onPreset)); m_onPreset = max(0,min(250,(int)m_onPreset));
m_offPreset = top[FPSTR(_offPreset)] | m_offPreset; m_offPreset = top[FPSTR(_offPreset)] | m_offPreset;
m_offPreset = max(0,min(250,(int)m_offPreset)); m_offPreset = max(0,min(250,(int)m_offPreset));
m_nightTimeOnly = top[FPSTR(_nightTime)] | m_nightTimeOnly; m_nightTimeOnly = top[FPSTR(_nightTime)] | m_nightTimeOnly;
m_mqttOnly = top[FPSTR(_mqttOnly)] | m_mqttOnly; m_mqttOnly = top[FPSTR(_mqttOnly)] | m_mqttOnly;
m_offOnly = top[FPSTR(_offOnly)] | m_offOnly; m_offOnly = top[FPSTR(_offOnly)] | m_offOnly;
DEBUG_PRINT(FPSTR(_name)); DEBUG_PRINT(FPSTR(_name));
if (!initDone) { if (!initDone) {
// reading config prior to setup() // reading config prior to setup()
DEBUG_PRINTLN(F(" config loaded.")); DEBUG_PRINTLN(F(" config loaded."));
} else { } else {
if (oldPin != PIRsensorPin || oldEnabled != enabled) { if (oldPin != PIRsensorPin || oldEnabled != enabled) {
// check if pin is OK // check if pin is OK
if (oldPin != PIRsensorPin && oldPin >= 0) { if (oldPin != PIRsensorPin && oldPin >= 0) {
// if we are changing pin in settings page // if we are changing pin in settings page
// deallocate old pin // deallocate old pin
pinManager.deallocatePin(oldPin, PinOwner::UM_PIR); pinManager.deallocatePin(oldPin, PinOwner::UM_PIR);
if (pinManager.allocatePin(PIRsensorPin, false, PinOwner::UM_PIR)) { if (pinManager.allocatePin(PIRsensorPin, false, PinOwner::UM_PIR)) {
pinMode(PIRsensorPin, INPUT_PULLUP); pinMode(PIRsensorPin, INPUT_PULLUP);
} else { } else {
// allocation failed // allocation failed
PIRsensorPin = -1; PIRsensorPin = -1;
enabled = false; enabled = false;
} }
} }
if (enabled) { if (enabled) {
sensorPinState = digitalRead(PIRsensorPin); sensorPinState = digitalRead(PIRsensorPin);
} }
} }
DEBUG_PRINTLN(F(" config (re)loaded.")); DEBUG_PRINTLN(F(" config (re)loaded."));
} }
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features // use "return !top["newestParameter"].isNull();" when updating Usermod with new features
return !top[FPSTR(_offOnly)].isNull(); return !top[FPSTR(_offOnly)].isNull();
} }
/** /**
* getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!). * getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!).
* This could be used in the future for the system to determine whether your usermod is installed. * This could be used in the future for the system to determine whether your usermod is installed.
*/ */
uint16_t getId() uint16_t getId()
{ {
return USERMOD_ID_PIRSWITCH; return USERMOD_ID_PIRSWITCH;
} }
}; };
// strings to reduce flash memory usage (used more than twice) // strings to reduce flash memory usage (used more than twice)
const char PIRsensorSwitch::_name[] PROGMEM = "PIRsensorSwitch"; const char PIRsensorSwitch::_name[] PROGMEM = "PIRsensorSwitch";
const char PIRsensorSwitch::_enabled[] PROGMEM = "PIRenabled"; const char PIRsensorSwitch::_enabled[] PROGMEM = "PIRenabled";
const char PIRsensorSwitch::_switchOffDelay[] PROGMEM = "PIRoffSec"; const char PIRsensorSwitch::_switchOffDelay[] PROGMEM = "PIRoffSec";
const char PIRsensorSwitch::_onPreset[] PROGMEM = "on-preset"; const char PIRsensorSwitch::_onPreset[] PROGMEM = "on-preset";
const char PIRsensorSwitch::_offPreset[] PROGMEM = "off-preset"; const char PIRsensorSwitch::_offPreset[] PROGMEM = "off-preset";
const char PIRsensorSwitch::_nightTime[] PROGMEM = "nighttime-only"; const char PIRsensorSwitch::_nightTime[] PROGMEM = "nighttime-only";
const char PIRsensorSwitch::_mqttOnly[] PROGMEM = "mqtt-only"; const char PIRsensorSwitch::_mqttOnly[] PROGMEM = "mqtt-only";
const char PIRsensorSwitch::_offOnly[] PROGMEM = "off-only"; const char PIRsensorSwitch::_offOnly[] PROGMEM = "off-only";

View File

@ -81,7 +81,9 @@ class UsermodTemperature : public Usermod {
temperature = readDallas(); temperature = readDallas();
lastMeasurement = millis(); lastMeasurement = millis();
waitingForConversion = false; waitingForConversion = false;
DEBUG_PRINTF("Read temperature %2.1f.\n", temperature); //DEBUG_PRINTF("Read temperature %2.1f.\n", temperature); // does not work properly on 8266
DEBUG_PRINT(F("Read temperature "));
DEBUG_PRINTLN(temperature);
} }
bool findSensor() { bool findSensor() {

View File

@ -317,15 +317,15 @@ class MultiRelay : public Usermod {
* addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object). * addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object).
* Values in the state object may be modified by connected clients * Values in the state object may be modified by connected clients
*/ */
void addToJsonState(JsonObject &root) { //void addToJsonState(JsonObject &root) {
} //}
/** /**
* readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object). * readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object).
* Values in the state object may be modified by connected clients * Values in the state object may be modified by connected clients
*/ */
void readFromJsonState(JsonObject &root) { //void readFromJsonState(JsonObject &root) {
} //}
/** /**
* provide the changeable values * provide the changeable values
@ -335,11 +335,12 @@ class MultiRelay : public Usermod {
top[FPSTR(_enabled)] = enabled; top[FPSTR(_enabled)] = enabled;
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) { for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
String parName = FPSTR(_relay_str); parName += "-"; parName += i; parName += "-"; String parName = FPSTR(_relay_str); parName += '-'; parName += i;
top[parName+"pin"] = _relay[i].pin; JsonObject relay = top.createNestedObject(parName);
top[parName+FPSTR(_activeHigh)] = _relay[i].mode; relay["pin"] = _relay[i].pin;
top[parName+FPSTR(_delay_str)] = _relay[i].delay; relay[FPSTR(_activeHigh)] = _relay[i].mode;
top[parName+FPSTR(_external)] = _relay[i].external; relay[FPSTR(_delay_str)] = _relay[i].delay;
relay[FPSTR(_external)] = _relay[i].external;
} }
DEBUG_PRINTLN(F("MultiRelay config saved.")); DEBUG_PRINTLN(F("MultiRelay config saved."));
} }
@ -363,12 +364,19 @@ class MultiRelay : public Usermod {
enabled = top[FPSTR(_enabled)] | enabled; enabled = top[FPSTR(_enabled)] | enabled;
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) { for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
String parName = FPSTR(_relay_str); parName += "-"; parName += i; parName += "-"; String parName = FPSTR(_relay_str); parName += '-'; parName += i;
oldPin[i] = _relay[i].pin; oldPin[i] = _relay[i].pin;
_relay[i].pin = top[parName]["pin"] | _relay[i].pin;
_relay[i].mode = top[parName][FPSTR(_activeHigh)] | _relay[i].mode;
_relay[i].external = top[parName][FPSTR(_external)] | _relay[i].external;
_relay[i].delay = top[parName][FPSTR(_delay_str)] | _relay[i].delay;
// begin backwards compatibility (beta) remove when 0.13 is released
parName += '-';
_relay[i].pin = top[parName+"pin"] | _relay[i].pin; _relay[i].pin = top[parName+"pin"] | _relay[i].pin;
_relay[i].mode = top[parName+FPSTR(_activeHigh)] | _relay[i].mode; _relay[i].mode = top[parName+FPSTR(_activeHigh)] | _relay[i].mode;
_relay[i].external = top[parName+FPSTR(_external)] | _relay[i].external; _relay[i].external = top[parName+FPSTR(_external)] | _relay[i].external;
_relay[i].delay = top[parName+FPSTR(_delay_str)] | _relay[i].delay; _relay[i].delay = top[parName+FPSTR(_delay_str)] | _relay[i].delay;
// end compatibility
_relay[i].delay = min(600,max(0,abs((int)_relay[i].delay))); // bounds checking max 10min _relay[i].delay = min(600,max(0,abs((int)_relay[i].delay))); // bounds checking max 10min
} }
@ -396,7 +404,7 @@ class MultiRelay : public Usermod {
DEBUG_PRINTLN(F(" config (re)loaded.")); DEBUG_PRINTLN(F(" config (re)loaded."));
} }
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features // use "return !top["newestParameter"].isNull();" when updating Usermod with new features
return true; return !top[F("relay-0")]["pin"].isNull();
} }
/** /**

View File

@ -114,6 +114,7 @@ class FourLineDisplayUsermod : public Usermod {
U8X8 *u8x8 = nullptr; // pointer to U8X8 display object U8X8 *u8x8 = nullptr; // pointer to U8X8 display object
#ifndef FLD_SPI_DEFAULT #ifndef FLD_SPI_DEFAULT
int8_t ioPin[5] = {FLD_PIN_SCL, FLD_PIN_SDA, -1, -1, -1}; // I2C pins: SCL, SDA int8_t ioPin[5] = {FLD_PIN_SCL, FLD_PIN_SDA, -1, -1, -1}; // I2C pins: SCL, SDA
uint32_t ioFrequency = 400000; // in Hz (minimum is 100000, baseline is 400000 and maximum should be 3400000)
DisplayType type = SSD1306; // display type DisplayType type = SSD1306; // display type
#else #else
int8_t ioPin[5] = {FLD_PIN_CLOCKSPI, FLD_PIN_DATASPI, FLD_PIN_CS, FLD_PIN_DC, FLD_PIN_RESET}; // SPI pins: CLK, MOSI, CS, DC, RST int8_t ioPin[5] = {FLD_PIN_CLOCKSPI, FLD_PIN_DATASPI, FLD_PIN_CS, FLD_PIN_DC, FLD_PIN_RESET}; // SPI pins: CLK, MOSI, CS, DC, RST
@ -155,6 +156,7 @@ class FourLineDisplayUsermod : public Usermod {
static const char _flip[]; static const char _flip[];
static const char _sleepMode[]; static const char _sleepMode[];
static const char _clockMode[]; static const char _clockMode[];
static const char _busClkFrequency[];
// If display does not work or looks corrupted check the // If display does not work or looks corrupted check the
// constructor reference: // constructor reference:
@ -248,6 +250,7 @@ class FourLineDisplayUsermod : public Usermod {
initDone = true; initDone = true;
DEBUG_PRINTLN(F("Starting display.")); DEBUG_PRINTLN(F("Starting display."));
if (!(type == SSD1306_SPI || type == SSD1306_SPI64)) u8x8->setBusClock(ioFrequency); // can be used for SPI too
u8x8->begin(); u8x8->begin();
setFlipMode(flip); setFlipMode(flip);
setContrast(contrast); //Contrast setup will help to preserve OLED lifetime. In case OLED need to be brighter increase number up to 255 setContrast(contrast); //Contrast setup will help to preserve OLED lifetime. In case OLED need to be brighter increase number up to 255
@ -683,6 +686,7 @@ class FourLineDisplayUsermod : public Usermod {
top[FPSTR(_screenTimeOut)] = screenTimeout/1000; top[FPSTR(_screenTimeOut)] = screenTimeout/1000;
top[FPSTR(_sleepMode)] = (bool) sleepMode; top[FPSTR(_sleepMode)] = (bool) sleepMode;
top[FPSTR(_clockMode)] = (bool) clockMode; top[FPSTR(_clockMode)] = (bool) clockMode;
top[FPSTR(_busClkFrequency)] = ioFrequency/1000;
DEBUG_PRINTLN(F("4 Line Display config saved.")); DEBUG_PRINTLN(F("4 Line Display config saved."));
} }
@ -714,6 +718,7 @@ class FourLineDisplayUsermod : public Usermod {
screenTimeout = (top[FPSTR(_screenTimeOut)] | screenTimeout/1000) * 1000; screenTimeout = (top[FPSTR(_screenTimeOut)] | screenTimeout/1000) * 1000;
sleepMode = top[FPSTR(_sleepMode)] | sleepMode; sleepMode = top[FPSTR(_sleepMode)] | sleepMode;
clockMode = top[FPSTR(_clockMode)] | clockMode; clockMode = top[FPSTR(_clockMode)] | clockMode;
ioFrequency = min(3400, max(100, (int)(top[FPSTR(_busClkFrequency)] | ioFrequency/1000))) * 1000; // limit frequency
DEBUG_PRINT(FPSTR(_name)); DEBUG_PRINT(FPSTR(_name));
if (!initDone) { if (!initDone) {
@ -739,12 +744,13 @@ class FourLineDisplayUsermod : public Usermod {
setup(); setup();
needsRedraw |= true; needsRedraw |= true;
} }
if (!(type == SSD1306_SPI || type == SSD1306_SPI64)) u8x8->setBusClock(ioFrequency); // can be used for SPI too
setContrast(contrast); setContrast(contrast);
setFlipMode(flip); setFlipMode(flip);
if (needsRedraw && !wakeDisplay()) redraw(true); if (needsRedraw && !wakeDisplay()) redraw(true);
} }
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features // use "return !top["newestParameter"].isNull();" when updating Usermod with new features
return !(top["pin"][2]).isNull(); return !(top[_busClkFrequency]).isNull();
} }
/* /*
@ -757,10 +763,11 @@ class FourLineDisplayUsermod : public Usermod {
}; };
// strings to reduce flash memory usage (used more than twice) // strings to reduce flash memory usage (used more than twice)
const char FourLineDisplayUsermod::_name[] PROGMEM = "4LineDisplay"; const char FourLineDisplayUsermod::_name[] PROGMEM = "4LineDisplay";
const char FourLineDisplayUsermod::_contrast[] PROGMEM = "contrast"; const char FourLineDisplayUsermod::_contrast[] PROGMEM = "contrast";
const char FourLineDisplayUsermod::_refreshRate[] PROGMEM = "refreshRateSec"; const char FourLineDisplayUsermod::_refreshRate[] PROGMEM = "refreshRateSec";
const char FourLineDisplayUsermod::_screenTimeOut[] PROGMEM = "screenTimeOutSec"; const char FourLineDisplayUsermod::_screenTimeOut[] PROGMEM = "screenTimeOutSec";
const char FourLineDisplayUsermod::_flip[] PROGMEM = "flip"; const char FourLineDisplayUsermod::_flip[] PROGMEM = "flip";
const char FourLineDisplayUsermod::_sleepMode[] PROGMEM = "sleepMode"; const char FourLineDisplayUsermod::_sleepMode[] PROGMEM = "sleepMode";
const char FourLineDisplayUsermod::_clockMode[] PROGMEM = "clockMode"; const char FourLineDisplayUsermod::_clockMode[] PROGMEM = "clockMode";
const char FourLineDisplayUsermod::_busClkFrequency[] PROGMEM = "i2c-freq-kHz";

View File

@ -4,18 +4,6 @@
#ifdef WLED_USE_ETHERNET #ifdef WLED_USE_ETHERNET
#include "pin_manager.h" #include "pin_manager.h"
// The following six pins are neither configurable nor
// can they be re-assigned through IOMUX / GPIO matrix.
// See https://docs.espressif.com/projects/esp-idf/en/latest/esp32/hw-reference/esp32/get-started-ethernet-kit-v1.1.html#ip101gri-phy-interface
const managed_pin_type esp32_nonconfigurable_ethernet_pins[6] = {
{ 21, true }, // RMII EMAC TX EN == When high, clocks the data on TXD0 and TXD1 to transmitter
{ 19, true }, // RMII EMAC TXD0 == First bit of transmitted data
{ 22, true }, // RMII EMAC TXD1 == Second bit of transmitted data
{ 25, false }, // RMII EMAC RXD0 == First bit of received data
{ 26, false }, // RMII EMAC RXD1 == Second bit of received data
{ 27, true }, // RMII EMAC CRS_DV == Carrier Sense and RX Data Valid
};
// For ESP32, the remaining five pins are at least somewhat configurable. // For ESP32, the remaining five pins are at least somewhat configurable.
// eth_address is in range [0..31], indicates which PHY (MAC?) address should be allocated to the interface // eth_address is in range [0..31], indicates which PHY (MAC?) address should be allocated to the interface
// eth_power is an output GPIO pin used to enable/disable the ethernet port (and/or external oscillator) // eth_power is an output GPIO pin used to enable/disable the ethernet port (and/or external oscillator)
@ -37,15 +25,16 @@ typedef struct EthernetSettings {
eth_clock_mode_t eth_clk_mode; eth_clock_mode_t eth_clk_mode;
} ethernet_settings; } ethernet_settings;
ethernet_settings ethernetBoards[] = { const ethernet_settings ethernetBoards[] = {
// None // None
{ {
}, },
// WT32-EHT01 // WT32-EHT01
// (*) NOTE: silkscreen on board revision v1.2 may be wrong: // Please note, from my testing only these pins work for LED outputs:
// silkscreen on v1.2 says IO35, but appears to be IO5 // IO2, IO4, IO12, IO14, IO15
// silkscreen on v1.2 says RXD, and appears to be IO35 // These pins do not appear to work from my testing:
// IO35, IO36, IO39
{ {
1, // eth_address, 1, // eth_address,
16, // eth_power, 16, // eth_power,
@ -97,14 +86,27 @@ ethernet_settings ethernetBoards[] = {
// ESP3DEUXQuattro // ESP3DEUXQuattro
{ {
1, // eth_address, 1, // eth_address,
-1, // eth_power, -1, // eth_power,
23, // eth_mdc, 23, // eth_mdc,
18, // eth_mdio, 18, // eth_mdio,
ETH_PHY_LAN8720, // eth_type, ETH_PHY_LAN8720, // eth_type,
ETH_CLOCK_GPIO17_OUT // eth_clk_mode ETH_CLOCK_GPIO17_OUT // eth_clk_mode
} }
}; };
#endif #endif
#define WLED_ETH_RSVD_PINS_COUNT 6
// The following six pins are neither configurable nor
// can they be re-assigned through IOMUX / GPIO matrix.
// See https://docs.espressif.com/projects/esp-idf/en/latest/esp32/hw-reference/esp32/get-started-ethernet-kit-v1.1.html#ip101gri-phy-interface
const managed_pin_type esp32_nonconfigurable_ethernet_pins[WLED_ETH_RSVD_PINS_COUNT] = {
{ 21, true }, // RMII EMAC TX EN == When high, clocks the data on TXD0 and TXD1 to transmitter
{ 19, true }, // RMII EMAC TXD0 == First bit of transmitted data
{ 22, true }, // RMII EMAC TXD1 == Second bit of transmitted data
{ 25, false }, // RMII EMAC RXD0 == First bit of received data
{ 26, false }, // RMII EMAC RXD1 == Second bit of received data
{ 27, true }, // RMII EMAC CRS_DV == Carrier Sense and RX Data Valid
};
#endif #endif

View File

@ -1,4 +1,5 @@
#include "wled.h" #include "wled.h"
#include "wled_ethernet.h"
/* /*
* Sending XML status files to client * Sending XML status files to client
@ -186,6 +187,52 @@ void sappends(char stype, const char* key, char* val)
} }
} }
void extractPin(JsonObject &obj, const char *key) {
if (obj[key].is<JsonArray>()) {
JsonArray pins = obj[key].as<JsonArray>();
for (JsonVariant pv : pins) {
if (pv.as<int>() > -1) { oappend(","); oappendi(pv.as<int>()); }
}
} else {
if (obj[key].as<int>() > -1) { oappend(","); oappendi(obj[key].as<int>()); }
}
}
// oappens used pins by scanning JsonObject (1 level deep)
void fillUMPins(JsonObject &mods)
{
for (JsonPair kv : mods) {
// kv.key() is usermod name or subobject key
// kv.value() is object itself
JsonObject obj = kv.value();
if (!obj.isNull()) {
// element is an JsonObject
if (!obj["pin"].isNull()) {
extractPin(obj, "pin");
} else {
// scan keys (just one level deep as is possible with usermods)
for (JsonPair so : obj) {
const char *key = so.key().c_str();
if (strstr(key, "pin")) {
// we found a key containing "pin" substring
if (strlen(strstr(key, "pin")) == 3) {
// and it is at the end, we found another pin
extractPin(obj, key);
continue;
}
}
if (!obj[so.key()].is<JsonObject>()) continue;
JsonObject subObj = obj[so.key()];
if (!subObj["pin"].isNull()) {
// get pins from subobject
extractPin(subObj, "pin");
}
}
}
}
}
}
//get values for settings form in javascript //get values for settings form in javascript
void getSettingsJS(byte subPage, char* dest) void getSettingsJS(byte subPage, char* dest)
@ -198,7 +245,8 @@ void getSettingsJS(byte subPage, char* dest)
if (subPage <1 || subPage >8) return; if (subPage <1 || subPage >8) return;
if (subPage == 1) { if (subPage == 1)
{
sappends('s',SET_F("CS"),clientSSID); sappends('s',SET_F("CS"),clientSSID);
byte l = strlen(clientPass); byte l = strlen(clientPass);
@ -264,50 +312,53 @@ void getSettingsJS(byte subPage, char* dest)
} }
} }
if (subPage == 2) { if (subPage == 2)
{
char nS[8]; char nS[8];
// add reserved and usermod pins as d.um_p array // add reserved and usermod pins as d.um_p array
oappend(SET_F("d.um_p=[6,7,8,9,10,11"));
DynamicJsonDocument doc(JSON_BUFFER_SIZE/2); DynamicJsonDocument doc(JSON_BUFFER_SIZE/2);
JsonObject mods = doc.createNestedObject(F("um")); JsonObject mods = doc.createNestedObject(F("um"));
usermods.addToConfig(mods); usermods.addToConfig(mods);
oappend(SET_F("d.um_p=[")); if (!mods.isNull()) fillUMPins(mods);
if (!mods.isNull()) {
uint8_t i=0; #ifdef WLED_ENABLE_DMX
for (JsonPair kv : mods) { oappend(SET_F(",2")); // DMX hardcoded pin
if (!kv.value().isNull()) { #endif
// element is an JsonObject
JsonObject obj = kv.value(); //Note: Using pin 3 (RX) disables Adalight / Serial JSON
if (obj["pin"] != nullptr) {
if (obj["pin"].is<JsonArray>()) { #ifdef WLED_DEBUG
JsonArray pins = obj["pin"].as<JsonArray>(); oappend(SET_F(",1")); // debug output (TX) pin
for (JsonVariant pv : pins) { #endif
if (i++) oappend(SET_F(","));
oappendi(pv.as<int>()); #if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_PSRAM)
} if (psramFound()) oappend(SET_F(",16,17")); // GPIO16 & GPIO17 reserved for SPI RAM
} else { #endif
if (i++) oappend(SET_F(","));
oappendi(obj["pin"].as<int>()); #ifdef WLED_USE_ETHERNET
} if (ethernetType != WLED_ETH_NONE && ethernetType < WLED_NUM_ETH_TYPES) {
} for (uint8_t p=0; p<WLED_ETH_RSVD_PINS_COUNT; p++) { oappend(","); oappend(itoa(esp32_nonconfigurable_ethernet_pins[p].pin,nS,10)); }
} if (ethernetBoards[ethernetType].eth_power>=0) { oappend(","); oappend(itoa(ethernetBoards[ethernetType].eth_power,nS,10)); }
if (ethernetBoards[ethernetType].eth_mdc>=0) { oappend(","); oappend(itoa(ethernetBoards[ethernetType].eth_mdc,nS,10)); }
if (ethernetBoards[ethernetType].eth_mdio>=0) { oappend(","); oappend(itoa(ethernetBoards[ethernetType].eth_mdio,nS,10)); }
switch (ethernetBoards[ethernetType].eth_clk_mode) {
case ETH_CLOCK_GPIO0_IN:
case ETH_CLOCK_GPIO0_OUT:
oappend(SET_F(",0"));
break;
case ETH_CLOCK_GPIO16_OUT:
oappend(SET_F(",16"));
break;
case ETH_CLOCK_GPIO17_OUT:
oappend(SET_F(",17"));
break;
} }
if (i) oappend(SET_F(","));
oappend(SET_F("6,7,8,9,10,11")); // flash memory pins
#ifdef WLED_ENABLE_DMX
oappend(SET_F(",2")); // DMX hardcoded pin
#endif
//Adalight / Serial in requires pin 3 to be unused. However, Serial input can not be prevented by WLED
#ifdef WLED_DEBUG
oappend(SET_F(",1")); // debug output (TX) pin
#endif
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_PSRAM)
if (psramFound()) oappend(SET_F(",16,17")); // GPIO16 & GPIO17 reserved for SPI RAM
#endif
//TODO: add reservations for Ethernet shield pins
#ifdef WLED_USE_ETHERNET
#endif
} }
#endif
oappend(SET_F("];")); oappend(SET_F("];"));
// set limits // set limits
@ -338,9 +389,9 @@ void getSettingsJS(byte subPage, char* dest)
uint8_t nPins = bus->getPins(pins); uint8_t nPins = bus->getPins(pins);
for (uint8_t i = 0; i < nPins; i++) { for (uint8_t i = 0; i < nPins; i++) {
lp[1] = 48+i; lp[1] = 48+i;
if (pinManager.isPinOk(pins[i])) sappend('v', lp, pins[i]); if (pinManager.isPinOk(pins[i])) sappend('v',lp,pins[i]);
} }
sappend('v', lc, bus->getLength()); sappend('v',lc,bus->getLength());
sappend('v',lt,bus->getType()); sappend('v',lt,bus->getType());
sappend('v',co,bus->getColorOrder()); sappend('v',co,bus->getColorOrder());
sappend('v',ls,bus->getStart()); sappend('v',ls,bus->getStart());