mirror of
https://github.com/esphome/esphome.git
synced 2025-02-01 10:40:56 +00:00
rework peer mac_address opdate.
adding static peer option.
This commit is contained in:
parent
36ca1e7213
commit
d23346165e
@ -1,8 +1,13 @@
|
|||||||
from esphome import automation
|
from esphome import automation, core
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import CONF_COMMAND, CONF_ID, CONF_PAYLOAD, CONF_TRIGGER_ID
|
from esphome.const import (
|
||||||
from esphome.core import CORE
|
CONF_COMMAND,
|
||||||
|
CONF_ID,
|
||||||
|
CONF_MAC_ADDRESS,
|
||||||
|
CONF_PAYLOAD,
|
||||||
|
CONF_TRIGGER_ID,
|
||||||
|
)
|
||||||
|
|
||||||
CODEOWNERS = ["@nielsnl68", "@jesserockz"]
|
CODEOWNERS = ["@nielsnl68", "@jesserockz"]
|
||||||
|
|
||||||
@ -13,7 +18,7 @@ ESPNowProtocol = espnow_ns.class_("ESPNowProtocol")
|
|||||||
ESPNowListener = espnow_ns.class_("ESPNowListener")
|
ESPNowListener = espnow_ns.class_("ESPNowListener")
|
||||||
|
|
||||||
ESPNowPacket = espnow_ns.class_("ESPNowPacket")
|
ESPNowPacket = espnow_ns.class_("ESPNowPacket")
|
||||||
ESPNowPeer = espnow_ns.class_("Peer")
|
ESPNowPeer = cg.uint64
|
||||||
|
|
||||||
ESPNowPacketConst = ESPNowPacket.operator("const")
|
ESPNowPacketConst = ESPNowPacket.operator("const")
|
||||||
|
|
||||||
@ -36,7 +41,7 @@ SendAction = espnow_ns.class_("SendAction", automation.Action)
|
|||||||
NewPeerAction = espnow_ns.class_("NewPeerAction", automation.Action)
|
NewPeerAction = espnow_ns.class_("NewPeerAction", automation.Action)
|
||||||
DelPeerAction = espnow_ns.class_("DelPeerAction", automation.Action)
|
DelPeerAction = espnow_ns.class_("DelPeerAction", automation.Action)
|
||||||
SetKeeperAction = espnow_ns.class_("SetKeeperAction", automation.Action)
|
SetKeeperAction = espnow_ns.class_("SetKeeperAction", automation.Action)
|
||||||
|
SetStaticPeerAction = espnow_ns.class_("SetStaticPeerAction", automation.Action)
|
||||||
|
|
||||||
CONF_AUTO_ADD_PEER = "auto_add_peer"
|
CONF_AUTO_ADD_PEER = "auto_add_peer"
|
||||||
CONF_CONFORMATION_TIMEOUT = "conformation_timeout"
|
CONF_CONFORMATION_TIMEOUT = "conformation_timeout"
|
||||||
@ -47,21 +52,19 @@ CONF_ON_BROADCAST = "on_broadcast"
|
|||||||
CONF_ON_SENT = "on_sent"
|
CONF_ON_SENT = "on_sent"
|
||||||
CONF_ON_NEW_PEER = "on_new_peer"
|
CONF_ON_NEW_PEER = "on_new_peer"
|
||||||
CONF_PEER = "peer"
|
CONF_PEER = "peer"
|
||||||
CONF_PEERS = "peers"
|
CONF_PEER_ID = "peer_id"
|
||||||
|
CONF_PREDEFINED_PEERS = "predefined_peers"
|
||||||
CONF_USE_SENT_CHECK = "use_sent_check"
|
CONF_USE_SENT_CHECK = "use_sent_check"
|
||||||
CONF_WIFI_CHANNEL = "wifi_channel"
|
CONF_WIFI_CHANNEL = "wifi_channel"
|
||||||
CONF_PROTOCOL_MODE = "protocol_mode"
|
CONF_PROTOCOL_MODE = "protocol_mode"
|
||||||
CONF_KEEPER = "keeper"
|
|
||||||
|
|
||||||
CONF_MAC_CHARS = "0123456789-AbCdEfGhIjKlMnOpQrStUvWxYz+aBcDeFgHiJkLmNoPqRsTuVwXyZ"
|
|
||||||
|
|
||||||
validate_command = cv.Range(min=1, max=250)
|
validate_command = cv.Range(min=1, max=250)
|
||||||
|
|
||||||
ESPNowProtocol_mode = espnow_ns.enum("ESPNowProtocol_mode")
|
ESPNowProtocol_mode = espnow_ns.enum("ESPNowProtocol_mode")
|
||||||
ENUM_MODE = {
|
ENUM_MODE = {
|
||||||
"universal": ESPNowProtocol_mode.universal,
|
"universal": ESPNowProtocol_mode.pm_universal,
|
||||||
"keeper": ESPNowProtocol_mode.keeper,
|
"keeper": ESPNowProtocol_mode.pm_keeper,
|
||||||
"drudge": ESPNowProtocol_mode.drudge,
|
"drudge": ESPNowProtocol_mode.pm_drudge,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -75,48 +78,19 @@ def validate_raw_data(value):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def convert_mac_address(value):
|
def espnow_hex(mac_address):
|
||||||
parts = value.split(":")
|
it = reversed(mac_address.parts)
|
||||||
if len(parts) != 6:
|
num = "".join(f"{part:02X}" for part in it)
|
||||||
raise cv.Invalid("MAC Address must consist of 6 : (colon) separated parts")
|
return cg.RawExpression(f"0x{num}ULL")
|
||||||
parts_int = 0
|
|
||||||
if any(len(part) != 2 for part in parts):
|
|
||||||
raise cv.Invalid("MAC Address must be format XX:XX:XX:XX:XX:XX")
|
|
||||||
for part in parts:
|
|
||||||
try:
|
|
||||||
parts_int = (parts_int << 8) + int(part, 16)
|
|
||||||
except ValueError:
|
|
||||||
# pylint: disable=raise-missing-from
|
|
||||||
raise cv.Invalid(
|
|
||||||
"MAC Address parts must be hexadecimal values from 00 to FF"
|
|
||||||
)
|
|
||||||
return parts_int
|
|
||||||
|
|
||||||
|
|
||||||
def validate_peer(value):
|
DEFINE_PEER_CONFIG = cv.maybe_simple_value(
|
||||||
if isinstance(value, (int)):
|
{
|
||||||
return value
|
cv.Optional(CONF_PEER_ID): cv.declare_id(ESPNowPeer),
|
||||||
|
cv.Required(CONF_MAC_ADDRESS): cv.mac_address,
|
||||||
value = cv.string_strict(value)
|
},
|
||||||
|
key=CONF_MAC_ADDRESS,
|
||||||
if value.lower() == CONF_KEEPER:
|
)
|
||||||
return 0x0
|
|
||||||
|
|
||||||
if value.find(":") != -1:
|
|
||||||
return convert_mac_address(value)
|
|
||||||
|
|
||||||
if len(value) == 8:
|
|
||||||
mac = 0
|
|
||||||
for x in range(8, 0, -1):
|
|
||||||
n = CONF_MAC_CHARS.find(value[x - 1])
|
|
||||||
if n == -1:
|
|
||||||
raise cv.Invalid(f"peer code is invalid. ({value}|{x})")
|
|
||||||
mac = (mac << 6) + n
|
|
||||||
return mac
|
|
||||||
|
|
||||||
raise cv.Invalid(
|
|
||||||
f"peer code '{value}' needs to be 8 characters width, or a valid Mac address or a hexidacimal value of 12 chars width starting with '0x'"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
CONFIG_SCHEMA = cv.Schema(
|
CONFIG_SCHEMA = cv.Schema(
|
||||||
@ -126,10 +100,10 @@ CONFIG_SCHEMA = cv.Schema(
|
|||||||
cv.Optional(CONF_AUTO_ADD_PEER, default=False): cv.boolean,
|
cv.Optional(CONF_AUTO_ADD_PEER, default=False): cv.boolean,
|
||||||
cv.Optional(CONF_USE_SENT_CHECK, default=True): cv.boolean,
|
cv.Optional(CONF_USE_SENT_CHECK, default=True): cv.boolean,
|
||||||
cv.Optional(
|
cv.Optional(
|
||||||
CONF_CONFORMATION_TIMEOUT, default="5000ms"
|
CONF_CONFORMATION_TIMEOUT, default="5s"
|
||||||
): cv.positive_time_period_milliseconds,
|
): cv.positive_time_period_milliseconds,
|
||||||
cv.Optional(CONF_RETRIES, default=5): cv.int_range(min=1, max=10),
|
cv.Optional(CONF_RETRIES, default=5): cv.int_range(min=1, max=10),
|
||||||
cv.Optional(CONF_KEEPER): cv.templatable(validate_peer),
|
cv.Optional(CONF_PREDEFINED_PEERS): cv.ensure_list(DEFINE_PEER_CONFIG),
|
||||||
cv.Optional(CONF_ON_RECEIVE): automation.validate_automation(
|
cv.Optional(CONF_ON_RECEIVE): automation.validate_automation(
|
||||||
{
|
{
|
||||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ESPNowReceiveTrigger),
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(ESPNowReceiveTrigger),
|
||||||
@ -154,7 +128,6 @@ CONFIG_SCHEMA = cv.Schema(
|
|||||||
cv.Optional(CONF_COMMAND): validate_command,
|
cv.Optional(CONF_COMMAND): validate_command,
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_PEERS): cv.ensure_list(validate_peer),
|
|
||||||
},
|
},
|
||||||
cv.only_on_esp32,
|
cv.only_on_esp32,
|
||||||
).extend(cv.COMPONENT_SCHEMA)
|
).extend(cv.COMPONENT_SCHEMA)
|
||||||
@ -164,7 +137,7 @@ async def to_code(config):
|
|||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
await cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
|
|
||||||
if CORE.using_arduino:
|
if core.CORE.using_arduino:
|
||||||
cg.add_library("WiFi", None)
|
cg.add_library("WiFi", None)
|
||||||
|
|
||||||
cg.add_define("USE_ESPNOW")
|
cg.add_define("USE_ESPNOW")
|
||||||
@ -175,12 +148,11 @@ async def to_code(config):
|
|||||||
cg.add(var.set_conformation_timeout(config[CONF_CONFORMATION_TIMEOUT]))
|
cg.add(var.set_conformation_timeout(config[CONF_CONFORMATION_TIMEOUT]))
|
||||||
cg.add(var.set_retries(config[CONF_RETRIES]))
|
cg.add(var.set_retries(config[CONF_RETRIES]))
|
||||||
|
|
||||||
if CONF_KEEPER in config:
|
for conf in config.get(CONF_PREDEFINED_PEERS, []):
|
||||||
template_ = await cg.templatable(config[CONF_KEEPER], [], cg.uint64)
|
mac = espnow_hex(conf.get(CONF_MAC_ADDRESS))
|
||||||
cg.add(var.set_keeper(template_))
|
if CONF_PEER_ID in conf:
|
||||||
|
cg.new_variable(conf[CONF_PEER_ID], mac)
|
||||||
for conf in config.get(CONF_PEERS, []):
|
cg.add(var.add_peer(mac))
|
||||||
cg.add(var.add_peer(conf))
|
|
||||||
|
|
||||||
for conf in config.get(CONF_ON_SENT, []):
|
for conf in config.get(CONF_ON_SENT, []):
|
||||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||||
@ -233,6 +205,25 @@ async def register_protocol(var, config):
|
|||||||
cg.add(var.set_protocol_mode(config[CONF_PROTOCOL_MODE]))
|
cg.add(var.set_protocol_mode(config[CONF_PROTOCOL_MODE]))
|
||||||
|
|
||||||
|
|
||||||
|
def validate_peer(value):
|
||||||
|
if isinstance(value, cv.Lambda):
|
||||||
|
return cv.returning_lambda(value)
|
||||||
|
if value.find(":") != -1:
|
||||||
|
return cv.mac_address(value)
|
||||||
|
return cv.use_id(ESPNowPeer)(value)
|
||||||
|
|
||||||
|
|
||||||
|
async def register_peer(var, config, args):
|
||||||
|
peer = config.get(CONF_MAC_ADDRESS, 0xFFFFFFFFFFFF)
|
||||||
|
if isinstance(peer, core.MACAddress):
|
||||||
|
peer = espnow_hex(peer)
|
||||||
|
if isinstance(peer, core.ID):
|
||||||
|
peer = await cg.get_variable(peer)
|
||||||
|
|
||||||
|
template_ = await cg.templatable(peer, args, cg.uint64)
|
||||||
|
cg.add(var.set_mac_address(template_))
|
||||||
|
|
||||||
|
|
||||||
@automation.register_action(
|
@automation.register_action(
|
||||||
"espnow.broadcast",
|
"espnow.broadcast",
|
||||||
SendAction,
|
SendAction,
|
||||||
@ -240,7 +231,7 @@ async def register_protocol(var, config):
|
|||||||
{
|
{
|
||||||
cv.GenerateID(): cv.use_id(ESPNowComponent),
|
cv.GenerateID(): cv.use_id(ESPNowComponent),
|
||||||
cv.Required(CONF_PAYLOAD): cv.templatable(validate_raw_data),
|
cv.Required(CONF_PAYLOAD): cv.templatable(validate_raw_data),
|
||||||
cv.Optional(CONF_COMMAND, default=0): cv.templatable(validate_command),
|
cv.Optional(CONF_COMMAND): cv.templatable(validate_command),
|
||||||
},
|
},
|
||||||
key=CONF_PAYLOAD,
|
key=CONF_PAYLOAD,
|
||||||
),
|
),
|
||||||
@ -251,9 +242,9 @@ async def register_protocol(var, config):
|
|||||||
cv.Schema(
|
cv.Schema(
|
||||||
{
|
{
|
||||||
cv.GenerateID(): cv.use_id(ESPNowComponent),
|
cv.GenerateID(): cv.use_id(ESPNowComponent),
|
||||||
cv.Required(CONF_PEER): cv.templatable(validate_peer),
|
cv.Required(CONF_MAC_ADDRESS): validate_peer,
|
||||||
cv.Required(CONF_PAYLOAD): cv.templatable(validate_raw_data),
|
cv.Required(CONF_PAYLOAD): cv.templatable(validate_raw_data),
|
||||||
cv.Optional(CONF_COMMAND, default=0): cv.templatable(validate_command),
|
cv.Optional(CONF_COMMAND): cv.templatable(validate_command),
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@ -261,13 +252,11 @@ async def send_action(config, action_id, template_arg, args):
|
|||||||
var = cg.new_Pvariable(action_id, template_arg)
|
var = cg.new_Pvariable(action_id, template_arg)
|
||||||
await cg.register_parented(var, config[CONF_ID])
|
await cg.register_parented(var, config[CONF_ID])
|
||||||
|
|
||||||
peer = config.get(CONF_PEER, 0xFFFFFFFFFFFF)
|
await register_peer(var, config, args)
|
||||||
template_ = await cg.templatable(peer, args, cg.uint64)
|
|
||||||
cg.add(var.set_peer(template_))
|
|
||||||
|
|
||||||
command = config.get(CONF_COMMAND, 0)
|
if command := config.get(CONF_COMMAND):
|
||||||
template_ = await cg.templatable(command, args, cg.uint8)
|
template_ = await cg.templatable(command, args, cg.uint8)
|
||||||
cg.add(var.set_command(template_))
|
cg.add(var.set_command(template_))
|
||||||
|
|
||||||
data = config.get(CONF_PAYLOAD, [])
|
data = config.get(CONF_PAYLOAD, [])
|
||||||
if isinstance(data, bytes):
|
if isinstance(data, bytes):
|
||||||
@ -281,14 +270,14 @@ async def send_action(config, action_id, template_arg, args):
|
|||||||
|
|
||||||
|
|
||||||
@automation.register_action(
|
@automation.register_action(
|
||||||
"espnow.peer.new",
|
"espnow.peer.add",
|
||||||
NewPeerAction,
|
NewPeerAction,
|
||||||
cv.maybe_simple_value(
|
cv.maybe_simple_value(
|
||||||
{
|
{
|
||||||
cv.GenerateID(): cv.use_id(ESPNowComponent),
|
cv.GenerateID(): cv.use_id(ESPNowComponent),
|
||||||
cv.Required(CONF_PEER): cv.templatable(validate_peer),
|
cv.Required(CONF_MAC_ADDRESS): validate_peer,
|
||||||
},
|
},
|
||||||
key=CONF_PEER,
|
key=CONF_MAC_ADDRESS,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@automation.register_action(
|
@automation.register_action(
|
||||||
@ -297,25 +286,29 @@ async def send_action(config, action_id, template_arg, args):
|
|||||||
cv.maybe_simple_value(
|
cv.maybe_simple_value(
|
||||||
{
|
{
|
||||||
cv.GenerateID(): cv.use_id(ESPNowComponent),
|
cv.GenerateID(): cv.use_id(ESPNowComponent),
|
||||||
cv.Required(CONF_PEER): cv.templatable(validate_peer),
|
cv.Required(CONF_MAC_ADDRESS): validate_peer,
|
||||||
},
|
},
|
||||||
key=CONF_PEER,
|
key=CONF_MAC_ADDRESS,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@automation.register_action(
|
@automation.register_action(
|
||||||
"espnow.keeper.set",
|
"espnow.static.peer",
|
||||||
SetKeeperAction,
|
SetStaticPeerAction,
|
||||||
cv.maybe_simple_value(
|
cv.Schema(
|
||||||
{
|
{
|
||||||
cv.GenerateID(): cv.use_id(ESPNowComponent),
|
cv.GenerateID(): cv.use_id(ESPNowComponent),
|
||||||
cv.Required(CONF_PEER): cv.templatable(validate_peer),
|
cv.Required(CONF_PEER_ID): cv.use_id(ESPNowPeer),
|
||||||
},
|
cv.Required(CONF_MAC_ADDRESS): validate_peer,
|
||||||
key=CONF_PEER,
|
}
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
async def peer_action(config, action_id, template_arg, args):
|
async def peer_action(config, action_id, template_arg, args):
|
||||||
var = cg.new_Pvariable(action_id, template_arg)
|
var = cg.new_Pvariable(action_id, template_arg)
|
||||||
await cg.register_parented(var, config[CONF_ID])
|
await cg.register_parented(var, config[CONF_ID])
|
||||||
template_ = await cg.templatable(config[CONF_PEER], args, cg.uint64)
|
if peer_id := config.get(CONF_PEER_ID):
|
||||||
cg.add(var.set_peer(template_))
|
peer = await cg.get_variable(peer_id)
|
||||||
|
cg.add(var.set_peer_id(peer))
|
||||||
|
|
||||||
|
await register_peer(var, config, args)
|
||||||
|
|
||||||
return var
|
return var
|
||||||
|
@ -29,34 +29,23 @@ static const size_t SEND_BUFFER_SIZE = 200;
|
|||||||
|
|
||||||
ESPNowComponent *ESPNowComponent::static_{nullptr}; // NOLINT
|
ESPNowComponent *ESPNowComponent::static_{nullptr}; // NOLINT
|
||||||
|
|
||||||
std::string espnow_encode_peer(uint64_t peer) {
|
void show_packet(const std::string &title, const ESPNowPacket &packet) {
|
||||||
if (peer == FAILED) {
|
ESP_LOGV(TAG, "%s packet. Peer: '%s', Protocol:%c%c%c-%02x, Sequents: %d.%d, Size: %d, Valid: %s", title.c_str(),
|
||||||
|
packet.get_peer_code().c_str(), packet.at(3), packet.at(4), packet.at(5), packet.at(6), packet.at(7),
|
||||||
|
packet.attempts, packet.content_size(), packet.is_valid() ? "Yes" : "No");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string peer_str(const uint64_t peer) {
|
||||||
|
char mac[24];
|
||||||
|
if (peer == 0)
|
||||||
return "[Not Set]";
|
return "[Not Set]";
|
||||||
} else if (peer == ESPNOW_BROADCAST_ADDR)
|
if (peer == ESPNOW_BROADCAST_ADDR)
|
||||||
return "[BroadCast]";
|
return "[Broadcast]";
|
||||||
|
uint8_t *peer_ = (uint8_t *) &peer;
|
||||||
std::string str1 = "";
|
snprintf(mac, sizeof(mac), "%02X:%02X:%02X:%02X:%02X:%02X", peer_[0], peer_[1], peer_[2], peer_[3], peer_[4],
|
||||||
str1.reserve(8);
|
peer_[5]);
|
||||||
do {
|
|
||||||
str1.push_back(chars[peer & 63]); // Add on the left
|
|
||||||
peer = peer >> 6;
|
|
||||||
} while (peer != 0);
|
|
||||||
return str1;
|
|
||||||
};
|
|
||||||
|
|
||||||
uint64_t espnow_decode_peer(std::string peer) {
|
|
||||||
uint64_t mac = 0;
|
|
||||||
if (peer.size() != 8)
|
|
||||||
return FAILED;
|
|
||||||
|
|
||||||
for (int pos = peer.size(); pos > 0; pos--) {
|
|
||||||
char *p = strchr(chars, peer[pos - 1]);
|
|
||||||
if (p == nullptr)
|
|
||||||
return FAILED;
|
|
||||||
mac = (mac << 6) + (p - chars);
|
|
||||||
}
|
|
||||||
return mac;
|
return mac;
|
||||||
};
|
}
|
||||||
|
|
||||||
/* ESPNowComponent ********************************************************************** */
|
/* ESPNowComponent ********************************************************************** */
|
||||||
|
|
||||||
@ -65,8 +54,7 @@ ESPNowComponent::ESPNowComponent() { ESPNowComponent::static_ = this; } // NOLI
|
|||||||
void ESPNowComponent::dump_config() {
|
void ESPNowComponent::dump_config() {
|
||||||
ESP_LOGCONFIG(TAG, "esp_now:");
|
ESP_LOGCONFIG(TAG, "esp_now:");
|
||||||
|
|
||||||
ESP_LOGCONFIG(TAG, " Own Peer code: %s.", espnow_encode_peer(this->own_peer_address_).c_str());
|
ESP_LOGCONFIG(TAG, " Own Peer code: %s.", peer_str(this->own_peer_address_).c_str());
|
||||||
ESP_LOGCONFIG(TAG, " Keeper Peer code: %s.", espnow_encode_peer(this->get_keeper()).c_str());
|
|
||||||
ESP_LOGCONFIG(TAG, " Wifi channel: %d.", this->wifi_channel_);
|
ESP_LOGCONFIG(TAG, " Wifi channel: %d.", this->wifi_channel_);
|
||||||
ESP_LOGCONFIG(TAG, " Auto add new peers: %s.", this->auto_add_peer_ ? "Yes" : "No");
|
ESP_LOGCONFIG(TAG, " Auto add new peers: %s.", this->auto_add_peer_ ? "Yes" : "No");
|
||||||
ESP_LOGCONFIG(TAG, " Use sent status: %s.", this->use_sent_check_ ? "Yes" : "No");
|
ESP_LOGCONFIG(TAG, " Use sent status: %s.", this->use_sent_check_ ? "Yes" : "No");
|
||||||
@ -74,13 +62,6 @@ void ESPNowComponent::dump_config() {
|
|||||||
ESP_LOGCONFIG(TAG, " Send retries: %d.", this->retries_);
|
ESP_LOGCONFIG(TAG, " Send retries: %d.", this->retries_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ESPNowComponent::show_packet(const std::string &title, const ESPNowPacket &packet) {
|
|
||||||
ESP_LOGV(TAG, "%s packet. Peer: '%s', Header: %c%c%c, Protocol:%c%c%c-%02x, Sequents: %d.%d, Size: %d, Valid: %s",
|
|
||||||
title.c_str(), packet.get_peer_code().c_str(), packet.at(0), packet.at(1), packet.at(2), packet.at(3),
|
|
||||||
packet.at(4), packet.at(5), packet.at(6), packet.at(7), packet.attempts, packet.content_size(),
|
|
||||||
packet.is_valid() ? "Yes" : "No");
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ESPNowComponent::validate_channel_(uint8_t channel) {
|
bool ESPNowComponent::validate_channel_(uint8_t channel) {
|
||||||
wifi_country_t g_self_country;
|
wifi_country_t g_self_country;
|
||||||
esp_wifi_get_country(&g_self_country);
|
esp_wifi_get_country(&g_self_country);
|
||||||
@ -146,9 +127,6 @@ void ESPNowComponent::setup() {
|
|||||||
for (auto id : this->peers_) {
|
for (auto id : this->peers_) {
|
||||||
this->add_peer(id);
|
this->add_peer(id);
|
||||||
}
|
}
|
||||||
if (this->get_keeper() != 0) {
|
|
||||||
this->add_peer(this->get_keeper());
|
|
||||||
}
|
|
||||||
|
|
||||||
this->send_queue_ = xQueueCreate(SEND_BUFFER_SIZE, sizeof(ESPNowPacket));
|
this->send_queue_ = xQueueCreate(SEND_BUFFER_SIZE, sizeof(ESPNowPacket));
|
||||||
if (this->send_queue_ == nullptr) {
|
if (this->send_queue_ == nullptr) {
|
||||||
@ -362,7 +340,8 @@ void ESPNowComponent::on_data_received(const uint8_t *addr, const uint8_t *data,
|
|||||||
} else {
|
} else {
|
||||||
packet.timestamp = millis();
|
packet.timestamp = millis();
|
||||||
}
|
}
|
||||||
ESPNowComponent::static_->show_packet("Receive", packet);
|
|
||||||
|
show_packet("Receive", packet);
|
||||||
|
|
||||||
if (packet.is_valid()) {
|
if (packet.is_valid()) {
|
||||||
xQueueSendToBack(ESPNowComponent::static_->receive_queue_, (void *) &packet, 10);
|
xQueueSendToBack(ESPNowComponent::static_->receive_queue_, (void *) &packet, 10);
|
||||||
@ -407,7 +386,7 @@ void ESPNowComponent::on_data_sent(const uint8_t *mac_addr, esp_now_send_status_
|
|||||||
if (xQueuePeek(ESPNowComponent::static_->send_queue_, (void *) &packet, 10 / portTICK_PERIOD_MS) == pdTRUE) {
|
if (xQueuePeek(ESPNowComponent::static_->send_queue_, (void *) &packet, 10 / portTICK_PERIOD_MS) == pdTRUE) {
|
||||||
if (packet.peer != peer) {
|
if (packet.peer != peer) {
|
||||||
ESP_LOGE(TAG, " Invalid mac address. Expected: %s (%d.%d); got: %s", packet.get_peer_code().c_str(),
|
ESP_LOGE(TAG, " Invalid mac address. Expected: %s (%d.%d); got: %s", packet.get_peer_code().c_str(),
|
||||||
packet.get_sequents(), packet.attempts, espnow_encode_peer(peer).c_str());
|
packet.get_sequents(), packet.attempts, peer_str(peer).c_str());
|
||||||
return;
|
return;
|
||||||
} else if (status != ESP_OK) {
|
} else if (status != ESP_OK) {
|
||||||
ESP_LOGE(TAG, "Sent packet failed for %s (%d.%d)", packet.get_peer_code().c_str(), packet.get_sequents(),
|
ESP_LOGE(TAG, "Sent packet failed for %s (%d.%d)", packet.get_peer_code().c_str(), packet.get_sequents(),
|
||||||
@ -425,12 +404,10 @@ void ESPNowComponent::on_data_sent(const uint8_t *mac_addr, esp_now_send_status_
|
|||||||
/* ESPNowProtocol ********************************************************************** */
|
/* ESPNowProtocol ********************************************************************** */
|
||||||
|
|
||||||
bool ESPNowProtocol::send(uint64_t peer, const uint8_t *data, uint8_t len, uint8_t command) {
|
bool ESPNowProtocol::send(uint64_t peer, const uint8_t *data, uint8_t len, uint8_t command) {
|
||||||
if (peer == 0x0 && this->parent_ != nullptr) {
|
if (peer == 0ULL)
|
||||||
peer = this->parent_->get_keeper();
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
ESPNowPacket packet(peer, data, len, this->get_protocol_id(), command); // NOLINT
|
ESPNowPacket packet(peer, data, len, this->get_protocol_id(), command); // NOLINT
|
||||||
packet.set_sequents(this->get_next_sequents(packet.peer));
|
packet.set_sequents(this->get_next_sequents(peer));
|
||||||
return this->parent_->send(packet);
|
return this->parent_->send(packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
#include "esphome/core/helpers.h"
|
#include "esphome/core/helpers.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
#include "esp_mac.h"
|
||||||
#include <esp_now.h>
|
#include <esp_now.h>
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
@ -32,15 +33,17 @@ static const uint8_t ESPNOW_COMMAND_ACK = 0x06;
|
|||||||
static const uint8_t ESPNOW_COMMAND_NAK = 0x15;
|
static const uint8_t ESPNOW_COMMAND_NAK = 0x15;
|
||||||
static const uint8_t ESPNOW_COMMAND_RESEND = 0x05;
|
static const uint8_t ESPNOW_COMMAND_RESEND = 0x05;
|
||||||
|
|
||||||
static const char chars[] = "0123456789-AbCdEfGhIjKlMnOpQrStUvWxYz+aBcDeFgHiJkLmNoPqRsTuVwXyZ";
|
|
||||||
static const uint64_t FAILED = 0;
|
static const uint64_t FAILED = 0;
|
||||||
|
|
||||||
|
struct ESPNowPacket;
|
||||||
|
|
||||||
template<typename T> std::string espnow_i2h(T i) { return sprintf("%04x", i); }
|
template<typename T> std::string espnow_i2h(T i) { return sprintf("%04x", i); }
|
||||||
|
|
||||||
std::string espnow_rdm(std::string::size_type length);
|
std::string espnow_rdm(std::string::size_type length);
|
||||||
|
|
||||||
std::string espnow_encode_peer(uint64_t peer);
|
std::string peer_str(const uint64_t peer);
|
||||||
uint64_t espnow_decode_peer(std::string peer);
|
|
||||||
|
void show_packet(const std::string &title, const ESPNowPacket &packet);
|
||||||
|
|
||||||
struct ESPNowPacket {
|
struct ESPNowPacket {
|
||||||
uint64_t peer{0};
|
uint64_t peer{0};
|
||||||
@ -107,7 +110,7 @@ struct ESPNowPacket {
|
|||||||
inline bool is_peer(const uint8_t *peer) const { return memcmp(peer, this->get_peer(), 6) == 0; }
|
inline bool is_peer(const uint8_t *peer) const { return memcmp(peer, this->get_peer(), 6) == 0; }
|
||||||
|
|
||||||
inline uint8_t *get_peer() const { return (uint8_t *) &(this->peer); }
|
inline uint8_t *get_peer() const { return (uint8_t *) &(this->peer); }
|
||||||
inline std::string get_peer_code() const { return espnow_encode_peer(this->peer); }
|
inline std::string get_peer_code() const { return peer_str(this->peer); }
|
||||||
|
|
||||||
inline uint32_t get_protocol() const { return this->content.prefix.protocol & 0x00FFFFFF; }
|
inline uint32_t get_protocol() const { return this->content.prefix.protocol & 0x00FFFFFF; }
|
||||||
inline void set_protocol(uint32_t protocol) {
|
inline void set_protocol(uint32_t protocol) {
|
||||||
@ -140,7 +143,7 @@ struct ESPNowPacket {
|
|||||||
|
|
||||||
class ESPNowComponent;
|
class ESPNowComponent;
|
||||||
|
|
||||||
enum ESPNowProtocol_mode { universal, keeper, drudge };
|
enum ESPNowProtocol_mode { pm_universal, pm_keeper, pm_drudge };
|
||||||
|
|
||||||
class ESPNowProtocol : public Parented<ESPNowComponent> {
|
class ESPNowProtocol : public Parented<ESPNowComponent> {
|
||||||
public:
|
public:
|
||||||
@ -148,7 +151,7 @@ class ESPNowProtocol : public Parented<ESPNowComponent> {
|
|||||||
ESPNowProtocol_mode get_protocol_mode() { return this->protocol_mode_; }
|
ESPNowProtocol_mode get_protocol_mode() { return this->protocol_mode_; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
ESPNowProtocol_mode protocol_mode_{universal};
|
ESPNowProtocol_mode protocol_mode_{pm_universal};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual uint32_t get_protocol_id() = 0;
|
virtual uint32_t get_protocol_id() = 0;
|
||||||
@ -189,11 +192,11 @@ class ESPNowProtocol : public Parented<ESPNowComponent> {
|
|||||||
std::map<uint64_t, uint8_t> next_sequents_{};
|
std::map<uint64_t, uint8_t> next_sequents_{};
|
||||||
std::string get_mode_name_() {
|
std::string get_mode_name_() {
|
||||||
switch (this->protocol_mode_) {
|
switch (this->protocol_mode_) {
|
||||||
case universal:
|
case pm_universal:
|
||||||
return "Universal";
|
return "Universal";
|
||||||
case keeper:
|
case pm_keeper:
|
||||||
return "Keeper";
|
return "Keeper";
|
||||||
case drudge:
|
case pm_drudge:
|
||||||
return "Drudge";
|
return "Drudge";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -259,8 +262,7 @@ class ESPNowComponent : public Component {
|
|||||||
void set_conformation_timeout(uint32_t timeout) { this->conformation_timeout_ = timeout; }
|
void set_conformation_timeout(uint32_t timeout) { this->conformation_timeout_ = timeout; }
|
||||||
void set_retries(uint8_t value) { this->retries_ = value; }
|
void set_retries(uint8_t value) { this->retries_ = value; }
|
||||||
void set_pairing_protocol(ESPNowProtocol *pairing_protocol) { this->pairing_protocol_ = pairing_protocol; }
|
void set_pairing_protocol(ESPNowProtocol *pairing_protocol) { this->pairing_protocol_ = pairing_protocol; }
|
||||||
void set_keeper(uint64_t keeper) { this->keeper_ = keeper; }
|
|
||||||
uint64_t get_keeper() { return this->keeper_; }
|
|
||||||
uint64_t get_own_peer_address() { return this->own_peer_address_; }
|
uint64_t get_own_peer_address() { return this->own_peer_address_; }
|
||||||
|
|
||||||
void setup() override;
|
void setup() override;
|
||||||
@ -290,8 +292,6 @@ class ESPNowComponent : public Component {
|
|||||||
|
|
||||||
ESPNowDefaultProtocol *get_default_protocol();
|
ESPNowDefaultProtocol *get_default_protocol();
|
||||||
|
|
||||||
void show_packet(const std::string &title, const ESPNowPacket &packet);
|
|
||||||
|
|
||||||
static void espnow_task(void *params);
|
static void espnow_task(void *params);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@ -302,7 +302,6 @@ class ESPNowComponent : public Component {
|
|||||||
uint8_t wifi_channel_{0};
|
uint8_t wifi_channel_{0};
|
||||||
uint32_t conformation_timeout_{5000};
|
uint32_t conformation_timeout_{5000};
|
||||||
uint8_t retries_{5};
|
uint8_t retries_{5};
|
||||||
uint64_t keeper_{0};
|
|
||||||
|
|
||||||
bool auto_add_peer_{false};
|
bool auto_add_peer_{false};
|
||||||
bool use_sent_check_{true};
|
bool use_sent_check_{true};
|
||||||
@ -326,49 +325,55 @@ class ESPNowComponent : public Component {
|
|||||||
static ESPNowComponent *static_; // NOLINT
|
static ESPNowComponent *static_; // NOLINT
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/********************************* Actions **************************************/
|
||||||
template<typename... Ts> class SendAction : public Action<Ts...>, public Parented<ESPNowComponent> {
|
template<typename... Ts> class SendAction : public Action<Ts...>, public Parented<ESPNowComponent> {
|
||||||
public:
|
TEMPLATABLE_VALUE(uint64_t, mac_address);
|
||||||
TEMPLATABLE_VALUE(uint64_t, peer);
|
|
||||||
TEMPLATABLE_VALUE(uint8_t, command);
|
TEMPLATABLE_VALUE(uint8_t, command);
|
||||||
TEMPLATABLE_VALUE(std::vector<uint8_t>, payload);
|
TEMPLATABLE_VALUE(std::vector<uint8_t>, payload);
|
||||||
|
|
||||||
|
public:
|
||||||
void play(Ts... x) override {
|
void play(Ts... x) override {
|
||||||
uint64_t peer = this->peer_.value(x...);
|
uint64_t peer = this->mac_address_.value(x...);
|
||||||
uint8_t command = this->command_.value(x...);
|
uint8_t command = this->command_.value(x...);
|
||||||
std::vector<uint8_t> payload = this->payload_.value(x...);
|
std::vector<uint8_t> payload = this->payload_.value(x...);
|
||||||
ESP_LOGVV("SendAction", "send to 0x%12llx, command %d, payload size: %d", peer, command, payload.size());
|
|
||||||
|
|
||||||
this->parent_->get_default_protocol()->send(peer, payload.data(), payload.size(), command);
|
this->parent_->get_default_protocol()->send(peer, payload.data(), payload.size(), command);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename... Ts> class NewPeerAction : public Action<Ts...>, public Parented<ESPNowComponent> {
|
template<typename... Ts> class NewPeerAction : public Action<Ts...>, public Parented<ESPNowComponent> {
|
||||||
public:
|
public:
|
||||||
TEMPLATABLE_VALUE(uint64_t, peer);
|
TEMPLATABLE_VALUE(uint64_t, mac_address);
|
||||||
void play(Ts... x) override {
|
void play(Ts... x) override {
|
||||||
auto peer = this->peer_.value(x...);
|
uint64_t mac_address = this->mac_address_.value(x...);
|
||||||
parent_->add_peer(peer);
|
parent_->add_peer(mac_address);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename... Ts> class DelPeerAction : public Action<Ts...>, public Parented<ESPNowComponent> {
|
template<typename... Ts> class DelPeerAction : public Action<Ts...>, public Parented<ESPNowComponent> {
|
||||||
public:
|
public:
|
||||||
TEMPLATABLE_VALUE(uint64_t, peer);
|
TEMPLATABLE_VALUE(uint64_t, mac_address);
|
||||||
void play(Ts... x) override {
|
void play(Ts... x) override {
|
||||||
auto peer = this->peer_.value(x...);
|
uint64_t mac_address = this->mac_address_.value(x...);
|
||||||
parent_->del_peer(peer);
|
parent_->del_peer(mac_address);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename... Ts> class SetKeeperAction : public Action<Ts...>, public Parented<ESPNowComponent> {
|
template<typename... Ts> class SetStaticPeerAction : public Action<Ts...>, public Parented<ESPNowComponent> {
|
||||||
public:
|
public:
|
||||||
TEMPLATABLE_VALUE(uint64_t, peer);
|
TEMPLATABLE_VALUE(uint64_t, mac_address);
|
||||||
|
void set_peer_id(uint64_t &peer_id) { this->peer_id_ = &peer_id; }
|
||||||
void play(Ts... x) override {
|
void play(Ts... x) override {
|
||||||
auto peer = this->peer_.value(x...);
|
uint64_t mac_address = this->mac_address_.value(x...);
|
||||||
parent_->set_keeper(peer);
|
*(this->peer_id_) = mac_address;
|
||||||
|
if (mac_address != 0)
|
||||||
|
parent_->add_peer(mac_address);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
uint64_t *peer_id_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/********************************* triggers **************************************/
|
||||||
class ESPNowSentTrigger : public Trigger<const ESPNowPacket, bool> {
|
class ESPNowSentTrigger : public Trigger<const ESPNowPacket, bool> {
|
||||||
public:
|
public:
|
||||||
explicit ESPNowSentTrigger(ESPNowComponent *parent) {
|
explicit ESPNowSentTrigger(ESPNowComponent *parent) {
|
||||||
|
@ -7,7 +7,8 @@ esp32:
|
|||||||
board: esp32dev
|
board: esp32dev
|
||||||
framework:
|
framework:
|
||||||
type: esp-idf
|
type: esp-idf
|
||||||
# version: 5.1.5
|
# version: 5.2.1
|
||||||
|
# platform_version: 6.6.0
|
||||||
|
|
||||||
esphome:
|
esphome:
|
||||||
name: "${name}"
|
name: "${name}"
|
||||||
@ -28,11 +29,7 @@ logger:
|
|||||||
level: verbose
|
level: verbose
|
||||||
|
|
||||||
espnow:
|
espnow:
|
||||||
auto_add_peer: false
|
auto_add_peer: true
|
||||||
peers:
|
|
||||||
- FF:FF:FF:FF:FF:FF
|
|
||||||
- flW1QA3k
|
|
||||||
|
|
||||||
on_receive:
|
on_receive:
|
||||||
- logger.log:
|
- logger.log:
|
||||||
format: "Received: '%s' from '%s' command: %d RSSI: %d"
|
format: "Received: '%s' from '%s' command: %d RSSI: %d"
|
||||||
@ -44,11 +41,12 @@ espnow:
|
|||||||
packet.rssi,
|
packet.rssi,
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# this works only when esp_idf v5.1.5+ is being used.
|
||||||
on_broadcast:
|
on_broadcast:
|
||||||
- command: 123
|
- command: 123
|
||||||
then:
|
then:
|
||||||
- logger.log:
|
- logger.log:
|
||||||
format: "Broadcast Received from: '%s' RSSI: %d: %s"
|
format: "Broadcast from: '%s' RSSI: %d: %s"
|
||||||
args:
|
args:
|
||||||
[
|
[
|
||||||
packet.get_peer_code().c_str(),
|
packet.get_peer_code().c_str(),
|
||||||
|
@ -7,7 +7,8 @@ esp32:
|
|||||||
board: esp32dev
|
board: esp32dev
|
||||||
framework:
|
framework:
|
||||||
type: esp-idf
|
type: esp-idf
|
||||||
# version: 5.1.5
|
# version: 5.2.1
|
||||||
|
# platform_version: 6.6.0
|
||||||
|
|
||||||
esphome:
|
esphome:
|
||||||
name: "${name}"
|
name: "${name}"
|
||||||
@ -27,11 +28,18 @@ esphome:
|
|||||||
logger:
|
logger:
|
||||||
level: verbose
|
level: verbose
|
||||||
|
|
||||||
|
globals:
|
||||||
|
- id: hub_address
|
||||||
|
type: uint64_t
|
||||||
|
initial_value: "0xE86BEA23CD98"
|
||||||
|
restore_value: yes
|
||||||
|
|
||||||
espnow:
|
espnow:
|
||||||
auto_add_peer: true
|
auto_add_peer: true
|
||||||
peers:
|
predefined_peers:
|
||||||
- FF:FF:FF:FF:FF:FF
|
- peer_id: keeper
|
||||||
keeper: rmT7YF9o
|
mac_address: E8:6B:EA:23:CD:98
|
||||||
|
|
||||||
on_receive:
|
on_receive:
|
||||||
- logger.log:
|
- logger.log:
|
||||||
format: "Received: '%s' from '%s' command: %d RSSI: %d"
|
format: "Received: '%s' from '%s' command: %d RSSI: %d"
|
||||||
@ -65,12 +73,30 @@ interval:
|
|||||||
- interval: 5sec
|
- interval: 5sec
|
||||||
then:
|
then:
|
||||||
- espnow.send:
|
- espnow.send:
|
||||||
peer: keeper
|
mac_address: keeper
|
||||||
payload: "tesing the test"
|
payload: "Used static keeper value"
|
||||||
|
command: 222
|
||||||
|
- espnow.send:
|
||||||
|
mac_address: E8:6B:EA:23:CD:98
|
||||||
|
payload: "used fixed mac address"
|
||||||
|
command: 123
|
||||||
|
- espnow.send:
|
||||||
|
# dynamic peer address
|
||||||
|
mac_address: !lambda return keeper;
|
||||||
|
payload: "use keeper dynamicly "
|
||||||
|
command: 62
|
||||||
|
- espnow.send:
|
||||||
|
mac_address: !lambda return id(hub_address);
|
||||||
|
payload: "Using a global numberic value dynamicly"
|
||||||
|
command: 132
|
||||||
|
|
||||||
binary_sensor:
|
binary_sensor:
|
||||||
- platform: gpio
|
- platform: gpio
|
||||||
pin: GPIO39
|
pin: GPIO39
|
||||||
name: Button
|
name: Button
|
||||||
on_click:
|
on_click:
|
||||||
- espnow.peer.del: rmT7YF9o
|
- espnow.static.peer:
|
||||||
|
peer_id: keeper
|
||||||
|
mac_address: 80:6B:EA:23:CD:87
|
||||||
|
- espnow.peer.add: 80:6B:EA:23:AA:BB
|
||||||
|
- espnow.peer.del: 80:6B:EA:23:AA:BB
|
||||||
|
Loading…
x
Reference in New Issue
Block a user