Lab 1

Preview

During this lab, we wanted to become more comfortable with the Artemis board, since we would be using this microcontroller for the duration of this class. In order to do so, I tested some basic example programs on the board, and implemented basic bluetooth functionality to make it compatible with the jupyter lab virtual environment.

Test Programs

After receiving the board, the first order of business was making sure that it worked properly. To do this, I was tasked with uploading a few example programs onto the board and making sure the output was as expected. The first one was a blink program, just to make sure that I could actually program the Artemis board. Next, I made sure that the serial communication worked, and that two way messages could be sent and received. After that, I tested the analog temperature sensor on the board. And finally, I also ensured that the microphone functioned properly.

You can see a video of the blink program, and screenshots of the serial monitor during the other tests below.

Blink Serial
Temperature Microphone

Setting up Bluetooth

The next section of the lab involved getting bluetooth working with the Artemis board. Following the instructions to create a virtual environment on my laptop was straightforward. Next, I uploaded the example BLE script to my Artemis and obtained its MAC address.

Virtual Env
MAC Address

After that, I generated a UUID to use, and updated my connections yaml file to use it and the MAC address found earlier. Using this, I was able to successfully connect to my microcontroller. However, this took a few tries. I initially kept getting weird timeout errors where my laptop was unable to find my robot, despite it showing up on a bluetooth scanner. The solution, it turned out, was two-fold. On one hand, I had mistakenly selected the wrong board type in the Arduino IDE when programming my Artemis. On the other, with the help of Jonathan, I also had to make a small change to the provided base_ble.py to connect to the first suitable device found, instead of proceeding to accidentally filter my Artemis board out of the list of candidates in _get_ble_device().

UUID Connections
Connected

Using Bluetooth

With a connection established, I proceeded to the other tasks in the lab. I modified the Arduino script to implement the ECHO case. The provided code already read the incoming message and placed it in char_arr. After some experimentation, I found that by simply clearing then appending the incoming message into tx_estring_value, I could write its value to tx_characteristic_string, which would then send the message back to my laptop in the main BLE loop via write_data(). Using the receive_string() method in the juypter notebook, I completed a loop where my laptop would recieve an echoed value from the Artemis that I had originally sent it.

However, this process was tedious and required me to separately run a command to receive messages from my Artemis. So, I created a notification handler to receive timestamps from my robot. Since all my timestamps started with "T:", followed by the numerical time, I simply removed the first two characters from the recieved string before printing the result.

Echo Getting Time
Notif Handler Getting Time with Handler

Bluetooth Experimentation

With my notification handler working well, I did some experimentation on the performance of the data transmission. I set up a couple more command types by adding to the existing Arduino script and modifying cmd_types.py in my jupyter notebook. The first command recorded a timestamp, and sent it to my laptop, and repeated the process for 500 times. You can see the results of this below, where the units are in milliseconds. Evidently, in between the timestamp recording, transmitting the data takes a non-neglible amount of time itself. In the example output below, this transmission can take up to 21ms! If the data being measured changed quickly or high granularity was required, this delay would certainly miss out on important details we'd want to be recorded.

Output

So, a second command was added which flipped the script a little bit. It would first record the 500 timestamps, but this time, one after another without delay. Following this, it would send all datapoints back to my laptop at once. This way, data would be collected pretty much as fast as the Artemis board could record it. As expected, the delay between datapoints decreased substantially to >1ms (the timestamps are the same since I only recorded with ms precision). Since the Artemis board runs on a Ambiq Apollo3 clocked at 48 MHz, optimistically assuming that measuring and recording a timestamp each take a single clock cycle, this results in a period of 41.7ns. This matches up with our measured results since with this period, the board can theoretically take thousands of datapoints within 1ms.

Using this approach, I added a third command which did the same thing as the second, measure-all-timestamps-then-transmit approach. However, this command also recorded the temperature using the onboard sensor. You can see the results below. As expected, the delay between datapoints is still substantially better than when transmitting each point before recording the next, but is also substantially slower than when we only recorded timestamps. After doing some research online, this can be easily explained by the fact that analogRead(), the function used to measure the temperature, is a famously inefficient and time-consuming function to run, and is primarily used over faster alternatives for convenience purposes. However, even this delay is still neglible compared to the bluetooth delay we previously measured.

Measuring Timestamps Measuring Temperature

Overall, comparing these approaches, the second approach of recording all data first is superior in most cases due the much greater granularity of data it can gain. However, there are a few cases where the first approach of transmitting each datapoint immediately may be better. One might need the data as soon as it is measured to be processed on the receiving laptop, for example, like if the robot was using bluetooth to transmit data about its surroundings that the laptop might use to decide what path the robot should take or actions it should execute. Additionally, this approach also uses significantly less memory, since it can discard previously measured datapoints as soon as they are sent to the base station. On the other hand, storing all the data before transmission can get expensive quickly. With only 384 kB of RAM, assuming we are measuring temperature and timestamps (which require 4 and 2 bytes respectively), only allows us to measure up to 65536 datapoints. This can run out quite quickly with our datapoint gathering speed, and can run out even faster if other large blocks of memory are used elsewhere. So, using the first approach would also be suitable if you would like to measure a very large amount of data or if memory is limited.