Particle Boron Cellular LTE M1 (before 2021)

The B404 and B402 have been depreciated.   The recommended replacement is the B404X.  

Cellular Data Usage

A ping to the Particle Cloud occurs every 23 minutes if the device is awake and it has not already communicated with the Particle Cloud.   If you publish data to the Particle Cloud at least every 20 minutes, then the data usage should be at most 1.4 MB/mo (64 + 622 bytes per publish for full string, less for integer/floating point values).   Other events will cause data usage such as the initial connection to the cloud (4400 bytes + each function, variable, subscription), reconnection to cloud (135 bytes), cellular handshake (or cold start) every three days (6 kB), etc.  

~ 5 MB/month for a Boron waking every 5 minutes using the EN pin & external timer.   ~ 5 MB/mo for Boron in SEMI_AUTOMATIC mode with no connection to the Particle Cloud and sending two float values to Ubidots every 5 minutes with power down in between TLP5110 timer wake up.  

The Boron LTE, E Series LTE, and Electron LTE does not support the commands used to implement getDataUsage() and setDataUsage().   You can get the data usage via web API using the command: https://api.particle.io/v1/sims/XXXX/data_usage?access_token=YYYY   where XXXX is the ICCID and access_token is your personal access token.  

Optimizing Cellular Data Use with Cloud connectivity

Study of Boron wake up ever 5 min data usage (~5 MB/mo)

 

Power Consumption

My test using the INA219 current sensor for 24 hours while my Boron LTE connected to a battery published one message ever hour to the Particle Cloud measured a maximum current draw of 5.9 mA-h.   I used the Adafruit INA219 FeatherWing attached to the Boron LTE.   The INA219 was measured every 1 ms and then I calculated the cumulative values of the measured mA times the time in hours to get mA-h.  

Blynk Mobile App to Track Cellular Connection Performance

Here is how to create a Blynk app to monitor Boron cellular strength & quality.  

Blynk widget configurations:


#include "Particle.h"

/////////////////////////////////////////////////////////////////////////
// Blynk
//#define BLYNK_DEBUG // Optional, this enables lots of prints
//#define BLYNK_PRINT Serial // Defines the object that is used for printing
#define BLYNK_NO_BUILTIN   // Disable built-in analog & digital pin operations (saves memory)
// Load Particle library: Blynk
#include <blynk.h>
// Blynk authorization token
char auth[] = "your-blynk-auth-code";
/////////////////////////////////////////////////////////////////////////
// Global variables for blinkLEDnoDelay()
unsigned long LEDblinkPeriod = 8;
unsigned long LEDblinkLast = 0;
byte LEDblinkPWM = 0;
bool LEDblinkState = false;
byte LEDlastMode = 0;
void blinkLEDnoDelay(byte pin, byte mode);
/////////////////////////////////////////////////////////////////////////
const unsigned long TIMER_INTERVAL_CELL_PUBLISH_MS = 1200000; // 20 min
unsigned long timerLastCellPublish = 0;
struct cell_sig_t {
  float strengthAvg = 0.0;
  float strengthMin = 100.0;
  float qualityAvg = 0.0;
  float qualityMin = 100.0;
  unsigned long samples = 0;
  unsigned long timer_low_ms = millis();
  unsigned long timer_limit_low_ms = millis();
} cellSig;
/////////////////////////////////////////////////////////////////////////
byte LEDmode = 0;  // 0 = okay; 1 = low cell signal strength/quality; 2 = Particle Cloud connection fail;  3 = Particle publish() fail


void setup() {
 
  pinMode(D7, OUTPUT);
  digitalWrite(D7, LOW);

  Serial.begin(9600);
  waitFor(Serial.isConnected, 30000);
  delay(1000);
  Serial.printlnf("System version: %s", System.version().c_str());
  Serial.printlnf("Free RAM %d", System.freeMemory());

  Blynk.begin(auth);

  Serial.println("Setup complete");
} // setup()

void loop() {

  Blynk.run();

  if (PLATFORM_ID == PLATFORM_BORON) {
    CellularCheck();
    CellularPublish();
  }

  // LEDmode = 0;  // 0 = okay; 1 = low cell signal strength/quality; 2 = Particle Cloud connection fail;  3 = Particle publish() fail
  blinkLEDnoDelay(D7, LEDmode);

} // loop()

void CellularCheck() {
  float strength = Cellular.RSSI().getStrength();
  cellSig.strengthAvg += strength;
  if (strength < cellSig.strengthMin) 
    cellSig.strengthMin = strength;
  float quality = Cellular.RSSI().getQuality();
  cellSig.qualityAvg += quality;
  if (quality < cellSig.qualityMin) 
    cellSig.qualityMin = quality;
  cellSig.samples++;
} // CellularCheck()


void CellularPublish() {
  if (timerLastCellPublish > millis())  timerLastCellPublish = millis();
  if ((millis() - timerLastCellPublish) > TIMER_INTERVAL_CELL_PUBLISH_MS) {
    cellSig.strengthAvg = cellSig.strengthAvg / float(cellSig.samples);
    cellSig.qualityAvg = cellSig.qualityAvg / float(cellSig.samples);
    Serial.println();
    Serial.printlnf("%u, %.1f%% avg, %.1f%%, %.1f, %.1f, %d, %d", millis(), cellSig.strengthAvg, cellSig.qualityAvg, Cellular.RSSI().getStrengthValue(), Cellular.RSSI().getQualityValue(), Cellular.RSSI().rssi, Cellular.RSSI().qual);
    Serial.printlnf("strength: %.1f%% avg %.1f%% min, quality: %.1f%% avg %.1f%% min, %u samples", cellSig.strengthAvg, cellSig.strengthMin, cellSig.qualityAvg, cellSig.qualityMin, cellSig.samples);
    byte PubResult = Particle.publish("cellular", String::format("strength: %.1f%% avg %.1f%% min, quality: %.1f%% avg %.1f%% min", cellSig.strengthAvg, cellSig.strengthMin, cellSig.qualityAvg, cellSig.qualityMin), PRIVATE);
    if (PubResult == 0)
      Serial.println("Particle.Publish() NOT successful");
    
    Blynk.virtualWrite(V0, cellSig.strengthAvg);
    Blynk.virtualWrite(V1, cellSig.strengthMin);
    Blynk.virtualWrite(V2, cellSig.qualityAvg);
    Blynk.virtualWrite(V3, cellSig.qualityMin);

    // LEDmode = 0;  // 0 = okay; 1 = low cell signal strength/quality; 2 = Particle Cloud connection fail;  3 = Particle publish() fail
    LEDmode = 0;
    if (cellSig.strengthAvg < 26.0 || cellSig.qualityAvg < 3.0)
      LEDmode = 1; //  1 = blink slow constantly
    if (PubResult == 0) {
      Serial.println("Particle.Publish() NOT successful");
      LEDmode = 3; //  3 = slow burst every 1 second
    }
    if (Particle.connected() == false) {
      //byte PubResult = Particle.publish("thingspeak_ai", jw.getBuffer(), PRIVATE);
      LEDmode = 2; //  2 = blink fast constantly
    } // Particle.connected()

    cellSig.samples = 0;
    cellSig.strengthAvg = 0.0;
    cellSig.qualityAvg = 0.0;
    cellSig.strengthMin = 100.0;
    cellSig.qualityMin = 100.0;
    timerLastCellPublish = millis();
  } // timer

} // CellularPublish()


void blinkLEDnoDelay(byte pin, byte mode) {
  // Blink the LED on 'pin' without using delay() according to
  // the 'mode' argument defined below. 
  // pin must support PWM!. 
  // Rev 20200528.
  // 
  // mode:
  //  0 = breathing
  //  1 = blink slow constantly
  //  2 = blink fast constantly
  //  3 = slow burst every 1 second
  //  4 = fast burst every 1 second
  //
  // Required global variables: LEDblinkPeriod, LEDblinkLast, LEDblinkPWM, LEDblinkState, LEDlastMode
  if (mode == 0) {
    // breathing
    LEDblinkPeriod = 8;
    if (LEDlastMode != mode) {
      LEDblinkPWM = 0;
      LEDblinkState = true;
      digitalWrite(pin, LOW);
    }
    if (millis() - LEDblinkLast >= LEDblinkPeriod) {
        if (LEDblinkPWM > 254) LEDblinkState = false;
        if (LEDblinkPWM < 1) LEDblinkState = true;
        if (LEDblinkState) {
            LEDblinkPWM++;
        } else {
            LEDblinkPWM--;
        }
        analogWrite(pin, LEDblinkPWM);
        LEDlastMode = mode;
        LEDblinkLast = millis();
    }
  } else if (mode == 1) {
    // blink slow constantly
    LEDblinkPeriod = 1000;
    if (millis() - LEDblinkLast >= LEDblinkPeriod) {
        digitalWrite(pin, LEDblinkState);
        LEDblinkState = !LEDblinkState;
        LEDlastMode = mode;
        LEDblinkLast = millis();
    }
  } else if (mode == 2) {
    // blink fast constantly
    LEDblinkPeriod = 100;
    if (millis() - LEDblinkLast >= LEDblinkPeriod) {
        digitalWrite(pin, LEDblinkState);
        LEDblinkState = !LEDblinkState;
        LEDlastMode = mode;
        LEDblinkLast = millis();
    }
  } else if (mode == 3) {
    // slow burst every 1 second
    // Slow 4 blinks (lazy burst) followed by 1 sec pause
    if (LEDlastMode != mode) {
      LEDblinkPWM = 0;
      LEDblinkState = true;
      LEDblinkPeriod = 100;
    }
    if (millis() - LEDblinkLast >= LEDblinkPeriod) {
        if (LEDblinkPWM < 7) {
          if (LEDblinkPWM == 0) LEDblinkState = true;
          digitalWrite(pin, LEDblinkState);
          LEDblinkPeriod = 100;
          LEDblinkState = !LEDblinkState;
          LEDblinkPWM++;
        } else {
          digitalWrite(pin, LOW);
          LEDblinkPWM = 0;
          LEDblinkPeriod = 1000;
        }
        LEDlastMode = mode;
        LEDblinkLast = millis();
    }
  } else if (mode == 4) {
    // fast burst every 1 second
    // Fast 4 blinks (burst) followed by 1 sec pause
    if (LEDlastMode != mode) {
      LEDblinkPWM = 0;
      LEDblinkState = true;
      LEDblinkPeriod = 25;
    }
    if (millis() - LEDblinkLast >= LEDblinkPeriod) {
        if (LEDblinkPWM < 7) {
          if (LEDblinkPWM == 0) LEDblinkState = true;
          digitalWrite(pin, LEDblinkState);
          LEDblinkPeriod = 25;
          LEDblinkState = !LEDblinkState;
          LEDblinkPWM++;
        } else {
          digitalWrite(pin, LOW);
          LEDblinkPWM = 0;
          LEDblinkPeriod = 1000;
        }
        LEDlastMode = mode;
        LEDblinkLast = millis();
    }
  } // mode
}   // blinkLEDnoDelay()

 


Do you need help developing or customizing a IoT product for your needs?   Send me an email requesting a free one hour phone / web share consultation.  

 

The information presented on this website is for the author's use only.   Use of this information by anyone other than the author is offered as guidelines and non-professional advice only.   No liability is assumed by the author or this web site.