Arduino, HM-10 and App Inventor 2

 
Hopefully this guide will give you a good introduction to using the HM-10 with App Inventor 2. I also hope that this takes you beyond the usual starter guides that do not go past very basic information.

Although I am using an Arduino the principles will be the same for any other microprocessor or indeed for using the HM-10 on its own. Warning: This is going to be a very long post.

ARD_HM-10_AI2_01_00.gif

To use this guide you should be somewhat familiar with App Inventor, have a BLE enabled Android device, and of course have an Arduino and a HM-10.

 

BLE
HM-10
App Inventor 2
Example Project Part 1: Turn an LED on and off basic
Example Project Part 2: Two-way control of an LED
Example Project Part 3: Making the control codes complex
Example Project Part 4: Change how the codes are dealt with
Example Project Part 5: 3 LEDs and 3 switches

 

 

 

BLE

This is a very brief introduction to BLE and no where near the whole story.

BLE is not an upgrade to Bluetooth Classic, it is a different system with different intended uses. BLE works in a very different way to the earlier Bluetooth. BLE is designed for low energy applications and achieves this by using infrequent small packets of data. It is not really designed for continuous connections and large amounts of data. For this, Bluetooth Classic is a better choice. In essence, BLE achieves its low power consumption by not being connected very often, unlike Bluetooth Classic which maintains a constant connection.

There are 2 ways BLE devices can talk to each other; Broadcaster + Observer, and, Central + Peripheral. The HM-10 can use both methods.

  • With Broadcaster + Observer there isn’t a standard connection, the Broadcaster, usually some kind of sensor, sends out periodic signals (advertising packets) which the Observer listens for. The Broadcaster does not normally know if anything is listening or not.
  • The Central + Peripheral scenario is more like (but not exactly the same) as the classic connection. When the Central (master) device finds a Peripheral (slave) device it wants to connect to it initiates a connection and takes on the master role managing the connection and timings.

Because Bluetooth Classic assumes you are going to use a single connection and the connection is going to be established for a while it does not need to be particular quick at making connections. BLE, on the other hand, is designed to make a lot of short term connections and so is designed to connect and disconnect very quickly.

BLE is all about services and characteristics. A service is a collection of related characteristics and a characteristic is where the data is at. On a typical BLE device you may have a service that contains characteristics dealing with the modules properties such as; the manufacture name, the device name, the firmware ID and/or version number. It may then also have a second service that groups the characteristics that hold the actual data. These may be things like temperature, humidity, brightness. This means, to use BLE, you generally need to know the characteristics you want to use (more specifically the UUIDs for the characteristics).

With BLE the values of these properties are available all the time. If you want to know the manufactures name you read the valve from the manufactures name characteristic. If you want to know the temperature, you read the value from the temperature characteristic. This is very different to how Classic Bluetooth works. With Classic Bluetooth, you only have a single communication channel and all data is sent via the one channel.

Each service and characteristic has a unique identifier called a UUID. This is basically a 24 bit number. In the below HM-10 section, you can see the 2 HM-10 custom characteristic UUIDs; 0000FFE1-0000-1000-8000-00805F9B34FB and 0000FFE2-0000-1000-8000-00805F9B34FB. Sometimes these can be shortened to FFE1 and FFE2.

 

 

HM-10

The HM-10 is a low cost serial BLE module made by Jinan Huamao. It has a serial/UART layer which is good and bad depending on what you want to do. The UART layer sits above the BLE layer and makes it very easy to use with the Arduino. For creating simple connections or using with or as a basic iBeacon the HM-10 is ideal, especially for Arduino hobbyists like myself. Unfortunately the UART layer hides the BLE layer from the user and so limits what you can do with it. We are stuck with what BLE functionality Jinan Huamao give us.

While you can create a classic style connection using 2 HM-10s, BLE was not designed for this and if this is all you need then you would be better suited with Bluetooth Classic modules like the HC-05s or a HC-05 and a HC-06. Creating a standard connection with BLE defeats the purpose of BLE and negates many of the benefits of BLE, including the low energy aspects.

For general use, the HM-10 has 2 custom characteristics under a custom service:

CUSTOM SERVICE
• UUID: 0000FFE0-0000-1000-8000-00805F9B34FB

CUSTOM CHARACTERISTICS
• UUID: 0000FFE1-0000-1000-8000-00805F9B34FB
• CUSTOM CHARACTERISTIC
• READ/WRITE/NOTIFY

• UUID: 0000FFE2-0000-1000-8000-00805F9B34FB
• CUSTOM CHARACTERISTIC
• WRITE

Characteristic FFE1 is active by default. FFE2 is non active and has to be turned on before it can be used. Notice that the characteristic FFE2 is write only. This means you can use it to send data to the HM-10 but you cannot use it to send data from the HM-10. In the following examples I will use UUID FFE1 to read and write data.

For more information about the HM-10 see the HM-10 Bluetooth 4 BLE Modules post.

Because of the serial/UART layer and the fact that the HM-120 has limited custom characteristics ,I will be using a single characteristic for all data, means using the HM-10 is not really using BLE as it was intended. If I were do the same thing using a different chip or module that allow more control over the BLE (like the Nordic BLE modules or an Arduino 101) then I would create separate characteristics for the different devices; LEDs would have their own, the switches would have their own. I may, if there weren’t too many, even create separate characteristics for each individual LED or switch. If you want to control just 2 lights having separate characteristics for each light is manageable. If you have many lights, then creating and managing a separate characteristic for each becomes very laborious.


 

App Inventor 2

App Inventor 2 is a fairly easy way to get in to creating Android apps. It uses the Blockly programming system rather than text and does a lot of the heavy lifting for you. Although it initially may appear simple you can create some surprisingly complex apps with it. AI2 is constantly being developed and updated and one of the latest updates is the new BLE extension. There has been a BLE extension for a while now but this has always been experimental. The new extension hopes to replace the old module with a new more comprehensive and stable system. Note that, at the time of writing, the new BLE extension is still in beta but from my own trials I have found it to work well and be very stable.

To get the most out of this guide you should be a little familiar with App Inventor and how to use the programming blocks. as such I don’t go in to too much detail about App Inventor 2 and if you want to know more start with the App Inventor 2 tutorials.

The new BLE extension is part of AI2 IOT (everything seems to be going IOT) and details about this can be found on the AI2 IOT site. For details about the actual extension and the blocks it has see the BluetoothLE Extension page. The new BLE extension can be downloaded at http://iot.appinventor.mit.edu/assets/resources/edu.mit.appinventor.ble.aix. Please be aware that the extension, although fairly mature, is still in testing and certain things may change. You can download the version I am using for the below examples here. Note that when you save an aia file the extensions get saved with them. This means you do not need to keep importing the extensions every time you work on an app.

I will be using the BaseConnect aia file as the starting point for the app. This is a bare bones app designed as a template allowing a quick way to scan and connect to BLE devices. The BaseConnect aia file already contains the BLE extension but it may be an older version.

App Inventor IOT officially supports the Arduino 101 and the BBC micro:bit, and they have additional support for these boards, but any BLE module, such as the HM-10, can be used.


 

Example Project Part 1: Turn an LED on and off basic

I start with very basic app that allows you to control an LED; turn it on, turn it off. I will then slowly develop this to a robust and extensible system. Something that can be extended and adapted.

ARD_HM-10_AI2_01_00.gif

First; set up the Arduino and the HM-10

 

Arduino and HM-10 Circuit 01

A very simple circuit; an Arduino, a HM-10, an LED, and a resistor. The HM-10 is connected to the Arduino’s pins D8 and D9. Pin D2 has a resistor and an LED.
Arduino D8 (AltSoftSerial receive) to HM-10 TX
Arduino D9 (AltSoftSerial transmit) to a voltage divider and then to the HM-10 RX pin
Arduino D2 to a 330 ohm resistor and the LED.

The HM-10 RX pin is 3.3v. The Arduino TX pin is 5V (on my 5V Nano at least). The voltage divider reduces the 5V to 3.3v and stops the world from being destroyed (or at least the RX pin on the HM-10). 5V Arduinos see 3.3v as HIGH so we can connect the HM-10 TX pin directly to the Arduino RX pin without destroying everything.

The voltage divider is made from 2 resistors. A 1K ohm and a 2K ohm.

ARD_HM-10_AI2_01_20_Circuit

ARD_HM-10_AI2_01_20_Breadboard_1600
I am using a premade voltage divider and a premade LED that has the resistor built in.

 

Arduino Sketch

Like the circuit, the sketch is fairly simple. It basically repeats the following;
check for received data,
check the received data for either a “0” or a “1”,
if it finds a “0” then turn the LED off,
if it finds a “1′ then turn the LED on.

The sketch uses AltSoftSerial and this needs to be downloaded and installed before the sketch will compile. On ATmega 328 based Arduinos, such as the Nano, AltSoftSerial uses pins 8 and 9. Other Types of Arduino may have different pins. Check the AltSoftSerial pages for details.

//  Arduino, HM-10, App Inventor 2
//
//  Example Project Part 1: Turn an LED on and off basic
//  By Martyn Currey. www.martyncurrey.com
//
//  Pins
//  BT VCC to Arduino 5V out. 
//  BT GND to GND
//  Arduino D8 (ASS RX) - BT TX no need voltage divider 
//  Arduino D9 (ASS TX) - BT RX through a voltage divider
//  Arduino D2 - Resistor + LED
 
// https://www.pjrc.com/teensy/td_libs_AltSoftSerial.html
#include <AltSoftSerial.h>
AltSoftSerial ASSserial; 
 
byte LEDPin = 2;
char c=' ';
 
 
void setup() 
{
    Serial.begin(9600);
    Serial.print("Sketch:   ");   Serial.println(__FILE__);
    Serial.print("Uploaded: ");   Serial.println(__DATE__);
    Serial.println(" ");
 
    ASSserial.begin(9600);  
    Serial.println("ASSserial started at 9600");
    Serial.println(" ");
 
    pinMode(LEDPin, OUTPUT); 
 
}
 
void loop()
{
     // Read from the Bluetooth module and turn the LED on and off
    if (ASSserial.available())
    {
        c = ASSserial.read();
        Serial.println(c);
 
        // The ascii code for 0 is dec 48
        // The ascii code for 1 is dec 49
        if ( c== 48) { digitalWrite(LEDPin, LOW); }
        if ( c== 49) { digitalWrite(LEDPin, HIGH); }
    }
}

This is a very simple sketch. It checks for serial data and if any is available it checks for a “0” or a “1”. If there is a “0” or a “1” the LED is turned on or off accordingly. Any other characters are ignored.

You can check that the sketch works by opening up the serial monitor. Whatever is received from the HM-10 is displayed in the serial monitor.

ARD_HM-10_AI2_01_50_serialMonitor

 

Android App

We start with a very simple app that will send 2 control codes; “0” and “1”. The “0” is the code to turn the LED off and the “1” is the code to turn the LED on. This means the app requires some way to send the codes and the Arduino requires a way to receive and detect them. The initial app is dumb in that it does not know the actual state of the LED, it simply sends the codes when the user clicks the buttons. More advanced features will be added later.

To make life a little easier I am starting with the BaseConnect template created by the AI2 team. The BaseConnect aia file is the bare bones necessary to scan for and connect to BLE devices. There are a couple of issues with it (like the size of the text in the device list) but it is a good place to start. When developing BLE apps you do not need to use the BaseConnect file but should be used as a reference if you are just starting with BLE on AI2.

You can download the BaseConnect template from http://iot.appinventor.mit.edu/assets/resources/IoT_BaseConnect.aia. I doubt it will change but if you want to be sure you are using the same version as me you can download from here.

ARD_HM-10_AI2_01_10_BaseConnect
Open App Inventer, upload the BaseConnect aia file and save it as “ARD_HM10_AI2_Single_LED_01″. If you have an Android device and the HM-10 available connect using the MIT AI2 Companion app and give it a try.
Click Scan and see what you can find.

ARD_HM-10_AI2_01_11_BaseConnect
Here the app has found various BLE devices including a HM-10. Unfortunately the text is too small see them.

In the designer, select ListBLE and change the TextSize to 48. We can now read the names of the found BLE devices.
ARD_HM-10_AI2_01_12a_BaseConnectARD_HM-10_AI2_01_13a_BaseConnect

The BaseConnect app is very basic and in the Blocks section we can see that it contains only 7 blocks or procedures. What each procedure does should be self explanatory.

ARD_HM-10_AI2_01_16_BaseConnect

 

Add the LED controls buttons

Next is to add the controls to turn the LED on and of. For this I am using a couple of buttons. Along with the buttons I am using some text labels that are used as spacers. The buttons are inside an Horizontal Arrangement.

ARD_HM-10_AI2_01_14_BaseConnectARD_HM-10_AI2_01_15_BaseConnect

I then renamed the elements:
ARD_HM-10_AI2_01_30ARD_HM-10_AI2_01_31

Download ARD_HM10_AI2_Single_LED_01.aia

Once you have added the controls save as ARD_HM10_AI2_Single_LED_02

 
We now need to add the functions to send the control codes. This is not as straight forward as you may expect, especially if you have used Bluetooth Classic. With Bluetooth Classic, once you have made a connection you simply write the data to the connection channel. With BLE you need to write the data to a specific characteristic and this means you need to know the correct UUID for that characteristic and also the UUID for the service the characteristic is under. With BLE we no longer have a connection channel we have characteristics.

In this example I am using the default HM-10 custom characteristic 0000FFE1-0000-1000-8000-00805F9B34FB which is under the custom service 0000FFE0-0000-1000-8000-00805F9B34FB.

The AI2 BLE extension allows us to write various types of data and for this example we could use bytes or strings, since I am using “0” and “1” I could use either. I am going with strings so that using more complex control codes in the future will require less changes.
ARD_HM-10_AI2_01_32
The BLE Write blocks normally expect the data to be in a list. Fortunately, if you use plain data as I do below, AI2 will convert it for you.
You can see, to be able to write data we need the service UUID and the characteristic UUID.

The WriteStrings function is blind in that it just sends the data and it has no idea if the HM-10 ever receives it. In the BLE extension we can check for a completed transmission by using the WriteStringsWithResponse block.
ARD_HM-10_AI2_01_33
This instructs the HM-10 to send back an acknowledgement. AI2 gets the acknowledgement and this triggers an event in AI2 that we can check. For now we do not need this so I am just using the WriteStrings function.

 
In the Blocks editor, add a couple of global variables to hold the UUID numbers and add the button click event blocks for the LED control buttons:
ARD_HM-10_AI2_01_34
You should be able to work out that, when the ON button is clicked the code sends a “1” and when the OFF button is clicked it sends a “0”.

Open up the companion app and give it a go. It works, sort of, as long as your Android device has Bluetooth enabled and you connect to the HM-10 before you click one of the LED control buttons. If you try to scan while Bluetooth is turned off you get a system error. If you click one of the LED control buttons before making a connection you get a system error.

If you try to scan when Bluetooth is disabled you get a system error message and then the Android asks if you want to enable Bluetooth. At present there isn’t a clean way to check if Bluetooth is enabled using the BLE extension. There is a way with Bluetooth Classic. So as a work around we can add the Bluetooth Classic client and then check to see if Bluetooth is enabled. If it is not issue our own message with a notifier.

Download ARD_HM10_AI2_Single_LED_02.aia
Save the app as ARD_HM10_AI2_Single_LED_03 before moving on.

 
Add the Bluetooth Client and then add a notifier:
ARD_HM-10_AI2_01_35_add_BTARD_HM-10_AI2_01_36_add_notifier

We now need to change what happens when the Scan button is clicked. When the button is clicked we want to first check to see if Bluetooth is enabled, if it is we can scan. If it is not we want to issue an error message. We check to see if Bluetooth is enable with the BluetoothClient.Enabled block.

In the Blocks editor add a check for Bluetooth to the ButtonScan.Click procedure. At the same time add an else to the if/then block and a call to the notifier. The call to the notifier displays the error message when Bluetooth is not enabled.
ARD_HM-10_AI2_01_37

Now if we try to scan before Bluetooth is turned on we get a warning:
ARD_HM-10_AI2_01_38

With the LED control buttons, we need to check to see if we are connected before trying to send the control codes. We can do this with the BLE isDeviceConnected block.
ARD_HM-10_AI2_01_39
Now if you click the ON or OFF button when the HM-10 is not connected nothing happens. You could, if you wish, have an error message displayed but you will find you can get a lot of error messages and they quickly become annoying, therefore, I find it better to simply not do anything. Or, you could make the buttons inactive until a connection is made. I leave that for you to do.

Give the app another try. It should be better. When Bluetooth is not enabled we get a nice message telling us to turn it on. When we click the LED control buttons without a connection we no longer get the system error message. What happens if we click the other buttons? We get more system error messages.

ARD_HM-10_AI2_01_40ARD_HM-10_AI2_01_41ARD_HM-10_AI2_01_42

We could add more checks but a better way (to me at least) is to use toggle buttons.

At the moment we have one button to start a scan and one button to stop the scan. These can be combined in to one toggle button; SCAN and STOP SCAN. The app will start with the button showing SCAN and when it is clicked, if Bluetooth is enabled, the scan will start and the button changes to STOP SCAN. This means the user can only press the STOP SCAN button if they have already pressed the SCAN button. This method also means the button text can be used as a kind of status flag. We can then do the same with the Connect and Disconnect buttons.

In the designer I changed the Scan button text to SCAN and deleted the Stop Scan button. In the blocks editor I deleted the ButtonStopScan.Click event function. We no longer need it.
ARD_HM-10_AI2_01_43

I extended the ButtonScan.Click event procedure:
ARD_HM-10_AI2_01_44
Now the function first checks the button text and if the text is “SCAN” we know we need to start a scan. If the text is not “SCAN” it must be “STOP SCAN” so we stop the scan.

Now do the same for the Connect and Disconnect buttons. Combining the two buttons in to one toggle button will mean the user can only disconnect when there is an active connection but it will not stop the user clicking the Connect button before they have selected a device to connect to. We can look at this after we have combined the buttons.

To the ButtonConnect.Click event function add a check to see what button text is displayed and move the disconnect instructions:
ARD_HM-10_AI2_01_46

In the Blocks editor, delete the ButtonDisconnect.Click event function and then delete the ButtonDisconnect button in the Designer. On the ButtonConnect button change the text to be “CONNECT”.
ARD_HM-10_AI2_01_45

If you give the app a try you should find the connect and disconnect works but you still have the system errors if you try to connect before scanning and selecting a device. Let’s fix that next.

The AI2 ListView element has a couple of properties we can use to determine if one of the list elements has been selected; List.Selection and List.SelectionIndex. Before a selection is made SelectionIndex is 0 and List.Selection is null or “”. Using SelectionIndex we can simply see if SelectionIndex is greater than 0 to see if the user has selected something.
ARD_HM-10_AI2_01_47

Give it a try. It should work. At least it works the first time you try to connect. What happens if you scan, connect and then disconnect and then connect again without scanning. The app will try to connect to the same device again. The app remembers which item in the list you selected and uses it again. You can either leave this as the standard behaviour or you can reset the list index when a connection is made. I will leave it, there are other things I want to fix.

At the moment, when the user clicks the CONNECT/DISCONNECT button the button text changes straight away. It changes even when there is a problem and the connection is not successful. To correct this, move the instructions to change the button text to the BluetoothLE1.Connected and BluetoothLE1.Disonnected functions. You should now have something like the following:
ARD_HM-10_AI2_01_48

In the ButtonConect.Click event function there is an instruction to set the LabelStatus to “Status: Connecting”, we could do the same with the CONNECT/DISCONNECT button. I leave for you to implement if you so wish.

We now have an app that looks like this. It is starting to look a little cleaner and leaner.
ARD_HM-10_AI2_01_49

Here’s what we have so far:
ARD_HM10_AI2_Single_LED_03 - designer
ARD_HM10_AI2_Single_LED_03 - blocks

Download ARD_HM10_AI2_Single_LED_03.aia

Save this as ARD_HM10_AI2_Single_LED_04

The next step, of course, is to combine the LED control buttons into one toggle button that says either “ON” or “OFF”.

To combine the LED control buttons in to one toggle button we use exactly the same method as above. We have one button and when clicked we check the button text. If it says “ON” we know the LED is on and we need to turn it off. If it is not “ON” then is must be (should be) “OFF” so the LED is off and we need to turn it on.

We will use the on button and rename it to BTN_LED.

One thing to notice is that the LED button will now reflect the LED status not the command. This means the controls being sent are reversed. In the previous code, the ON button was used to turn the LED on. Now, if the button says “ON” it means the LED is on and it needs turning off.

We can now delete the OFF button and the button spacer:
ARD_HM-10_AI2_01_52

After adding the if/then condition statement to check the button text and moving the blocks we have:
ARD_HM-10_AI2_01_51

This works fine. The on/off button now fully controls the LED. There are still a couple of things to correct.
1. When the app first starts the button says “ON” and the LED is normally off at the beginning.
2. The button doesn’t change colour.
3. We don’t need to check for a connection in 2 places.

In the Designer change the button text to “OFF” and the button back ground colour to red. Now when it starts the default text will be OFF.

To change the button colour we use the button BackgroundColor property. The colours are grabbed from the Color menu.
ARD_HM-10_AI2_01_53
AI2 has far more colour options but for this example I am using the basic colours from the colour menu.

Here we are with the colour commands. The button now shows green when the LED is on and red for when it is off.
ARD_HM-10_AI2_01_54
ARD_HM-10_AI2_01_55

Rather than checking for a connection after checking the button text, we can check for a connection first. In this way we only need to have the check once. For this we just need to move one of the if/thens above the button text checks.
ARD_HM-10_AI2_01_56

The app works the same but the code is a little better.

 
There is something else I want to look at. When you click the SCAN button, the app keeps scanning until you click the STOP SCAN button. When you make a connection, the app continues to scan and it does not need to. To change this, let’s make it so that when we click the CONNECT button we stop the scanning at the same time.

If we look at the SCAN button function
ARD_HM-10_AI2_01_57
If the button text is not equal to “SCAN” it must be “STOP SCAN” and to stop scanning the app processes the statements at the bottom.

ARD_HM-10_AI2_01_58
We could simple duplicate these statements in the CONNECT button function but a better way is to move these statements in to their own procedure and call the procedure.

Grab a new procedure block and move the stop scan blocks to it. Rename the new procedure to StopScan.
ARD_HM-10_AI2_01_59

After a new procedure has been created it is added to the procedure menu:
ARD_HM-10_AI2_01_60
We now have the call StopScan procedure block. Drag this out and put it in the ButtonScan. Click procedure.
ARD_HM-10_AI2_01_61

To stop scanning when the CONNECT button is clicked, add the StopScan procedure call to the ButtonConnect.Click procedure in the CONNECT section after the ListBLE SecitionIndex check.
ARD_HM-10_AI2_01_62

One last thing I want to do, add a title to the app. In the designer, add 3 labels to the very top of the screen.
ARD_HM-10_AI2_01_63
rename to
ARD_HM-10_AI2_01_64
Edit the spacers:
Height = 10 pixels
Width = Fill Parent
Text = “”

Edit the title:
Height = automatic
Width = Fill Parent
Text Alignment = center
Text = “LED CONTROL”
FontSize = 30.

ARD_HM-10_AI2_01_66

We now have
ARD_HM-10_AI2_01_65

That’s about it for the first part. We have an app that can control an LED via a remote connection with an HM-10.

Download ARD_HM10_AI2_Single_LED_04.aia

In part 2 I add a button switch on the Arduino and have 2 way control.

 
 

 

Example Project Part 2: Two-way control of an LED

In this part we add a switch on the Arduino side so that we can control the LED by a physical switch as well as from the app. The button switch will act as a toggle switch (same as the button in the app) and control the LED locally. This means, on the Arduino, we need add code that;
– checks the switch, and
– if the switch is pressed change the state of the LED
– let the app know when the LED has changed.

We let the app know by sending the control codes to the app from the Arduino. Same as before; “0” for off and “1” for on.
In the app we will need to check for incoming data and if the data is a “0” or “1” set the LED control button accordingly.

 

Circuit

To the circuit, add a button switch + pull down resistor to pin D3.
ARD_HM-10_AI2_02_01_Circuit_ButtonSwitch

ARD_HM-10_AI2_02_02_Breadboard_ButtonSwitch

 

Arduino Sketch. Example Project Part 2: Turn an LED on and off 2way control

Now that we have a button switch we need to check it. The code for this is inside its own function called checkSwitch() which we call from the main loop. checkSwitch() checks to see if the switch has been pressed and if so changes the LED, at the same time it sends the appropriate control code to the app via the serial connection to the HM-10.

The code that checks the received serial data is the same as before but this has also been moved to its own function called checkRecievedData().

Using functions like this keeps the code in the main loop clean and tidy. It also means it is easier to reuse the same code.

//  Arduino, HM-10, App Inventor 2
//
//  Example Project Part 2: Turn an LED on and off 2 way control 01
//  By Martyn Currey. www.martyncurrey.com
//
//  Pins
//  BT VCC to Arduino 5V out. 
//  BT GND to GND
//  Arduino D8 (ASS RX) - BT TX no need voltage divider 
//  Arduino D9 (ASS TX) - BT RX through a voltage divider
//  Arduino D2 - Resistor + LED
//  Arduino D3 - 10K pull down resistor + button switch
 
 
// AltSoftSerial uses D9 for TX and D8 for RX. While using AltSoftSerial D10 cannot be used for PWM.
// Remember to use a voltage divider on the Arduino TX pin / Bluetooth RX pin
// Download from https://www.pjrc.com/teensy/td_libs_AltSoftSerial.html
 
#include <AltSoftSerial.h>
AltSoftSerial ASSserial; 
 
 
// Constants for hardware
const byte LEDPin = 2;
const byte SwitchPin = 3;
 
// general variables
boolean LED_State = false;
boolean switch_State = false;
boolean oldswitch_State = false;
 
char c=' ';
 
 
void setup()  
{
    Serial.begin(9600);
    Serial.print("Sketch:   ");   Serial.println(__FILE__);
    Serial.print("Uploaded: ");   Serial.println(__DATE__);
    Serial.println(" ");
 
    ASSserial.begin(9600); 
    Serial.println("AltSoftSerial started at 9600"); 
    Serial.println(" ");
 
    pinMode(LEDPin, OUTPUT); 
    digitalWrite(LEDPin,LOW);
 
    pinMode(SwitchPin, INPUT); 
 
 
} // void setup()
 
 
void loop()  
{
       checkSwitch();
       checkRecievedData();
}
 
 
void checkSwitch()
{
     // Simple toggle switch function with even simpler debounce.
     boolean state1 = digitalRead(SwitchPin); delay(1);
     boolean state2 = digitalRead(SwitchPin); delay(1);
     boolean state3 = digitalRead(SwitchPin); 
 
     if ((state1 == state2) && (state1==state3))   
     { 
          switch_State = state1;  
          if ( (switch_State == HIGH) && (oldswitch_State == LOW) )
          {
               // toggle LED_State.
               LED_State = ! LED_State;  
 
               // If LED_State is HIGH then LED needs to be turned on.
               if ( LED_State == HIGH) 
               {  
                    digitalWrite(LEDPin,HIGH);   // turn on the LED
                    ASSserial.print("1" );       // tell the app the LED is now on
                    Serial.println("Sent - 1");    
               }
 
               else                     
               {  
                   digitalWrite(LEDPin,LOW);    // turn off the LED                  
                   ASSserial.print("0");        // tell the app the LED is now off
                   Serial.println("Sent - 0");    
               }    
          }          
          oldswitch_State = switch_State;
      }
}
 
 
void checkRecievedData()
{
     // Read from the Bluetooth module and turn the LED on and off
    if (ASSserial.available())
    {
        c = ASSserial.read();
        Serial.println(c);
 
        // The ascii code for 0 is dec 48
        // The ascii code for 1 is dec 49
        if ( c== 48) { digitalWrite(LEDPin, LOW);     LED_State = LOW;  }
        if ( c== 49) { digitalWrite(LEDPin, HIGH);    LED_State = HIGH; }
    }
}

 

Updating the app

Before continuing, save the previous work as ARD_HM10_AI2_Single_LED_05

At the moment the app can only send control codes, it cannot receive them. We now need to give the app the ability to receive data.

To receive data from BLE devices there a few of things we need to know.
– The service UUID
– The characteristic UUID
– The data type (byte, string, integer, float, short, long)

We are using the default custom service and characteristic on the HM-10 so we know the UUIDs. We are using strings, so we know the data type.

The BLE extension has various blocks for receiving different kinds of data, since we are using strings we use the .StringsReceived block
ARD_HM-10_AI2_02_03

We cannot use the .StringsReceived block on its own. It relies on a data received event that is created with the .RegisterForStrings block
ARD_HM-10_AI2_02_04
Note that this is turn relies on the characteristic having a notify property. Behind the scenes AI2 tells the BLE module to turn on notifications and then uses the notifications to generate the data received event.

When we are finished with the connection it is good practice to unregister or cancel the data received event and this is done with the .UnregisterForValues block
ARD_HM-10_AI2_02_06

 
We cannot register before we have a connection so a good place to have the .RegisterForStrings block is inside the BluetoothLE1.Connected procedure. Then, after a connection is established the .RegisterForStrings block is called straight away.

The .UnregisterForValues block should be called before the call to disconnect. If you try to .UnregisterForValues after the connection has been broken you will get an error.

This gives us:
ARD_HM-10_AI2_02_08_Corrected_1
ARD_HM-10_AI2_02_08_Corrected_2

 
The new app has 2 possible ways to change the LED control button;
1 – by clicking the button,
2 – by the control code.

This means we have 2 parts of the app that can change the button to “ON” and 2 parts that can change the button to “OFF”. Rather than duplicate the same code I have moved the blocks that change the LED button properties to their own procedures
ARD_HM-10_AI2_02_09

Now, to change the button we simply call LED_BUTTON_ON or LED_BUTTON_OFF. In the BTN_LED.Click event procedure I have replaced the blocks with a call to the appropriate procedure.
ARD_HM-10_AI2_02_10

 
All that is left is to check for received data and update the LED control button if required.

As mentioned earlier, the .StringsReceived block is called when new data is received.
ARD_HM-10_AI2_02_03

This gives us a list rather than plain data so we need to process the list. First, double check that we actually have a list with the “is a list?” block.
ARD_HM-10_AI2_02_11

then loop through all items in the list with the “for each item in list” block. The list is the stringValues.
ARD_HM-10_AI2_02_12
We should only have 1 character and so only have one element in the list and you may feel that we do not need to loop through all elements. For this simple example we probably don’t but it is good practice and later you may have an app that receives a lot of data very quickly and you will then need to do this. Also, what happens if somebody presses the button really quickly?

Now we need to add the check to see if the data is a “1” or a “0” and if it is change the LED control button.
ARD_HM-10_AI2_02_13

You should notice that I use 2 if statements rather than 1 if/then. The above checks for a “0” and then checks for a “1”. Doing it like this means incorrect characters are ignored.

 
And that’s it. We now have 2 way control.

Here is what we have so far
ARD_HM-10_AI2_02_14

ARD_HM10_AI2_Single_LED_05 - blocks_CORRECTED

Download ARD_HM10_AI2_Single_LED_05.aia
Download Arduino Sketch. Example Project Part 2: Turn an LED on and off 2way control

 
If all you want to do is turn things on and off this method works well and you can expand it by using additional single character codes:
0 & 1 – LED 1 on/off
2 & 3 – LED 2 on/off
4 & 5 – Lights on/off
6 & 7 –
8 & 9 –
After you run out of numbers use letters, there are many and this method will allow you to control a lot of LEDS, or lights, or buzzers, or locks, etc.

What if you want to control something that does not have a simple on/off status or send data that is not simple binary. In the next part I look at expanding the control codes beyond the simple “0” and “1” so that we can then expand what we can control.

 

 

Example Project Part 3: Making the control codes complex

In part 3, all we do is change the control codes. We make them more complex. The function of the app and sketch remains the same and the LED is turned on and off in exactly the same way but we do it in a more complex way.

Although this may seem like a waste, changing the control codes will allow us to add more complex operations; like dimming an LED or controlling an RGB LED.

 

The new control codes

Instead of the single character codes used previously, multi character codes will now be used. For the LED we will have a 3 character codes; “L10″ and “L11″.
L10 – L for LED, 1 for LED #1, and 0 for off
L11 – L for LED, 1 for LED #1, and 1 for on

Once we have the code to accommodate 1 LED, you should be able to see, that to add a second LED we simply use “L20″ and “L21″. For a third LED we would use “L30″ and “L31″.

Using multi character codes is not so straight forward though. We need to check all the characters in the code, not just one. Is this a code for LEDs, which LED, is it on or off? We have another problem. When using serial communications you cannot guarantee you will get all the data in one go. It is very possible that the codes arrive in pieces. “L” followed by “11” or “L1″ followed bu “0”. This is true for both the app and the Arduino and means we need a way to tell we have the whole code. There are many ways of doing this. My preferred method, for use with ascii, is to use start and end markers. Start and end markers means we enclose the data in start and end markers. We then keep receiving data until we have both markers, at which point we know we have a complete code. In the below example I use “[” and “]”.

On the Arduino side (controls to the Arduino) I will be using fixed length codes; “[L10]” and “[L11]”. On the AI2 side I will use comma separated data “[L,1,0]” and “[L,1,1]”. AI2 has better data manipulation functions and we can directly transfer a variable that contains commas to a a list of separated values. (You can do this with any character not just commas).

In the Arduino sketch I am using Robins2’s recvWithStartEndMarkers() function. This function is part of a very good post on the Arduino forum. All I have done is change the start and end characters to “[” and “]”. If you want to learn more about using serial on the Arduino, this post is a good place to start.

In the app I have created a Blockly version of the recvWithStartEndMarkers() function. It is not exactly the same but it does a similar thing.

 

Circuit

The circuit is exactly the same as before. One LED and one button switch connected to an Arduino.
ARD_HM-10_AI2_02_02_Breadboard_ButtonSwitch

ARD_HM-10_AI2_02_01_Circuit_ButtonSwitch

 

Arduino Sketch. Example Project Part 3: Complex control codes

There are few things to note about the new sketch. We now have new variables that help handle incoming data; maxDataLength, receivedChars[], and, newCommand.

maxDataLength is used to store the maximum length of the the incoming data. This means commands cannot be longer than the value of maxDataLength.

receivedChars[] stores the complete received command

newCommand is a flag used to tell the sketch if there is a new command or not.

A new function, recvWithStartEndMarkers(), has been added. This checks the incoming data for the start and end markers and then copies any valid data if finds in to the receivedChars[] char array.

Inside the processCommand() function, the received command is checked. If it is a valid command action is taken.
if (strcmp (“L10″,receivedChars) == 0)
if (strcmp (“L11″,receivedChars) == 0)

This may not be the best way to do this. After all if we are checking “L10″ and “L11″ as single entities we might as well have stayed with “1” and “0”. I will address this is part 4.

You should note that the codes being sent from the Arduino have a slightly different format to the ones it is receiving.

//  Arduino, HM-10, App Inventor 2
//
//  Example Project Part 3: Complex control codes
//  By Martyn Currey. www.martyncurrey.com
//
//  Pins
//  BT VCC to Arduino 5V out. 
//  BT GND to GND
//  Arduino D8 (ASS RX) - BT TX no need voltage divider 
//  Arduino D9 (ASS TX) - BT RX through a voltage divider
//  Arduino D2 - Resistor + LED
//  Arduino D3 - 10K pull down resistor + button switch
 
// AltSoftSerial uses D9 for TX and D8 for RX. While using AltSoftSerial D10 cannot be used for PWM.
// Remember to use a voltage divider on the Arduino TX pin / Bluetooth RX pin
// Download from https://www.pjrc.com/teensy/td_libs_AltSoftSerial.html
 
#include <AltSoftSerial.h>
AltSoftSerial ASSserial; 
 
// Variables used for incoming data
const byte maxDataLength = 20;
char receivedChars[21] ;
boolean newCommand = false;
 
// Constants for hardware
const byte LEDPin = 2;
const byte SwitchPin = 3;
 
// general variables
boolean LED_State = false;
boolean switch_State = false;
boolean oldswitch_State = false;
 
void setup()  
{
    Serial.begin(9600);
    Serial.print("Sketch:   ");   Serial.println(__FILE__);
    Serial.print("Uploaded: ");   Serial.println(__DATE__);
    Serial.println(" ");
 
    ASSserial.begin(9600); 
    Serial.println("AltSoftSerial started at 9600"); 
    Serial.println(" ");
 
    pinMode(LEDPin, OUTPUT); 
    digitalWrite(LEDPin,LOW);
 
    pinMode(SwitchPin, INPUT); 
 
} // void setup()
 
 
void loop()  
{
       checkSwitch();
       recvWithStartEndMarkers();                // check to see if we have received any new commands
       if (newCommand)  {   processCommand();  }    // if we have a new command do something about it
}
 
 
 
/*
****************************************
* Function checkSwitch()
* checks the status of a button switch and turns an LED on or off depending on the switch status
* 
* passed:
*  
* global: 
*      switch1_State
*      LED1_State
*      oldswitch1_State
*
* Returns:
*          
* Sets:
*      switch1_State
*      LED1_State
*      oldswitch1_State
*/
void checkSwitch()
{
     // Simple toggle switch function with even simpler debounce.
     boolean state1 = digitalRead(SwitchPin); delay(1);
     boolean state2 = digitalRead(SwitchPin); delay(1);
     boolean state3 = digitalRead(SwitchPin); 
     if ((state1 == state2) && (state1==state3))   
     { 
          switch_State = state1;  
          if ( (switch_State == HIGH) && (oldswitch_State == LOW) )
          {
               LED_State = ! LED_State;  
               if ( LED_State == HIGH) 
               {  
                    ASSserial.print("[L,1,1]" );  
                    digitalWrite(LEDPin,HIGH);  
                    Serial.println("Sent - [L,1,1]");    
               }
 
               else                     
               {  
                   ASSserial.print("[L,1,0]");   
                   digitalWrite(LEDPin,LOW);   
                   Serial.println("Sent - [L,1,0]");    
               }    
          }          
          oldswitch_State = switch_State;
      }
}
 
 
 
/*
****************************************
* Function processCommand
* parses data commands contained in receivedChars[]
* receivedChars[] has not been checked for errors
* 
* passed:
*  
* global: 
*       receivedChars[]
*       newData
*
* Returns:
*          
* Sets:
*       receivedChars[]
*       newData
*/
void processCommand()
{
    if (strcmp ("L10",receivedChars) == 0) 
    {
        digitalWrite(LEDPin,LOW);
        LED_State = LOW; 
        Serial.println("LED1 LOW");  
    }
 
    else if (strcmp ("L11",receivedChars) == 0)
    {
        digitalWrite(LEDPin,HIGH);
        LED_State = HIGH; 
        Serial.println("LED1 HIGH");  
    }
 
 
    receivedChars[0] = '\0';
    newCommand = false;
 
}
 
 
// function recvWithStartEndMarkers by Robin2 of the Arduino forums
// See  http://forum.arduino.cc/index.php?topic=288234.0
/*
****************************************
* Function recvWithStartEndMarkers
* reads serial data and returns the content between a start marker and an end marker.
* 
* passed:
*  
* global: 
*       receivedChars[]
*       newData
*
* Returns:
*          
* Sets:
*       newData
*       receivedChars
*
*/
void recvWithStartEndMarkers()
{
     static boolean recvInProgress = false;
     static byte ndx = 0;
     char startMarker = '[';
     char endMarker = ']';
     char rc;
 
     if (ASSserial.available() > 0) 
     {
          rc = ASSserial.read();
          if (recvInProgress == true) 
          {
               if (rc != endMarker) 
               {
                    receivedChars[ndx] = rc;
                    ndx++;
                    if (ndx > maxDataLength) { ndx = maxDataLength; }
               }
               else 
               {
                     receivedChars[ndx] = '\0'; // terminate the string
                     recvInProgress = false;
                     ndx = 0;
                     newCommand = true;
               }
          }
          else if (rc == startMarker) { recvInProgress = true; }
     }
}

 

Updating the app

Before proceeding, save the previous aia file as ARD_HM10_AI2_Single_LED_06

To see if the Arduino sketch works, in the BTN_LED.Click procedure, change the codes sent from the app to “[L10]” and “[L11]”
ARD_HM-10_AI2_03_01
If you load up the companion app and give it a try you should find the app can control the LED but when the button switch is pressed the LED control button in the app does not change. This is because the Arduino is sending the new control codes and the app does not yet know about them. Let’s change that.

In the previous version of the app, the app only had to deal with single characters and receiving these and checking them is fairly simple. Now we have more than one character and, like the Arduino sketch, we need a way to get all the data together and ensure we have a complete command.

In the Arduino sketch, the receivedChars variable was used to store the received data. In the app we will use something similar, a global variable called BT_receivedData_Buffer
ARD_HM-10_AI2_03_02
and when we get new data we add it to the buffer. In the BluetoothLE1.StringsReceived procedure, remove the blocks that check for a “1” and a “0” and replace with
ARD_HM-10_AI2_03_04

This should give you
ARD_HM-10_AI2_03_03
Now whenever new data is received it is added to the buffer rather than being acted upon straight away. Of course, this means we now need to do something with the buffer.

To handle the buffer create (drag from the menu) a new procedure and rename it to PROCESS_BUFFER. At this point it might be a good idea to think about what we have and what we want to do.

We have a variable that may or may not contain a control code. We know that a complete code will be sandwiched between the start and end markers so we can use these to determine is we have a complete code. If the buffer contains a start marker “[” and we have a end marker “]” and the start marker is before the end marker there is a good chance we have a code. We then take that code and see if it is one we want. If it is, we do something.

Rather than trying to step through this process one block at a time I will show my solution and then try to explain how it works.

ARD_HM-10_AI2_03_05

First, local or temporary variables are created
ARD_HM-10_AI2_03_06
As the local variables are created the values of startMarkerPos and endMarkerPos are set. If the marker character is not found startMarkerPos and/or endMarkerPos will be 0.

Next, the position of the markers is checked. If the markers are found their positions will be > 0, the next check confirms that the start marker is before the end marker.
ARD_HM-10_AI2_03_07

After this, the actual command is copied to the command local variable.
ARD_HM-10_AI2_03_08

and then the command (including the start and end markers) is removed from the buffer
ARD_HM-10_AI2_03_09

At this point the variable command stores a command which is sent to the processCommand procedure. We haven’t created this yet.

ARD_HM-10_AI2_03_15

Finally, there is a check to see if there are any more start and end markers and if so the whole process is repeated.

All this is contained in a while loop. In the form while not done, keep going.
ARD_HM-10_AI2_03_13
This is one of my preferred ways of looping when there is more than one condition used to exit the loop. I use a variable called Done, set this to false at the start and then, when finished set it to true to drop out of the loop. I then get “while not done”. which is easy to read and, to me at least, makes sense. I suspose you could also used while not finished, and if you swaped the polarity, while thereIsSomethingLeftToDo.

We then need to call the PROCESS_BUFFER procedure so we add a call in the BluetoothLE1.StringsReceived function
ARD_HM-10_AI2_03_10

 
All this will work well as long as the start marker is always before the end marker. What happens if we get an end marker first. The app will never progress. It will find both markers but the start marker will not be first so it drops out of the loop. Next time the buffer is checked it will have exactly the same result.

To stop this happening I add a trim to start marker function. This trims the buffer so that the first character will be a start marker (as long as there is one).
ARD_HM-10_AI2_03_11

And the call to TrimToStartMarker is placed at the beginning of the PROCESS_BUFFER procedure.
ARD_HM-10_AI2_03_12

All that is left to do is check the code is valid and change the LED control button. Here is the PROCESS_COMMAND procedure.
ARD_HM-10_AI2_03_14

 
Here what the blocks look like now
ARD_HM10_AI2_Single_LED_06 - blocks_CORRECTED

Download ARD_HM10_AI2_Single_LED_06.aia
Download Arduino Sketch. Example Project Part 3: Complex control codes

In part 4 we change how the control codes are dealt with, should be short and simple. In part 5 we add more LEDs and switches. In part 6 we try sliders and control the brightness of an LED. Stay tuned.

 

 

Example Project Part 4: Change how the codes are dealt with

In this section we start to prepare for additional devices such as more LEDs.

At the moment, the control codes are checked as single entities, for example “L10″ and “L11″. Here we break the codes in to separate parts or characters and deal with each character separately:
L for LED
1 for LED number
1/0 for on or off.

If all you want to do is switch 2 or 3 things on and off then it doesn’t really matter how you check the codes. It does make a difference when you start to control many different things though.

 

Example Project Part 4: Complex control codes II

In this section we are only changing the processCommand() function.

void processCommand()
{
    // The LED command is 3 characters 
    // chr 1 = L for LED
    // chr 2 = 1 for the LED number
    // chr 3 = 0 or 1 for on/off
 
    if ( receivedChars[0] =='L')
    // do we have an LED command
    {
         if ( receivedChars[1]=='1' )
         // is it for LED #1
         {
              receivedChars[2] = receivedChars[2] -48;
              // subtracting 48 turns acsii in to actual values. "0" becomes 0 and "1" becomes 1.
              LED_State = receivedChars[2];
              digitalWrite(LEDPin,LED_State);
              Serial.print("LED1 state = "); Serial.println(LED_State);
         }
    }
 
    receivedChars[0] = '\0';
    newCommand = false;
}

When using char arrays we can address each character individually by using its position in the array ( remember that Arduino arrays start at position 0) and that is what I am doing here.

if ( receivedChars[0] =='L')

First I check the first character to determine what type of command we have. Of course, we only have a single LED so the command is an LED command.

if ( receivedChars[1]=='1' )

Next I check which LED the command is for. Again, we only have one LED so it should be for LED #1.

Lastly, will turn the LED on or off.Because the LED only has two states and the command code has 2 states, I can use the value from the command directly (after converting it to an actual value). I do not need to check it. However, if your are not 100% certain you are going to receive a “0” or a “1” then it should be checked.

To convert a ascii number to an actual number simply subtract DEC 48. The ascii value of “0” is 48 and the ascii value of “1” is 49. So by subtracting 48 we get the actual value.

If you want to know more about how I develop this kind of code have a look at the App Inventor and Bluetooth Classic posts, especially Turning a LED on and off with an Arduino, Bluetooth and Android. Part III 3 LEDs and 3 Switches. This has an example of how a list of if/thens can be condensed in to just a few lines of code.

 

Update the app

Before proceeding, save the previous aia file as ARD_HM10_AI2_Single_LED_07.

As before, I will show the blocks than explain what they do.
ARD_HM-10_AI2_04_01

The new bits:
First the command is split into a list using the comma as the seperator. Each list element is a single character.
ARD_HM-10_AI2_04_03

then the first list element is compared to an “L”
ARD_HM-10_AI2_04_04

if it is an “L” the COMMAND_LED procedure is called.
ARD_HM-10_AI2_04_02

COMMAND_LED is a new procedure and deals only with LED commands. First it checks the LED number (the second list element)
ARD_HM-10_AI2_04_05

and then sets the LED control button depending on the value of the third list element.
ARD_HM-10_AI2_04_06

I have also started to add error trapping. By using 2 if statements and an else statement we are trapping non correct values. If we used if = “0” turn LED on else turn L:ED off. Any non 0 value would be turn on the LED.
At the moment I just have comments but these can be replaced with something like a pop-up error message.

Here are the blocks:
ARD_HM10_AI2_Single_LED_07 - blocks

Download Arduino Sketch. Example Project Part 4: Complex control codes II
Download ARD_HM10_AI2_Single_LED_07.aia

 

 
All this work and we are still only turning a single LED on or off. Hopefully though, you should be able to see how the Arduino sketch and the app can now be easily extended and adding new commands should not be straight forwards We see if this is true in the next part.

 

 

Example Project Part 5: 3 LEDs and 3 switches

Here we add more LEDs and more switches and expand the control codes to accommodate them.
 

Circuit

Add switches to pins D3 and D4. Add LEDS to D6 and D7.

I’ve moved things around a bit to make the switches easier to access but the circuit is basically the same.
ARD_HM-10_AI2_05_00_Breadboard_1600

ARD_HM-10_AI2_05_01_CIRCUIT

 

Arduino Sketch. Example Project Part 5: 3 leds and 3 switches

I do not go in to how the sketch was created. It is the same as the one used in the Bluetooth Classic guide (I copied the Bluetooth Classic sketch) so if you want to know more see the Turning a LED on and off with an Arduino, Bluetooth and Android. Part III 3 LEDs and 3 Switches post. This goes through my usual process of how I create then refine sketches.

A couple of notes though. The command sent to the app is created as required. A temp command variable is used and the values are replaced before transmitting the command.
For the received commands I am assuming the received values will be correct and in range and so use the value directly from the command. The LED pins are stored in an array, this means I can use the LED number from the command as the index to the LED pin array. I no longer need to check which pin it is.

/*
 *  Arduino, HM-10, App Inventor 2
 * 
 *  Example Project Part 5: 3 leds and 3 switches
 *  By Martyn Currey. www.martyncurrey.com
*
* Turn LEDs on and off from an Android app via a HM-10 or from a physical switches connected to the Arduino
* Uses the following pins
*
* D9 - AltsoftSerial RX
* D8 - AltsoftSerial TX
* D7 - LED + resistor
* D6 - LED + resistor
* D5 - LED + resistor
* D4 - Button switch 
* D3 - Button switch 
* D2 - Button switch 
*/
 
// AltSoftSerial uses D9 for RX and D8 for TX. While using AltSoftSerial D10 cannot be used for PWM.
// Remember to use a voltage divider on the Arduino TX pin / Bluetooth RX pin
// Download from https://www.pjrc.com/teensy/td_libs_AltSoftSerial.html
 
#include <AltSoftSerial.h>
AltSoftSerial BTserial; 
 
 
// Variables used for incoming data
const byte maxDataLength = 20;
char receivedChars[21] ;
boolean newData = false;
 
// Constants for hardware
const byte SWITCH_PIN[] = {2,3,4};
const byte LED_PIN[] = {5,6,7};
 
 
// general variables
boolean LED_State[] = {false,false,false};
boolean switch_State[] = {false,false,false};
boolean oldswitch_State[] = {false,false,false};
 
 
void setup()  
{
    for (byte pin = 0; pin < 3; pin++) 
    {
         // Set the button switch pins for input
         pinMode(SWITCH_PIN[pin], INPUT); 
 
         // Set the LED pins for output and make them LOW
         pinMode(LED_PIN[pin], OUTPUT);  digitalWrite(LED_PIN[pin],LOW);
    }
 
    // open serial communication for debugging
    Serial.begin(9600);
    Serial.print("Sketch:   ");   Serial.println(__FILE__);
    Serial.print("Uploaded: ");   Serial.println(__DATE__);
    Serial.println(" ");
 
    //  open software serial connection to the Bluetooth module.
    BTserial.begin(9600); 
    Serial.println("AltSoftSerial started at 9600"); 
 
    newData = false;
 
} // void setup()
 
 
void loop()  
{
    for (byte switchNum = 1; switchNum < 4; switchNum++) 
    {
           checkSwitch(switchNum);
    }
    recvWithStartEndMarkers();                // check to see if we have received any new commands
    if (newData)  {   processCommand();  }    // if we have a new command do something about it
}
 
 
/*
****************************************
* Function checkSwitch()
* checks the status of a button switch and turns an LED on or off depending on the switch status
* 
* passed:
*  
* global: 
*      switch_State[]
*      LED_State[]
*      oldswitch_State[]
*
* Returns:
*          
* Sets:
*      switch_State[]
*      LED_State[]
*      oldswitch_State[]
*/
void checkSwitch( byte pos)
{
     // pos = 1,2,3. Array pos = 0,1,2 so convert by subtracting 1
     pos = pos-1;
 
     // very simple debouce.
     boolean state1 = digitalRead(SWITCH_PIN[pos]); delay(1);
     boolean state2 = digitalRead(SWITCH_PIN[pos]); delay(1);
     boolean state3 = digitalRead(SWITCH_PIN[pos]); delay(1);
     if ((state1 == state2) && (state1==state3))   
     { 
          switch_State[pos] = state1;  
          if ( (switch_State[pos] == HIGH) && (oldswitch_State[pos] == LOW) )
          {
               LED_State[pos] = ! LED_State[pos];  // flip the status.
 
               // Rather than have a long list of possible commands I create the command as required.
               // Values in the temp command are replaced depending on which button switch has been pressed.
               char TMPcmd[8] = "[L,1,0]";
               TMPcmd[3] = pos+1+48;             // pos+1 should be 1,2, or 3.   convert a numeric value to ascii by adding 48. 
               TMPcmd[5] = LED_State[pos]+48;    // LED_State should be 0 or 1
               BTserial.print(TMPcmd);
 
               digitalWrite(LED_PIN[pos],LED_State[pos]);  
               Serial.println(TMPcmd);                    
          }          
          oldswitch_State[pos] = switch_State[pos];
      }
}
 
 
 
 
/*
****************************************
* Function processCommand
* parses data commands contained in receivedChars[]
* receivedChars[] has not been checked for errors
* 
* passed:
*  
* global: 
*       receivedChars[]
*       newData
*
* Returns:
*          
* Sets:
*       receivedChars[]
*       newData
*/
void processCommand()
{
     Serial.print("receivedChars = ");   Serial.println(receivedChars);
 
    if (receivedChars[0] == 'L')      // do we have a LED command?
    {
        // we know the LED command has a fixed length "L10"
        // and the value at pos 1 is the LED and the value at pos 2 is 0 or 1 (on/off). 
        // 0 and 1 is the same as LOW and HIGH so we can use 0/1 instead of LOW/HIGH
 
        byte LEDnum = receivedChars[1] - 48;          // convert ascii to value by subtracting 48
        boolean LEDstatus = receivedChars[2] - 48;
 
        digitalWrite(LED_PIN[LEDnum-1],LEDstatus);
        LED_State[LEDnum-1] = LEDstatus;
    }
 
    receivedChars[0] = '\0';
    newData = false;
}
 
 
 
// function recvWithStartEndMarkers by Robin2 of the Arduino forums
// See  http://forum.arduino.cc/index.php?topic=288234.0
/*
****************************************
* Function recvWithStartEndMarkers
* reads serial data and returns the content between a start marker and an end marker.
* 
* passed:
*  
* global: 
*       receivedChars[]
*       newData
*
* Returns:
*          
* Sets:
*       newData
*       receivedChars
*
*/
void recvWithStartEndMarkers()
{
     static boolean recvInProgress = false;
     static byte ndx = 0;
     char startMarker = '[';
     char endMarker = ']';
     char rc;
 
     if (BTserial.available() > 0) 
     {
          rc = BTserial.read();
          if (recvInProgress == true) 
          {
               if (rc != endMarker) 
               {
                    receivedChars[ndx] = rc;
                    ndx++;
                    if (ndx > maxDataLength) { ndx = maxDataLength; }
               }
               else 
               {
                     receivedChars[ndx] = '\0'; // terminate the string
                     recvInProgress = false;
                     ndx = 0;
                     newData = true;
               }
          }
          else if (rc == startMarker) { recvInProgress = true; }
     }
}

 

Updating the app

Save the current project as ARD_HM10_AI2_3LEDs_3Switches_08

A lot of the hard work has already been done. In this part we simply duplicate controls and extend the if/then condition list in the COMMAND_LED procedure.

First we add 2 more LED control buttons. I have also added spacer labels between the buttons.
The spacer labels are:
Height – Fill parent
Width – 5 pixels
HasMargins – unchecked
ARD_HM-10_AI2_05_12

Above the buttons I have added labels to identify the buttons. The label sizes are 30% width the same as the buttons. I have unchecked the HasMargins property so that the sizes match correctly. The labels also have spacer labels separating them the same as the buttons..
ARD_HM-10_AI2_05_11

Of course, now that we have new buttons we new blocks to look after them.

I simply duplicated the BTN_LED01.Click procedure and changed the references to BTN_LED02 and BTN_LED03 and updated the command to be sent. You can do the same with the LED_BUTTON_ON and LED_BUTTON_OFF procedures. Copy them and then change the references.

ARD_HM-10_AI2_05_15

ARD_HM-10_AI2_05_16

This takes care of the button clicks now we take care of the extra commands the app may receive.

Since we are still only receiving LED commands we do not need to change the processCommand procedure and only need to update the COMMAND_LED procedure.

We now have commands for LED #2 and LED #3. All I have done is copy the original blocks and updated the values being checked and which button on/off procedure that is called.

The COMMAND_LED procedure could be made a little smaller. For example, we know, regardless of which LED is being used, the on/off code should be 0 or 1. We can check for this at the beginning rather than 3 separate times. Another option is to put the button elements in to a list and then use the LED number as the index to the list. Similar to how the Arduino sketch now works. I’ve kept it simple and simply have 3 similar blocks of code.
ARD_HM-10_AI2_05_17

If you read the previous parts you should remember that the LED command has been split in to a list and element 1 is the “L” for LED, element 2 is the LED number (1 to 3) and element 3 is the on/off code (0 or 1).

Element 1 is checked in the processCommand procedure and if found the command list is passed to the COMMAND_LED procedure.
ARD_HM-10_AI2_05_20

In the COMMAND_LED procedure element 2, the the LED number, is checked
ARD_HM-10_AI2_05_21

and if a 1, 2 or 3 element 3, the on/off code, is then checked. If the app finds a “0” or a “1” the button off or button on procedure is called.
ARD_HM-10_AI2_05_22

And that’s it. We are able to control 3 LEDs. Here are the blocks

ARD_HM10_AI2_08-blocks

Download Arduino Sketch. Example Project Part 5: 3 LEDs and 3 switches
Download ARD_HM10_AI2_3LEDs_3Switches_08.aia

 
 
 

3 thoughts on “Arduino, HM-10 and App Inventor 2

  1. Excellent tutorial.
    For those of us that would like to simply wire up the components…and test…
    Might I recommend to compile the app,…and have it available for download on google apps?
    We can always come back and tweak the code to our liking afterwards….
    Great job BTW…

Leave a Reply

Your email address will not be published. Required fields are marked *


six + 8 =

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>