Introduction
The purpose of Lab 2 was to set up a Bluetooth Low Energy (BLE) communication between the Artermis Nano and the computer. The computer would interface with the board by sending and receiving data from a Python program in a Jupyter Notebook to an Arduino program.
Prelab
Setup
I first updated Python and pip to the latest versions, and installed a Python virtual environment (venv). A virtual environment allows for the installation of Python packages that is separate from other environments and the main Python installation. This way, the specific environment can have specific versions of packages that might otherwise conflict with the other environments. Then, after installing a few Python packages on the virtual environment, I flashed the Artermis with the Arduino Bluetooth sketch, causing it to print the board's MAC address to the Serial Monitor as can be seen below.
Codebase
The codebase is separated between Python and Arduino. The Python code consists of various modules responsible for establishing a Bluetooth connection to the Artermis, defining an enumeration of command types, creating a method to print outputs in a comprehensible manner (by including timestamp, color-coding the information, etc.), and assisting with debugging using log messages. The main file utilizing these modules is the Jupyter Notebook file demo.ipynb. The notebook incrementally generates a UUID, establishes a BLE connection, receives data from the Artemis as programmed initially on the Arduino sketch described below, sends commands to the Artemis and prints out the response, and finally disconnects the BLE connection, each step generating an output that is easily readable by the user with the ability to only run each section with memory of the previous sections.
The Arduino code handles the data sent to the Artemis via Bluetooth from the Jupyter Notebook depending on the type of command. The command establishes an expected set (or lack) of arguments which, for example, can be pretty-printed to the Serial Monitor. Also, some commands are handled by sending back some specific data such as a timestamp or processor temperature back to the notebook. Additionally, there are C++ header files defining helper modules for the Arduino code such as an enhanced String class with added functionality on top of a String, and a specifc type of String that is sent via Bluetooth by the Bluetooth module.
Lab Tasks
Configurations
After obtaining the MAC address, I updated the appropriate value in connections.yaml. Then, I generated a new UUID in the notebook and replaced the value in the Arduino code as well as in the same yaml file. Finally, to ensure that the bluetooth connection would be established on my Windows computer, I changed
if IS_ATLEAST_MAC_OS_12:to
if True:Unfortunately, this did not work, and I ended up using the former condition in the if statement for a successful connection.
Jupyter Notebook demo.ipynb
The next step was to test that the BLE connection worked by flashing the Arduino sketch to the board and running the notebook cells that performed the aforementioned set of incremental steps. As can be seen below, I successfully established a BLE connection.
Then, I ran the cell sending the "PING" command to the Artemis and received a "PONG" back. In the next cell, the notebook sent the "SEND_TWO_INTS" command with "2|-6" as the arguments. The Arduino code parsed the arguments and pretty printed them to the Serial Monitor. Both of these can be seen in the images below.
Finally, I ran the disconnect cell and the Bluetooth connection was terminated.
Send Echo
This task required sending an ECHO command with a String as an argument from the notebook to the Artemis and receiving back the same string with more characters prepended and appended to it. On the Python side, I wrote a function that would send the command with a parameterized value and print the response:
On the Arduino side, I added the following handler code in the case where the ECHO command was received:
Invoking the function with argument "Command 1" resulted in the following response:
Artemis Robot Says: "Command 1" :D
Get Time
This task required adding a new command type GET_TIME_MILLIS, sending which to the board would cause it to reply with the current time in milliseconds formatted as "T:xxxxx" in accordance with the String characteristic type. On the Python side, I wrote a similar function that would send the command and print the response:
On the Arduino side, I added the following handler code in the case where the GET_TIME_MILLIS command was received:
Invoking the function resulted in the following response:
T:424303
Notification Handler
This task required setting up a notification handler in Python to receive any string value. This would essentially serve as an event listener for the receive string event, and invoke a callback function. The callback function converted the bytearray to a string and printed it. The Python code can be seen below.
I invoked the notification handler by sending a GET_TIME_MILLIS command, whose response was sent to the callback function and printed as a String to the output:
T:428983
Get Temperature
This task required adding a new command GET_TEMP_5s, whose receipt on the Arduino side would trigger the handler code to send back an array of five timestamped processor temperature readings, each taken every second for 5s upon receipt. The Python code for this can be seen below.
Evidently, I reused the same callback function to print the response from the Artemis. On the Arduino code side, I added the new command type and added handler code as follows:
As evident from the code, I sent each characteristic string after one pair of timestamp and temperature reading as so to prevent going over the maximum character limit. Invoking the Python function caused the following response to be printed:
Get Temperature Rapid
Similar to the previous task, this task required adding the GET_TEMP_5s_RAPID command, which would send back every temperature reading over a 5 second period, without a 1 second delay as before. The Python code for this can be seen below.
The Arduino code to handle this new command type can be seen below.
Again, similar to the previous task, I sent each string after only one pair of timestamp and temperature reading to avoid going over the limit. Invoking the function caused the following abridged output:
Limitations
If the board has 384 kB of RAM, this is equivalent to 3145728 b of memory, which is the exact value that is displayed on the Arduino IDE output after flashing the board with any sketch. Hence, assuming that the memory is not occupied by anything else, exactly that much data can be stored to send without running out of memory.
5 seconds of 16-bit values taken at 150 Hz would result in 5 s x (150 times / 1 s) x (16 b) = 12000 b of data. It would take 3145728 b / (12000 b / 5 s) = 1,310.72 s, or approximately 21 minutes and 50 seconds to saturate the RAM with data. Considering the task from above where a pair of timestamp and temperature values were stored and sent, albeit immediately, this is two values taken at some sampling rate for a period of 5 seconds. If they were not sent immediately after one pair of data, the RAM would saturate much faster, causing major limitations.