mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 07:03:55 +00:00 
			
		
		
		
	add zephyr
This commit is contained in:
		
							
								
								
									
										187
									
								
								esphome/ble/ble_interface.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										187
									
								
								esphome/ble/ble_interface.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,187 @@ | |||||||
|  | from bleak import BleakClient, BleakScanner | ||||||
|  | from bleak.backends.characteristic import BleakGATTCharacteristic | ||||||
|  | from ble_serial.bluetooth.constants import ble_chars | ||||||
|  | import logging | ||||||
|  | import asyncio | ||||||
|  | from typing import Optional | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class BLE_interface: | ||||||
|  |     def __init__(self, adapter: str, service: str): | ||||||
|  |         self._send_queue = asyncio.Queue() | ||||||
|  |  | ||||||
|  |         self.scan_args = dict(adapter=adapter) | ||||||
|  |         if service: | ||||||
|  |             self.scan_args["service_uuids"] = [service] | ||||||
|  |  | ||||||
|  |     async def connect(self, addr_str: str, addr_type: str, timeout: float): | ||||||
|  |         if addr_str: | ||||||
|  |             device = await BleakScanner.find_device_by_address( | ||||||
|  |                 addr_str, timeout=timeout, **self.scan_args | ||||||
|  |             ) | ||||||
|  |         else: | ||||||
|  |             logging.warning( | ||||||
|  |                 "Picking first device with matching service, " | ||||||
|  |                 "consider passing a specific device address, especially if there could be multiple devices" | ||||||
|  |             ) | ||||||
|  |             device = await BleakScanner.find_device_by_filter( | ||||||
|  |                 lambda dev, ad: True, timeout=timeout, **self.scan_args | ||||||
|  |             ) | ||||||
|  |  | ||||||
|  |         assert device, "No matching device found!" | ||||||
|  |  | ||||||
|  |         # address_type used only in Windows .NET currently | ||||||
|  |         self.dev = BleakClient( | ||||||
|  |             device, | ||||||
|  |             address_type=addr_type, | ||||||
|  |             timeout=timeout, | ||||||
|  |             disconnected_callback=self.handle_disconnect, | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |         logging.info(f"Trying to connect with {device}") | ||||||
|  |         await self.dev.connect() | ||||||
|  |         logging.info(f"Device {self.dev.address} connected") | ||||||
|  |  | ||||||
|  |     async def setup_chars(self, write_uuid: str, read_uuid: str, mode: str): | ||||||
|  |         self.read_enabled = "r" in mode | ||||||
|  |         self.write_enabled = "w" in mode | ||||||
|  |  | ||||||
|  |         if self.write_enabled: | ||||||
|  |             self.write_char = self.find_char( | ||||||
|  |                 write_uuid, ["write", "write-without-response"] | ||||||
|  |             ) | ||||||
|  |         else: | ||||||
|  |             logging.info("Writing disabled, skipping write UUID detection") | ||||||
|  |  | ||||||
|  |         if self.read_enabled: | ||||||
|  |             self.read_char = self.find_char(read_uuid, ["notify", "indicate"]) | ||||||
|  |             await self.dev.start_notify(self.read_char, self.handle_notify) | ||||||
|  |         else: | ||||||
|  |             logging.info("Reading disabled, skipping read UUID detection") | ||||||
|  |  | ||||||
|  |     def find_char( | ||||||
|  |         self, uuid: Optional[str], req_props: [str] | ||||||
|  |     ) -> BleakGATTCharacteristic: | ||||||
|  |         name = req_props[0] | ||||||
|  |  | ||||||
|  |         # Use user supplied UUID first, otherwise try included list | ||||||
|  |         if uuid: | ||||||
|  |             uuid_candidates = [uuid] | ||||||
|  |         else: | ||||||
|  |             uuid_candidates = ble_chars | ||||||
|  |             logging.debug(f"No {name} uuid specified, trying builtin list") | ||||||
|  |  | ||||||
|  |         results = [] | ||||||
|  |         for srv in self.dev.services: | ||||||
|  |             for c in srv.characteristics: | ||||||
|  |                 if c.uuid in uuid_candidates: | ||||||
|  |                     results.append(c) | ||||||
|  |  | ||||||
|  |         if uuid: | ||||||
|  |             assert ( | ||||||
|  |                 len(results) > 0 | ||||||
|  |             ), f"No characteristic with specified {name} UUID {uuid} found!" | ||||||
|  |         else: | ||||||
|  |             assert ( | ||||||
|  |                 len(results) > 0 | ||||||
|  |             ), f"""No characteristic in builtin {name} list {uuid_candidates} found! | ||||||
|  |                     Please specify one with {'-w/--write-uuid' if name == 'write' else '-r/--read-uuid'}, see also --help""" | ||||||
|  |  | ||||||
|  |         res_str = "\n".join(f"\t{c} {c.properties}" for c in results) | ||||||
|  |         logging.debug(f"Characteristic candidates for {name}: \n{res_str}") | ||||||
|  |  | ||||||
|  |         # Check if there is a intersection of permission flags | ||||||
|  |         results[:] = [c for c in results if set(c.properties) & set(req_props)] | ||||||
|  |  | ||||||
|  |         assert len(results) > 0, f"No characteristic with {req_props} property found!" | ||||||
|  |  | ||||||
|  |         assert ( | ||||||
|  |             len(results) == 1 | ||||||
|  |         ), f"Multiple matching {name} characteristics found, please specify one" | ||||||
|  |  | ||||||
|  |         # must be valid here | ||||||
|  |         found = results[0] | ||||||
|  |         logging.info(f"Found {name} characteristic {found.uuid} (H. {found.handle})") | ||||||
|  |         return found | ||||||
|  |  | ||||||
|  |     def set_receiver(self, callback): | ||||||
|  |         self._cb = callback | ||||||
|  |         logging.info("Receiver set up") | ||||||
|  |  | ||||||
|  |     async def send_loop(self): | ||||||
|  |         assert hasattr(self, "_cb"), "Callback must be set before receive loop!" | ||||||
|  |         while True: | ||||||
|  |             data = await self._send_queue.get() | ||||||
|  |             if data is None: | ||||||
|  |                 break  # Let future end on shutdown | ||||||
|  |             if not self.write_enabled: | ||||||
|  |                 logging.warning(f"Ignoring unexpected write data: {data}") | ||||||
|  |                 continue | ||||||
|  |             logging.debug(f"Sending {data}") | ||||||
|  |             await self.dev.write_gatt_char(self.write_char, data) | ||||||
|  |  | ||||||
|  |     def stop_loop(self): | ||||||
|  |         logging.info("Stopping Bluetooth event loop") | ||||||
|  |         self._send_queue.put_nowait(None) | ||||||
|  |  | ||||||
|  |     async def disconnect(self): | ||||||
|  |         if hasattr(self, "dev") and self.dev.is_connected: | ||||||
|  |             if hasattr(self, "read_char"): | ||||||
|  |                 await self.dev.stop_notify(self.read_char) | ||||||
|  |             await self.dev.disconnect() | ||||||
|  |             logging.info("Bluetooth disconnected") | ||||||
|  |  | ||||||
|  |     def queue_send(self, data: bytes): | ||||||
|  |         self._send_queue.put_nowait(data) | ||||||
|  |  | ||||||
|  |     def handle_notify(self, handle: int, data: bytes): | ||||||
|  |         logging.debug(f"Received notify from {handle}: {data}") | ||||||
|  |         if not self.read_enabled: | ||||||
|  |             logging.warning(f"Read unexpected data, dropping: {data}") | ||||||
|  |             return | ||||||
|  |         self._cb(data) | ||||||
|  |  | ||||||
|  |     def handle_disconnect(self, client: BleakClient): | ||||||
|  |         logging.warning(f"Device {client.address} disconnected") | ||||||
|  |         self.stop_loop() | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def receive_callback(value: bytes): | ||||||
|  |     print("Received:", value) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | async def hello_sender(ble: BLE_interface): | ||||||
|  |     while True: | ||||||
|  |         await asyncio.sleep(1 / 100) | ||||||
|  |         # print("Sending...") | ||||||
|  |         ble.queue_send( | ||||||
|  |             b"123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\n" | ||||||
|  |         ) | ||||||
|  |         # ble.queue_send(b"123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\n") | ||||||
|  |         # ble.queue_send(b"123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\n") | ||||||
|  |  | ||||||
|  |  | ||||||
|  | async def main(): | ||||||
|  |     # None uses default/autodetection, insert values if needed | ||||||
|  |     ADAPTER = "hci0" | ||||||
|  |     SERVICE_UUID = "6ba1b218-15a8-461f-9fa8-5dcae273eafd" | ||||||
|  |     WRITE_UUID = "f75c76d2-129e-4dad-a1dd-7866124401e7" | ||||||
|  |     # READ_UUID = '2c55e69e-4993-11ed-b878-0242ac120002' | ||||||
|  |     READ_UUID = "ed9da18c-a800-4f66-a670-aa7547e34453" | ||||||
|  |     DEVICE = "EF:45:95:65:46:FD" | ||||||
|  |  | ||||||
|  |     ble = BLE_interface(ADAPTER, SERVICE_UUID) | ||||||
|  |     ble.set_receiver(receive_callback) | ||||||
|  |  | ||||||
|  |     try: | ||||||
|  |         await ble.connect(DEVICE, "public", 10.0) | ||||||
|  |         await ble.setup_chars(WRITE_UUID, READ_UUID, "rw") | ||||||
|  |  | ||||||
|  |         await asyncio.gather(ble.send_loop(), hello_sender(ble)) | ||||||
|  |     finally: | ||||||
|  |         await ble.disconnect() | ||||||
|  |  | ||||||
|  |  | ||||||
|  | if __name__ == "__main__": | ||||||
|  |     logging.basicConfig(level=logging.INFO) | ||||||
|  |     asyncio.run(main()) | ||||||
							
								
								
									
										53
									
								
								esphome/components/ipsp/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								esphome/components/ipsp/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,53 @@ | |||||||
|  | import esphome.codegen as cg | ||||||
|  | import esphome.config_validation as cv | ||||||
|  | from esphome.const import ( | ||||||
|  |     CONF_ID, | ||||||
|  | ) | ||||||
|  | from esphome.components.nrf52 import add_zephyr_prj_conf_option | ||||||
|  |  | ||||||
|  | ipsp_ns = cg.esphome_ns.namespace("ipsp") | ||||||
|  | Network = ipsp_ns.class_("Network", cg.Component) | ||||||
|  |  | ||||||
|  | CONFIG_SCHEMA = cv.All( | ||||||
|  |     cv.Schema( | ||||||
|  |         { | ||||||
|  |             cv.GenerateID(): cv.declare_id(Network), | ||||||
|  |         } | ||||||
|  |     ).extend(cv.COMPONENT_SCHEMA), | ||||||
|  |     cv.only_with_zephyr, | ||||||
|  | ) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | async def to_code(config): | ||||||
|  |     var = cg.new_Pvariable(config[CONF_ID]) | ||||||
|  |     await cg.register_component(var, config) | ||||||
|  |     add_zephyr_prj_conf_option("CONFIG_BT", True) | ||||||
|  |     add_zephyr_prj_conf_option("CONFIG_BT_SMP", True) | ||||||
|  |     add_zephyr_prj_conf_option("CONFIG_BT_PERIPHERAL", True) | ||||||
|  |     add_zephyr_prj_conf_option("CONFIG_BT_CENTRAL", True) | ||||||
|  |     add_zephyr_prj_conf_option("CONFIG_BT_L2CAP_DYNAMIC_CHANNEL", True) | ||||||
|  |     add_zephyr_prj_conf_option("CONFIG_BT_DEVICE_NAME", "Test IPSP node")  # TODO | ||||||
|  |     add_zephyr_prj_conf_option("CONFIG_NETWORKING", True) | ||||||
|  |     add_zephyr_prj_conf_option("CONFIG_NET_IPV6", True) | ||||||
|  |     add_zephyr_prj_conf_option("CONFIG_NET_IPV4", False) | ||||||
|  |     add_zephyr_prj_conf_option("CONFIG_NET_UDP", False) | ||||||
|  |     add_zephyr_prj_conf_option("CONFIG_NET_TCP", True) | ||||||
|  |     add_zephyr_prj_conf_option("CONFIG_TEST_RANDOM_GENERATOR", True) | ||||||
|  |     add_zephyr_prj_conf_option("CONFIG_NET_L2_BT", True) | ||||||
|  |     add_zephyr_prj_conf_option("CONFIG_INIT_STACKS", True) | ||||||
|  |     add_zephyr_prj_conf_option("CONFIG_NET_PKT_RX_COUNT", 10) | ||||||
|  |     add_zephyr_prj_conf_option("CONFIG_NET_PKT_TX_COUNT", 10) | ||||||
|  |     add_zephyr_prj_conf_option("CONFIG_NET_BUF_RX_COUNT", 20) | ||||||
|  |     add_zephyr_prj_conf_option("CONFIG_NET_BUF_TX_COUNT", 20) | ||||||
|  |     add_zephyr_prj_conf_option("CONFIG_NET_IF_UNICAST_IPV6_ADDR_COUNT", 3) | ||||||
|  |     add_zephyr_prj_conf_option("CONFIG_NET_IF_MCAST_IPV6_ADDR_COUNT", 4) | ||||||
|  |     add_zephyr_prj_conf_option("CONFIG_NET_MAX_CONTEXTS", 6) | ||||||
|  |  | ||||||
|  |     add_zephyr_prj_conf_option("CONFIG_NET_CONFIG_AUTO_INIT", True) | ||||||
|  |     add_zephyr_prj_conf_option("CONFIG_NET_CONFIG_SETTINGS", True) | ||||||
|  |     add_zephyr_prj_conf_option("CONFIG_NET_CONFIG_MY_IPV6_ADDR", "2001:db8::1") | ||||||
|  |     add_zephyr_prj_conf_option("CONFIG_NET_CONFIG_PEER_IPV6_ADDR", "2001:db8::2") | ||||||
|  |     add_zephyr_prj_conf_option("CONFIG_NET_CONFIG_BT_NODE", True) | ||||||
|  |     if True: | ||||||
|  |         add_zephyr_prj_conf_option("CONFIG_LOG", True) | ||||||
|  |         add_zephyr_prj_conf_option("CONFIG_NET_LOG", True) | ||||||
							
								
								
									
										31
									
								
								esphome/components/ipsp/network.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								esphome/components/ipsp/network.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | |||||||
|  | #include "network.h" | ||||||
|  | #include <zephyr/net/net_if.h> | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* Define my IP address where to expect messages */ | ||||||
|  | #define MY_IP6ADDR { { { 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, \ | ||||||
|  | 			 0, 0, 0, 0, 0, 0, 0, 0x1 } } } | ||||||
|  |  | ||||||
|  | static struct in6_addr in6addr_my = MY_IP6ADDR; | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace ipsp { | ||||||
|  |  | ||||||
|  | void Network::setup() { | ||||||
|  | 	if (net_addr_pton(AF_INET6, | ||||||
|  | 			  CONFIG_NET_CONFIG_MY_IPV6_ADDR, | ||||||
|  | 			  &in6addr_my) < 0) { | ||||||
|  | 		// LOG_ERR("Invalid IPv6 address %s", | ||||||
|  | 		// 	CONFIG_NET_CONFIG_MY_IPV6_ADDR); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	do { | ||||||
|  | 		struct net_if_addr *ifaddr; | ||||||
|  |  | ||||||
|  | 		ifaddr = net_if_ipv6_addr_add(net_if_get_default(), | ||||||
|  | 					      &in6addr_my, NET_ADDR_MANUAL, 0); | ||||||
|  | 	} while (0); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | }  // namespace shell | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										10
									
								
								esphome/components/ipsp/network.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								esphome/components/ipsp/network.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | |||||||
|  | #pragma once | ||||||
|  | #include "esphome/core/component.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace ipsp { | ||||||
|  | class Network : public Component { | ||||||
|  | 	void setup() override; | ||||||
|  | }; | ||||||
|  | } | ||||||
|  | } | ||||||
| @@ -27,8 +27,10 @@ | |||||||
| #include "esphome/core/log.h" | #include "esphome/core/log.h" | ||||||
|  |  | ||||||
| #ifdef USE_NRF52 | #ifdef USE_NRF52 | ||||||
|  | #ifdef USE_ARDUINO | ||||||
| #include <Adafruit_TinyUSB.h> // for Serial | #include <Adafruit_TinyUSB.h> // for Serial | ||||||
| #endif | #endif | ||||||
|  | #endif | ||||||
|  |  | ||||||
| namespace esphome { | namespace esphome { | ||||||
| namespace logger { | namespace logger { | ||||||
| @@ -64,12 +66,16 @@ void Logger::write_header_(int level, const char *tag, int line) { | |||||||
|  |  | ||||||
|   const char *color = LOG_LEVEL_COLORS[level]; |   const char *color = LOG_LEVEL_COLORS[level]; | ||||||
|   const char *letter = LOG_LEVEL_LETTERS[level]; |   const char *letter = LOG_LEVEL_LETTERS[level]; | ||||||
|  | #ifdef USE_ARDUINO | ||||||
|   void *current_task = xTaskGetCurrentTaskHandle(); |   void *current_task = xTaskGetCurrentTaskHandle(); | ||||||
|   if (current_task == main_task) { |   if (current_task == main_task) { | ||||||
|  | #endif | ||||||
|     this->printf_to_buffer_("%s[%s][%s:%03u]: ", color, letter, tag, line); |     this->printf_to_buffer_("%s[%s][%s:%03u]: ", color, letter, tag, line); | ||||||
|  | #ifdef USE_ARDUINO | ||||||
|   } else { |   } else { | ||||||
|     this->printf_to_buffer_("%s[%s][%s:%03u]%s[%s]%s: ", color, letter, tag, line, ESPHOME_LOG_BOLD(ESPHOME_LOG_COLOR_RED), pcTaskGetName(current_task), color); |     this->printf_to_buffer_("%s[%s][%s:%03u]%s[%s]%s: ", color, letter, tag, line, ESPHOME_LOG_BOLD(ESPHOME_LOG_COLOR_RED), pcTaskGetName(current_task), color); | ||||||
|   } |   } | ||||||
|  | #endif | ||||||
| } | } | ||||||
|  |  | ||||||
| void HOT Logger::log_vprintf_(int level, const char *tag, int line, const char *format, va_list args) {  // NOLINT | void HOT Logger::log_vprintf_(int level, const char *tag, int line, const char *format, va_list args) {  // NOLINT | ||||||
| @@ -230,7 +236,9 @@ void HOT Logger::log_message_(int level, const char *tag, int offset) { | |||||||
| Logger::Logger(uint32_t baud_rate, size_t tx_buffer_size) : baud_rate_(baud_rate), tx_buffer_size_(tx_buffer_size) { | Logger::Logger(uint32_t baud_rate, size_t tx_buffer_size) : baud_rate_(baud_rate), tx_buffer_size_(tx_buffer_size) { | ||||||
|   // add 1 to buffer size for null terminator |   // add 1 to buffer size for null terminator | ||||||
|   this->tx_buffer_ = new char[this->tx_buffer_size_ + 1];  // NOLINT |   this->tx_buffer_ = new char[this->tx_buffer_size_ + 1];  // NOLINT | ||||||
|  | #ifdef USE_ARDUINO | ||||||
|   this->main_task = xTaskGetCurrentTaskHandle(); |   this->main_task = xTaskGetCurrentTaskHandle(); | ||||||
|  | #endif | ||||||
| } | } | ||||||
|  |  | ||||||
| #ifndef USE_LIBRETINY | #ifndef USE_LIBRETINY | ||||||
|   | |||||||
| @@ -3,34 +3,47 @@ import esphome.codegen as cg | |||||||
| import esphome.config_validation as cv | import esphome.config_validation as cv | ||||||
| from esphome.const import ( | from esphome.const import ( | ||||||
|     CONF_BOARD, |     CONF_BOARD, | ||||||
|     CONF_FRAMEWORK, |  | ||||||
|     KEY_CORE, |     KEY_CORE, | ||||||
|     KEY_TARGET_FRAMEWORK, |     KEY_TARGET_FRAMEWORK, | ||||||
|     KEY_TARGET_PLATFORM, |     KEY_TARGET_PLATFORM, | ||||||
|     PLATFORM_NRF52, |     PLATFORM_NRF52, | ||||||
|  |     CONF_TYPE, | ||||||
|  |     CONF_FRAMEWORK, | ||||||
| ) | ) | ||||||
| from esphome.core import CORE, coroutine_with_priority | from esphome.core import CORE, coroutine_with_priority | ||||||
|  | from esphome.helpers import ( | ||||||
|  |     write_file_if_changed, | ||||||
|  | ) | ||||||
|  | from typing import Union | ||||||
|  |  | ||||||
| # force import gpio to register pin schema | # force import gpio to register pin schema | ||||||
| from .gpio import nrf52_pin_to_code  # noqa | from .gpio import nrf52_pin_to_code  # noqa | ||||||
|  |  | ||||||
| AUTO_LOAD = ["nrf52_nrfx"] | # def AUTO_LOAD(): | ||||||
|  | #     # if CORE.using_arduino: | ||||||
|  | #         return ["nrf52_nrfx"] | ||||||
|  | #     # return [] | ||||||
|  |  | ||||||
|  | KEY_NRF52 = "nrf52" | ||||||
|  |  | ||||||
|  |  | ||||||
| def set_core_data(config): | def set_core_data(config): | ||||||
|  |     CORE.data[KEY_NRF52] = {} | ||||||
|  |     CORE.data[KEY_NRF52][KEY_PRJ_CONF_OPTIONS] = {} | ||||||
|     CORE.data[KEY_CORE][KEY_TARGET_PLATFORM] = PLATFORM_NRF52 |     CORE.data[KEY_CORE][KEY_TARGET_PLATFORM] = PLATFORM_NRF52 | ||||||
|     CORE.data[KEY_CORE][KEY_TARGET_FRAMEWORK] = "arduino" |     CORE.data[KEY_CORE][KEY_TARGET_FRAMEWORK] = config[CONF_FRAMEWORK][CONF_TYPE] | ||||||
|     return config |     return config | ||||||
|  |  | ||||||
|  |  | ||||||
| # https://github.com/platformio/platform-nordicnrf52/releases | # https://github.com/platformio/platform-nordicnrf52/releases | ||||||
| ARDUINO_PLATFORM_VERSION = cv.Version(10, 3, 0) | NORDICNRF52_PLATFORM_VERSION = cv.Version(10, 3, 0) | ||||||
|  |  | ||||||
|  |  | ||||||
| def _arduino_check_versions(value): | def _platform_check_versions(value): | ||||||
|     value = value.copy() |     value = value.copy() | ||||||
|     value[CONF_PLATFORM_VERSION] = value.get( |     value[CONF_PLATFORM_VERSION] = value.get( | ||||||
|         CONF_PLATFORM_VERSION, _parse_platform_version(str(ARDUINO_PLATFORM_VERSION)) |         CONF_PLATFORM_VERSION, | ||||||
|  |         _parse_platform_version(str(NORDICNRF52_PLATFORM_VERSION)), | ||||||
|     ) |     ) | ||||||
|     return value |     return value | ||||||
|  |  | ||||||
| @@ -46,20 +59,32 @@ def _parse_platform_version(value): | |||||||
|  |  | ||||||
| CONF_PLATFORM_VERSION = "platform_version" | CONF_PLATFORM_VERSION = "platform_version" | ||||||
|  |  | ||||||
| ARDUINO_FRAMEWORK_SCHEMA = cv.All( | PLATFORM_FRAMEWORK_SCHEMA = cv.All( | ||||||
|     cv.Schema( |     cv.Schema( | ||||||
|         { |         { | ||||||
|             cv.Optional(CONF_PLATFORM_VERSION): _parse_platform_version, |             cv.Optional(CONF_PLATFORM_VERSION): _parse_platform_version, | ||||||
|         } |         } | ||||||
|     ), |     ), | ||||||
|     _arduino_check_versions, |     _platform_check_versions, | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | FRAMEWORK_ZEPHYR = "zephyr" | ||||||
|  | FRAMEWORK_ARDUINO = "arduino" | ||||||
|  | FRAMEWORK_SCHEMA = cv.typed_schema( | ||||||
|  |     { | ||||||
|  |         FRAMEWORK_ZEPHYR: PLATFORM_FRAMEWORK_SCHEMA, | ||||||
|  |         FRAMEWORK_ARDUINO: PLATFORM_FRAMEWORK_SCHEMA, | ||||||
|  |     }, | ||||||
|  |     lower=True, | ||||||
|  |     space="-", | ||||||
|  |     default_type=FRAMEWORK_ARDUINO, | ||||||
| ) | ) | ||||||
|  |  | ||||||
| CONFIG_SCHEMA = cv.All( | CONFIG_SCHEMA = cv.All( | ||||||
|     cv.Schema( |     cv.Schema( | ||||||
|         { |         { | ||||||
|             cv.Required(CONF_BOARD): cv.string_strict, |             cv.Required(CONF_BOARD): cv.string_strict, | ||||||
|             cv.Optional(CONF_FRAMEWORK, default={}): ARDUINO_FRAMEWORK_SCHEMA, |             cv.Optional(CONF_FRAMEWORK, default={}): FRAMEWORK_SCHEMA, | ||||||
|         } |         } | ||||||
|     ), |     ), | ||||||
|     set_core_data, |     set_core_data, | ||||||
| @@ -68,10 +93,31 @@ CONFIG_SCHEMA = cv.All( | |||||||
| nrf52_ns = cg.esphome_ns.namespace("nrf52") | nrf52_ns = cg.esphome_ns.namespace("nrf52") | ||||||
|  |  | ||||||
|  |  | ||||||
|  | PrjConfValueType = Union[bool, str, int] | ||||||
|  | KEY_PRJ_CONF_OPTIONS = "prj_conf_options" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def add_zephyr_prj_conf_option(name: str, value: PrjConfValueType): | ||||||
|  |     """Set an zephyr prj conf value.""" | ||||||
|  |     if not CORE.using_zephyr: | ||||||
|  |         raise ValueError("Not an zephyr project") | ||||||
|  |     if name in CORE.data[KEY_NRF52][KEY_PRJ_CONF_OPTIONS]: | ||||||
|  |         old_value = CORE.data[KEY_NRF52][KEY_PRJ_CONF_OPTIONS][name] | ||||||
|  |         if old_value != value: | ||||||
|  |             raise ValueError( | ||||||
|  |                 f"{name} alread set with value {old_value}, new value {value}" | ||||||
|  |             ) | ||||||
|  |     CORE.data[KEY_NRF52][KEY_PRJ_CONF_OPTIONS][name] = value | ||||||
|  |  | ||||||
|  |  | ||||||
| @coroutine_with_priority(1000) | @coroutine_with_priority(1000) | ||||||
| async def to_code(config): | async def to_code(config): | ||||||
|     cg.add(nrf52_ns.setup_preferences()) |     cg.add(nrf52_ns.setup_preferences()) | ||||||
|     if config[CONF_BOARD] == "nrf52840": |     if config[CONF_BOARD] == "nrf52840": | ||||||
|  |         if CORE.using_zephyr: | ||||||
|  |             # this board works with https://github.com/adafruit/Adafruit_nRF52_Bootloader | ||||||
|  |             config[CONF_BOARD] = "adafruit_itsybitsy_nrf52840" | ||||||
|  |         elif CORE.using_arduino: | ||||||
|             # it has most generic GPIO mapping |             # it has most generic GPIO mapping | ||||||
|             config[CONF_BOARD] = "nrf52840_dk_adafruit" |             config[CONF_BOARD] = "nrf52840_dk_adafruit" | ||||||
|     cg.add_platformio_option("board", config[CONF_BOARD]) |     cg.add_platformio_option("board", config[CONF_BOARD]) | ||||||
| @@ -79,14 +125,16 @@ async def to_code(config): | |||||||
|     cg.add_define("ESPHOME_BOARD", config[CONF_BOARD]) |     cg.add_define("ESPHOME_BOARD", config[CONF_BOARD]) | ||||||
|     cg.add_define("ESPHOME_VARIANT", "nrf52840") |     cg.add_define("ESPHOME_VARIANT", "nrf52840") | ||||||
|     conf = config[CONF_FRAMEWORK] |     conf = config[CONF_FRAMEWORK] | ||||||
|     cg.add_platformio_option("framework", "arduino") |     cg.add_platformio_option(CONF_FRAMEWORK, conf[CONF_TYPE]) | ||||||
|     cg.add_build_flag("-DUSE_ARDUINO") |  | ||||||
|     cg.add_platformio_option("platform", conf[CONF_PLATFORM_VERSION]) |     cg.add_platformio_option("platform", conf[CONF_PLATFORM_VERSION]) | ||||||
|     # make sure that firmware.zip is created |     # make sure that firmware.zip is created | ||||||
|  |     # for Adafruit_nRF52_Bootloader | ||||||
|     cg.add_platformio_option("board_upload.protocol", "nrfutil") |     cg.add_platformio_option("board_upload.protocol", "nrfutil") | ||||||
|     cg.add_platformio_option("board_upload.use_1200bps_touch", "true") |     cg.add_platformio_option("board_upload.use_1200bps_touch", "true") | ||||||
|     cg.add_platformio_option("board_upload.require_upload_port", "true") |     cg.add_platformio_option("board_upload.require_upload_port", "true") | ||||||
|     cg.add_platformio_option("board_upload.wait_for_upload_port", "true") |     cg.add_platformio_option("board_upload.wait_for_upload_port", "true") | ||||||
|  |     if CORE.using_arduino: | ||||||
|  |         cg.add_build_flag("-DUSE_ARDUINO") | ||||||
|         # watchdog |         # watchdog | ||||||
|         cg.add_build_flag("-DNRFX_WDT_ENABLED=1") |         cg.add_build_flag("-DNRFX_WDT_ENABLED=1") | ||||||
|         cg.add_build_flag("-DNRFX_WDT0_ENABLED=1") |         cg.add_build_flag("-DNRFX_WDT0_ENABLED=1") | ||||||
| @@ -96,3 +144,43 @@ async def to_code(config): | |||||||
|         cg.add_platformio_option( |         cg.add_platformio_option( | ||||||
|             "board_build.variants_dir", os.path.dirname(os.path.realpath(__file__)) |             "board_build.variants_dir", os.path.dirname(os.path.realpath(__file__)) | ||||||
|         ) |         ) | ||||||
|  |     elif CORE.using_zephyr: | ||||||
|  |         cg.add_build_flag("-DUSE_ZEPHYR") | ||||||
|  |         cg.add_platformio_option( | ||||||
|  |             "platform_packages", ["framework-zephyr@~2.30400.230914"] | ||||||
|  |         ) | ||||||
|  |         # cpp support | ||||||
|  |         add_zephyr_prj_conf_option("CONFIG_NEWLIB_LIBC", False) | ||||||
|  |         add_zephyr_prj_conf_option("CONFIG_NEWLIB_LIBC_NANO", True) | ||||||
|  |         add_zephyr_prj_conf_option("CONFIG_NEWLIB_LIBC_FLOAT_PRINTF", True) | ||||||
|  |         add_zephyr_prj_conf_option("CONFIG_CPLUSPLUS", True) | ||||||
|  |         add_zephyr_prj_conf_option("CONFIG_LIB_CPLUSPLUS", True) | ||||||
|  |         # watchdog | ||||||
|  |         add_zephyr_prj_conf_option("CONFIG_WATCHDOG", True) | ||||||
|  |         add_zephyr_prj_conf_option("CONFIG_WDT_DISABLE_AT_BOOT", False) | ||||||
|  |     else: | ||||||
|  |         raise NotImplementedError | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def _format_prj_conf_val(value: PrjConfValueType) -> str: | ||||||
|  |     if isinstance(value, bool): | ||||||
|  |         return "y" if value else "n" | ||||||
|  |     if isinstance(value, int): | ||||||
|  |         return str(value) | ||||||
|  |     if isinstance(value, str): | ||||||
|  |         return f'"{value}"' | ||||||
|  |     raise ValueError | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # Called by writer.py | ||||||
|  | def copy_files(): | ||||||
|  |     if CORE.using_zephyr: | ||||||
|  |         want_opts = CORE.data[KEY_NRF52][KEY_PRJ_CONF_OPTIONS] | ||||||
|  |         contents = ( | ||||||
|  |             "\n".join( | ||||||
|  |                 f"{name}={_format_prj_conf_val(value)}" | ||||||
|  |                 for name, value in sorted(want_opts.items()) | ||||||
|  |             ) | ||||||
|  |             + "\n" | ||||||
|  |         ) | ||||||
|  |         write_file_if_changed(CORE.relative_build_path("zephyr/prj.conf"), contents) | ||||||
|   | |||||||
| @@ -1,4 +1,5 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  | #include <cstdint> | ||||||
|  |  | ||||||
| namespace esphome { | namespace esphome { | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,11 +1,10 @@ | |||||||
| #ifdef USE_NRF52 | #ifdef USE_NRF52 | ||||||
| 
 | #ifdef USE_ARDUINO | ||||||
| #include <Arduino.h> | #include <Arduino.h> | ||||||
| #include "Adafruit_nRFCrypto.h" | #include "Adafruit_nRFCrypto.h" | ||||||
| #include "nrfx_wdt.h" | #include "nrfx_wdt.h" | ||||||
| 
 | 
 | ||||||
| namespace esphome { | namespace esphome { | ||||||
| 
 |  | ||||||
| void yield() { ::yield(); } | void yield() { ::yield(); } | ||||||
| uint32_t millis() { return ::millis(); } | uint32_t millis() { return ::millis(); } | ||||||
| void delay(uint32_t ms) { ::delay(ms); } | void delay(uint32_t ms) { ::delay(ms); } | ||||||
| @@ -58,7 +57,7 @@ void nrf52GetMacAddr(uint8_t *mac) | |||||||
|     mac[1] = src[4]; |     mac[1] = src[4]; | ||||||
|     mac[0] = src[5] | 0xc0; // MSB high two bits get set elsewhere in the bluetooth stack
 |     mac[0] = src[5] | 0xc0; // MSB high two bits get set elsewhere in the bluetooth stack
 | ||||||
| } | } | ||||||
| 
 |  | ||||||
| }  // namespace esphome
 | }  // namespace esphome
 | ||||||
| 
 | 
 | ||||||
|  | #endif | ||||||
| #endif  // USE_RP2040
 | #endif  // USE_RP2040
 | ||||||
							
								
								
									
										45
									
								
								esphome/components/nrf52/core_zephyr.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								esphome/components/nrf52/core_zephyr.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,45 @@ | |||||||
|  | #ifdef USE_NRF52 | ||||||
|  | #ifdef USE_ZEPHYR | ||||||
|  |  | ||||||
|  | #include <zephyr/kernel.h> | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | void yield() { ::k_yield(); } | ||||||
|  | uint32_t millis() { return ::k_ticks_to_ms_floor32(k_uptime_ticks()); } | ||||||
|  | void delay(uint32_t ms) { ::k_msleep(ms); } | ||||||
|  | uint32_t micros() { return ::k_ticks_to_us_floor32(k_uptime_ticks()); } | ||||||
|  |  | ||||||
|  | void arch_init() { | ||||||
|  |     // TODO | ||||||
|  | } | ||||||
|  | void arch_feed_wdt() { | ||||||
|  |     // TODO | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void nrf52GetMacAddr(uint8_t *mac) | ||||||
|  | { | ||||||
|  |     const uint8_t *src = (const uint8_t *)NRF_FICR->DEVICEADDR; | ||||||
|  |     mac[5] = src[0]; | ||||||
|  |     mac[4] = src[1]; | ||||||
|  |     mac[3] = src[2]; | ||||||
|  |     mac[2] = src[3]; | ||||||
|  |     mac[1] = src[4]; | ||||||
|  |     mac[0] = src[5] | 0xc0; // MSB high two bits get set elsewhere in the bluetooth stack | ||||||
|  | } | ||||||
|  |  | ||||||
|  | }  // namespace esphome | ||||||
|  |  | ||||||
|  | void setup(); | ||||||
|  | void loop(); | ||||||
|  |  | ||||||
|  | int main(){ | ||||||
|  |     setup(); | ||||||
|  |     while(1) { | ||||||
|  |         loop(); | ||||||
|  |         esphome::yield(); | ||||||
|  |     } | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #endif | ||||||
|  | #endif  // USE_RP2040 | ||||||
| @@ -1,10 +1,10 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #ifdef USE_NRF52 | #ifdef USE_NRF52 | ||||||
|  |  | ||||||
| #include <Arduino.h> |  | ||||||
| #include "esphome/core/hal.h" | #include "esphome/core/hal.h" | ||||||
|  | #ifdef USE_ZEPHYR | ||||||
|  | struct device; | ||||||
|  | #endif | ||||||
| namespace esphome { | namespace esphome { | ||||||
| namespace nrf52 { | namespace nrf52 { | ||||||
|  |  | ||||||
| @@ -14,7 +14,7 @@ class NRF52GPIOPin : public InternalGPIOPin { | |||||||
|   void set_inverted(bool inverted) { inverted_ = inverted; } |   void set_inverted(bool inverted) { inverted_ = inverted; } | ||||||
|   void set_flags(gpio::Flags flags) { flags_ = flags; } |   void set_flags(gpio::Flags flags) { flags_ = flags; } | ||||||
|  |  | ||||||
|   void setup() override { pin_mode(flags_); } |   void setup() override; | ||||||
|   void pin_mode(gpio::Flags flags) override; |   void pin_mode(gpio::Flags flags) override; | ||||||
|   bool digital_read() override; |   bool digital_read() override; | ||||||
|   void digital_write(bool value) override; |   void digital_write(bool value) override; | ||||||
| @@ -30,6 +30,9 @@ class NRF52GPIOPin : public InternalGPIOPin { | |||||||
|   uint8_t pin_; |   uint8_t pin_; | ||||||
|   bool inverted_; |   bool inverted_; | ||||||
|   gpio::Flags flags_; |   gpio::Flags flags_; | ||||||
|  | #ifdef USE_ZEPHYR | ||||||
|  |   const device * gpio_ = nullptr; | ||||||
|  | #endif | ||||||
| }; | }; | ||||||
|  |  | ||||||
| }  // namespace nrf52 | }  // namespace nrf52 | ||||||
|   | |||||||
| @@ -1,9 +1,11 @@ | |||||||
| #ifdef USE_NRF52 | #ifdef USE_NRF52 | ||||||
|  | #ifdef USE_ARDUINO | ||||||
| 
 | 
 | ||||||
| #include "gpio.h" | #include "gpio.h" | ||||||
| #include "esphome/core/log.h" | #include "esphome/core/log.h" | ||||||
| #include <functional> | #include <functional> | ||||||
| #include <vector> | #include <vector> | ||||||
|  | #include "Arduino.h" | ||||||
| 
 | 
 | ||||||
| namespace esphome { | namespace esphome { | ||||||
| namespace nrf52 { | namespace nrf52 { | ||||||
| @@ -77,6 +79,9 @@ void NRF52GPIOPin::attach_interrupt(void (*func)(void *), void *arg, gpio::Inter | |||||||
|   irq_arg = arg; |   irq_arg = arg; | ||||||
|   attachInterrupt(pin_, pin_irq, mode); |   attachInterrupt(pin_, pin_irq, mode); | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | void NRF52GPIOPin::setup() { pin_mode(flags_); } | ||||||
|  | 
 | ||||||
| void NRF52GPIOPin::pin_mode(gpio::Flags flags) { | void NRF52GPIOPin::pin_mode(gpio::Flags flags) { | ||||||
|   pinMode(pin_, flags_to_mode(flags, pin_));  // NOLINT
 |   pinMode(pin_, flags_to_mode(flags, pin_));  // NOLINT
 | ||||||
| } | } | ||||||
| @@ -109,4 +114,5 @@ bool IRAM_ATTR ISRInternalGPIOPin::digital_read() { | |||||||
| 
 | 
 | ||||||
| }  // namespace esphome
 | }  // namespace esphome
 | ||||||
| 
 | 
 | ||||||
|  | #endif | ||||||
| #endif  // USE_NRF52
 | #endif  // USE_NRF52
 | ||||||
							
								
								
									
										143
									
								
								esphome/components/nrf52/gpio_zephyr.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								esphome/components/nrf52/gpio_zephyr.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,143 @@ | |||||||
|  | #ifdef USE_NRF52 | ||||||
|  | #ifdef USE_ZEPHYR | ||||||
|  |  | ||||||
|  | #include "gpio.h" | ||||||
|  | #include "esphome/core/log.h" | ||||||
|  | // #include <functional> | ||||||
|  | // #include <vector> | ||||||
|  | // #include <device.h> | ||||||
|  | #include <zephyr/drivers/gpio.h> | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace nrf52 { | ||||||
|  |  | ||||||
|  | static const char *const TAG = "nrf52"; | ||||||
|  |  | ||||||
|  | static int flags_to_mode(gpio::Flags flags, uint8_t pin) { | ||||||
|  |   if (flags == gpio::FLAG_INPUT) {  // NOLINT(bugprone-branch-clone) | ||||||
|  |     return GPIO_INPUT; | ||||||
|  |   } else if (flags == gpio::FLAG_OUTPUT) { | ||||||
|  |     return GPIO_OUTPUT; | ||||||
|  |   } else if (flags == (gpio::FLAG_INPUT | gpio::FLAG_PULLUP)) { | ||||||
|  |     return GPIO_INPUT | GPIO_PULL_UP; | ||||||
|  |   } else if (flags == (gpio::FLAG_INPUT | gpio::FLAG_PULLDOWN)) { | ||||||
|  |     return GPIO_INPUT | GPIO_PULL_DOWN; | ||||||
|  |   } else if (flags == (gpio::FLAG_OUTPUT | gpio::FLAG_OPEN_DRAIN)) { | ||||||
|  |       return GPIO_OUTPUT | GPIO_OPEN_DRAIN; | ||||||
|  |   } else { | ||||||
|  |     return GPIO_DISCONNECTED; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | 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::setup() { | ||||||
|  |   const struct device * gpio = nullptr; | ||||||
|  |   if(pin_ < 32) { | ||||||
|  | #define GPIO0 DT_NODELABEL(gpio0) | ||||||
|  | #if DT_NODE_HAS_STATUS(GPIO0, okay) | ||||||
|  |   gpio = DEVICE_DT_GET(GPIO0); | ||||||
|  | #else | ||||||
|  | #error "gpio0 is disabled" | ||||||
|  | #endif | ||||||
|  |   } else { | ||||||
|  | #define GPIO1 DT_NODELABEL(gpio1) | ||||||
|  | #if DT_NODE_HAS_STATUS(GPIO1, okay) | ||||||
|  |   gpio = DEVICE_DT_GET(GPIO1); | ||||||
|  | #else | ||||||
|  | #error "gpio1 is disabled" | ||||||
|  | #endif | ||||||
|  |   } | ||||||
|  |   //TODO why no ready?? | ||||||
|  |   // if (!device_is_ready(gpio)) { | ||||||
|  |     gpio_ = gpio; | ||||||
|  |   // } else { | ||||||
|  |   //   ESP_LOGE(TAG, "gpio %u is not ready.", pin_); | ||||||
|  |   // } | ||||||
|  |   gpio_ = gpio; | ||||||
|  |   pin_mode(flags_);  | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void NRF52GPIOPin::pin_mode(gpio::Flags flags) { | ||||||
|  |   if(nullptr == gpio_) { | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |   gpio_pin_configure(gpio_, pin_, flags_to_mode(flags, pin_)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | std::string NRF52GPIOPin::dump_summary() const { | ||||||
|  |   char buffer[32]; | ||||||
|  |   snprintf(buffer, sizeof(buffer), "GPIO%u", pin_); | ||||||
|  |   return buffer; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool NRF52GPIOPin::digital_read() { | ||||||
|  |   if(nullptr == gpio_) { | ||||||
|  |     //TODO invert ?? | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
|  |   return bool(gpio_pin_get(gpio_, pin_)) != inverted_;  // NOLINT | ||||||
|  | } | ||||||
|  | void NRF52GPIOPin::digital_write(bool value) { | ||||||
|  |   if(nullptr == gpio_) { | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |   gpio_pin_set(gpio_, pin_, value != inverted_ ? 1 : 0); | ||||||
|  | } | ||||||
|  | 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 | ||||||
|  | #endif  // USE_NRF52 | ||||||
							
								
								
									
										31
									
								
								esphome/components/shell/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								esphome/components/shell/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | |||||||
|  | import esphome.codegen as cg | ||||||
|  | import esphome.config_validation as cv | ||||||
|  | from esphome.const import ( | ||||||
|  |     CONF_ID, | ||||||
|  | ) | ||||||
|  | from esphome.components.nrf52 import add_zephyr_prj_conf_option | ||||||
|  |  | ||||||
|  | shell_ns = cg.esphome_ns.namespace("shell") | ||||||
|  | Shell = shell_ns.class_("Shell", cg.Component) | ||||||
|  |  | ||||||
|  | CONFIG_SCHEMA = cv.All( | ||||||
|  |     cv.Schema( | ||||||
|  |         { | ||||||
|  |             cv.GenerateID(): cv.declare_id(Shell), | ||||||
|  |         } | ||||||
|  |     ).extend(cv.COMPONENT_SCHEMA), | ||||||
|  |     cv.only_with_zephyr, | ||||||
|  | ) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | async def to_code(config): | ||||||
|  |     var = cg.new_Pvariable(config[CONF_ID]) | ||||||
|  |     await cg.register_component(var, config) | ||||||
|  |     add_zephyr_prj_conf_option("CONFIG_SHELL", True) | ||||||
|  |     add_zephyr_prj_conf_option("CONFIG_SHELL_BACKENDS", True) | ||||||
|  |     add_zephyr_prj_conf_option("CONFIG_SHELL_BACKEND_SERIAL", True) | ||||||
|  |     add_zephyr_prj_conf_option("CONFIG_NET_SHELL_DYN_CMD_COMPLETION", True) | ||||||
|  |     add_zephyr_prj_conf_option("CONFIG_NET_SHELL", True) | ||||||
|  |     add_zephyr_prj_conf_option("CONFIG_GPIO_SHELL", True) | ||||||
|  |     # add_zephyr_prj_conf_option("CONFIG_ADC_SHELL", True) | ||||||
|  |     add_zephyr_prj_conf_option("CONFIG_NET_L2_BT_SHELL", True) | ||||||
							
								
								
									
										21
									
								
								esphome/components/shell/shell.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								esphome/components/shell/shell.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | |||||||
|  | #include "shell.h" | ||||||
|  | #include <zephyr/usb/usb_device.h> | ||||||
|  |  | ||||||
|  | struct device; | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace shell { | ||||||
|  |  | ||||||
|  | void Shell::setup() { | ||||||
|  | #if DT_NODE_HAS_COMPAT(DT_CHOSEN(zephyr_shell_uart), zephyr_cdc_acm_uart) | ||||||
|  | 	const device *dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_shell_uart)); | ||||||
|  | 	if (!device_is_ready(dev) || usb_enable(NULL)) { | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | #else | ||||||
|  | #error "shell is not set in device tree" | ||||||
|  | #endif | ||||||
|  | } | ||||||
|  |  | ||||||
|  | }  // namespace shell | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										10
									
								
								esphome/components/shell/shell.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								esphome/components/shell/shell.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | |||||||
|  | #pragma once | ||||||
|  | #include "esphome/core/component.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace shell { | ||||||
|  | class Shell : public Component { | ||||||
|  | 	void setup() override; | ||||||
|  | }; | ||||||
|  | } | ||||||
|  | } | ||||||
| @@ -599,6 +599,7 @@ only_on_rp2040 = only_on(PLATFORM_RP2040) | |||||||
| only_on_nrf52 = only_on(PLATFORM_NRF52) | only_on_nrf52 = only_on(PLATFORM_NRF52) | ||||||
| only_with_arduino = only_with_framework("arduino") | only_with_arduino = only_with_framework("arduino") | ||||||
| only_with_esp_idf = only_with_framework("esp-idf") | only_with_esp_idf = only_with_framework("esp-idf") | ||||||
|  | only_with_zephyr = only_with_framework("zephyr") | ||||||
|  |  | ||||||
|  |  | ||||||
| # Adapted from: | # Adapted from: | ||||||
|   | |||||||
| @@ -676,6 +676,10 @@ class EsphomeCore: | |||||||
|     def using_esp_idf(self): |     def using_esp_idf(self): | ||||||
|         return self.target_framework == "esp-idf" |         return self.target_framework == "esp-idf" | ||||||
|  |  | ||||||
|  |     @property | ||||||
|  |     def using_zephyr(self): | ||||||
|  |         return self.target_framework == "zephyr" | ||||||
|  |  | ||||||
|     def add_job(self, func, *args, **kwargs): |     def add_job(self, func, *args, **kwargs): | ||||||
|         self.event_loop.add_job(func, *args, **kwargs) |         self.event_loop.add_job(func, *args, **kwargs) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -10,6 +10,7 @@ | |||||||
| #include <cstdarg> | #include <cstdarg> | ||||||
| #include <cstdio> | #include <cstdio> | ||||||
| #include <cstring> | #include <cstring> | ||||||
|  | #include <strings.h> | ||||||
|  |  | ||||||
| #if defined(USE_ESP8266) | #if defined(USE_ESP8266) | ||||||
| #include <osapi.h> | #include <osapi.h> | ||||||
| @@ -48,7 +49,9 @@ | |||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #ifdef USE_NRF52 | #ifdef USE_NRF52 | ||||||
|  | #ifdef USE_ARDUINO | ||||||
| #include "Adafruit_nRFCrypto.h" | #include "Adafruit_nRFCrypto.h" | ||||||
|  | #endif | ||||||
| #include "esphome/components/nrf52/core.h" | #include "esphome/components/nrf52/core.h" | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| @@ -246,7 +249,11 @@ bool random_bytes(uint8_t *data, size_t len) { | |||||||
|   fclose(fp); |   fclose(fp); | ||||||
|   return true; |   return true; | ||||||
| #elif defined(USE_NRF52) | #elif defined(USE_NRF52) | ||||||
|  | #ifdef USE_ARDUINO | ||||||
|   return nRFCrypto.Random.generate(data, len); |   return nRFCrypto.Random.generate(data, len); | ||||||
|  | #elif USE_ZEPHYR | ||||||
|  | //TODO | ||||||
|  | #endif | ||||||
| #else | #else | ||||||
| #error "No random source available for this configuration." | #error "No random source available for this configuration." | ||||||
| #endif | #endif | ||||||
| @@ -518,13 +525,14 @@ void hsv_to_rgb(int hue, float saturation, float value, float &red, float &green | |||||||
| } | } | ||||||
|  |  | ||||||
| // System APIs | // System APIs | ||||||
| #if defined(USE_ESP8266) || defined(USE_RP2040) || defined(USE_HOST) | #if defined(USE_ESP8266) || defined(USE_RP2040) || defined(USE_HOST) || defined(USE_NRF52) | ||||||
| // ESP8266 doesn't have mutexes, but that shouldn't be an issue as it's single-core and non-preemptive OS. | // ESP8266 doesn't have mutexes, but that shouldn't be an issue as it's single-core and non-preemptive OS. | ||||||
| Mutex::Mutex() {} | Mutex::Mutex() {} | ||||||
| void Mutex::lock() {} | void Mutex::lock() {} | ||||||
| bool Mutex::try_lock() { return true; } | bool Mutex::try_lock() { return true; } | ||||||
| void Mutex::unlock() {} | void Mutex::unlock() {} | ||||||
| #elif defined(USE_ESP32) || defined(USE_LIBRETINY) || defined(USE_NRF52) | //TODO | ||||||
|  | #elif defined(USE_ESP32) || defined(USE_LIBRETINY) /*|| defined(USE_NRF52)*/ | ||||||
| Mutex::Mutex() { handle_ = xSemaphoreCreateMutex(); } | Mutex::Mutex() { handle_ = xSemaphoreCreateMutex(); } | ||||||
| void Mutex::lock() { xSemaphoreTake(this->handle_, portMAX_DELAY); } | void Mutex::lock() { xSemaphoreTake(this->handle_, portMAX_DELAY); } | ||||||
| bool Mutex::try_lock() { return xSemaphoreTake(this->handle_, 0) == pdTRUE; } | bool Mutex::try_lock() { return xSemaphoreTake(this->handle_, 0) == pdTRUE; } | ||||||
|   | |||||||
| @@ -21,8 +21,10 @@ | |||||||
| #include <FreeRTOS.h> | #include <FreeRTOS.h> | ||||||
| #include <semphr.h> | #include <semphr.h> | ||||||
| #elif defined(USE_NRF52) | #elif defined(USE_NRF52) | ||||||
|  | #ifdef USE_ARDUINO | ||||||
| #include <Arduino.h> | #include <Arduino.h> | ||||||
| #endif | #endif | ||||||
|  | #endif | ||||||
|  |  | ||||||
| #define HOT __attribute__((hot)) | #define HOT __attribute__((hot)) | ||||||
| #define ESPDEPRECATED(msg, when) __attribute__((deprecated(msg))) | #define ESPDEPRECATED(msg, when) __attribute__((deprecated(msg))) | ||||||
| @@ -548,7 +550,8 @@ class Mutex { | |||||||
|   Mutex &operator=(const Mutex &) = delete; |   Mutex &operator=(const Mutex &) = delete; | ||||||
|  |  | ||||||
|  private: |  private: | ||||||
| #if defined(USE_ESP32) || defined(USE_LIBRETINY) || defined(USE_NRF52) | //TODO | ||||||
|  | #if defined(USE_ESP32) || defined(USE_LIBRETINY) /*|| defined(USE_NRF52)*/ | ||||||
|   SemaphoreHandle_t handle_; |   SemaphoreHandle_t handle_; | ||||||
| #endif | #endif | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -310,6 +310,10 @@ def copy_src_tree(): | |||||||
|                 CORE.relative_src_path("esphome.h"), |                 CORE.relative_src_path("esphome.h"), | ||||||
|                 ESPHOME_H_FORMAT.format(include_s + '\n#include "pio_includes.h"'), |                 ESPHOME_H_FORMAT.format(include_s + '\n#include "pio_includes.h"'), | ||||||
|             ) |             ) | ||||||
|  |     elif CORE.is_nrf52: | ||||||
|  |         from esphome.components.nrf52 import copy_files | ||||||
|  |  | ||||||
|  |         copy_files() | ||||||
|  |  | ||||||
|  |  | ||||||
| def generate_defines_h(): | def generate_defines_h(): | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user