mirror of
https://github.com/esphome/esphome.git
synced 2025-04-15 15:20:27 +01:00
add zephyr ota
This commit is contained in:
parent
eedabbfbb9
commit
02d0cd701b
@ -22,7 +22,6 @@ from esphome.const import (
|
||||
CONF_LOGGER,
|
||||
CONF_NAME,
|
||||
CONF_OTA,
|
||||
CONF_FOTA,
|
||||
CONF_MQTT,
|
||||
CONF_MDNS,
|
||||
CONF_DISABLED,
|
||||
@ -49,7 +48,7 @@ from esphome.util import (
|
||||
get_serial_ports,
|
||||
)
|
||||
from esphome.log import color, setup_log, Fore
|
||||
from .zephyr_tools import smpmgr_scan, smpmgr_upload, list_pyocd
|
||||
from .zephyr_tools import smpmgr_upload, list_pyocd
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@ -92,10 +91,10 @@ def choose_upload_log_host(
|
||||
options = []
|
||||
for port in get_serial_ports():
|
||||
options.append((f"{port.path} ({port.description})", port.path))
|
||||
if show_ota and CONF_FOTA in CORE.config:
|
||||
options.append(
|
||||
(f"mcumgr {port.path} ({port.description})", f"mcumgr {port.path}")
|
||||
)
|
||||
# if show_ota and CONF_FOTA in CORE.config:
|
||||
# options.append(
|
||||
# (f"mcumgr {port.path} ({port.description})", f"mcumgr {port.path}")
|
||||
# )
|
||||
if default == "SERIAL":
|
||||
return choose_prompt(options, purpose=purpose)
|
||||
pyocd = list_pyocd()
|
||||
@ -113,19 +112,19 @@ def choose_upload_log_host(
|
||||
options.append((f"Over The Air ({CORE.address})", CORE.address))
|
||||
if default == "OTA":
|
||||
return CORE.address
|
||||
if show_ota and CONF_FOTA in CORE.config:
|
||||
if default is None or default == "FOTA":
|
||||
ble_devices = asyncio.run(smpmgr_scan())
|
||||
if len(ble_devices) == 0:
|
||||
_LOGGER.warning("No FOTA service found!")
|
||||
for device in ble_devices:
|
||||
options.append(
|
||||
(
|
||||
f"FOTA over Bluetooth LE({device.address}) {device.name}",
|
||||
f"mcumgr {device.address}",
|
||||
)
|
||||
)
|
||||
return choose_prompt(options, purpose=purpose)
|
||||
# if show_ota and CONF_FOTA in CORE.config:
|
||||
# if default is None or default == "FOTA":
|
||||
# ble_devices = asyncio.run(smpmgr_scan())
|
||||
# if len(ble_devices) == 0:
|
||||
# _LOGGER.warning("No FOTA service found!")
|
||||
# for device in ble_devices:
|
||||
# options.append(
|
||||
# (
|
||||
# f"FOTA over Bluetooth LE({device.address}) {device.name}",
|
||||
# f"mcumgr {device.address}",
|
||||
# )
|
||||
# )
|
||||
# return choose_prompt(options, purpose=purpose)
|
||||
if show_mqtt and CONF_MQTT in CORE.config:
|
||||
options.append((f"MQTT ({CORE.config['mqtt'][CONF_BROKER]})", "MQTT"))
|
||||
if default == "OTA":
|
||||
@ -355,8 +354,7 @@ def upload_program(config, args, host):
|
||||
firmware = os.path.abspath(
|
||||
CORE.relative_pioenvs_path(CORE.name, "zephyr", "app_update.bin")
|
||||
)
|
||||
if CONF_FOTA in config:
|
||||
return asyncio.run(smpmgr_upload(config, host.split(" ")[1], firmware))
|
||||
return asyncio.run(smpmgr_upload(config, host.split(" ")[1], firmware))
|
||||
|
||||
if CONF_OTA not in config:
|
||||
raise EsphomeError(
|
||||
|
@ -1,80 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2012-2014 Wind River Systems, Inc.
|
||||
* Copyright (c) 2020 Prevas A/S
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/bluetooth/bluetooth.h>
|
||||
#include <zephyr/bluetooth/conn.h>
|
||||
#include <zephyr/bluetooth/gatt.h>
|
||||
#include <zephyr/mgmt/mcumgr/transport/smp_bt.h>
|
||||
|
||||
#define LOG_LEVEL LOG_LEVEL_DBG
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_REGISTER(smp_bt_sample);
|
||||
|
||||
static struct k_work advertise_work;
|
||||
|
||||
static const struct bt_data ad[] = {
|
||||
BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
|
||||
BT_DATA_BYTES(BT_DATA_UUID128_ALL,
|
||||
0x84, 0xaa, 0x60, 0x74, 0x52, 0x8a, 0x8b, 0x86,
|
||||
0xd3, 0x4c, 0xb7, 0x1d, 0x1d, 0xdc, 0x53, 0x8d),
|
||||
};
|
||||
|
||||
static void advertise(struct k_work *work)
|
||||
{
|
||||
int rc;
|
||||
|
||||
bt_le_adv_stop();
|
||||
|
||||
rc = bt_le_adv_start(BT_LE_ADV_CONN_NAME, ad, ARRAY_SIZE(ad), NULL, 0);
|
||||
if (rc) {
|
||||
LOG_ERR("Advertising failed to start (rc %d)", rc);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_INF("Advertising successfully started");
|
||||
}
|
||||
|
||||
static void connected(struct bt_conn *conn, uint8_t err)
|
||||
{
|
||||
if (err) {
|
||||
LOG_ERR("Connection failed (err 0x%02x)", err);
|
||||
} else {
|
||||
LOG_INF("Connected");
|
||||
}
|
||||
}
|
||||
|
||||
static void disconnected(struct bt_conn *conn, uint8_t reason)
|
||||
{
|
||||
LOG_INF("Disconnected (reason 0x%02x)", reason);
|
||||
k_work_submit(&advertise_work);
|
||||
}
|
||||
|
||||
BT_CONN_CB_DEFINE(conn_callbacks) = {
|
||||
.connected = connected,
|
||||
.disconnected = disconnected,
|
||||
};
|
||||
|
||||
static void bt_ready(int err)
|
||||
{
|
||||
if (err != 0) {
|
||||
LOG_ERR("Bluetooth failed to initialise: %d", err);
|
||||
} else {
|
||||
k_work_submit(&advertise_work);
|
||||
}
|
||||
}
|
||||
|
||||
void start_smp_bluetooth_adverts(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
k_work_init(&advertise_work, advertise);
|
||||
rc = bt_enable(bt_ready);
|
||||
|
||||
if (rc != 0) {
|
||||
LOG_ERR("Bluetooth enable failed: %d", rc);
|
||||
}
|
||||
}
|
@ -84,6 +84,7 @@ def zephyr_to_code(conf):
|
||||
|
||||
# zephyr_add_prj_conf("LOG", True)
|
||||
# zephyr_add_prj_conf("MCUBOOT_UTIL_LOG_LEVEL_WRN", True)
|
||||
# zephyr_add_prj_conf("BOOTLOADER_MCUBOOT", True)
|
||||
|
||||
|
||||
def _format_prj_conf_val(value: PrjConfValueType) -> str:
|
||||
|
@ -21,7 +21,7 @@ CODEOWNERS = ["@esphome/core"]
|
||||
|
||||
def AUTO_LOAD():
|
||||
if CORE.using_zephyr:
|
||||
return ["ota_mcuboot"]
|
||||
return ["zephyr_ota_mcumgr"]
|
||||
return ["ota_network"]
|
||||
|
||||
|
||||
@ -34,7 +34,7 @@ CONF_ON_ERROR = "on_error"
|
||||
ota_ns = cg.esphome_ns.namespace("ota")
|
||||
OTAState = ota_ns.enum("OTAState")
|
||||
if CORE.using_zephyr:
|
||||
OTAComponent = cg.esphome_ns.namespace("ota_mcuboot").class_(
|
||||
OTAComponent = cg.esphome_ns.namespace("zephyr_ota_mcumgr").class_(
|
||||
"OTAComponent", cg.Component
|
||||
)
|
||||
else:
|
||||
@ -50,7 +50,7 @@ OTAEndTrigger = ota_ns.class_("OTAEndTrigger", automation.Trigger.template())
|
||||
OTAErrorTrigger = ota_ns.class_("OTAErrorTrigger", automation.Trigger.template())
|
||||
|
||||
|
||||
def not_supported_by_zephyr(value):
|
||||
def _not_supported_by_zephyr(value):
|
||||
if CORE.using_zephyr:
|
||||
raise cv.Invalid(f"Not supported by zephyr framework({value})")
|
||||
return value
|
||||
@ -67,7 +67,7 @@ CONFIG_SCHEMA = cv.Schema(
|
||||
cv.GenerateID(): cv.declare_id(OTAComponent),
|
||||
cv.Optional(CONF_SAFE_MODE, default=True): cv.boolean,
|
||||
cv.Optional(CONF_VERSION, default=_default_ota_version()): cv.All(
|
||||
cv.one_of(1, 2, int=True), not_supported_by_zephyr
|
||||
cv.one_of(1, 2, int=True), _not_supported_by_zephyr
|
||||
),
|
||||
cv.SplitDefault(
|
||||
CONF_PORT,
|
||||
@ -78,9 +78,9 @@ CONFIG_SCHEMA = cv.Schema(
|
||||
rtl87xx=8892,
|
||||
): cv.All(
|
||||
cv.port,
|
||||
not_supported_by_zephyr,
|
||||
_not_supported_by_zephyr,
|
||||
),
|
||||
cv.Optional(CONF_PASSWORD): cv.All(cv.string, not_supported_by_zephyr),
|
||||
cv.Optional(CONF_PASSWORD): cv.All(cv.string, _not_supported_by_zephyr),
|
||||
cv.Optional(
|
||||
CONF_REBOOT_TIMEOUT, default="5min"
|
||||
): cv.positive_time_period_milliseconds,
|
||||
|
26
esphome/components/zephyr_ble_server/__init__.py
Normal file
26
esphome/components/zephyr_ble_server/__init__.py
Normal file
@ -0,0 +1,26 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
)
|
||||
|
||||
from esphome.components.nrf52.zephyr import zephyr_add_prj_conf
|
||||
|
||||
zephyr_ble_server_ns = cg.esphome_ns.namespace("zephyr_ble_server")
|
||||
BLEServer = zephyr_ble_server_ns.class_("BLEServer", cg.Component)
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(BLEServer),
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA),
|
||||
cv.only_with_zephyr,
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
zephyr_add_prj_conf("BT", True)
|
||||
zephyr_add_prj_conf("BT_PERIPHERAL", True)
|
||||
await cg.register_component(var, config)
|
71
esphome/components/zephyr_ble_server/ble_server.cpp
Normal file
71
esphome/components/zephyr_ble_server/ble_server.cpp
Normal file
@ -0,0 +1,71 @@
|
||||
#include "ble_server.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include <zephyr/bluetooth/bluetooth.h>
|
||||
#include <zephyr/bluetooth/conn.h>
|
||||
|
||||
namespace esphome {
|
||||
namespace zephyr_ble_server {
|
||||
|
||||
static const char *const TAG = "zephyr_ble_server";
|
||||
|
||||
static struct k_work advertise_work;
|
||||
static const struct bt_data ad[] = {
|
||||
BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
|
||||
#ifdef USE_OTA
|
||||
|
||||
BT_DATA_BYTES(BT_DATA_UUID128_ALL, 0x84, 0xaa, 0x60, 0x74, 0x52, 0x8a, 0x8b, 0x86, 0xd3, 0x4c, 0xb7, 0x1d, 0x1d,
|
||||
0xdc, 0x53, 0x8d),
|
||||
#endif
|
||||
};
|
||||
|
||||
const struct bt_le_adv_param *adv_param = BT_LE_ADV_CONN_NAME;
|
||||
|
||||
static void advertise(struct k_work *work) {
|
||||
bt_le_adv_stop();
|
||||
|
||||
int rc = bt_le_adv_start(adv_param, ad, ARRAY_SIZE(ad), NULL, 0);
|
||||
if (rc) {
|
||||
ESP_LOGE(TAG, "Advertising failed to start (rc %d)", rc);
|
||||
return;
|
||||
}
|
||||
ESP_LOGI(TAG, "Advertising successfully started");
|
||||
}
|
||||
|
||||
static void connected(struct bt_conn *conn, uint8_t err) {
|
||||
if (err) {
|
||||
ESP_LOGE(TAG, "Connection failed (err 0x%02x)", err);
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Connected");
|
||||
}
|
||||
}
|
||||
|
||||
static void disconnected(struct bt_conn *conn, uint8_t reason) {
|
||||
ESP_LOGI(TAG, "Disconnected (reason 0x%02x)", reason);
|
||||
k_work_submit(&advertise_work);
|
||||
}
|
||||
|
||||
static void bt_ready(int err) {
|
||||
if (err != 0) {
|
||||
ESP_LOGE(TAG, "Bluetooth failed to initialise: %d", err);
|
||||
} else {
|
||||
k_work_submit(&advertise_work);
|
||||
}
|
||||
}
|
||||
|
||||
BT_CONN_CB_DEFINE(conn_callbacks) = {
|
||||
.connected = connected,
|
||||
.disconnected = disconnected,
|
||||
};
|
||||
|
||||
void BLEServer::setup() {
|
||||
k_work_init(&advertise_work, advertise);
|
||||
|
||||
int rc = bt_enable(bt_ready);
|
||||
if (rc != 0) {
|
||||
ESP_LOGE(TAG, "Bluetooth enable failed: %d", rc);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace zephyr_ble_server
|
||||
} // namespace esphome
|
14
esphome/components/zephyr_ble_server/ble_server.h
Normal file
14
esphome/components/zephyr_ble_server/ble_server.h
Normal file
@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace zephyr_ble_server {
|
||||
|
||||
class BLEServer : public Component {
|
||||
public:
|
||||
void setup() override;
|
||||
};
|
||||
|
||||
} // namespace zephyr_ble_server
|
||||
} // namespace esphome
|
16
esphome/components/zephyr_mcumgr/__init__.py
Normal file
16
esphome/components/zephyr_mcumgr/__init__.py
Normal file
@ -0,0 +1,16 @@
|
||||
from esphome.components.nrf52.zephyr import zephyr_add_prj_conf
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
zephyr_add_prj_conf("NET_BUF", True)
|
||||
zephyr_add_prj_conf("ZCBOR", True)
|
||||
zephyr_add_prj_conf("MCUMGR", True)
|
||||
|
||||
zephyr_add_prj_conf("MCUMGR_GRP_IMG", True)
|
||||
|
||||
zephyr_add_prj_conf("IMG_MANAGER", True)
|
||||
zephyr_add_prj_conf("STREAM_FLASH", True)
|
||||
zephyr_add_prj_conf("FLASH_MAP", True)
|
||||
zephyr_add_prj_conf("FLASH", True)
|
||||
|
||||
zephyr_add_prj_conf("BOOTLOADER_MCUBOOT", True)
|
14
esphome/components/zephyr_ota_mcumgr/__init__.py
Normal file
14
esphome/components/zephyr_ota_mcumgr/__init__.py
Normal file
@ -0,0 +1,14 @@
|
||||
from esphome.components.nrf52.zephyr import zephyr_add_prj_conf
|
||||
|
||||
DEPENDENCIES = ["zephyr_ble_server"]
|
||||
AUTO_LOAD = ["zephyr_mcumgr"]
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
zephyr_add_prj_conf("MCUMGR_TRANSPORT_BT", True)
|
||||
zephyr_add_prj_conf("MCUMGR_TRANSPORT_BT_REASSEMBLY", True)
|
||||
|
||||
zephyr_add_prj_conf("MCUMGR_GRP_OS", True)
|
||||
zephyr_add_prj_conf("MCUMGR_GRP_OS_MCUMGR_PARAMS", True)
|
||||
|
||||
zephyr_add_prj_conf("NCS_SAMPLE_MCUMGR_BT_OTA_DFU_SPEEDUP", True)
|
@ -3,9 +3,9 @@
|
||||
#include "esphome/components/ota/ota_component.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace ota_mcuboot {
|
||||
namespace zephyr_ota_mcumgr {
|
||||
|
||||
class OTAComponent : public ota::OTAComponent {};
|
||||
|
||||
} // namespace ota_mcuboot
|
||||
} // namespace zephyr_ota_mcumgr
|
||||
} // namespace esphome
|
@ -555,7 +555,6 @@ CONF_OSCILLATION_COMMAND_TOPIC = "oscillation_command_topic"
|
||||
CONF_OSCILLATION_OUTPUT = "oscillation_output"
|
||||
CONF_OSCILLATION_STATE_TOPIC = "oscillation_state_topic"
|
||||
CONF_OTA = "ota"
|
||||
CONF_FOTA = "fota"
|
||||
CONF_OUTPUT = "output"
|
||||
CONF_OUTPUT_ID = "output_id"
|
||||
CONF_OUTPUTS = "outputs"
|
||||
|
@ -27,10 +27,10 @@ pyparsing >= 3.0
|
||||
argcomplete>=2.0.0
|
||||
|
||||
# for mcumgr
|
||||
git+https://github.com/tomaszduda23/smp/#f9b85266485062f6a466989e8f59b913cc83b08b
|
||||
git+https://github.com/tomaszduda23/smpclient/#d25c8035ae2858fd41a106058297b619d58fbcb5
|
||||
git+https://github.com/tomaszduda23/smp/@f9b85266485062f6a466989e8f59b913cc83b08b
|
||||
git+https://github.com/tomaszduda23/smpclient/@d25c8035ae2858fd41a106058297b619d58fbcb5
|
||||
# move to framework?
|
||||
git+https://github.com/tomaszduda23/pyOCD/#949193f7cbf09081f8e46d6b9d2e4a79e536997e
|
||||
git+https://github.com/tomaszduda23/pyOCD/@949193f7cbf09081f8e46d6b9d2e4a79e536997e
|
||||
bleak==0.21.1
|
||||
pydantic==2.16.2
|
||||
cbor2==5.6.1
|
||||
|
Loading…
x
Reference in New Issue
Block a user