The Photon can stream out a whole lot of data using TCPClient, quite reliably, and pretty quickly, at least under ideal conditions. I had a done a test sending out 1 Mbyte of data, then closing and reopening the connection earlier, but the question of what happens if you do that over one connection, continuously, came up, so I tested that here.
I still get over 900 Kbytes/sec. Every byte sent out is accounted for (no dropped, added, or corrupted bytes), and you can do it without a single delay() call in the code.
The test ran for 9 hours and 44 minutes and 30,812 MB, yes, over 30 GB, was transferred without error. The connection closed at that point for unknown reasons, but a new connection was opened and things proceeded normally after that.
Of course this doesn’t prove that the Photon will always perform that well, it merely shows that it can. I will not be repeating this test with the Electron, unless someone wants to donate an unlimited SIM to me as I can think of better ways to spend US$30,000! LOL
// Test app for sending large amounts of data over a TCP connection
#include "Particle.h"
// This test can be run with system thread enabled or disabled. It's a little faster
// with it enabled, about 1150 Kbytes/sec. vs. about 900 Kbytes/sec with it disabled
// (the default).
// SYSTEM_THREAD(ENABLED);
// Retained memory is used to keep track of reboots.
// PHOTON ONLY: Connect VBAT to GND to the retained memory will be initialized on power-up if
// you are not using a coin cell battery
// Do NOT connect VBAT to GND on an Electron or you will short out the 3.3V regulator!
STARTUP(System.enableFeature(FEATURE_RETAINED_MEMORY));
// Finite state machine states
enum State { STATE_CONNECT, STATE_FILL_BUFFER, STATE_SEND, STATE_RETRY_WAIT };
// Keeps track of the number of times we've run setup(), which should be equal to the number
// of reboots since we don't use sleep modes in this test app
retained int setupCount = 0;
// Various constants
// bufSize is the number of bytes we make in a typical write call. Making this 2048 or
// larger can cause data corruption on the Photon. 1024 seems experimentally to be ideal;
// if you make it smaller it works without errors but the data rate drops.
const size_t bufSize = 1024;
// Various timeout values.
const unsigned long retryWaitTimeMs = 2000;
const unsigned long sendTimeoutMs = 60000;
// Set to the IP address of the server to connect to
IPAddress serverAddr(192,168,2,4);
const int serverPort = 7123;
// Global variables
State state = STATE_CONNECT;
TCPClient client;
unsigned long stateTime = 0;
unsigned char buf[bufSize];
size_t sentInBuf;
unsigned long totalSent = 0;
unsigned char bufStartChar;
void setup() {
Serial.begin(9600);
// Increment retained variable; should happen on every reboot (since we don't use sleep in
// this program).
setupCount++;
}
void loop() {
switch(state) {
case STATE_CONNECT:
Serial.printlnf("** starting setupCount=%d millis=%lu", setupCount, millis());
if (!client.connect(serverAddr, serverPort)) {
// Connection failed
stateTime = millis();
state = STATE_RETRY_WAIT;
break;
}
totalSent = 0;
bufStartChar = (unsigned char) setupCount;
state = STATE_FILL_BUFFER;
// Fall through
case STATE_FILL_BUFFER:
// Each buffer has different bytes so we can detect corruption on the server
for(size_t ii = 0; ii < bufSize; ii++) {
buf[ii] = bufStartChar++;
}
sentInBuf = 0;
state = STATE_SEND;
stateTime = millis();
// Fall through
case STATE_SEND:
if (client.connected()) {
int count = client.write(&buf[sentInBuf], bufSize - sentInBuf);
if (count == -16) {
// Special case: Internal buffer is full, just retry at the same offset next time
// I'm pretty sure the result code for this is different on the Core, and probably the Electron.
if (millis() - stateTime > sendTimeoutMs) {
// I never hit this debug statement in my tests
Serial.printlnf("** timeout sending sentInBuf=%u totalSent=%lu millis=%lu", sentInBuf, totalSent, millis());
client.stop();
stateTime = millis();
state = STATE_RETRY_WAIT;
}
}
else
if (count > 0) {
// Normal case, sent some bytes. count may be less than the buffer size, which
// means we need to send the rest later.
stateTime = millis();
totalSent += count;
sentInBuf += count;
if (sentInBuf >= bufSize) {
// Sent whole buffer, refill next time
state = STATE_FILL_BUFFER;
}
}
else {
// Error
Serial.printlnf("** error sending error=%d sentInBuf=%u totalSent=%lu millis=%lu", count, sentInBuf, totalSent, millis());
client.stop();
stateTime = millis();
state = STATE_RETRY_WAIT;
}
}
else {
Serial.printlnf("** connection closed totalSent=%lu millis=%lu", totalSent, millis());
client.stop();
stateTime = millis();
state = STATE_RETRY_WAIT;
}
break;
case STATE_RETRY_WAIT:
if (millis() - stateTime > retryWaitTimeMs) {
// Wait a few seconds before retrying
state = STATE_CONNECT;
break;
}
break;
}
}