4. Embeded Programming

Embedded programming, refers to the development of software for embedded systems. These systems are designed to perform specific tasks within larger mechanical or electrical systems, unlike general-purpose computers, which are designed to handle a wide range of tasks. There are several Programming Languages and development environments (IDEs), all of them with their own capabilities and functionalities.

This week we started programming, for our group assignment we compared different programming languages and the available controllers in our Fab, you can check it here. For my final project, I will be using the built-in accelerometer from the XIAO nRF52840 (sense), so I decided to compare two ways of reading the sensor’s information and sending it via Bluetooth. I will be using CircuitPython and Arduino IDE.

I started by doing some research about the XIAO nRF52840 (sense) to see all its available features. I had previously used this board to make a small project, the manufacturer has a webpage that explains how to use all the features available for the board.

Here’s the pinout of the board I’ll be using:

Here’s a table for all the sensors and built-in features:

CIRCUITPYTHON

I used Visual Studio Code with the CircuitPython extension. You can see all the steps to install CircuitPython on the XIAO here.

After the installation, you will see that one of the files inside the board is called “code.py” Every time you make a code you need to paste it in here and save it (you can’t save another file to program the XIAO). You can add the libraries you need inside the “lib” folder.

I’ve never used Bluetooth from the nRF52840 (sense), so I browsed the web until I found some examples to use the CircuitPython libraries, here’s the link to download the libraries and examples I used. This code gave me an idea of how to make my code:

Bluetooth Example

    # SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries
    # SPDX-License-Identifier: MIT

    # Basic structure example for using the BLE Connect Controller sensors
    # To use, start this program, and start the Adafruit Bluefruit LE Connect app.
    # Connect, and then select Controller and enable the sensors

    from adafruit_ble import BLERadio
    from adafruit_ble.advertising.standard import ProvideServicesAdvertisement
    from adafruit_ble.services.nordic import UARTService
    from adafruit_bluefruit_connect.packet import Packet

    # Only the packet classes that are imported will be known to Packet.
    from adafruit_bluefruit_connect.accelerometer_packet import AccelerometerPacket
    from adafruit_bluefruit_connect.gyro_packet import GyroPacket
    from adafruit_bluefruit_connect.location_packet import LocationPacket
    from adafruit_bluefruit_connect.magnetometer_packet import MagnetometerPacket
    from adafruit_bluefruit_connect.quaternion_packet import QuaternionPacket

    ble = BLERadio()
    uart_server = UARTService()
    advertisement = ProvideServicesAdvertisement(uart_server)

    while True:
        print("WAITING...")
        # Advertise when not connected.
        ble.start_advertising(advertisement)
        while not ble.connected:
            pass

        # Connected
        ble.stop_advertising()
        print("CONNECTED")

        # Loop and read packets
        while ble.connected:
            # Keeping trying until a good packet is received
            try:
                packet = Packet.from_stream(uart_server)
            except ValueError:
                continue

            # Accelerometer
            if isinstance(packet, AccelerometerPacket):
                print("Accelerometer:", packet.x, packet.y, packet.z)

            # Gyro
            if isinstance(packet, GyroPacket):
                print("Gyro:", packet.x, packet.y, packet.z)

            # Location
            if isinstance(packet, LocationPacket):
                print("Location:", packet.latitude, packet.longitude, packet.altitude)

            # Magnetometer
            if isinstance(packet, MagnetometerPacket):
                print("Magnetometer", packet.x, packet.y, packet.z)

            # Quaternion
            if isinstance(packet, QuaternionPacket):
                print("Quaternion:", packet.x, packet.y, packet.z, packet.w)

        # Disconnected
        print("DISCONNECTED")     
                    

You can find this example inside of the zip file exaples>bluebruitconnect. The file is named "bluebruitconnect_sensors.py"

I combined it with a code previously made to read the accelerometer data:

Using the Accelerometer

		import time
		import board
		import busio
		import digitalio
		from adafruit_lsm6ds.lsm6ds3 import LSM6DS3
		import pwmio
		from adafruit_motor import servo
		
		# create a PWMOut object on Pin A2.
		pwm = pwmio.PWMOut(board.A2, duty_cycle=2 ** 15, frequency=50)
		
		# Create a servo object, my_servo.
		my_servo = servo.Servo(pwm)
		
		# Define los pines a los que están conectados los LEDs
		
		led_pin1 = board.D7  #  pin  para el LED
		# Configura el LED como salida
		led1 = digitalio.DigitalInOut(led_pin1)
		led1.direction = digitalio.Direction.OUTPUT
		
		led_pin2 = board.D8  #  pin  para el LED
		# Configura el LED como salida
		led2 = digitalio.DigitalInOut(led_pin2)
		led2.direction = digitalio.Direction.OUTPUT
		
		led_pin3 = board.D9  #  pin  para el LED
		# Configura el LED como salida
		led3 = digitalio.DigitalInOut(led_pin3)
		led3.direction = digitalio.Direction.OUTPUT
		
		
		
		imu_power = digitalio.DigitalInOut (board.IMU_PWR)
		imu_power.direction = digitalio.Direction.OUTPUT
		imu_power.value = True
		time.sleep (0.1)
		i2c = busio.I2C(board.IMU_SCL, board.IMU_SDA)
		sensor = LSM6DS3(i2c)
		while True:
			aсс_х, асс_у, acc_z = sensor.acceleration
			print(f'X:{aсс_х} Y:{асс_у} Z:{acc_z}')
			time.sleep(0.1)
			if(aсс_х<=-8 and асс_у<=2 and асс_у>-1 and acc_z<=4): #recoje
				#led1.value=True
				#led2.value=False
				#led3.value=False
				led1.value=True #nover motor
				led2.value=False #en una direccion
				time.sleep(0.1)
			if(acc_z>=7 and aсс_х>=-3 and aсс_х<= 1): #lanzar placa hacia arriba
				# led1.value=False
				# led2.value=True
				# led3.value=False
				led1.value=False #detener motor
				led2.value=False #detener motor
				my_servo.angle = 44 #lanzo
				time.sleep(0.4)
			   # my_servo.angle = 120 #retorna el servo
			if(aсс_х < 3 and aсс_х > -3 and acc_z<=-5): #nada placa viendo acia abajo 
				# led1.value=False
				# led2.value=False
				# led3.value=True
				led1.value=False #detener motor
				led2.value=False #detener motor
				led3.value=True #prender led que no es nada
				time.sleep(0.5)
				for angle in range(44, 115, 3):  # 0 - 180 degrees, 5 degrees at a time.
					my_servo.angle = angle
					time.sleep(0.2)
				  
					
					 
				# my_servo.angle = 120 #retorna el servo
				# time.sleep(0.1)   
	

And here’s the final result:

Sending Accelerometer data via Bluetooth

		import time
		import board
		import busio
		import digitalio
		from adafruit_lsm6ds.lsm6ds3 import LSM6DS3
		import adafruit_ble
		from adafruit_ble.advertising.standard import ProvideServicesAdvertisement
		from adafruit_ble.services.nordic import UARTService
		
		# Initialize the accelerometer
		imu_power = digitalio.DigitalInOut(board.IMU_PWR)
		imu_power.direction = digitalio.Direction.OUTPUT
		imu_power.value = True
		time.sleep(0.1)
		i2c = busio.I2C(board.IMU_SCL, board.IMU_SDA)
		sensor = LSM6DS3(i2c)
		
		# Initialize Bluetooth
		ble = adafruit_ble.BLERadio()
		uart_service = UARTService()
		advertisement = ProvideServicesAdvertisement(uart_service)
	while True:		
		print("WAITING...")
		# Advertise when not connected.
		ble.start_advertising(advertisement)
		while not ble.connected:
			pass

		# Connected
		ble.stop_advertising()
		print("CONNECTED")
		
		while ble.connected:
			# Read accelerometer data
			acc_x, acc_y, acc_z = sensor.acceleration
			print(f'X:{acc_x} Y:{acc_y} Z:{acc_z}')
			
			# Send data over Bluetooth
			uart_service.write(f'X:{acc_x} Y:{acc_y} Z:{acc_z}\n')
			
			time.sleep(0.1)
				
		print("Disconnected!") 
	

I used the bluetooth setup from the example but since I wasn´t going to handle different connections I changed uart_server for uart_service, and I used the accelerometer setup from my previous code.

I used an Android phone to connect it to the board via Bluetooth, I downloaded the "Serial Bluetooth Termial" app to open a terminal and see the data:

ARDUINO IDE

The second way I tried is by using Arduino IDE. The Seed Studio site explains how to download the right libraries. The zip file also contains several application examples.

After downloading the libraries, I opened the Arduino IDE and opened the following example code, this code connects to a phone to send and recieve data.

Connecting XIAO to phone via Bluetooth

						#include < bluefruit.h>
						#include < Adafruit_LittleFS.h>
						#include < InternalFileSystem.h>
						
						// BLE Service
						BLEDfu  bledfu;  // OTA DFU service
						BLEDis  bledis;  // device information
						BLEUart bleuart; // uart over ble
						BLEBas  blebas;  // battery
						
						void setup()
						{
							Serial.begin(115200);
						
						#if CFG_DEBUG
							// Blocking wait for connection when debug mode is enabled via IDE
							while ( !Serial ) yield();
						#endif
							
							Serial.println("Bluefruit52 BLEUART Example");
							Serial.println("---------------------------\n");
						
							// Setup the BLE LED to be enabled on CONNECT
							// Note: This is actually the default behavior, but provided
							// here in case you want to control this LED manually via PIN 19
							Bluefruit.autoConnLed(true);
						
							// Config the peripheral connection with maximum bandwidth 
							// more SRAM required by SoftDevice
							// Note: All config***() function must be called before begin()
							Bluefruit.configPrphBandwidth(BANDWIDTH_MAX);
						
							Bluefruit.begin();
							Bluefruit.setTxPower(4);    // Check bluefruit.h for supported values
							//Bluefruit.setName(getMcuUniqueID()); // useful testing with multiple central connections
							Bluefruit.Periph.setConnectCallback(connect_callback);
							Bluefruit.Periph.setDisconnectCallback(disconnect_callback);
						
							// To be consistent OTA DFU should be added first if it exists
							bledfu.begin();
						
							// Configure and Start Device Information Service
							bledis.setManufacturer("Adafruit Industries");
							bledis.setModel("Bluefruit Feather52");
							bledis.begin();
						
							// Configure and Start BLE Uart Service
							bleuart.begin();
						
							// Start BLE Battery Service
							blebas.begin();
							blebas.write(100);
						
							// Set up and start advertising
							startAdv();
						
							Serial.println("Please use Adafruit's Bluefruit LE app to connect in UART mode");
							Serial.println("Once connected, enter character(s) that you wish to send");
						}
						
						void startAdv(void)
						{
							// Advertising packet
							Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
							Bluefruit.Advertising.addTxPower();
						
							// Include bleuart 128-bit uuid
							Bluefruit.Advertising.addService(bleuart);
						
							// Secondary Scan Response packet (optional)
							// Since there is no room for 'Name' in Advertising packet
							Bluefruit.ScanResponse.addName();
							
							/* Start Advertising
							* - Enable auto advertising if disconnected
							* - Interval:  fast mode = 20 ms, slow mode = 152.5 ms
							* - Timeout for fast mode is 30 seconds
							* - Start(timeout) with timeout = 0 will advertise forever (until connected)
							* 
							* For recommended advertising interval
							* https://developer.apple.com/library/content/qa/qa1931/_index.html   
							*/
							Bluefruit.Advertising.restartOnDisconnect(true);
							Bluefruit.Advertising.setInterval(160, 320); // en unidades de 0.625 ms
							// in unit of 0.625 ms
							Bluefruit.Advertising.setFastTimeout(30);      // number of seconds in fast mode
							Bluefruit.Advertising.start(0);                // 0 = Don't stop advertising after n seconds  
						}
						
						void loop()
						{
							// Forward data from HW Serial to BLEUART
							while (Serial.available())
							{
							// Delay to wait for enough input, since we have a limited transmission buffer
							delay(2);
						
							uint8_t buf[64];
							int count = Serial.readBytes(buf, sizeof(buf));
							bleuart.write( buf, count );
							}
						
							// Forward from BLEUART to HW Serial
							while ( bleuart.available() )
							{
							uint8_t ch;
							ch = (uint8_t) bleuart.read();
							Serial.write(ch);
							}
						}
						
						// callback invoked when central connects
						void connect_callback(uint16_t conn_handle)
						{
							delay(500);  // Pequeño retraso antes de continuar
							// Get the reference to current connection
							BLEConnection* connection = Bluefruit.Connection(conn_handle);
						
							char central_name[32] = { 0 };
							connection->getPeerName(central_name, sizeof(central_name));
						
							Serial.print("Connected to ");
							Serial.println(central_name);
						}
						
						/**
							* Callback invoked when a connection is dropped
							* @param conn_handle connection where this event happens
							* @param reason is a BLE_HCI_STATUS_CODE which can be found in ble_hci.h
							*/
						void disconnect_callback(uint16_t conn_handle, uint8_t reason)
						{
							(void) conn_handle;
							(void) reason;
						
							Serial.println();
							Serial.print("Disconnected, reason = 0x"); Serial.println(reason, HEX);
						}							
				

I have never programmed this board using Arduino IDE so I also researched how to read the data from the accelerometer, I found a section called “The 6-Axis IMU Usage on Seeed Studio XIAO nRF52840 Sense” which explains how to view the data on the serial monitor.

The following example shows on the serial monitor the data from the built-in sensors.

Viewing Sensors data on the Serial Monitor

	#include "LSM6DS3.h"
	#include "Wire.h"

	//Create a instance of class LSM6DS3
	LSM6DS3 myIMU(I2C_MODE, 0x6A);    //I2C device address 0x6A

	void setup() {
		// put your setup code here, to run once:
		Serial.begin(9600);
		while (!Serial);
		//Call .begin() to configure the IMUs
		if (myIMU.begin() != 0) {
			Serial.println("Device error");
		} else {
			Serial.println("Device OK!");
		}
	}

	void loop() {
		//Accelerometer
		Serial.print("\nAccelerometer:\n");
		Serial.print(" X1 = ");
		Serial.println(myIMU.readFloatAccelX(), 4);
		Serial.print(" Y1 = ");
		Serial.println(myIMU.readFloatAccelY(), 4);
		Serial.print(" Z1 = ");
		Serial.println(myIMU.readFloatAccelZ(), 4);

		//Gyroscope
		Serial.print("\nGyroscope:\n");
		Serial.print(" X1 = ");
		Serial.println(myIMU.readFloatGyroX(), 4);
		Serial.print(" Y1 = ");
		Serial.println(myIMU.readFloatGyroY(), 4);
		Serial.print(" Z1 = ");
		Serial.println(myIMU.readFloatGyroZ(), 4);

		//Thermometer
		Serial.print("\nThermometer:\n");
		Serial.print(" Degrees C1 = ");
		Serial.println(myIMU.readTempC(), 4);
		Serial.print(" Degrees F1 = ");
		Serial.println(myIMU.readTempF(), 4);

		delay(1000);
	}
					

I used the two examples to build the following code:

Sending Accelerometer data via Bluetooth

		#include < bluefruit.h>
		#include < Adafruit_LittleFS.h>
		#include < InternalFileSystem.h>
		#include "LSM6DS3.h"
		#include "Wire.h"
		
		// BLE Service
		BLEDfu  bledfu;  // OTA DFU service
		BLEDis  bledis;  // device information
		BLEUart bleuart; // uart over ble
		BLEBas  blebas;  // battery
		
		// Create an instance of class LSM6DS3
		LSM6DS3 myIMU(I2C_MODE, 0x6A);    // I2C device address 0x6A
		
		void setup()
		{
			Serial.begin(115200);
		
		#if CFG_DEBUG
			// Blocking wait for connection when debug mode is enabled via IDE
			while (!Serial) yield();
		#endif
		
			// Initialize the IMU
			if (myIMU.begin() != 0) {
			Serial.println("Device error");
			} else {
			Serial.println("Device OK!");
			}
		
			// Setup the BLE LED to be enabled on CONNECT
			Bluefruit.autoConnLed(true);
		
			// Config the peripheral connection with maximum bandwidth
			Bluefruit.configPrphBandwidth(BANDWIDTH_MAX);
		
			Bluefruit.begin();
			Bluefruit.setTxPower(4);    // Check bluefruit.h for supported values
			Bluefruit.Periph.setConnectCallback(connect_callback);
			Bluefruit.Periph.setDisconnectCallback(disconnect_callback);
		
			// To be consistent OTA DFU should be added first if it exists
			bledfu.begin();
		
			// Configure and Start Device Information Service
			bledis.setManufacturer("Adafruit Industries");
			bledis.setModel("Bluefruit Feather52");
			bledis.begin();
		
			// Configure and Start BLE Uart Service
			bleuart.begin();
		
			// Start BLE Battery Service
			blebas.begin();
			blebas.write(100);
		
			// Set up and start advertising
			startAdv();
		
		}
		
		void startAdv(void)
		{
			// Advertising packet
			Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
			Bluefruit.Advertising.addTxPower();
		
			// Include bleuart 128-bit uuid
			Bluefruit.Advertising.addService(bleuart);
		
			// Secondary Scan Response packet (optional)
			Bluefruit.ScanResponse.addName();
		
			/* Start Advertising
			* - Enable auto advertising if disconnected
			* - Interval:  fast mode = 20 ms, slow mode = 152.5 ms
			* - Timeout for fast mode is 30 seconds
			* - Start(timeout) with timeout = 0 will advertise forever (until connected)
			*/
			Bluefruit.Advertising.restartOnDisconnect(true);
			Bluefruit.Advertising.setInterval(160, 320); // in unit of 0.625 ms
			Bluefruit.Advertising.setFastTimeout(30);    // number of seconds in fast mode
			Bluefruit.Advertising.start(0);              // 0 = Don't stop advertising after n seconds
		}
		
		void loop()
		{
			// Read accelerometer data
			float accelX = myIMU.readFloatAccelX();
			float accelY = myIMU.readFloatAccelY();
			float accelZ = myIMU.readFloatAccelZ();
		
			// Format the accelerometer data as a string
			String accelData = "Accel X: " + String(accelX, 4) + ", Y: " + String(accelY, 4) + ", Z: " + String(accelZ, 4);
		
			// Send the accelerometer data over BLE
			if (Bluefruit.connected()) {
			bleuart.println(accelData);
			}
		
			// Print the accelerometer data to the Serial Monitor
			Serial.println(accelData);
		
			// Delay for a second before the next reading
			delay(1000);
		}
		
		// callback invoked when central connects
		void connect_callback(uint16_t conn_handle)
		{
			delay(500);  // Small delay before continuing
			// Get the reference to current connection
			BLEConnection* connection = Bluefruit.Connection(conn_handle);
		
			char central_name[32] = { 0 };
			connection->getPeerName(central_name, sizeof(central_name));
		
			Serial.print("Connected to ");
			Serial.println(central_name);
		}
		
		/**
			* Callback invoked when a connection is dropped
			* @param conn_handle connection where this event happens
			* @param reason is a BLE_HCI_STATUS_CODE which can be found in ble_hci.h
			*/
		void disconnect_callback(uint16_t conn_handle, uint8_t reason)
		{
			(void) conn_handle;
			(void) reason;
		
			Serial.println();
			Serial.print("Disconnected, reason = 0x"); Serial.println(reason, HEX);
		}
					

I connected the board to my phone using the same app:

Summary

I found that these two options are useful for programming the board but the Arduino IDE used more complex programming configurations that can be useful depending on the use case. I will come down to personal preference on which option you choose. I think I’ll be using CircuitPython to program this XIAO since it’s easier to use and I have used Arduino IDE to program other boards and I want to get used to Python.