mirror of
https://github.com/wled/WLED.git
synced 2025-11-28 20:27:43 +00:00
Compare commits
11 Commits
add-report
...
copilot/im
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9dd3759ca0 | ||
|
|
54db224085 | ||
|
|
ccf0702713 | ||
|
|
113cdf33ea | ||
|
|
1e77bc0f8d | ||
|
|
1071bab711 | ||
|
|
b596fcad69 | ||
|
|
66b6aa4e0a | ||
|
|
a2e5f5595d | ||
|
|
fbf7af5cb8 | ||
|
|
8dd0d77ca8 |
@@ -358,6 +358,12 @@ writeChunks(
|
||||
name: "PAGE_settings_pin",
|
||||
method: "gzip",
|
||||
filter: "html-minify"
|
||||
},
|
||||
{
|
||||
file: "settings_pins.htm",
|
||||
name: "PAGE_settings_pins",
|
||||
method: "gzip",
|
||||
filter: "html-minify"
|
||||
}
|
||||
],
|
||||
"wled00/html_settings.h"
|
||||
|
||||
@@ -457,6 +457,7 @@ static_assert(WLED_MAX_BUSSES <= 32, "WLED_MAX_BUSSES exceeds hard limit");
|
||||
#define SUBPAGE_UM 8
|
||||
#define SUBPAGE_UPDATE 9
|
||||
#define SUBPAGE_2D 10
|
||||
#define SUBPAGE_PINS 11
|
||||
#define SUBPAGE_LOCK 251
|
||||
#define SUBPAGE_PINREQ 252
|
||||
#define SUBPAGE_CSS 253
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
<button type=submit id="b" onclick="window.location=getURL('/')">Back</button>
|
||||
<button type="submit" onclick="window.location=getURL('/settings/wifi')">WiFi Setup</button>
|
||||
<button type="submit" onclick="window.location=getURL('/settings/leds')">LED Preferences</button>
|
||||
<button type="submit" onclick="window.location=getURL('/settings/pins')">Pin Manager</button>
|
||||
<button id="2dbtn" type="submit" onclick="window.location=getURL('/settings/2D')">2D Configuration</button>
|
||||
<button type="submit" onclick="window.location=getURL('/settings/ui')">User Interface</button>
|
||||
<button id="dmxbtn" style="display:none;" type="submit" onclick="window.location=getURL('/settings/dmx')">DMX Output</button>
|
||||
|
||||
96
wled00/data/settings_pins.htm
Normal file
96
wled00/data/settings_pins.htm
Normal file
@@ -0,0 +1,96 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
|
||||
<title>Pin Manager</title>
|
||||
<script src="common.js" type="text/javascript"></script>
|
||||
<script>
|
||||
var d=document, loc=false, pinsTimer=null;
|
||||
function gId(s){return d.getElementById(s);}
|
||||
function gN(s){return d.getElementsByName(s)[0];}
|
||||
function S(){
|
||||
getLoc();
|
||||
loadPins();
|
||||
pinsTimer = setInterval(loadPins, 250);
|
||||
}
|
||||
function B(){window.open(getURL('/settings'),'_self');}
|
||||
function getOwnerName(o,t,n){
|
||||
if(!o) return "Unknown";
|
||||
if(o&0x80){
|
||||
switch(o){
|
||||
case 0x81: return "Ethernet";
|
||||
case 0x82: return "LED Digital";
|
||||
case 0x83: return "LED On/Off";
|
||||
case 0x84: return "LED PWM";
|
||||
case 0x85: return getBtnTypeName(t);
|
||||
case 0x86: return "IR Receiver";
|
||||
case 0x87: return "Relay";
|
||||
case 0x88: return "SPI RAM";
|
||||
case 0x89: return "Debug";
|
||||
case 0x8A: return "DMX Output";
|
||||
case 0x8B: return "I2C";
|
||||
case 0x8C: return "SPI";
|
||||
case 0x8D: return "DMX Input";
|
||||
case 0x8E: return "HUB75";
|
||||
}
|
||||
}
|
||||
return n || ("UM #"+o);
|
||||
}
|
||||
function getBtnTypeName(t){
|
||||
var n=["None","Reserved","Push","Push High","Switch","PIR","Touch","Analog","Analog Inv","Touch Switch"];
|
||||
return "Button ("+(n[t]||"?")+")";
|
||||
}
|
||||
function getCaps(c){
|
||||
var r=[],f=["Touch","Analog","PWM","Boot","In Only"];
|
||||
for(var i=0;i<5;i++)if(c&(1<<i))r.push(f[i]);
|
||||
return r.length?r.join(", "):"-";
|
||||
}
|
||||
function loadPins(){
|
||||
fetch(getURL('/json/pins'),{method:'get'})
|
||||
.then(r=>r.json())
|
||||
.then(j=>{
|
||||
var cn="",pins=j.pins||[];
|
||||
if(!pins.length){
|
||||
cn="No pins available.";
|
||||
}else{
|
||||
cn='<table><tr><th>Pin</th><th>Owner</th><th>Functions</th><th>State</th></tr>';
|
||||
for(var p of pins){
|
||||
var st="";
|
||||
if(typeof p.s!=='undefined'){
|
||||
st='<span class="ps" style="background:'+(p.s?'#0b0':'#b00')+'"></span>';
|
||||
}
|
||||
var ow=p.a?getOwnerName(p.o,p.t,p.n):'<span style="color:#0b0">Available</span>';
|
||||
if(typeof p.u!=='undefined')ow+=p.u?' (PU)':' (No PU)';
|
||||
cn+='<tr><td>GPIO '+p.p+'</td><td>'+ow+'</td><td>'+getCaps(p.c||0)+'</td><td>'+st+'</td></tr>';
|
||||
}
|
||||
cn+='</table>';
|
||||
}
|
||||
gId('pins').innerHTML=cn;
|
||||
})
|
||||
.catch(e=>{gId('pins').innerHTML='Error loading pin info';});
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
@import url("style.css");
|
||||
body{text-align:center;background:#222;margin:0;padding:10px}
|
||||
h2{margin:10px 0;color:#fff;font-family:Verdana,sans-serif}
|
||||
table{width:100%;border-collapse:collapse;margin:10px 0;font-family:Verdana,sans-serif;font-size:14px}
|
||||
th,td{padding:8px;border:1px solid #444;color:#fff}
|
||||
th{background:#333}
|
||||
tr:nth-child(even){background:#2a2a2a}
|
||||
tr:nth-child(odd){background:#252525}
|
||||
.ps{display:inline-block;width:12px;height:12px;border-radius:50%}
|
||||
button{background:#333;color:#fff;font-family:Verdana,sans-serif;border:1px solid #444;border-radius:20px;padding:8px 16px;margin:5px;cursor:pointer}
|
||||
button:hover{background:#444}
|
||||
#pins{min-height:200px}
|
||||
</style>
|
||||
</head>
|
||||
<body onload="S()">
|
||||
<h2>Pin Manager</h2>
|
||||
<div id="pins">Loading...</div>
|
||||
<hr>
|
||||
<button type="button" onclick="loadPins()">Refresh</button>
|
||||
<button type="button" onclick="B()">Back</button>
|
||||
</body>
|
||||
</html>
|
||||
@@ -160,6 +160,7 @@ void serializeState(JsonObject root, bool forPreset = false, bool includeBri = t
|
||||
void serializeInfo(JsonObject root);
|
||||
void serializeModeNames(JsonArray arr);
|
||||
void serializeModeData(JsonArray fxdata);
|
||||
void serializePins(JsonObject root);
|
||||
void serveJson(AsyncWebServerRequest* request);
|
||||
#ifdef WLED_ENABLE_JSONLIVE
|
||||
bool serveLiveLeds(AsyncWebServerRequest* request, uint32_t wsClient = 0);
|
||||
|
||||
141
wled00/json.cpp
141
wled00/json.cpp
@@ -1045,6 +1045,142 @@ void serializeNodes(JsonObject root)
|
||||
}
|
||||
}
|
||||
|
||||
// Pin capability flags - only "special" capabilities useful for debugging
|
||||
#define PIN_CAP_TOUCH 0x01 // has touch capability
|
||||
#define PIN_CAP_ADC 0x02 // has ADC capability (analog input)
|
||||
#define PIN_CAP_PWM 0x04 // can be used for PWM (analog LED output)
|
||||
#define PIN_CAP_BOOT 0x08 // bootloader/strapping pin (affects boot mode)
|
||||
#define PIN_CAP_INPUT_ONLY 0x10 // input only pin (cannot be used as output)
|
||||
|
||||
void serializePins(JsonObject root)
|
||||
{
|
||||
JsonArray pins = root.createNestedArray(F("pins"));
|
||||
|
||||
for (int gpio = 0; gpio < WLED_NUM_PINS; gpio++) {
|
||||
bool canInput = PinManager::isPinOk(gpio, false);
|
||||
bool canOutput = PinManager::isPinOk(gpio, true);
|
||||
bool isAllocated = PinManager::isPinAllocated(gpio);
|
||||
|
||||
// Skip pins that are neither usable nor allocated (truly unusable pins)
|
||||
if (!canInput && !canOutput && !isAllocated) continue;
|
||||
|
||||
JsonObject pinObj = pins.createNestedObject();
|
||||
pinObj["p"] = gpio; // pin number
|
||||
|
||||
// Calculate capabilities - only "special" ones for debugging
|
||||
uint8_t caps = 0;
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
// Check ADC capability
|
||||
if (digitalPinToAnalogChannel(gpio) >= 0) caps |= PIN_CAP_ADC;
|
||||
|
||||
// Check touch capability (not available on C3)
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
if (digitalPinToTouchChannel(gpio) >= 0) caps |= PIN_CAP_TOUCH;
|
||||
#endif
|
||||
|
||||
// PWM - all output-capable GPIO can do PWM on ESP32
|
||||
if (canOutput) caps |= PIN_CAP_PWM;
|
||||
|
||||
// Input-only pins (ESP32 classic: GPIO34-39)
|
||||
if (canInput && !canOutput) caps |= PIN_CAP_INPUT_ONLY;
|
||||
|
||||
// Bootloader/strapping pins
|
||||
#if defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||
if (gpio == 0 || gpio == 3 || gpio == 45 || gpio == 46) caps |= PIN_CAP_BOOT;
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32S2)
|
||||
if (gpio == 0 || gpio == 45 || gpio == 46) caps |= PIN_CAP_BOOT;
|
||||
#elif defined(CONFIG_IDF_TARGET_ESP32C3)
|
||||
if (gpio == 2 || gpio == 8 || gpio == 9) caps |= PIN_CAP_BOOT;
|
||||
#else // ESP32 classic
|
||||
if (gpio == 0 || gpio == 2 || gpio == 12 || gpio == 15) caps |= PIN_CAP_BOOT;
|
||||
#endif
|
||||
#else
|
||||
// ESP8266
|
||||
if (gpio < 16) caps |= PIN_CAP_PWM; // all GPIO 0-15 support PWM
|
||||
if (gpio == 17) caps |= PIN_CAP_ADC; // Only A0 (GPIO17) has ADC on ESP8266
|
||||
// ESP8266 strapping pins
|
||||
if (gpio == 0 || gpio == 2 || gpio == 15) caps |= PIN_CAP_BOOT;
|
||||
// GPIO16 is input-only on ESP8266
|
||||
if (gpio == 16) caps |= PIN_CAP_INPUT_ONLY;
|
||||
#endif
|
||||
|
||||
pinObj["c"] = caps; // capabilities
|
||||
|
||||
// Add allocated status and owner
|
||||
PinOwner owner = PinManager::getPinOwner(gpio);
|
||||
pinObj["a"] = isAllocated; // allocated status
|
||||
|
||||
if (isAllocated) {
|
||||
uint8_t ownerVal = static_cast<uint8_t>(owner);
|
||||
pinObj["o"] = ownerVal; // owner ID
|
||||
|
||||
// For usermod owners (low bit not set), try to get the usermod name
|
||||
if (!(ownerVal & 0x80) && ownerVal > 0) {
|
||||
Usermod* um = UsermodManager::lookup(ownerVal);
|
||||
if (um) {
|
||||
// Get usermod name by calling addToConfig and extracting the key
|
||||
StaticJsonDocument<256> tmpDoc;
|
||||
JsonObject tmpObj = tmpDoc.to<JsonObject>();
|
||||
um->addToConfig(tmpObj);
|
||||
// The first key in the object is the usermod name
|
||||
JsonObject::iterator it = tmpObj.begin();
|
||||
if (it != tmpObj.end()) {
|
||||
pinObj["n"] = it->key().c_str(); // usermod name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// For button pins, check if internal pullup/pulldown would be used and get state
|
||||
bool isButton = false;
|
||||
int buttonIndex = -1;
|
||||
for (int b = 0; b < WLED_MAX_BUTTONS; b++) {
|
||||
if (btnPin[b] >= 0 && btnPin[b] == gpio && buttonType[b] != BTN_TYPE_NONE) {
|
||||
isButton = true;
|
||||
buttonIndex = b;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// For relay pin, get state
|
||||
if (isAllocated && gpio == rlyPin) {
|
||||
pinObj["m"] = 1; // mode: output/relay
|
||||
// Relay state: when LEDs are on (bri > 0), relay is in active mode
|
||||
// rlyMde: true = active high, false = active low
|
||||
bool relayActive = bri > 0;
|
||||
bool relayState = relayActive ? rlyMde : !rlyMde;
|
||||
pinObj["s"] = relayState ? 1 : 0;
|
||||
}
|
||||
// For button pins, get state and type using isButtonPressed() from button.cpp
|
||||
else if (isAllocated && isButton && buttonIndex >= 0) {
|
||||
pinObj["m"] = 0; // mode: input/button
|
||||
pinObj["t"] = buttonType[buttonIndex]; // button type
|
||||
// Use isButtonPressed() which handles all button types correctly
|
||||
bool state = isButtonPressed(buttonIndex);
|
||||
pinObj["s"] = state ? 1 : 0; // state
|
||||
|
||||
// Pullup status (when not using touch or analog)
|
||||
if (buttonType[buttonIndex] != BTN_TYPE_TOUCH &&
|
||||
buttonType[buttonIndex] != BTN_TYPE_TOUCH_SWITCH &&
|
||||
buttonType[buttonIndex] != BTN_TYPE_ANALOG &&
|
||||
buttonType[buttonIndex] != BTN_TYPE_ANALOG_INVERTED) {
|
||||
pinObj["u"] = disablePullUp ? 0 : 1; // pullup enabled
|
||||
}
|
||||
}
|
||||
// For other allocated output pins that are simple GPIO (BusOnOff, Multi Relay, etc.)
|
||||
else if (isAllocated && (owner == PinOwner::BusOnOff || owner == PinOwner::UM_MultiRelay)) {
|
||||
pinObj["m"] = 1; // mode: output
|
||||
pinObj["s"] = digitalRead(gpio); // state
|
||||
}
|
||||
// Fallback for button-owned pins not found in btnPin array (show digitalRead state)
|
||||
else if (isAllocated && owner == PinOwner::Button) {
|
||||
pinObj["m"] = 0; // mode: input
|
||||
pinObj["s"] = digitalRead(gpio) == LOW ? 1 : 0; // state (assume active low)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// deserializes mode data string into JsonArray
|
||||
void serializeModeData(JsonArray fxdata)
|
||||
{
|
||||
@@ -1103,7 +1239,7 @@ class LockedJsonResponse: public AsyncJsonResponse {
|
||||
void serveJson(AsyncWebServerRequest* request)
|
||||
{
|
||||
enum class json_target {
|
||||
all, state, info, state_info, nodes, effects, palettes, fxdata, networks, config
|
||||
all, state, info, state_info, nodes, effects, palettes, fxdata, networks, config, pins
|
||||
};
|
||||
json_target subJson = json_target::all;
|
||||
|
||||
@@ -1117,6 +1253,7 @@ void serveJson(AsyncWebServerRequest* request)
|
||||
else if (url.indexOf(F("fxda")) > 0) subJson = json_target::fxdata;
|
||||
else if (url.indexOf(F("net")) > 0) subJson = json_target::networks;
|
||||
else if (url.indexOf(F("cfg")) > 0) subJson = json_target::config;
|
||||
else if (url.indexOf(F("pins")) > 0) subJson = json_target::pins;
|
||||
#ifdef WLED_ENABLE_JSONLIVE
|
||||
else if (url.indexOf("live") > 0) {
|
||||
serveLiveLeds(request);
|
||||
@@ -1160,6 +1297,8 @@ void serveJson(AsyncWebServerRequest* request)
|
||||
serializeNetworks(lDoc); break;
|
||||
case json_target::config:
|
||||
serializeConfig(lDoc); break;
|
||||
case json_target::pins:
|
||||
serializePins(lDoc); break;
|
||||
case json_target::state_info:
|
||||
case json_target::all:
|
||||
JsonObject state = lDoc.createNestedObject("state");
|
||||
|
||||
@@ -715,6 +715,7 @@ void serveSettings(AsyncWebServerRequest* request, bool post) {
|
||||
#ifndef WLED_DISABLE_2D
|
||||
else if (url.indexOf( "2D") > 0) subPage = SUBPAGE_2D;
|
||||
#endif
|
||||
else if (url.indexOf(F("pins")) > 0) subPage = SUBPAGE_PINS;
|
||||
else if (url.indexOf(F("lock")) > 0) subPage = SUBPAGE_LOCK;
|
||||
}
|
||||
else if (url.indexOf("/update") >= 0) subPage = SUBPAGE_UPDATE; // update page, for PIN check
|
||||
@@ -808,6 +809,7 @@ void serveSettings(AsyncWebServerRequest* request, bool post) {
|
||||
#ifndef WLED_DISABLE_2D
|
||||
case SUBPAGE_2D : content = PAGE_settings_2D; len = PAGE_settings_2D_length; break;
|
||||
#endif
|
||||
case SUBPAGE_PINS : content = PAGE_settings_pins; len = PAGE_settings_pins_length; break;
|
||||
case SUBPAGE_LOCK : {
|
||||
correctPIN = !strlen(settingsPIN); // lock if a pin is set
|
||||
serveMessage(request, 200, strlen(settingsPIN) > 0 ? PSTR("Settings locked") : PSTR("No PIN set"), FPSTR(s_redirecting), 1);
|
||||
|
||||
Reference in New Issue
Block a user