Skip to content

Particle code

The example shown here configures an Argon device to advertise a single service which allows a BLE client to read its battery level. The code provided can be used as a model to advertise other services based on sensor readings, for example.

As well as exposing the battery level as a BLE service, the code also publishes information as a JSON string to the Particle device cloud and logs the same information to the serial console. It is therefore possible to check the information in several places to make sure that the code is working correctly.

The code is introduced a section at a time to allow for some minimal explanation. The first section defines a set of global variables/constants.

Globals

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
#include "Particle.h"

const unsigned long UPDATE_INTERVAL_MS = 2000;
unsigned long lastUpdate = 0;
uint8_t lastBattery = 100;
char lastMsg[128];
const char* argonName = "My Argon";
const char* serviceUuid = "3f1a1596-ee7f-42bd-84d1-b1a294f82ecf";
const BleUuid batteryLevelService(serviceUuid);
BleCharacteristic batteryLevelCharacteristic("bat", BleCharacteristicProperty::NOTIFY, BleUuid(0x2A19), batteryLevelService);

Explanation

Line 1: Import the main Particle library

Line 3: Define the update interval in milliseconds

Line 4: Milliseconds since last update

Line 5: Most recent battery level percentage

Line 6: Character buffer to hold the most recent published message

Line 7: Name to use as BLE device identifier. This is the nae that will be displayed in a BLE scan.

Line 8: Service UUID for battery service. You can generate a UUID value using a cloud service such as this one, but be aware that some BLE services and characteristics have pre-assigned values.

Line 9: Define the BLE battery level service

Line 10: The battery_level characteristic provides the battery level of the device. The characteristic is identified by the short UUID, 0x2A19

Setup

As usual, the setup() function carries out one-time activitiesto initialise the sketch.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
void setup() {    
    Serial.begin();
    pinMode(PWR, INPUT);
    pinMode(CHG, INPUT);
    BLE.addCharacteristic(batteryLevelCharacteristic);
    BleAdvertisingData advData;
    advData.appendLocalName(argonName);
    advData.appendServiceUUID(batteryLevelService);
    BLE.advertise(&advData);
}

Explanation

Line 2: Initialise serial output to allow information messages to be sent to the console.

Lines 3 & 4: The Argon defines three 'internal' pins, PWR, CHG and BATT. These two configure PWR and CHG as inputs so that we can read their values in the code. PWR is 0 when the Argon is on battery power and 1 when on USB. // CHG: 0=charging, 1=not charging

Line 5: Add the battery level characteristic to the current BLE configuration.

Line 6: Define the variable advData to hold the information that will be advertised to potential clients.

Line 7: Set the name used to identify the Argon in a BLE scan.

Line 8: Add the battery level service to the advertising data.

Line 9: Expose the BLE data.

Loop

During the loop(), the aim is to update the battery level at an interval defined by UPDATE_INTERVAL_MS. This is done by comparing the last timestamp to the current value of millis() to avoid using a blocking delay.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
void loop() {
    if (millis() - lastUpdate >= UPDATE_INTERVAL_MS) {
        lastUpdate = millis();

        float voltage = analogRead(BATT) * 0.0011224;
        if (voltage >= 3.7) {
            lastBattery = 100;
        }
        else {
            lastBattery = int((voltage - 3.3) / 0.4 * 100);
        }

        char buf[128];
        snprintf(buf, sizeof(buf), "{'voltage': %.1f, 'PWR': %d,  'CHG': %d, 'LEVEL': %d}", 
                                     voltage, digitalRead(PWR), digitalRead(CHG), lastBattery);

        Particle.publish("battery", buf, PRIVATE);
        Log.info(buf);
        strcpy(lastMsg, buf);

        if (BLE.connected()) {
            batteryLevelCharacteristic.setValue(&lastBattery, 1);
        }
    }
}

Explanation

Lines 5 - 11: Calculate the Argon battery voltage using the 'internal' BATT pin. See the DeviceOS API documentation. The nominal voltage of the battery is 3.7V, so anything greater than that counts as 100%. The battery is considered empty if the voltage drops to 3.3V, so the voltage range is 0.4V. These are the values used in the main calculation on line 10.

Line 14: Prepare a JSON string for logging and publishing.

Line 22: Update the value of the BLE characteristic.

Testing

You should test that the Particle sketch is working correctly before trying to write a mobile app to read the battery level. That way, any errors seen later on will definitely be related to the mobile code.

The nRF Connect app is the recommended tool for checking that your Argon is advertising the correct data. Soe other apps such as BlueFruit may cache some data which can mean that some names do not show up correctly.