Atermis and Bluetooth
This lab aimed to provide the necessary background to establishing a connection between the Artemis and the Bluetooth stack via a series of Python commands sent from a computer.
Lab 1A
Blink
Serial
Temperature sensor
Microphone
Lab 1B: Prelab
To begin the lab, I double checked the versions of Python and pip I had running on my computer where the latest releases and created a new virtual environment. Upon installing the required packages, I proceeded to browse through the codebase to understand the different functions that I will be using in the lab. This was a very important step that reaped great benefits later on in the lab when I was able to quickly identify the functions I needed to use and how to use them.
Configuration
To setup the Artemis and Bluetooth connection, I began by installing the ArduinoBLE
library,
copied the MAC address printed out to the serial monitor after burning the code to the board.
I updated the connection.yaml
file with the appropriate configurations, including a UUID
I generated for the BLEService
.
Note: My computer's MAC address is omitted from the snippet below for privacy reasons.
artemis_address: "[redacted]"
ble_service: "b4735562-362d-4d17-b3e4-c4cd7256fa09"
// ...existing code...
Lab Tasks
ECHO
The ECHO
command asks the board to receive the same string that the computer sent to it. This is a simple test to ensure that the connection is working.
I sent the string HiHello
to the board and returned an augemnted string Robot says -> HiHello:)
which I could see was correctly received by the board in my notebook.
And the string was successfully received by my computer.
SEND_THREE_FLOATS
The SEND_THREE_FLOATS
command asks the board to send three floating point values to the computer.
This was an extension of the existing SEND_TWO_INTS
command, which was already provided. I simply adjusted the code
work for floating point values. The command is sent as a string delimtted by |
which enables the board to parse the floating point
characters properly. I then simply printed the three floating point values to the serial monitor.
GET_TIME_MILLIS
The GET_TIME_MILLIS
command asks the board to reply with the current time in milliseconds to the computer in a format of T:123456
.
This was a new commmand so I had to update the CommandTypes
enum in the C code and the cmd_types.py
file in the Python code. The command ultimately
involved a call to the millis()
function by the board and correctly formatting that to what the computer expected.
int time;
time = (int)millis();
// ...other code...
and on the computer, we receive the time in milliseconds correctly formatted.
NOTIFICATION HANDLER
I setup a notification handler that could receive bytes from the board and process it into strings. This means I didn't need to call the command to receive a string from the board every time I needed. The notification handler was set up as:
def notification_handler(uuid, byte_array):
print(ble.bytearray_to_string(byte_array))
ble.start_notify(ble.uuid['RX_STRING'], notification_handler)
Then when I sent a command like GET_TIME_MILLIS
to the board, the handler would receive the response and print it out.
LOOP_GET_TIME_MILLIS
To achieve this task, I created a new command called GET_TIME_MILLIS_LOOP
that gets the current time in milliseconds and sends it to my laptop
I did this for 1000
data points and use a notification handler in python to received the data in an array.
for (int i = 0; i < 1000; i++) {
// ...code to get time in milliseconds and
// send it to the board similarly to GET_TIME_MILLIS
}
In python, I used the following to send the command to the board ble.send_command(CMD.GET_TIME_MILLIS_LOOP, "")
I then used the following methodology to determine the effective data transfer rate of this method after all 1000 time stamps were received.
Taking the last time stamp minus the first time stamp, divided by 1000 points, we have about 21.453
milliseconds per message.
Hence, approximately 46
messages per second.
Since we have 6 characters per message, and one byte per character, we transfer 6
bytes per message.
Finally this works out to a rate of 6*46 = 276
bytes per second.
SEND_TIME_DATA
In this command, I prepopulated an array initialized globally with 1000
time stamps.
I then sent this array back to the computer one by one using the SEND_TIME_DATA
command.
The received data was handled by a notification handler and stored in an array in python.
The following method builds the array of time stamps.
void build_array() {
for (int i = 0; i < 1000; i++) {
time_array[i] = (int)millis();
}
}
Then we send it from the board to the computer.
case SEND_TIME_DATA:
build_array();
for (int i = 0; i < 1000; i++) {
tx_estring_value.clear();
tx_estring_value.append(time_array[i]);
tx_characteristic_string.writeValue(tx_estring_value.c_str());
}
break;
GET_TEMP_READINGS
Here, I created a global array of 1000
temperature readings seprately from the time readings.
Then when the GET_TEMP_READINGS
command is sent to the board, it will send the temperature readings and time readings one by one.
The following method builds the array of temperature readings.
void build_temp() {
for (int i = 0; i < 1000; i++) {
temp_array[i] = (int)getTempDegC();
time_array[i] = (int)millis();
}
}
Then we send it from the board to the computer.
case GET_TEMP_READINGS:
build_temp();
for (int i = 0; i < 1000; i++) {
// ..code to send both time and temp readings...
}
break;
The data was received and processed by a notification handler in python.
Comparison of Each Method
As earlier mentioned, the LOOP_GET_TIME_MILLIS
which computes the time and send the message to the computer on each iteration had an effective
data transfer rate of 276
bytes per second. On the other hand, SEND_TIME_DATA
command pre-computes the time and then send it one by one. By a similar analogy,
I calculated the effective data transfer rate of the SEND_TIME_DATA
command as follows:
Taking the last time stamp minus the first time stamp, divided by 1000 points, we have about 0.032
milliseconds per message.
Hence, approximately 31250
messages per second.
Since we have 6 characters per message, and one byte per character, we transfer 6
bytes per message.
Finally this works out to a rate of 6*31250 = 187500
bytes per second.
This is a significant difference in the data transfer rate. The second method was able to send 1000 data points faster than the first method.
However, since the Artemis board is memory-constrained to about 384kB
of RAM, there is a trade off between the data transfer rate and the memory usage.
In cases where the data is not very large, we can use the SEND_TIME_DATA
for faster data transfer, but in other cases, for instance, where the array is bigger
than 384kB
, we can use the LOOP_GET_TIME_MILLIS
command.
Each time reading is a 32-bit integer, we can therefore store up to 90,000 data points. However, the RAM also hold other information so this number is only an approximation. When we store bluetooth temperature and time readings, this number is shared between both arrays.
Discussion
Through this lab, I gained valuable experience with Bluetooth communication with the Artemis board. I also understood the tradeoffs between real-time data processing and batch data transfer.