ECE4960-2022

Course on "Fast Robots", offered Spring 2022 in the ECE dept at Cornell University

This project is maintained by CEI-lab

Cornell University: ECE 4960

Return to main page

Lab 2, Bluetooth

Objective

The purpose of this lab is to establish communication between your computer and the Artemis board through the bluetooth stack. We will be using Python inside a Jupyter notebook on the computer end and the Arduino programming language on the Artemis side.

Prelab

Before you begin this lab, please read up on this fantastic summary of Bluetooth Low Energy (BLE).

Computer Setup

Install Python

You will need to install Python 3 and pip. If you already have them installed, please make sure you have the latest releases (Python >= 3.9 and pip >= 21.0).

How to check for versions? In a Command Line Interface (CLI), run the below commands to check for Python and pip versions:

Some Windows users may need to use `python` instead of `python3`

python3 --version
python3 -m pip --version


Minimum Requirements: Linux kernel 4.15+ and bluez 5.48+

Simply use your package manager to install python3 and pip :).

Minimum Requirements: Windows 10

  1. Install/Upgrade Python 3.10
  2. Install/Upgrade pip

Recommended OS: macOS 12.0

It may work with (some) older versions. If you have issues with older versions, contact the teaching team. We will try out best to help you.

  1. Open a terminal and run the command: python3.
  2. This should bring up a xcode dialog box asking you to install the necessary software.

[Relevant xkcd]

Virtual Environment

A virtual environment is a Python environment such that the Python interpreter, libraries and scripts installed into it are isolated from those installed in other virtual environments, and (by default) any libraries installed in a “system” Python, i.e., one which is installed as part of your operating system.

Install venv

Run the following commands in a Command Line Interface (CLI):

  1. Install venv
     python3 -m pip install --user virtualenv
    
  2. Navigate to your project directory (folder).

    Place all your Python scripts and Jupyter notebooks within this directory.

  3. Create a new virtual environment named “ece4960_ble”
     python3 -m venv ece4960_ble
    

    venv will create a virtual Python installation in the newly created ece4960_ble directory.

Using a virtual environment

For any lab using Python scripts (or Jupyter notebooks), you will first need to activate your virtual environment.

  1. Activate the virtual environment

    This will only work from the project folder (the one containing the ece4960_ble folder).

    source ece4960_ble/bin/activate
    .\ece4960_ble\Scripts\activate
    source ece4960_ble/bin/activate
  2. Your CLI prompt should now have the prefix (ece4960_ble).
  3. To deactivate the virtual environment, run the following command:
     deactivate
    

    Your CLI prompt should no longer display the prefix.

Install Python Packages

  1. Activate your virtual environment:

    Your CLI prompt should now have the prefix (ece4960_ble).

  2. Install packages:
     pip install numpy pyyaml colorama nest_asyncio bleak jupyterlab.
    

    NOTE: In Windows, you may see an error while installing some of these packages. Follow the download link in the error message to download and install (the unnecessarily huge) C++ build tools.

Lab2 Codebase

NOTE: The codebase has been updated on 21 Feb, 2022 to fix a bug (in the Arduino code) related to converting negative float values into a string. Please re-download the codebase or apply (manually or using git) this patch to your old codebase.

  1. Download and unzip the codebase into your project directory.

    MD5SUM checksum: e2b9c785dfa6d7f27749d3ba016e0537

    Last Updated: 21 Feb, 2022 (Version: 1.1)

  2. Copy the ble_python directory into your project directory.

Start Jupyter Server

We will be using Jupyter notebooks to write Python code. Before you can open a Jupyter notebook, you need to start the Jupyter server. If the Jupyter server is stopped, you will not be able to run/open/modify any Jupyter notebooks.

  1. In a CLI, navigate to your project directory and activate your virtual environment.
  2. Start the Jupyter server

    The Jupyter server can only access notebooks from the directory it was started in.

     jupyter lab
    

    macOS users may have to use Jupyter lab (capital J).

  3. A browser tab should open up with JupyterLab.

    If no window opens up, click on the URL displayed in the CLI.

You will be writing most of your Python code using this browser window on Jupyter notebooks. For students proficient in Python, you may choose to write some of your modules in Python script files and import them into your Jupyter notebook.

If you are new to JupyterLab, please go through the Introduction to JupyterLab tutorial (available under the Tutorials page).

Artemis Board Setup

Install ArduinoBLE

Install ArduinoBLE from the library manager (Tools -> Manage Libraries…) in the Arduino IDE.

Burn Artemis Codebase

  1. Load and burn the sketch ble_arduino.ino into the Artemis board from the directory ble_arduino in the codebase.

    Make sure you change the serial baud rate to 115200 bps.

  2. The Artemis board should now print its MAC address.

Code Summary

The Python and Artemis packages provide you with the base code necessary to establish a communication channel between your computer and the Artemis board through BLE. You will use them to setup an initial communication channel in this lab, and work towards a more comprehensive robot control protocol in your future lab sessions.

Bluetooth Library Limitations

Though a characteristic value can be up to 512 bytes long (according to the Bluetooth Core Specification), the ArduinoBLE library limits the maximum size to 255 bytes. We are using a more conservative size limitation of 150 bytes. The provided Python codebase throws an error if you attempt to send data that is larger than the member variable ArtemisBLEController.max_write_length (150 bytes). On the Arduino side, the macro MAX_MSG_SIZE (defined in EString.h) is used to set the character array sizes used in various classes.

Processing Commands on the Artemis Board

The ArduinoBLE library provides different types of constructors for handling different types of data. We recommend using BLEFloatCharacteristic, BLEIntCharacteristic and BLECStringCharacteristic (a new type defined in BLECStringCharacteristic.h); these three types should be sufficient for all the labs.

Refrain from using the C++ish data type “string” in Arduino and its associated BLE constructor BLEStringCharacteristic unless you absolutely know what you are doing. Though it may seem to simplify handling strings, they could pose issues in later labs (Ref).

A string in C (also known as C-string) is an array of characters, followed by a NULL character. The class EString (Estring stands for EnhancedString, defined in EString.h) can be used to easily manipulate character arrays. The header provides getter and setter functions to convert between a character array and a EString object. The append() functions allow to append a float, double, int, character array or string literal to the EString. All the functions defined inside EString are considered to be “unsafe” because they are vulnerable to buffer overruns; modify or use them accordingly.

A robot command is a string of the format "<command_type>:<value_1>|<value_2>|<value_3>...". The BLECStringCharacteristic constructor is used to define a writable string GATT characteristic for communicating robot commands. The command type is an integer, and a value may be a float (or double), integer or string literal.

The characters : and | are used as delimiters for the command type and different values. Command messages received on the Artemis that do not strictly match this format may lead to undefined behavior, due to the functions (itoa, atoi, strtok) used to tokenize the robot command string. The command format, command type, value type and delimiters may be changed in RobotCommand.h.

For example, we could use the SEND_TWO_INTS command to send two integers 2 and -3 to the Artemis board (or robot) from the computer. The string data that is essentially sent (or written) would be the string "1:2|-3|". The Arduino sketch (in the base code) essentially reads this data and determines the type of the robot command by extracting the integer value in the string data before the character :. It then seeks to extract the two integers from the rest of the string data which are separated by the character |.

The class RobotCommand (defined in RobotCommand.h) is used to extract values of different data types from the robot command string sent to the Artemis board. The functions get_command_type() and get_next_value() internally use strtok() to extract the command type, float, int and string literals from the robot command string.

The sample sketch file ble_arduino.ino defines 2 Read and 1 Write BLE GATT characteristics; one for receiving robot commands, and two for sending a string and a float value. The setup() function sets up the BLE characteristics and provides an example of how to utilize the EString class. The loop() function listens for connections and handles the communication in read_data() and write_data() The handle_command() function together with the enum CommandTypes depicts how a command message may be handled. It also provides some examples of using the get_command_type() and get_next_value() functions.

Sending Commands to the Artemis Board

  1. In JupyterLab, open the demo.ipynb notebook in the directory ble_python.
  2. Follow the instructions in the notebook to test and use BLE.

    Run through all the cells before you begin to work on the lab tasks.

Configuration

  1. The MAC address of the Artemis board should match the value of artemis_address in the configuration file (connection.yaml).

    A valid MAC address is a 12 digit hexadecimal number, often represented as 6 pairs of hexadecimal digits separated by colons. If the Artemis board displays a MAC address that is lesser than 12 digits, left pad the appropriate pairs with 0s. For example, if the displayed MAC address is C0:C2:8A:89:98:8, update the configuration file(connection.yaml) with

    artemis_address: 'C0:C2:8A:89:98:08'
    
  2. The UUIDs used in the Arduino sketch should match those used by Python in the configuration file (connection.yaml).
  3. The command types defined in enum CommandTypes in the Arduino sketch should match those defined in cmd_types.py.

Tasks

  1. Send an ECHO command with a string value from the computer to the Artemis board, and receive an augmented string on the computer.

    For example, the computer sends the string value “HiHello” to the Artemis board using the ECHO command, and the computer receives the augmented string “Robot says -> HiHello :)” from a read GATT characteristic.

  2. Send three floats to the Artemis board using the SEND_THREE_FLOATS command and extract the three float values in the Arduino sketch.
  3. Setup a notification handler in Python to receive the float value (the BLEFloatCharactersitic in Arduino) from the Artemis board. In the callback function, store the float value into a (global) variable such that it is updated every time the characteristic value changes. This way we do not have to explicitly use the receive_float() function to get the float value.
  4. In your report, briefly explain the difference between the two approaches:
    1. Receive a float value in Python using receive_float() on a characteristic that is defined as BLEFloatCharactersitic in the Arduino side
    2. Receive a float value in Python using receive_string() (and subsequently converting it to a float type in Python) on a characteristic that is defined as a BLECStringCharactersitic in the Arduino side

Additional tasks for students in ECE 5960

Perform an analysis on the communication performance.

TIP: Use the notification handler for this. If you find a better way, include your approach in the write-up.

  1. Effective Data Rate: Send a message from the computer and receive a reply from the Artemis board. Note the respective times for each event, calculate the data rate and include at least one plot to support your write-up.
  2. Reliability: What happens when you send data at a higher rate from the robot to the computer? Does the computer read all the data published (without missing anything) from the Artemis board? Include your answer in the write-up.

Write-up

To demonstrate that you’ve successfully completed the lab, please upload a brief lab report (<600 words), with code snippets (not included in the word count), photos, and/or videos documenting that everything works and what you did to make it happen.