add Bluetooth <-> Forumslader forwarding

This commit is contained in:
King Kévin 2017-08-16 18:17:15 +02:00
parent 6add61fd1e
commit 09af8785e9
7 changed files with 439 additions and 79 deletions

View File

@ -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

View File

@ -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

View File

@ -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; i<sizeof(data_in); i++) {
data_in[i]=0xaa;
}
uint8_t data_out[512] = {0x00};
if (!flash_sdcard_write_data(10000, data_in)) {
printf("could not write data\n");
} else if (!flash_sdcard_read_data(10000, data_out)) {
printf("could not read data\n");
} else {
bool prog_ok = true;
for (uint16_t i=0; i<sizeof(data_in) && i<sizeof(data_out); i++) {
prog_ok = (prog_ok&&(data_in[i]==data_out[i]));
}
if (prog_ok) {
printf("programming succeeded\n");
} else {
printf("programming failed\n");
}
}
*/
printf("setup Bluetooth UART: ");
radio_bluetooth_setup(); // setup USART
printf("OK\n");
printf("setup GPS: ");
radio_gps_setup();
bool gps_rtc_synced = false; // has the RTC been synced with the GPS time
printf("OK\n");
printf("setup SD card file system: ");
FATFS card_fs;
@ -462,12 +434,6 @@ void main(void)
bool char_flag = false; // a new character has been received
while (true) { // infinite loop
iwdg_reset(); // kick the dog
while (usart_received) { // data received over UART
action = true; // action has been performed
led_toggle(); // toggle LED
c = usart_getchar(); // store receive character
char_flag = true; // notify character has been received
}
while (usb_cdcacm_received) { // data received over USB
action = true; // action has been performed
led_toggle(); // toggle LED
@ -495,6 +461,9 @@ void main(void)
action = true; // action has been performed
printf("button pressed\n");
led_toggle(); // toggle LED
if (!sensor_forumslader_transmit("hello\n")) {
printf("could not transmit to forumslader\n");
}
for (uint32_t i=0; i<1000000; i++) { // wait a bit to remove noise and double trigger
__asm__("nop");
}
@ -512,43 +481,58 @@ void main(void)
printf("time: %02d:%02d:%02d\n", time_tm->tm_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
}
}
}

139
lib/radio_bluetooth.c Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
*
*/
/** library for receiving NMEA-0183 like messages over a Bluetooth module (code)
* @file radio_bluetooth.c
* @author King Kévin <kingkevin@cuvoodoo.info>
* @date 2017
* @note peripherals used: USART @ref radio_bluetooth_usart
*/
/* standard libraries */
#include <stdint.h> // standard integer types
#include <stdio.h> // standard I/O facilities
#include <stdlib.h> // general utilities
#include <string.h> // string utilities
/* STM32 (including CM3) libraries */
#include <libopencm3/stm32/rcc.h> // real-time control clock library
#include <libopencm3/stm32/gpio.h> // general purpose input output library
#include <libopencm3/stm32/usart.h> // universal synchronous asynchronous receiver transmitter library
#include <libopencm3/cm3/nvic.h> // interrupt handler
#include <libopencmsis/core_cm3.h> // 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<strlen(message); i++) { // copy message to transmit to internal buffer
tx_buffer[strlen(message)-1-i] = message[i]; // copy in reverse orders to we can count remaining characters to transmit
}
tx_buffer_i = strlen(message); // save number of characters to transmit
radio_bluetooth_transmitted = false; // reset transmit flag
usart_enable_tx_interrupt(USART(RADIO_BLUETOOTH_USART)); // enable transmit interrupt
return true;
}
/** USART interrupt service routine called when data has been received */
void USART_ISR(RADIO_BLUETOOTH_USART)(void)
{
if (usart_get_flag(USART(RADIO_BLUETOOTH_USART), USART_SR_RXNE)) { // data has been received
char c = usart_recv(USART(RADIO_BLUETOOTH_USART)); // put character in buffer (and clear flag)
// only save data if there is space in the buffer
if (rx_buffer_i<LENGTH(rx_buffer)) {
rx_buffer[rx_buffer_i++] = c;
}
if ('\n'==c) { // end of message received
// verify if the message is correctly formatted
if (rx_buffer_i<1 || '$'!=rx_buffer[0]) {
goto malformatted;
} else if (rx_buffer_i<2 || '\r'!=rx_buffer[rx_buffer_i-2]) {
goto malformatted;
}
// copy message to user
for (uint8_t i = 0; i < rx_buffer_i && i < LENGTH(radio_bluetooth_message); i++) {
radio_bluetooth_message[i] = rx_buffer[i];
}
// terminate string
if (rx_buffer_i<LENGTH(radio_bluetooth_message)) {
radio_bluetooth_message[rx_buffer_i] = '\0';
} else {
radio_bluetooth_message[LENGTH(radio_bluetooth_message)-1] = '\0';
}
radio_bluetooth_received = true; // notify user a message is available
malformatted:
rx_buffer_i = 0; // reset buffer
}
}
if (usart_get_flag(USART(RADIO_BLUETOOTH_USART), USART_SR_TXE) && tx_buffer_i) { // data has been transmitted
usart_send(USART(RADIO_BLUETOOTH_USART),tx_buffer[--tx_buffer_i]); // put data in transmit register
if (0==tx_buffer_i) { // no data in the buffer to transmit
usart_disable_tx_interrupt(USART(RADIO_BLUETOOTH_USART)); // disable transmit interrupt
radio_bluetooth_transmitted = true; // notify user
}
}
}

37
lib/radio_bluetooth.h Normal file
View File

@ -0,0 +1,37 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*/
/** library for receiving NMEA-0183 like messages over a Bluetooth module (API)
* @file radio_bluetooth.h
* @author King Kévin <kingkevin@cuvoodoo.info>
* @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);

139
lib/sensor_forumslader.c Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
*
*/
/** library for receiving forumslader NMEA-0183 like messages (code)
* @file sensor_forumslader.c
* @author King Kévin <kingkevin@cuvoodoo.info>
* @date 2017
* @note peripherals used: USART @ref sensor_forumslader_usart
*/
/* standard libraries */
#include <stdint.h> // standard integer types
#include <stdio.h> // standard I/O facilities
#include <stdlib.h> // general utilities
#include <string.h> // string utilities
/* STM32 (including CM3) libraries */
#include <libopencm3/stm32/rcc.h> // real-time control clock library
#include <libopencm3/stm32/gpio.h> // general purpose input output library
#include <libopencm3/stm32/usart.h> // universal synchronous asynchronous receiver transmitter library
#include <libopencm3/cm3/nvic.h> // interrupt handler
#include <libopencmsis/core_cm3.h> // 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<strlen(message); i++) { // copy message to transmit to internal buffer
tx_buffer[strlen(message)-1-i] = message[i]; // copy in reverse orders to we can count remaining characters to transmit
}
tx_buffer_i = strlen(message); // save number of characters to transmit
sensor_forumslader_transmitted = false; // reset transmit flag
usart_enable_tx_interrupt(USART(SENSOR_FORUMSLADER_USART)); // enable transmit interrupt
return true;
}
/** USART interrupt service routine called when data has been received */
void USART_ISR(SENSOR_FORUMSLADER_USART)(void)
{
if (usart_get_flag(USART(SENSOR_FORUMSLADER_USART), USART_SR_RXNE)) { // data has been received
char c = usart_recv(USART(SENSOR_FORUMSLADER_USART)); // put character in buffer (and clear flag)
// only save data if there is space in the buffer
if (rx_buffer_i<LENGTH(rx_buffer)) {
rx_buffer[rx_buffer_i++] = c;
}
if ('\n'==c) { // end of message received
// verify if the message is correctly formatted
if (rx_buffer_i<1 || '$'!=rx_buffer[0]) {
goto malformatted;
} else if (rx_buffer_i<3 || ';'!=rx_buffer[rx_buffer_i-3] || '\r'!=rx_buffer[rx_buffer_i-2]) {
goto malformatted;
}
// copy message to user
for (uint8_t i = 0; i < rx_buffer_i && i < LENGTH(sensor_forumslader_message); i++) {
sensor_forumslader_message[i] = rx_buffer[i];
}
// terminate string
if (rx_buffer_i<LENGTH(sensor_forumslader_message)) {
sensor_forumslader_message[rx_buffer_i] = '\0';
} else {
sensor_forumslader_message[LENGTH(sensor_forumslader_message)-1] = '\0';
}
sensor_forumslader_received = true; // notify user a message is available
malformatted:
rx_buffer_i = 0; // reset buffer
}
}
if (usart_get_flag(USART(SENSOR_FORUMSLADER_USART), USART_SR_TXE) && tx_buffer_i) { // data has been transmitted
usart_send(USART(SENSOR_FORUMSLADER_USART),tx_buffer[--tx_buffer_i]); // put data in transmit register
if (0==tx_buffer_i) { // no data in the buffer to transmit
usart_disable_tx_interrupt(USART(SENSOR_FORUMSLADER_USART)); // disable transmit interrupt
sensor_forumslader_transmitted = true; // notify user
}
}
}

37
lib/sensor_forumslader.h Normal file
View File

@ -0,0 +1,37 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*/
/** library for receiving forumslader NMEA-0183 like messages (API)
* @file sensor_forumslader.h
* @author King Kévin <kingkevin@cuvoodoo.info>
* @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);