From a712e306b9ad83f32ff70beabb1dd222ab2f0b9f Mon Sep 17 00:00:00 2001 From: Liam Wickins Date: Mon, 2 Nov 2020 16:34:21 +0000 Subject: [PATCH] d7ap_fs: use D7AP stack logging rather than NWL. Add missing logging enable cmake option. phy: some defensive programming changes to prevent possible divide by zero and stack overrun. serial_interface: if PLATFORM_MODEM_INTERFACE_UART is not defined then don't try to register a serial interface. alp_layer: initialise with a default D7 interface. alp_layer: add support for application layer to accept write file operations in the same manner as read file operations. Useful if we want to bypass fs completely. log: added new stack logging options to give improved trace read out. log: added timestamps to logging output. d7ap: these structs are used for constructing payloads in the stack and rely on byte alignment of members. platforms/native: now supports emulated uart, timer, radio and buttons. hwradio: extended API for platform/native implementation only. console: new platform/native console option added. apps/sensor_pull: bug fix as fs layer is not initialised before initialising the stack. platforms/B_L072Z_LRWAN1: add support for USART4 which is excellent for TX pini only debug logging as an alt. to the JLink output. platforms/B_L072Z_LRWAN1: add support for including GPS chip into platform build. chip/neo_m8n_gps: new chip driver support (using UART) for the M8N GPS receiver. apps/gateway_gps: new variant GPS gateway for testing GPS enabled sensor. apps/sensor_gps: new GPS enabled sensor application. --- stack/apps/gateway_gps/CMakeLists.txt | 37 ++ stack/apps/gateway_gps/app.c | 252 ++++++++ stack/apps/sensor_gps/CMakeLists.txt | 32 + stack/apps/sensor_gps/app.c | 243 +++++++ stack/apps/sensor_pull/sensor_pull.c | 9 +- .../components/console/CMakeLists.txt | 6 +- .../components/console/console_native.c | 34 + stack/framework/components/log/log.c | 30 +- .../hal/chips/neo_m8n_gps/CMakeLists.txt | 35 + stack/framework/hal/chips/neo_m8n_gps/gps.c | 224 +++++++ stack/framework/hal/chips/neo_m8n_gps/gps.h | 54 ++ stack/framework/hal/chips/neo_m8n_gps/ubx.h | 200 ++++++ stack/framework/hal/inc/hwradio.h | 4 + .../platforms/B_L072Z_LRWAN1/CMakeLists.txt | 18 +- .../hal/platforms/B_L072Z_LRWAN1/inc/ports.h | 12 +- .../hal/platforms/NATIVE/CMakeLists.txt | 56 +- .../framework/hal/platforms/NATIVE/README.md | 43 ++ .../hal/platforms/NATIVE/inc/button.h | 75 +++ .../hal/platforms/NATIVE/platf_button.c | 106 +++ .../hal/platforms/NATIVE/platf_main.c | 49 +- .../hal/platforms/NATIVE/platf_radio.c | 610 ++++++++++++++++++ .../hal/platforms/NATIVE/platf_timer.c | 173 +++++ .../hal/platforms/NATIVE/platf_uart.c | 163 +++++ stack/framework/inc/alp_layer.h | 2 + stack/framework/inc/d7ap.h | 14 +- stack/framework/inc/log.h | 3 + stack/modules/alp/alp_layer.c | 15 +- stack/modules/alp/serial_interface.c | 2 + stack/modules/d7ap/phy.c | 48 +- stack/modules/d7ap_fs/CMakeLists.txt | 3 + stack/modules/d7ap_fs/d7ap_fs.c | 4 +- 31 files changed, 2498 insertions(+), 58 deletions(-) create mode 100644 stack/apps/gateway_gps/CMakeLists.txt create mode 100644 stack/apps/gateway_gps/app.c create mode 100644 stack/apps/sensor_gps/CMakeLists.txt create mode 100644 stack/apps/sensor_gps/app.c create mode 100644 stack/framework/components/console/console_native.c create mode 100644 stack/framework/hal/chips/neo_m8n_gps/CMakeLists.txt create mode 100644 stack/framework/hal/chips/neo_m8n_gps/gps.c create mode 100644 stack/framework/hal/chips/neo_m8n_gps/gps.h create mode 100644 stack/framework/hal/chips/neo_m8n_gps/ubx.h create mode 100644 stack/framework/hal/platforms/NATIVE/README.md create mode 100644 stack/framework/hal/platforms/NATIVE/inc/button.h create mode 100644 stack/framework/hal/platforms/NATIVE/platf_button.c create mode 100644 stack/framework/hal/platforms/NATIVE/platf_radio.c create mode 100644 stack/framework/hal/platforms/NATIVE/platf_timer.c create mode 100644 stack/framework/hal/platforms/NATIVE/platf_uart.c diff --git a/stack/apps/gateway_gps/CMakeLists.txt b/stack/apps/gateway_gps/CMakeLists.txt new file mode 100644 index 000000000..7fee991da --- /dev/null +++ b/stack/apps/gateway_gps/CMakeLists.txt @@ -0,0 +1,37 @@ +# +# OSS-7 - An opensource implementation of the DASH7 Alliance Protocol for ultra +# lowpower wireless sensor communication +# +# Copyright 2015 University of Antwerp +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +#See the explanation of APP_OPTION and APP_PARAM in cmake/app_macros.cmake +#for details on how to add application-specific CMake GUI entries + +#By convention, application parameters should be prefixed with '${APP_PREFIX}' +#Some examples: +#APP_OPTION(${APP_PREFIX}_ "Option explanation" ) +#APP_PARAM(${APP_PREFIX}_ "" "Parameter explanation") +# +#Cache properties can be set on application parameters just like on regular cache parameters +#SET_PROPERTY(CACHE ${APP_PREFIX}_ PROPERTY STRINGS "value1;value2") +# +IF(${MODULE_LORAWAN}) + SET(libs alp d7ap d7ap_fs lorawan framework) +ELSE() + SET(libs alp d7ap d7ap_fs framework) +ENDIF() + +APP_BUILD(NAME ${APP_NAME} SOURCES app.c LIBS ${libs}) diff --git a/stack/apps/gateway_gps/app.c b/stack/apps/gateway_gps/app.c new file mode 100644 index 000000000..c6136a8a5 --- /dev/null +++ b/stack/apps/gateway_gps/app.c @@ -0,0 +1,252 @@ +/* * OSS-7 - An opensource implementation of the DASH7 Alliance Protocol for ultra + * lowpower wireless sensor communication + * + * Copyright 2015 University of Antwerp + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * \author Liam Wickins + */ + +#include +#include +#include + +#include "hwuart.h" +#include "hwleds.h" +#include "hwsystem.h" +#include "hwlcd.h" + +#include "scheduler.h" +#include "timer.h" +#include "log.h" +#include "debug.h" +#include "d7ap_fs.h" +#include "fifo.h" +#include "version.h" +#include "compress.h" + +#include "platform_defs.h" + +#include "d7ap.h" +#include "alp_layer.h" +#include "dae.h" +#include "modem_interface.h" +#include "platform.h" + +#ifdef PLATFORM_USE_NEO_M8N_GPS +#include "gps.h" +#endif + + +#define GPS_POS_FILE_ID 0x40 +#define GPS_ENABLE_FILE_ID 0x41 +#define GPS_DISABLE_FILE_ID 0x42 + + +// LEDs used to indicate device status +#define LED_COMMAND_PENDING 0 +#define LED_COMMAND_RECEIVED 0 +#define LED_GPS_TRACKING 2 +#define LED_GPS_FIXED 3 + + +static alp_interface_config_d7ap_t itf_config = (alp_interface_config_d7ap_t){ + .itf_id = ALP_ITF_ID_D7ASP, + .d7ap_session_config = { + .qos = { + .qos_resp_mode = SESSION_RESP_MODE_ANY, + .qos_retry_mode = SESSION_RETRY_MODE_NO + }, + .dormant_timeout = 0, + .addressee = { + .ctrl = { + .nls_method = AES_NONE, + .id_type = ID_TYPE_NBID, + }, + .access_class = 0x11, + .id = { } + } + } +}; + +static bool gps_remote_enable = false; + + +#if PLATFORM_NUM_BUTTONS > 0 + +#include "button.h" + +#endif + +#if PLATFORM_NUM_LEDS > 0 + #include "hwleds.h" + + void led_blink_off() + { + led_off(LED_COMMAND_RECEIVED); + } + + void led_blink() + { + led_on(LED_COMMAND_RECEIVED); + + timer_post_task_delay(&led_blink_off, TIMER_TICKS_PER_SEC * 0.2); + } +#endif + + +void send_task() +{ + uint8_t file_id; + alp_command_t *cmd; + cmd = alp_layer_command_alloc(false, false); + alp_append_forward_action(cmd, (alp_interface_config_t*)&itf_config, sizeof(itf_config)); + file_id = gps_remote_enable ? GPS_DISABLE_FILE_ID : GPS_ENABLE_FILE_ID; + alp_append_write_file_data_action(cmd, file_id, 0, 0, NULL, false, false); + alp_layer_process(cmd); +} + +static error_t host_interface_send(uint8_t* payload, uint8_t payload_length, + __attribute__((__unused__)) uint8_t expected_response_length, + __attribute__((__unused__)) uint16_t* trans_id, + __attribute__((__unused__)) alp_interface_config_t* session_config) +{ +#if PLATFORM_NUM_LEDS > 0 + led_blink(); +#endif + printf("\nD7ALP message opcode=%02x file_id=%02x offset=%u len=%u\n", payload[0], payload[1], payload[2], payload[3]); + +#ifdef PLATFORM_USE_NEO_M8N_GPS + if (payload[0] == ALP_OP_RETURN_FILE_DATA) + { + if (payload[1] == GPS_POS_FILE_ID && payload[3] >= sizeof(gps_event_t)) + { + gps_event_t *event = (gps_event_t *)&payload[4]; + printf("\nGPS_POS: itow=%u fixed=%u ttff=%u lon=%d lat=%d\n", event->pos.itow, event->pos.is_fixed, event->pos.ttff, event->pos.longitude, event->pos.latitude); +#if PLATFORM_NUM_LEDS > 0 + led_on(LED_GPS_TRACKING); + if (event->pos.is_fixed) + led_on(LED_GPS_FIXED); + else + led_off(LED_GPS_FIXED); +#endif + } + } +#endif + return 0; +} + +void host_interface_register() { + static alp_interface_t alp_host_interface = { + .itf_id = ALP_ITF_ID_HOST, + .itf_cfg_len = 0, + .itf_status_len = 0, + .send_command = host_interface_send, + .init = NULL, + .deinit = NULL, + .unique = false + }; + + alp_layer_register_interface(&alp_host_interface); +} + +#if PLATFORM_NUM_BUTTONS > 0 + +static void button_callback(button_id_t button_id) +{ + printf("\nButton pressed - request %u\n", !gps_remote_enable); + send_task(); +} +#endif + +void on_alp_command_completed_cb(uint8_t tag_id, bool success) +{ + if (success) + { + printf("\nCommand (%i) completed successfully\n", tag_id); + gps_remote_enable = !gps_remote_enable; + + if (gps_remote_enable) + { +#if PLATFORM_NUM_LEDS > 0 + led_on(LED_GPS_TRACKING); +#endif + } + else + { +#if PLATFORM_NUM_LEDS > 0 + led_off(LED_GPS_TRACKING); + led_off(LED_GPS_FIXED); +#endif + } + } + else + { + printf("\nCommand failed, no ack received\n"); + } +#if PLATFORM_NUM_LEDS > 0 + led_off(LED_COMMAND_PENDING); +#endif + +#if PLATFORM_NUM_BUTTONS == 0 + timer_post_task_delay(&send_task, TIMER_TICKS_PER_SEC * 1); +#endif + +} + +static alp_init_args_t alp_init_args = { + .alp_command_completed_cb = on_alp_command_completed_cb, +}; + + +void bootstrap() +{ + printf("\nDevice booted\n"); + +#if PLATFORM_NUM_LEDS > 0 + led_off(LED_COMMAND_PENDING); + led_off(LED_COMMAND_RECEIVED); + led_off(LED_GPS_TRACKING); + led_off(LED_GPS_FIXED); +#endif + +#if PLATFORM_NUM_BUTTONS > 0 + ubutton_register_callback(0, button_callback); +#endif + + d7ap_fs_init(); + d7ap_init(); + + d7ap_fs_write_dll_conf_active_access_class(0x01); // set to first AC, which is continuous FG scan + + host_interface_register(); + + alp_layer_init(&alp_init_args, false); + +#ifdef HAS_LCD + lcd_write_string("GW %s", _GIT_SHA1); +#endif + +#if PLATFORM_NUM_LEDS > 0 + sched_register_task(&led_blink_off); +#endif + +#if PLATFORM_NUM_BUTTONS == 0 + sched_register_task(&send_task); + timer_post_task_delay(&send_task, TIMER_TICKS_PER_SEC * 0); +#endif +} + diff --git a/stack/apps/sensor_gps/CMakeLists.txt b/stack/apps/sensor_gps/CMakeLists.txt new file mode 100644 index 000000000..bcf845a46 --- /dev/null +++ b/stack/apps/sensor_gps/CMakeLists.txt @@ -0,0 +1,32 @@ +# +# OSS-7 - An opensource implementation of the DASH7 Alliance Protocol for ultra +# lowpower wireless sensor communication +# +# Copyright 2015 University of Antwerp +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +#See the explanation of APP_OPTION and APP_PARAM in cmake/app_macros.cmake +#for details on how to add application-specific CMake GUI entries + +#By convention, application parameters should be prefixed with '${APP_PREFIX}' +#Some examples: +#APP_OPTION(${APP_PREFIX}_ "Option explanation" ) +#APP_PARAM(${APP_PREFIX}_ "" "Parameter explanation") +# +#Cache properties can be set on application parameters just like on regular cache parameters +#SET_PROPERTY(CACHE ${APP_PREFIX}_ PROPERTY STRINGS "value1;value2") +# + +APP_BUILD(NAME ${APP_NAME} SOURCES app.c LIBS alp d7ap d7ap_fs framework) diff --git a/stack/apps/sensor_gps/app.c b/stack/apps/sensor_gps/app.c new file mode 100644 index 000000000..55bafb8c1 --- /dev/null +++ b/stack/apps/sensor_gps/app.c @@ -0,0 +1,243 @@ +/* * OSS-7 - An opensource implementation of the DASH7 Alliance Protocol for ultra + * lowpower wireless sensor communication using Neo M8N GPS + * + * Copyright 2015 University of Antwerp + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * \author Liam Wickins + * + */ + + +#include +#include + +#include "hwleds.h" +#include "hwsystem.h" +#include "hwlcd.h" + +#include "scheduler.h" +#include "timer.h" +#include "debug.h" +#include "d7ap_fs.h" +#include "log.h" +#include "compress.h" + +#include "d7ap.h" +#include "alp_layer.h" +#include "dae.h" +#include "platform_defs.h" +#include "modules_defs.h" +#include "platform.h" + +#ifdef PLATFORM_USE_NEO_M8N_GPS +#include "gps.h" +#endif + +#ifdef MODULE_LORAWAN + #error "sensor_pull app is not compatible with LoRaWAN, so disable MODULE_LORAWAN in cmake" +#endif + +//#if !defined(USE_SX127X) && !defined(USE_NETDEV_DRIVER) +// #error "background frames are only supported by the sx127x driver for now" +//#endif + + +#define GPS_POS_FILE_ID 0x40 +#define GPS_ENABLE_FILE_ID 0x41 +#define GPS_DISABLE_FILE_ID 0x42 +#define GPS_REPORTING_PERIOD_FILE_ID 0x43 + + +// LEDs used to indicate device status +#define LED_COMMAND_PENDING 0 +#define LED_COMMAND_RECEIVED 1 +#define LED_GPS_TRACKING 2 +#define LED_GPS_FIXED 3 + + +static uint32_t gps_reporting_period = 10; + + +#if PLATFORM_NUM_LEDS > 0 + #include "hwleds.h" + + void led_blink_off() + { + led_off(LED_COMMAND_RECEIVED); + } + + void led_blink() + { + led_on(LED_COMMAND_RECEIVED); + + timer_post_task_delay(&led_blink_off, TIMER_TICKS_PER_SEC * 1.0); + } +#endif + + +static alp_interface_config_d7ap_t itf_config = (alp_interface_config_d7ap_t){ + .itf_id = ALP_ITF_ID_D7ASP, + .d7ap_session_config = { + .qos = { + .qos_resp_mode = SESSION_RESP_MODE_ANY, + .qos_retry_mode = SESSION_RETRY_MODE_NO + }, + .dormant_timeout = 0, + .addressee = { + .ctrl = { + .nls_method = AES_NONE, + .id_type = ID_TYPE_NBID, + }, + .access_class = 0x01, + .id = { } + } + } +}; + + +void on_alp_command_completed_cb(uint8_t tag_id, bool success) +{ + if(success) + printf("\nCommand (%i) completed successfully\n", tag_id); + else + printf("\nCommand failed, no ack received\n"); +#if PLATFORM_NUM_LEDS > 0 + led_off(LED_COMMAND_PENDING); +#endif +} + +void on_alp_command_result_cb(alp_command_t *alp_command, alp_interface_status_t* origin_itf_status) +{ + if(origin_itf_status && (origin_itf_status->itf_id == ALP_ITF_ID_D7ASP) && (origin_itf_status->len > 0)) { + d7ap_session_result_t* d7_result = ((d7ap_session_result_t*)origin_itf_status->itf_status); + printf("\nrecv response @ %i dB link budget\n", d7_result->rx_level); + } + printf("\nresponse payload length: %u\n", fifo_get_size(&alp_command->alp_command_fifo)); + fifo_skip(&alp_command->alp_command_fifo, fifo_get_size(&alp_command->alp_command_fifo)); +} + +static alp_status_codes_t alp_unhandled_read_action_cb(const alp_interface_status_t* origin_itf_status, alp_operand_file_data_request_t operand, uint8_t* alp_response) +{ + printf("\nalp_unhandled_read_action_cb file_id=%02x offset=%u length=%u\n", operand.file_offset.file_id, operand.file_offset.offset, operand.requested_data_length); + return ALP_STATUS_FILE_ID_NOT_EXISTS; +} + +static alp_status_codes_t alp_unhandled_write_action_cb(const alp_interface_status_t* origin_itf_status, alp_operand_file_data_t *operand) +{ +#if PLATFORM_NUM_LEDS > 0 + led_blink(); +#endif + if (operand->file_offset.file_id == GPS_ENABLE_FILE_ID) + { +#if PLATFORM_NUM_LEDS > 0 + led_on(LED_GPS_TRACKING); +#endif + printf("\ngps_enable\n"); +#ifdef PLATFORM_USE_NEO_M8N_GPS + gps_enable(); +#endif + } + else if (operand->file_offset.file_id == GPS_DISABLE_FILE_ID) + { +#if PLATFORM_NUM_LEDS > 0 + led_off(LED_GPS_TRACKING); + led_off(LED_GPS_FIXED); +#endif + printf("\ngps_disable\n"); +#ifdef PLATFORM_USE_NEO_M8N_GPS + gps_disable(); +#endif + } + else if (operand->file_offset.file_id == GPS_REPORTING_PERIOD_FILE_ID) + { + gps_reporting_period = *(uint32_t *)operand->data; +#ifdef PLATFORM_USE_NEO_M8N_GPS + printf("\ngps_reporting_period = %u\n", gps_reporting_period); +#endif + } + else + { + printf("\nfile_id not supported\n"); + return ALP_STATUS_FILE_ID_NOT_EXISTS; + } + return ALP_STATUS_OK; +} + +static alp_init_args_t alp_init_args = { + .alp_command_completed_cb = on_alp_command_completed_cb, + .alp_command_result_cb = on_alp_command_result_cb, + .alp_unhandled_read_action_cb = alp_unhandled_read_action_cb, + .alp_unhandled_write_action_cb = alp_unhandled_write_action_cb +}; + + +#ifdef PLATFORM_USE_NEO_M8N_GPS + +void gps_event_handler(gps_event_t *event) +{ + static unsigned int cnt = 0; + +#if PLATFORM_NUM_LEDS > 0 + if (event->pos.is_fixed) + led_on(LED_GPS_FIXED); + else + led_off(LED_GPS_FIXED); +#endif + + if ((cnt++ % gps_reporting_period) == 0) + { + printf("\n*GPS_POS itow=%u fixed=%u ttff=%u lon=%d lat=%d\n", event->pos.itow, event->pos.is_fixed, event->pos.ttff, event->pos.longitude, event->pos.latitude); + alp_command_t *cmd; + cmd = alp_layer_command_alloc(false, false); + alp_append_forward_action(cmd, (alp_interface_config_t*)&itf_config, sizeof(itf_config)); + alp_append_return_file_data_action(cmd, GPS_POS_FILE_ID, 0, sizeof(gps_event_t), (uint8_t *)event); + alp_layer_process(cmd); +#if PLATFORM_NUM_LEDS > 0 + led_on(LED_COMMAND_PENDING); +#endif + } + else + printf("\nGPS_POS itow=%u fixed=%u ttff=%u lon=%d lat=%d\n", event->pos.itow, event->pos.is_fixed, event->pos.ttff, event->pos.longitude, event->pos.latitude); + +} +#endif + +void bootstrap() +{ + printf("\nDevice booted\n"); + +#if PLATFORM_NUM_LEDS > 0 + led_off(LED_COMMAND_PENDING); + led_off(LED_COMMAND_RECEIVED); + led_off(LED_GPS_TRACKING); + led_off(LED_GPS_FIXED); +#endif + + d7ap_fs_init(); + d7ap_init(); + + alp_layer_init(&alp_init_args, false); + d7ap_fs_write_dll_conf_active_access_class(0x11); // use scanning AC + +#ifdef PLATFORM_USE_NEO_M8N_GPS + gps_init(gps_event_handler); + gps_enable(); + gps_disable(); +#endif + +#if PLATFORM_NUM_LEDS > 0 + sched_register_task(&led_blink_off); +#endif +} diff --git a/stack/apps/sensor_pull/sensor_pull.c b/stack/apps/sensor_pull/sensor_pull.c index 6a0794f54..e17b96bd2 100644 --- a/stack/apps/sensor_pull/sensor_pull.c +++ b/stack/apps/sensor_pull/sensor_pull.c @@ -53,8 +53,8 @@ #error "sensor_pull app is not compatible with LoRaWAN, so disable MODULE_LORAWAN in cmake" #endif -#if !defined(USE_SX127X) && !defined(USE_NETDEV_DRIVER) - #error "background frames are only supported by the sx127x driver for now" +#if !defined(USE_SX127X) && !defined(USE_NETDEV_DRIVER) && !defined(USE_DUMMY_RADIO) + #error "background frames are only supported by the sx127x or dummy driver for now" #endif @@ -99,10 +99,13 @@ void bootstrap() { log_print_string("Device booted\n"); + d7ap_fs_init(); + d7ap_init(); alp_layer_init(NULL, false); d7ap_fs_write_dll_conf_active_access_class(0x11); // use scanning AC + init_user_files(); #if defined USE_HTS221 @@ -114,5 +117,5 @@ void bootstrap() #endif sched_register_task(&execute_sensor_measurement); - timer_post_task_delay(&execute_sensor_measurement, SENSOR_INTERVAL_SEC); + timer_post_task_delay(&execute_sensor_measurement, 0); } diff --git a/stack/framework/components/console/CMakeLists.txt b/stack/framework/components/console/CMakeLists.txt index 63b091d33..f5f746826 100644 --- a/stack/framework/components/console/CMakeLists.txt +++ b/stack/framework/components/console/CMakeLists.txt @@ -19,4 +19,8 @@ #Each Framework component must generate a single OBJECT library named #'${COMPONENT_LIBRARY_NAME}' -ADD_LIBRARY(${COMPONENT_LIBRARY_NAME} OBJECT console.c) +IF("${PLATFORM}" STREQUAL "NATIVE") + ADD_LIBRARY(${COMPONENT_LIBRARY_NAME} OBJECT console_native.c) +ELSE() + ADD_LIBRARY(${COMPONENT_LIBRARY_NAME} OBJECT console.c) +ENDIF() diff --git a/stack/framework/components/console/console_native.c b/stack/framework/components/console/console_native.c new file mode 100644 index 000000000..2119ef28a --- /dev/null +++ b/stack/framework/components/console/console_native.c @@ -0,0 +1,34 @@ +#include +#include + +#include "framework_defs.h" + +#ifdef FRAMEWORK_CONSOLE_ENABLED + +#include "platform_defs.h" +#include "console.h" +#include "debug.h" + +void console_init(void) { +} + +void console_enable(void) { +} + +void console_disable(void) { +} + +inline void console_print_byte(uint8_t byte) { + putc(byte); +} + +inline void console_print_bytes(uint8_t* bytes, uint8_t length) { + while (length--) + console_print_byte(*bytes++); +} + +inline void console_print(char* string) { + console_print_bytes((uint8_t*) string, strlen(string)); +} + +#endif diff --git a/stack/framework/components/log/log.c b/stack/framework/components/log/log.c index 615e154a5..bcda53143 100644 --- a/stack/framework/components/log/log.c +++ b/stack/framework/components/log/log.c @@ -33,9 +33,31 @@ #include "framework_defs.h" #include "hwsystem.h" #include "SEGGER_RTT.h" +#include "timer.h" #ifdef FRAMEWORK_LOG_ENABLED +static char stack_layer_str[19][4] = +{ + "APP", + "PHY", + "DLL", + "MAC", + "NWL", + "TRN", + "SES", + "D7A", + "ALP", + "RAD", + "TIM", + "---", + "---", + "---", + "---", + "---", + "FWK", + "EM " +}; static uint32_t NGDEF(counter); @@ -49,7 +71,7 @@ __LINK_C void log_print_string(char* format, ...) { va_list args; va_start(args, format); - printf("\n\r[%03d] ", NG(counter)++); + printf("\n\r[%u] [%03d] ", timer_get_counter_value(), NG(counter)++ ); vprintf(format, args); va_end(args); } @@ -58,14 +80,14 @@ __LINK_C void log_print_stack_string(log_stack_layer_t type, char* format, ...) { va_list args; va_start(args, format); - printf("\n\r[%03d] ", NG(counter)++); + printf("\n\r[%u] [%03d] [%s] ", timer_get_counter_value(), NG(counter)++, stack_layer_str[type]); vprintf(format, args); va_end(args); } __LINK_C void log_print_data(uint8_t* message, uint32_t length) { - printf("\n\r[%03d]", NG(counter)++); + printf("\n\r[%u] [%03d]", timer_get_counter_value(), NG(counter)++); for( uint32_t i=0 ; i + * + */ +#include +#include +#include + +#include "hwuart.h" +#include "platform_defs.h" +#include "scheduler.h" +#include "timer.h" +#include "fifo.h" +#include "gps.h" +#include "ubx.h" +#include "log.h" + +#define GPS_INTERVAL_SEC (1 * TIMER_TICKS_PER_SEC) +#define GPS_BUFFER_SIZE 256 + + +// UBX messages are received into a FIFO area of memory and parsed periodically +// at a rate of GPS_INTERVAL_SEC. The parsing function will extract complete +// UBX packets once they have been fully received into the FIFO. +static uart_handle_t* uart; +static gps_callback_t gps_callback = NULL; +static bool is_gps_enabled = false; +static bool is_gps_fixed = false; +static fifo_t gps_fifo; +static uint8_t gps_fifo_buffer[GPS_BUFFER_SIZE]; +static uint32_t ttff; + + +void ubx_checksum(UBX_Packet_t *packet, uint8_t ck[2]) +{ + ck[0] = ck[1] = 0; + uint8_t *buffer = &packet->msgClass; + + /* Taken directly from Section 29 UBX Checksum of the u-blox8 + * receiver specification + */ + for (unsigned int i = 0; i < packet->msgLength + 4; i++) + { + ck[0] = ck[0] + buffer[i]; + ck[1] = ck[1] + ck[0]; + } +} + +void ubx_set_checksum(UBX_Packet_t *packet) +{ + uint8_t ck[2]; + + ubx_checksum(packet, ck); + packet->payloadAndCrc[packet->msgLength] = ck[0]; + packet->payloadAndCrc[packet->msgLength+1] = ck[1]; +} + +void ubx_send(UBX_Packet_t *packet) +{ + unsigned int total_length = UBX_HEADER_AND_CRC_LENGTH + packet->msgLength; + uart_send_bytes(uart, (uint8_t *)packet, total_length); +} + +int parse_gps_fifo(UBX_Packet_t *packet) +{ + uint8_t byte; + uint32_t occupancy; + int idx; + + occupancy = fifo_get_size(&gps_fifo); + + /* Check for minimum allowed message size */ + if (occupancy < UBX_HEADER_AND_CRC_LENGTH) + return -1; + + /* Find SYNC1 byte */ + for (idx = 0; idx <= (occupancy - UBX_HEADER_AND_CRC_LENGTH); idx++) + { + fifo_peek(&gps_fifo, &byte, idx, 1); + if (UBX_PACKET_SYNC_CHAR1 == byte) + goto ubx_sync_start; + } + + /* No SYNC1 so flush checked bytes so far... */ + fifo_skip(&gps_fifo, idx); + return -1; + +ubx_sync_start: + /* Check SYNC2 is present */ + fifo_peek(&gps_fifo, &byte, idx + 1, 1); + if (UBX_PACKET_SYNC_CHAR2 != byte) + { + fifo_skip(&gps_fifo, idx + 2); + return -1; /* Invalid SYNC2 */ + } + + /* Extract length field and check it is received fully */ + uint16_t payload_length; + fifo_peek(&gps_fifo, &byte, idx + 4, 1); + payload_length = byte; + fifo_peek(&gps_fifo, &byte, idx + 5, 1); + payload_length |= (uint16_t)byte << 8; + uint16_t total_length = payload_length + UBX_HEADER_AND_CRC_LENGTH; + if (total_length > GPS_BUFFER_SIZE) + { + /* Message is too big to store so throw it away */ + fifo_clear(&gps_fifo); + return -1; + } + + /* Check message is fully received */ + if (total_length > occupancy) + return -1; + + /* Copy message into packet structure */ + fifo_peek(&gps_fifo, (uint8_t *)packet, idx, total_length); + fifo_skip(&gps_fifo, idx + total_length); + + return 0; +} + + +void gps_process() +{ + if (is_gps_enabled) + { + UBX_Packet_t packet; + int ret; + do + { + ret = parse_gps_fifo(&packet); + if (ret == 0) + { + //printf("UBX=%02x-%02x ITOW=%u\n", packet.msgClass, packet.msgId, packet.UBX_NAV_STATUS.iTOW); + if (UBX_IS_MSG(packet, UBX_MSG_CLASS_NAV, UBX_MSG_ID_NAV_STATUS)) + { + is_gps_fixed = packet.UBX_NAV_STATUS.gpsFix; + ttff = packet.UBX_NAV_STATUS.ttff; + } + if (UBX_IS_MSG(packet, UBX_MSG_CLASS_NAV, UBX_MSG_ID_NAV_POSLLH) && gps_callback) + { + gps_event_t evt; + evt.id = GPS_EVENT_POS; + evt.pos.is_fixed = is_gps_fixed; + evt.pos.itow = packet.UBX_NAV_POSLLH.iTOW; + evt.pos.ttff = ttff; + evt.pos.height = packet.UBX_NAV_POSLLH.height; + evt.pos.longitude = packet.UBX_NAV_POSLLH.lon; + evt.pos.latitude = packet.UBX_NAV_POSLLH.lat; + gps_callback(&evt); + } + } + } while (ret == 0); + timer_post_task_delay(&gps_process, GPS_INTERVAL_SEC); + } +} + + +void gps_uart_rx_cb(uint8_t byte) +{ + fifo_put_byte(&gps_fifo, byte); +} + +void gps_init(gps_callback_t cb) +{ + uart = uart_init(PLATFORM_GPS_UART, PLATFORM_GPS_BAUDRATE, 0); + assert(uart_enable(uart)); + uart_set_rx_interrupt_callback(uart, gps_uart_rx_cb); + uart_rx_interrupt_enable(uart); + gps_callback = cb; + sched_register_task(&gps_process); + fifo_init(&gps_fifo, gps_fifo_buffer, GPS_BUFFER_SIZE); +} + +void gps_enable(void) +{ + if (is_gps_enabled) + return; + // Send a 4 byte message 00 00 00 00 to wakeup the module on its UART RX pin + // This assumes the device was powered down using UBX_RXM_PMREQ with UART + // wake-up source selected + uint8_t buf[4] = { 0 }; + uart_send_bytes(uart, buf, 4); + fifo_clear(&gps_fifo); + is_gps_enabled = true; + timer_post_task_delay(&gps_process, GPS_INTERVAL_SEC); +} + +void gps_disable(void) +{ + if (!is_gps_enabled) + return; + // Send UBX_RX_PMREQ to put the device into low power state and allow it to + // be woken up on its UART RX pin + UBX_Packet_t packet; + memset(&packet, 0, sizeof(packet)); + UBX_SET_PACKET_HEADER(packet, UBX_MSG_CLASS_RXM, UBX_MSG_ID_RXM_PMREQ, sizeof(packet.UBX_RXM_PMREQ)); + packet.UBX_RXM_PMREQ.version = 0; + packet.UBX_RXM_PMREQ.flags = 0x6; + packet.UBX_RXM_PMREQ.duration = 0; + packet.UBX_RXM_PMREQ.wakeupSources = UBX_RXM_PMREQ_WAKEUP_SOURCE_UART; + ubx_set_checksum(&packet); + ubx_send(&packet); + is_gps_enabled = false; +} diff --git a/stack/framework/hal/chips/neo_m8n_gps/gps.h b/stack/framework/hal/chips/neo_m8n_gps/gps.h new file mode 100644 index 000000000..f9a30cecf --- /dev/null +++ b/stack/framework/hal/chips/neo_m8n_gps/gps.h @@ -0,0 +1,54 @@ +/* * OSS-7 - An opensource implementation of the DASH7 Alliance Protocol for ultra + * lowpower wireless sensor communication + * + * Copyright 2015 University of Antwerp + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*! \file gps.h + * + * \author Liam Wickins + * + */ +#ifndef __NEO_M8N_GPS_H_ +#define __NEO_M8N_GPS_H_ + +typedef enum +{ + GPS_EVENT_POS +} gps_event_id_t; + +#pragma pack (1) +typedef struct +{ + gps_event_id_t id; + union { + struct { + uint32_t itow; + uint32_t longitude; + uint32_t latitude; + uint32_t height; + uint32_t ttff; + bool is_fixed; + } pos; + }; +} gps_event_t; + +typedef void (*gps_callback_t)(gps_event_t *event); + +void gps_init(gps_callback_t cb); +void gps_enable(void); +void gps_disable(void); + +#endif // __NEO_M8N_GPS_H_ diff --git a/stack/framework/hal/chips/neo_m8n_gps/ubx.h b/stack/framework/hal/chips/neo_m8n_gps/ubx.h new file mode 100644 index 000000000..a7a90ff38 --- /dev/null +++ b/stack/framework/hal/chips/neo_m8n_gps/ubx.h @@ -0,0 +1,200 @@ +/* * OSS-7 - An opensource implementation of the DASH7 Alliance Protocol for ultra + * lowpower wireless sensor communication + * + * Copyright 2015 University of Antwerp + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*! \file ubx.h + * + * \author Liam Wickins + * + */ + +#ifndef __NEO_M8N_UBX_H_ +#define __NEO_M8N_UBX_H_ + +#include + +#pragma pack (1) + +#define UBX_PACKET_SYNC_CHAR1 0xB5 +#define UBX_PACKET_SYNC_CHAR2 0x62 + +#define UBX_HEADER_AND_CRC_LENGTH 8 +#define UBX_HEADER_LENGTH 6 +#define UBX_CRC_LENGTH 2 + + +#define UBX_SET_PACKET_HEADER(p, cls, id, length) \ + p.syncChars[0] = UBX_PACKET_SYNC_CHAR1; \ + p.syncChars[1] = UBX_PACKET_SYNC_CHAR2; \ + p.msgClass = cls; \ + p.msgId = id; \ + p.msgLength = length + +#define UBX_IS_MSG(p, cls, id) (cls == p.msgClass && id == p.msgId) + + +#define UBX_PACKET_SIZE(p) UBX_HEADER_AND_CRC_LENGTH + p.msgLength + +typedef enum +{ + UBX_MSG_CLASS_NAV = 0x01, + UBX_MSG_CLASS_RXM = 0x02, + UBX_MSG_CLASS_INF = 0x04, + UBX_MSG_CLASS_ACK = 0x05, + UBX_MSG_CLASS_CFG = 0x06, + UBX_MSG_CLASS_MON = 0x0A, + UBX_MSG_CLASS_AID = 0x0B, + UBX_MSG_CLASS_TIM = 0x0D, + UBX_MSG_CLASS_LOG = 0x21 +} UBX_MessageClass_t; + +typedef enum +{ + UBX_MSG_ID_RXM_PMREQ = 0x41, +} UBX_MessageID_RXM_t; + +typedef enum +{ + UBX_MSG_ID_ACK_ACK = 0x01, + UBX_MSG_ID_ACK_NACK = 0x00 +} UBX_MessageID_ACK_t; + +typedef enum +{ + UBX_MSG_ID_NAV_AOPSTATUS = 0x60, + UBX_MSG_ID_NAV_CLOCK = 0x22, + UBX_MSG_ID_NAV_DGPS = 0x31, + UBX_MSG_ID_NAV_DOP = 0x04, + UBX_MSG_ID_NAV_POSECEF = 0x01, + UBX_MSG_ID_NAV_POSLLH = 0x02, + UBX_MSG_ID_NAV_PVT = 0x07, + UBX_MSG_ID_NAV_SBAS = 0x32, + UBX_MSG_ID_NAV_SOL = 0x06, + UBX_MSG_ID_NAV_STATUS = 0x03, + UBX_MSG_ID_NAV_SVINFO = 0x30, + UBX_MSG_ID_NAV_TIMEGPS = 0x20, + UBX_MSG_ID_NAV_TIMEUTC = 0x21, + UBX_MSG_ID_NAV_VELECEF = 0x11, + UBX_MSG_ID_NAV_VELNED = 0x12 +} UBX_MessageID_NAV_t; + +/* This message can be concatenated multiple times into a single message */ +typedef struct +{ + uint8_t clsID; + uint8_t msgID; +} UBX_ACK_t; + +/****************************** RXM *************************************/ + +#define UBX_RXM_PMREQ_WAKEUP_SOURCE_UART 0x08U + +typedef struct +{ + uint8_t version; + uint8_t reserved1[3]; + uint32_t duration; + uint32_t flags; + uint32_t wakeupSources; +} UBX_RXM_PMREQ_t; + +/****************************** NAV *************************************/ + +#define UBX_NAV_STATUS_FLAGS_GPSFIXOK 0x01U +#define UBX_NAV_STATUS_FLAGS_DIFFSOLN 0x02U +#define UBX_NAV_STATUS_FLAGS_WKNSET 0x04U +#define UBX_NAV_STATUS_FLAGS_TOWSET 0x08U + +#define UBX_NAV_STATUS_FIXSTAT_DGPSISTAT 0x01U +#define UBX_NAV_STATUS_FIXSTAT_MAPMATCHING 0xC0U + +enum { + UBX_NAV_STATUS_FIXSTAT_MAPMATCHING_NONE, + UBX_NAV_STATUS_FIXSTAT_MAPMATCHING_VALID_NOT_USED, + UBX_NAV_STATUS_FIXSTAT_MAPMATCHING_VALID_AND_USED, + UBX_NAV_STATUS_FIXSTAT_MAPMATCHING_VALID_AND_USED_DR, +}; + +#define UBX_NAV_STATUS_FLAGS2_PSMSTATE 0x03U + +enum { + UBX_NAV_STATUS_FLAGS2_PSMSTATE_ACQUISITION, + UBX_NAV_STATUS_FLAGS2_PSMSTATE_TRACKING, + UBX_NAV_STATUS_FLAGS2_PSMSTATE_OPTIMIZED, + UBX_NAV_STATUS_FLAGS2_PSMSTATE_INACTIVE, +}; + +#define UBX_NAV_STATUS_FLAGS2_SPOOFDETSTATE 0x18U + +enum { + UBX_NAV_STATUS_FLAGS2_SPOOFDETSTATE_UNKNOWN_OFF, + UBX_NAV_STATUS_FLAGS2_SPOOFDETSTATE_INDICATED, + UBX_NAV_STATUS_FLAGS2_SPOOFDETSTATE_MULTI_INDICATED, +}; + +enum { + UBX_NAV_STATUS_GPSFIX_NOFIX, + UBX_NAV_STATUS_GPSFIX_DEAD_RECKONING, + UBX_NAV_STATUS_GPSFIX_2DFIX, + UBX_NAV_STATUS_GPSFIX_3DFIX, + UBX_NAV_STATUS_GPSFIX_GPS_DEAD_RECKONING, + UBX_NAV_STATUS_GPSFIX_TIME_ONLY +}; + +typedef struct +{ + uint32_t iTOW; + uint8_t gpsFix; + uint8_t flags; + uint8_t fixStat; + uint8_t flags2; + uint32_t ttff; + uint32_t msss; +} UBX_NAV_STATUS_t; + +typedef struct +{ + uint32_t iTOW; + int32_t lon; + int32_t lat; + int32_t height; + int32_t hMSL; + uint32_t hAcc; + uint32_t vAcc; +} UBX_NAV_POSLLH_t; + +#pragma anon_unions + +typedef struct +{ + uint8_t syncChars[2]; + uint8_t msgClass; + uint8_t msgId; + uint16_t msgLength; /* Excludes header and CRC bytes */ + union + { + uint8_t payloadAndCrc[64]; /* CRC is appended to payload */ + UBX_ACK_t UBX_ACK; + UBX_NAV_STATUS_t UBX_NAV_STATUS; + UBX_NAV_POSLLH_t UBX_NAV_POSLLH; + UBX_RXM_PMREQ_t UBX_RXM_PMREQ; + }; +} UBX_Packet_t; + +#pragma pack () + +#endif // __NEO_M8N_UBX_H_ diff --git a/stack/framework/hal/inc/hwradio.h b/stack/framework/hal/inc/hwradio.h index dec05a382..0d7a9d188 100644 --- a/stack/framework/hal/inc/hwradio.h +++ b/stack/framework/hal/inc/hwradio.h @@ -457,6 +457,10 @@ void hw_radio_set_tx_power(int8_t eirp); void hw_radio_set_rx_timeout(uint32_t timeout); +void hwradio_set_addr(int); +void hwradio_enter_low_power_mode(); +bool hwradio_wakeup_from_lowpower_mode(); + #endif //__HW_RADIO_H_ /** @}*/ diff --git a/stack/framework/hal/platforms/B_L072Z_LRWAN1/CMakeLists.txt b/stack/framework/hal/platforms/B_L072Z_LRWAN1/CMakeLists.txt index a8881bc73..e28299d98 100644 --- a/stack/framework/hal/platforms/B_L072Z_LRWAN1/CMakeLists.txt +++ b/stack/framework/hal/platforms/B_L072Z_LRWAN1/CMakeLists.txt @@ -37,10 +37,16 @@ PLATFORM_OPTION(PLATFORM_SX127X_USE_VCC_TXCO "Platform has access to the VCC TCX PLATFORM_OPTION(PLATFORM_USE_NUCLEO_IKS01A2_SHIELD "The NUCLEO IKS01A2 shield is mounted" FALSE) PLATFORM_OPTION(PLATFORM_USE_EXTERNAL_MODEM "Use an external serial connected oss7-modem" FALSE) PLATFORM_OPTION(PLATFORM_FS_SYSTEMFILES_IN_SEPARATE_LINKER_SECTION "A dedicated linker section is used for storing the systemfiles" TRUE) # on stm32l0 we use this to move the systemfiles to embedded EEPROM +PLATFORM_OPTION(PLATFORM_USE_NEO_M8N_GPS "Platform uses M8N GPS module" TRUE) + +# GPS +# uses USART1 (PA9/PA10) +PLATFORM_PARAM(PLATFORM_GPS_UART "1" STRING "The UART port used by the GPS configuration." ) +PLATFORM_PARAM(PLATFORM_GPS_BAUDRATE "115200" STRING "The baudrate used by the GPS configuration." ) # CONSOLE -# uses USART2, (PA2 / PA3) -PLATFORM_PARAM(PLATFORM_CONSOLE_UART "1" STRING "The UART port used by the console UART configuration." ) +# uses USART4 (PA0) +PLATFORM_PARAM(PLATFORM_CONSOLE_UART "2" STRING "The UART port used by the console UART configuration." ) PLATFORM_PARAM(PLATFORM_CONSOLE_BAUDRATE "115200" STRING "The baudrate used by the second console configuration." ) PLATFORM_PARAM(PLATFORM_CONSOLE_LOCATION "0" STRING "The location") # TODO remove after removing the pins param from the API @@ -92,7 +98,9 @@ SET(LINKER_FLAGS "-Xlinker -Map=linker.map" CACHE STRING "") # Add additional definitions to the 'platform_defs.h' file generated by cmake PLATFORM_HEADER_DEFINE( + NUMBER PLATFORM_GPS_UART NUMBER PLATFORM_CONSOLE_UART + PLATFORM_GPS_BAUDRATE PLATFORM_CONSOLE_LOCATION PLATFORM_CONSOLE_BAUDRATE PLATFORM_MODEM_INTERFACE_UART @@ -103,6 +111,7 @@ PLATFORM_HEADER_DEFINE( BOOL PLATFORM_USE_DEBUGPINS PLATFORM_USE_USB_CDC + PLATFORM_USE_NEO_M8N_GPS PLATFORM_SX127X_USE_RESET_PIN PLATFORM_SX127X_USE_DIO3_PIN PLATFORM_SX127X_USE_PA_BOOST @@ -133,6 +142,11 @@ ADD_LIBRARY(PLATFORM OBJECT ADD_CHIP("stm32l0xx") ADD_CHIP("stm32_common") +#Include the source for GPS chip +IF(${PLATFORM_USE_NEO_M8N_GPS}) + ADD_CHIP("neo_m8n_gps") +ENDIF() + #Include the sources for the radio chip, if needed IF(NOT ("${${PLATFORM_PREFIX}_RADIO}" STREQUAL "none")) ADD_CHIP(${${PLATFORM_PREFIX}_RADIO}) diff --git a/stack/framework/hal/platforms/B_L072Z_LRWAN1/inc/ports.h b/stack/framework/hal/platforms/B_L072Z_LRWAN1/inc/ports.h index f0912306f..e773efa48 100644 --- a/stack/framework/hal/platforms/B_L072Z_LRWAN1/inc/ports.h +++ b/stack/framework/hal/platforms/B_L072Z_LRWAN1/inc/ports.h @@ -36,7 +36,7 @@ static const spi_port_t spi_ports[SPI_COUNT] = { } }; -#define UART_COUNT 2 +#define UART_COUNT 3 static const uart_port_t uart_ports[UART_COUNT] = { { // USART2, connected to VCOM of debugger USB connection @@ -53,7 +53,15 @@ static const uart_port_t uart_ports[UART_COUNT] = { .alternate = GPIO_AF4_USART1, .uart = USART1, .irq = USART1_IRQn - } + }, + { + // USART4, exposed on CN6 header: TX=PA0 (only) + .tx = PIN(GPIO_PORTA, 0), + .rx = PIN(GPIO_PORTA, 1), + .alternate = GPIO_AF6_USART4, + .uart = USART4, + .irq = USART4_5_IRQn + } }; #define I2C_COUNT 1 diff --git a/stack/framework/hal/platforms/NATIVE/CMakeLists.txt b/stack/framework/hal/platforms/NATIVE/CMakeLists.txt index 98aa944f0..fe6a4b606 100644 --- a/stack/framework/hal/platforms/NATIVE/CMakeLists.txt +++ b/stack/framework/hal/platforms/NATIVE/CMakeLists.txt @@ -20,9 +20,40 @@ #Check that the correct toolchain for the platform is being used REQUIRE_TOOLCHAIN(gcc) +# Dummy UART for console interface +PLATFORM_PARAM(USE_DUMMY_RADIO "1" STRING "A dummy radio is used by this platform." ) +PLATFORM_PARAM(PLATFORM_CONSOLE_UART "0" STRING "The UART port used by the console UART configuration." ) +PLATFORM_PARAM(PLATFORM_CONSOLE_BAUDRATE "0" STRING "The baudrate used by the second console configuration." ) +PLATFORM_PARAM(PLATFORM_CONSOLE_LOCATION "0" STRING "The location") # TODO remove after removing the pins param from the API + +# Dummy timers +PLATFORM_PARAM(PLATFORM_NUM_TIMERS "4" STRING "The number of dummy timers supported." ) + +# UART emulation +PLATFORM_PARAM(PLATFORM_UART_DEV0 "/tmp/S0" STRING "A virtual terminal created using socat for serial comms." ) +PLATFORM_PARAM(PLATFORM_MODEM_INTERFACE_UART "0" STRING "Virtual TTY number to use for modem interface." ) +PLATFORM_PARAM(PLATFORM_MODEM_INTERFACE_BAUDRATE "115200" STRING "Virtual TTY dummy baudrate for modem interface." ) + +# Button emulation +PLATFORM_PARAM(PLATFORM_NUM_BUTTONS "10" STRING "Buttons emulated by keyboard input keys using keys 0-9." ) + +# No LEDs +PLATFORM_PARAM(PLATFORM_NUM_LEDS "0" STRING "The number of LEDs.") + +# GPS +PLATFORM_OPTION(PLATFORM_USE_NEO_M8N_GPS "Platform uses M8N GPS module" TRUE) +PLATFORM_PARAM(PLATFORM_UART_DEV1 "/dev/ttyUSB0" STRING "A real serial UART connected to an M8N GPS board" ) +PLATFORM_PARAM(PLATFORM_GPS_UART "1" STRING "The UART port used by the GPS configuration." ) +PLATFORM_PARAM(PLATFORM_GPS_BAUDRATE "115200" STRING "The baudrate used by the GPS configuration." ) + #Make the 'inc' directory available so 'platform.h' can be found EXPORT_GLOBAL_INCLUDE_DIRECTORIES(inc) +# Add platform specific linker flags +INSERT_LINKER_FLAGS(BEFORE OBJECTS INSERT "-Wl,-gc-sections") +INSERT_LINKER_FLAGS(BEFORE LINK_LIBRARIES INSERT "-Wl,--start-group") +INSERT_LINKER_FLAGS(AFTER LINK_LIBRARIES INSERT "-lgcc -lc -Wl,--end-group") + INCLUDE_DIRECTORIES(inc) @@ -33,9 +64,32 @@ EXPORT_GLOBAL_INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) #Define the 'platform library'. Every platform must define a 'PLATFORM' object library ADD_LIBRARY(PLATFORM OBJECT platf_main.c - libc_overrides.c + platf_button.c + platf_uart.c + platf_radio.c + platf_timer.c + libc_overrides.c inc/platform.h ) +PLATFORM_HEADER_DEFINE( + BOOL USE_DUMMY_RADIO + PLATFORM_USE_NEO_M8N_GPS + STRING PLATFORM_UART_DEV0 + PLATFORM_UART_DEV1 + NUMBER PLATFORM_NUM_TIMERS + PLATFORM_NUM_LEDS + PLATFORM_MODEM_INTERFACE_UART + PLATFORM_MODEM_INTERFACE_BAUDRATE + PLATFORM_NUM_BUTTONS + PLATFORM_GPS_UART + PLATFORM_GPS_BAUDRATE + ) + +#Include the source for GPS chip +IF(${PLATFORM_USE_NEO_M8N_GPS}) + ADD_CHIP("neo_m8n_gps") +ENDIF() + #Build the 'platform_defs.h' settings file PLATFORM_BUILD_SETTINGS_FILE() diff --git a/stack/framework/hal/platforms/NATIVE/README.md b/stack/framework/hal/platforms/NATIVE/README.md new file mode 100644 index 000000000..df6f4b243 --- /dev/null +++ b/stack/framework/hal/platforms/NATIVE/README.md @@ -0,0 +1,43 @@ +# Overview + +The native platform allows the stack to be run on a Linux computer. The +emulation presently supports: + +- Push button emulation with 10 virtual buttons using numbered keys 0-9 +- Unlimited mumber of sensor application instances +- Console serial output to the same terminal the application is launched +- Emulated radio based on multi-cast IP communications +- Emulated UART in conjunction with the socat tool to allow pyd7a domain + to interact with the gateway using the serial modem interface +- Option to connect to real peripherals (eg GPS) using a real UART + on your Linux machine via the emulated UART layer + +# Why is this useful? + +- Access to physical hardware is not always possible +- Allow application and stack to be tested independently of hardware +- Full stack debug trace can be CPU intensive on small embedded CPUs +- More rapidly try out stack or application ideas + +# Quick start + +Launch virtual serial ports using socat as follows: + +socat pty,link=/tmp/S0,raw,echo=0 pty,link=/tmp/S1,raw,echo=0 & + +Run the following command to build using the assigned device /tmp/S0 from socat above: + +cmake -DCMAKE_TOOLCHAIN_FILE=/cmake/toolchains/gcc.cmake -DAPP_GATEWAY=y -DAPP_SENSOR_PUSH=y -DAPP_SENSOR_PULL=y -DPLATFORM_UART_DEV0=/tmp/S0 + +Launch your applications in separate terminal windows: + +./apps/gateway/gateway.elf +./apps/sensor_pull/sensor_pull/elf + +You can now interact with the gateway using pyd7a and the assigned device /tmp/S1 from socat above: + +PYTHONPATH=`pwd` python examples/query_nodes.py -d /tmp/S1 + +# Related repositories + +[pyd7a](https://github.com/MOSAIC-LoPoW/pyd7a) provides a collection of python modules, supporting the DASH7 Alliance Protocol in general, and OSS-7 in particular. diff --git a/stack/framework/hal/platforms/NATIVE/inc/button.h b/stack/framework/hal/platforms/NATIVE/inc/button.h new file mode 100644 index 000000000..bb6906fc2 --- /dev/null +++ b/stack/framework/hal/platforms/NATIVE/inc/button.h @@ -0,0 +1,75 @@ +/* * OSS-7 - An opensource implementation of the DASH7 Alliance Protocol for ultra + * lowpower wireless sensor communication + * + * Copyright 2015 University of Antwerp + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __PLATFORM_USERBUTTON_H_ +#define __PLATFORM_USERBUTTON_H_ + +#include "link_c.h" +#include "types.h" +#include "platform.h" + +/* \brief The identifiers for the buttons + */ +typedef uint8_t button_id_t; + +/* \brief The callback function for when a button is pressed + * + * \param button_id The id of the button that was pressed + * **/ +typedef void (*ubutton_callback_t)(button_id_t button_id); + +/* \brief Check whether a button is currently pressed or not. + * + * if an invalid button_id is supplied, this function returns false + * + * \return bool TRUE if the button is pressed + * FALSE if the button is not pressed or if an invalid button id was supplied + * */ +__LINK_C bool ubutton_pressed(button_id_t button_id); + +/* \brief Register a function to be called when the button with the specified is pressed + * + * Multiple callback functions can be registered for the same button but the same function cannot be registered twice. + * If a previously registered callback is re-registered for the same button, EALREADY is returned + * + * \param button_id The id of the button for which to register the callback '0' for PB0, '1' for PB1 + * \param callback The function to call when the button is pressed + * \return error_t SUCCESS if the callback was successfully registered + * ESIZE if an invalid button_id was specified + * EINVAL if callback is 0x0 + * EALREADY if the callback was already registered for this button + * ENOMEM if the callback could not be registered because there are already too many + * callbacks registered for this button + */ +__LINK_C error_t ubutton_register_callback(button_id_t button_id, ubutton_callback_t callback); + +/* \brief Deregister a callback function previously registered using 'register_button_callback' + * + * \param button_id The id of the button for which to register the callback '0' for PB0, '1' for PB1 + * \param callback The function to deregister + * \return error_t SUCCESS if the callback was successfully deregistered + * ESIZE if an invalid button_id was specified + * EINVAL if callback is 0x0 + * EALREADY if the callback was not registered for this button + */ +__LINK_C error_t ubutton_deregister_callback(button_id_t button_id, ubutton_callback_t callback); + +//not a user function +void __ubutton_init(); + +#endif diff --git a/stack/framework/hal/platforms/NATIVE/platf_button.c b/stack/framework/hal/platforms/NATIVE/platf_button.c new file mode 100644 index 000000000..fa9717192 --- /dev/null +++ b/stack/framework/hal/platforms/NATIVE/platf_button.c @@ -0,0 +1,106 @@ +/* * OSS-7 - An opensource implementation of the DASH7 Alliance Protocol for ultra + * lowpower wireless sensor communication + * + * Copyright 2015 University of Antwerp + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include + +#include "platform.h" +#include "button.h" +#include "hwgpio.h" +#include "hwatomic.h" +#include "scheduler.h" +#include "errors.h" + + +typedef struct +{ + ubutton_callback_t callback; +} button_info_t; + +static char stack_top[8192]; +static int button_handler(void *arg); + +static button_info_t buttons[PLATFORM_NUM_BUTTONS]; + + +__LINK_C void __ubutton_init() +{ + for (int i = 0; i < PLATFORM_NUM_BUTTONS; i++) + buttons[i].callback = NULL; + clone(button_handler, &stack_top[8192], CLONE_VM, NULL); +} + +__LINK_C bool ubutton_pressed(button_id_t button_id) +{ + return false; // Polling not supported in this emulation +} + +__LINK_C error_t ubutton_register_callback(button_id_t button_id, ubutton_callback_t callback) +{ + if(button_id >= PLATFORM_NUM_BUTTONS) + return ESIZE; + + start_atomic(); + buttons[button_id].callback = callback; + end_atomic(); + + return SUCCESS; +} + +__LINK_C error_t ubutton_deregister_callback(button_id_t button_id, ubutton_callback_t callback) +{ + return ubutton_register_callback(button_id, NULL); +} + + +static struct termios orig_termios; + +static void restore_termio(void) +{ + tcsetattr(STDIN_FILENO, TCSAFLUSH, &orig_termios); +} + +static int button_handler(void *arg) +{ + tcgetattr(STDIN_FILENO, &orig_termios); + atexit(restore_termio); + struct termios raw = orig_termios; + raw.c_lflag &= ~(ECHO | ICANON); + tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw); + + while (1) + { + int c; + if (read(STDIN_FILENO, &c, 1) == 1) + { + uint8_t button_id = c - '0'; + if (button_id >= 0 && button_id < PLATFORM_NUM_BUTTONS && + buttons[button_id].callback) + { + buttons[button_id].callback(button_id); + } + } + } + + return 0; +} diff --git a/stack/framework/hal/platforms/NATIVE/platf_main.c b/stack/framework/hal/platforms/NATIVE/platf_main.c index 1004b4f97..844e2652d 100644 --- a/stack/framework/hal/platforms/NATIVE/platf_main.c +++ b/stack/framework/hal/platforms/NATIVE/platf_main.c @@ -16,6 +16,7 @@ * limitations under the License. */ +#include #include "bootstrap.h" #include "hwgpio.h" #include "hwleds.h" @@ -27,13 +28,14 @@ #include "errors.h" #include "blockdevice_ram.h" #include "framework_defs.h" +#include "button.h" #define METADATA_SIZE (4 + 4 + (12 * FRAMEWORK_FS_FILE_COUNT)) // on native we use a RAM blockdevice as NVM as well for now -uint8_t d7ap_fs_metadata[METADATA_SIZE]; -uint8_t d7ap_files_data[FRAMEWORK_FS_PERMANENT_STORAGE_SIZE]; -uint8_t d7ap_volatile_files_data[FRAMEWORK_FS_VOLATILE_STORAGE_SIZE]; +extern uint8_t d7ap_fs_metadata[METADATA_SIZE]; +extern uint8_t d7ap_files_data[FRAMEWORK_FS_PERMANENT_STORAGE_SIZE]; +extern uint8_t d7ap_volatile_files_data[FRAMEWORK_FS_VOLATILE_STORAGE_SIZE]; static blockdevice_ram_t metadata_bd = (blockdevice_ram_t){ .base.driver = &blockdevice_driver_ram, @@ -63,16 +65,19 @@ void __platform_init() blockdevice_init(metadata_blockdevice); blockdevice_init(persistent_files_blockdevice); blockdevice_init(volatile_blockdevice); + // The emulated radio library for native platform requires a unique address + // per application instance, so we use the PID to provide this + hwradio_set_addr(getpid()); } void __platform_post_framework_init() { + __ubutton_init(); } -int main() +int main(void) { - //initialise the platform itself - __platform_init(); + __platform_init(); //do not initialise the scheduler, this is done by __framework_bootstrap() __framework_bootstrap(); //initialise platform functionality that depends on the framework @@ -83,23 +88,27 @@ int main() } // empty stubs -__LINK_C uart_handle_t* uart_init(uint8_t port_idx, uint32_t baudrate, uint8_t pins) {} -__LINK_C bool uart_enable(uart_handle_t* uart) {} -__LINK_C bool uart_disable(uart_handle_t* uart) {} -__LINK_C void uart_send_bytes(uart_handle_t* uart, void const *data, size_t length) {} -__LINK_C error_t uart_rx_interrupt_enable(uart_handle_t* uart) {} -__LINK_C void uart_set_rx_interrupt_callback(uart_handle_t* uart, uart_rx_inthandler_t rx_handler) {} -__LINK_C void uart_set_error_callback(uart_handle_t* uart, uart_error_handler_t error_handler) {} __LINK_C error_t hw_gpio_set(pin_id_t pin_id) {} system_reboot_reason_t hw_system_reboot_reason(void) {} -__LINK_C void hw_enter_lowpower_mode(uint8_t mode) {} -__LINK_C hwtimer_tick_t hw_timer_getvalue(hwtimer_id_t timer_id) {} -__LINK_C const hwtimer_info_t* hw_timer_get_info(hwtimer_id_t timer_id) {} -__LINK_C error_t hw_timer_schedule(hwtimer_id_t timer_id, hwtimer_tick_t tick ) {} -__LINK_C error_t hw_timer_init(hwtimer_id_t timer_id, uint8_t frequency, timer_callback_t compare_callback, timer_callback_t overflow_callback) {} -__LINK_C bool hw_timer_is_overflow_pending(hwtimer_id_t id) {} -__LINK_C error_t hw_timer_cancel(hwtimer_id_t timer_id) {} + +// We don't really support low power mode, but we can sleep this process until an "interrupt" +// arises. We use the timer tick or radio IRQ to force a wake-up. +__LINK_C void hw_enter_lowpower_mode(uint8_t mode) +{ + hwradio_enter_low_power_mode(); // Informs radio module we are entering low power state + hwtimer_tick_t tick = hw_timer_getvalue(0); + while (hw_timer_getvalue(0) == tick && !hwradio_wakeup_from_lowpower_mode()) + usleep(10); +} + __LINK_C uint64_t hw_get_unique_id(void) { return 0xFFFFFFFFFFFFFF;} __LINK_C void hw_watchdog_feed(void) {}; __LINK_C void __watchdog_init(void) {}; __LINK_C void hw_watchdog_get_timeout(void) {}; + +void hw_reset() { + printf("\n!!! hw_reset !!!"); + for (;;) usleep(1000); +} + +void hw_busy_wait(int16_t us) { usleep(us); } diff --git a/stack/framework/hal/platforms/NATIVE/platf_radio.c b/stack/framework/hal/platforms/NATIVE/platf_radio.c new file mode 100644 index 000000000..f413c71fc --- /dev/null +++ b/stack/framework/hal/platforms/NATIVE/platf_radio.c @@ -0,0 +1,610 @@ +/* * OSS-7 - An opensource implementation of the DASH7 Alliance Protocol for ultra + * lowpower wireless sensor communication + * + * Copyright 2015 University of Antwerp + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*! \file dummy_radio.c + * + * \author Liam Wickins + * + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fifo.h" +#include "timer.h" +#include "hwradio.h" +#include "errors.h" +#include "log.h" + + +// RSSI levels +#define RSSI_BELOW_NOISE_LEVEL -120 +#define RSSI_ABOVE_NOISE_LEVEL 0 + +#define US_PER_SEC 1000000 +#define NS_PER_SEC 1000000000UL + +#define RX_QUEUE_SIZE 4 +#define MCAST_GROUP "239.0.0.1" +#define PORT 5000 + +#ifdef HAL_RADIO_LOG_ENABLED + +#define DPRINT(...) log_print_stack_string(LOG_STACK_RADIO, __VA_ARGS__) +#define DPRINT_DATA(p, n) log_print_data(p, n) + +#else + +#define DPRINT(...) +#define DPRINT_DATA(p, n) + +#endif + +static hwradio_init_args_t dummy_radio; +static uint32_t own_addr = 0; + +static uint8_t send_data[1024]; +static unsigned int send_length = 0; +static bool refill_enable = false; + +static char stack_top[8192]; +static int recv_fd = -1; +static int send_fd = -1; +static char rx_buffer[65536]; +static volatile hw_radio_state_t radio_opmode = HW_STATE_OFF; +static hw_radio_packet_t *rx_packets[4]; +static fifo_t rx_packet_fifo; +static bool radio_idle = false; +static volatile bool radio_low_power_wakeup = false; +static volatile int16_t last_rssi = RSSI_BELOW_NOISE_LEVEL; + +static uint32_t radio_sync_size = 0; +static uint32_t radio_center_freq = 0; +static uint32_t radio_bitrate = 0; +static uint32_t radio_bw = 0; + +static void execute_send(); + +// Internal radio packet structure -- it contains additional meta data that +// is required for filtering of received packets over UDP multi-cast. +// The source_addr is set through the additional API call hwradio_set_addr(). +// This should be unique on the network of applications but if running on +// a single local machine, one can use the PID to set the address since PID +// is unique on a single machine. + +struct radio_packet_header +{ + uint32_t source_addr; + uint64_t timestamp; + uint32_t center_freq; + uint32_t bw; + uint32_t bitrate; +}; + + +// This is used purely for performance related timestamping to assess any delays +// of network packets when debugging problems -- it has no functional purpose + +static uint64_t timestamp() +{ + uint64_t val; + struct timespec tp; + clock_gettime(CLOCK_MONOTONIC, &tp); + val = (tp.tv_sec * NS_PER_SEC) + tp.tv_nsec; + return val; +} + +// The main handling of transmitting and receiving UDP packets happens here. The main +// consideration is to: +// i) Filter out any packets that we shouldn't process i.e., packets +// send by oneself or packets received when the radio is not configured +// on the same channel or indeed if the radio is switched off or idle +// ii) Strip out preamble and SYNC bytes +// iii) Allocating of packet structure and queueing +// iv) Notification to stack with suitable radio delays based on bitrate + +static int radio_handler(void *arg) +{ + int no_frame_received_count = 0; + + while (1) + { + // Notify any FIFOd radio RX packets + while (fifo_get_size(&rx_packet_fifo)) + { + // Do not notify unless radio is in RX state and not idle + if (radio_opmode != HW_STATE_RX || radio_idle) + break; + + // Pop next packet off the queue and RX notify + hw_radio_packet_t *packet; + fifo_pop(&rx_packet_fifo, (uint8_t *)&packet, sizeof(hw_radio_packet_t *)); + DPRINT("radio_handler: notify packet size=%u", packet->length); + dummy_radio.rx_packet_cb(packet); + } + + // Check for a pending TX packet to schedule -- this will also handle TX complete notifications + if (send_length > 0 && radio_opmode == HW_STATE_TX) + execute_send(); + + // Try to receive any RX radio packets + //DPRINT("radio_handler: recv..."); + int rc = recv(recv_fd, rx_buffer, sizeof(rx_buffer), 0); + + // Check in case no packet was received + if (rc == -1) + { + // If no packets have been received within a certain timeframe and the RSSI has not been + // consumed already (i.e., by calling get_rssi) then clear down the RSSI here + no_frame_received_count++; + if (no_frame_received_count >= 50) + last_rssi = RSSI_BELOW_NOISE_LEVEL; // Set this value to below the noise level + continue; + } else + no_frame_received_count = 0; // Reset RSSI clear down counter + + // Ignore packets that are not of the required size (should never happen in theory) + if (rc >= sizeof(struct radio_packet_header)) + { + uint32_t recv_addr; + struct radio_packet_header *packet = (struct radio_packet_header *)rx_buffer; + uint64_t trx = timestamp(); // Timestamp for debugging RX timing + + DPRINT("radio_handler: received UDP packet of %d bytes", rc); + + // Force low power mode wakeup, if the main scheduler process is sleeping + radio_low_power_wakeup = true; + + // Set a nominal RSSI level for receive level + last_rssi = RSSI_ABOVE_NOISE_LEVEL; + + // Filter received UDP packets in accordance with the source + // address (i.e., ignore packets from self) and radio properties + // such as frequency, bw and bitrate and the radio operating state + if (packet->source_addr != own_addr && + packet->bw == radio_bw && + packet->center_freq == radio_center_freq && + packet->bitrate == radio_bitrate && + radio_opmode == HW_STATE_RX && + !radio_idle) + { + int preamble_length = 0; + uint8_t *p = rx_buffer + sizeof(struct radio_packet_header); + rc += sizeof(struct radio_packet_header); // Skip over radio packet header now since we've filtered now + + // Skip preamble bytes -- it is assumed that AA bytes are stripped from the beginning of each frame + // where present + while (*p == 0xAA && rc) + { + p++; + rc--; + preamble_length++; + } + + DPRINT("latency=%lu ns preamble_length=%d detected, remaining bytes=%d", (trx-packet->timestamp), preamble_length, rc); + + // Skip the sync word if configured and there was a preamble detected + if (preamble_length && rc >= radio_sync_size) + { + p += radio_sync_size; + rc -= radio_sync_size; + } + + // Proceed if we have a payload remaining + if (rc > 0) + { + hw_radio_packet_t *packet = dummy_radio.alloc_packet_cb(rc); + if (!packet) + { + DPRINT("radio_handler: packet dropped as unable to allocate packet buffer"); + continue; + } + packet->length = rc; + memcpy(&packet->data, p, rc); + packet->rx_meta.crc_status = HW_CRC_VALID; // No CRC errors in this implementation + packet->rx_meta.rssi = RSSI_ABOVE_NOISE_LEVEL; + packet->rx_meta.timestamp = timer_get_counter_value(); + DPRINT("radio_handler: queued radio packet %p of %d bytes", packet, packet->length); + if (fifo_put(&rx_packet_fifo, (uint8_t *)&packet, sizeof(hw_radio_packet_t *)) != SUCCESS) + DPRINT("radio_handler: packet dropped as RX FIFO is full"); + } + else + { + DPRINT("radio_handler: discarding empty data payload"); + } + } + else + { + DPRINT("radio_handler: packet dropped as does not meet RX criteria"); + } + } + } +} + +// This function will send a UDP radio packet that has already been constructed by a +// hw_radio_send_payload API call. It will also handle notifications back to the stack and +// differentiate between when the radio is in refill mode (i.e., BG ETA flooding) or +// normal transmission mode. + +static void execute_send() +{ + static uint64_t delay_send_tstart = 0; + static unsigned int len = 0; + + if (len == 0) + { + // Initiate transmission start procedure + len = send_length; + delay_send_tstart = timestamp(); + + DPRINT("execute_send: new packet len=%i scheduled for transmission", len); + } + + // Compute time until packet "transmitted" (i.e., radio TX time) + uint64_t delay_ns = ((NS_PER_SEC * (len * 8)) / radio_bitrate); + if ((timestamp() - delay_send_tstart) < delay_ns) + return; + + // Schedule transmission of packet + struct sockaddr_in servaddr; + bzero(&servaddr,sizeof(servaddr)); + servaddr.sin_family = AF_INET; + servaddr.sin_addr.s_addr = inet_addr(MCAST_GROUP); + servaddr.sin_port = htons(PORT); + + DPRINT("execute_send: packet len=%i scheduled for transmission after delay_ns=%lu", len, delay_ns); + + if (sendto(send_fd, send_data, len, 0, + (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0){ + DPRINT("execute_send: sendto failed"); + } + + // This marks the packet as "consumed" + send_length = 0; + len = 0; + + // Notify based on refill mode + if (refill_enable) + { + DPRINT("execute_send: tx_refill_cb"); + dummy_radio.tx_refill_cb(len); + } + else + { + DPRINT("execute_send: tx_packet_cb"); + dummy_radio.tx_packet_cb(timer_get_counter_value()); + } +} + +// This routine will initialise our dummy radio -- it includes creating a cloned process for +// handling the received packets which is done async to the main scheduler process. + +error_t hw_radio_init(hwradio_init_args_t* init_args) +{ + DPRINT("hw_radio_init"); + dummy_radio = *init_args; + + // A FIFO is used for queueing received UDP packets since these typically get received in a burst and not + // in accordance with normal radio timing + fifo_init(&rx_packet_fifo, (uint8_t *)rx_packets, sizeof(hw_radio_packet_t *) * RX_QUEUE_SIZE); + + // Global send_fd used for transmitting UDP radio packets + send_fd = socket(AF_INET, SOCK_DGRAM, 0); + if (send_fd < 0) + { + perror("socket"); + return -1; + } + + // Global recv_fd used for transmitting UDP radio packets + recv_fd = socket(AF_INET, SOCK_DGRAM, 0); + if (recv_fd < 0) { + perror("socket"); + return -1; + } + + // This allows our receive UDP port number to be reused by multiple processes which is only ever + // allowed for multicast packets + int yes = 1; + if (setsockopt(recv_fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) + { + perror("setsockopt SO_REUSEADDR"); + close(recv_fd); + exit(-1); + } + + // Binds our receive UDP port to a reusable port + struct sockaddr_in addr; + bzero((char *)&addr, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_ANY); + addr.sin_port = htons(PORT); + + if (bind(recv_fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) + { + perror("bind"); + close(recv_fd); + exit(-1); + } + + // Configures our receive UDP socket for multicast traffic on the + // designated multicast group + struct ip_mreq mreq; + mreq.imr_multiaddr.s_addr = inet_addr(MCAST_GROUP); + mreq.imr_interface.s_addr = htonl(INADDR_ANY); + if (setsockopt(recv_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, + &mreq, sizeof(mreq)) < 0) { + perror("setsockopt IP_ADD_MEMBERSHIP"); + close(recv_fd); + exit(-1); + } + + // To allow notification of received packets, the recv_fd has a small timeout on + // rather than blocking forever. + struct timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 10; + if (setsockopt (recv_fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, + sizeof(timeout)) < 0) + { + perror("setsockopt timeout"); + close(recv_fd); + return -1; + } + + // We create a new process that runs the radio_handler -- it must be created with + // shared memory so that the main scheduler can interact with the stored radio state + clone(radio_handler, &stack_top[8192], CLONE_VM, NULL); + + DPRINT("own_addr = %08X", own_addr); + + return SUCCESS; +} + +/** \brief Stop the radio driver, and free the hardware resources (SPI, GPIO interrupts, ...) + */ +void hw_radio_stop(void) +{ + DPRINT("hw_radio_stop"); + radio_idle = true; +} + +error_t hw_radio_set_idle(void) +{ + DPRINT("hw_radio_set_idle"); + radio_idle = true; + + return SUCCESS; +} + +// This will construct a radio packet by prepending a radio packet header +// to the supplied data payload. The actual sending happens inside the +// radio_handler process. + +error_t hw_radio_send_payload(uint8_t * data, uint16_t len) +{ + assert(send_length == 0); + + struct radio_packet_header *packet = (struct radio_packet_header *)send_data; + packet->timestamp = timestamp(); + + DPRINT("hw_radio_send_payload(%u) ttx=%lu", len, packet->timestamp); + + packet->source_addr = own_addr; + packet->center_freq = radio_center_freq; + packet->bw = radio_bw; + packet->bitrate = radio_bitrate; + memcpy(send_data + sizeof(struct radio_packet_header), data, len); + send_length = len + sizeof(struct radio_packet_header); + + return SUCCESS; +} + +bool hw_radio_tx_busy(void) +{ + DPRINT("hw_radio_tx_busy"); + return false; +} + +bool hw_radio_rx_busy(void) +{ + DPRINT("hw_radio_rx_busy"); + return false; +} + +bool hw_radio_rssi_valid(void) +{ + DPRINT("hw_radio_rssi_valid"); + return true; +} + +int16_t hw_radio_get_rssi(void) +{ + int16_t rssi = last_rssi; + DPRINT("hw_radio_get_rssi"); + + last_rssi = RSSI_BELOW_NOISE_LEVEL; + + return rssi; +} + +hw_radio_state_t hw_radio_get_opmode(void) +{ + DPRINT("hw_radio_get_opmode = %u", radio_opmode); + return radio_opmode; +} + +void hw_radio_set_opmode(hw_radio_state_t opmode) +{ + DPRINT("hw_radio_set_opmode(%u)", opmode); + + radio_opmode = opmode; + radio_idle = false; // We clear the idle state here since the global state change overrides it + + // This only switches the opmode it doesn't schedule send or receive which is done by the radio_handler process + + if (opmode == HW_STATE_TX) + { + DPRINT("HW_STATE_TX"); + } + else if (opmode == HW_STATE_RX) + { + DPRINT("HW_STATE_RX"); + } + else if (opmode == HW_STATE_OFF) + { + DPRINT("HW_STATE_OFF"); + } + else if (opmode == HW_STATE_SLEEP) + { + DPRINT("HW_STATE_SLEEP"); + } + else if (opmode == HW_STATE_IDLE) + { + DPRINT("HW_STATE_IDLE"); + } + else if (opmode == HW_STATE_RESET) + { + DPRINT("HW_STATE_RESET"); + } + else if (opmode == HW_STATE_STANDBY) + { + DPRINT("HW_STATE_STANDBY"); + } +} + +void hw_radio_set_center_freq(uint32_t center_freq) +{ + DPRINT("hw_radio_set_center_freq(%u)", center_freq); + radio_center_freq = center_freq; // Used for packet filtering +} + +void hw_radio_set_rx_bw_hz(uint32_t bw_hz) +{ + DPRINT("hw_radio_set_rx_bw_hz(%u)", bw_hz); + radio_bw = bw_hz; // Used for packet filtering +} + +void hw_radio_set_bitrate(uint32_t bps) +{ + DPRINT("hw_radio_set_bitrate(%u)", bps); + radio_bitrate = bps; // Used for packet filtering +} + +void hw_radio_set_tx_fdev(uint32_t fdev) +{ + DPRINT("hw_radio_set_tx_fdev(%u)", fdev); +} + +void hw_radio_set_preamble_size(uint16_t size) +{ + DPRINT("hw_radio_set_preamble_size(%u)", size); +} + +void hw_radio_set_preamble_detector(uint8_t preamble_detector_size, uint8_t preamble_tol) +{ + DPRINT("hw_radio_set_preamble_detector(%u,%u)", preamble_detector_size, preamble_tol); +} + +void hw_radio_set_rssi_config(uint8_t rssi_smoothing, uint8_t rssi_offset) +{ + DPRINT("hw_radio_set_rssi_config(%u,%u)", rssi_smoothing, rssi_offset); +} + +void hw_radio_set_dc_free(uint8_t scheme) +{ + DPRINT("hw_radio_set_dc_free(%u)", scheme); +} + +void hw_radio_set_sync_word(uint8_t *sync_word, uint8_t sync_size) +{ + DPRINT("hw_radio_set_sync_word(%02x,%u)", *sync_word, sync_size); + radio_sync_size = sync_size; // Used for removal of SYNC word + // TODO: Sync word is not presently validated +} + +void hw_radio_set_crc_on(uint8_t enable) +{ + DPRINT("hw_radio_set_crc_on(%u)", enable); + // Not used -- CRC always assumed to be correct +} + +void hw_radio_set_payload_length(uint16_t length) +{ + DPRINT("hw_radio_set_payload_length(%u)", length); +} + +bool hw_radio_is_idle(void) +{ + DPRINT("hw_radio_is_idle"); + return radio_idle; +} + +bool hw_radio_is_rx(void) +{ + DPRINT("hw_radio_is_rx"); + return radio_opmode == HW_STATE_RX; +} + +void hw_radio_enable_refill(bool enable) +{ + DPRINT("hw_radio_enable_refill(%u)", enable); + refill_enable = enable; // Used to control TX notification behaviour +} + +void hw_radio_enable_preloading(bool enable) +{ + DPRINT("hw_radio_enable_preloading(%u)", enable); +} + +void hw_radio_set_tx_power(int8_t eirp) +{ + DPRINT("hw_radio_set_tx_power(%d)", eirp); +} + +void hw_radio_set_rx_timeout(uint32_t timeout) +{ + DPRINT("hw_radio_set_rx_timeout(%u)", timeout); +} + +void hwradio_set_addr(int addr) +{ + // Own address is used as the source_addr field in the radio packet header for UDP transmissions + own_addr = addr; +} + +void hwradio_enter_low_power_mode() +{ + // Allows the radio module to know when the CPU (scheduler) has entered low power mode + radio_low_power_wakeup = false; +} + +bool hwradio_wakeup_from_lowpower_mode() +{ + return radio_low_power_wakeup; +} diff --git a/stack/framework/hal/platforms/NATIVE/platf_timer.c b/stack/framework/hal/platforms/NATIVE/platf_timer.c new file mode 100644 index 000000000..21e61a737 --- /dev/null +++ b/stack/framework/hal/platforms/NATIVE/platf_timer.c @@ -0,0 +1,173 @@ +/* * OSS-7 - An opensource implementation of the DASH7 Alliance Protocol for ultra + * lowpower wireless sensor communication + * + * Copyright 2015 University of Antwerp + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/*! \file dummy_timer.c + * + * \author Liam Wickins + * + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include "hwtimer.h" +#include "log.h" +#include "platform_defs.h" + +#if HAL_PERIPH_LOG_ENABLED + +#define DPRINT(...) log_print_stack_string(LOG_STACK_TIMER, __VA_ARGS__) + +#else + +#define DPRINT(...) + +#endif + +#define NS_PER_SEC 1000000000UL +#define MSEC (NS_PER_SEC/1000) + +// Can be set through the make system (default is 4) +#define NUM_TIMERS PLATFORM_NUM_TIMERS + +// Timer state is stored on a per timer basis with a maximum of NUM_TIMERS +static bool timers_init = false; +static volatile bool timer_schedule_active[NUM_TIMERS]; +static volatile hwtimer_tick_t timer_schedule_tick[NUM_TIMERS]; +static void (*timer_fired[NUM_TIMERS])(void); +static void (*timer_overflow[NUM_TIMERS])(void); +static volatile hwtimer_tick_t tick = 0; // This is the global tick counter +static char stack_top[8192]; + +// High resolution timer +static uint64_t time_now() +{ + uint64_t val; + struct timespec tp; + clock_gettime(CLOCK_MONOTONIC, &tp); + val = (tp.tv_sec * NS_PER_SEC) + tp.tv_nsec; + //printf("\nval = %lu\n", val); + return val; +} + + +// The timer handler is pretty simple. It just wakes up once every tick and updates its tick counter, +// notifying any elapsed scheduled timers or overflows as it goes. + +static int timer_handler(void *arg) +{ + static uint64_t last_t; + + last_t = time_now(); // Start-up condition so we know initial high-res time value + + while (1) + { + uint64_t t = time_now(); + + unsigned int msec = ((t - last_t) / MSEC); + + //DPRINT("tick = %u - %u:%u", msec, timer_schedule_active[0], timer_schedule_tick[0]); + + // In some cases we may have elapsed more than 1 ms since the last call so we iterate the + // number of milliseconds elapsed and call the scheduler for each tick that has elapsed + + for (unsigned int k = 0; k < msec; k++) + { + tick++; + + for (int i = 0; i < NUM_TIMERS; i++) + { + if (timer_schedule_active[i]) + { + //DPRINT("checking %u : %u <> %u", i, timer_schedule_tick[i], tick); + if (tick == timer_schedule_tick[i]) + { + //DPRINT("!!!!!!!!!!!! FIRED !!!!!!!!!!!!!!"); + //timer_schedule_active[i] = false; + timer_fired[i](); + } + } + + if (tick == 0 && timer_overflow[i]) + { + //DPRINT("!!!!!!!!!!!! OVERFLOW !!!!!!!!!!!!!!"); + timer_overflow[i](); + } + } + last_t = t; + } + + usleep(1000); // Assumes 1 ms timer tick but it is not guaranteed to wake-up after 1 ms + } +} + + +__LINK_C hwtimer_tick_t hw_timer_getvalue(hwtimer_id_t timer_id) +{ + //DPRINT("hw_timer_getvalue(%u)=%u", timer_id, tick); + return tick; // Return global tick value +} + +__LINK_C const hwtimer_info_t* hw_timer_get_info(hwtimer_id_t timer_id) +{ + static const hwtimer_info_t timer_info = { + .min_delay_ticks = 0, + }; + return &timer_info; +} + +__LINK_C error_t hw_timer_schedule(hwtimer_id_t timer_id, hwtimer_tick_t t ) +{ + DPRINT("hw_timer_schedule(%u, %u) @ %u", timer_id, tick, hw_timer_getvalue(timer_id)); + timer_schedule_tick[timer_id] = t; + timer_schedule_active[timer_id] = true; +} + +__LINK_C error_t hw_timer_init(hwtimer_id_t timer_id, uint8_t frequency, timer_callback_t compare_callback, timer_callback_t overflow_callback) +{ + DPRINT("hw_timer_init(%u, %u)", timer_id, frequency); + + timer_fired[timer_id] = compare_callback; + timer_overflow[timer_id] = overflow_callback; + + // We do a one-time init of the async timer task first time this function is called + if (!timers_init) + { + // This cloned process will handle all timers + timers_init = true; + clone(timer_handler, &stack_top[8192], CLONE_VM, NULL); + } + + return 0; +} + +__LINK_C bool hw_timer_is_overflow_pending(hwtimer_id_t id) +{ + return false; // We don't have interrupt pending in this emulation +} + +__LINK_C error_t hw_timer_cancel(hwtimer_id_t timer_id) +{ + DPRINT("hw_timer_cancel(%u)", timer_id); + timer_schedule_active[timer_id] = false; // Just set the timer active flag +} diff --git a/stack/framework/hal/platforms/NATIVE/platf_uart.c b/stack/framework/hal/platforms/NATIVE/platf_uart.c new file mode 100644 index 000000000..9894598c7 --- /dev/null +++ b/stack/framework/hal/platforms/NATIVE/platf_uart.c @@ -0,0 +1,163 @@ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "platform_defs.h" +#include "hwuart.h" + +struct uart_handle +{ + int fd; + char dev[256]; + volatile bool is_irq_enabled; + volatile bool is_enabled; + volatile uart_rx_inthandler_t rx_handler; + uint8_t stack[8192]; +}; + +static struct uart_handle dummy_uarts[] = +{ +#ifdef PLATFORM_UART_DEV0 + { + .dev = PLATFORM_UART_DEV0 + }, +#endif +#ifdef PLATFORM_UART_DEV1 + { + .dev = PLATFORM_UART_DEV1 + }, +#endif +#ifdef PLATFORM_UART_DEV2 + { + .dev = PLATFORM_UART_DEV2 + }, +#endif +}; + +int uart_handler(void *arg) +{ + uart_handle_t *uart = (uart_handle_t *)arg; + while (1) + { + char c; + int ret; + ret = read(uart->fd, &c, 1); + if (ret == 1 && uart->is_enabled && uart->is_irq_enabled && uart->rx_handler) + { + //printf("\nRX byte(%s): %02x\n", uart->dev, (uint8_t)c); + uart->rx_handler((uint8_t)c); + } + } + + return 0; +} + + +uart_handle_t* uart_init(uint8_t port_idx, uint32_t baudrate, uint8_t pins) +{ + uart_handle_t *uart = &dummy_uarts[port_idx]; + uart->fd = open(uart->dev, O_RDWR | O_NOCTTY); + + struct termios raw; + tcgetattr(uart->fd, &raw); + + if (baudrate == 1200) + baudrate = B1200; + else if (baudrate == 2400) + baudrate = B2400; + else if (baudrate == 4800) + baudrate = B4800; + else if (baudrate == 9600) + baudrate = B9600; + else if (baudrate == 19200) + baudrate = B19200; + else if (baudrate == 38400) + baudrate = B38400; + else if (baudrate == 57600) + baudrate = B57600; + else if (baudrate == 57600) + baudrate = B57600; + else if (baudrate == 115200) + baudrate = B115200; + else + assert(0); + + cfsetispeed(&raw, baudrate); + cfsetospeed(&raw, baudrate); + + raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); + raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON | CSTOPB); + raw.c_oflag &= ~(OPOST); + raw.c_cflag |= (CS8); + tcsetattr(uart->fd, TCSAFLUSH, &raw); + + clone(uart_handler, &uart->stack[8192], CLONE_VM, uart); + + return uart; +} + +bool uart_disable(uart_handle_t* uart) +{ + uart->is_enabled = false; +} + +bool uart_get_rx_port_state(uart_handle_t* uart) +{ + return false; +} + +bool uart_enable(uart_handle_t* uart) +{ + uart->is_enabled = true; +} + +void uart_send_byte(uart_handle_t* uart, uint8_t data) +{ + if (uart->is_enabled) + write(uart->fd, &data, 1); +} + +void uart_send_bytes(uart_handle_t* uart, void const *data, size_t length) +{ + if (uart->is_enabled) + write(uart->fd, data, length); +} + +void uart_send_string(uart_handle_t* uart, const char *string) +{ + if (uart->is_enabled) + write(uart->fd, string, strlen(string)); +} + +error_t uart_rx_interrupt_enable(uart_handle_t* uart) +{ + uart->is_irq_enabled = true; + return 0; +} + +void uart_rx_interrupt_disable(uart_handle_t* uart) +{ + uart->is_irq_enabled = false; +} + +void uart_set_rx_interrupt_callback(uart_handle_t* uart, + uart_rx_inthandler_t rx_handler) +{ + uart->rx_handler = rx_handler; +} + +void cdc_set_rx_interrupt_callback(uart_rx_inthandler_t rx_handler) +{ +} + +void uart_set_error_callback(uart_handle_t* uart, uart_error_handler_t error_handler) +{ +} diff --git a/stack/framework/inc/alp_layer.h b/stack/framework/inc/alp_layer.h index e0f204f23..3b3d6967a 100644 --- a/stack/framework/inc/alp_layer.h +++ b/stack/framework/inc/alp_layer.h @@ -48,6 +48,7 @@ typedef void (*alp_command_completed_callback)(uint8_t tag_id, bool success); typedef void (*alp_command_result_callback)(alp_command_t* command, alp_interface_status_t* origin_itf_status); typedef alp_status_codes_t (*alp_unhandled_read_action_callback)(const alp_interface_status_t* origin_itf_status, alp_operand_file_data_request_t operand, uint8_t* alp_response); +typedef alp_status_codes_t (*alp_unhandled_write_action_callback)(const alp_interface_status_t* origin_itf_status, alp_operand_file_data_t *operand); typedef struct { alp_command_completed_callback alp_command_completed_cb; @@ -62,6 +63,7 @@ typedef struct { * It is important to know this callback is called while a D7AP transaction is in process thus be sure to return within transaction timeout limits! */ alp_unhandled_read_action_callback alp_unhandled_read_action_cb; + alp_unhandled_write_action_callback alp_unhandled_write_action_cb; } alp_init_args_t; typedef enum { diff --git a/stack/framework/inc/d7ap.h b/stack/framework/inc/d7ap.h index c2b33b896..67eeb9b71 100644 --- a/stack/framework/inc/d7ap.h +++ b/stack/framework/inc/d7ap.h @@ -68,7 +68,9 @@ typedef enum AES_CCM_32 = 0x07, /* data confidentiality and authenticity*/ } nls_method_t; -typedef struct { +#pragma pack(1) + +typedef struct __attribute__((__packed__)) { union { uint8_t raw; struct { @@ -91,7 +93,7 @@ typedef struct __attribute__((__packed__)) { uint8_t id[8]; // TODO assuming 8 byte id for now } d7ap_addressee_t; -typedef struct { +typedef struct __attribute__((__packed__)) { union { uint8_t raw; struct { @@ -137,7 +139,7 @@ typedef enum { SESSION_RETRY_MODE_NO = 0 } d7ap_session_retry_mode_t; -typedef struct { +typedef struct __attribute__((__packed__)) { union { uint8_t raw; struct { @@ -172,10 +174,12 @@ typedef struct{ } d7ap_resource_desc_t; // override alp_interface_config_t -typedef struct { +typedef struct __attribute__ ((__packed__)) { uint8_t itf_id; d7ap_session_config_t d7ap_session_config; -} __attribute__ ((__packed__)) alp_interface_config_d7ap_t; +} alp_interface_config_d7ap_t; + +#pragma pack() //=========================== prototypes ====================================== diff --git a/stack/framework/inc/log.h b/stack/framework/inc/log.h index 58afc20e7..9566950f6 100644 --- a/stack/framework/inc/log.h +++ b/stack/framework/inc/log.h @@ -44,6 +44,7 @@ /*! \brief The source in the stack from which the log originates */ typedef enum { + LOG_STACK_APP = 0x00, LOG_STACK_PHY = 0x01, LOG_STACK_DLL = 0x02, LOG_STACK_MAC = 0x03, @@ -52,6 +53,8 @@ typedef enum LOG_STACK_SESSION = 0x06, LOG_STACK_D7AP = 0x07, LOG_STACK_ALP = 0x08, + LOG_STACK_RADIO = 0x09, + LOG_STACK_TIMER = 0x0A, LOG_STACK_FWK = 0x10, LOG_STACK_EM = 0x11 } log_stack_layer_t; // TODO stack specific, move to stack component? diff --git a/stack/modules/alp/alp_layer.c b/stack/modules/alp/alp_layer.c index 88c40d00b..f0ff457f8 100644 --- a/stack/modules/alp/alp_layer.c +++ b/stack/modules/alp/alp_layer.c @@ -82,7 +82,8 @@ static uint8_t alp_data2[ALP_QUERY_COMPARE_BODY_MAX_SIZE]; // temp buffer static extern alp_interface_t* interfaces[MODULE_ALP_INTERFACE_SIZE]; -static itf_ctrl_t current_itf_ctrl; +// Use a default interface of D7 to ensure that an interface at least gets initialised at start-up +static itf_ctrl_t current_itf_ctrl = { .interface = ALP_ITF_ID_D7ASP }; static bool ignore_itf_ctrl_write_callback = false; static void process_async(void* arg); @@ -342,13 +343,17 @@ static alp_status_codes_t process_op_write_file_properties(alp_action_t* action) return rc == SUCCESS ? ALP_STATUS_OK : alp_translate_error(rc); } -static alp_status_codes_t process_op_write_file_data(alp_action_t* action) { +static alp_status_codes_t process_op_write_file_data(alp_action_t* action, alp_command_t* command) { DPRINT("WRITE FILE %i LEN %i OFFSET %i", action->file_data_operand.file_offset.file_id, action->file_data_operand.provided_data_length, action->file_data_operand.file_offset.offset); if (action->file_data_operand.provided_data_length > ALP_PAYLOAD_MAX_SIZE) return ALP_STATUS_EXCEEDS_MAX_ALP_SIZE; - + int rc = d7ap_fs_write_file(action->file_data_operand.file_offset.file_id, action->file_data_operand.file_offset.offset, action->file_data_operand.data, action->file_data_operand.provided_data_length); + + if (rc == -ENOENT && init_args != NULL && init_args->alp_unhandled_write_action_cb != NULL) // give the application layer the chance to fullfill this request ... + return init_args->alp_unhandled_write_action_cb(&command->origin_itf_status, &action->file_data_operand); + return rc == SUCCESS ? ALP_STATUS_OK : alp_translate_error(rc); } @@ -552,7 +557,7 @@ static alp_status_codes_t process_op_create_file(alp_action_t* action) { static alp_status_codes_t write_itf_command(itf_ctrl_action_t action) { - int rc = d7ap_fs_write_file(INTERFACE_CTRL_FILE_ID, 0, &action, 1); // gets handled in write file callback + int rc = d7ap_fs_write_file(INTERFACE_CTRL_FILE_ID, 0, (uint8_t *)&action, 1); // gets handled in write file callback return rc == SUCCESS ? ALP_STATUS_OK : alp_translate_error(rc); } @@ -709,7 +714,7 @@ static void process_async(void* arg) alp_status = process_op_read_file_properties(&action, resp_command); break; case ALP_OP_WRITE_FILE_DATA: - alp_status = process_op_write_file_data(&action); + alp_status = process_op_write_file_data(&action, command); break; case ALP_OP_WRITE_FILE_PROPERTIES: alp_status = process_op_write_file_properties(&action); diff --git a/stack/modules/alp/serial_interface.c b/stack/modules/alp/serial_interface.c index e68862df1..49a04e0e3 100644 --- a/stack/modules/alp/serial_interface.c +++ b/stack/modules/alp/serial_interface.c @@ -75,6 +75,7 @@ static void serial_interface_cmd_handler(fifo_t* cmd_fifo) } void serial_interface_register() { +#ifdef PLATFORM_MODEM_INTERFACE_UART alp_modem_interface = (alp_interface_t) { .itf_id = ALP_ITF_ID_SERIAL, .itf_cfg_len = 0, @@ -94,6 +95,7 @@ void serial_interface_register() { #endif modem_interface_register_handler(&serial_interface_cmd_handler, SERIAL_MESSAGE_TYPE_ALP_DATA); +#endif } static error_t serial_interface_send(uint8_t* payload, uint8_t payload_length, diff --git a/stack/modules/d7ap/phy.c b/stack/modules/d7ap/phy.c index 2935ed1a9..17e6bbf70 100644 --- a/stack/modules/d7ap/phy.c +++ b/stack/modules/d7ap/phy.c @@ -293,10 +293,11 @@ static void packet_transmitted(timer_tick_t timestamp) static void packet_received(hw_radio_packet_t* hw_radio_packet) { - assert(state == STATE_RX || state == STATE_BG_SCAN); // we are in interrupt context here, so mark packet for further processing, // schedule it and return - DPRINT("packet received @ %i , RSSI = %d", hw_radio_packet->rx_meta.timestamp, hw_radio_packet->rx_meta.rssi); + DPRINT("state %i packet received @ %i , RSSI = %d", state, hw_radio_packet->rx_meta.timestamp, hw_radio_packet->rx_meta.rssi); + + assert(state == STATE_RX || state == STATE_BG_SCAN); packet_t* packet = packet_queue_find_packet(hw_radio_packet); @@ -635,11 +636,14 @@ void status_write() { write_file_counter++; if(write_file_counter == 100) { write_file_counter = 0; - uint16_t bg_trigger_ratio = 1024 * total_rssi_triggers / total_bg; - uint16_t scan_timeout_ratio = 1024 * (total_fg - total_succeeded_fg) / total_fg; - uint8_t buffer[4] = {(uint8_t)(bg_trigger_ratio >> 8), (uint8_t)(bg_trigger_ratio & 0xFF), (uint8_t)(scan_timeout_ratio >> 8), (uint8_t)(scan_timeout_ratio & 0xFF)}; - d7ap_fs_write_file(D7A_FILE_DLL_STATUS_FILE_ID, 8, buffer, 4); - DPRINT("wrote to file 0x%02X the bg trigger ratio %d and scan timeout ratio %d", D7A_FILE_DLL_STATUS_FILE_ID, bg_trigger_ratio, scan_timeout_ratio); + if (total_fg && total_bg) + { + uint16_t bg_trigger_ratio = 1024 * total_rssi_triggers / total_bg; + uint16_t scan_timeout_ratio = 1024 * (total_fg - total_succeeded_fg) / total_fg; + uint8_t buffer[4] = {(uint8_t)(bg_trigger_ratio >> 8), (uint8_t)(bg_trigger_ratio & 0xFF), (uint8_t)(scan_timeout_ratio >> 8), (uint8_t)(scan_timeout_ratio & 0xFF)}; + d7ap_fs_write_file(D7A_FILE_DLL_STATUS_FILE_ID, 8, buffer, 4); + DPRINT("wrote to file 0x%02X the bg trigger ratio %d and scan timeout ratio %d", D7A_FILE_DLL_STATUS_FILE_ID, bg_trigger_ratio, scan_timeout_ratio); + } } } @@ -766,6 +770,7 @@ error_t phy_send_packet(hw_radio_packet_t* packet, phy_tx_config_t* config, phy_ DPRINT("start sending @ %i\n", timer_get_counter_value()); hw_radio_send_payload(fg_frame.encoded_packet, fg_frame.encoded_length); + hw_radio_set_opmode(HW_STATE_TX); // Put radio into TX mode explicitly return SUCCESS; // TODO other return codes } @@ -871,6 +876,8 @@ error_t phy_send_packet_with_advertising(hw_radio_packet_t* packet, phy_tx_confi memcpy(&fg_frame.encoded_packet[preamble_len], &sync_word, 2); fg_frame.encoded_length = encode_packet(packet, &fg_frame.encoded_packet[preamble_len + 2]); fg_frame.encoded_length += preamble_len + 2; // add preamble + syncword + DPRINT("FG packet: %d", fg_frame.encoded_length); + DPRINT_DATA(fg_frame.encoded_packet, fg_frame.encoded_length); uint8_t payload_len; payload_len = assemble_background_payload(); @@ -913,13 +920,15 @@ static void fill_in_fifo(uint16_t remaining_bytes_len) { DEBUG_BG_END(); timer_tick_t current = timer_get_counter_value(); - // DPRINT("fill in fifo, bg adv, currently %d untill %d\n", current, bg_adv.stop_time); + DPRINT("fill in fifo, bg adv, remaining %u currently %d untill %d\n", remaining_bytes_len, current, bg_adv.stop_time); // calculate the time needed to flush the remaining bytes in the TX uint16_t flush_duration = phy_calculate_tx_duration(current_channel_id.channel_header.ch_class, PHY_CODING_PN9, // override FEC, we need the time for the BG_THRESHOLD bytes in the fifo, regardless of coding remaining_bytes_len, true); // don't take syncword and preamble into account + DPRINT("flush_duration=%u calc=%u", flush_duration, current + 2 * bg_adv.tx_duration + flush_duration); + if (bg_adv.stop_time > current + 2 * bg_adv.tx_duration + flush_duration) bg_adv.eta = (bg_adv.stop_time - current) - 2 * bg_adv.tx_duration; // ETA is updated according the real current time else if(bg_adv.stop_time > current + bg_adv.tx_duration + flush_duration) @@ -953,15 +962,23 @@ static void fill_in_fifo(uint16_t remaining_bytes_len) uint8_t preamble[24]; preamble_len = (bg_adv.stop_time - current) * (bg_adv.packet_size / (float)bg_adv.tx_duration); // TODO instead of current we should use the timestamp + + // We have to prevent the preamble_len calculation from exceeding the size of the FIFO and truncate + // it if necessary otherwise we'll trample over the stack + if (preamble_len > sizeof(preamble)) + { + DPRINT("WARN: truncated preamble length from %u to %u", preamble_len, sizeof(preamble)); + preamble_len = sizeof(preamble); + } DPRINT("ETA %d, packet size %d, tx_duration %d, current time %d\n", bg_adv.eta, bg_adv.packet_size, bg_adv.tx_duration, timer_get_counter_value()); + bg_adv.eta = 0; + fg_frame.bg_adv = false; + DPRINT("Add preamble_bytes: %d\n", preamble_len); memset(preamble, 0xAA, preamble_len); hw_radio_send_payload(preamble, preamble_len); DEBUG_BG_END(); - - bg_adv.eta = 0; - fg_frame.bg_adv = false; } } else @@ -980,7 +997,7 @@ error_t phy_start_background_scan(phy_rx_config_t* config, phy_rx_packet_callbac received_callback = rx_cb; uint8_t packet_len; - //DPRINT("START BG scan @ %i", timer_get_counter_value()); + DPRINT("START BG scan @ %i", timer_get_counter_value()); // We should not initiate a background scan before TX is completed assert(state != STATE_TX); @@ -1002,6 +1019,13 @@ error_t phy_start_background_scan(phy_rx_config_t* config, phy_rx_packet_callbac DEBUG_RX_START(); + // This code looks a bit dubious to me. It seems to rely on packets being received whilst the RSSI + // is being sampled. However, the timing of RSSI sampling may not always align with the BG transmit timing + // and the RSSI event could be missed i.e., sampling could be perfectly out-of-phase with the transmitter. + // Ideally the RSSI scanning would be happening asynchronously inside the radio hardware with a wake-up interrupt. + // Alternatively, the BG RSSI scan period should be set to be tsched/2 (Nyquist) or at least some number lower + // to avoid this issue. + int16_t rssi = hw_radio_get_rssi(); if (rssi <= config->rssi_thr) { diff --git a/stack/modules/d7ap_fs/CMakeLists.txt b/stack/modules/d7ap_fs/CMakeLists.txt index 021093167..a05e0476b 100644 --- a/stack/modules/d7ap_fs/CMakeLists.txt +++ b/stack/modules/d7ap_fs/CMakeLists.txt @@ -23,6 +23,9 @@ MODULE_OPTION(${MODULE_PREFIX}_USE_DEFAULT_SYSTEMFILES "Use the default D7AP systemfiles values" TRUE) +MODULE_OPTION(${MODULE_PREFIX}_LOG_ENABLED "Enable logging for FS layer" FALSE) +MODULE_HEADER_DEFINE(BOOL ${MODULE_PREFIX}_LOG_ENABLED) + MODULE_PARAM(${MODULE_PREFIX}_FILE_SIZE_MAX "256" STRING "The default buffer size for file operations" ) MODULE_HEADER_DEFINE( BOOL ${MODULE_PREFIX}_USE_DEFAULT_SYSTEMFILES diff --git a/stack/modules/d7ap_fs/d7ap_fs.c b/stack/modules/d7ap_fs/d7ap_fs.c index e39776a7b..3dbc85f5f 100644 --- a/stack/modules/d7ap_fs/d7ap_fs.c +++ b/stack/modules/d7ap_fs/d7ap_fs.c @@ -42,10 +42,8 @@ // length($data) == $header.length == $header.allocated_length /////////////////////////////////////// - - #if defined(FRAMEWORK_LOG_ENABLED) && defined(MODULE_D7AP_FS_LOG_ENABLED) -#define DPRINT(...) log_print_stack_string(LOG_STACK_NWL, __VA_ARGS__) +#define DPRINT(...) log_print_stack_string(LOG_STACK_D7AP, __VA_ARGS__) #define DPRINT_DATA(...) log_print_data(__VA_ARGS__) #else #define DPRINT(...)