1
0
mirror of https://github.com/esphome/esphome.git synced 2025-10-30 14:43:51 +00:00

init commit for nrf52

This commit is contained in:
Tomasz Duda
2024-01-10 13:34:37 +01:00
parent 0fa0904bc5
commit 237dad7c58
11 changed files with 289 additions and 14 deletions

View File

@@ -35,6 +35,7 @@ from esphome.const import (
PLATFORM_ESP8266,
PLATFORM_RP2040,
SECRETS_FILES,
PLATFORM_NRF52,
)
from esphome.core import CORE, EsphomeError, coroutine
from esphome.helpers import indent, is_ip_address
@@ -297,6 +298,38 @@ def upload_using_platformio(config, port):
return platformio_api.run_platformio_cli_run(config, CORE.verbose, *upload_args)
def update_progress(progress=0, done=False, log_message=""):
import click
del done, log_message # Unused parameters
if progress == 0:
return
if progress % 40 == 0:
click.echo("#", nl=True)
else:
click.echo("#", nl=False)
def upload_adafruit_nrfutil(config, port):
from esphome import platformio_api
from pathlib import Path
from nordicsemi.dfu.dfu_transport_serial import DfuTransportSerial
from nordicsemi.dfu.dfu_transport import DfuEvent
from nordicsemi.dfu.dfu import Dfu
idedata = platformio_api.get_idedata(config)
dfu_package = str(Path(idedata.firmware_elf_path).with_suffix(".zip"))
serial_backend = DfuTransportSerial(port)
serial_backend.register_events_callback(DfuEvent.PROGRESS_EVENT, update_progress)
dfu = Dfu(dfu_package, dfu_transport=serial_backend)
try:
dfu.dfu_send_images()
except Exception as e:
raise EsphomeError(f"Unable to send image: {e}")
def upload_program(config, args, host):
if get_port_type(host) == "SERIAL":
if CORE.target_platform in (PLATFORM_ESP32, PLATFORM_ESP8266):
@@ -309,7 +342,10 @@ def upload_program(config, args, host):
if CORE.target_platform in (PLATFORM_BK72XX, PLATFORM_RTL87XX):
return upload_using_platformio(config, host)
return 1 # Unknown target platform
if CORE.target_platform in (PLATFORM_NRF52):
return upload_adafruit_nrfutil(config, host)
raise EsphomeError(f"Unknown target platform: {CORE.target_platform}")
if CONF_OTA not in config:
raise EsphomeError(

View File

@@ -22,6 +22,7 @@ from esphome.const import (
PLATFORM_ESP32,
PLATFORM_ESP8266,
PLATFORM_RP2040,
PLATFORM_NRF52,
)
from esphome.core import CORE, EsphomeError, Lambda, coroutine_with_priority
from esphome.components.esp32 import add_idf_sdkconfig_option, get_esp32_variant
@@ -101,6 +102,8 @@ ESP_ARDUINO_UNSUPPORTED_USB_UARTS = [USB_SERIAL_JTAG]
UART_SELECTION_RP2040 = [USB_CDC, UART0, UART1]
UART_SELECTION_NRF52 = [USB_CDC]
HARDWARE_UART_TO_UART_SELECTION = {
UART0: logger_ns.UART_SELECTION_UART0,
UART0_SWAP: logger_ns.UART_SELECTION_UART0_SWAP,
@@ -140,6 +143,8 @@ def uart_selection(value):
component = get_libretiny_component()
if component in UART_SELECTION_LIBRETINY:
return cv.one_of(*UART_SELECTION_LIBRETINY[component], upper=True)(value)
if CORE.is_nrf52:
return cv.one_of(*UART_SELECTION_NRF52, upper=True)(value)
raise NotImplementedError
@@ -179,6 +184,7 @@ CONFIG_SCHEMA = cv.All(
rp2040=USB_CDC,
bk72xx=DEFAULT,
rtl87xx=DEFAULT,
nrf52=USB_CDC,
): cv.All(
cv.only_on(
[
@@ -187,6 +193,7 @@ CONFIG_SCHEMA = cv.All(
PLATFORM_RP2040,
PLATFORM_BK72XX,
PLATFORM_RTL87XX,
PLATFORM_NRF52,
]
),
uart_selection,
@@ -263,7 +270,7 @@ async def to_code(config):
if config.get(CONF_ESP8266_STORE_LOG_STRINGS_IN_FLASH):
cg.add_build_flag("-DUSE_STORE_LOG_STR_IN_FLASH")
if CORE.using_arduino:
if CORE.using_arduino and not CORE.is_nrf52:
if config[CONF_HARDWARE_UART] == USB_CDC:
cg.add_build_flag("-DARDUINO_USB_CDC_ON_BOOT=1")
if CORE.is_esp32 and get_esp32_variant() == VARIANT_ESP32C3:

View File

@@ -26,6 +26,10 @@
#include "esphome/core/hal.h"
#include "esphome/core/log.h"
#ifdef USE_NRF52
#include <Adafruit_TinyUSB.h> // for Serial
#endif
namespace esphome {
namespace logger {
@@ -228,10 +232,11 @@ void Logger::pre_setup() {
if (this->baud_rate_ > 0) {
#ifdef USE_ARDUINO
switch (this->uart_) {
#ifndef USE_NRF52
case UART_SELECTION_UART0:
#ifdef USE_ESP8266
case UART_SELECTION_UART0_SWAP:
#endif
#endif // USE_ESP8266
#ifdef USE_RP2040
this->hw_serial_ = &Serial1;
Serial1.begin(this->baud_rate_);
@@ -242,14 +247,15 @@ void Logger::pre_setup() {
#else
this->hw_serial_ = &Serial;
Serial.begin(this->baud_rate_);
#endif
#endif
#endif // ARDUINO_USB_CDC_ON_BOOT
#endif // USE_RP2040
#endif // USE_NRF52
#ifdef USE_ESP8266
if (this->uart_ == UART_SELECTION_UART0_SWAP) {
Serial.swap();
}
Serial.setDebugOutput(ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE);
#endif
#endif // USE_ESP8266
break;
case UART_SELECTION_UART1:
#ifdef USE_RP2040
@@ -258,10 +264,10 @@ void Logger::pre_setup() {
#else
this->hw_serial_ = &Serial1;
Serial1.begin(this->baud_rate_);
#endif
#endif // USE_RP2040
#ifdef USE_ESP8266
Serial1.setDebugOutput(ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE);
#endif
#endif // USE_ESP8266
break;
#if defined(USE_ESP32) && !defined(USE_ESP32_VARIANT_ESP32C3) && !defined(USE_ESP32_VARIANT_ESP32C6) && \
!defined(USE_ESP32_VARIANT_ESP32S2) && !defined(USE_ESP32_VARIANT_ESP32S3)
@@ -290,7 +296,7 @@ void Logger::pre_setup() {
#endif // USE_ESP32_VARIANT_ESP32S2 || USE_ESP32_VARIANT_ESP32S3 || USE_ESP32_VARIANT_ESP32C3
break;
#endif // USE_ESP32 && (USE_ESP32_VARIANT_ESP32S2 || USE_ESP32_VARIANT_ESP32S3 || USE_ESP32_VARIANT_ESP32C3)
#ifdef USE_RP2040
#if defined(USE_RP2040) || defined(USE_NRF52)
case UART_SELECTION_USB_CDC:
this->hw_serial_ = &Serial;
Serial.begin(this->baud_rate_);
@@ -401,7 +407,7 @@ void Logger::set_log_level(const std::string &tag, int log_level) {
this->log_levels_.push_back(LogLevelOverride{tag, log_level});
}
#if defined(USE_ESP32) || defined(USE_ESP8266) || defined(USE_RP2040) || defined(USE_LIBRETINY)
#if defined(USE_ESP32) || defined(USE_ESP8266) || defined(USE_RP2040) || defined(USE_LIBRETINY) || defined(USE_NRF52)
UARTSelection Logger::get_uart() const { return this->uart_; }
#endif

View File

@@ -25,7 +25,7 @@ namespace esphome {
namespace logger {
#if defined(USE_ESP32) || defined(USE_ESP8266) || defined(USE_RP2040) || defined(USE_LIBRETINY)
#if defined(USE_ESP32) || defined(USE_ESP8266) || defined(USE_RP2040) || defined(USE_LIBRETINY) || defined(USE_NRF52)
/** Enum for logging UART selection
*
* Advanced configuration (pin selection, etc) is not supported.
@@ -37,7 +37,9 @@ enum UARTSelection {
UART_SELECTION_UART1,
UART_SELECTION_UART2,
#else
#ifndef USE_NRF52
UART_SELECTION_UART0 = 0,
#endif
UART_SELECTION_UART1,
#if defined(USE_ESP32)
#if !defined(USE_ESP32_VARIANT_ESP32C3) && !defined(USE_ESP32_VARIANT_ESP32C6) && \
@@ -58,7 +60,7 @@ enum UARTSelection {
#ifdef USE_ESP8266
UART_SELECTION_UART0_SWAP,
#endif // USE_ESP8266
#ifdef USE_RP2040
#if defined(USE_RP2040) || defined(USE_NRF52)
UART_SELECTION_USB_CDC,
#endif // USE_RP2040
#endif // USE_LIBRETINY
@@ -78,7 +80,7 @@ class Logger : public Component {
#ifdef USE_ESP_IDF
uart_port_t get_uart_num() const { return uart_num_; }
#endif
#if defined(USE_ESP32) || defined(USE_ESP8266) || defined(USE_RP2040) || defined(USE_LIBRETINY)
#if defined(USE_ESP32) || defined(USE_ESP8266) || defined(USE_RP2040) || defined(USE_LIBRETINY) || defined(USE_NRF52)
void set_uart_selection(UARTSelection uart_selection) { uart_ = uart_selection; }
/// Get the UART used by the logger.
UARTSelection get_uart() const;
@@ -165,6 +167,9 @@ class Logger : public Component {
#if defined(USE_ESP32) || defined(USE_ESP8266) || defined(USE_RP2040)
UARTSelection uart_{UART_SELECTION_UART0};
#endif
#ifdef USE_NRF52
UARTSelection uart_{UART_SELECTION_USB_CDC};
#endif
#ifdef USE_LIBRETINY
UARTSelection uart_{UART_SELECTION_DEFAULT};
#endif

View File

@@ -0,0 +1,112 @@
#ifdef USE_NRF52
#include "gpio.h"
#include "esphome/core/log.h"
#include <functional>
#include <vector>
namespace esphome {
namespace nrf52 {
static const char *const TAG = "nrf52";
static int IRAM_ATTR flags_to_mode(gpio::Flags flags, uint8_t pin) {
// For nRF52 extra modes are available.
// Standard drive is typically 2mA (min 1mA) '0' sink (low) or '1' source (high). High drive (VDD > 2.7V) is typically 10mA low, 9mA high (min 6mA)
// OUTPUT_S0S1 Standard '0', standard '1' same as OUTPUT
// OUTPUT_H0S1 High drive '0', standard '1'
// OUTPUT_S0H1 Standard '0', high drive '1'
// OUTPUT_H0H1 High drive '0', high 'drive '1''
// OUTPUT_D0S1 Disconnect '0' standard '1' (normally used for wired-or connections)
// OUTPUT_D0H1 Disconnect '0', high drive '1' (normally used for wired-or connections)
// OUTPUT_S0D1 Standard '0'. disconnect '1' (normally used for wired-and connections)
// OUTPUT_H0D1 High drive '0', disconnect '1' (normally used for wired-and connections)
// NOTE P0.27 should be only low (standard) drive, low frequency
if (flags == gpio::FLAG_INPUT) { // NOLINT(bugprone-branch-clone)
return INPUT;
} else if (flags == gpio::FLAG_OUTPUT) {
return OUTPUT;
} else if (flags == (gpio::FLAG_INPUT | gpio::FLAG_PULLUP)) {
return INPUT_PULLUP;
} else if (flags == (gpio::FLAG_INPUT | gpio::FLAG_PULLDOWN)) {
return INPUT_PULLDOWN;
} else if (flags == (gpio::FLAG_OUTPUT | gpio::FLAG_OPEN_DRAIN)) {
return OUTPUT_S0D1;
} else {
return INPUT;
}
}
struct ISRPinArg {
uint8_t pin;
bool inverted;
};
//TODO implement
//TODO test
void (*irq_cb)(void *);
void* irq_arg;
static void pin_irq(void){
irq_cb(irq_arg);
}
ISRInternalGPIOPin NRF52GPIOPin::to_isr() const {
auto *arg = new ISRPinArg{}; // NOLINT(cppcoreguidelines-owning-memory)
arg->pin = pin_;
arg->inverted = inverted_;
return ISRInternalGPIOPin((void *) arg);
}
void NRF52GPIOPin::attach_interrupt(void (*func)(void *), void *arg, gpio::InterruptType type) const {
uint32_t mode = ISR_DEFERRED;
switch (type) {
case gpio::INTERRUPT_RISING_EDGE:
mode |= inverted_ ? FALLING : RISING;
break;
case gpio::INTERRUPT_FALLING_EDGE:
mode |= inverted_ ? RISING : FALLING;
break;
case gpio::INTERRUPT_ANY_EDGE:
mode |= CHANGE;
break;
default:
return;
}
irq_cb = func;
irq_arg = arg;
attachInterrupt(pin_, pin_irq, mode);
}
void NRF52GPIOPin::pin_mode(gpio::Flags flags) {
pinMode(pin_, flags_to_mode(flags, pin_)); // NOLINT
}
std::string NRF52GPIOPin::dump_summary() const {
char buffer[32];
snprintf(buffer, sizeof(buffer), "GPIO%u", pin_);
return buffer;
}
bool NRF52GPIOPin::digital_read() {
return bool(digitalRead(pin_)) != inverted_; // NOLINT
}
void NRF52GPIOPin::digital_write(bool value) {
digitalWrite(pin_, value != inverted_ ? 1 : 0); // NOLINT
}
void NRF52GPIOPin::detach_interrupt() const {
detachInterrupt(pin_);
}
} // namespace nrf52
// using namespace nrf52;
// TODO seems to not work???
bool IRAM_ATTR ISRInternalGPIOPin::digital_read() {
auto *arg = reinterpret_cast<nrf52::ISRPinArg *>(arg_);
return bool(digitalRead(arg->pin)) != arg->inverted; // NOLINT
}
} // namespace esphome
#endif // USE_NRF52

View File

@@ -0,0 +1,38 @@
#pragma once
#ifdef USE_NRF52
#include <Arduino.h>
#include "esphome/core/hal.h"
namespace esphome {
namespace nrf52 {
class NRF52GPIOPin : public InternalGPIOPin {
public:
void set_pin(uint8_t pin) { pin_ = pin; }
void set_inverted(bool inverted) { inverted_ = inverted; }
void set_flags(gpio::Flags flags) { flags_ = flags; }
void setup() override { pin_mode(flags_); }
void pin_mode(gpio::Flags flags) override;
bool digital_read() override;
void digital_write(bool value) override;
std::string dump_summary() const override;
void detach_interrupt() const override;
ISRInternalGPIOPin to_isr() const override;
uint8_t get_pin() const override { return pin_; }
bool is_inverted() const override { return inverted_; }
protected:
void attach_interrupt(void (*func)(void *), void *arg, gpio::InterruptType type) const override;
uint8_t pin_;
bool inverted_;
gpio::Flags flags_;
};
} // namespace nrf52
} // namespace esphome
#endif // USE_NRF52

View File

@@ -0,0 +1,58 @@
from esphome import pins
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.const import (
CONF_ID,
CONF_MODE,
CONF_INVERTED,
CONF_NUMBER,
)
nrf52_ns = cg.esphome_ns.namespace("nrf52")
NRF52GPIOPin = nrf52_ns.class_("NRF52GPIOPin", cg.InternalGPIOPin)
def _translate_pin(value):
if isinstance(value, dict) or value is None:
raise cv.Invalid(
"This variable only supports pin numbers, not full pin schemas "
"(with inverted and mode)."
)
if isinstance(value, int):
return value
try:
return int(value)
except ValueError:
pass
# e.g. P0.27
if len(value) >= len("P0.0") and value[0] == "P" and value[2] == ".":
return cv.int_(value[len("P")].strip()) * 32 + cv.int_(
value[len("P0.") :].strip()
)
raise cv.Invalid(f"Invalid pin: {value}")
def validate_gpio_pin(value):
value = _translate_pin(value)
if value < 0 or value > (32 + 16):
raise cv.Invalid(f"NRF52: Invalid pin number: {value}")
return value
NRF52_PIN_SCHEMA = cv.All(
pins.gpio_base_schema(
NRF52GPIOPin,
validate_gpio_pin,
),
)
@pins.PIN_SCHEMA_REGISTRY.register("nrf52", NRF52_PIN_SCHEMA)
async def nrf52_pin_to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
num = config[CONF_NUMBER]
cg.add(var.set_pin(num))
cg.add(var.set_inverted(config[CONF_INVERTED]))
cg.add(var.set_flags(pins.gpio_flags_expr(config[CONF_MODE])))
return var

View File

@@ -1548,6 +1548,7 @@ class SplitDefault(Optional):
bk72xx=vol.UNDEFINED,
rtl87xx=vol.UNDEFINED,
host=vol.UNDEFINED,
nrf52=vol.UNDEFINED,
):
super().__init__(key)
self._esp8266_default = vol.default_factory(esp8266)
@@ -1579,6 +1580,7 @@ class SplitDefault(Optional):
self._bk72xx_default = vol.default_factory(bk72xx)
self._rtl87xx_default = vol.default_factory(rtl87xx)
self._host_default = vol.default_factory(host)
self._nrf52 = vol.default_factory(nrf52)
@property
def default(self):
@@ -1621,6 +1623,8 @@ class SplitDefault(Optional):
return self._rtl87xx_default
if CORE.is_host:
return self._host_default
if CORE.is_nrf52:
return self._nrf52
raise NotImplementedError
@default.setter

View File

@@ -14,6 +14,7 @@ PLATFORM_HOST = "host"
PLATFORM_BK72XX = "bk72xx"
PLATFORM_RTL87XX = "rtl87xx"
PLATFORM_LIBRETINY_OLDSTYLE = "libretiny"
PLATFORM_NRF52 = "nrf52"
TARGET_PLATFORMS = [
PLATFORM_ESP32,
@@ -23,6 +24,7 @@ TARGET_PLATFORMS = [
PLATFORM_BK72XX,
PLATFORM_RTL87XX,
PLATFORM_LIBRETINY_OLDSTYLE,
PLATFORM_NRF52,
]
SOURCE_FILE_EXTENSIONS = {".cpp", ".hpp", ".h", ".c", ".tcc", ".ino"}

View File

@@ -21,6 +21,7 @@ from esphome.const import (
PLATFORM_RTL87XX,
PLATFORM_RP2040,
PLATFORM_HOST,
PLATFORM_NRF52,
)
from esphome.coroutine import FakeAwaitable as _FakeAwaitable
from esphome.coroutine import FakeEventLoop as _FakeEventLoop
@@ -659,6 +660,10 @@ class EsphomeCore:
def is_host(self):
return self.target_platform == PLATFORM_HOST
@property
def is_nrf52(self):
return self.target_platform == PLATFORM_NRF52
@property
def target_framework(self):
return self.data[KEY_CORE][KEY_TARGET_FRAMEWORK]

View File

@@ -20,6 +20,8 @@
#elif defined(USE_LIBRETINY)
#include <FreeRTOS.h>
#include <semphr.h>
#elif defined(USE_NRF52)
#include <Arduino.h>
#endif
#define HOT __attribute__((hot))
@@ -546,7 +548,7 @@ class Mutex {
Mutex &operator=(const Mutex &) = delete;
private:
#if defined(USE_ESP32) || defined(USE_LIBRETINY)
#if defined(USE_ESP32) || defined(USE_LIBRETINY) || defined(USE_NRF52)
SemaphoreHandle_t handle_;
#endif
};