The iOS app I am writing uses BLE to communicate with an Arduino. The "robustness" of the connection is critical to the app and, of course, many things can cause interference and disconnects between two BLE devices. I am trying to develop a simple ACK/NAK "protocol" between the Arduino and the iOS device (iPad in my case) so that if the iOS device DOES NOT receive an ACK from the Arduino (literally the word "ACK"), then it tries to re-initialize the BLE connection.
It is THAT re-initialization that I cannot seem to get working.
The BLE card works PERFECTLY when initially opened from the very beginning of the app. It connects to the BLE device and the iPad receives ACK's from the Arduino every time I write to the BLE device (which is an Adafruit Bluefruit SPI LE Friend)... in fact it does it so well, I have to force the Livecode script code to "think" it didn't receive an ACK. BUT...
The BLE Card is the first card after the stack is opened. Once the BLE card script reaches a certain point... I perform a "go to anycaster_card1" (my main user-interactive card) within the BLE Card handlers ... specifically at the successful processing of the "mergBLEPeripheralDidDiscoverCharacteristicsForService" handler within the BLE Card. This, in turn, causes a "closeCard" to be sent to the BLE card which has the following handler:
Code: Select all
on closeCard
mergBLEStopScanningForPeripherals
mergBLEStopAdvertising
mergBLEDeleteService fld "service" of card "BLE_Card"
end closeCard
Actually, I am somewhat surprised that the "closeCard" processing doesn't drop the BLE connection, itself... but it doesn't. The BLE Adafruit module has a "connection" LED which lights when a BLE connection has been established. It comes on when the iOS script initially calls the BLE Card script and goes out when I force the disconnect with "mergBLECancelConnectionToPeripheral tPeripheral" from my main card ... and DOES NOT come back on when I try the "go to BLE_Card" as I simulate the BLE disconnect.... and no communication occurs again... until I stop the app altogether, return to the iPAD "icon screen" (not sure what the accepted name for this "desktop" is on an iOS device)... and then re-select my app again.
Upon calling "go to BLE_Card" after issuing the "mergBLECancelConnectionToPeripheral tPeripheral" command, the BLE card handler "on mergBLEDidDisconnectPeripheral pUUID" does get triggered and the "pUUID" variable matches the "tPeripheral" value used to stop the connection. I don't know if that's relevant or not.
I would TRULY appreciate your assistance on this matter. Call it a gut feeling but I get the impression that somehow something gets "struck" in the mergBLE implementation and once a connection is established, it CANNOT be re-established without removing the ENTIRE stack from memory.. by stopping the app and returning to the iOS icon page (or whatever it's called... dunno) and then calling the app again.
I am including the whole BLE_Card script below.
Cheers
Doug
NOTE** Most of this code is directly from the mergBLE example included in the LC 8 package... the original fields from that example have been left but made invisible on my version of the BLE Card to try to ensure I didn't "break" the original script. Most of the variables are used elsewhere .. I tend to "overuse" globals (for historic coding reasons) and copy all variable setups to all of the cards and most objects within my app... just my "development" methodology.
You will see several "answer" commands commented out. I've used those to check the BLE card's functions since mergBLE ONLY runs under iOS, I do not have the wealth of the LC debug features to assist me.
Code: Select all
global blinkID, BLEInit, BLESrc
global currentObj
global Dry
global isLower, isOpen, isUpper
global lowerPos
global maxFreq, maxQ, maxVol, maxWetDry, minFreq, MinMax, minQ, minVol, minWetDry, ModeStr, ModeVal
global preButton, Preset, PresetButton, PresetFile, PresetLine
global rectID
global saveID, sCentralA, skipDrag, skipLimit, skipLine, skipStop, skipUp, sPeripheralA, stackName, stopBlink, startBLE
global UpDn, upperPos
global vborderRef
global waitDelay, wborderRef, Wet, wetdry, WetDryValue
local altheConnections
local b, baseItem, buttonID
local c, calv, calx
local d, diffVal
local e
local f
local g
local h
local i
local makeInt
local rectPos, resultVal
local temp1, temp2, temp3, temp, thisMode, thisModeVal
local v
local w
local x
local y
local z
on openCard
if the platform <> "iphone" then
go to card "anycaster_card1"
end if
put empty into field "central state" of card "BLE_Card"
put empty into field "peripheral state" of card "BLE_Card"
put empty into field "service" of card "BLE_Card"
put empty into field "writable" of card "BLE_Card"
put empty into field "notify" of card "BLE_Card"
put empty into field "connections" of card "BLE_Card"
//removed error trapping.. just to ensure this wasn't interfering with the re-initialization attempts...
// no difference with or without the "try"
//answer "start initializing BLE"
//try // just incase the desktop build isn't installed
mergBLEInitialize
//end try
//answer "end initializing BLE"
end openCard
on closeCard
mergBLEStopScanningForPeripherals
mergBLEStopAdvertising
mergBLEDeleteService fld "service" of card "BLE_Card"
end closeCard
on mergBLEDidDiscoverPeripheral pPeripheral, pName, pRSSI
// the "name" of my BLE module contains the word "Adafruit"... I filter this message for that name to ignore any other local BLE devices
// I have un-conditionalized this part of the code in other iterations of this code...
// it makes no difference to the re-initialization attempt ... remember, this code works PERFECTLY when "freshly" started.
if (pName contains "AnyCaster") or (pName contains "Adafruit") then
put pName into sPeripheralA[pPeripheral]["name"]
put pRSSI into sPeripheralA[pPeripheral]["RSSI"]
mergBLEConnectPeripheral pPeripheral
end if
end mergBLEDidDiscoverPeripheral
// occasionally (rare) triggers on initial startup if iOS device is too far away from BLE device
on mergBLEDidFailToConnectPeripheral pPeripheral, pError
answer "Failed to connect to "&sPeripheralA[pPeripheral]["name"]&cr&pError
end mergBLEDidFailToConnectPeripheral
on mergBLEDidConnectPeripheral pPeripheral
updateConnections
mergBLEPeripheralDiscoverServices pPeripheral, fld "service"
end mergBLEDidConnectPeripheral
// never triggered with an error even after re-initialization attempt
on mergBLEPeripheralDidDiscoverServices pPeripheral, pServices, pError
if pError is not empty then
answer "Error discovering services for "&sPeripheralA[pPeripheral]["name"] & cr & pError
else
if fld "service" is among the lines of pServices then
mergBLEPeripheralDiscoverCharacteristicsForService pPeripheral, fld "service" --, fld "characteristic"
end if
end if
end mergBLEPeripheralDidDiscoverServices
on mergBLEPeripheralDidDiscoverCharacteristicsForService pPeripheral, pService, pCharacteristics, pError
//answer "Go ahead?" with "Yes" or "No"
put "mergBLEPeripheralDidDiscoverCharacteristicsForService" && pPeripheral, pService, pCharacteristics, pError
if pError is not empty then
answer "Error discovering characteristics for "&sPeripheralA[pPeripheral]["name"] & cr & pError
else
repeat for each line tCharacteristic in pCharacteristics
/*
answer "ANSWER #3" & return & \
"pPeripheral: " & return & tab & pPeripheral & return && \
"pService: " & return & tab & pService & return & \
"pName:" & return & tab & sPeripheralA[pPeripheral]["name"] & return & \
"tCharacteristic: " & return & tab & tCharacteristic & return & \
"pValue: " & return & tab & pValue
*/
switch
case tCharacteristic is fld "notify" of card "BLE_Card"
mergBLEPeripheralSetNotificationsForCharacteristic pPeripheral, pService, fld "notify" of card "BLE_Card", true
break
case tCharacteristic is fld "writable" of card "BLE_Card"
--mergBLEPeripheralWriteValueForCharacteristic pPeripheral, pService, tCharacteristic, "Initialize stuff" & "?"
writeCharacteristic pService, tCharacteristic, "Initialize stuff"
put true into BLEInit
break
default
-- read our random writable characteristic
mergBLEPeripheralReadValueForCharacteristic pPeripheral, pService, tCharacteristic
break
end switch
end repeat
end if
put 0 into x
repeat until ((the currentFrame of image "cat_69.gif" of card "BLE_Card" > 11) or (x > 5000))
wait 10 milliseconds
add the currentFrame of image "cat_69.gif" of card "BLE_Card" to x
put x into line 2 of field "connections" of card "BLE_Card"
end repeat
// here's where I go to my main card and this causes THIS card to process the "closeCard" message handler
go to card "anycaster_card1"
end mergBLEPeripheralDidDiscoverCharacteristicsForService
on mergBLEPeripheralDidUpdateValueForCharacteristic pPeripheral, pCharacteristic, pValue, pError
if pError is not empty then
answer "Error reading value for characteristic for "&sPeripheralA[pPeripheral]["name"] & cr & pError
else
/*
answer "ANSWER #2" & return & \
"pPeripheral: " & return & tab & pPeripheral & return & \
"pService: " & return & tab & pService & return & \
"pName:" & return & tab & sPeripheralA[pPeripheral]["name"] & return & \
"pCharacteristic: " & return & tab & pCharacteristic & return & \
"pValue: " & return & tab & pValue & return & \
"fld notify: " & return & tab & fld "notify" of card "BLE_Card" & return & \
"fld writable: " & return & tab & fld "writable" of card "BLE_Card"
*/
switch
case pCharacteristic is fld "notify" of card "BLE_Card"
// custom command "interpret_incoming" simply formats the incoming messages from the Arduino ...
// can't see why this would have any effect on the re-initialization attempt
put interpret_incoming(pValue) after field "debugField" of card "anycaster_card1"
break
case pCharacteristic is fld "writable"
answer "at Writable ... pCharacteristic: " & pCharacteristic & "... Should never get here ... check ANSWER #2"
break
default
answer "at Default ... pCharacteristic: " & pCharacteristic & " ... Should never get here ... check ANSWER #2"
go to card "anycaster_card1"
break
end switch
end if
end mergBLEPeripheralDidUpdateValueForCharacteristic
// never triggered even after re-initialization attempt
on mergBLEDidDisconnectPeripheral pUUID
updateConnections
answer "mergBLEDidDisconnectPeripheral" && pUUID
end mergBLEDidDisconnectPeripheral
// never triggered even after re-initialization attempt
on mergBLECentralDidUpdateValueForCharacteristic pCentral, pCharacteristic, pValue
answer "mergBLECentralDidUpdateValueForCharacteristic" && pCentral && pCharacteristic && pValue
/*
answer "ANSWER #1" & return & \
"pCentral: " & return & tab & pCentral & return & \
"pService: " & return & tab & pService & return & \
"pName:" & return & tab & sPeripheralA[pPeripheral]["name"] & return & \
"pCharacteristic: " & return & tab & pCharacteristic & return & \
"pValue: " & return & tab & pValue
if pCharacteristic is fld "writable" and pValue is an integer then
lock messages
set the thumbPosition of scrollbar "writable value" to pValue
unlock messages
end if
*/
end mergBLECentralDidUpdateValueForCharacteristic
// never triggered even after re-initialization attempt
on mergBLECentralDidSubscribeToCharacteristic pCentral, pCharacteristic
//put "" into sCentralA[pCentral]
//updateConnections
answer "mergBLECentralDidSubscribeToCharacteristic" && pCentral && pCharacteristic
end mergBLECentralDidSubscribeToCharacteristic
// never triggered even after re-initialization attempt
on mergBLECentralDidUnsubscribeFromCharacteristic pCentral, pCharacteristic
//delete variable sCentralA[pCentral]
//updateConnections
answer "mergBLECentralDidUnsubscribeFromCharacteristic" && pCentral && pCharacteristic
end mergBLECentralDidUnsubscribeFromCharacteristic
on updateConnections
repeat for each key tCentral in sCentralA
put "Central "& tCentral & return after theConnections
end repeat
repeat for each key tPeripheral in sPeripheralA
put "Peripheral "&sPeripheralA[tPeripheral]["name"] & return after theConnections
end repeat
put theConnections into fld "connections"
end updateConnections
on mergBLECentralManagerDidUpdateState pState
--answer pState
put pState into fld "central state" of card "BLE_Card"
if pState is "powered on" then
mergBLEScanForPeripheralsWithServices fld "service" of card "BLE_Card"
else
put empty into field "service" of card "BLE_Card"
put empty into field "writable" of card "BLE_Card"
put empty into field "notify" of card "BLE_Card"
put empty into field "connections" of card "BLE_Card"
end if
end mergBLECentralManagerDidUpdateState
on mergBLEPeripheralManagerDidUpdateState pState
--answer pState
put pState into fld "peripheral state" of card "BLE_Card"
if pState is "powered on" then
put "6E400001-B5A3-F393-E0A9-E50E24DCCA9E" into pService
put pService into field "service" of card "BLE_Card"
mergBLECreateService fld "service" of card "BLE_Card"
put "6E400002-B5A3-F393-E0A9-E50E24DCCA9E" into pCharacteristic
put pCharacteristic into field "writable" of card "BLE_Card"
mergBLEAddCharacteristicToService fld "service" of card "BLE_Card", fld "writable" of card "BLE_Card", false, true, true, false, false, false, false, false
put "6E400003-B5A3-F393-E0A9-E50E24DCCA9E" into field "notify" of card "BLE_Card"
mergBLEAddCharacteristicToService fld "service" of card "BLE_Card", fld "notify" of card "BLE_Card", false, false, false, true, true, false, false, false
/*
answer field "service" of card "BLE_Card" & crlf & \
field "writable" of card "BLE_Card" & crlf & \
field "notify" of card "BLE_Card"
*/
else
put empty into field "service" of card "BLE_Card"
put empty into field "writable" of card "BLE_Card"
put empty into field "notify" of card "BLE_Card"
put empty into field "connections" of card "BLE_Card"
put empty into sPeripheralA
put empty into sCentralA
put empty into pCharacteristics
end if
end mergBLEPeripheralManagerDidUpdateState
// never triggered with an error even after re-initialization attempt
on mergBLEPeripheralManagerDidStartAdvertising pError
if pError is not empty then
answer pError
end if
end mergBLEPeripheralManagerDidStartAdvertising
// never triggered with an error even after re-initialization attempt
on mergBLEPeripheralManagerDidAddService pService, pError
if pError is not empty then
answer pError
end if
end mergBLEPeripheralManagerDidAddService
on writeCharacteristic pService, pCharacteristic, pValue
repeat for each key tPeripheral in sPeripheralA
put pValue & return after field "debugField" of card "anycaster_card1"
put empty into field "fromAnycaster" of card "anycaster_card1"
put empty into field "verifyField" of card "anycaster_card1"
/*
answer "Stack OUTPUT" & return & \
"tPeripheral: " & return & tab & tPeripheral & return && \
"pService: " & return & tab & pService & return & \
"pName:" & return & tab & sPeripheralA[tPeripheral]["name"] & return & \
"pCharacteristic: " & return & tab & pCharacteristic & return & \
"pValue: " & return & tab & pValue
*/
// I use the "?" character to indicate the end of a string on the Arduino
put pValue & "?" into pValue
mergBLEPeripheralWriteValueForCharacteristic tPeripheral, pService, pCharacteristic, pValue
if doPre then
wait for 200 milliseconds with messages
end if
end repeat
end writeCharacteristic