I’ve enjoyed using Blynk and have many successes using both ESP8266 and ESP32 boards over wifi to build projects. Currently I’m trying to connect to the blynk server using a Nimbelink skywire-4g-lte-cat-1 cellular modem on the Verizon network. I have wired the modem to an ESP32 board and can successfully send data to dweet.io using a highly adapted arduino sketch from Nimbelink. I tried to create a library using the GSM files as a template, but at the moment that is beyond my scope of expertise, so in the short term (so I can get things working) I’m trying to use the existing sketch I have to talk to the Blynk server. I’m feeling lost and any direction is appreciated. The most promising option I’ve found is using the user defined connection.
Any help or direction is appreciated.
Thank - Craig
char temp_char; // need this before includes for some Arduino IDE versions
// otherwise "'Serial' was not declared in this scope" error occurs
//
// #include <SoftwareSerial.h>
#include <HardwareSerial.h>
/*
* Define modem model
* Uncomment only the modem that you are using.
* Make sure only one modem is uncommented!
*/
//#define NL_SW_1xRTT_V // Verizon 2G Modem
//#define NL_SW_1xRTT_S // Sprint 2G Modem
//#define NL_SW_1xRTT_A // Aeris 2G Modem
//#define NL_SW_GPRS // AT&T/T-Mobile 2G Modem
//#define NL_SW_EVDO_V // Verizon 3G Modem
//#define NL_SW_EVDO_A // Aeris 3G Modem
//#define NL_SW_HSPAP // AT&T/T-Mobile 3G Modem
//#define NL_SW_HSPAPG // AT&T/T-Mobile 3G Modem w/ GPS
//#define NL_SW_HSPAPE // GSM 3G Modem, EU
//#define NL_SW_LTE_TSVG // Verizon 4G LTE Modem
//#define NL_SW_LTE_TNAG // AT&T/T-Mobile 4G LTE Modem
//#define NL_SW_LTE_TEUG // GSM 4G LTE Modem, EU
#define NL_SW_LTE_GELS3 // VZW LTE Cat 1 Modem
#define MDVER03 // Modem version, for GELS3
/*
* Define shield model
* Uncomment only one!
*
* If using the NL_AB_ST_NCL shield + Leonardo:
* - J3 and J4 should have jumpers between pins 2 and 3
* If using the NL_AB_ST_NCL shield + Uno:
* - J3 and J4 should have jumpers between pins 1 and 2
*/
//#define NL_SWDK // Skywire Development Kit
#define NL_AB_ST_NCL // Skywire Arduino Shield
/*
* Define Arduino type
* Uncomment only one!
*
* NOTE:
* Debug info on the Serial Monitor is unreliable when using
* the Arduino Uno with the Cat 1 modem (at any baud rate)
*/
#define NL_ESP32
//#define NL_ARDUINO_LEONARDO
//#define NL_ARDUINO_UNO
// the pinouts on the SWDK are not compatible with the Arduino Uno
#if defined NL_ARDUINO_UNO && defined NL_SWDK
#error Arduino + SWDK not supported
#endif
#if defined NL_ARDUINO_UNO && defined NL_SW_LTE_GELS3
#pragma message("Debug info is unreliable for Arduino Uno + Cat 1 modem")
#endif
#if defined NL_ESP32 && undefined NL_SW_LTE_GELS3
#pragma message("ESP32 and modems other than Cat 1 modem not tested")
#endif
// Assign APN if 3G GSM or LTE Modem
#if defined NL_SW_HSPAP || defined NL_SW_HSPAPG || defined NL_SW_HSPAPE || defined NL_SW_LTE_TSVG || defined NL_SW_LTE_TNAG || defined NL_SW_LTE_TEUG || defined NL_SW_LTE_GELS3
/* -- CHANGE TO YOUR APN -- */
#define APN (String)"VZWINTERNET"
// #define APN (String)"vzwinternet"
#endif
/*
* Assign device ID
* Type your MEID/IMEI here
* MEID/IMEI is located on top of your Skywire modem
*/
/* -- CHANGE TO YOUR DEVICE ID: IMEI OR MEID -- */
#define DEVICE_ID (String)"MEIDSTRINGHERE" // First unit
// #define DEVICE_ID (String)"MEIDSTRINGHERE" // Second unit
// define Serial ports based on Arduino board type
#if defined NL_ESP32
#define Debug Serial // To USB converter
HardwareSerial Serial1(1); // Pins 4 rx and 2 tx hacked HardwareSerial.cpp
#define SW_Serial Serial1
HardwareSerial Serial2(2); // Pins 16 rx and 17 tx
#elif defined NL_ARDUINO_LEONARDO
#define Debug Serial
#define SW_Serial Serial1 // Pins 0 and 1
#elif defined NL_ARDUINO_UNO
#define Debug Serial
SoftwareSerial SW_Serial(2, 8); // RX, TX
#endif
// Define I/O pins for different CPUs
#if defined NL_ESP32
#define MDPWRPIN 32 // Monitors Modem Power
#define MDONOFF 0 // Turns Modem Power on
#define MDRESET 15 // Resets Modem (soft reboot)
#define LEDPIN 2 // Lights the LED on Modem??? - Not used now
#elif defined NL_ARDUINO_LEONARDO
#define MDPWRPIN 8
#define MDONOFF 12
#define MDRESET 9
#define LEDPIN 13
#elif defined NL_ARDUINO_UNO
#define MDPWRPIN 8
#define MDONOFF 12
#define MDRESET 9
#define LEDPIN 13
#endif
/*
* define "Skywire_ON" signal level depending on shield type
* The NL_AB_ST_NCL shield has a transistor that connects ON_OFF to ground,
* requiring a HIGH output on pin 12 in order to drive ON_OFF LOW
*/
#if defined NL_SWDK
#define SW_ON LOW
#elif defined NL_AB_ST_NCL
// #define SW_ON HIGH
#define SW_ON LOW
#endif
// initialize a few variables
char incomingByte = 0;
unsigned int dweetCtr = 0;
// code that initializes the serial ports and modem, waits for valid network connection
void setup()
{
String currentString = "";
String modemResponse = "";
int c;
bool connectionGood = false;
// initialize serial debug communication with PC over USB connection
Debug.begin(115200);
while (!Debug) ; // wait for serial debug port to connect to PC
// Initialize serial port to communicate with modem
Debug.println("Initializing modem COM port");
SW_Serial.begin(115200);
while (!SW_Serial);
// SW_Serial.setRxBufferSize(1024);
for (int q = 5; q > 0; q--)
{
Debug.println(q, DEC); // Debug.flush();
delay(250);
}
Debug.println("Wait for power valid");
pinMode(MDPWRPIN, INPUT); // Watch for module power to go valid
while (digitalRead(MDPWRPIN) == LOW);
Debug.println("Power good"); // Debug.flush();
digitalWrite(LEDPIN, LOW);
pinMode(LEDPIN, OUTPUT); // Blink the LED
delay(5000); // Wait a while
#define HARDRESET
#ifdef HARDRESET
Debug.println("Modem hard reset"); // Debug.flush();
pinMode(MDRESET, INPUT);
digitalWrite(MDRESET, LOW);
pinMode(MDRESET, OUTPUT);
delay(1500); // modem requires >15ms pulse for reset
pinMode(MDRESET, INPUT);
delay(5000); // Wait a while
#endif
Debug.println("Socket Dial and Sending Information to Dweet.io Example");
// Start cellular modem
Debug.println("Starting Cellular Modem"); // Debug.flush();
#define ON_OFF
#ifdef ON_OFF
/*
* Arduino I/O pin 12 is connected to modem ON_OFF signal.
* ON_OFF has internal 200k pullup resister and needs to be driven low
* by external signal for >1s to startup.
* Arduino defaults to I/O pin as input with no pullup on reset.
*/
//Set logic level to LOW
digitalWrite(MDONOFF, LOW);
// Turn on I/O pin for ON_OFF
pinMode(MDONOFF, OUTPUT);
// Turn on Skywire modem
digitalWrite(MDONOFF, SW_ON);
// Wierd that the module spec says > 100uS
#if defined NL_SW_LTE_TSVG || defined NL_SW_LTE_TNAG || defined NL_SW_LTE_TEUG || defined NL_SW_HSPAP || defined NL_SW_HSPAPG || defined NL_SW_HSPAPE
delay(5100); // modem requires >5s pulse
#else
delay(1500); // modem requires >1s pulse
#endif
// Return ON_OFF I/O pin to input/hi-Z state
pinMode(MDONOFF, INPUT);
#endif
// #define JUSTRAWSTUFF
#ifdef JUSTRAWSTUFF
Debug.println("PreWaiting for any string"); // Debug.flush();
while (1) {
c = SW_Serial.read();
if (c != -1){
Debug.write (c); // Debug.flush();
digitalWrite(LEDPIN, HIGH);
// } else {
// Debug.write ('.');
}
}
#endif
// SW_Serial.flush(); // Flush the input buffer
// Wait for modem to send ready string
Debug.println("Waiting for ready string"); // Debug.flush();
WaitForResponse("", "^SYSSTART", 30000, modemResponse);
digitalWrite(LEDPIN, HIGH); // Diag LED
Debug.println("Got ^SYSSTART"); // Debug.flush();
// send "AT" command to confirm modem is turned on
Debug.println("Test AT command");
WaitForResponse("AT\r", "OK", 100, modemResponse);
// turn on echo for Cat 1 modem
#if defined NL_SW_LTE_GELS3
WaitForResponse("ATE1\r", "OK", 500, modemResponse);
WaitForResponse("ATI1\r", "OK", 500, modemResponse);
#endif
// #define SOFTRESET
#ifdef SOFTRESET
// Soft reset of modem
Debug.println("Resetting modem");
#if defined NL_SW_LTE_GELS3
WaitForResponse("AT+SOFTRESET\r", "OK", 500, modemResponse);
#else
WaitForResponse("ATZ\r", "OK", 500, modemResponse);
#endif
#endif
delay (500);
/*
* In order for SoftwareSerial (only used with the Uno) to provide a reliable output,
* the data rate between the Arduino and Skywire modem must be lowered to 38400
*/
#if defined NL_ARDUINO_UNO
// change modem data rate
SW_Serial.print("AT+IPR=38400\r");
delay(1000);
// restart SoftwareSerial module at lower data rate
SW_Serial.end();
SW_Serial.begin(38400);
while (!SW_Serial) ;
while(PrintModemResponse() > 0) ; // print any characters sent after data rate change
#endif
// turn off URC (unsolicited result code) messages for Cat 1 modem
#if defined NL_SW_LTE_GELS3
WaitForResponse("AT+CEREG=0\r", "OK", 500, modemResponse);
#endif
// SIM-based Skywire-specific setup
#if defined NL_SW_GPRS || defined NL_SW_HSPAP || defined NL_SW_HSPAPG || defined NL_SW_HSPAPE
// activate SIM detect
Debug.println("Activating SIM card detect");
WaitForResponse("AT#SIMDET=1\r", "OK", 500, modemResponse);
#endif
// turn on verbose error messages
WaitForResponse("AT+CMEE=2\r", "OK", 1000, modemResponse);
#if defined NL_SW_GPRS || defined NL_SW_HSPAP || defined NL_SW_HSPAPG || defined NL_SW_HSPAPE || defined NL_SW_LTE_TSVG || defined NL_SW_LTE_TNAG || defined NL_SW_LTE_TEUG || defined NL_SW_LTE_GELS3
// Setup PDP context
Debug.println("Setting up PDP context");
String pdp;
#if defined NL_SW_LTE_TSVG
// deactivate context and set context configuration
WaitForResponse("AT#SGACT=3,0\r", "OK", 1000, modemResponse);
WaitForResponse("AT#SCFG=3,3,300,90,600,50\r", "OK", 1000, modemResponse);
pdp = "AT+CGDCONT=3,\"IP\",\"" + APN + "\"\r";
#elif defined NL_SW_LTE_GELS3
WaitForResponse("AT+CGACT=0,3\r", "OK", 1000, modemResponse);
WaitForResponse("AT+CGSN\r", "OK", 1000, modemResponse);
#if defined MDVER02 || defined MDVER03
// WaitForResponse("AT+CGDCONT=3,"IPV4V6","VZWINTERNET"\r", "OK", 1000, modemResponse);
pdp = "AT+CGDCONT=3,\"IPV4V6\",\"" + APN + "\"\r";
#elif MDVER01
WaitForResponse("AT+SQNSCFG=3,3,300,90,600,50\r", "OK", 1000, modemResponse);
pdp = "AT+CGDCONT=3,\"IP\",\"" + APN + "\"\r";
#endif
#else
WaitForResponse("AT#SGACT=1,0\r", "OK", 1000, modemResponse);
pdp = "AT+CGDCONT=1,\"IP\",\"" + APN + "\"\r";
#endif
WaitForResponse(pdp, "OK", 2000, modemResponse);
delay(10000);
#endif
// Check signal strength
WaitForResponse("AT+CSQ\r", "OK", 500, modemResponse);
// send command to modem to get firmware version
WaitForResponse("AT+CGMR\r", "OK", 500, modemResponse);
// activate PDP context
bool contextActivated = false;
String modemResp = "";
// wait for successful context activation
while(!contextActivated)
{
#if defined NL_SW_LTE_TSVG
SW_Serial.print("AT#SGACT=3,1\r");
#elif defined NL_SW_LTE_GELS3
#if defined MDVER02 || defined MDVER03
WaitForResponse("AT^SISS=0,\"srvType\",\"Socket\"\r", "OK", 500, modemResponse);
WaitForResponse("AT^SISS=0,\"conId\",3\r", "OK", 500, modemResponse);
WaitForResponse("AT^SISS=0,\"address\",\"socktcp://dweet.io:80\"\r", "OK", 500, modemResponse);
WaitForResponse("AT^SICA=1,3\r", "OK", 500, modemResponse);
WaitForResponse("AT^SICA?\r", "OK", 500, modemResponse);
SW_Serial.print("AT^SISO=0\r");
#elif MDVER01
SW_Serial.print("AT+CGACT=1,3\r");
#endif
#else
SW_Serial.print("AT#SGACT=1,1\r");
#endif
delay(5000);
modemResp = GetModemResponse();
Debug.println(modemResp);
if (modemResp.indexOf("^SISW: 0,1") >= 0)
{
Debug.println("Activation Successful");
contextActivated = true;
}
else
{
Debug.println("Activation Failed");
// deactivate context before trying again
#if defined NL_SW_LTE_TSVG
WaitForResponse("AT#SGACT=3,0\r", "OK", 1000, modemResponse);
#elif defined NL_SW_LTE_GELS3
WaitForResponse("AT+CGACT=0,3\r", "OK", 1000, modemResponse);
#else
WaitForResponse("AT#SGACT=1,0\r", "OK", 1000, modemResponse);
#endif
}
delay(100);
while(SW_Serial.available()) SW_Serial.read(); // consume any remaining bytes
}
// Check for network connection
Debug.println("Waiting for network connection");
connectionGood = false;
while(!connectionGood)
{
// send command to modem to get network status
#if defined NL_SW_1xRTT_A || defined NL_SW_1xRTT_S || defined NL_SW_1xRTT_V || defined NL_SW_EVDO_A || defined NL_SW_EVDO_V
SW_Serial.print("AT+CREG?\r");
#elif defined NL_SW_LTE_GELS3
SW_Serial.print("AT+CEREG?\r");
#else
SW_Serial.print("AT+CGREG?\r");
#endif
currentString = "";
delay(1000);
// Read serial port buffer1 for UART connected to modem and print that message back out to debug serial over USB
while(SW_Serial.available() > 0)
{
//read incoming byte from modem
incomingByte=SW_Serial.read();
//write byte out to debug serial over USB
Debug.print(incomingByte);
// add current byte to the string we are building
currentString += char(incomingByte);
// check currentString to see if network status is "0,1" or "0,5" which means we are connected
if((currentString.substring(currentString.length()-3, currentString.length()) == "0,1") ||
(currentString.substring(currentString.length()-3, currentString.length()) == "0,5"))
{
connectionGood = true;
while(PrintModemResponse() > 0); // consume rest of message once 0,1 or 0,5 is found
}
}
}
delay(2000);
}
// Main loop that sends the information to dweet.io
void loop()
{
String modemResponse = "";
int http_timeout = 0;
int cmd_len, c;
// increment dummy counter to send to dweet
dweetCtr = (dweetCtr + 1) % 100;
// Setup HTTP connection to dweet.io
Debug.println("Initiating Socket Dial");
#if defined NL_SW_LTE_TSVG
WaitForResponse("AT#SD=3,0,80,\"dweet.io\"\r", "CONNECT", 2000, modemResponse);
#elif defined NL_SW_LTE_GELS3
#ifdef MDVER01
WaitForResponse("AT+SQNSD=3,0,80,\"dweet.io\"\r", "CONNECT", 2000, modemResponse);
#endif
#else // all other Skywire modems
WaitForResponse("AT#SD=1,0,80,\"dweet.io\"\r", "CONNECT", 2000, modemResponse);
#endif
/*
* HTTP POST example to dweet.io. Sends variables temp and pressure
* to dweet.io page for your device ID
*/
float temp = 12.34;
float pressure = 56.78;
String http_command;
String ATCMDstring, ATCMDresp;
Debug.println("Sending data to dweet.io");
// Build string to send to dweet.io
http_command = "POST /dweet/for/" + DEVICE_ID + "?temperature=" + temp + "&pressure=" + pressure + "&counter=" + String(dweetCtr) + " HTTP/1.1\r\n\r\n";
cmd_len = http_command.length();
Debug.println("Command length is " + String(cmd_len));
ATCMDstring = "AT^SISW=0," + String(cmd_len);
ATCMDresp = "^SISW: 0," + String(cmd_len) + ",0";
Debug.println(ATCMDstring + "," + ATCMDresp);
SW_Serial.println(ATCMDstring);
Debug.println("Waiting for response string");
WaitForResponse("", ATCMDresp, 40000, modemResponse);
SW_Serial.print(http_command);
delay(5000);
while (PrintModemResponse() > 0);
// Print output
Debug.println("\nInformation sent to dweet.io.");
Debug.println("See https://dweet.io/get/latest/dweet/for/" + DEVICE_ID + " to verify");
delay(2000);
/*
* HTTP GET example for dweet.io. Gets information from your device ID
* and prints it to the screen.
*/
Debug.println("\r\nGetting data from dweet.io");
// ATMCMDstring = "AT^SISR=0,1000";
// SW_Serial.println(ATCMDstring);
// Debug.println("Waiting for response string");
// WaitForResponse("", ATCMDresp, 40000, modemResponse);
// SW_Serial.print("AT^SISR=0,1000\r");
// delay(30000);
// while (PrintModemResponse() > 0);
cmd_len=54;
ATCMDstring = "AT^SISW=0," + String(cmd_len);
ATCMDresp = "^SISW: 0," + String(cmd_len) + ",0";
Debug.println(ATCMDstring + "," + ATCMDresp);
SW_Serial.println(ATCMDstring);
Debug.println("Waiting for response string");
WaitForResponse("", ATCMDresp, 40000, modemResponse);
// Build string to send to dweet.io
http_command = "GET /get/latest/dweet/for/" + DEVICE_ID + " HTTP/1.1\r\n\r\n";
SW_Serial.print(http_command);
delay(5000);
while (!SW_Serial.available() && http_timeout < 1000) // wait for receive or timeout
{
http_timeout++;
delay(10);
}
while (PrintModemResponse() > 0);
// #define HACKMEOUT
#ifdef HACKMEOUT
WaitForResponse("AT^SISR=0,1000\r", "OK", 40000, modemResponse);
// close connection to dweet.io
#if defined NL_SW_LTE_TSVG
WaitForResponse("AT#SH=3\r", "OK", 1000, modemResponse);
#elif defined NL_SW_LTE_GELS3
#ifdef MDVER01
SW_Serial.print("+++\r"); // escape sequence for Cat 1 modem
delay(1000);
while(SW_Serial.available()) SW_Serial.read();
WaitForResponse("AT+SQNSH=3\r", "OK", 1000, modemResponse);
#else
delay(5000);
WaitForResponse("AT^SISC=0\r", "OK", 10000, modemResponse);
#endif
#else
WaitForResponse("AT#SH=1\r", "OK", 1000, modemResponse);
#endif
delay(5000);
while(PrintModemResponse() > 0); // print remaining characters (if any)
#endif // End HACKMEOUT
// #define DIRECTCONSOLE
#ifdef DIRECTCONSOLE
// HACKED IN TO GIVE CONSOLE CONTROL
Debug.println("Starting direct console");
while (1) {
// look for console input
c = Debug.read();
if (c != -1){
SW_Serial.write (c);
digitalWrite(LEDPIN, HIGH);
} else {
digitalWrite(LEDPIN, LOW);
}
// look for modem input
c = SW_Serial.read();
if (c != -1){
Debug.write (c);
digitalWrite(LEDPIN, HIGH);
} else {
digitalWrite(LEDPIN, LOW);
}
}
#endif // End DIRECTCONSOLE
}
// sends a command to the modem, waits for the specified number of milliseconds,
// checks whether the modem response contains the expected response, and
// appends the remaining response characters to the out parameter respOut
// returns true if command received the expected response
bool SendModemCommand(String command, String expectedResp, int msToWait, String& respOut)
{
int cmd_timeout = 0;
SW_Serial.print(command);
delay(msToWait);
// wait for data to become available, but timeout eventually if no response is received
while(!SW_Serial.available())
{
cmd_timeout++;
if (cmd_timeout == 1000)
{
Debug.println("command timeout");
return false;
}
delay(10);
}
// read response from modem
String resp = "";
respOut = "";
while(SW_Serial.available() > 0)
{
resp += char(SW_Serial.read());
if(resp.endsWith(expectedResp))
{
respOut = resp;
while(SW_Serial.available() > 0)
respOut += char(SW_Serial.read()); // append remaining response characters (if any)
return true;
}
}
respOut = resp;
return false;
}
// empty read buffer
void ConsumeModemResponse()
{
int c;
while(SW_Serial.available()) {
c = SW_Serial.read();
Debug.print('.');
Debug.print(c);
}
}
// repeatedly sends command to the modem until correct response is received
void WaitForResponse(String command, String expectedResp, int msToWait, String& respOut)
{
bool isExpectedResp;
do {
isExpectedResp = SendModemCommand(command, expectedResp, msToWait, respOut);
Debug.println(respOut);
SW_Serial.flush(); // just in case any characters weren't transmitted
Debug.flush();
ConsumeModemResponse(); // just in case any characters remain in RX buffer
} while(!isExpectedResp);
}
// returns modem response as a String
String GetModemResponse()
{
String resp = "";
while(SW_Serial.available() > 0)
{
resp += char(SW_Serial.read());
}
return resp;
}
// consumes and prints modem response
int PrintModemResponse()
{
String resp = "";
while(SW_Serial.available() > 0)
{
// read incoming modem response into temporary string
resp += char(SW_Serial.read());
}
Debug.println(resp);
//return number of characters in modem response buffer -- should be zero, but some may have come in since last test
return SW_Serial.available();
}