13. Networking and Communications

The primary objective of this week is to design, construct, and connect two nodes wirelessly.

Research

The nodes will be connected via BLE (Bluetooth Low Energy) technology, although other communication protocols will also be discussed.

This week's group assignment, which consists of connecting two projects, can be found here.

In recent decades, communication technologies, both wired and wireless, have undergone significant evolution, enabling more efficient and versatile connection between electronic devices. These technologies are based on the implementation of various communication protocols, each with its own specific characteristics and applications.

One of the most prevalent communication protocols utilized between microcontrollers and other embedded devices is the I2C (Inter-Integrated Circuit) protocol. This serial bus protocol enables bidirectional communication between multiple devices through the use of a single pair of data lines, comprising one for clock (SCL) and one for data (SDA). I2C is distinguished by its straightforward implementation, minimal power consumption, and its capacity to regulate up to 128 devices on a unified network.

I2C Protocol

Schematization of I2C Protocol

Image taken from: Margolis, M., Jepson, B. & Weldin, N. R. (2020). Arduino Cookbook. Recipes to Begin, Expand, and Enhance Your Projects (3rd Ed.). O'Reilly Media

Another widely used protocol is SPI (Serial Peripheral Interface), which allows synchronous serial communication between microcontrollers and peripherals. Unlike I2C, SPI uses a 4-line bus: one for clock (SCLK), one for sending data (MOSI), one for receiving data (MISO) and one for device selection (or Slave Selection SS). SPI stands out for its higher data transfer rate and ease of implementation, making it a popular choice for applications requiring higher performance.

SPI Protocol

Schematization of SPI Protocol

Image taken from: Margolis, M., Jepson, B. & Weldin, N. R. (2020). Arduino Cookbook. Recipes to Begin, Expand, and Enhance Your Projects (3rd Ed.). O'Reilly Media

In contrast, the UART (Universal Asynchronous Receiver-Transmitter) protocol is an asynchronous serial communication standard that is widely used in data transmission between devices. Unlike I2C and SPI, UART employs only two signal lines: one for transmitting (TX) and one for receiving (RX) data. In the context of UART communication, the transmission of data is accomplished through the use of frames, each of which is comprised of a start bit, data bits, and a parity bit (if present). The start bit, which is represented by a logic low signal (0), serves to indicate the beginning of the frame. The data bits, which typically consist of 8 bits, are transmitted from the least significant bit to the most significant bit. Finally, the parity bit (if present) is used to detect errors during transmission. The parity bit can be either even or odd, indicating that the total number of bits in a given frame is even or odd, respectively. The stop bit is a high logic signal (1) indicating the end of the frame. Its length can be 1 or 2 bits. The use of the parity bit and the length of the stop bit depend on the specific configuration of the UART protocol used. UART is distinguished by its simplicity, low transfer rate, and wide compatibility, rendering it an appropriate choice for straightforward communication applications, such as connecting devices via serial ports.

UART Protocol

Schematization of UART Protocol

Image taken from: https://embeddedwala.com/Blogs/DigitalCommunication/Getting-Started-with-UART:-A-Beginners-Guide

In the field of wireless communications, WiFi and Bluetooth are two of the most prominent protocols. The WiFi protocol, based on the IEEE 802.11 standard, enables the connection of devices to a wireless local area network (WLAN) via high-frequency radio waves. This technology is characterized by its high data transfer speed and its ability to support multiple users simultaneously, making it a popular choice for applications that require a high-speed network connection, such as video streaming or Internet access.

WiFi Logo

WiFi Logo

Image taken from: https://es.m.wikipedia.org/wiki/Archivo:WiFi_Logo.svg

For antoher way, Bluetooth is a wireless communication technology that enables electronic devices to connect over short distances. It was initially developed in 1994 by a consortium of companies led by Ericsson. Bluetooth is used to transfer files, music, voice, and data between compatible devices such as cell phones, computers, headsets, and speakers. It is characterized by low power consumption, low cost, and wide compatibility between different brands and models. Bluetooth has become an industry standard for wireless device connectivity, being widely adopted in a variety of personal and consumer applications. Bluetooth employs short-range radio waves to establish a wireless connection between devices. When two Bluetooth devices meet, they exchange information to authenticate each other and establish a secure link. Once connected, they can transmit data at rates of up to 24 Mbps, depending on the version. Bluetooth employs a frequency hopping system to circumvent interference, oscillating at a rate of approximately 1600 times per second. This enables a dependable connection even in environments with a considerable amount of electromagnetic noise. The system achieves low power consumption by cycling on and off, allowing devices to enter a low-power mode when not transmitting.

Bluetooth Logo

Bluetooth Logo

Image taken from: https://www.creativebloq.com/news/bluetooth-logo-secret

Communication technologies and protocols, both wired and wireless, play a pivotal role in the interconnection of electronic devices, enabling data exchange and remote control in an efficient and versatile manner. Each protocol possesses its own distinctive characteristics and advantages, rendering it suitable for a diverse range of applications and communication requirements.

Bluetooth Low Energy (BLE)

Bluetooth Low Energy (BLE) is a wireless personal area network (WPAN) technology designed and marketed by the Bluetooth Special Interest Group (SIG) for applications in the healthcare, fitness, and beacon industries, as well as the security and home entertainment sectors. BLE differs from the original Bluetooth technology in that it is specifically designed to provide low power consumption while simultaneously maintaining a similar communication range. It is also the inaugural open wireless communication technology, offering communication between mobile devices, computers, and other smaller devices, including button cell devices. BLE chips are designed to operate on low power, facilitating communication between smaller devices, such as button cells, and Bluetooth devices, which operate at 2.4 GHz (within one of the ISM bands) with a transfer rate of 1 Mbps at the physical layer. These chips have a wide range of potential applications within the industry, in addition to their similar size to traditional Bluetooth devices. Furthermore, they are equipped with security measures, such as AES encryption and configurable security schemes.

In contrast to the standard Bluetooth communication, which is based on an asynchronous serial connection (UART), a BLE radio functions as a community bulletin board. The computers that connect to it are analogous to community members reading the bulletin board. Each radio assumes the role of either a bulletin board or a reader. If a radio is designated as a bulletin board (referred to as a peripheral device in BLE terminology), it posts data for all radios in the community to read. If your radio is a reader (referred to as a central device in BLE terms), it reads information from any of the bulletin boards (referred to as peripheral devices) that contain the data that you wish to access and process. It can be considered that the peripheral devices serve as a repository of information for the reader radios, analogous to a server in a client-server transaction. Similarly, the central devices are the clients of the Bluetooth® LE world because they access the information available from the peripherals, thus providing the functionality to interact with the BLE network.

A BLE edge device may be conceptualized as a bulletin board, with the central devices functioning as viewers of the board. The central devices observe the services, obtain the data, and then move on. Each transaction is rapid (on the order of a few milliseconds), allowing multiple central devices to concurrently retrieve data from a peripheral.

The information presented by a peripheral is structured into services, each of which is subdivided into features. The services may be considered the notices on a bulletin board, while the features represent the individual paragraphs of those notices. If one is a peripheral device, one simply updates each service feature as needed, without concern for whether or not the central devices read them. Conversely, if one is a central device, one connects to the peripheral and then reads the boxes one desires. If a given feature is both readable and writable, then both the peripheral and the central device are capable of modifying it.

The central devices serve as clients, reading and writing information from the peripheral devices. The latter are designated as servers and are responsible for providing sensor data in a readable format and offering read/write capabilities for controlling actuators such as motors, lights, and so on.

ble-bulletin-board-model

BLE Bulletin Board Model

Image taken from: https://www.arduino.cc/reference/en/libraries/arduinoble/

A BLE peripheral will provide services, which in turn will provide features. The user has the option of defining their own services or utilising standard services (see section 3.4). Services are identified by unique numbers, known as UUIDs. It is important to be aware of the UUIDs of other contexts. Standard services have a 16-bit UUID, while custom services have a 128-bit UUID. The ability to define services and features depends on the radio in use and its firmware.

Central Device code


  #include "ArduinoBLE.h2"
  #include "Arduino_APDS9960.h"
  
  const char* deviceServiceUuid = "19b10000-e8f2-537e-4f6c-d104768a1214";
  const char* deviceServiceCharacteristicUuid = "19b10001-e8f2-537e-4f6c-d104768a1214";
  
  int gesture = -1;
  int oldGestureValue = -1;   
  
  void setup() {
    Serial.begin(9600);
    while (!Serial);
    
    if (!APDS.begin()) {
      Serial.println("* Error initializing APDS9960 sensor!");
    } 
  
    APDS.setGestureSensitivity(80); 
    
    if (!BLE.begin()) {
      Serial.println("* Starting Bluetooth® Low Energy module failed!");
      while (1);
    }
    
    BLE.setLocalName("Nano 33 BLE (Central)"); 
    BLE.advertise();
  
    Serial.println("Arduino Nano 33 BLE Sense (Central Device)");
    Serial.println(" ");
  }
  
  void loop() {
    connectToPeripheral();
  }
  
  void connectToPeripheral(){
    BLEDevice peripheral;
    
    Serial.println("- Discovering peripheral device...");
  
    do
    {
      BLE.scanForUuid(deviceServiceUuid);
      peripheral = BLE.available();
    } while (!peripheral);
    
    if (peripheral) {
      Serial.println("* Peripheral device found!");
      Serial.print("* Device MAC address: ");
      Serial.println(peripheral.address());
      Serial.print("* Device name: ");
      Serial.println(peripheral.localName());
      Serial.print("* Advertised service UUID: ");
      Serial.println(peripheral.advertisedServiceUuid());
      Serial.println(" ");
      BLE.stopScan();
      controlPeripheral(peripheral);
    }
  }
  
  void controlPeripheral(BLEDevice peripheral) {
    Serial.println("- Connecting to peripheral device...");
  
    if (peripheral.connect()) {
      Serial.println("* Connected to peripheral device!");
      Serial.println(" ");
    } else {
      Serial.println("* Connection to peripheral device failed!");
      Serial.println(" ");
      return;
    }
  
    Serial.println("- Discovering peripheral device attributes...");
    if (peripheral.discoverAttributes()) {
      Serial.println("* Peripheral device attributes discovered!");
      Serial.println(" ");
    } else {
      Serial.println("* Peripheral device attributes discovery failed!");
      Serial.println(" ");
      peripheral.disconnect();
      return;
    }
  
    BLECharacteristic gestureCharacteristic = peripheral.characteristic(deviceServiceCharacteristicUuid);
      
    if (!gestureCharacteristic) {
      Serial.println("* Peripheral device does not have gesture_type characteristic!");
      peripheral.disconnect();
      return;
    } else if (!gestureCharacteristic.canWrite()) {
      Serial.println("* Peripheral does not have a writable gesture_type characteristic!");
      peripheral.disconnect();
      return;
    }
    
    while (peripheral.connected()) {
      gesture = gestureDetectection();
  
      if (oldGestureValue != gesture) {  
        oldGestureValue = gesture;
        Serial.print("* Writing value to gesture_type characteristic: ");
        Serial.println(gesture);
        gestureCharacteristic.writeValue((byte)gesture);
        Serial.println("* Writing value to gesture_type characteristic done!");
        Serial.println(" ");
      }
    
    }
    Serial.println("- Peripheral device disconnected!");
  }
    
  int gestureDetectection() {
    if (APDS.gestureAvailable()) {
      gesture = APDS.readGesture();
  
      switch (gesture) {
        case GESTURE_UP:
          Serial.println("- UP gesture detected");
          break;
        case GESTURE_DOWN:
          Serial.println("- DOWN gesture detected");
          break;
        case GESTURE_LEFT:
          Serial.println("- LEFT gesture detected");
          break;
        case GESTURE_RIGHT:
          Serial.println("- RIGHT gesture detected");
          break;
        default:
          Serial.println("- No gesture detected");
          break;
        }
      }
      return gesture;
  }
  

Periperial Device code


#include "ArduinoBLE.h"
      
enum {
  GESTURE_NONE  = -1,
  GESTURE_UP    = 0,
  GESTURE_DOWN  = 1,
  GESTURE_LEFT  = 2,
  GESTURE_RIGHT = 3
};

const char* deviceServiceUuid = "19b10000-e8f2-537e-4f6c-d104768a1214";
const char* deviceServiceCharacteristicUuid = "19b10001-e8f2-537e-4f6c-d104768a1214";

int gesture = -1;

BLEService gestureService(deviceServiceUuid); 
BLEByteCharacteristic gestureCharacteristic(deviceServiceCharacteristicUuid, BLERead | BLEWrite);


void setup() {
  Serial.begin(9600);
  while (!Serial);  
  
  pinMode(LEDR, OUTPUT);
  pinMode(LEDG, OUTPUT);
  pinMode(LEDB, OUTPUT);
  pinMode(LED_BUILTIN, OUTPUT);
  
  digitalWrite(LEDR, HIGH);
  digitalWrite(LEDG, HIGH);
  digitalWrite(LEDB, HIGH);
  digitalWrite(LED_BUILTIN, LOW);

  
  if (!BLE.begin()) {
    Serial.println("- Starting Bluetooth® Low Energy module failed!");
    while (1);
  }

  BLE.setLocalName("Arduino Nano 33 BLE (Peripheral)");
  BLE.setAdvertisedService(gestureService);
  gestureService.addCharacteristic(gestureCharacteristic);
  BLE.addService(gestureService);
  gestureCharacteristic.writeValue(-1);
  BLE.advertise();

  Serial.println("Nano 33 BLE (Peripheral Device)");
  Serial.println(" ");
}

void loop() {
  BLEDevice central = BLE.central();
  Serial.println("- Discovering central device...");
  delay(500);

  if (central) {
    Serial.println("* Connected to central device!");
    Serial.print("* Device MAC address: ");
    Serial.println(central.address());
    Serial.println(" ");

    while (central.connected()) {
      if (gestureCharacteristic.written()) {
         gesture = gestureCharacteristic.value();
         writeGesture(gesture);
       }
    }
    
    Serial.println("* Disconnected to central device!");
  }
}

void writeGesture(int gesture) {
  Serial.println("- Characteristic  has changed!");
  
   switch (gesture) {
      case GESTURE_UP:
        Serial.println("* Actual value: UP (red LED on)");
        Serial.println(" ");
        digitalWrite(LEDR, LOW);
        digitalWrite(LEDG, HIGH);
        digitalWrite(LEDB, HIGH);
        digitalWrite(LED_BUILTIN, LOW);
        break;
      case GESTURE_DOWN:
        Serial.println("* Actual value: DOWN (green LED on)");
        Serial.println(" ");
        digitalWrite(LEDR, HIGH);
        digitalWrite(LEDG, LOW);
        digitalWrite(LEDB, HIGH);
        digitalWrite(LED_BUILTIN, LOW);
        break;
      case GESTURE_LEFT:
        Serial.println("* Actual value: LEFT (blue LED on)");
        Serial.println(" ");
        digitalWrite(LEDR, HIGH);
        digitalWrite(LEDG, HIGH);
        digitalWrite(LEDB, LOW);
        digitalWrite(LED_BUILTIN, LOW);
        break;
      case GESTURE_RIGHT:
        Serial.println("* Actual value: RIGHT (built-in LED on)");
        Serial.println(" ");
        digitalWrite(LEDR, HIGH);
        digitalWrite(LEDG, HIGH);
        digitalWrite(LEDB, HIGH);
        digitalWrite(LED_BUILTIN, HIGH);
        break;
      default:
        digitalWrite(LEDR, HIGH);
        digitalWrite(LEDG, HIGH);
        digitalWrite(LEDB, HIGH);
        digitalWrite(LED_BUILTIN, LOW);
        break;
    }      
}
Monito_Serial

Established connection displayed through Serial Monitor.