mirror of
https://github.com/rstrouse/ESPSomfy-RTS.git
synced 2026-10-31 13:52:15 +01:00
fix: warn of radio pins that block flashing over USB
GPIO2 on the original ESP32, 46 on the S2/S3 and 8 on the C3/C6 don't stop a normal boot, but held at the wrong level at reset they keep esptool from starting the download mode. The WT32-ETH01 wiring puts TX on GPIO2. Each warning now says what the pin does and what to do: move it for a pin that stops the boot, disconnect it to flash for one that only blocks USB. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
658f5eabeb
commit
92e69d599f
6 changed files with 87 additions and 40 deletions
|
|
@ -1,13 +1,14 @@
|
||||||
// The strapping pins whose level at reset can keep each chip from starting
|
// The strapping pins whose level at reset can keep each chip from starting
|
||||||
// the firmware, as src/BootPins.h has them for the log. A radio that drives
|
// the firmware, or from being flashed over USB, as src/BootPins.h has them
|
||||||
// one at power-up stops the board booting until EN is pressed (issue #34).
|
// for the log. A radio that drives one at reset stops the board booting
|
||||||
// Keep the pins, the radio pins checked and the shared RX/TX rule in step
|
// until EN is pressed (issue #34), or esptool from connecting. Keep the
|
||||||
// with that file.
|
// pins, the radio pins checked and the shared RX/TX rule in step with that
|
||||||
|
// file.
|
||||||
// The minify step joins this file into index.js; Node loads it on its own for
|
// The minify step joins this file into index.js; Node loads it on its own for
|
||||||
// the tests (test/js).
|
// the tests (test/js).
|
||||||
class BootPins {
|
class BootPins {
|
||||||
// A warning for each of the radio config's pins that can stop the board
|
// A warning for each of the radio config's pins that can stop the board
|
||||||
// booting.
|
// booting or being flashed.
|
||||||
static radioWarnings(chipModel, config) {
|
static radioWarnings(chipModel, config) {
|
||||||
const chip = BootPins.CHIPS[chipModel || ''] || {};
|
const chip = BootPins.CHIPS[chipModel || ''] || {};
|
||||||
const warnings = [];
|
const warnings = [];
|
||||||
|
|
@ -15,24 +16,28 @@ class BootPins {
|
||||||
const pin = config[field];
|
const pin = config[field];
|
||||||
if (field === 'RXPin' && pin === config.TXPin) continue;
|
if (field === 'RXPin' && pin === config.TXPin) continue;
|
||||||
const effect = chip[pin];
|
const effect = chip[pin];
|
||||||
if (effect) warnings.push(`${name} is on GPIO-${pin}, a strapping pin: ${effect}. The radio can hold it there and stop the board booting after a power cut, so move it to another pin.`);
|
if (effect) warnings.push(`${name} is on GPIO-${pin}, a strapping pin the radio can drive at reset. ${effect}`);
|
||||||
}
|
}
|
||||||
return warnings;
|
return warnings;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
BootPins.DOWNLOAD = 'Held low, it starts the download mode instead of the firmware: move it to another pin.';
|
||||||
|
BootPins.FLASH_1V8 = 'Held high, it powers the flash at 1.8 V and the board bootloops: move it to another pin.';
|
||||||
|
BootPins.NO_USB_HIGH = 'Held high, it stops flashing over USB, so disconnect it to flash.';
|
||||||
|
BootPins.NO_USB_LOW = 'Held low, it stops flashing over USB, so disconnect it to flash.';
|
||||||
// By the chip model the header sends, '' for the original ESP32.
|
// By the chip model the header sends, '' for the original ESP32.
|
||||||
BootPins.CHIPS = {
|
BootPins.CHIPS = {
|
||||||
'': { 0: 'low at reset starts the download mode', 12: 'high at reset powers the flash at 1.8 V' },
|
'': { 0: BootPins.DOWNLOAD, 12: BootPins.FLASH_1V8, 2: BootPins.NO_USB_HIGH },
|
||||||
s2: { 0: 'low at reset starts the download mode', 45: 'high at reset powers the flash at 1.8 V' },
|
s2: { 0: BootPins.DOWNLOAD, 45: BootPins.FLASH_1V8, 46: BootPins.NO_USB_HIGH },
|
||||||
s3: { 0: 'low at reset starts the download mode', 45: 'high at reset powers the flash at 1.8 V' },
|
s3: { 0: BootPins.DOWNLOAD, 45: BootPins.FLASH_1V8, 46: BootPins.NO_USB_HIGH },
|
||||||
c3: { 9: 'low at reset starts the download mode' },
|
c3: { 9: BootPins.DOWNLOAD, 8: BootPins.NO_USB_LOW },
|
||||||
c5: { 28: 'low at reset starts the download mode' },
|
c5: { 28: BootPins.DOWNLOAD },
|
||||||
c6: { 9: 'low at reset starts the download mode' },
|
c6: { 9: BootPins.DOWNLOAD, 8: BootPins.NO_USB_LOW },
|
||||||
p4: { 35: 'low at reset starts the download mode' },
|
p4: { 35: BootPins.DOWNLOAD },
|
||||||
p4es: { 35: 'low at reset starts the download mode' }
|
p4es: { 35: BootPins.DOWNLOAD }
|
||||||
};
|
};
|
||||||
// The radio pins the CC1101 drives at power-up, before it is set up: GDO0
|
// The radio pins the CC1101 drives: at power-up GDO0 puts out a clock, GDO2
|
||||||
// puts out a clock, GDO2 CHIP_RDYn, and SO follows CHIP_RDYn while CSN is
|
// CHIP_RDYn, and SO follows CHIP_RDYn while CSN is low. Once it is set up,
|
||||||
// low.
|
// GDO2 puts out what it hears.
|
||||||
BootPins.RADIO_PINS = [['TXPin', 'TX'], ['RXPin', 'RX'], ['MISOPin', 'MISO']];
|
BootPins.RADIO_PINS = [['TXPin', 'TX'], ['RXPin', 'RX'], ['MISOPin', 'MISO']];
|
||||||
if (typeof module !== 'undefined' && module.exports) module.exports = { BootPins };
|
if (typeof module !== 'undefined' && module.exports) module.exports = { BootPins };
|
||||||
|
|
|
||||||
|
|
@ -6,30 +6,37 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// The strapping pins whose level at reset can keep the chip from starting
|
// The strapping pins whose level at reset can keep the chip from starting
|
||||||
// the firmware. The ROM reads them before any code runs, so a radio that
|
// the firmware, or from being flashed over USB. The ROM reads them before
|
||||||
// drives one at power-up stops the board booting until EN is pressed (issue
|
// any code runs, so a radio that drives one at reset stops the board booting
|
||||||
// #34). The strapping pins that only pick a download mode, quiet the boot
|
// until EN is pressed (issue #34), or esptool from connecting. The strapping
|
||||||
// log or set the SDIO timing aren't listed. The web UI has the same pins in
|
// pins that only quiet the boot log or set the SDIO timing aren't listed.
|
||||||
// data-src/js/pins.js, and checks the same radio pins as
|
// The web UI has the same pins in data-src/js/pins.js, and checks the same
|
||||||
// transceiver_config_t::apply(): keep the three in step.
|
// radio pins as transceiver_config_t::apply(): keep the three in step.
|
||||||
//
|
constexpr const char *BOOT_PIN_DOWNLOAD = "Held low, it starts the download mode instead of the firmware: move it to another pin.";
|
||||||
// Returns what the pin does at reset, or nullptr for a pin that can't stop
|
constexpr const char *BOOT_PIN_FLASH_1V8 = "Held high, it powers the flash at 1.8 V and the board bootloops: move it to another pin.";
|
||||||
// the boot.
|
constexpr const char *BOOT_PIN_NO_USB_HIGH = "Held high, it stops flashing over USB, so disconnect it to flash.";
|
||||||
|
constexpr const char *BOOT_PIN_NO_USB_LOW = "Held low, it stops flashing over USB, so disconnect it to flash.";
|
||||||
|
|
||||||
|
// Returns what the pin does at reset and what to do, or nullptr for a pin
|
||||||
|
// that is safe.
|
||||||
inline const char *bootPinEffect(uint8_t pin) {
|
inline const char *bootPinEffect(uint8_t pin) {
|
||||||
#if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3)
|
#if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3)
|
||||||
if(pin == 0) return "low at reset starts the download mode";
|
if(pin == 0) return BOOT_PIN_DOWNLOAD;
|
||||||
if(pin == 45) return "high at reset powers the flash at 1.8 V";
|
if(pin == 45) return BOOT_PIN_FLASH_1V8;
|
||||||
|
if(pin == 46) return BOOT_PIN_NO_USB_HIGH;
|
||||||
#elif defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C6)
|
#elif defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C6)
|
||||||
if(pin == 9) return "low at reset starts the download mode";
|
if(pin == 9) return BOOT_PIN_DOWNLOAD;
|
||||||
|
if(pin == 8) return BOOT_PIN_NO_USB_LOW;
|
||||||
#elif defined(CONFIG_IDF_TARGET_ESP32C5)
|
#elif defined(CONFIG_IDF_TARGET_ESP32C5)
|
||||||
if(pin == 28) return "low at reset starts the download mode";
|
if(pin == 28) return BOOT_PIN_DOWNLOAD;
|
||||||
#elif defined(CONFIG_IDF_TARGET_ESP32P4)
|
#elif defined(CONFIG_IDF_TARGET_ESP32P4)
|
||||||
if(pin == 35) return "low at reset starts the download mode";
|
if(pin == 35) return BOOT_PIN_DOWNLOAD;
|
||||||
#else
|
#else
|
||||||
// The original ESP32. MTDI picks the flash voltage unless the
|
// The original ESP32. MTDI picks the flash voltage unless the
|
||||||
// VDD_SDIO efuses are burned.
|
// VDD_SDIO efuses are burned.
|
||||||
if(pin == 0) return "low at reset starts the download mode";
|
if(pin == 0) return BOOT_PIN_DOWNLOAD;
|
||||||
if(pin == 12) return "high at reset powers the flash at 1.8 V";
|
if(pin == 12) return BOOT_PIN_FLASH_1V8;
|
||||||
|
if(pin == 2) return BOOT_PIN_NO_USB_HIGH;
|
||||||
#endif
|
#endif
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -577,7 +577,7 @@ void transceiver_config_t::load() {
|
||||||
}
|
}
|
||||||
static void warnBootPin(const char *name, uint8_t pin) {
|
static void warnBootPin(const char *name, uint8_t pin) {
|
||||||
const char *effect = bootPinEffect(pin);
|
const char *effect = bootPinEffect(pin);
|
||||||
if(effect) ESP_LOGW(TAG, "Radio %s is on GPIO%u, a strapping pin: %s. The radio can hold it there and stop the board booting after a power cut, so move it to another pin.", name, pin, effect);
|
if(effect) ESP_LOGW(TAG, "Radio %s is on GPIO%u, a strapping pin the radio can drive at reset. %s", name, pin, effect);
|
||||||
}
|
}
|
||||||
void transceiver_config_t::apply() {
|
void transceiver_config_t::apply() {
|
||||||
Preferences pref;
|
Preferences pref;
|
||||||
|
|
@ -603,9 +603,10 @@ void transceiver_config_t::apply() {
|
||||||
pref.end();
|
pref.end();
|
||||||
if(!radioInit) return;
|
if(!radioInit) return;
|
||||||
ESP_LOGD(TAG, "Applying radio settings - Setting Data Pins RX:%u TX:%u", this->RXPin, this->TXPin);
|
ESP_LOGD(TAG, "Applying radio settings - Setting Data Pins RX:%u TX:%u", this->RXPin, this->TXPin);
|
||||||
// The pins the CC1101 drives at power-up, before it is set up: GDO0
|
// The pins the CC1101 drives: at power-up GDO0 puts out a clock, GDO2
|
||||||
// puts out a clock, GDO2 CHIP_RDYn, and SO follows CHIP_RDYn while CSN
|
// CHIP_RDYn, and SO follows CHIP_RDYn while CSN is low. Once it is
|
||||||
// is low. The web UI checks the same (data-src/js/pins.js).
|
// set up, GDO2 puts out what it hears. The web UI checks the same
|
||||||
|
// (data-src/js/pins.js).
|
||||||
warnBootPin("TX", this->TXPin);
|
warnBootPin("TX", this->TXPin);
|
||||||
if(this->RXPin != this->TXPin) warnBootPin("RX", this->RXPin);
|
if(this->RXPin != this->TXPin) warnBootPin("RX", this->RXPin);
|
||||||
warnBootPin("MISO", this->MISOPin);
|
warnBootPin("MISO", this->MISOPin);
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,22 @@ test('RX on GPIO12 warns of the flash voltage on the original ESP32', () => {
|
||||||
assert.match(warnings[0], /1\.8 V/);
|
assert.match(warnings[0], /1\.8 V/);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('TX on GPIO2 warns of flashing over USB on the original ESP32', () => {
|
||||||
|
// The WT32-ETH01 wiring in the wiki.
|
||||||
|
const warnings = BootPins.radioWarnings('', Object.assign({}, esp32Defaults, { TXPin: 2 }));
|
||||||
|
assert.equal(warnings.length, 1);
|
||||||
|
assert.match(warnings[0], /^TX is on GPIO-2, .*Held high, it stops flashing over USB, so disconnect it to flash\.$/);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('GPIO8 held low blocks flashing on the C3 and C6', () => {
|
||||||
|
const config = { SCKPin: 4, CSNPin: 7, MOSIPin: 6, MISOPin: 5, TXPin: 10, RXPin: 8 };
|
||||||
|
for (const chip of ['c3', 'c6']) {
|
||||||
|
const warnings = BootPins.radioWarnings(chip, config);
|
||||||
|
assert.equal(warnings.length, 1);
|
||||||
|
assert.match(warnings[0], /^RX is on GPIO-8, .*Held low, it stops flashing over USB/);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
test('an unknown chip model warns of nothing', () => {
|
test('an unknown chip model warns of nothing', () => {
|
||||||
assert.deepEqual(BootPins.radioWarnings('h2', Object.assign({}, esp32Defaults, { RXPin: 12 })), []);
|
assert.deepEqual(BootPins.radioWarnings('h2', Object.assign({}, esp32Defaults, { RXPin: 12 })), []);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -19,10 +19,18 @@ static void test_gpio0_picks_the_boot_mode() {
|
||||||
TEST_ASSERT_NOT_NULL(strstr(effect, "download mode"));
|
TEST_ASSERT_NOT_NULL(strstr(effect, "download mode"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_gpio2_blocks_flashing_over_usb() {
|
||||||
|
// It only counts with GPIO0 low, so the board boots, but esptool can't
|
||||||
|
// start the download mode while it is high.
|
||||||
|
const char *effect = bootPinEffect(2);
|
||||||
|
TEST_ASSERT_NOT_NULL(effect);
|
||||||
|
TEST_ASSERT_NOT_NULL(strstr(effect, "high"));
|
||||||
|
TEST_ASSERT_NOT_NULL(strstr(effect, "USB"));
|
||||||
|
}
|
||||||
|
|
||||||
static void test_harmless_strapping_pins_are_not_listed() {
|
static void test_harmless_strapping_pins_are_not_listed() {
|
||||||
// 2 only matters for the download mode, 5 and 15 set the SDIO timing and
|
// 5 and 15 set the SDIO timing and quiet the boot log. 5 is the default
|
||||||
// the boot log. 5 is the default CSN.
|
// CSN.
|
||||||
TEST_ASSERT_NULL(bootPinEffect(2));
|
|
||||||
TEST_ASSERT_NULL(bootPinEffect(5));
|
TEST_ASSERT_NULL(bootPinEffect(5));
|
||||||
TEST_ASSERT_NULL(bootPinEffect(15));
|
TEST_ASSERT_NULL(bootPinEffect(15));
|
||||||
}
|
}
|
||||||
|
|
@ -34,14 +42,17 @@ static void test_default_radio_pins_are_safe() {
|
||||||
}
|
}
|
||||||
|
|
||||||
static void test_other_chips_boot_pins_are_safe_here() {
|
static void test_other_chips_boot_pins_are_safe_here() {
|
||||||
|
TEST_ASSERT_NULL(bootPinEffect(8));
|
||||||
TEST_ASSERT_NULL(bootPinEffect(9));
|
TEST_ASSERT_NULL(bootPinEffect(9));
|
||||||
TEST_ASSERT_NULL(bootPinEffect(45));
|
TEST_ASSERT_NULL(bootPinEffect(45));
|
||||||
|
TEST_ASSERT_NULL(bootPinEffect(46));
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int, char **) {
|
int main(int, char **) {
|
||||||
UNITY_BEGIN();
|
UNITY_BEGIN();
|
||||||
RUN_TEST(test_gpio12_picks_the_flash_voltage);
|
RUN_TEST(test_gpio12_picks_the_flash_voltage);
|
||||||
RUN_TEST(test_gpio0_picks_the_boot_mode);
|
RUN_TEST(test_gpio0_picks_the_boot_mode);
|
||||||
|
RUN_TEST(test_gpio2_blocks_flashing_over_usb);
|
||||||
RUN_TEST(test_harmless_strapping_pins_are_not_listed);
|
RUN_TEST(test_harmless_strapping_pins_are_not_listed);
|
||||||
RUN_TEST(test_default_radio_pins_are_safe);
|
RUN_TEST(test_default_radio_pins_are_safe);
|
||||||
RUN_TEST(test_other_chips_boot_pins_are_safe_here);
|
RUN_TEST(test_other_chips_boot_pins_are_safe_here);
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,12 @@ static void test_gpio0_picks_the_boot_mode() {
|
||||||
TEST_ASSERT_NOT_NULL(bootPinEffect(0));
|
TEST_ASSERT_NOT_NULL(bootPinEffect(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_gpio46_blocks_flashing_over_usb() {
|
||||||
|
const char *effect = bootPinEffect(46);
|
||||||
|
TEST_ASSERT_NOT_NULL(effect);
|
||||||
|
TEST_ASSERT_NOT_NULL(strstr(effect, "USB"));
|
||||||
|
}
|
||||||
|
|
||||||
static void test_gpio12_is_safe() {
|
static void test_gpio12_is_safe() {
|
||||||
// 12 is the S3's default SCK.
|
// 12 is the S3's default SCK.
|
||||||
TEST_ASSERT_NULL(bootPinEffect(12));
|
TEST_ASSERT_NULL(bootPinEffect(12));
|
||||||
|
|
@ -32,6 +38,7 @@ int main(int, char **) {
|
||||||
UNITY_BEGIN();
|
UNITY_BEGIN();
|
||||||
RUN_TEST(test_gpio45_picks_the_flash_voltage);
|
RUN_TEST(test_gpio45_picks_the_flash_voltage);
|
||||||
RUN_TEST(test_gpio0_picks_the_boot_mode);
|
RUN_TEST(test_gpio0_picks_the_boot_mode);
|
||||||
|
RUN_TEST(test_gpio46_blocks_flashing_over_usb);
|
||||||
RUN_TEST(test_gpio12_is_safe);
|
RUN_TEST(test_gpio12_is_safe);
|
||||||
RUN_TEST(test_default_radio_pins_are_safe);
|
RUN_TEST(test_default_radio_pins_are_safe);
|
||||||
return UNITY_END();
|
return UNITY_END();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue