When a Mobile App communicates with a BLE (Bluetooth 4.0+) device, it commonly has App as master and device as slave paradigm. The App performs various actions such as scan, connect, read/write characteristics and listen to notifications.
Most actions require certain pre-requisites e.g. Read/Write characteristic cannot be done prior to discovering the characteristic. Many of the actions are asynchronous. Under some conditions, the app cannot execute certain actions when the slave Bluetooth device is in the middle of another action.
Considering these factors, the suitable way to handle BLE communication is the Hierarchical State Machine design pattern. The communication can be broadly split into two major states namely Disconnected and Connected state. If an error occurs in any of the two major states it switches to the Error state. The actions and sub-states are substantially different in each state.
In this state, the App is not connected to the device. This will have different sub-states:
Initial – This is the very beginning of the state cycle. This state can accept triggers to Start Scan or to Connect to a device. This will lead to either Scanning state or Connecting State or Error state.
Scanning – When in the Scanning state, the app will look for the BLE devices that are advertising. When a device is found, it can either add it to its collection or raise an event to notify the listeners. (This will depend upon the App specific requirements).
When it receives ScanCompleted trigger or StopScan trigger, it will return to the Initial State. If an error occurs during this state, it will go to the Error state.
Connecting – When an app sends a trigger to Connect, the state machine will switch to the Connecting state. In this state, it will attempt to connect to the specified device. After the connection is established, it will switch to the Connected State. In case of failure, it will go to the Error state.
In this state, App can perform various actions such as discovering service(s), discovering characteristics, reading from and writing to characteristics. It can also listen to the notifications.
All of the operations are asynchronous operations. When an operation completes, the state machine can take an action such as raise an event/call a method and then the state is returned to the idle state. If the operation fails, it changes to the Error state.
Idle – This is the very beginning of the state when an App connects to a device. This can accepts triggers to Discover services and to Disconnect from the device.
Discovering Services – In this state, the app can discover the services of the peripheral. When the desired service is discovered, it can raise a trigger to discover it characteristics. E.g. An App can request Arduino/Raspberry Pi to discover the Weather service.
Alternatively (not in this State Diagram), if App wants list of all services, it can raise an event or add to its collection and switch to the Idle state when finished.
Discovering Characteristics – In this state, the App can discover the characteristics of the given service. When the desired characteristic is found, it will switch to the Discovered Characteristic state. In the same example the app can request weather service to enumerate its characteristics. The service can return characteristics such as Temperature, Humidity etc.
Discovered Characteristic – In this state, the App can perform various permitted operations on the characteristic e.g. Read, Write or Enable notifications. This would depend upon what is supported by the characteristic. App can also disconnect from this state.
Reading Characteristic – When the state machine is issued trigger to read the value of a characteristic, the state machine changes to this state. When the read operation is completed, it changes back to the Discovered Characteristic state. E.g. if an App request to read the Temperature, at this time the state will change to Reading Characteristic. When the Temperature is read, it will return back to the Discovered Characteristic.
Writing Characteristic – When the state machine is issued a trigger to write to a characteristic, the state machine changes to this state. When the write operation is completed, it changes back to the Discovered Characteristic.
Notifications Enabled – When an App wants to listen to the notification of a characteristics, it will trigger Enable Notifications. The state machine will switch to this state and listen to the notifications. When a notification is received, it can take the desired actions. The listening can be turned off by Disable Notifications trigger.
This state is reached, when device malfunctions or user has turned off Bluetooth settings or due to any other cause(s). This can be reached from either Connected state or Disconnected state. App can send a Reset trigger to the state machine restore it back to the Initial state.
Hierarchical State machine is a good pattern to represent states and various sub-states of a machine. It provides cleaner picture of what actions can be taken in each state and what effects that will cause.