From 09af8785e98b77de0b374df9e060b4ae60893947 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?King=20K=C3=A9vin?= Date: Wed, 16 Aug 2017 18:17:15 +0200 Subject: [PATCH] add Bluetooth <-> Forumslader forwarding --- Makefile | 2 +- README.md | 24 +++++++ application.c | 140 +++++++++++++++++---------------------- lib/radio_bluetooth.c | 139 ++++++++++++++++++++++++++++++++++++++ lib/radio_bluetooth.h | 37 +++++++++++ lib/sensor_forumslader.c | 139 ++++++++++++++++++++++++++++++++++++++ lib/sensor_forumslader.h | 37 +++++++++++ 7 files changed, 439 insertions(+), 79 deletions(-) create mode 100644 lib/radio_bluetooth.c create mode 100644 lib/radio_bluetooth.h create mode 100644 lib/sensor_forumslader.c create mode 100644 lib/sensor_forumslader.h diff --git a/Makefile b/Makefile index c7c9376..a40bcfa 100644 --- a/Makefile +++ b/Makefile @@ -28,7 +28,7 @@ FIRMWARE = $(APPLICATION) $(BOOTLOADER) # which development board is used # supported are: SYSTEM_BOARD, MAPLE_MINI, BLUE_PILL, CORE_BOARD -BOARD = SYSTEM_BOARD +BOARD = CORE_BOARD # opencm3 libraries OPENCM3_DIR := libopencm3 diff --git a/README.md b/README.md index c26de3a..606cfad 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,29 @@ While GPS also provides speed, the forumslader data based on the dynamo is much On top of the forumslader commands, the logger provides additional commands to list, retrieve, and delete the logged data files. +bluetooth +--------- + +The forumslogger uses a common HC-05 Bluetooth module for communication with the user. +Accordingly it is necessary to configure this Bluetooth module. +For that enter the AT control mode: + * connect the Bluetooth module to a USB to UART converter + * press and hold the button on the module + * plug the USB to UART converter to a computer + * the module should blink every two seconds, indicating it entered the AT control mode + * connect to the module using the USB to UART serial port with a baud rate of 38400 (8N1) +Alternative war to enter the AT control mode are described [here](https://alselectro.wordpress.com/2014/10/18/bluetooth-module-hc-05how-to-pair-2-modulesat-command-walkthrough/). + +Once connect configure the Bluetooth module: + * enter `AT` to check if the UART communication is working. The module should respond with OK + * change default PIN for a tiny bit of better security: `AT+PSWD=1337` + * change the normal mode baud rate: `AT+UART=115200,0,0` (the Forumslader uses 9600 but there is no good technical reason to keep this slow speed, and the forumslogger uses 115200) + * set the device name: `AT+NAME=forumslogger` (different to `Forumslader` to avoid confusion) + +Now connect to Bluetooth module to the forumlogger. +When powered the module will rapidly blink (in normal mode). +Once paired and connected the module will double blink every 3 seconds. + state ----- @@ -43,6 +66,7 @@ working: - file read and write (using fatfs) - GPS data parsing - RTC date and time synchronisation to GPS time +- forward Bluetooth <-> Forumslader data TODO: - log forumslader data diff --git a/application.c b/application.c index 08536bf..23a7b4c 100644 --- a/application.c +++ b/application.c @@ -39,9 +39,10 @@ /* own libraries */ #include "global.h" // board definitions #include "print.h" // printing utilities -#include "usart.h" // USART utilities #include "usb_cdcacm.h" // USB CDC ACM utilities +#include "sensor_forumslader.h" // forumslader communication #include "radio_gps.h" // GPS communication +#include "radio_bluetooth.h" // Bluetooth communication #include "flash_sdcard.h" // to read/write logs on SD card /* FatFs library */ @@ -67,9 +68,7 @@ size_t putc(char c) length = 0; // don't print string termination character } else if ('\r' == c || '\n' == c) { // send CR+LF newline for most carriage return and line feed combination if (0==newline || c==newline) { // send newline only if not already send (and only once on \r\n or \n\r) - usart_putchar_nonblocking('\r'); // send CR over USART usb_cdcacm_putchar('\r'); // send CR over USB - usart_putchar_nonblocking('\n'); // send LF over USART usb_cdcacm_putchar('\n'); // send LF over USB length += 2; // remember we printed 2 characters newline = c; // remember on which character we sent the newline @@ -77,7 +76,6 @@ size_t putc(char c) length = 0; // the \r or \n of \n\r or \r\n has already been printed } } else { - usart_putchar_nonblocking(c); // send byte over USART usb_cdcacm_putchar(c); // send byte over USB newline = 0; // clear new line length++; // remember we printed 1 character @@ -324,7 +322,6 @@ void main(void) { rcc_clock_setup_in_hse_8mhz_out_72mhz(); // use 8 MHz high speed external clock to generate 72 MHz internal clock - #if DEBUG // enable functionalities for easier debug DBGMCU_CR |= DBGMCU_CR_IWDG_STOP; // stop independent watchdog counter when code is halted @@ -339,7 +336,6 @@ void main(void) #endif board_setup(); // setup board - usart_setup(); // setup USART (for printing) usb_cdcacm_setup(); // setup USB CDC ACM (for printing) printf("\nwelcome to the CuVoodoo STM32F1 forumlader-logger\n"); // print welcome message @@ -362,42 +358,18 @@ void main(void) nvic_enable_irq(NVIC_RTC_IRQ); // allow the RTC to interrupt printf("OK\n"); - // setup GPS communication - printf("setup GPS: "); - radio_gps_setup(); + printf("setup forumslader UART: "); + sensor_forumslader_setup(); // setup USART printf("OK\n"); -/* - // setup SD card - printf("setup SD card: "); - if (flash_sdcard_setup()) { - printf("OK\n"); - } else { - printf("failed\n"); - } - printf("card size: %U bytes\n", flash_sdcard_size()); - printf("erase size: %u bytes\n", flash_sdcard_erase_size()); - uint8_t data_in[512] = {0xaa}; - for (uint16_t i=0; itm_hour, time_tm->tm_min, time_tm->tm_sec); } } + while (sensor_forumslader_received) { // a forumslader message has been received + sensor_forumslader_received = false; // clear flag + action = true; // action has been performed + radio_bluetooth_transmit((const char*)sensor_forumslader_message); // forward message over Bluetooth + printf("forumslader: %s", sensor_forumslader_message); // print message + } + while (radio_bluetooth_received) { // a message has been received over Bluetooth + radio_bluetooth_received = false; // clear flag + action = true; // action has been performed + sensor_forumslader_transmit((const char*)radio_bluetooth_message); // forward message to forumslader + printf("Bluetooth: %s", radio_bluetooth_message); // print message + } while (radio_gps_received) { // a GPS message has been received radio_gps_received = false; // clear flag action = true; // action has been performed - //printf("%s", radio_gps_message); // print GPS message - if (0==strncmp((const char *)radio_gps_message,"$GPRMC,",7)) { // get time from GPS - uint8_t arg = 0; - uint8_t arg_start = 0; - for (uint8_t i = 0; i < LENGTH(radio_gps_message) && radio_gps_message[i]!='\0'; i++) { - if (','==radio_gps_message[i]) { - if (1==arg) { // got time - if (i-arg_start<7) { // time not provided - break; - } else { - time_tm->tm_hour = (radio_gps_message[arg_start+1]-'0')*10+(radio_gps_message[arg_start+2]-'0')*1; // set hours - time_tm->tm_min = (radio_gps_message[arg_start+3]-'0')*10+(radio_gps_message[arg_start+4]-'0')*1; // set minutes - time_tm->tm_sec = (radio_gps_message[arg_start+5]-'0')*10+(radio_gps_message[arg_start+6]-'0')*1; // set seconds - } - } else if (2==arg) { // got validity - if (i-arg_start<2) { // validity not provided - break; - } else if ('A'!=radio_gps_message[arg_start+1]) { // not valid - break; - } - } else if (9==arg) { // got date - if (i-arg_start<7) { // date not provided - break; - } else { - time_tm->tm_mday = (radio_gps_message[arg_start+1]-'0')*10+(radio_gps_message[arg_start+2]-'0')*1; // set day of month - time_tm->tm_mon = (radio_gps_message[arg_start+3]-'0')*10+(radio_gps_message[arg_start+4]-'0')*1; // set month - time_tm->tm_year = 2000+(radio_gps_message[arg_start+5]-'0')*10+(radio_gps_message[arg_start+6]-'0')*1-1900; // set year - time_rtc = mktime(time_tm); // get back seconds - rtc_set_counter_val(time_rtc); // save date/time to internal RTC - //printf("GPS date saved: %d-%02d-%02d %02d:%02d:%02d\n", 1900+time_tm->tm_year, time_tm->tm_mon, time_tm->tm_mday, time_tm->tm_hour, time_tm->tm_min, time_tm->tm_sec); + //printf("GPS: %s", radio_gps_message); // print GPS message + if (!gps_rtc_synced) { // if the RTC has not been synced with the GPS time, try to do it + if (0==strncmp((const char *)radio_gps_message,"$GPRMC,",7)) { // get time from GPS + uint8_t arg = 0; + uint8_t arg_start = 0; + for (uint8_t i = 0; i < LENGTH(radio_gps_message) && radio_gps_message[i]!='\0'; i++) { + if (','==radio_gps_message[i]) { // argument end + if (1==arg) { // got time + if (i-arg_start<7) { // time not provided + break; + } else { + time_tm->tm_hour = (radio_gps_message[arg_start+1]-'0')*10+(radio_gps_message[arg_start+2]-'0')*1; // set hours + time_tm->tm_min = (radio_gps_message[arg_start+3]-'0')*10+(radio_gps_message[arg_start+4]-'0')*1; // set minutes + time_tm->tm_sec = (radio_gps_message[arg_start+5]-'0')*10+(radio_gps_message[arg_start+6]-'0')*1; // set seconds + } + } else if (2==arg) { // got validity + if (i-arg_start<2) { // validity not provided + break; + } else if ('A'!=radio_gps_message[arg_start+1]) { // not valid + break; + } + } else if (9==arg) { // got date + if (i-arg_start<7) { // date not provided + break; + } else { + time_tm->tm_mday = (radio_gps_message[arg_start+1]-'0')*10+(radio_gps_message[arg_start+2]-'0')*1; // set day of month + time_tm->tm_mon = (radio_gps_message[arg_start+3]-'0')*10+(radio_gps_message[arg_start+4]-'0')*1; // set month + time_tm->tm_year = 2000+(radio_gps_message[arg_start+5]-'0')*10+(radio_gps_message[arg_start+6]-'0')*1-1900; // set year + time_rtc = mktime(time_tm); // get back seconds + rtc_set_counter_val(time_rtc); // save date/time to internal RTC + gps_rtc_synced = true; // remember we synced the time + printf("GPS date saved: %d-%02d-%02d %02d:%02d:%02d\n", 1900+time_tm->tm_year, time_tm->tm_mon, time_tm->tm_mday, time_tm->tm_hour, time_tm->tm_min, time_tm->tm_sec); + } } + arg++; // next argument starts + arg_start = i; // save start of next argument } - arg++; // next argument starts - arg_start = i; // save start of next argument } } } diff --git a/lib/radio_bluetooth.c b/lib/radio_bluetooth.c new file mode 100644 index 0000000..de1f05c --- /dev/null +++ b/lib/radio_bluetooth.c @@ -0,0 +1,139 @@ +/* This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +/** library for receiving NMEA-0183 like messages over a Bluetooth module (code) + * @file radio_bluetooth.c + * @author King Kévin + * @date 2017 + * @note peripherals used: USART @ref radio_bluetooth_usart + */ +/* standard libraries */ +#include // standard integer types +#include // standard I/O facilities +#include // general utilities +#include // string utilities + +/* STM32 (including CM3) libraries */ +#include // real-time control clock library +#include // general purpose input output library +#include // universal synchronous asynchronous receiver transmitter library +#include // interrupt handler +#include // Cortex M3 utilities + +#include "radio_bluetooth.h" // forumslader header and definitions +#include "global.h" // common methods + +/** @defgroup radio_bluetooth_usart USART peripheral used for communication with the Bluetooth module + * @{ + */ +#define RADIO_BLUETOOTH_USART 2 /**< USART peripheral */ +/** @} */ + +#define RADIO_BLUETOOTH_BAUDRATE 115200 /**< USART baud rate for the Bluetooth communication, in bits per second (with 8N1 8 bits, no parity bit, 1 stop bit settings) */ + +static volatile char rx_buffer[82] = {0}; /**< buffer for received data (82 chars is the maximum NMEA-0183 message length) */ +static volatile uint8_t rx_buffer_i = 0; /**< current position of read received data */ +static volatile char tx_buffer[82] = {0}; /**< buffer to transmit data */ +static volatile uint8_t tx_buffer_i = 0; /**< number or remaining characters to transmit */ + +volatile bool radio_bluetooth_received = false; +volatile bool radio_bluetooth_transmitted = false; +volatile char radio_bluetooth_message[82+1] = {0}; + +void radio_bluetooth_setup(void) +{ + /* enable USART I/O peripheral */ + rcc_periph_clock_enable(USART_PORT_RCC(RADIO_BLUETOOTH_USART)); // enable clock for USART port peripheral + rcc_periph_clock_enable(USART_RCC(RADIO_BLUETOOTH_USART)); // enable clock for USART peripheral + rcc_periph_clock_enable(RCC_AFIO); // enable pin alternate function (USART) + gpio_set_mode(USART_PORT(RADIO_BLUETOOTH_USART), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, USART_PIN_TX(RADIO_BLUETOOTH_USART)); // setup GPIO pin USART transmit + gpio_set_mode(USART_PORT(RADIO_BLUETOOTH_USART), GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, USART_PIN_RX(RADIO_BLUETOOTH_USART)); // setup GPIO pin USART receive + gpio_set(USART_PORT(RADIO_BLUETOOTH_USART), USART_PIN_RX(RADIO_BLUETOOTH_USART)); // pull up to avoid noise when not connected + + /* setup USART parameters */ + usart_set_baudrate(USART(RADIO_BLUETOOTH_USART), RADIO_BLUETOOTH_BAUDRATE); // set baud rate + usart_set_databits(USART(RADIO_BLUETOOTH_USART), 8); // set data bits + usart_set_stopbits(USART(RADIO_BLUETOOTH_USART), USART_STOPBITS_1); // set stop bit + usart_set_mode(USART(RADIO_BLUETOOTH_USART), USART_MODE_TX_RX); // enable transmit and receive + usart_set_parity(USART(RADIO_BLUETOOTH_USART), USART_PARITY_NONE); // set parity + usart_set_flow_control(USART(RADIO_BLUETOOTH_USART), USART_FLOWCONTROL_NONE); // set flow control + + nvic_enable_irq(USART_IRQ(RADIO_BLUETOOTH_USART)); // enable the USART interrupt + usart_enable_rx_interrupt(USART(RADIO_BLUETOOTH_USART)); // enable receive interrupt + usart_enable(USART(RADIO_BLUETOOTH_USART)); // enable USART + + /* reset buffer states */ + radio_bluetooth_received = false; + rx_buffer_i = 0; + radio_bluetooth_transmitted = false; + tx_buffer_i = 0; +} + +bool radio_bluetooth_transmit(const char* message) +{ + if (strlen(message)>LENGTH(tx_buffer)) { // message to transmit is too long + return false; + } + while (tx_buffer_i) { // idle until buffer is empty + __WFI(); // sleep until interrupt + } + for (uint8_t i=0; i. + * + */ +/** library for receiving NMEA-0183 like messages over a Bluetooth module (API) + * @file radio_bluetooth.h + * @author King Kévin + * @date 2017 + * @note peripherals used: USART @ref radio_bluetooth_usart + */ +#pragma once + +/** has the message been transmitted */ +extern volatile bool radio_bluetooth_transmitted; +/** has a message been received */ +extern volatile bool radio_bluetooth_received; +/** last valid message received (including \r\n ending and \0) */ +extern volatile char radio_bluetooth_message[82+1]; + +/** setup USART peripheral for Bluetooth communication */ +void radio_bluetooth_setup(void); +/** transmit message + * @param[in] message message to transmit (must be NULL terminated) + * @return is message will be transmitted (e.g. is not too long) + * @note blocking is a transmission is already ongoing + */ +bool radio_bluetooth_transmit(const char* message); diff --git a/lib/sensor_forumslader.c b/lib/sensor_forumslader.c new file mode 100644 index 0000000..ab088a8 --- /dev/null +++ b/lib/sensor_forumslader.c @@ -0,0 +1,139 @@ +/* This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +/** library for receiving forumslader NMEA-0183 like messages (code) + * @file sensor_forumslader.c + * @author King Kévin + * @date 2017 + * @note peripherals used: USART @ref sensor_forumslader_usart + */ +/* standard libraries */ +#include // standard integer types +#include // standard I/O facilities +#include // general utilities +#include // string utilities + +/* STM32 (including CM3) libraries */ +#include // real-time control clock library +#include // general purpose input output library +#include // universal synchronous asynchronous receiver transmitter library +#include // interrupt handler +#include // Cortex M3 utilities + +#include "sensor_forumslader.h" // forumslader header and definitions +#include "global.h" // common methods + +/** @defgroup sensor_forumslader_usart USART peripheral used for communication with the forumslader + * @{ + */ +#define SENSOR_FORUMSLADER_USART 1 /**< USART peripheral */ +/** @} */ + +#define SENSOR_FORUMSLADER_BAUDRATE 9600 /**< USART baudrate for the forumslader communication, in bits per second (with 8N1 8 bits, no parity bit, 1 stop bit settings) */ + +static volatile char rx_buffer[82] = {0}; /**< buffer for received data (82 chars is the maximum NMEA-0183 message length) */ +static volatile uint8_t rx_buffer_i = 0; /**< current position of read received data */ +static volatile char tx_buffer[82] = {0}; /**< buffer to transmit data */ +static volatile uint8_t tx_buffer_i = 0; /**< number or remaining characters to transmit */ + +volatile bool sensor_forumslader_received = false; +volatile bool sensor_forumslader_transmitted = false; +volatile char sensor_forumslader_message[82+1] = {0}; + +void sensor_forumslader_setup(void) +{ + /* enable USART I/O peripheral */ + rcc_periph_clock_enable(USART_PORT_RCC(SENSOR_FORUMSLADER_USART)); // enable clock for USART port peripheral + rcc_periph_clock_enable(USART_RCC(SENSOR_FORUMSLADER_USART)); // enable clock for USART peripheral + rcc_periph_clock_enable(RCC_AFIO); // enable pin alternate function (USART) + gpio_set_mode(USART_PORT(SENSOR_FORUMSLADER_USART), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, USART_PIN_TX(SENSOR_FORUMSLADER_USART)); // setup GPIO pin USART transmit + gpio_set_mode(USART_PORT(SENSOR_FORUMSLADER_USART), GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, USART_PIN_RX(SENSOR_FORUMSLADER_USART)); // setup GPIO pin USART receive + gpio_set(USART_PORT(SENSOR_FORUMSLADER_USART), USART_PIN_RX(SENSOR_FORUMSLADER_USART)); // pull up to avoid noise when not connected + + /* setup USART parameters */ + usart_set_baudrate(USART(SENSOR_FORUMSLADER_USART), SENSOR_FORUMSLADER_BAUDRATE); // set baud rate + usart_set_databits(USART(SENSOR_FORUMSLADER_USART), 8); // set data bits + usart_set_stopbits(USART(SENSOR_FORUMSLADER_USART), USART_STOPBITS_1); // set stop bit + usart_set_mode(USART(SENSOR_FORUMSLADER_USART), USART_MODE_TX_RX); // enable transmit and receive + usart_set_parity(USART(SENSOR_FORUMSLADER_USART), USART_PARITY_NONE); // set parity + usart_set_flow_control(USART(SENSOR_FORUMSLADER_USART), USART_FLOWCONTROL_NONE); // set flow control + + nvic_enable_irq(USART_IRQ(SENSOR_FORUMSLADER_USART)); // enable the USART interrupt + usart_enable_rx_interrupt(USART(SENSOR_FORUMSLADER_USART)); // enable receive interrupt + usart_enable(USART(SENSOR_FORUMSLADER_USART)); // enable USART + + /* reset buffer states */ + sensor_forumslader_received = false; + rx_buffer_i = 0; + sensor_forumslader_transmitted = false; + tx_buffer_i = 0; +} + +bool sensor_forumslader_transmit(const char* message) +{ + if (strlen(message)>LENGTH(tx_buffer)) { // message to transmit is too long + return false; + } + while (tx_buffer_i) { // idle until buffer is empty + __WFI(); // sleep until interrupt + } + for (uint8_t i=0; i. + * + */ +/** library for receiving forumslader NMEA-0183 like messages (API) + * @file sensor_forumslader.h + * @author King Kévin + * @date 2017 + * @note peripherals used: USART @ref sensor_forumslader_usart + */ +#pragma once + +/** has the forumslader message been transmitted */ +extern volatile bool sensor_forumslader_transmitted; +/** has a forumslader message been received */ +extern volatile bool sensor_forumslader_received; +/** last valid forumslader message received (including \r\n ending and \0) */ +extern volatile char sensor_forumslader_message[82+1]; + +/** setup USART peripheral for forumslader communication */ +void sensor_forumslader_setup(void); +/** transmit forumslader message + * @param[in] message message to transmit (must be NULL terminated) + * @return is message will be transmitted (e.g. is not too long) + * @note blocking is a transmission is already ongoing + */ +bool sensor_forumslader_transmit(const char* message);