I am using an Argon with OS 6.4.1 to connect via Bluetooth to a 3rd party device (Shelly relay). It all works great as long as the Shelly device runs pre-2.0 firmware.
Shelly added the requirement for Bluetooth pairing in their firmware 2.0 release, and I am having problems getting the Argon to fully pair with it.
I have verified that my phone (using an app called BLE Scanner) can easily pair and communicate with the Shelly, so it definitely works.
Using the Particle example code for LESC simple pairing (Just Works) from the Particle Docs, I see the following on the serial log:
0000013686 [app] INFO: about to connect
0000014441 [hal.ble] ERROR: Not a proven connection!!!
0000014441 [app] INFO: failed to connect, retrying
0000019442 [app] INFO: about to connect
0000020945 [app] INFO: onPairingEvent REQUEST_RECEIVED
0000022607 [app] INFO: onPairingEvent STATUS_UPDATED status=0 lesc=1 bonded=0
0000025037 [app] INFO: Connected, starting pairing...
0000027827 [hal.ble] ERROR: BLE read characteristic failed: 264, handle: 11.
0000027828 [app] INFO: RX LengthA 60
0000027828 [app] INFO: sent counter=0
0000029357 [hal.ble] ERROR: BLE read characteristic failed: 264, handle: 11.
0000029358 [app] INFO: RX LengthA 60
0000029359 [app] INFO: sent counter=1
0000031697 [hal.ble] ERROR: BLE read characteristic failed: 264, handle: 11.
0000031697 [app] INFO: RX LengthA 60
0000031698 [app] INFO: sent counter=2
0000033632 [hal.ble] ERROR: BLE read characteristic failed: 264, handle: 11.
0000033632 [app] INFO: RX LengthA 60
0000033633 [app] INFO: sent counter=3
0000035522 [hal.ble] ERROR: BLE read characteristic failed: 264, handle: 11.
0000035522 [app] INFO: RX LengthA 60
0000035523 [app] INFO: sent counter=4
On the Shelly phone app I see that a device (i.e. the Argon) connects BT, but the Shelly app never shows it being paired. Not pairing is also confirmed above by not being able to write a message to to the Shelly. (BTW if I run the same code on a pre-2.0 Shelly it works.)
Am i missing something in the pairing process in my code?
// Include Particle Device OS APIs
#include "Particle.h"
SYSTEM_THREAD(ENABLED);
SerialLogHandler logHandler;
void onPairingEvent(const BlePairingEvent& event, void* context);
void onDataReceived(const uint8_t* data, size_t len, const BlePeerDevice& peer, void* context);
void scanResultCallback(const BleScanResult *scanResult, void *context);
void stateConnect();
void stateRun();
BleAddress ShellyAddress = "ecda3bc249de"; //FAN - requires pairing LESC
//BleAddress ShellyAddress = "c049ef8ca546"; //SPARE - does not require pairing
//**************************************************
// Initialize the UUIDS for the Shelly Device switch
//**************************************************
BleUuid uuidSERVICE ("5f6D4F53-5F52-5043-5F53-56435F49445F");
BleUuid uuidRW ("5F6D4F53-5F52-5043-5F64-6174615F5F5F");
BleUuid uuidREAD_NOTIFY ("5F6D4F53-5F52-5043-5F72-785F63746C5F");
BleUuid uuidW ("5F6D4F53-5F52-5043-5F74-785F63746C5F");
//*****************************************************
//Below used for when we are a peripheral for the Boron
//*****************************************************
BleCharacteristic txCharacteristic (NULL, BleCharacteristicProperty::READ | BleCharacteristicProperty::NOTIFY | BleCharacteristicProperty::INDICATE, uuidREAD_NOTIFY, uuidSERVICE);
BleCharacteristic txrxCharacteristic(NULL, BleCharacteristicProperty::WRITE | BleCharacteristicProperty::READ, uuidRW, uuidSERVICE, onDataReceived, NULL);
BleCharacteristic rxCharacteristic (NULL, BleCharacteristicProperty::WRITE, uuidW, uuidSERVICE);
BleCharacteristic peerTxCharacteristic;
BleCharacteristic peerRxCharacteristic;
BleCharacteristic peerRxTxCharacteristic;
const char setswStatus[] = "{\"id\":123,\"method\":\"Switch.Set\",\"params\":{\"id\":0,\"on\":true}}";
BlePeerDevice peer;
BleCharacteristic counterCharacteristic;
enum class State {
SCAN,
CONNECT,
RUN,
WAIT
};
State state = State::CONNECT; //Don't scn - we know address for Shelly
unsigned long stateTime;
BleAddress serverAddr;
int counter = 0;
void setup() {
waitFor(Serial.isConnected, 10000);
BLE.on();
BLE.setPairingIoCaps(BlePairingIoCaps::DISPLAY_YESNO);
BLE.setPairingAlgorithm(BlePairingAlgorithm::LESC_ONLY);
BLE.onPairingEvent(onPairingEvent);
}
void loop() {
switch(state) {
case State::SCAN:
state = State::WAIT;
//BLE.scan(scanResultCallback, NULL);
break;
case State::CONNECT:
stateConnect();
break;
case State::RUN:
stateRun();
break;
case State::WAIT:
if (millis() - stateTime >= 5000) {
state = State::CONNECT;
}
break;
}
}
void onPairingEvent(const BlePairingEvent& event, void* context) {
if (event.type == BlePairingEventType::REQUEST_RECEIVED) {
Log.info("onPairingEvent REQUEST_RECEIVED");
}
else
if (event.type == BlePairingEventType::PASSKEY_DISPLAY) {
char passKeyStr[BLE_PAIRING_PASSKEY_LEN + 1];
memcpy(passKeyStr, event.payload.passkey, BLE_PAIRING_PASSKEY_LEN);
passKeyStr[BLE_PAIRING_PASSKEY_LEN] = 0;
Log.info("onPairingEvent PASSKEY_DISPLAY %s", passKeyStr);
}
else
if (event.type == BlePairingEventType::STATUS_UPDATED) {
Log.info("onPairingEvent STATUS_UPDATED status=%d lesc=%d bonded=%d",
event.payload.status.status,
(int)event.payload.status.lesc,
(int)event.payload.status.bonded);
}
else
if (event.type == BlePairingEventType::NUMERIC_COMPARISON) {
Log.info("onPairingEvent NUMERIC_COMPARISON");
}
}
void onDataReceived(const uint8_t* data, size_t len, const BlePeerDevice& peer, void* context) {
Log.info("dataReceived");
}
void stateConnect() {
Log.info("about to connect");
peer = BLE.connect(ShellyAddress);
if (!peer.connected()) {
Log.info("failed to connect, retrying");
state = State::WAIT;
stateTime = millis();
return;
}
Log.info("Connected, starting pairing...");
peer.getCharacteristicByUUID(peerTxCharacteristic, uuidW);
peer.getCharacteristicByUUID(peerRxCharacteristic, uuidRW);
peer.getCharacteristicByUUID(peerRxTxCharacteristic, uuidREAD_NOTIFY);
//I've tried with and without this - no difference
//The Shelly is in pairing mode when I start the Argon
//BLE.startPairing(peer);
state = State::RUN;
stateTime = millis();
}
void stateRun() {
if (!peer.connected()) {
// Server disconnected
state = State::WAIT;
stateTime = millis();
return;
}
if (millis() - stateTime < 2000) {
return;
}
if (BLE.isPairing(peer)) {
// Still in process of doing LESC secure pairing
Log.info("Still pairing...");
return;
}
stateTime = millis();
WriteReadBLE(setswStatus); //Turn shelly relay on
Log.info("sent counter=%d", counter);
counter++;
}
int WriteReadBLE(const char * msg)
{
int ret = 1; //Assume success
//*****************************
//If we've disconnected get out
//*****************************
if (!peer.connected())
{
return 0;
}
int len = TxBLEpacket(peerTxCharacteristic, peerRxCharacteristic, peerRxTxCharacteristic, msg);
Log.info("RX LengthA %d",len);
if (len < 1)
{
ret = 0; //Problem
Log.info("TX Problem");
}
return ret;
}
//******************************
//Write a message out a BLE link
//******************************
int TxBLEpacket(BleCharacteristic &pTx, BleCharacteristic &pRx, BleCharacteristic &pRxTx, const char * txbuf)
{
int len = strlen(txbuf);
//*******************************
//Set correct endianess of length
//*******************************
len = change_endian( (uint8_t *) &len);
//*******************************************
//Send the length of the packet we're sending
//*******************************************
pTx.setValue((uint8_t *)&len,4);
//***********************
//Send the actual message
//***********************
pRx.setValue((uint8_t *)txbuf,strlen(txbuf));
//**********************
//Get length of response
//**********************
pRxTx.getValue((uint8_t *)&len,4);
//****************************************
//Set correct endianess of returned length
//****************************************
len = change_endian( (uint8_t *) &len);
//**********************************************************************
//Return number of bytes that device returned to us and needs to be read
//**********************************************************************
return len;
}
//********************************
//Change endianess of passed value
//********************************
int change_endian(const uint8_t * v)
{
uint8_t res[4];
//Swap bytes
res[0] = v[3];
res[1] = v[2];
res[2] = v[1];
res[3] = v[0];
return *(int *)res;
}