Electron Data Usage Monitor with Photon

This was just a simple little program I wrote to test out the new cellular data usage APIs in 0.5.0. There is a bit of code you install on the Electron with whatever else you’re testing, a program you install on a Photon, and you connect the serial ports together and voila! Periodically the Electron sends this data over a dedicated serial port (1, 4, or 5, typically) to the Photon which then does something with it. In this example, it just publishes it to the cloud, but you could do some analysis on the Photon itself if you wanted to.

Of course the reason to do this is that the publish occurs over Wi-Fi, so it doesn’t affect the data usage on the Electron, making the numbers much more reliable during testing.

But mostly it was just a quick little experiment for fun; feel free to steal bits of the code if you so desire, or not.

Electron source (requires 0.5.0):

// Electron-side code for Electron Data Usage Monitor
// Normally you'd just copy bits of this out into your own application. You need:
// 1. The UsageReport class between "Begin Usage Report Module and "End Usage Report Module"
// 2. The UsageReport global variable (also selects which port to use)
// 3. The call to usageReport.setup() from setup()
// 4. The call to usageReport.loop() from loop()
// You can use this with any Electron serial port
// Serial1: TX pin connects to RX on Photon
// Serial4: C3 pin connects to RX on Photon
// Serial5: C1 pin connects to RX on Photon
// You select which port in the constructor for the usageReport global variable below.

#include "Particle.h"

#include "Serial5/Serial5.h"

// Connect a button between this pin and ground for testing. When you hit it, a small amount of
// data will be published so you can make sure the counters are incremented. At most one
// publish per second will be done. Note that if you tie this low, it will publish every
// second, so beware of that!
const int publishButton = D2;

// Begin Usage Report Module
class UsageReport {
	UsageReport(USARTSerial &serialPort);
	virtual ~UsageReport();

	void setup();
	void loop();

	USARTSerial &serialPort;
	const unsigned long reportIntervalMs = 30000; // every 30 seconds
	unsigned long reportLast = 0;

// Normally you construct one of these as a global variable like this:
// UsageReport usageReport(Serial5);
UsageReport::UsageReport(USARTSerial &serialPort) : serialPort(serialPort) {

UsageReport::~UsageReport() {

// Call usageReport.setup() from setup()
void UsageReport::setup(void) {

// Call usageReport.loop() from loop()
void UsageReport::loop(void) {
	if (millis() - reportLast >= reportIntervalMs) {
		reportLast = millis();

		// Time to issue another report

		// getDataUsage is documented here: https://docs.particle.io/reference/firmware/electron/#getdatausage-
		CellularData data;
		if (Cellular.getDataUsage(data)) {
			serialPort.printf("\n%lu,%d,%d,%d,%d,%d\n", millis(), data.cid, data.tx_session, data.rx_session, data.tx_total, data.rx_total);
		else {
			// Serial.println("unable to get usage data");

// End Usage Report Module

// Globals
UsageReport usageReport(Serial5);
unsigned long lastPublish = 0;

void setup() {

	pinMode(publishButton, INPUT_PULLUP);

	// Make sure you add a call to usageReport.setup() to your code

void loop() {
	// Make sure you add a call to usageReport.loop() to your code

	if (digitalRead(publishButton) == LOW) {
		if (millis() - lastPublish >= 1000) {
			lastPublish = millis();

			char testData[100];
			snprintf(testData, sizeof(testData), "testing some data millis=%lu", millis());

			Particle.publish("testdata", testData, 60, PRIVATE);


Photon source (can use 0.4.9 or 0.5.0):

// Photon-side code for Electron Data Usage Montor

#include "Particle.h"

// Constant
const size_t serialBufSize = 64;

// Globals
char serialBuf[serialBufSize];
int serialBufLen = 0;

void setup() {
	Serial.begin(9600); // USB Serial
	Serial1.begin(9600); // Electron

void loop() {

	while(Serial1.available() > 0) {
		serialBuf[serialBufLen] = Serial1.read();
		if (serialBuf[serialBufLen] == '\n') {
			// End of transmission
			serialBuf[serialBufLen] = 0;

			if (serialBufLen > 0) {
				unsigned long remoteMillis;
				int cid, tx_session, rx_session, tx_total, rx_total;

				if (sscanf(serialBuf, "%lu,%d,%d,%d,%d,%d", &remoteMillis, &cid, &tx_session, &rx_session, &tx_total, &rx_total) == 6) {
					Particle.publish("usage", serialBuf, 60, PRIVATE);
				else {
					Serial.printlnf("corrupted: %s", serialBuf);

				serialBufLen = 0;
		else {
			if ((size_t)++serialBufLen >= serialBufSize) {
				strcpy(serialBuf, "overflow");
				serialBufLen = strlen(serialBuf);

Wiring. The button on the Electron prototype board triggers a publish so you can make sure the counters are incrementing properly. The two prototype boards have GND connected together, and C1 (Serial 5 TX) on the Electron connects to RX on the Photon.

From the Dashboard: