Skip to content

Sprint 2

How do I disconnect a Bluetooth connection programmatically? - Learning story #219

After looking around a bit on the EPS32 BLE github repository. I found the BLEServer.cpp file containing the server class with all the methods possible from the server. After looking around a bit, I found the disconnect() method which only requires that you pass the BLE connection id. To get the connection id, I ceated the ConnectedDevice structure containing the IP address of the device and the connection id. I tested the method on the ESP32 and it worked.

ConnectedDevice struct

struct ConnectedDevice {
    uint16_t connId;
    BLEAddress remoteAddress;
};

Method where the device is disconnected

/**
 * @brief Starts pairing mode
 */
void startPairingMode() {
    if(this->isClientConnected()) {
        // Disconnect from the connected client
        // Starts advertising in ServerCallbacks onDisconnect
        this->bleServer->disconnect(this->serverCallbacks->connectedDevice->connId);
    }
}

File containing the base disconnect method

How do I disable and (re-)enable BLE advertising on an ESP32? - Learning story #220

During the development of the pairing mode feature, I ran into a problem where the device wouldn’t reconnect. After a little digging, I found out that that may be due to the device not advertising. So I needed to know how to disable and re-enable advertising.

The static BLEDevice::startAdvertising() method was quickly found under the BLEDevice class. I also found the BLEServer::startAdvertising() method under the BLEServer class. This made starting advertising a lot easier, but there was no BLEDevice::stopAdvertising() method. So i had to search for this method a bit.
After referencing the library’s github page, I was able to find the stop() method in the BLEAdvertising.cpp file. After finding the methods, it was quite easy to use them to manage ble advertising.

Stopping advertising

/**
 * @brief Is called when a device connects to the server
 * @param bleServer the server
 */
void onConnect(BLEServer *bleServer, esp_ble_gatts_cb_param_t *param) {  
    // Create connected device data
    this->connectedDevice = new ConnectedDevice {
        param->mtu.conn_id,
        BLEAddress(param->connect.remote_bda)
    }

    // Stop advertising
    bleServer->getAdvertising()->stop();
}

Starting advertising

/**
 * @brief Is called when a device disconnects from the server
 * @param bleServer the server
 */
void onDisconnect(BLEServer* bleServer) {
    delete this->connectedDevice;
    this->connectedDevice = nullptr;
    bleServer->startAdvertising();
}

Start method
Stop method

How does the ESP32 watchdog get triggered? - Learning story #222

I had problems where the device would throw a task watchdog timer exception whenever a device initiated a connection via Bluetooth.
After doing a little digging, I found out that a watchdog is a hardware timer designed to make sure the device doesn’t freeze while the device is running.
The ESP32 has 2 types of watchdogs: 1. Interrupt Watchdog Timer (IWDT) 2. Task Watchdog Timer (TWDT)

Interrupt Watchdog Timer

The interrupt watchdog is triggered whenever an interrupt service routine is blocked for too long. A service interrupt routine is a software routine that hardware invokes in response to an interrupt. These interrupts come from other hardware and can for instance be used to trigger key presses. Blocking a service interrupt routine can be undesirable due to interrupts being expected to run almost immediately most of the time.

Task Watchdog Timer

The task watchdog timer is triggered whenever a block of code takes too long to execute. This is indicative of blocking code and is therefore malicious to a working device. FreeRTOS, which ESP-IDF is based on, which, in turn ESP Arduino is based on. All processes except for the main process manage idle tasks which do not have priority. These processes are only run whenever the device sleeps or blocks for a period of time.

The solution

After reviewing what a watchdog is, it seems that my thread was not giving enough time to the idle threads to run the onConnect() interrupt, causing the watchdog to panic. After adding a delay in my loop() method, the exceptions stopped occuring.

Watchdog documentation
Stackoverflow answer explaining task watchdog timer
Explaination service interrupts

How do I make a hold action for a button - Learning story #221

What is the definition of a button hold action in this learning story

A button hold action is a way to define the act of holding a button for a pre-determined amount of time to perform certain functions.

How does the logic for a button hold action work

When the button is pressed, you record at what point in time that press started. After the button is let go, you calculate how long ago the press was started and run the method that is associated with that timespan.

How do I code a hold action

I found multiple examples but found this code snippet to be the easiest to understand:

unsigned long startMillis;
unsigned long currentMillis;
const unsigned long period = 5000;  //period during which button input  is valid
const byte buttonPin1 = A1;    //button on pin A1
byte currentButtonState;
byte previousButtonState;
int count = 0;
boolean counting;  //EDIT - corrected mistake

void setup()
{
  Serial.begin(115200);
  pinMode(buttonPin1, INPUT_PULLUP);
  Serial.println("Press the button as many times as possible in 5 seconds");
  counting = true;    //turn on the counting indicator
  startMillis = millis();  //save the start time
}

void loop()
{
  currentMillis = millis();
  previousButtonState = currentButtonState;
  if (currentMillis - startMillis <= period)  //true until the period elapses.  Note that this is the reverse of BWOD
  {
    currentButtonState = digitalRead(buttonPin1);
    if (currentButtonState == LOW and previousButtonState == HIGH)  //the button has become pressed
    {
      count++;    //increment count
      Serial.println(count);
    }
  }
  else  //period has ended
  {
    if (counting == true)  //if we were counting
    {
      Serial.print("Time is up");
      counting = false;    //prevent the message being displayed again
    }
  }
}

Conclusion

Eventually I implemented my own hold action by creating a button class and having it track the periods in which a button is pressed.

Arduino forums comment explaining hold action

How do I send Bluetooth messages from the ESP32 to the Mobile device? - Learning story #234

How does Bluetooth Low Energy exchange data?

The Bluetooth Low Energy protocol uses GATT which defines services with characteristics that hold data. The client can request those values when required. The server can change the values of the characteristics and is able to notify the client of the changes. Because data is only communicated when requested, the client and server can effectively communicate in real time without having to be connected the entire time.

How do I notify the client of a characteristic value change from the ESP32?

Characteristics have attributes, one of the possible characteristics is: NOTIFY, which makes the client listen for changes to the value of the characteristics. The client can then be notified of any value changes done on the serverside by calling the notify() method on the characteristic.

Code snippet

// notify changed value
if (deviceConnected) {
    pCharacteristic->setValue((uint8_t*)&value, 4);
    pCharacteristic->notify();
    value++;
    delay(3); // bluetooth stack will go into congestion, if too many packets are sent, in 6 hours test i was able to go as low as 3ms
}

GATT explaination
Code snippet showing the notify method being used

How do I make a CI/CD pipeline for arduino code

What can I use

As I dont have access to an ESP32 emulator in the CI/CD pipeline, the only real way to verify the code is to check if it compiles. To do this, I had a look at the Arduino-CLI to see if there’s any possibilities there.

What are the steps required to check whether the code compiles

  1. First, the Arduino-cli needs to be installed. Luckily, this is quite easy as you only have to download the application file to the pipeline’s docker image.
  2. Extract the tar file to its own folder in usr/local/bin. It is then available from the command line.
  3. Add the ESP32 board manager to the Arduino-cli config file after which we update the Arduino-cli’s indexes so it detects and downloads the new board manager.
  4. Install all necessary libraries.
  5. Start compiling the code.

Arduino-cli Example


Last update: June 1, 2023