Occasionally the broadcast data read from the peripheral is incorrect.
The same sketch running on an UNO R4 WiFi always reads the data correctly. The only differences between the sketches is the libraries and code used for the different LCDs.
The data is being read from a Victron BMV-700 battery monitor. This broadcasts an identifier, a message counter and the encrypted data over BLE. There are multiple transmissions per second but the message counter, which increments every time the data has been refreshed, only changes about once per second.
The received data is decrypted for display and is checked to see if the any of the values just read are outside of the saved minimum or maximum for that value. If it is, the value just read is saved as the new minimum or maximum. The photo below shows the invalid values (circled in red) displayed on the Giga Display Shield with the correct values (circled in green) shown on the UNO R4 WiFi LCD.
For testing the encrypted data is also logged through the serial port. This shows that the encrypted data read by the Giga is different to the encrypted data read by the UNO R4 WiFi. This indicates that the error is not occurring during the decryption. The WinMerge screen shot below shows that 2 bytes of data are different in message 09BD.
It also shows that the Giga is not receiving all the data being broadcast. On the photo above it can be seen that the RSSI shown on the Giga is -83dBm while the R4 shows -69dBm. The device being read is about 10 meters from the Arduinos.
The Bluetooth code is based on the ArduinoBLE - Central - ScanCallback example.
Here is the main sketch for the Giga:
/*Victron Display
Display the data read from a Victron BMV-700 using bluetooth on an LCD.
*/
/*******************************************************************************
SCREEN RELATED
Standard fonts are 6 pixels wide by 8 pixels high.
*******************************************************************************/
//Screen size constants
/*******************************************************************************
Values for PICSimlab and XC4630: 320 x 240
#include <Adafruit_GFX.h>
#include <MCUFRIEND_kbv.h>
MCUFRIEND_kbv tft;
const int scnW = 320;
const int scnH = 240;
const int MESSAGE_X_POS = 0;
const int MESSAGE_Y_POS = 220;
const int MESSAGE_FS = 2;
*******************************************************************************/
/*******************************************************************************
Values for Giga Display Shield: 800 x 480
*******************************************************************************/
#include <Arduino_GigaDisplay_GFX.h>
#include <Arduino_GigaDisplay.h>
GigaDisplay_GFX tft;
const int scnW = 800;
const int scnH = 480;
const int MESSAGE_X_POS = 0;
const int MESSAGE_Y_POS = 440;
const int MESSAGE_FS = 4;
/*******************************************************************************
Values for Adafruit P2050: 480 x 320
const int scnW = 480;
const int scnH = 320;
*******************************************************************************/
// Assign human-readable names to some common 16-bit color values:
#define BLACK 0x0000
#define BLUE 0x001F
#define RED 0xF800
#define GREEN 0x07E0
#define CYAN 0x07FF
#define MAGENTA 0xF81F
#define YELLOW 0xFFE0
#define ORANGE 0xFD20
#define WHITE 0xFFFF
#define GREY 0x8410
//Define constants for item colours
const int VICTRON_COLOUR = 0x051B;
//Screen constants
#define scnNONE 0
#define scnVICTRONHISTORYDETAILS 16
//The current screen displayed, defaults to none (0)
int curScreen = scnNONE;
//Time variables used for display
int timeSecond;
int timeMinute;
int timeHour;
//Timestamp variable for daily maximum and minimum values
long secondsToday = 0;
//Variables for daily maximum and minimum values and timestamps
float batterySoc_Vmax = 0;
long batterySoc_VmaxT = 0;
float batterySoc_Vmin = 99;
long batterySoc_VminT = 0;
float batteryV_Vmax = 0;
long batteryV_VmaxT = 0;
float batteryV_Vmin = 99;
long batteryV_VminT = 0;
float batteryA_Vmax = -99;
long batteryA_VmaxT = 0;
float batteryA_Vmin = 99;
long batteryA_VminT = 0;
float batteryAh_Vmax = -99;
long batteryAh_VmaxT = 0;
float batteryAh_Vmin = 99;
long batteryAh_VminT = 0;
//For measuring time between samples
unsigned long lastUpdate;
#define SAMPLET 1000
//Includes for BMV700.ino
#include <ArduinoBLE.h>
const String BMVNAME = "BMV-700";
//Values read from the Victron Battery Monitor
float batterySoc_V;
float batteryV_V;
float batteryA_V;
float batteryAh_V;
int rssi_V;
int iCount = 0;
String tempString;
const int TEMP_STRING_LENGTH = 200;
String textToDisplay;
String textToLCD;
const int MAX_TEXT_LENGTH = 40;
void setup() {
Serial.begin(115200);
//Reserve space for global strings
tempString.reserve(TEMP_STRING_LENGTH);
textToDisplay.reserve(MAX_TEXT_LENGTH);
textToLCD.reserve(MAX_TEXT_LENGTH);
/*******************************************************************************
Values for PICSimlab and XC4630: 320 x 240
tft.reset();
uint16_t ID = tft.readID();
Serial.print("TFT ID = 0x");
Serial.println(ID, HEX);
//PICSimLab returns ID 0
if (ID == 0) {
tft.begin(0x9341);
tft.setRotation(3);
} else {
tft.begin(ID);
tft.setRotation(1);
}
tft.setTextSize(1);
*******************************************************************************/
/*******************************************************************************
Values for Giga Display Shield: 800 x 480
*******************************************************************************/
tft.begin();
tft.setRotation(1);
tft.setTextSize(3);
tft.fillScreen(BLACK);
tft.setTextColor(WHITE);
//Print sketch information, from: https://forum.arduino.cc/t/sketch-name-as-variable/245271/5?u=dl144
Serial.println(F("Sketch: " __FILE__ ", " __DATE__ ", " __TIME__));
tft.println("Sketch: ");
tft.println(__FILE__);
tft.println();
tft.print("Compiled: ");
tft.print(__DATE__);
tft.print(", ");
tft.println(__TIME__);
tft.println();
Serial.println(F("Victron BMV-700 BLE test"));
tft.println(F("Victron BMV-700 BLE test"));
// begin initialization
if (!BLE.begin()) {
Serial.println("Starting Bluetooth® Low Energy module failed!");
tft.setTextColor(RED);
tft.println("Starting Bluetooth® Low Energy module failed!");
while (1);
}
Serial.println("Bluetooth® Low Energy Central scan callback");
tft.println("Bluetooth® Low Energy Central scan callback");
// set the discovered event handle
BLE.setEventHandler(BLEDiscovered, bleCentralDiscoverHandler);
BLE.scanForName(BMVNAME, true);
tft.print(F("Scanning for: "));
tft.println(BMVNAME);
delay(2000);
//Turn off text wrapping
tft.setTextWrap(false);
}
void loop() {
getTime();
getBMV700Data();
victronScreenHistoryDetails();
}
void getTime() {
//Create timestamp value
secondsToday = millis() / 1000;
//Convert time in seconds to hours, minutes and seconds
timeHour = secondsToday / 3600;
timeMinute = (secondsToday / 60) % 60;
timeSecond = secondsToday % 60;
}
The sketch for the UNO R4 WiFi is the same with only the code for the XC4630 LCD uncommented and the code for the Giga Display Shield commented out.
Here is the code that checks and processes the Bluetooth data:
/*Victron BMV BLE Monitor
Uses the Bluetooth BLE Product Advertisement beacon to read data from
the Victron BMV-700 battery monitor.
The data is stored in the Victron Manufacturer Data record type 0x10.
*/
#include <Crypto.h>
#include <AES.h>
#include <CTR.h>
//key[16] contains 16 byte key(128 bit) for encryption
byte key[16]={0xFF, 0xCD, 0x25, 0x03, 0x5B, 0xFC, 0x6F, 0x84, 0xB7, 0xBD, 0x23, 0xD6, 0x8A, 0x37, 0x11, 0x21};
//encryptedText[16] contains the encrypted data from the BMV-700
byte encryptedText[16];
//decryptedText[16] stores the text after decryption
byte decryptedText[16];
//Create an object of AES128-CTR class
CTR<AES128> ctraes128;
//This is where the encrypted data starts in the Manufacturers Data string
const int DATA_OFFSET = 10;
const int SCANDELAY = 300;
//bmvManufacturerData is the 25 byte data array the data will get copied into
int const bmvDataLen = 25;
uint8_t bmvManufacturerData[bmvDataLen] = {};
unsigned long bleLastScan = 0;
uint8_t lastCounter;
void getBMV700Data() {
if (millis() - bleLastScan > SCANDELAY) {
bleLastScan = millis();
BLE.poll();
}
} //void getBMV700Data()
void bleCentralDiscoverHandler(BLEDevice peripheral) {
rssi_V = peripheral.rssi();
peripheral.manufacturerData(bmvManufacturerData, bmvDataLen);
//Check for updated data
if (bmvManufacturerData[7] != lastCounter) {
//Decrypt the data
printData();
decryptData();
//Save the values encoded in the decrypted data
convertData();
lastCounter = bmvManufacturerData[7];
iCount = lastCounter;
} //if (bmvManufacturerData[7] != lastCounter)
} //void bleCentralDiscoverHandler(BLEDevice peripheral)()
void decryptData() {
//Extract the required information from the Maunufacturers Data and decrypt it
//Get IV value
uint8_t nonce_counter[16] = {bmvManufacturerData[7], bmvManufacturerData[8], 0};
ctraes128.setIV(nonce_counter, 16);
//Set Key for AES
ctraes128.setKey(key, 16);
//Copy the encrypted data from the BMV-700
for (int x = 0; x < 15; x++) {
encryptedText[x] = bmvManufacturerData[x + DATA_OFFSET];
}
//Add a 16th byte as the BMV-700 only provides 15 bytes of data
// encryptedText[15] = 0xFF;
//Decrypt the data from the BMV-700
// ctraes128.decrypt(decryptedText, encryptedText, 16);
ctraes128.decrypt(decryptedText, encryptedText, 15);
}
void convertData() {
//Extract the values encoded in the decrypted string
bool neg;
//Battery Voltage conversion
neg = (decryptedText[3] & 0x80) >> 7; // extract sign bit for signed int
int32_t batt_mV10 = ((decryptedText[3] & 0x7F) << 8) | decryptedText[2]; // exclude sign bit from byte 3
if (neg) batt_mV10 = batt_mV10 - 32768; // 2's complement = val - 2^(b-1) b = bit# = 16
float fbatteryVoltage = batt_mV10 / 100.0;
//Battery Current conversion
neg = (decryptedText[10] & 0x80) >> 7; // bit 21
int32_t mA = ((decryptedText[8] & 0xFC) >> 2) + ((decryptedText[9] & 0x03) << 6) | // bits 0 - 7
(((decryptedText[9] & 0xFC) >> 2) + ((decryptedText[10] & 0x03) << 6) << 8) | // bits 8 - 15
(((decryptedText[10] & 0x7C) >> 2) << 16); // bits 16 - 20
if (neg) mA = mA - 2097152; // 2's complement = val - 2^(b-1) where b = bits = 22
float fbatteryCurrent = mA / 1000.0;
//Consumed Ah conversion
uint32_t mAh100 = decryptedText[11] | // bits 0 - 7
(decryptedText[12] << 8) | // bits 8 - 15
((decryptedText[13] & 0x0F) << 16); // bits 16 - 19
float fconsumedAh = mAh100 / 10.0;
//State of Charge conversion
uint16_t soc01 = ((decryptedText[13] & 0xF0) >> 4) | // bits 0 - 3
((decryptedText[14] & 0x0F) << 4) | // bits 4 - 7
((decryptedText[14] & 0x30) << 4); // bits 8 - 9
float fsOC = soc01 / 10.0;
//Copy the converted values to the global variables for display
batteryV_V = fbatteryVoltage;
batteryA_V = fbatteryCurrent;
batteryAh_V = fconsumedAh * - 1.0;
batterySoc_V = fsOC;
//Check for new maxima and minima
if (batterySoc_V > batterySoc_Vmax) {
batterySoc_Vmax = batterySoc_V;
batterySoc_VmaxT = secondsToday;
}
if (batterySoc_V < batterySoc_Vmin) {
batterySoc_Vmin = batterySoc_V;
batterySoc_VminT = secondsToday;
}
if (batteryV_V > batteryV_Vmax) {
batteryV_Vmax = batteryV_V;
batteryV_VmaxT = secondsToday;
}
if (batteryV_V < batteryV_Vmin) {
batteryV_Vmin = batteryV_V;
batteryV_VminT = secondsToday;
}
if (batteryA_V > batteryA_Vmax) {
batteryA_Vmax = batteryA_V;
batteryA_VmaxT = secondsToday;
}
if (batteryA_V < batteryA_Vmin) {
batteryA_Vmin = batteryA_V;
batteryA_VminT = secondsToday;
}
if (batteryAh_V > batteryAh_Vmax) {
batteryAh_Vmax = batteryAh_V;
batteryAh_VmaxT = secondsToday;
}
if (batteryAh_V < batteryAh_Vmin) {
batteryAh_Vmin = batteryAh_V;
batteryAh_VminT = secondsToday;
}
}
void printData() {
//Print the counter value
for (int x = 8; x > 6; x--) {
if (bmvManufacturerData[x] < 0x10) {
Serial.print("0");
}
Serial.print(bmvManufacturerData[x], HEX);
}
//Print the encrypted data
Serial.print(" Enc: ");
for (int x = 0; x < bmvDataLen; x++) {
if (bmvManufacturerData[x] < 0x10) {
Serial.print("0x0");
} else {
Serial.print("0x");
}
Serial.print(bmvManufacturerData[x], HEX);
Serial.print(",");
}
Serial.println();
}
The tests were done using IDE 2.3.6, Giga Board 4.3.1, UNO R4 Board 1.4.1 and ArduinoBLE 1.4.0.
Any ideas why the Bluetooth data read by the Giga is sometimes incorrect?
thanks,
Michael