diff --git a/interface/src/app/main/SensorsAnalogDialog.tsx b/interface/src/app/main/SensorsAnalogDialog.tsx
index 179d1fc2b..83e29b635 100644
--- a/interface/src/app/main/SensorsAnalogDialog.tsx
+++ b/interface/src/app/main/SensorsAnalogDialog.tsx
@@ -115,6 +115,23 @@ const SensorsAnalogDialog = ({
[]
);
+ const analogGPIOMenuItems = () =>
+ // add selectedItem.g to the list
+ [
+ ...(analogGPIOList?.includes(selectedItem.g) || selectedItem.g === undefined
+ ? analogGPIOList
+ : [selectedItem.g, ...analogGPIOList])
+ ]
+ .filter((gpio, idx, arr) => arr.indexOf(gpio) === idx)
+ .sort((a, b) => a - b)
+ .map((gpio: number) => {
+ return (
+
+ );
+ });
+
// Reset form when dialog opens or selectedItem changes
useEffect(() => {
if (open) {
@@ -162,15 +179,11 @@ const SensorsAnalogDialog = ({
label="GPIO"
value={editItem.g}
sx={{ width: '9ch' }}
- disabled={editItem.s}
+ disabled={editItem.s || !creating}
select
onChange={updateFormValue}
>
- {analogGPIOList?.map((gpio: number) => (
-
- ))}
+ {analogGPIOMenuItems()}
= ({
error={!!errors}
{...rest}
aria-label="Error"
- slotProps={{
- inputLabel: {
- style: rest.disabled ? { color: 'grey' } : undefined
+ sx={{
+ '& .MuiInputBase-input.Mui-disabled': {
+ WebkitTextFillColor: 'grey'
}
}}
+ {...(rest.disabled && {
+ slotProps: {
+ select: {
+ IconComponent: () => null
+ },
+ inputLabel: {
+ style: { color: 'grey' }
+ }
+ }
+ })}
+ color={rest.disabled ? 'secondary' : 'primary'}
/>
{errors?.map((e) => (
diff --git a/src/core/analogsensor.cpp b/src/core/analogsensor.cpp
index 988cca9a6..2a06ef9b5 100644
--- a/src/core/analogsensor.cpp
+++ b/src/core/analogsensor.cpp
@@ -472,8 +472,9 @@ void AnalogSensor::loop() {
measure(); // take the measurements
}
-// update analog information name and offset
+// update analog information name, offset, factor, uom, type, deleted, is_system
// a type value of -1 is used to delete the sensor
+// the gpio is the key
bool AnalogSensor::update(uint8_t gpio, std::string & name, double offset, double factor, uint8_t uom, int8_t type, bool deleted, bool is_system) {
// first see if we can find the sensor in our customization list
bool found_sensor = false;
@@ -521,7 +522,7 @@ bool AnalogSensor::update(uint8_t gpio, std::string & name, double offset, doubl
}
// we didn't find it, it's new, so create and store it in the customization list
- // gpio is already checked in web interface, should never trigger.
+ // gpio is already checked in web interface
if (!found_sensor && EMSESP::system_.is_valid_gpio(gpio)) {
found_sensor = true;
EMSESP::webCustomizationService.update([&](WebCustomization & settings) {
@@ -534,7 +535,7 @@ bool AnalogSensor::update(uint8_t gpio, std::string & name, double offset, doubl
newSensor.type = type;
newSensor.is_system = is_system;
settings.analogCustomizations.push_back(newSensor);
- LOG_DEBUG("Adding new customization for analog sensor GPIO %02d", gpio);
+ LOG_DEBUG("Adding customization for analog sensor GPIO %02d", gpio);
return StateUpdateResult::CHANGED; // persist the change
});
}
diff --git a/src/core/system.cpp b/src/core/system.cpp
index f24a3f9e1..f78560fdc 100644
--- a/src/core/system.cpp
+++ b/src/core/system.cpp
@@ -415,8 +415,8 @@ void System::reload_settings() {
EMSESP::webSettingsService.read([&](WebSettings & settings) {
version_ = settings.version;
// first check gpios, priority to rx and tx
- rx_gpio_ = is_valid_gpio(settings.rx_gpio) ? settings.rx_gpio : 0;
- tx_gpio_ = is_valid_gpio(settings.tx_gpio) ? settings.tx_gpio : 0;
+ rx_gpio_ = is_valid_gpio(settings.rx_gpio);
+ tx_gpio_ = is_valid_gpio(settings.tx_gpio);
pbutton_gpio_ = is_valid_gpio(settings.pbutton_gpio) ? settings.pbutton_gpio : 0;
dallas_gpio_ = is_valid_gpio(settings.dallas_gpio) ? settings.dallas_gpio : 0;
led_gpio_ = is_valid_gpio(settings.led_gpio) ? settings.led_gpio : 0;
@@ -456,9 +456,9 @@ void System::reload_settings() {
});
}
-// check for valid ESP32 pins
-bool System::is_valid_gpio(uint8_t pin) {
- auto valid_gpios = valid_gpio_list();
+// check if a pin is valid ESP32 pin and not used by application settings
+bool System::is_valid_gpio(uint8_t pin, bool exclude_used) {
+ auto valid_gpios = valid_gpio_list(exclude_used);
return std::find(valid_gpios.begin(), valid_gpios.end(), pin) != valid_gpios.end();
}
@@ -2356,12 +2356,12 @@ std::vector System::string_range_to_vector(const std::string & range) {
return valid_gpios;
}
-// return a list of valid GPIOs for the ESP32 board
+// return a list of valid GPIOs for the ESP32 board that can be used
// notes:
-// - we allow 0, which is used sometimes to indicate a disabled pin
-// - also allow input only pins are accepted (34-39) on some boards
+// - we allow 0, which is used sometimes to indicate a disabled pin (e.g. button, led)
+// - we also allow input only pins are accepted (34-39) on some boards, excluding 39
// - and allow pins 33-38 for octal SPI for 32M vchip version on some boards
-std::vector System::valid_gpio_list() {
+std::vector System::valid_gpio_list(bool exclude_used) {
// get free gpios based on board/platform type
#if CONFIG_IDF_TARGET_ESP32C3
// https://www.wemos.cc/en/latest/c3/c3_mini.html
@@ -2390,21 +2390,25 @@ std::vector System::valid_gpio_list() {
valid_gpios.erase(std::remove(valid_gpios.begin(), valid_gpios.end(), 22), valid_gpios.end());
}
- // filter out GPIOs already used in application settings, gpio 0 means disabled, except for pbutton
- for (const auto & gpio : valid_gpios) {
- if (gpio == EMSESP::system_.pbutton_gpio_
- || (gpio
- && (gpio == EMSESP::system_.led_gpio_ || gpio == EMSESP::system_.dallas_gpio_ || gpio == EMSESP::system_.rx_gpio_
- || gpio == EMSESP::system_.tx_gpio_))) {
- valid_gpios.erase(std::remove(valid_gpios.begin(), valid_gpios.end(), gpio), valid_gpios.end());
+ // filter out GPIOs already used in application settings and analog sensors, if enabled
+ if (exclude_used) {
+ // application settings
+ for (const auto & gpio : valid_gpios) {
+ if (gpio == EMSESP::system_.pbutton_gpio_
+ || (gpio
+ && (gpio == EMSESP::system_.led_gpio_ || gpio == EMSESP::system_.dallas_gpio_ || gpio == EMSESP::system_.rx_gpio_
+ || gpio == EMSESP::system_.tx_gpio_))) {
+ valid_gpios.erase(std::remove(valid_gpios.begin(), valid_gpios.end(), gpio), valid_gpios.end());
+ }
}
- }
- // filter out GPIOs already used in analog sensors, if enabled
- if (EMSESP::system_.analog_enabled_) {
- for (const auto & sensor : EMSESP::analogsensor_.sensors()) {
- if (std::find(valid_gpios.begin(), valid_gpios.end(), sensor.gpio()) != valid_gpios.end()) {
- valid_gpios.erase(std::find(valid_gpios.begin(), valid_gpios.end(), sensor.gpio()));
+ // analog sensors
+ if (EMSESP::system_.analog_enabled_) {
+ // TODO: check if core_voltage and supply_voltage are already used
+ for (const auto & sensor : EMSESP::analogsensor_.sensors()) {
+ if (std::find(valid_gpios.begin(), valid_gpios.end(), sensor.gpio()) != valid_gpios.end()) {
+ valid_gpios.erase(std::find(valid_gpios.begin(), valid_gpios.end(), sensor.gpio()));
+ }
}
}
}
diff --git a/src/core/system.h b/src/core/system.h
index a73b9700c..bd6e6500f 100644
--- a/src/core/system.h
+++ b/src/core/system.h
@@ -141,7 +141,7 @@ class System {
static void extractSettings(const char * filename, const char * section, JsonObject output);
static bool saveSettings(const char * filename, const char * section, JsonObject input);
- bool is_valid_gpio(uint8_t pin);
+ bool is_valid_gpio(uint8_t pin, bool exclude_used = false);
static bool load_board_profile(std::vector & data, const std::string & board_profile);
static bool readCommand(const char * data);
@@ -342,7 +342,7 @@ class System {
test_set_all_active_ = n;
}
- static std::vector valid_gpio_list();
+ static std::vector valid_gpio_list(bool exclude_used = false);
#if CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2
float temperature() {
diff --git a/src/web/WebCustomizationService.cpp b/src/web/WebCustomizationService.cpp
index b05e20947..1005a2ae8 100644
--- a/src/web/WebCustomizationService.cpp
+++ b/src/web/WebCustomizationService.cpp
@@ -117,6 +117,13 @@ StateUpdateResult WebCustomization::update(JsonObject root, WebCustomization & c
auto analogJsons = root["as"].as();
for (const JsonObject analogJson : analogJsons) {
// create each of the sensor, overwriting any previous settings
+ // if the gpio is invalid skip the sensor
+ if (!EMSESP::system_.is_valid_gpio(analogJson["gpio"].as(), true)) {
+ EMSESP::logger().warning("Invalid GPIO %d for Sensor %s. Skipping.",
+ analogJson["gpio"].as(),
+ analogJson["name"].as().c_str());
+ continue;
+ }
auto analog = AnalogCustomization();
analog.gpio = analogJson["gpio"];
analog.name = analogJson["name"].as();
diff --git a/src/web/WebDataService.cpp b/src/web/WebDataService.cpp
index 3db967c7d..f040b883b 100644
--- a/src/web/WebDataService.cpp
+++ b/src/web/WebDataService.cpp
@@ -157,8 +157,9 @@ void WebDataService::sensor_data(AsyncWebServerRequest * request) {
root["analog_enabled"] = EMSESP::analog_enabled();
root["platform"] = EMSESP_PLATFORM;
+ // send back a list of valid GPIOs that can be used for analog sensors, excluding those already used
JsonArray valid_gpio_list = root["valid_gpio_list"].to();
- for (const auto & gpio : EMSESP::system_.valid_gpio_list()) {
+ for (const auto & gpio : EMSESP::system_.valid_gpio_list(true)) {
valid_gpio_list.add(gpio);
}