diff --git a/esphome/__main__.py b/esphome/__main__.py index 97460e289a..24c125a3b2 100644 --- a/esphome/__main__.py +++ b/esphome/__main__.py @@ -317,9 +317,18 @@ def upload_adafruit_nrfutil(config, port): from nordicsemi.dfu.dfu_transport_serial import DfuTransportSerial from nordicsemi.dfu.dfu_transport import DfuEvent from nordicsemi.dfu.dfu import Dfu + import serial idedata = platformio_api.get_idedata(config) dfu_package = str(Path(idedata.firmware_elf_path).with_suffix(".zip")) + ser = serial.Serial(port, 2400) + time.sleep(DfuTransportSerial.SERIAL_PORT_OPEN_WAIT_TIME) + ser.setDTR(True) + time.sleep(DfuTransportSerial.DTR_RESET_WAIT_TIME) + ser.close() + + time.sleep(2) + serial_backend = DfuTransportSerial(port) serial_backend.register_events_callback(DfuEvent.PROGRESS_EVENT, update_progress) dfu = Dfu(dfu_package, dfu_transport=serial_backend) diff --git a/esphome/components/dfu/__init__.py b/esphome/components/dfu/__init__.py new file mode 100644 index 0000000000..bef8a35ff7 --- /dev/null +++ b/esphome/components/dfu/__init__.py @@ -0,0 +1,24 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.const import ( + CONF_ID, +) + +dfu_ns = cg.esphome_ns.namespace("dfu") +DeviceFirmwareUpdate = dfu_ns.class_("DeviceFirmwareUpdate", cg.Component) + +CONFIG_SCHEMA = cv.All( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(DeviceFirmwareUpdate), + } + ).extend(cv.COMPONENT_SCHEMA), + cv.only_on_nrf52, +) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + # week symbol do not work for some reason so use wrap instaed + cg.add_build_flag("-Wl,--wrap=tud_cdc_line_state_cb") + await cg.register_component(var, config) diff --git a/esphome/components/dfu/dfu.cpp b/esphome/components/dfu/dfu.cpp new file mode 100644 index 0000000000..7f99e15e87 --- /dev/null +++ b/esphome/components/dfu/dfu.cpp @@ -0,0 +1,49 @@ +#include "dfu.h" +#include +#include // for Serial + +extern "C" { + +volatile bool goto_dfu = false; +void __wrap_tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts) { + (void) rts; + + // DTR = false is counted as disconnected + if (!dtr) { + // touch2400 only with first CDC instance (Serial) + if (itf == 0) { + cdc_line_coding_t coding; + tud_cdc_get_line_coding(&coding); + + if (coding.bit_rate == 2400) { + goto_dfu = true; + } + } + } +} +} + +namespace esphome { +namespace dfu { + +#define DFU_DBL_RESET_MEM 0x20007F7C +#define DFU_DBL_RESET_MAGIC 0x5A1AD5 // SALADS +uint32_t *dbl_reset_mem = ((uint32_t *) DFU_DBL_RESET_MEM); + +void DeviceFirmwareUpdate::loop() { + if (goto_dfu) { + goto_dfu = false; + (*dbl_reset_mem) = DFU_DBL_RESET_MAGIC; + pinMode(14, OUTPUT); + pinMode(16, OUTPUT); + digitalWrite(14, 1); + digitalWrite(16, 1); + + delay(50); + digitalWrite(14, 0); + digitalWrite(16, 0); + } +} + +} // namespace dfu +} // namespace esphome diff --git a/esphome/components/dfu/dfu.h b/esphome/components/dfu/dfu.h new file mode 100644 index 0000000000..221406773c --- /dev/null +++ b/esphome/components/dfu/dfu.h @@ -0,0 +1,10 @@ +#pragma once +#include "esphome/core/component.h" + +namespace esphome { +namespace dfu { +class DeviceFirmwareUpdate : public Component { + void loop() override; +}; +} +} diff --git a/esphome/config_validation.py b/esphome/config_validation.py index 95088cba4e..7f11dfdb34 100644 --- a/esphome/config_validation.py +++ b/esphome/config_validation.py @@ -57,6 +57,7 @@ from esphome.const import ( TYPE_GIT, TYPE_LOCAL, VALID_SUBSTITUTIONS_CHARACTERS, + PLATFORM_NRF52, ) from esphome.core import ( CORE, @@ -595,6 +596,7 @@ def only_with_framework(frameworks): only_on_esp32 = only_on(PLATFORM_ESP32) only_on_esp8266 = only_on(PLATFORM_ESP8266) only_on_rp2040 = only_on(PLATFORM_RP2040) +only_on_nrf52 = only_on(PLATFORM_NRF52) only_with_arduino = only_with_framework("arduino") only_with_esp_idf = only_with_framework("esp-idf")