mirror of https://github.com/ArduPilot/ardupilot
215 lines
5.4 KiB
C++
215 lines
5.4 KiB
C++
#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;
|
|
}
|
|
}
|