The basic circuit is pretty simple. It supplies 3.3V from a regulated power supply to the BR-XB-LE4.0-S2, and has an LED connected from pin PIO_2 to ground. This LED will light when the BR-XB-LE4.0-S2 has a good Bluetooth connection with another device.

The Terminal Program

The basics of communicating with Bluetooth low energy devices have been covered extensively in the blog discussing the Texas Instruments SensorTag and the blog that introduced the Texas Instruments Key Fob. This blog reviews the basics a bit, but goes through them quickly. See either of the other blogs for a more in-depth look at communicating with Bluetooth low energy devices.



XBee Adapter Board

Breadboard Power Supply

Breadboard (Part 20791)


Hookup Wire

Assorted LEDs


Assorted Resistors


Blue Radios

Parallax Inc.

Dr Robot


Byte Works, Inc.







You may already have most of the items to build the circuits described here. The BR-XB-LE4.0-S2 uses an Xbee footprint with two rows of 10 pins spaces 2mm apart. That’s a bit awkward for building breadboard projects, so the second part is the Xbee Adapter Board, which maps the Xbee 2mm pins to the 2.54mm spacing of a standard breadboard. The power supply is the other component that deserves a close look. The power supply shown provides 5V or 3.3V from either a USB connection or a standard center-positive power jack with a 6-12 volt range. Use the 3.3V power output for the BR-XB-LE4.0-S2. Everything else on the parts list is pretty common.

Do not use a 5v power supply with the BR-XB-LE4.0-S2. The device is designed for 3V, although 3.3V works well; using 5V could damage the part.

Like all Bluetooth low energy devices, communication with the BR-XB-LE4.0-S2 is though services offered by the device. The BR-XB-LE4.0-S2 has a single service with characteristics used set up a serial communications stream that allows a program to send simple text commands to the device and get replies back from the device in the form of text replies. The most natural and flexible way to experiment with the deice is to write a text-based program that lets you type commands and displays anything sent back from the device—in other words, a terminal program. The rest of this section walks through the terminal program. If you like, you can download the complete program at the end of the blog and skip to the end of this section, picking up with sending your first command to the device. You don't need to understand the internals of the program until you want to write your own program to control sensors or other electronics connected to the BR-XB-LE4.0-S2.

! Define the various UUIDs

blueRadiosUUID$ = "DA2B84F1-6279-48DE-BDC0-AFBEA0226079"

infoUUID$ = "99564A02-DC01-4D3C-B04E-3BB1EF0571B2"

modeUUID$ = "A87988B9-694C-479C-900E-95DFA6C00A24"

rxUUID$ = "BF03260C-7205-4C25-AF43-93B1C299D159"

txUUID$ = "18CDA784-4BD3-4370-85BB-BFED91EC86AF"

The program starts by defining the UUIDs for the service and the four characteristics used by the terminal program. We'll see more about this service and these characteristics in a moment.

! Set to 1 for verbose debug output.

debug% = 0

When set to 1, the debug flag causes the program to spew a lot of diagnostic output. This could be useful to debug problems, but there's no reason to see all that output unless it is really needed, so the flag is turned off.

! This is the device we've connected to, if any.

DIM blueRadiosPeripheral AS BLEPeripheral

This is where we store the object connected to the device once we make the connection. The program needs to remember the device in a global variable, since disposing of the object will also disconnect from the device.

! Used to create a pause before accepting commands.

DIM commandsAllowed AS INTEGER, commandTime AS DOUBLE, delay AS DOUBLE

delay = 0.5

After sending a command, the program needs to wait a bit for the device to reply. Waiting a full half a second is pretty conservative. You can play with the delay time by adjusting this value.

! Print the version.

PRINT "Blue Radios AT Terminal 1.0"


! Start BLE processing and scan for a Blue Radios device.



uuid(1) = blueRadiosUUID$


Start scanning for a BR-XB-LE4.0-S2 device. Actually, we're really scanning for any device with the appropriate service, so if Blue Radios creates a new device that offers the same service—and maybe a few more—the program will still work.

! Called when a peripheral is found. If it is a BlueRadios device, we

! initiate a connection to is and stop scanning for peripherals.


! Parameters:

!    time - The time when the peripheral was discovered.

!    peripheral - The peripheral that was discovered.

!    services - List of services offered by the device.

!    advertisements - Advertisements (information provided by the

!        device without the need to read a service/characteristic)

!    rssi - Received Signal Strength Indicator

SUB BLEDiscoveredPeripheral (time AS DOUBLE, peripheral AS BLEPeripheral, services() AS STRING, advertisements(,) AS STRING, rssi)

blueRadiosPeripheral = peripheral

IF debug% THEN

 PRINT "Attempting to connect to "; blueRadiosPeripheral.bleName





BLEDiscoveredPeripheral is called when iOS finds a Bluetooth low energy device with the service we are looking for. It connects to the device and then stops scanning, preserving battery life.

! Called to report information about the connection status of the

! peripheral or to report that services have been discovered.


! Parameters:

!    time - The time when the information was received.

!    peripheral - The peripheral.

!    kind - The kind of call. One of

!        1 - Connection completed

!        2 - Connection failed

!        3 - Connection lost

!        4 - Services discovered

!    message - For errors, a human-readable error message.

!    err - If there was an error, the Apple error number. If there

!        was no error, this value is 0.

SUB BLEPeripheralInfo (time AS DOUBLE, peripheral AS BLEPeripheral, kind AS INTEGER, message AS STRING, err AS LONG)


IF kind = 1 THEN

 ! The connection was established. Look for available services.


ELSE IF kind = 2 OR kind = 3 THEN

 PRINT "The connection was lost."

ELSE IF kind = 4 THEN

 ! Services were found. If it is the main service, begin discovery

 ! of its characteristics.

 DIM availableServices(1) AS BLEService

 availableServices =

 FOR a = 1 TO UBOUND(availableServices, 1)

   IF debug% THEN

     PRINT "Found service "; availableServices(a).UUID


   IF availableServices(a).UUID = blueRadiosUUID$ THEN

     peripheral.discoverCharacteristics(uuid, availableServices(a))





BLEPeripheralInfo is called when the connection status changes or when a service is discovered.

For a new connection, BLEPeripheralInfo starts the discovery process for all available services. Once the services are discovered, it is called again. This time, it asks for all of the characteristics associated with the services.

! Called to report information about a characteristic or included

! services for a service. If it is one we are interested in, start

! handling it.


! Parameters:

!    time - The time when the information was received.

!    peripheral - The peripheral.

!    service - The service whose characteristic or included

!        service was found.

!    kind - The kind of call. One of

!        1 - Characteristics found

!        2 - Included services found

!    message - For errors, a human-readable error message.

!    err - If there was an error, the Apple error number. If there

!        was no error, this value is 0.

SUB BLEServiceInfo (time AS DOUBLE, peripheral AS BLEPeripheral, service AS BLEService, kind AS INTEGER, message AS STRING, err AS LONG)

IF kind = 1 THEN

 ! Get the characteristics.

 DIM characteristics(1) AS BLECharacteristic

 characteristics = service.characteristics

 FOR i = 1 TO UBOUND(characteristics, 1)

   IF debug% THEN

     PRINT "Found characteristic "; i; ": "; characteristics(i).uuid


   IF characteristics(i).uuid = infoUUID$ THEN






BLEServiceInfo is called as the characteristics for the services are discovered. As soon as the info characteristic is found, we read it.

! Called to return information from a characteristic.


! Parameters:

!    time - The time when the information was received.

!    peripheral - The peripheral.

!    characteristic - The characteristic whose information

!        changed.

!    kind - The kind of call. One of

!        1 - Called after a discoverDescriptors call.

!        2 - Called after a readCharacteristics call.

!        3 - Called to report status after a writeCharacteristics

!            call.

!    message - For errors, a human-readable error message.

!    err - If there was an error, the Apple error number. If there

!        was no error, this value is 0.


SUB BLECharacteristicInfo (time AS DOUBLE, peripheral AS BLEPeripheral, characteristic AS BLECharacteristic, kind AS INTEGER, message AS STRING, err AS LONG)

IF kind = 2 THEN

 DIM ch AS BLECharacteristic

 DIM value(1) AS INTEGER

 value = characteristic.value

 SELECT CASE characteristic.uuid

   CASE infoUUID$

     ! The device returned the initial information.

     DIM value(0) AS INTEGER

     value = characteristic.value

     IF debug% THEN

       PRINT "Info: "; valueToHex(value)

     END IF

     IF (value(1) BITAND $02) = $02 THEN

       ! Start watching for data from the device.

       ch = findCharacteristic(txUUID$)

       peripheral.setNotify(ch, 1)

       ! Set the mode to remote command mode.

       value = [2]

       ch = findCharacteristic(modeUUID$)

       peripheral.writeCharacteristic(ch, value, 1)


       PRINT "This device does not support terminal mode."


     END IF

BLECharacteristicInfo is called when the device returns information after we read a characteristic. The first characteristic handled is the info characteristic; the read request was issued earlier in BLEServiceInfo. The program starts watching the TX characteristic, which is how the device will send information back to the terminal program. It also sets the command mode to 2. This tells the device we want to use the AT command set to send commands directly to the device. Setting the mode to 1 tells the device to pass anything it sees on through the serial port. There are two other modes, 0 for idle and 4 for firmware updates. You might use idle mode if you want to put the device to sleep after accessing it, but there is no reason to use firmware update mode unless you really have new firmware to load.


     ! The device sent back information via TX.

     data% = characteristic.value

     data$ = ""

     FOR i = 1 TO UBOUND(data%, 1)    

       IF data%(i) <> 13 THEN

         data$ = data$ & CHR(data%(i))

       END IF


     PRINT data$

When information comes back from the device, it comes through the TX service. Our terminal program prints this information to the techBASIC console.

   CASE modeUUID$

     ! The device sent back the mode.

     data% = characteristic.value

     IF debug% THEN

       PRINT "Mode: "; data%(1)

     END IF


     PRINT "Unexpected value from "; characteristic.uuid; ": "; valueToHex(characteristic.value)


The device kindly lets us know when the mode has actually been changed. For our program, this is essentially a debug point, but it is possible to set the mode from hardware. If you wire that possibility into a circuit, this is how the software gets notified that it has been used.

ELSE IF kind = 3 THEN

 ! Write response received.

 IF debug% THEN

   r$ = "Response from characteristic " & characteristic.uuid

   r$ = r$ & " with error code " & STR(err)

   PRINT r$


 ! All write responses indicate we can accept a new command. Set the

 ! flag, but be sure and wait a short time for other response pieces to

 ! arrive.

 IF characteristic.uuid = modeUUID$ THEN

   ! The mode has been set.

   IF debug% THEN




 commandsAllowed = 1

 commandTime = time + delay



The program sends commands to the device using the RX characteristic. We'll see that code in a moment. The device calls BLECharacteristicInfo with kind set to 3 after processing the command sent via the RX service.  This happens the first time after the mode is set to command mode, indicating the device is ready for us to start sending commands. It happens again after each command is processed, letting us know another command can be sent. The program sets commandsAllowed to 1, indicating that it's OK to accept new commands, and sets the timer to a half second in the future, indicating when it is OK to process the commands.

! Called when nothing else is happening.


! Check to see if commands can be accepted. If so, get a user command.

SUB nullEvent (time AS DOUBLE)

IF commandsAllowed AND (time > commandTime) THEN

 ! Let the user type a command, sending it to RX.

 LINE INPUT "> "; line$

 DIM line%(LEN(line$) + 1)

 FOR i = 1 TO LEN(line$)

   line%(i) = ASC(MID(line$, i, 1))


 line%(UBOUND(line%, 1)) = 13

 DIM ch AS BLECharacteristic

 ch = findCharacteristic(rxUUID$)

 blueRadiosPeripheral.writeCharacteristic(ch, line%, 1)

 ! Don't allow additional commmands until this one is complete.

 commandsAllowed = 0



nullEvent is called whenever the program isn't doing something else, like processing information sent back from the Bluetooth low energy device. Assuming commands are allowed, which happens after initialization is complete or the last command is processed, it puts up a prompt asking for a new command. Once the command is typed, it's sent to the device and subsequent input is disabled until the command is complete.

! Convert an array of byte values to a hexadecimal string.


! Parameters:

!value - The array of bytes.


! Returns: A string of hexadecimal digits representing the value.


s$ = ""

FOR i = 1 TO UBOUND(value, 1)

 s$ = s$ & RIGHT(HEX(value(i)), 2)


valueToHex = s$


valueToHex is a utility function used to print formatted hexadecimal values.

! Find a characteristic for the main blueRadiosUUID service by

! characteristic UUID. This cannot be done until after characteristics

! have been discovered.


! Parameters:

!uuid - The UUID of the characteristic to find.


! Returns: The characteristic.

FUNCTION findCharacteristic (uuid AS STRING) AS BLECharacteristic

! Find the main BlueRadios service.

DIM availableServices(1) AS BLEService

availableServices =

FOR a = 1 TO UBOUND(availableServices, 1)

 IF availableServices(a).UUID = blueRadiosUUID$ THEN

   ! Find the characteristic.

   DIM availableCharacteristics(1) AS BLECharacteristic

   availableCharacteristics = availableServices(a).characteristics

   FOR c = 1 TO UBOUND(availableCharacteristics, 1)

     IF availableCharacteristics(c).uuid = uuid THEN

       findCharacteristic = availableCharacteristics(c)

       GOTO 99

     END IF




PRINT "An expected characteristic was not found."




findCharacteristic is a utility function used to find a specific characteristic on the device. It loops over the characteristics on the device until it finds the one we're asking for and then returns it.

That’s it. Most of the work is actually setting up and handling the handshaking needed with any Bluetooth low energy device. It's time to make sure the hardware and software work.

Power up the BR-XB-LE4.0-S2 and run the terminal program. After a few seconds, you should see a > prompt on the techBASIC console, and the LED hooked to pin PIO_2 should light, indicating there is an active Bluetooth low energy connection. Type the command


The BR-XB-LE4.0-S2 should respond


Digital Output

There are several digital output pins on the BR-XB-LE4.0-S2. Some are used for various housekeeping functions or for serial I/O. We've used one of those already; pin PIO_2 is set high when there is an active Bluetooth low energy connection. We wired an LED to this pin to indicate the connection status. Three pins are readily available for our use, PIO_0, PIO_8 and PIO_9. We're going to test digital output by wiring an LED to each pin. You can use resistorized LEDs like the ones shown in the photo, or standard LEDs in series with a 330Ω resistor.



and the lower LED should light. The ATSPIO command is used to set the state and value of a digital I/O pin. The first number is the number of the pin, one of 0, 8 or 9 for our circuit. The second number is 0 to use the port for input, or 1 to use it for output. We're sending signals to the pin to light or dim an LED, so we set this value to 1. The last number is 0 to turn the pin off, dimming the LED, or 1 to turn it on. Go ahead and turn some LEDs on or off with the command.

Digital output can be used for anything from lighting a simple LED to controlling an H bridge used to turn an electric motor on or off.

Several other pins can be used for digital I/O if the functionality they normally control is disabled. See the specification sheet and command set for details.

Digital Input

Start by setting all three digital I/O pins to input using the commands




Remove the LEDs from the previous circuit and grab a piece of wire. Connect one end to PIO_0, leaving the other loose for the moment, or better still, connecting it to ground. Enter the command


This asks for the current state of pin 0. The program responds


telling you digital I/O pin 0 is off. Now connect the wire to +3.3V and try again. This time, the program responds


to let you know digital I/O pin 0 is on. The other digital input pins should respond the same way. Keeping in mind that the BR-XB-LE4.0-S2 wants 3V, not 5V, this gives three pins that can be wired to switches, TTL circuits with a couple of resistors to drop the voltage from 5V to 3V, or any other on/off sensor you might want to use, like a contact sensor on the front of a robot. As with digital output, there are other pins that can be used for digital input if their normal functionality is disabled.

Analog Input

You can fry the BR-XB-LE4.0-S2 by applying a voltage across a digital input that is set as an output. Always set a pin to input using the ATSPIO command with a second parameter of 0 before applying a voltage to a pin.

Analog input ports measure a voltage, converting it to a digital value. There are five available analog-to-digital converters (ADC). Each of them measured voltages in the range 0 to 1.25V.

Pick any potentiometer you have available, and a resistor whose value is about 90% of the maximum resistance of the potentiometer. This will allow a voltage drop of 0 to 1.25V across the potentiometer. Wire the circuit to one of the ADC pins as shown. The photo shows the circuit connected to ADC2. From the terminal program, type


The program will respond with a value from 0 to 1250 indicating the voltage across the potentiometer, perhaps


as I saw for one of my readings. Changing the resistance of the potentiometer will change the value reported by the ATADC? command. The same idea works with the other ADC ports, too.

Analog inputs are used with a wide variety of sensors, from simple thermistors to sophisticated accelerometers. Any sensor that generates an analog signal should work fine, although you may need to be careful of the voltages. As always, don't go over 3.3 volts.

Exploring More Commands

There are a lot more commands available, from commands that switch the mode back and forth to serial to status commands. Blue Radios publishes the complete command set in the document nBlue Bluetooth 4.0 AT.s Command Set. You'll need to join their forum to get a copy of the document, and proof of purchase of the BR-XB-LE4.0-S2 will be required to join. See their software page for instructions.

As you can see, this tiny Bluetooth low energy device is a fantastic part for building hobby projects. It’s scalable, too. For a few dozen devices, the BR-XB-LE4.0-S2  can be used as a part of the circuit. If the project warrants tens of thousands of parts, Blue Radios can mass produce the components. Of all of the Bluetooth low energy parts available as 2012 comes to a close, this is my personal favorite for bobby projects.

Download the source for the terminal program by clicking the Download button. See the techBASIC Quick Start Guide for step-by-step instructions on moving source code to techBASIC on your iPad or iPhone.