AP_WindVane: add Arduino script and readme to allow conection to Bluetooth wind-vane

This commit is contained in:
Iampete1 2022-12-18 21:51:58 +00:00 committed by Andrew Tridgell
parent a3812d7632
commit 34d2a5acaa
5 changed files with 271 additions and 0 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 KiB

View File

@ -0,0 +1,214 @@
#include <bluefruit.h>
// This code uses the bluefruit libary52 from Adafruit
// Documentation here: https://learn.adafruit.com/bluefruit-nrf52-feather-learning-guide/bluefruit-nrf52-api
// Should work with any nrf52 Bluetooth Arduino, tested with Seeed XIAO nRF52840 Sense
// Wind vane address, found with mobile app
#define WIND_VANE_ADDRESS 0xDDB7BEB45638
// Define services and charicteristics to moniter
BLEClientService env_sense(0x181A); // Environmental Sensing
BLEClientCharacteristic wind_speed(0x2A72); // Apparent Wind Speed
BLEClientCharacteristic wind_dir(0x2A73); // Apparent Wind Direcetion
BLEClientService bat_sense(0x180F); // Battery Sensing
BLEClientCharacteristic bat_level(0x2A19); // Battery level
ble_gap_addr_t addr;
float speed;
float direction;
uint8_t battery_level;
bool speed_new;
bool direction_new;
bool battery_new;
uint32_t last_connect_attempt;
#define RECONNECT_TIME_MS 1000
#define NMEA_BAUD 57600
#define LOW_BAT_BLINK_MS 200
void setup()
{
// Disconected
digitalWrite(LED_RED, LOW);
uint64_t address = WIND_VANE_ADDRESS;
memcpy(addr.addr, &address, BLE_GAP_ADDR_LEN);
addr.addr_type = BLE_GAP_ADDR_TYPE_RANDOM_STATIC;
// Extra info and debugging on USB, NMEA on serial 1
Serial.begin(115200);
Serial1.begin(NMEA_BAUD);
Serial.println("ArduPilot Bluetooth windvane to NMEA translator");
Bluefruit.begin(0, 1);
Bluefruit.setName("ArduPilot Bluetooth");
// Initialize services client
env_sense.begin();
bat_sense.begin();
// Set notify callbacks
wind_speed.setNotifyCallback(speed_notify_callback);
wind_dir.setNotifyCallback(direction_notify_callback);
bat_level.setNotifyCallback(bat_notify_callback);
// Initalize Caricterstics
wind_speed.begin(&env_sense);
wind_dir.begin(&env_sense);
bat_level.begin(&bat_sense);
// Turn of LEDs, we do our own
Bluefruit.autoConnLed(false);
// Callbacks for Central
Bluefruit.Central.setDisconnectCallback(disconnect_callback);
Bluefruit.Central.setConnectCallback(connect_callback);
// Connect to known address
Bluefruit.Central.connect(&addr);
last_connect_attempt = millis();
}
bool low_battery;
uint32_t last_low_battery_blink;
char NMEA_buffer[50];
void loop()
{
if (!Bluefruit.connected(0)) {
// Disconected
digitalWrite(LED_RED, LOW);
digitalWrite(LED_GREEN, HIGH);
const uint32_t now = millis();
if (now - last_connect_attempt > RECONNECT_TIME_MS) {
// try and re-connect
Bluefruit.Central.connect(&addr);
last_connect_attempt = now;
}
return;
}
// Connected, show green LED
digitalWrite(LED_GREEN, LOW);
// flash red LED for low battery
if (low_battery) {
const uint32_t now = millis();
if (now - last_low_battery_blink > LOW_BAT_BLINK_MS) {
digitalWrite(LED_RED, !digitalRead(LED_RED));
last_low_battery_blink = now;
}
} else {
digitalWrite(LED_RED, HIGH);
}
if (speed_new && direction_new) {
speed_new = false;
direction_new = false;
Serial.printf("Wind Speed: %0.2f (m/s), Direction: %0.2f (deg), Battery: %u%%\n", speed, direction, battery_level);
sprintf(NMEA_buffer, "APMWV,%0.2f,R,%0.2f,M,A", direction, speed);
Serial1.printf("$%s*%02X\n",NMEA_buffer, NMEA_checksum(NMEA_buffer));
}
if (battery_new) {
battery_new = false;
sprintf(NMEA_buffer, "APXDR,G,%u,%%,BT", battery_level);
Serial1.printf("$%s*%02X\n",NMEA_buffer, NMEA_checksum(NMEA_buffer));
if (battery_level < 25) {
// Windvane enters low power mode at less than 20% battery
low_battery = true;
} else if (battery_level > 30) {
low_battery = false;
}
}
}
uint8_t NMEA_checksum(const char* NMEA_string)
{
uint8_t checksum = 0;
for (uint8_t i = 0; i < strlen(NMEA_string); i++) {
checksum ^= NMEA_string[i];
}
return checksum;
}
void connect_callback(uint16_t conn_handle)
{
Serial.println("Connected");
Serial.print("Discovering Environmental characteristics ... ");
if (!env_sense.discover(conn_handle) || !wind_speed.discover() || !wind_dir.discover()) {
Serial.println("Failed");
Bluefruit.disconnect(conn_handle);
return;
}
Serial.println("Found");
Serial.print("Discovering Battery characteristics ... ");
if (!bat_sense.discover(conn_handle) || !bat_level.discover()) {
Serial.println("failed");
Bluefruit.disconnect(conn_handle);
return;
}
Serial.println("Found");
if (wind_speed.enableNotify() && wind_dir.enableNotify() && bat_level.enableNotify()) {
Serial.println("Ready to receive");
} else {
Serial.println("Couldn't enable notify. Increase DEBUG LEVEL for troubleshooting");
}
}
void disconnect_callback(uint16_t conn_handle, uint8_t reason)
{
(void) conn_handle;
speed_new = false;
direction_new = false;
battery_new = false;
Serial.print("Disconnected, reason = 0x"); Serial.println(reason, HEX);
}
void speed_notify_callback(BLEClientCharacteristic* chr, uint8_t* data, uint16_t len)
{
if (len == 2) {
uint16_t value;
memcpy(&value, data, 2);
speed = value * 0.01;
speed_new = true;
}
}
void direction_notify_callback(BLEClientCharacteristic* chr, uint8_t* data, uint16_t len)
{
if (len == 2) {
uint16_t value;
memcpy(&value, data, 2);
direction = value * 0.01;
direction_new = true;
}
}
void bat_notify_callback(BLEClientCharacteristic* chr, uint8_t* data, uint16_t len)
{
if (len == 1) {
battery_level = data[0];
battery_new = true;
}
}

View File

@ -0,0 +1,57 @@
# Bluetooth to NMEA receiver
This script can be used with a Arduino compatible nRF52 Bluetooth module to receive wind readings from a CALYPSO Ultrasonic Portable Mini wind meter and output NMEA 0183 for use with ArduPilot. Such a sensor is smaller and lighter than typical sensors and removes the need to run cabling to the top of the mast.
## Hardware setup
Any Arduino compatible nRF52 Bluetooth board should work, this guide is written for a Seeed XIAO nRF52840 Sense. Three connections need to be made between the flight controller and XIAO. The image below shows the connections from both the top and bottom of the XIAO. 5v (Red), ground (black) and TX (green) to a serial port RX pin on the flight controller.
![XIAO connections](XIAO_connection_diagram.png)
## Script setup
Follow the instructions from the manufacturer to setup the Arduino envoroment for your board. Install the [Adafruit nRF52](https://github.com/adafruit/Adafruit_nRF52_Arduino/tree/master/libraries/Bluefruit52Lib) library as outlined here: https://github.com/adafruit/Adafruit_nRF52_Arduino#bsp-installation.
Use a smartphone and blue tooth tool to find the adress of your windvane. On Android [BLE Scanner](https://play.google.com/store/apps/details?id=com.macdom.ble.blescanner&hl=en_GB&gl=US) can be used, although there are many equivalent apps.
Locate the wind-vane and note down its address, in this case `DD:B7:BE:B4:56:38`.
![Wind-vane address](BLE_Scanner.jpg)
Take your the address and replace the one at the top of the script, removing colons and pre-fixing `0x` to denote a hexadecimal value. For example:
```
#define WIND_VANE_ADDRESS 0xDDB7BEB45638
```
Save the script and flash it to your nRF52 board. The LED will be red if no connection can be made it will go green once a connection has been established, this can take a few seconds. If the green LED is solid and the red LED flashing it is connected but the wind-vane has less than 25% battery remaining, it will go into low power mode once it reaches 20%.
Note! The wind-vane can only accept one connection at a time, if you cann't connect make sure nothing else is connected to the wind-vane.
## ArduPilot setup
The Arduino script is setup to output NMEA 0183 messages as they are already supported by ArduPilot.
Enable NMEA wind-vane with:
```
WNDVN_TYPE 4
WNDVN_SPEED_TYPE 4
```
Setup the serial port you have connected the nRF52 to with:
```
SERIALx_PROTOCOL 21
SERIALx_BAUD 57
```
After a re-boot you should see wind messages reported to the GCS. For example `MAV_APPWNDDIR` and `MAV_APPWNDSPD` can be selected in mission planner to display the apparent wind direction and speed.
![Mission Planner wind speed and direction](Mission_Planner_Quick.PNG)
## Future work
The script does send back the battery level of the wind-vane formatted as a NMEA `XDR` message. Some ArduPilot changes are required to allow this value to be parsed and displayed.

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB