Created
April 19, 2024 19:59
-
-
Save szczys/6496f48a305cf51faed503afd1bb10a5 to your computer and use it in GitHub Desktop.
Golioth Bluetooth Gateway Demo
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* main.c - Application main entry point */ | |
/* | |
* Copyright (c) 2020 SixOctets Systems | |
* | |
* SPDX-License-Identifier: Apache-2.0 | |
*/ | |
#include <stdio.h> | |
#include <stddef.h> | |
#include <errno.h> | |
#include <zephyr/kernel.h> | |
#include <zephyr/sys/printk.h> | |
#include <zephyr/sys/byteorder.h> | |
#include <zephyr/bluetooth/bluetooth.h> | |
#include <zephyr/bluetooth/hci.h> | |
#include <zephyr/bluetooth/conn.h> | |
#include <zephyr/bluetooth/uuid.h> | |
#include <zephyr/bluetooth/gatt.h> | |
#include <golioth/client.h> | |
#include <golioth/stream.h> | |
#include <samples/common/net_connect.h> | |
#include <samples/common/sample_credentials.h> | |
static struct golioth_client *client; | |
static K_SEM_DEFINE(golioth_connected, 0, 1); | |
static void on_client_event(struct golioth_client *client, enum golioth_client_event event, | |
void *arg) | |
{ | |
bool is_connected = (event == GOLIOTH_CLIENT_EVENT_CONNECTED); | |
if (is_connected) { | |
k_sem_give(&golioth_connected); | |
} | |
printk("Golioth client %s\n", is_connected ? "connected" : "disconnected"); | |
} | |
static int scan_start(void); | |
static struct bt_conn *default_conn; | |
static struct bt_uuid_16 discover_uuid = BT_UUID_INIT_16(0); | |
static struct bt_gatt_discover_params discover_params; | |
static struct bt_gatt_subscribe_params subscribe_params; | |
static double pow(double x, double y) | |
{ | |
double result = 1; | |
if (y < 0) { | |
y = -y; | |
while (y--) { | |
result /= x; | |
} | |
} else { | |
while (y--) { | |
result *= x; | |
} | |
} | |
return result; | |
} | |
static uint8_t notify_func(struct bt_conn *conn, | |
struct bt_gatt_subscribe_params *params, | |
const void *data, uint16_t length) | |
{ | |
double temperature; | |
uint32_t mantissa; | |
int8_t exponent; | |
if (!data) { | |
printk("[UNSUBSCRIBED]\n"); | |
params->value_handle = 0U; | |
return BT_GATT_ITER_STOP; | |
} | |
/* temperature value display */ | |
mantissa = sys_get_le24(&((uint8_t *)data)[1]); | |
exponent = ((uint8_t *)data)[4]; | |
temperature = (double)mantissa * pow(10, exponent); | |
printf("Temperature %gC.\n", temperature); | |
char sbuf[32]; | |
snprintk(sbuf, sizeof(sbuf), "{\"temperature\":%g}", temperature); | |
printf("Sending to Golioth: %s\n", sbuf); | |
int err = golioth_stream_set_async(client, | |
"sensor", | |
GOLIOTH_CONTENT_TYPE_JSON, | |
sbuf, | |
strlen(sbuf), | |
NULL, | |
NULL); | |
if (err) { | |
printf("Failed to push temperature: %d\n", err); | |
} | |
return BT_GATT_ITER_CONTINUE; | |
} | |
static uint8_t discover_func(struct bt_conn *conn, | |
const struct bt_gatt_attr *attr, | |
struct bt_gatt_discover_params *params) | |
{ | |
int err; | |
if (!attr) { | |
printk("Discover complete\n"); | |
(void)memset(params, 0, sizeof(*params)); | |
return BT_GATT_ITER_STOP; | |
} | |
printk("[ATTRIBUTE] handle %u\n", attr->handle); | |
if (!bt_uuid_cmp(discover_params.uuid, BT_UUID_HTS)) { | |
memcpy(&discover_uuid, BT_UUID_HTS_MEASUREMENT, sizeof(discover_uuid)); | |
discover_params.uuid = &discover_uuid.uuid; | |
discover_params.start_handle = attr->handle + 1; | |
discover_params.type = BT_GATT_DISCOVER_CHARACTERISTIC; | |
err = bt_gatt_discover(conn, &discover_params); | |
if (err) { | |
printk("Discover failed (err %d)\n", err); | |
} | |
} else if (!bt_uuid_cmp(discover_params.uuid, | |
BT_UUID_HTS_MEASUREMENT)) { | |
memcpy(&discover_uuid, BT_UUID_GATT_CCC, sizeof(discover_uuid)); | |
discover_params.uuid = &discover_uuid.uuid; | |
discover_params.start_handle = attr->handle + 2; | |
discover_params.type = BT_GATT_DISCOVER_DESCRIPTOR; | |
subscribe_params.value_handle = bt_gatt_attr_value_handle(attr); | |
err = bt_gatt_discover(conn, &discover_params); | |
if (err) { | |
printk("Discover failed (err %d)\n", err); | |
} | |
} else { | |
subscribe_params.notify = notify_func; | |
subscribe_params.value = BT_GATT_CCC_INDICATE; | |
subscribe_params.ccc_handle = attr->handle; | |
err = bt_gatt_subscribe(conn, &subscribe_params); | |
if (err && err != -EALREADY) { | |
printk("Subscribe failed (err %d)\n", err); | |
} else { | |
printk("[SUBSCRIBED]\n"); | |
} | |
return BT_GATT_ITER_STOP; | |
} | |
return BT_GATT_ITER_STOP; | |
} | |
static void connected(struct bt_conn *conn, uint8_t conn_err) | |
{ | |
char addr[BT_ADDR_LE_STR_LEN]; | |
int err; | |
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr)); | |
if (conn_err) { | |
printk("Failed to connect to %s (%u)\n", addr, conn_err); | |
bt_conn_unref(default_conn); | |
default_conn = NULL; | |
scan_start(); | |
return; | |
} | |
printk("Connected: %s\n", addr); | |
if (conn == default_conn) { | |
memcpy(&discover_uuid, BT_UUID_HTS, sizeof(discover_uuid)); | |
discover_params.uuid = &discover_uuid.uuid; | |
discover_params.func = discover_func; | |
discover_params.start_handle = BT_ATT_FIRST_ATTRIBUTE_HANDLE; | |
discover_params.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE; | |
discover_params.type = BT_GATT_DISCOVER_PRIMARY; | |
err = bt_gatt_discover(default_conn, &discover_params); | |
if (err) { | |
printk("Discover failed(err %d)\n", err); | |
return; | |
} | |
} | |
} | |
static bool eir_found(struct bt_data *data, void *user_data) | |
{ | |
bt_addr_le_t *addr = user_data; | |
int i; | |
printk("[AD]: %u data_len %u\n", data->type, data->data_len); | |
switch (data->type) { | |
case BT_DATA_UUID16_SOME: | |
case BT_DATA_UUID16_ALL: | |
if (data->data_len % sizeof(uint16_t) != 0U) { | |
printk("AD malformed\n"); | |
return true; | |
} | |
for (i = 0; i < data->data_len; i += sizeof(uint16_t)) { | |
struct bt_uuid *uuid; | |
uint16_t u16; | |
int err; | |
memcpy(&u16, &data->data[i], sizeof(u16)); | |
uuid = BT_UUID_DECLARE_16(sys_le16_to_cpu(u16)); | |
if (bt_uuid_cmp(uuid, BT_UUID_HTS)) { | |
continue; | |
} | |
err = bt_le_scan_stop(); | |
if (err) { | |
printk("Stop LE scan failed (err %d)\n", err); | |
continue; | |
} | |
err = bt_conn_le_create(addr, BT_CONN_LE_CREATE_CONN, | |
BT_LE_CONN_PARAM_DEFAULT, | |
&default_conn); | |
if (err) { | |
printk("Create connection failed (err %d)\n", | |
err); | |
scan_start(); | |
} | |
return false; | |
} | |
} | |
return true; | |
} | |
static void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type, | |
struct net_buf_simple *ad) | |
{ | |
char dev[BT_ADDR_LE_STR_LEN]; | |
bt_addr_le_to_str(addr, dev, sizeof(dev)); | |
printk("[DEVICE]: %s, AD evt type %u, AD data len %u, RSSI %i\n", | |
dev, type, ad->len, rssi); | |
/* We're only interested in connectable events */ | |
if (type == BT_HCI_ADV_IND || type == BT_HCI_ADV_DIRECT_IND) { | |
bt_data_parse(ad, eir_found, (void *)addr); | |
} | |
} | |
static int scan_start(void) | |
{ | |
/* Use active scanning and disable duplicate filtering to handle any | |
* devices that might update their advertising data at runtime. | |
*/ | |
struct bt_le_scan_param scan_param = { | |
.type = BT_LE_SCAN_TYPE_ACTIVE, | |
.options = BT_LE_SCAN_OPT_NONE, | |
.interval = BT_GAP_SCAN_FAST_INTERVAL, | |
.window = BT_GAP_SCAN_FAST_WINDOW, | |
}; | |
return bt_le_scan_start(&scan_param, device_found); | |
} | |
static void disconnected(struct bt_conn *conn, uint8_t reason) | |
{ | |
char addr[BT_ADDR_LE_STR_LEN]; | |
int err; | |
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr)); | |
printk("Disconnected: %s (reason 0x%02x)\n", addr, reason); | |
if (default_conn != conn) { | |
return; | |
} | |
bt_conn_unref(default_conn); | |
default_conn = NULL; | |
err = scan_start(); | |
if (err) { | |
printk("Scanning failed to start (err %d)\n", err); | |
} | |
} | |
BT_CONN_CB_DEFINE(conn_callbacks) = { | |
.connected = connected, | |
.disconnected = disconnected, | |
}; | |
int main(void) | |
{ | |
net_connect(); | |
const struct golioth_client_config *client_config = golioth_sample_credentials_get(); | |
client = golioth_client_create(client_config); | |
golioth_client_register_event_callback(client, on_client_event, NULL); | |
k_sem_take(&golioth_connected, K_FOREVER); | |
int err; | |
err = bt_enable(NULL); | |
if (err) { | |
printk("Bluetooth init failed (err %d)\n", err); | |
return 0; | |
} | |
printk("Bluetooth initialized\n"); | |
err = scan_start(); | |
if (err) { | |
printk("Scanning failed to start (err %d)\n", err); | |
return 0; | |
} | |
printk("Scanning successfully started\n"); | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# General config | |
CONFIG_HEAP_MEM_POOL_SIZE=4096 | |
CONFIG_NEWLIB_LIBC=y | |
# Networking | |
CONFIG_NET_SOCKETS_OFFLOAD=y | |
CONFIG_NET_IPV6=y | |
CONFIG_NET_IPV6_NBR_CACHE=n | |
CONFIG_NET_IPV6_MLD=n | |
# Increase native TLS socket implementation, so that it is chosen instead of | |
# offloaded nRF91 sockets | |
CONFIG_NET_SOCKETS_TLS_PRIORITY=35 | |
# Modem library | |
CONFIG_NRF_MODEM_LIB=y | |
CONFIG_NRF_MODEM_LIB_ON_FAULT_APPLICATION_SPECIFIC=y | |
# LTE connectivity with network connection manager | |
CONFIG_LTE_CONNECTIVITY=y | |
CONFIG_NET_CONNECTION_MANAGER=y | |
CONFIG_NET_CONNECTION_MANAGER_MONITOR_STACK_SIZE=1024 | |
# Increased sysworkq size, due to LTE connectivity | |
CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048 | |
# Disable options y-selected by NCS for no good reason | |
CONFIG_MBEDTLS_KEY_EXCHANGE_DHE_PSK_ENABLED=n | |
CONFIG_MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED=n | |
# Generate MCUboot compatible images | |
CONFIG_BOOTLOADER_MCUBOOT=y | |
# UART from BLE LTE gateway board config | |
CONFIG_NRF_SW_LPUART=y | |
CONFIG_NRF_SW_LPUART_INT_DRIVEN=y | |
CONFIG_UART_2_ASYNC=y | |
CONFIG_UART_2_INTERRUPT_DRIVEN=n | |
CONFIG_UART_2_NRF_HW_ASYNC=y | |
CONFIG_UART_2_NRF_HW_ASYNC_TIMER=2 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <nrf9160dk_nrf52840_reset_on_if5.dtsi> | |
/ { | |
chosen { | |
zephyr,bt-uart=&lpuart; | |
}; | |
}; | |
&gpiote { | |
interrupts = <49 NRF_DEFAULT_IRQ_PRIORITY>; | |
}; | |
&uart2 { | |
current-speed = <1000000>; | |
status = "okay"; | |
/delete-property/ hw-flow-control; | |
pinctrl-0 = <&uart2_default_alt>; | |
pinctrl-1 = <&uart2_sleep_alt>; | |
pinctrl-names = "default", "sleep"; | |
lpuart: nrf-sw-lpuart { | |
compatible = "nordic,nrf-sw-lpuart"; | |
status = "okay"; | |
req-pin = <21>; /* <&interface_to_nrf52840 3 0>; */ | |
rdy-pin = <19>; /* <&interface_to_nrf52840 2 0>; */ | |
}; | |
}; | |
&pinctrl { | |
uart2_default_alt: uart2_default_alt { | |
group1 { | |
psels = <NRF_PSEL(UART_TX, 0, 18)>, | |
<NRF_PSEL(UART_RX, 0, 17)>; | |
}; | |
}; | |
uart2_sleep_alt: uart2_sleep_alt { | |
group1 { | |
psels = <NRF_PSEL(UART_TX, 0, 18)>, | |
<NRF_PSEL(UART_RX, 0, 17)>; | |
low-power-enable; | |
}; | |
}; | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Use the reset line that is available starting from v0.14.0 of the DK. */ | |
#include <nrf9160dk_nrf52840_reset_on_if9.dtsi> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Golioth Firmware SDK with all dependencies | |
CONFIG_GOLIOTH_FIRMWARE_SDK=y | |
# Application | |
CONFIG_MAIN_STACK_SIZE=2048 | |
CONFIG_GOLIOTH_SAMPLE_COMMON=y | |
CONFIG_LOG_BACKEND_GOLIOTH=y | |
CONFIG_GOLIOTH_SETTINGS=y | |
CONFIG_GOLIOTH_STREAM=y | |
CONFIG_GOLIOTH_SAMPLE_HARDCODED_CREDENTIALS=n | |
CONFIG_FLASH=y | |
CONFIG_FLASH_MAP=y | |
CONFIG_NVS=y | |
CONFIG_SHELL=y | |
CONFIG_SETTINGS=y | |
CONFIG_SETTINGS_RUNTIME=y | |
CONFIG_GOLIOTH_SAMPLE_PSK_SETTINGS=y | |
CONFIG_GOLIOTH_SAMPLE_SETTINGS_AUTOLOAD=y | |
CONFIG_GOLIOTH_SAMPLE_SETTINGS_SHELL=y | |
CONFIG_LOG=y | |
CONFIG_EVENTFD_MAX=14 | |
CONFIG_LOG_PROCESS_THREAD_STACK_SIZE=1536 | |
CONFIG_MBEDTLS_ENABLE_HEAP=y | |
CONFIG_MBEDTLS_HEAP_SIZE=10240 | |
CONFIG_MBEDTLS_SSL_IN_CONTENT_LEN=2048 | |
CONFIG_MBEDTLS_SSL_OUT_CONTENT_LEN=2048 | |
CONFIG_NETWORKING=y | |
CONFIG_NET_IPV4=y | |
CONFIG_POSIX_MAX_FDS=23 | |
# Enable Bluetooth stack and libraries | |
CONFIG_BT=y | |
CONFIG_LOG=y | |
CONFIG_BT_CENTRAL=y | |
CONFIG_BT_SMP=y | |
CONFIG_BT_GATT_CLIENT=y | |
CONFIG_CBPRINTF_FP_SUPPORT=y |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment