Re-Initialising mergBLE

This is the place to get technical support and discuss all things to do with MergEXT

Moderators: heatherlaine, Klaus, FourthWorld, robinmiller, monte

Post Reply
dr_mumps
Posts: 14
Joined: Wed Dec 24, 2014 7:00 pm

Re-Initialising mergBLE

Post by dr_mumps » Fri Apr 13, 2018 5:34 am

I have been in contact with forum user: "bwmilby" who has helped me on some aspects of my problems with mergBLE... much appreciated!!... but this one "problem" remains and I desperately need to get it resolved. I am submitting this forum entry on the direct advice of Monte Goulding, the developer of the mergBLE functionality. This summarizes another thread in a different forum (Engine Contributors: disconnect and re-connect to MergBLE).

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
Since the BLE_Card performs a "closeCard" action when I make it "go to <my main card>", you would think that if I "go to BLE_Card" from my main card, that would COMPLETELY re-start the BLE connection... and it absolutely DOES NOT... and since I force the connection to stop with "mergBLECancelConnectionToPeripheral tPeripheral" (yes, I've double checked the "value" in the "tPeripheral" many, many times ... and this cancel command definitely stops the BLE connection), I would have thought THAT command would have "allowed" the "go to BLE_Card" to re-initialize... again.. not so.

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



dr_mumps
Posts: 14
Joined: Wed Dec 24, 2014 7:00 pm

Re: Re-Initialising mergBLE

Post by dr_mumps » Fri Apr 13, 2018 4:31 pm

Kudos to support@livecode.com for providing the solution I was seeking. Here is their response to my posting (although it was sent directly to them via email):

========== start of livecode response ==================
The mergBLEInitialize command just initializes the library, it doesn't cause any connections to be made and is is a noop if called more than once.

It looks like the issue is that you are relying on mergBLECentralManagerDidUpdateState being called after mergBLEInitialize. This does happen the when the library is initialized but not on repeat calls to mergBLEInitialize.

What you will need to do is to check in openCard if mergBLE is already initialized or not, start a scan if it is initialized and initialize if not.

Something like the below should work

Code: Select all

local sState

on openCard
    switch sState
         case "powered on"
               mergBLEScanForPeripheralsWithServices fld "service" of card "BLE_Card"
               break
         case empty
               mergBLEInitialize
               break
         default
              answer sState
    end switch
end openCard

// and then later on in the BLE processing ...

on mergBLECentralManagerDidUpdateState pState
   --answer pState
   put pState into sState
   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
I hope that helps, please let us know if we can be of any further assistance.
Kind regards
Elanor
========== end of livecode response ==================

of course, better doc's on the mergBLE function would have helped... but at least I'm over this hurdle!

Doug

ps... if you're wondering about my forum "handle" the "dr" does not refer to "doctor" but are my initials and the "mumps" is not the disease but my true expertise in the "Massachusetts General Hospital Utility Multi-Programming System (MUMPS), also known as M.... the main development environment for the the Veterans Hospitals around the world.

Post Reply

Return to “MergEXT”