Lab 1: The Artemis Board and Bluetooth
Part 1)
The first part of the lab involved installing the Arduino IDE and then connecting the Artemis board. After installing the necessary libraries and board managers for the Artemis I was able to successfully upload code to the microcontroller. This first part included running several Artemis library examples programs on the Artemis. This helped to gain experience using the board LED, serial message interface, the temperature sensor, and pulse density microphone.
Blink it up
The “Blink it up” example program is simple and just toggles the onboard LED on/off for one second each. Below is a video of the LED flashing after I uploaded the program file to the Artemis.
Serial
The “Serial” example involved using the Artemis UART peripherals to communicate with the Arduino serial interface and print into the serial monitor. The program first printed a counting series and then printed whatever the user typed into the serial command line. A screenshot of the serial monitor output is below.
Analog Read
The “Analog read” program uses the Artemis’ ADC to measure the internal die temperature sensor. In the video below, the temperature is being printed to the serial monitor in the “temp (counts)” column. At the start of the video I am not touching the board and the reading is consistently ~32900. After I hold the board you will see the temperature reading rise up to around ~33400.
Microphone Output
The “Microphone output” example uses the pulse density microphone and a fft library to detect the loudest sound frequency it hears. In the video below you can see the frequency being printed to the serial monitor, and then I play a 440-Hz A note near the board.
Part 2)
This part of the lab involved setting up bluetooth communication between the Artemis and my computer. On my computer I used a jupyter notebook in a virtual environment to run Python code to communicate with the Artemis. To do this, I had to use the ArduinoBLE library (bluetooth low energy) to write the code allowing the Artemis to send and receive messages.
Prelab
First I made sure I had Python3 and pip installed on my computer with updated versions. I then used the “venv” Python tool to create a virtual environment for the Fast Robots course with necessary packages installed that could run a localhost Jupyter lab browser on my computer. The lab codebase I had to download for this part consisted mainly of two folders: one folder containing the Arduino code to run on the Artemis and a couple files to define data types for BLE communication and different commands for the Artemis to respond to and execute, and another folder holding the Python code to run in the virtual environment. The Python folder consisted of a BLE controller file defining the interface and functions for using a bluetooth controller, along with configuration files for the bluetooth connection. I also had to install the ArduinoBLE library. After uploading the Arduino code without making any configuration changes to connect with my computer, the Arduino printed to the serial monitor its MAC address to advertise to other devices trying to connect. The output is below.
As mentioned before, there is a connections.yaml file in the lab handout code for the Python end. The first configuration step was to enter the MAC address printed by my Artemis as a variable in the file so that my computer knew which address to look for. The program uses a BLEService object to advertise an address and service and to add different types of characteristics to send/receive. Every Artemis in lab uses BLEService in its program so each artemis will have an associated BLEService UUID, essentially an identifier for a device. In my Arduino code I had to generate a new UUID to use for BLEService so that I wouldn’t connect to any other Artemis’. Once I entered the same generated UUID in the connections.yaml file, my Python program knew to connect with my Artemis which would also be using that same UUID. BLE uses different characteristics for different types of data to be transmitted via bluetooth. The BLEService object I used had characteristics set up for floats and strings. Each characteristic had an associated UUID, and the UUIDs are the same in both the Arduino and Python programs. For example, the Arduino code has a UUID for receiving a string (RX), and the Python program has the same UUID for transmitting a string (TX). This way I can use an ble controller object to send strings/floats to the Artemis securely since each transmissions is using a specific UUID. Below are pictures of the UUID configurations in Jupyter and Arduino where you can see the same UUIDs being used for the BLE service and characteristics.
Finally, here is a screenshot of the Jupyter notebook cell after creating a BLE controller and using it to connect to the Artemis. The image shows how the controller is looking to connect to the same MAC address of the Artemis.
The main tasks for this part consisted of several tests of sending different bluetooth commands for the Artemis to execute.
Echo
To communicate, I used the same enum of different command types in the Python and Arduino code so the Artemis knows how to react to each one. Commands in Python are sent via bluetooth with the command type and the message. For the echo command, I wrote the function in the Arduino IDE to send the same message back slightly altered if the board receives an echo command. Below is a picture of the code I wrote for the robot to handle an echo command, which is a case statement inside a handle_command function called anytime the robot receives a bluetooth message.
Finally, here is a screenshot of the output in Jupyter after sending the echo command. Since the Arduino handler function sends a string back in response, in Jupyter I need to explicitly tell the BLE controller to receive the response message.
Get_Time_Millis
The next command was to have the Artemis reply back with the current time in milliseconds as a string. Using the millis() command, I implemented the handler below.
The message received in Jupyter.
Notification Handler
This task required me to set up a notification handler in Python to call a handler function anytime the BLE controller received a string message from the Artemis. This involved the use of the ble.start_notify, which accept the UUID of the characteristic to listen on (in this case the UUID for receiving a string), and the function to call. I got this to work by sending the Artemis the GET_TIME_MILLIS command from last task. When the Artemis replied with the time in a string, the notification handler triggered and logged the time in the string. This is show in the screenshot below
Time loop
Now, I needed to write a loop in Arduino that just repeatedly gets the current time and then sends it as a string to my computer. The notification handler I wrote in the previous part could then be used to receive all the messages and print the times out so I can measure how fast it takes to send data over bluetooth. To do this I added a new command called TIME_LOOP. Below is the Arduino code I wrote to handler the new command and repeatedly send the current time.
Here are the results in Python of calling the new command after starting notifications.
Based on the log statements containg the timestamp of when each message was sent by the Artemis, the effective data transfer rate is around 7-8 ms.
Send Time Data
For this task I created a global variable for an array capable of holding 1000 timestamps (type unsigned long which is what millis() returns). I altered the TIME_LOOP command handler in Arduino that I wrote for the previous task. Instead of sending each timestamp at each iteration the timestamp is stored in the array. I then had to create a new command called SEND_TIME_DATA. Upon receiving this command the Artemis loops over the array of timestamps and sends each one as a string to my computer. In Python I set up a new notification handler to receive each timestamp string from the Artemis and to append it to a list. Then, I printed the list to make sure it received all 10000 timestamps. The Arduino code for the modified TIME_LOOP command and new SEND_TIME_DATA command is below.
Here is a screenshot of the Python output.
Get Temp Readings
Building off the previous task, I added another global variable for an array to hold 1000 temperature readings from the Artemis’ temperature sensor. I modified the TIME_LOOP command to read the sensor, convert the result to fahrenheit, and then store in the array after populating an entry in the time array. I then added a new command called GET_TEMP_READINGS. I wrote the handler for this command such that it will loop over both the time and temperature arrays and at each step send a string message to my computer containg one time reading and the corresponding temperature reading. In Python, I set up another notification handler to listen for strings and then split the string to get the individual time and temperature readings and store each in a separate list. This way I could print both lists and ensure that both contained all the readings. The altered TIME_LOOP code is below, along with the Arduino code for the new command.
Here is a screenshot of the Python code along with the first part of the list of timestamps.
Here is the output of the end of the time list and the start of the list of temperature readings.
Data Transfer Analysis
In the tasks for this part of the lab there were two main methods of data transfer. The first involved sending data over bluetooth as soon as it became available. This was used when I first wrote the TIME_LOOP command to repeatedly get the timestamp in milliseconds and then send it to my computer. The second method involved polling the sensor for some block of time continuously just gathering the data. Then, after all data is gathered send each data point consecutively. This was used first in the SEND_TIME_DATA command and then again in the GET_TEMP_READINGS command. The first method is beneficial in that you get each data point as soon as it is measured. But, your measurements might be less accurate or more noisy since the time between each measurement is delayed due to the bluetooth latency. This is better suited for time-constrained actions where you need data quickly to meet deadlines. The second method has more accurate measurements since each data point is recorded immediately after the next, but you have to wait longer until you can receive the data. This is better for background measurements and calculations that aren’t needed immediately. The second method can make measurements almost as fast as the clock frequency since each measurement is taken right after the next.
In the previous task I used global variables for arrays of unsigned long and float. These are both 4 bytes. If each data entry is 4 bytes, given the Artemis has 384 kB of RAM, this means the Artemis can store 96,000 data points to be sent. There is still memory overhead for the program and bluetooth communication so in reality that maximum data storage is a bit lower than 96,0000 data points.