From 7cb0abd4e869a77dab7491a0c624e31136957a32 Mon Sep 17 00:00:00 2001 From: HendrikVE Date: Thu, 7 Mar 2019 15:37:08 +0100 Subject: [PATCH] add configurator application --- configurator/Makefile | 31 +++ configurator/README.md | 32 +++ configurator/ble_service.h | 211 ++++++++++++++++++++ configurator/config.c | 50 +++++ configurator/config.h | 48 +++++ configurator/main.c | 391 +++++++++++++++++++++++++++++++++++++ configurator/main.h | 109 +++++++++++ 7 files changed, 872 insertions(+) create mode 100644 configurator/Makefile create mode 100644 configurator/README.md create mode 100644 configurator/ble_service.h create mode 100644 configurator/config.c create mode 100644 configurator/config.h create mode 100644 configurator/main.c create mode 100644 configurator/main.h diff --git a/configurator/Makefile b/configurator/Makefile new file mode 100644 index 0000000..fa93254 --- /dev/null +++ b/configurator/Makefile @@ -0,0 +1,31 @@ +# name of your application +APPLICATION = configurator + +# If no BOARD is found in the environment, use this default: +BOARD ?= nrf52dk + +# So far, NimBLE only works on nRF52 based platforms +BOARD_WHITELIST := nrf52dk nrf52840dk + +# This has to be the absolute path to the RIOT base directory: +RIOTBASE ?= $(CURDIR)/../RIOT + +# Include NimBLE +USEPKG += nimble + +USEMODULE += nimble_svc_gap +USEMODULE += nimble_svc_gatt + +FEATURES_REQUIRED += periph_flashpage + +# Comment this out to disable code in RIOT that does safety checking +# which is not needed in a production environment but helps in the +# development process: +DEVELHELP ?= 1 + +CFLAGS += -DDEBUG_ASSERT_VERBOSE + +# Change this to 0 show compiler invocation lines by default: +QUIET ?= 1 + +include $(RIOTBASE)/Makefile.include diff --git a/configurator/README.md b/configurator/README.md new file mode 100644 index 0000000..4805174 --- /dev/null +++ b/configurator/README.md @@ -0,0 +1,32 @@ +## About + +This application provides a simple Bluetooth LE service (via RIOT's +NimBLE port) for device configuration purposes. The included bluetooth +characteristics are taken from an +[application](https://github.com/HendrikVE/smarthome2/tree/master/ESP32/window_alert_riot) for +a sensor node, which is placed in a specific room and transmits data to +a MQTT-SN gateway. +The sent values ​​are stored persistently in MCU's internal flash via +RIOT's flashpage API. + +There is an Android App specifically written for this application. +Either build the app yourself +[from source](https://github.com/HendrikVE/smarthome2/tree/master/AndroidApp/WindowAlarmConfig), +download it from +[here](https://play.google.com/store/apps/details?id=de.vanappsteer.windowalarmconfig) +or use Nordics "nRF Connect"-App, available for +[Android](https://play.google.com/store/apps/details?id=no.nordicsemi.android.mcp) +and +[iOS](https://itunes.apple.com/us/app/nrf-connect/id1054362403). + +In the case you want to use Nordics app, you will need to write an empty string to the +characteristic "890f7b6f-cecc-4e3e-ade2-5f2907867f4b" in the end of the configuration process +to store the data permanently and to restart the device afterwards. + +Currently the application is only running on *nrf52dk* and *nrf52840dk* boards. + +## Clean install + +Add `JLINK_PRE_FLASH=erase` to your make command to erase the flash memory. +That way you avoid data left in the storage, especially on the flash page +used for configuration. diff --git a/configurator/ble_service.h b/configurator/ble_service.h new file mode 100644 index 0000000..e6702e3 --- /dev/null +++ b/configurator/ble_service.h @@ -0,0 +1,211 @@ +#ifndef BLE_SERVICE_H +#define BLE_SERVICE_H + +#include "host/ble_uuid.h" + +#define GATT_DEVICE_INFO_UUID 0x180A +#define GATT_MANUFACTURER_NAME_UUID 0x2A29 +#define GATT_MODEL_NUMBER_UUID 0x2A24 + +/* CONFIG SERVICE UUID */ +/* UUID = 2fa1dab8-3eef-40fc-8540-7fc496a10d75 */ +static const ble_uuid128_t gatt_svr_svc_cfg_uuid + = BLE_UUID128_INIT(0x75, 0x0d, 0xa1, 0x96, 0xc4, 0x7f, 0x40, 0x85, 0xfc, + 0x40, 0xef, 0x3e, 0xb8, 0xda, 0xa1, 0x2f); + +/* CONFIG DEVICE */ +/* UUID = d3491796-683b-4b9c-aafb-f51a35459d43 */ +static const ble_uuid128_t gatt_svr_chr_cfg_device_room_uuid + = BLE_UUID128_INIT(0x43, 0x9d, 0x45, 0x35, 0x1a, 0xf5, 0xfb, 0xaa, 0x9c, + 0x4b, 0x3b, 0x68, 0x96, 0x17, 0x49, 0xd3); + +/* UUID = 4745e11f-b403-4cfb-83bb-710d46897875 */ +static const ble_uuid128_t gatt_svr_chr_cfg_device_id_uuid + = BLE_UUID128_INIT(0x75, 0x78, 0x89, 0x46, 0x0d, 0x71, 0xbb, 0x83, 0xfb, + 0x4c, 0x03, 0xb4, 0x1f, 0xe1, 0x45, 0x47); + +/* CONFIG OTA */ +/* UUID = 2f44b103-444c-48f5-bf60-91b81dfa0a25 */ +static const ble_uuid128_t gatt_svr_chr_cfg_ota_host_uuid + = BLE_UUID128_INIT(0x25, 0x0a, 0xfa, 0x1d, 0xb8, 0x91, 0x60, 0xbf, 0xf5, + 0x48, 0x4c, 0x44, 0x03, 0xb1, 0x44, 0x2f); + +/* UUID = 4b95d245-db08-4c56-98f9-738faa8cfbb6 */ +static const ble_uuid128_t gatt_svr_chr_cfg_ota_filename_uuid + = BLE_UUID128_INIT(0xb6, 0xfb, 0x8c, 0xaa, 0x8f, 0x73, 0xf9, 0x98, 0x56, + 0x4c, 0x08, 0xdb, 0x45, 0xd2, 0x95, 0x4b); + +/* UUID = 1c93dce2-3796-4027-9f55-6d251c41dd34 */ +static const ble_uuid128_t gatt_svr_chr_cfg_ota_server_username_uuid + = BLE_UUID128_INIT(0x34, 0xdd, 0x41, 0x1c, 0x25, 0x6d, 0x55, 0x9f, 0x27, + 0x40, 0x96, 0x37, 0xe2, 0xdc, 0x93, 0x1c); + +/* UUID = 0e837309-5336-45a3-9b69-d0f7134f30ff */ +static const ble_uuid128_t gatt_svr_chr_cfg_ota_server_password_uuid + = BLE_UUID128_INIT(0xff, 0x30, 0x4f, 0x13, 0xf7, 0xd0, 0x69, 0x9b, 0xa3, + 0x45, 0x36, 0x53, 0x09, 0x73, 0x83, 0x0e); + +/* CONFIG WiFi */ +/* UUID = 8ca0bf1d-bb5d-4a66-9191-341fd805e288 */ +static const ble_uuid128_t gatt_svr_chr_cfg_wifi_ssid_uuid + = BLE_UUID128_INIT(0x88, 0xe2, 0x05, 0xd8, 0x1f, 0x34, 0x91, 0x91, 0x66, + 0x4a, 0x5d, 0xbb, 0x1d, 0xbf, 0xa0, 0x8c); + +/* UUID = fa41c195-ae99-422e-8f1f-0730702b3fc5 */ +static const ble_uuid128_t gatt_svr_chr_cfg_wifi_password_uuid + = BLE_UUID128_INIT(0xc5, 0x3f, 0x2b, 0x70, 0x30, 0x07, 0x1f, 0x8f, 0x2e, + 0x42, 0x99, 0xae, 0x95, 0xc1, 0x41, 0xfa); + +/* CONFIG MQTT */ +/* UUID = 69150609-18f8-4523-a41f-6d9a01d2e08d */ +static const ble_uuid128_t gatt_svr_chr_cfg_mqtt_user_uuid + = BLE_UUID128_INIT(0x8d, 0xe0, 0xd2, 0x01, 0x9a, 0x6d, 0x1f, 0xa4, 0x23, + 0x45, 0xf8, 0x18, 0x09, 0x06, 0x15, 0x69); + +/* UUID = 8bebec77-ea21-4c14-9d64-dbec1fd5267c */ +static const ble_uuid128_t gatt_svr_chr_cfg_mqtt_password_uuid + = BLE_UUID128_INIT(0x7c, 0x26, 0xd5, 0x1f, 0xec, 0xdb, 0x64, 0x9d, 0x14, + 0x4c, 0x21, 0xea, 0x77, 0xec, 0xeb, 0x8b); + +/* UUID = e3b150fb-90a2-4cd3-80c5-b1189e110ef1 */ +static const ble_uuid128_t gatt_svr_chr_cfg_mqtt_server_ip_uuid + = BLE_UUID128_INIT(0xf1, 0x0e, 0x11, 0x9e, 0x18, 0xb1, 0xc5, 0x80, 0xd3, + 0x4c, 0xa2, 0x90, 0xfb, 0x50, 0xb1, 0xe3); + +/* UUID = 4eeff953-0f5e-43ee-b1be-1783a8190b0d */ +static const ble_uuid128_t gatt_svr_chr_cfg_mqtt_server_port_uuid + = BLE_UUID128_INIT(0x0d, 0x0b, 0x19, 0xa8, 0x83, 0x17, 0xbe, 0xb1, 0xee, + 0x43, 0x5e, 0x0f, 0x53, 0xf9, 0xef, 0x4e); + +/* CONFIG SENSOR */ +/* UUID = 68011c92-854a-4f2c-a94c-5ee37dc607c3 */ +static const ble_uuid128_t gatt_svr_chr_cfg_sensor_poll_interval_ms_uuid + = BLE_UUID128_INIT(0xc3, 0x07, 0xc6, 0x7d, 0xe3, 0x5e, 0x4c, 0xa9, 0x2c, + 0x4f, 0x4a, 0x85, 0x92, 0x1c, 0x01, 0x68); + +/* RESTART */ +/* UUID = 890f7b6f-cecc-4e3e-ade2-5f2907867f4b */ +static const ble_uuid128_t gatt_svr_chr_cfg_restart_uuid + = BLE_UUID128_INIT(0x4b, 0x7f, 0x86, 0x07, 0x29, 0x5f, 0xe2, 0xad, 0x3e, + 0x4e, 0xcc, 0xce, 0x6f, 0x7b, 0x0f, 0x89); + +static int gatt_svr_chr_access_device_info_manufacturer( + uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, void *arg); + +static int gatt_svr_chr_access_device_info_model( + uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, void *arg); + +static int gatt_svr_chr_access_rw_demo( + uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, void *arg); + +/* define several bluetooth services for our device */ +static const struct ble_gatt_svc_def gatt_svr_svcs[] = { + /* + * access_cb defines a callback for read and write access events on + * given characteristics + */ + { + /* Service: Device Information */ + .type = BLE_GATT_SVC_TYPE_PRIMARY, + .uuid = BLE_UUID16_DECLARE(GATT_DEVICE_INFO_UUID), + .characteristics = (struct ble_gatt_chr_def[]) { { + /* Characteristic: * Manufacturer name */ + .uuid = BLE_UUID16_DECLARE(GATT_MANUFACTURER_NAME_UUID), + .access_cb = gatt_svr_chr_access_device_info_manufacturer, + .flags = BLE_GATT_CHR_F_READ, + }, { + /* Characteristic: Model number string */ + .uuid = BLE_UUID16_DECLARE(GATT_MODEL_NUMBER_UUID), + .access_cb = gatt_svr_chr_access_device_info_model, + .flags = BLE_GATT_CHR_F_READ, + }, { + 0, /* No more characteristics in this service */ + }, } + }, + { + /* Service: Config */ + .type = BLE_GATT_SVC_TYPE_PRIMARY, + .uuid = (ble_uuid_t*) &gatt_svr_svc_cfg_uuid.u, + .characteristics = (struct ble_gatt_chr_def[]) { { + /* Characteristic: Config device room */ + .uuid = (ble_uuid_t*) &gatt_svr_chr_cfg_device_room_uuid.u, + .access_cb = gatt_svr_chr_access_rw_demo, + .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE, + },{ + /* Characteristic: Config device id */ + .uuid = (ble_uuid_t*) &gatt_svr_chr_cfg_device_id_uuid.u, + .access_cb = gatt_svr_chr_access_rw_demo, + .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE, + },{ + /* Characteristic: Config ota host */ + .uuid = (ble_uuid_t*) &gatt_svr_chr_cfg_ota_host_uuid.u, + .access_cb = gatt_svr_chr_access_rw_demo, + .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE, + },{ + /* Characteristic: Config ota filename */ + .uuid = (ble_uuid_t*) &gatt_svr_chr_cfg_ota_filename_uuid.u, + .access_cb = gatt_svr_chr_access_rw_demo, + .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE, + },{ + /* Characteristic: Config ota server username */ + .uuid = (ble_uuid_t*) &gatt_svr_chr_cfg_ota_server_username_uuid.u, + .access_cb = gatt_svr_chr_access_rw_demo, + .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE, + },{ + /* Characteristic: Config ota server password */ + .uuid = (ble_uuid_t*) &gatt_svr_chr_cfg_ota_server_password_uuid.u, + .access_cb = gatt_svr_chr_access_rw_demo, + .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE, + },{ + /* Characteristic: Config wifi ssid */ + .uuid = (ble_uuid_t*) &gatt_svr_chr_cfg_wifi_ssid_uuid.u, + .access_cb = gatt_svr_chr_access_rw_demo, + .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE, + },{ + /* Characteristic: Config wifi password */ + .uuid = (ble_uuid_t*) &gatt_svr_chr_cfg_wifi_password_uuid.u, + .access_cb = gatt_svr_chr_access_rw_demo, + .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE, + },{ + /* Characteristic: Config mqtt user */ + .uuid = (ble_uuid_t*) &gatt_svr_chr_cfg_mqtt_user_uuid.u, + .access_cb = gatt_svr_chr_access_rw_demo, + .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE, + },{ + /* Characteristic: Config mqtt password */ + .uuid = (ble_uuid_t*) &gatt_svr_chr_cfg_mqtt_password_uuid.u, + .access_cb = gatt_svr_chr_access_rw_demo, + .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE, + },{ + /* Characteristic: Config mqtt server ip */ + .uuid = (ble_uuid_t*) &gatt_svr_chr_cfg_mqtt_server_ip_uuid.u, + .access_cb = gatt_svr_chr_access_rw_demo, + .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE, + },{ + /* Characteristic: Config mqtt server port */ + .uuid = (ble_uuid_t*) &gatt_svr_chr_cfg_mqtt_server_port_uuid.u, + .access_cb = gatt_svr_chr_access_rw_demo, + .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE, + },{ + /* Characteristic: Config sensor poll interval ms */ + .uuid = (ble_uuid_t*) &gatt_svr_chr_cfg_sensor_poll_interval_ms_uuid.u, + .access_cb = gatt_svr_chr_access_rw_demo, + .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE, + },{ + /* Characteristic: Config save and restart */ + .uuid = (ble_uuid_t*) &gatt_svr_chr_cfg_restart_uuid.u, + .access_cb = gatt_svr_chr_access_rw_demo, + .flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_WRITE, + },{ + 0, /* No more characteristics in this service */ + }, } + }, + { + 0, /* No more services */ + }, +}; + +#endif /*BLE_SERVICE_H*/ diff --git a/configurator/config.c b/configurator/config.c new file mode 100644 index 0000000..bfb3444 --- /dev/null +++ b/configurator/config.c @@ -0,0 +1,50 @@ +#include +#include + +#include "periph/flashpage.h" + +#include "config.h" + +static int read_page(int page, uint8_t buffer[]); +static int write_page(int page, uint8_t buffer[]); + +void config_read(union config_flashpage* config) +{ + read_page(FLASHPAGE_NUM_CONFIG, config->page_mem); +} + +void config_store(union config_flashpage* config) +{ + write_page(FLASHPAGE_NUM_CONFIG, config->page_mem); +} + +static int read_page(int page, uint8_t buffer[]) +{ + if ((page >= (int)FLASHPAGE_NUMOF) || (page < 0)) { + printf("error: page %i is invalid\n", page); + return 1; + } + + flashpage_read(page, buffer); + printf("Read flash page %i into local page buffer\n", page); + + return 0; +} + +static int write_page(int page, uint8_t buffer[]) +{ + if ((page >= (int)FLASHPAGE_NUMOF) || (page < 0)) { + printf("error: page %i is invalid\n", page); + return 1; + } + + if (flashpage_write_and_verify(page, buffer) != FLASHPAGE_OK) { + printf("error: verification for page %i failed\n", page); + return 1; + } + + printf("wrote local page buffer to flash page %i at addr %p\n", + page, flashpage_addr(page)); + + return 0; +} diff --git a/configurator/config.h b/configurator/config.h new file mode 100644 index 0000000..1b4c4b1 --- /dev/null +++ b/configurator/config.h @@ -0,0 +1,48 @@ +#ifndef CONFIG_H +#define CONFIG_H + +#include + +#define ALIGNMENT_ATTR +#define FLASHPAGE_NUM_CONFIG FLASHPAGE_NUMOF - 1 + +#define CONFIG_DEVICE_ROOM "CONFIG_DEVICE_ROOM" +#define CONFIG_DEVICE_ID "CONFIG_DEVICE_ID" +#define CONFIG_OTA_HOST "CONFIG_OTA_HOST" +#define CONFIG_OTA_FILENAME "CONFIG_OTA_FILENAME" +#define CONFIG_OTA_SERVER_USERNAME "CONFIG_OTA_SERVER_USERNAME" +#define CONFIG_OTA_SERVER_PASSWORD "CONFIG_OTA_SERVER_PASSWORD" +#define CONFIG_ESP_WIFI_SSID "CONFIG_ESP_WIFI_SSID" +#define CONFIG_ESP_WIFI_PASSWORD "CONFIG_ESP_WIFI_PASSWORD" +#define CONFIG_MQTT_USER "CONFIG_MQTT_USER" +#define CONFIG_MQTT_PASSWORD "CONFIG_MQTT_PASSWORD" +#define CONFIG_MQTT_SERVER_IP "CONFIG_MQTT_SERVER_IP" +#define CONFIG_MQTT_SERVER_PORT "CONFIG_MQTT_SERVER_PORT" +#define CONFIG_SENSOR_POLL_INTERVAL_MS "CONFIG_SENSOR_POLL_INTERVAL_MS" + +struct config_values { + uint8_t device_room[256]; + uint8_t device_id[256]; + uint8_t ota_host[256]; + uint8_t ota_filename[256]; + uint8_t ota_server_username[256]; + uint8_t ota_server_password[256]; + uint8_t wifi_ssid[256]; + uint8_t wifi_password[256]; + uint8_t mqtt_user[256]; + uint8_t mqtt_password[256]; + uint8_t mqtt_server_ip[256]; + uint8_t mqtt_server_port[32]; + uint8_t sensor_poll_interval_ms[32]; +}; + +static_assert(FLASHPAGE_SIZE >= 4096, "condition not met: FLASHPAGE_SIZE >= 4096"); +union config_flashpage { + struct config_values config_values; + uint8_t page_mem[FLASHPAGE_SIZE] ALIGNMENT_ATTR; +}; + +void config_read(union config_flashpage* config); +void config_store(union config_flashpage* config); + +#endif /*CONFIG_H*/ diff --git a/configurator/main.c b/configurator/main.c new file mode 100644 index 0000000..ab84f14 --- /dev/null +++ b/configurator/main.c @@ -0,0 +1,391 @@ +/* + * Copyright (C) 2019 Freie Universität Berlin + * + * 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 applications + * @{ + * + * @file + * @brief BLE service for configuration of a device using NimBLE and flashpage + * + * Test this application e.g. with Nordics "nRF Connect"-App + * iOS: https://itunes.apple.com/us/app/nrf-connect/id1054362403 + * Android: https://play.google.com/store/apps/details?id=no.nordicsemi.android.mcp + * + * Hint: There is an app specifically written for this service. + * You can find it here: + * https://github.com/HendrikVE/smarthome2/tree/master/AndroidApp/WindowAlarmConfig + * + * @author Hendrik van Essen + * + * @} + */ + +#include +#include +#include + +#include "periph/pm.h" +#include "host/ble_hs.h" +#include "host/util/util.h" +#include "host/ble_gatt.h" +#include "services/gap/ble_svc_gap.h" +#include "services/gatt/ble_svc_gatt.h" + +#include "main.h" + +#define STR_ANSWER_BUFFER_SIZE 100 + +static char str_answer[STR_ANSWER_BUFFER_SIZE]; + +static const char device_name[] = "Lord NimBLEer"; +static uint8_t own_addr_type; + +static void put_ad(uint8_t ad_type, uint8_t ad_len, const void *ad, uint8_t *buf, + uint8_t *len) +{ + buf[(*len)++] = ad_len + 1; + buf[(*len)++] = ad_type; + + memcpy(&buf[*len], ad, ad_len); + + *len += ad_len; +} + +static void update_ad(void) +{ + uint8_t ad[BLE_HS_ADV_MAX_SZ]; + uint8_t ad_len = 0; + uint8_t ad_flags = BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP; + + put_ad(BLE_HS_ADV_TYPE_FLAGS, 1, &ad_flags, ad, &ad_len); + put_ad(BLE_HS_ADV_TYPE_COMP_NAME, sizeof(device_name), device_name, ad, &ad_len); + + ble_gap_adv_set_data(ad, ad_len); +} + +static int gap_event_cb(struct ble_gap_event *event, void *arg) +{ + (void)arg; + + switch (event->type) { + case BLE_GAP_EVENT_CONNECT: + if (event->connect.status) { + start_advertise(); + } + break; + + case BLE_GAP_EVENT_DISCONNECT: + start_advertise(); + break; + } + + return 0; +} + +static void start_advertise(void) +{ + struct ble_gap_adv_params advp; + int rc; + + memset(&advp, 0, sizeof advp); + advp.conn_mode = BLE_GAP_CONN_MODE_UND; + advp.disc_mode = BLE_GAP_DISC_MODE_GEN; + rc = ble_gap_adv_start(own_addr_type, NULL, BLE_HS_FOREVER, + &advp, gap_event_cb, NULL); + + assert(rc == 0); + (void)rc; +} + +/* str needs to have size of at least 48 */ +void ble_uuid128_t_to_hex_str(char* str, const ble_uuid128_t *uuid) +{ + + str[0]= '\0'; + char hex[3]; + + for (int i = 15; i >= 0; i--) { + snprintf(hex, 3, "%02x", uuid->value[i]); + strcat(str, hex); + + if (i != 0) { + strcat(str, "-"); + } + } +} + +static int gatt_svr_chr_access_device_info_manufacturer( + uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, void *arg) +{ + puts("service 'device info: manufacturer' callback triggered"); + + (void) conn_handle; + (void) attr_handle; + (void) arg; + + snprintf(str_answer, STR_ANSWER_BUFFER_SIZE, + "This is RIOT! (Version: %s)\n", RIOT_VERSION); + puts(str_answer); + + int rc = os_mbuf_append(ctxt->om, str_answer, strlen(str_answer)); + + puts(""); + + return rc; +} + +static int gatt_svr_chr_access_device_info_model( + uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, void *arg) +{ + puts("service 'device info: model' callback triggered"); + + (void) conn_handle; + (void) attr_handle; + (void) arg; + + snprintf(str_answer, STR_ANSWER_BUFFER_SIZE, + "You are running RIOT on a(n) %s board, " + "which features a(n) %s MCU.", RIOT_BOARD, RIOT_MCU); + puts(str_answer); + + int rc = os_mbuf_append(ctxt->om, str_answer, strlen(str_answer)); + + puts(""); + + return rc; +} + +static int gatt_svr_chr_access_rw_demo( + uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, void *arg) +{ + puts("service 'rw demo' callback triggered"); + + (void) conn_handle; + (void) attr_handle; + (void) arg; + + int rc = 0; + + const ble_uuid_t* accessed_uuid = ctxt->chr->uuid; + + int cmp = ble_uuid_cmp(accessed_uuid, + (ble_uuid_t*) &gatt_svr_chr_cfg_restart_uuid.u); + if (cmp == 0) { + if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) { + puts("store config"); + config_store(&cfg_flashpage); + + puts("restart"); + pm_reboot(); + } + + return 0; + } + + int config_item_count = sizeof(cfg_flashpage_access) / sizeof(cfg_flashpage_access[0]); + + struct uuid_mem_mapper *mapper; + char uuid_str[48]; + for (int i = 0; i < config_item_count; i++) { + + mapper = &cfg_flashpage_access[i]; + + if (ble_uuid_cmp((ble_uuid_t*) mapper->uuid, accessed_uuid) == 0) { + + ble_uuid128_t_to_hex_str(uuid_str, mapper->uuid); + + switch (ctxt->op) { + + case BLE_GATT_ACCESS_OP_READ_CHR: + + /* be safe in case flash still contains garbage data */ + mapper->mem_p[mapper->mem_len - 1] = '\0'; + + printf("current value for uuid '%s': '%s'\n", + uuid_str, + (char*) mapper->mem_p); + + /* send given data to the client */ + rc = os_mbuf_append(ctxt->om, + mapper->mem_p, strlen((char*) mapper->mem_p)); + + break; + + case BLE_GATT_ACCESS_OP_WRITE_CHR: + + printf("old value for uuid '%s': '%s'\n", + uuid_str, + (char*) mapper->mem_p); + + uint16_t om_len; + om_len = OS_MBUF_PKTLEN(ctxt->om); + + /* read sent data */ + rc = ble_hs_mbuf_to_flat(ctxt->om, mapper->mem_p, + mapper->mem_len, &om_len); + /* we need to null-terminate the received string */ + mapper->mem_p[om_len] = '\0'; + + printf("new value for uuid '%s': '%s'\n", + uuid_str, + (char*) mapper->mem_p); + + break; + + default: + puts("unhandled operation!"); + rc = 1; + break; + } + + return rc; + } + } + + puts("unhandled uuid!"); + return 1; +} + +void init_default_config(void) +{ + + memcpy(cfg_flashpage.config_values.device_room, + CONFIG_DEVICE_ROOM, + sizeof(cfg_flashpage.config_values.device_room) + ); + + memcpy(cfg_flashpage.config_values.device_id, + CONFIG_DEVICE_ID, + sizeof(cfg_flashpage.config_values.device_id) + ); + + memcpy(cfg_flashpage.config_values.ota_host, + CONFIG_OTA_HOST, + sizeof(cfg_flashpage.config_values.ota_host) + ); + + memcpy(cfg_flashpage.config_values.ota_filename, + CONFIG_OTA_FILENAME, + sizeof(cfg_flashpage.config_values.ota_filename) + ); + + memcpy(cfg_flashpage.config_values.ota_server_username, + CONFIG_OTA_SERVER_USERNAME, + sizeof(cfg_flashpage.config_values.ota_server_username) + ); + + memcpy(cfg_flashpage.config_values.ota_server_password, + CONFIG_OTA_SERVER_PASSWORD, + sizeof(cfg_flashpage.config_values.ota_server_password) + ); + + memcpy(cfg_flashpage.config_values.wifi_ssid, + CONFIG_ESP_WIFI_SSID, + sizeof(cfg_flashpage.config_values.wifi_ssid) + ); + + memcpy(cfg_flashpage.config_values.wifi_password, + CONFIG_ESP_WIFI_PASSWORD, + sizeof(cfg_flashpage.config_values.wifi_password) + ); + + memcpy(cfg_flashpage.config_values.mqtt_user, + CONFIG_MQTT_USER, + sizeof(cfg_flashpage.config_values.mqtt_user) + ); + + memcpy(cfg_flashpage.config_values.mqtt_password, + CONFIG_MQTT_PASSWORD, + sizeof(cfg_flashpage.config_values.mqtt_password) + ); + + memcpy(cfg_flashpage.config_values.mqtt_server_ip, + CONFIG_MQTT_SERVER_IP, + sizeof(cfg_flashpage.config_values.mqtt_server_ip) + ); + + memcpy(cfg_flashpage.config_values.mqtt_server_port, + CONFIG_MQTT_SERVER_PORT, + sizeof(cfg_flashpage.config_values.mqtt_server_port) + ); + + memcpy(cfg_flashpage.config_values.sensor_poll_interval_ms, + CONFIG_SENSOR_POLL_INTERVAL_MS, + sizeof(cfg_flashpage.config_values.sensor_poll_interval_ms) + ); + + memcpy(cfg_flashpage.config_values.sensor_poll_interval_ms, + CONFIG_SENSOR_POLL_INTERVAL_MS, + sizeof(cfg_flashpage.config_values.sensor_poll_interval_ms) + ); +} + +int main(void) +{ + puts("Configurator Application"); + + config_read(&cfg_flashpage); + + /* + * if device_id is an empty string, the config flash page was probably + * not initialized yet, because it is the first run of the application + */ + uint8_t first_byte = cfg_flashpage.config_values.device_id[0]; + + /* check if first ascii is within range of allowed characters */ + if (first_byte >= 32 && first_byte <= 126) { + puts("use stored config from flash"); + } + else { + puts("use defaults as the flash seems to be empty " + "(first start -> config was never stored)"); + + init_default_config(); + } + + int rc = 0; + + rc = ble_gatts_count_cfg(gatt_svr_svcs); + assert(rc == 0); + + rc = ble_gatts_add_svcs(gatt_svr_svcs); + assert(rc == 0); + + /* set the device name */ + ble_svc_gap_device_name_set(device_name); + + /* initialize the GAP and GATT services */ + ble_svc_gap_init(); + ble_svc_gatt_init(); + /* XXX: seems to be needed to apply the added services */ + ble_gatts_start(); + + /* make sure synchronization of host and controller is done, this should + * always be the case */ + while (!ble_hs_synced()) {} + + /* configure device address */ + rc = ble_hs_util_ensure_addr(0); + assert(rc == 0); + rc = ble_hs_id_infer_auto(0, &own_addr_type); + assert(rc == 0); + (void)rc; + + /* generate the advertising data */ + update_ad(); + + /* start to advertise this node */ + puts("start advertising"); + start_advertise(); + + return 0; +} diff --git a/configurator/main.h b/configurator/main.h new file mode 100644 index 0000000..1b10f1f --- /dev/null +++ b/configurator/main.h @@ -0,0 +1,109 @@ +#ifndef MAIN_H +#define MAIN_H + +#include "ble_service.h" +#include "config.h" + +union config_flashpage cfg_flashpage = {}; + +struct uuid_mem_mapper { + const ble_uuid128_t* uuid; + uint8_t* mem_p; + size_t mem_len; +}; + +struct uuid_mem_mapper cfg_flashpage_access[] = { + { + &gatt_svr_chr_cfg_device_room_uuid, + cfg_flashpage.config_values.device_room, + sizeof(cfg_flashpage.config_values.device_room) + }, + { + &gatt_svr_chr_cfg_device_id_uuid, + cfg_flashpage.config_values.device_id, + sizeof(cfg_flashpage.config_values.device_id) + }, + { + &gatt_svr_chr_cfg_ota_host_uuid, + cfg_flashpage.config_values.ota_host, + sizeof(cfg_flashpage.config_values.ota_host) + }, + { + &gatt_svr_chr_cfg_ota_filename_uuid, + cfg_flashpage.config_values.ota_filename, + sizeof(cfg_flashpage.config_values.ota_filename) + }, + { + &gatt_svr_chr_cfg_ota_server_username_uuid, + cfg_flashpage.config_values.ota_server_username, + sizeof(cfg_flashpage.config_values.ota_server_username) + }, + { + &gatt_svr_chr_cfg_ota_server_password_uuid, + cfg_flashpage.config_values.ota_server_password, + sizeof(cfg_flashpage.config_values.ota_server_password) + }, + { + &gatt_svr_chr_cfg_wifi_ssid_uuid, + cfg_flashpage.config_values.wifi_ssid, + sizeof(cfg_flashpage.config_values.wifi_ssid) + }, + { + &gatt_svr_chr_cfg_wifi_password_uuid, + cfg_flashpage.config_values.wifi_password, + sizeof(cfg_flashpage.config_values.wifi_password) + }, + { + &gatt_svr_chr_cfg_mqtt_user_uuid, + cfg_flashpage.config_values.mqtt_user, + sizeof(cfg_flashpage.config_values.mqtt_user) + }, + { + &gatt_svr_chr_cfg_mqtt_password_uuid, + cfg_flashpage.config_values.mqtt_password, + sizeof(cfg_flashpage.config_values.mqtt_password) + }, + { + &gatt_svr_chr_cfg_mqtt_server_ip_uuid, + cfg_flashpage.config_values.mqtt_server_ip, + sizeof(cfg_flashpage.config_values.mqtt_server_ip) + }, + { + &gatt_svr_chr_cfg_mqtt_server_port_uuid, + cfg_flashpage.config_values.mqtt_server_port, + sizeof(cfg_flashpage.config_values.mqtt_server_port) + }, + { + &gatt_svr_chr_cfg_sensor_poll_interval_ms_uuid, + cfg_flashpage.config_values.sensor_poll_interval_ms, + sizeof(cfg_flashpage.config_values.sensor_poll_interval_ms) + }, +}; + +static void put_ad(uint8_t ad_type, uint8_t ad_len, const void *ad, uint8_t *buf, + uint8_t *len); + +static void update_ad(void); + +static int gap_event_cb(struct ble_gap_event *event, void *arg); + +static void start_advertise(void); + +/* str needs to have size of at least 48 */ +void ble_uuid128_t_to_hex_str(char* str, const ble_uuid128_t *uuid); + +static int gatt_svr_chr_access_device_info_manufacturer( + uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, void *arg); + +static int gatt_svr_chr_access_device_info_model( + uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, void *arg); + +static int gatt_svr_chr_access_rw_demo( + uint16_t conn_handle, uint16_t attr_handle, + struct ble_gatt_access_ctxt *ctxt, void *arg); + +void init_default_config(void); + +#endif /*MAIN_H*/