1
0
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:
Tomasz Duda 2024-02-12 21:17:33 +01:00
parent eedabbfbb9
commit 02d0cd701b
13 changed files with 172 additions and 113 deletions

View File

@ -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(

View File

@ -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);
}
}

View File

@ -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:

View File

@ -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,

View 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)

View 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

View 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

View 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)

View 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)

View File

@ -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

View File

@ -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"

View File

@ -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