Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BTHome: New feature #20996

Draft
wants to merge 8 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion drivers/isl29020/isl29020_saul.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ static int read(const void *dev, phydat_t *res)
res->val[0] = (int16_t)isl29020_read((const isl29020_t *)dev);
res->val[1] = 0;
res->val[2] = 0;
res->unit = UNIT_CD;
res->unit = UNIT_LUX; /* https://www.renesas.com/en/document/dst/isl29020-datasheet */
res->scale = 0;
return 1;
}
Expand Down
1 change: 1 addition & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ Here is a quick overview of the examples available in the RIOT:
|---------|-------------|
| [skald_eddystone](./skald_eddystone/README.md) | This example demonstrates the usage of `Skald` for creating an Google `Eddystone` beacon. |
| [skald_ibeacon](./skald_ibeacon/README.md) | This example demonstrates the usage of `Skald` for creating an Apple `iBeacon`. |
| [skald_bthome](./skald_bthome/README.md) | This example demonstrates the usage of `Skald` for sending SAUL measurements via [`BTHome`](https://bthome.io). |

### MQTT

Expand Down
31 changes: 31 additions & 0 deletions examples/skald_bthome/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# name of your application
APPLICATION = skald_bthome

# If no BOARD is found in the environment, use this default:
BOARD ?= nrf52dk

# This has to be the absolute path to the RIOT base directory:
RIOTBASE ?= $(CURDIR)/../..

# include Skald using SAUL
USEMODULE += saul_default
USEMODULE += skald_bthome_saul

DEVELHELP ?= 1

BTHOME_NAME = "RIOT"
BTHOME_ADV_INTERVAL ?= 60000
ENCRYPTION_KEY ?=

ifneq ($(ENCRYPTION_KEY),)
USEMODULE += skald_bthome_encrypt
CFLAGS += -DENCRYPTION_KEY="\"$(ENCRYPTION_KEY)\""
endif

CFLAGS += -DBTHOME_NAME="\"$(BTHOME_NAME)\""
CFLAGS += -DBTHOME_ADV_INTERVAL=$(BTHOME_ADV_INTERVAL)

# Change this to 0 show compiler invocation lines by default:
QUIET ?= 1

include $(RIOTBASE)/Makefile.include
7 changes: 7 additions & 0 deletions examples/skald_bthome/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Skald BTHome Example

This example demonstrates the usage of `Skald` for creating a BTHome
setup, advertising sensors in the SAUL registry.

Simply compile and flash, and verify your newly created beacon with any type of
BLE scanner / BTHome receiver (e.g. Home Assistant).
151 changes: 151 additions & 0 deletions examples/skald_bthome/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
/*
* Copyright (C) 2024 Martine S. Lenders
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/

/**
* @ingroup examples
* @{
*
* @file
* @brief BLE BTHome example using Skald
*
* @author Martine S. Lenders <[email protected]>
*
* @}
*/

#include <errno.h>
#include <stdio.h>

#include "saul_reg.h"
#include "ztimer.h"

#include "net/skald/bthome.h"

#ifndef CONFIG_BTHOME_SAUL_REG_DEVS
#define CONFIG_BTHOME_SAUL_REG_DEVS (16U)
#endif

#ifndef BTHOME_ADV_INTERVAL
#define BTHOME_ADV_INTERVAL (60000U)
#endif

static skald_bthome_ctx_t _ctx;
static skald_bthome_saul_t _saul_devs[CONFIG_BTHOME_SAUL_REG_DEVS];

#ifdef ENCRYPTION_KEY
int _get_encryption_key(void)
{
static const char enc_str[] = ENCRYPTION_KEY;
static uint8_t encryption_key[SKALD_BTHOME_KEY_LEN] = { 0 };
size_t enc_str_len = strlen(enc_str);
uint8_t key_len = 0;

uint8_t shift = 4;
for (unsigned i = 0; i < enc_str_len; i++) {
char c = enc_str[i];
unsigned char offset;

if (c >= '0' && c <= '9') {
offset = '0';
}
else if (c >= 'a' && c <= 'f') {
offset = 'a' - 10;
}
else {
continue;
}
encryption_key[i / 2] |= (c - offset) << shift;
shift = (shift) ? 0 : 4;
key_len = (i / 2) + 1;
if (key_len > SKALD_BTHOME_KEY_LEN) {
printf("Key was too long: %u bytes\n", key_len);
return -ENOBUFS;
}
}

if (key_len < SKALD_BTHOME_KEY_LEN) {
printf("Key was too short: %u bytes\n", key_len);
return -EINVAL;
}
memcpy(_ctx.key, encryption_key, key_len);
_ctx.encrypt = 1;
return key_len;
}
#endif

static int _add_text(skald_bthome_ctx_t *ctx, uint8_t obj_id, phydat_t *data, uint8_t idx)
{
static const char info[] = "RIOT";

(void)data;
(void)idx;
return skald_bthome_add_measurement(ctx, obj_id, info, strlen(info));
}

int main(void)
{
saul_reg_t *dev = saul_reg;
unsigned i = 0;
int res;

ztimer_sleep(ZTIMER_MSEC, 2000);
printf("Skald and the tale of Harald's home\n");

#ifdef ENCRYPTION_KEY
int key_len;

key_len = _get_encryption_key();

if (key_len != SKALD_BTHOME_KEY_LEN) {
printf(
"Key should be of length 16 (32 chars), "
"was \"%s\" (%u chars)\n",
ENCRYPTION_KEY,
strlen(ENCRYPTION_KEY)
);
return 1;
}
#endif
_ctx.skald.update_pkt = NULL;
_ctx.devs = NULL;
_ctx.skald.pkt.len = 0;
if (skald_bthome_init(&_ctx, NULL, BTHOME_NAME, 0) < 0) {
return 1;
}
if (!saul_reg) {
puts("Hark! The board does not know SAUL. :-(");
return 1;
}
while (dev && (i < CONFIG_BTHOME_SAUL_REG_DEVS)) {
_saul_devs[i].saul = *dev; /* copy registry entry */
_saul_devs[i].saul.next = NULL;
printf("Adding %s (%s) to BTHome.\n", dev->name, saul_class_to_str(dev->driver->type));
if ((res = skald_bthome_saul_add(&_ctx, &_saul_devs[i])) < 0) {
errno = -res;
perror("Unable to add sensor to BTHome");
dev = dev->next;
continue;
};
i++;
dev = dev->next;
}
assert(!saul_reg || _ctx.devs);
if (i < CONFIG_BTHOME_SAUL_REG_DEVS) {
memset(&_saul_devs[i].saul, 0, sizeof(_saul_devs[i].saul));
_saul_devs[i].obj_id = BTHOME_ID_TEXT;
_saul_devs[i].flags = SKALD_BTHOME_SAUL_FLAGS_CUSTOM;
_saul_devs[i].add_measurement = _add_text;
if ((res = skald_bthome_saul_add(&_ctx, &_saul_devs[i])) < 0) {
errno = -res;
perror("Unable to add text info to BTHome");
};
i++;
}
skald_bthome_advertise(&_ctx, BTHOME_ADV_INTERVAL);
return 0;
}
4 changes: 4 additions & 0 deletions makefiles/pseudomodules.inc.mk
Original file line number Diff line number Diff line change
Expand Up @@ -590,8 +590,12 @@ NO_PSEUDOMODULES += periph_common
PSEUDOMODULES += pio_autostart_%

# Submodules provided by Skald
PSEUDOMODULES += skald_update_pkt_cb
PSEUDOMODULES += skald_ibeacon
PSEUDOMODULES += skald_eddystone
PSEUDOMODULES += skald_bthome
PSEUDOMODULES += skald_bthome_encrypt
PSEUDOMODULES += skald_bthome_saul

PSEUDOMODULES += crypto_aes_128
PSEUDOMODULES += crypto_aes_192
Expand Down
16 changes: 16 additions & 0 deletions sys/Makefile.dep
Original file line number Diff line number Diff line change
Expand Up @@ -581,6 +581,22 @@ ifneq (,$(filter skald,$(USEMODULE)))
USEMODULE += ztimer_msec
endif

ifneq (,$(filter skald_bthome_encrypt,$(USEMODULE)))
USEMODULE += cipher_modes
USEMODULE += skald_bthome
endif

ifneq (,$(filter skald_bthome_saul,$(USEMODULE)))
USEMODULE += skald_bthome
USEMODULE += skald_update_pkt_cb
USEMODULE += saul_reg
endif

ifneq (,$(filter skald_update_pkt_cb,$(USEMODULE)))
USEMODULE += event
USEMODULE += skald
endif

ifneq (,$(filter bluetil_addr,$(USEMODULE)))
USEMODULE += fmt
endif
Expand Down
44 changes: 37 additions & 7 deletions sys/include/net/skald.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,15 @@

#include <stdint.h>

#include "kernel_defines.h"
#include "ztimer.h"
#include "net/ble.h"
#include "net/netdev/ble.h"

#if IS_USED(MODULE_SKALD_UPDATE_PKT_CB)
#include "event.h"
#endif

#ifdef __cplusplus
extern "C" {
#endif
Expand All @@ -68,16 +73,41 @@ typedef struct {
uint8_t u8[16]; /**< UUID with byte-wise access */
} skald_uuid_t;

/**
* @brief Forward declaration
*/
typedef struct skald_ctx skald_ctx_t;

/**
* @brief Advertising context holding the advertising data and state
*/
typedef struct {
netdev_ble_pkt_t pkt; /**< packet holding the advertisement (GAP) data */
ztimer_t timer; /**< timer for scheduling advertising events */
ztimer_now_t last; /**< last timer trigger (for offset compensation) */
uint8_t cur_chan; /**< keep track of advertising channels */
uint32_t adv_itvl_ms; /**< advertising interval [ms] */
} skald_ctx_t;
struct skald_ctx {
netdev_ble_pkt_t pkt; /**< packet holding the advertisement (GAP) data */
#if IS_USED(MODULE_SKALD_UPDATE_PKT_CB) || defined(DOXYGEN)
/**
* @brief callback to update packet on periodic advertisements
*
* Requires module `skald_pkt_update_cb`.
*/
void (*update_pkt)(skald_ctx_t *);
/**
* @brief Event queue to send periodic advertisements from
*
* Only available and needed with module `skald_pkt_update_cb`.
*/
event_queue_t queue;
/**
* @brief The event to trigger to send periodic advertisements
*
* Only available and needed with module `skald_pkt_update_cb`.
*/
event_t event;
#endif
ztimer_t timer; /**< timer for scheduling advertising events */
ztimer_now_t last; /**< last timer trigger (for offset compensation) */
uint8_t cur_chan; /**< keep track of advertising channels */
uint32_t adv_itvl_ms; /**< advertising interval [ms] */
};

/**
* @brief Initialize Skald and the underlying radio
Expand Down
Loading
Loading