A little over a month ago (June 2017), the MIT App Inventor team announced the release of a new expansion to App Inventor, allowing makers to design, create, and interact with the Internet of Things (IoT).
This included an enhanced BluetoothLE (ble.aix) extension and a handy home page: http://iot.appinventor.mit.edu which provides reference documentation, various teaching materials and learning aids.
However, the only information provided to date on the website, relating to hardware, is for the Arduino 101 and BBC micro:bit.
This tutorial demonstrates how to use MIT App Inventor to connect with an Adafruit Feather 32u4 Bluefruit LE and then use the Nordic UART serial link service to send commands from your smartphone and receive feedback. The demonstration app is designed to show all the necessary steps required to enable serial communication between a smartphone app and a Nordic chipset BLE device.
Using the Adafruit Feather 32u4 Bluefruit LEThe Adafruit Feather 32u4 Bluefruit is an 'all-in-one' Arduino-compatible (ATmega32u4 microcontroller @ 8MHz with 3.3V logic/power) + Bluetooth Low Energy (using an nRF51822 chipset from Nordic) development board with built in USB and battery charging.
The manufacturers, Adafruit, have provided a superb learning guide for this board: https://learn.adafruit.com/adafruit-feather-32u4-bluefruit-le/overview
This learning guide provides full instruction on how to get started, including Arduino IDE setup and details on how to install and use the Bluefruit LE (nRF51 BLE) library.
In our case we'll be using / modifying the "BLEUart" example sketch (bleuart_cmdmode.ino), which allows you to send and receive text data between the Feather 32u4 and a connected Bluetooth Low Energy Central device on the other end, such as your mobile phone.
But instead of using the Adafruit Bluefruit LE Connect APP for Android, we're going to develop our own app using MIT App Inventor. However, please note that you should still download the Adafruit App as this app checks the nRF51 firmware version and makes updates for you - and there have been quite a few. The Android App can be found here in the Google Play store.
Developing the AppThese instructions assume that you have prior experience and know-how on how to use the App Inventor IDE.
Start by creating a new project. Then design an app layout in the Designer screen. You will the following:
- ListPickers (used to display BLE devices found after a scan and display the BLE services available once a BLE device has connected).
- Buttons (used to handle LED control and to request a new BLE device scan).
The App also uses a number of hidden components:
- ActivityStarter: This is used to turn on Bluetooth if it is not enabled on the phone (using code snippet provided by puravidaapps.com)
- Bluetooth Client: This is required to make the "is Bluetooth enabled" check on the phone
- Notifier: This is used to display toast alert messages on the phone
- Clock: This is used to control when the "found BLE devices" check
- BluetoothLE: This is the BLE component (imported as an extension)
Now let's create the logic for the app. Switch to the "Blocks" view.
Let's start with Screen1.Initialise:
- Here we check vai "BTenabledChecker" process whether Bluetooth on the phone is enabled using BluetoothClient1.Enabled. If so we can start scanning for BLE devices using the BluetoothLE1.StartScanning method.
- If Bluetooth is not enabled on the phone we use ActivityStarter to invoke an action "android.bluetooth.adapter.action.REQUEST_ENABLE". This will prompt the use to switch on Bluetooth.
- Then there are two events that can be triggered following ActivityStarter, namely the ".AfterActivity" or the ".ActivityCanceled" events
A BluetoothLE1.DeviceFound event will trigger once the scanning process finds any BLE devices within range. Note that not all devices are found in the first attempt, so a button is used (btnScanAgain) to rescan.
BluetoothLE1.DeviceList is the result of the scan, which is in the format of a CSV text string. You convert to list using "list from csv row text" and add the result to the list picker using ListPickerBLEdevices.Elements method. The "call ListPickerBLEdevices.Open" method is then used to display the results so that the user can choose the device they want to connect to.
Once the user selects their device, a "ListPickerBLEdevices.AfterPicking" event is triggered. Here is assign/set the global variable BLEdeviceName using the "call BluetoothLE1.FoundDeviceName" method where we use ListPickerBLEdevices.SelectionIndex as the reference.
We then connect to the device using the "call BluetoothLE1.Connect" method.
Once the smartphone connects with the device, a "BluetoothLE1.Connected" event will be triggered. We now update some of the UI labels to inform user that device xxxx is connected to phone. We can now obtain what BLE services are available through the BLE device using the "call BluetoothLE1.SupportedServices" method. We then convert the CSV text string into a list and add to our "listBLEServices" listpicker. This list picker is then made visible on the smartphone UI so that the user can select which service they wish to use.
It is worth noting that this manual selection step is not really required as there is only one service available for UART serial communication with the BluefruitLE device. This manual selection is simply provided so that other BLE devices could be also be used. If other devices are used, it will be up to the user to know which custom service is the one use, as MIT App Inventor does not know.
The data received from the "call BluetoothLE1.SupportedServices" method is made up of two components, namely a UUID text string, e.g. 00001801-0000-1000-8000-00805f9b34fb and a descriptor, which at present just says "Unknown Service".
The part of the UUID text string that offers any meaning is the first 8 digits. This can be cross checked with the BLE GATT Services specification. As can be seen, those values starting with 18xxxx are standard while the 1530 and the 6e400001 appear to be custom services.
To help me work out what these custom services meant, I went to Google Play store and downloaded Nordic Semiconductor's nRF Connect for Mobile app. Using this app to scan and connect with the BluefruitLE device, it informed me that the 1530 service is used for updating firmware and the other 6e400001 is the correct service for UART communication.
For my app, I added in some nice-to-have code (called "parseServiceUUID" procedure) to help the user make better sense of which BluefruitLE services are provided. The output of this is shown above (e.g. so instead of 00001801-xxx unknown service, the list output is shown as Generic Attribute "service" (1801)). The app code is included as attachment to this tutorial, where you can review.
A "listBLEServices.AfterPicking" event is then triggered once the user selects the required service they want to use. We now update some of UI screen labels to show the user which service was selected. We then extract out the "characteristics" associated with services as these are needed to invoke write and read methods. We store our associated characteristics, which we obtain using the "call BluetoothLE1.GetCharacteristicsForService" method, in a global variable "CharacteristicsUUIDs" as we will need to use these later.
To work out which characteristic does what I again used the Nordic App to give me this information. As shown there are just two characteristics associated with the UART service (UUID starting with 6e400003 for TX and UUID starting with 6e400002 for RX).
Then with a little experimentation / testing, I was able to determine that the first item listed in my global "CharacteristicsUUIDs" list was the one to use when registering my callback (i.e. "call BluetoothLE1.RegisterForStrings" method) to listen for messages sent from the BluefruitLE device and that the second item listed was the one to use when sending messages to the BluefruitLE device (using the "call BluetoothLE1.WriteStrings" method).
Then with the WriteStrings method we send "Status" as our String Value to the BluefruitLE device. The Arduino code (more on that later) will then respond with a text string telling the app whether the LED on the BluefruitLE device is ON, OFF or BLNK (i.e. blinking).
We use the "when BluetoothLE1.StringsReceived" method to detect the LED state and we then update our phone UI to notify the user of the LED state via a button colour change and status label.
For the demo app the "LongClick" button method is used to trigger LED state change.
Modifying Arduino CodeAs mentioned earlier, we'll be using / modifying the "BLEUart" example sketch "bleuart_cmdmode.ino", which comes with the library.
We start by adding in some declarations for the LED, LED blinking time, a variable to help monitor how long and a LED / blink state.
// Pin Configuration and Firmware Declarations
#define LED_PIN 13
const unsigned long BLINKTIME = 1000;
unsigned long t_blink = 0L;
int blinkState = LOW;
Then in void setup() when define pin mode
pinMode(LED_PIN, OUTPUT);
Then in the main loop routine we delete all the code related to detecting user input from the Serial port as this is not needed. We can also get rid of the bool getUserInput(char buffer[], uint8_t maxSize) function.
Then to get the blink routine working we need to delete the following:
if (strcmp(ble.buffer, "OK") == 0) {
// no data
return;
}
Then we add in our new code to read the text strings and update the LED accordingly.
void loop(void)
{
// Now Check for incoming characters from Bluefruit
ble.println("AT+BLEUARTRX");
ble.readline();
ble.waitForOK();
String BLEbuffer = ble.buffer;
if (BLEbuffer.length() && BLEbuffer.indexOf("OK") == -1)
Serial.print(F("[Recv] ")); Serial.println(BLEbuffer);
if (BLEbuffer.indexOf("Status") >= 0) {
Serial.println(F("Status Request Received"));
ble.print("AT+BLEUARTTX=");
if (t_blink) {
ble.println("BLNK");
}
else {
if (blinkState)
ble.println("ON");
else
ble.println("OFF");
}
// check response stastus
if (! ble.waitForOK() ) {
Serial.println(F("Failed to get response"));
}
ble.println("AT+BLEUARTRX");
}
else if (BLEbuffer.indexOf("ON") >= 0) {
blinkState = HIGH;
digitalWrite(LED_PIN, blinkState);
t_blink = 0;
ble.print("AT+BLEUARTTX=");
ble.println("ON");
Serial.println(F("ON Request Received"));
ble.println("AT+BLEUARTRX");
}
else if (BLEbuffer.indexOf("OFF") >= 0) {
blinkState = LOW;
digitalWrite(LED_PIN, blinkState);
t_blink = 0;
ble.print("AT+BLEUARTTX=");
ble.println("OFF");
Serial.println(F("OFF Request Received"));
ble.println("AT+BLEUARTRX");
}
else if (BLEbuffer.indexOf("BLNK") >= 0) {
if (!t_blink) t_blink = millis();
ble.print("AT+BLEUARTTX=");
ble.println("BLNK");
Serial.println(F("BLINK Request Received"));
ble.println("AT+BLEUARTRX");
}
BLEbuffer = "";
if (t_blink) {
if (t_blink > millis()) t_blink = millis();
if ((millis() - t_blink) > BLINKTIME) {
blinkState = !blinkState;
digitalWrite(LED_PIN, blinkState);
t_blink = millis();
}
}
}
Once the code is uploaded onto the BluefruitLE device, open up the Serial Monitor to monitor progress.
The finished product
Comments