freematics-traccar-encrypted/esp32/libraries/FreematicsONE/FreematicsNetwork.cpp

714 lines
17 KiB
C++

/*************************************************************************
* Freematics Hub Client implementations for ESP8266-AT, SIM800, SIM5360
* Distributed under BSD license
* Visit http://freematics.com/products/freematics-one for more information
* (C)2017-2019 Stanley Huang <stanley@freematics.com.au
*************************************************************************/
#include <Arduino.h>
#include "FreematicsBase.h"
#include "FreematicsNetwork.h"
#define XBEE_BAUDRATE 115200
/*******************************************************************************
Implementation for ESP8266 WIFI (ESP8266 AT command-set)
*******************************************************************************/
bool UDPClientESP8266AT::begin(CFreematics* device, bool nocheck)
{
m_device = device;
m_device->xbBegin(XBEE_BAUDRATE);
for (byte n = 0; n < 5; n++) {
if (sendCommand("AT\r\n", 200)) {
return true;
}
}
return false;
}
void UDPClientESP8266AT::end()
{
//sendCommand("AT+CWQAP\r\n");
}
bool UDPClientESP8266AT::setup(const char* ssid, const char* password, unsigned int timeout)
{
bool gotIP = false;
// test the module by issuing ATE0 command and confirming response of "OK"
const char* cmds[] = {"ATE0\r\n", "AT+CWMODE=1\r\n", "AT+CIPMUX=0\r\n", "AT+CWDHCP=1,1\r\n"};
for (byte n = 0; n < sizeof(cmds) / sizeof(cmds[0]); n++) {
delay(100);
if (!sendCommand(cmds[n], 100)) {
delay(100);
sendCommand(cmds[n], 100);
}
if (rxLen) {
if (strstr_P(rxBuf, PSTR("WIFI GOT IP"))) {
// WIFI automatically connected
gotIP = true;
}
}
}
// generate and send AT command for joining AP
if (!gotIP) {
sprintf_P(buffer, PSTR("AT+CWJAP=\"%s\",\"%s\"\r\n"), ssid, password);
if (!sendCommand(buffer, 7000)) return false;
}
return true;
}
String UDPClientESP8266AT::getIP()
{
// get IP address
for (uint32_t t = millis(); millis() - t < 10000; ) {
if (sendCommand("AT+CIFSR\r\n") && !strstr_P(buffer, PSTR("0.0.0.0"))) {
char *p = strchr(buffer, '\"');
char *ip = p ? p + 1 : buffer;
if ((p = strchr(ip, '\"')) || (p = strchr(ip, '\r'))) *p = 0;
// output IP address
return ip;
}
delay(500);
}
return "";
}
bool UDPClientESP8266AT::open(const char* host, uint16_t port)
{
for (byte n = 0; n < 3; n++) {
close();
sprintf_P(buffer, PSTR("AT+CIPSTART=\"UDP\",\"%s\",%u,8000,0\r\n"), host, port);
if (sendCommand(buffer, 3000)) {
return true;
} else {
// check if already connected
if (strstr_P(buffer, PSTR("CONN"))) return true;
}
Serial.println(buffer);
}
close();
return false;
}
void UDPClientESP8266AT::close()
{
sendCommand("AT+CIPCLOSE\r\n", 500);
}
bool UDPClientESP8266AT::send(const char* data, unsigned int len)
{
sprintf_P(buffer, PSTR("AT+CIPSEND=%u\r\n"), len);
if (sendCommand(buffer, 100, ">") && sendCommand(data, 1000, "OK")) {
return true;
} else {
Serial.println(buffer);
return false;
}
}
char* UDPClientESP8266AT::receive(int* pbytes, unsigned int timeout)
{
if (!rxLen && !sendCommand(0, timeout, "+IPD,")) {
return 0;
}
if (rxLen) {
if (pbytes) *pbytes = rxLen;
rxLen = 0;
return rxBuf;
}
return 0;
}
bool UDPClientESP8266AT::sendCommand(const char* cmd, unsigned int timeout, const char* expected)
{
if (cmd) {
m_device->xbWrite(cmd);
}
buffer[0] = 0;
byte ret = m_device->xbReceive(buffer, sizeof(buffer), timeout, &expected, 1);
// reception
char *p = strstr_P(buffer, PSTR("+IPD,"));
if (p) {
p += 5;
rxLen = atoi(p);
char *q = strchr(p, ':');
if (q++) {
int maxLen = buffer + sizeof(buffer) - q;
if (rxLen > maxLen) rxLen = maxLen;
if (rxBuf) free(rxBuf);
rxBuf = (char*)malloc(rxLen + 1);
memcpy(rxBuf, q, rxLen);
rxBuf[rxLen] = 0;
} else {
rxLen = 0;
}
}
return ret != 0;
}
/*******************************************************************************
Implementation for SIM800 (SIM800 AT command-set)
*******************************************************************************/
bool UDPClientSIM800::begin(CFreematics* device, bool nocheck)
{
m_device = device;
if (m_stage == 0) {
device->xbBegin(XBEE_BAUDRATE);
m_stage = 1;
}
if (nocheck) {
device->xbTogglePower();
return true;
}
unsigned long t = millis();
for (;;) {
for (byte m = 0; m < 3; m++) {
sendCommand("AT\r");
if (sendCommand("ATE0\r") && sendCommand("ATI\r")) {
m_stage = 2;
return true;
}
}
if (millis() - t >= 30000) break;
device->xbTogglePower();
delay(2000);
}
return false;
}
void UDPClientSIM800::end()
{
sendCommand("AT+CPOWD=1\r");
m_stage = 1;
}
bool UDPClientSIM800::setup(const char* apn, unsigned int timeout, bool gps, const char* pin)
{
uint32_t t = millis();
bool success = false;
if (pin) {
sprintf_P(m_buffer, PSTR("AT+CPIN=\"%s\"\r"), pin);
sendCommand(m_buffer);
}
do {
sendCommand("AT+CREG?\r");
success = strstr_P(m_buffer, PSTR("+CREG: 0,1")) || strstr_P(m_buffer, PSTR("+CREG: 0,5"));
Serial.print('.');
if (success) break;
delay(500);
} while (millis() - t < timeout);
if (!success) return false;
do {
success = sendCommand("AT+CGATT?\r", 3000, "+CGATT: 1");
} while (!success && millis() - t < timeout);
if (*apn) {
sprintf_P(m_buffer, PSTR("AT+CSTT=\"%s\"\r"), apn);
sendCommand(m_buffer);
}
sendCommand("AT+CIICR\r");
return success;
}
String UDPClientSIM800::getIP()
{
for (uint32_t t = millis(); millis() - t < 60000; ) {
if (sendCommand("AT+CIFSR\r", 3000, ".")) {
char *p;
for (p = m_buffer; *p && !isdigit(*p); p++);
char *q = strchr(p, '\r');
if (q) *q = 0;
return p;
}
}
return "";
}
int UDPClientSIM800::getSignal()
{
if (sendCommand("AT+CSQ\r", 500)) {
char *p = strchr(m_buffer, ':');
if (p) {
int csq = atoi(p + 2);
if (csq == 0)
return -115;
else if (csq == 1)
return -111;
else if (csq != 99)
return csq * 2 - 114;
}
}
return 0;
}
String UDPClientSIM800::getOperatorName()
{
// display operator name
if (sendCommand("AT+COPS?\r") == 1) {
char *p = strstr(m_buffer, ",\"");
if (p) {
p += 2;
char *s = strchr(p, '\"');
if (s) *s = 0;
return p;
}
}
return "";
}
bool UDPClientSIM800::checkSIM()
{
return (sendCommand("AT+CPIN?\r") && strstr(m_buffer, "READY"));
}
bool UDPClientSIM800::open(const char* host, uint16_t port)
{
#if 0
if (host) {
if (!isdigit(host[0])) {
String ip = queryIP(host);
if (ip.length()) {
strncpy(udpIP, ip.c_str(), sizeof(udpIP) - 1);
}
}
}
#endif
//sendCommand("AT+CLPORT=\"UDP\",8000\r");
sendCommand("AT+CIPSRIP=1\r");
//sendCommand("AT+CIPUDPMODE=1\r");
sprintf_P(m_buffer, PSTR("AT+CIPSTART=\"UDP\",\"%s\",\"%u\"\r"), host, port);
if (sendCommand(m_buffer, 3000)) return true;
close();
return false;
}
void UDPClientSIM800::close()
{
sendCommand("AT+CIPCLOSE\r");
}
bool UDPClientSIM800::send(const char* data, unsigned int len)
{
sprintf_P(m_buffer, PSTR("AT+CIPSEND=%u\r"), len);
if (sendCommand(m_buffer, 200, ">")) {
m_device->xbWrite(data);
m_device->xbWrite("\r");
if (sendCommand(0, 5000, "\r\nSEND OK")) {
return true;
}
}
return false;
}
char* UDPClientSIM800::receive(int* pbytes, unsigned int timeout)
{
char *data = checkIncoming(pbytes);
if (data) return data;
if (sendCommand(0, timeout, "RECV FROM:")) {
return checkIncoming(pbytes);
}
return 0;
}
char* UDPClientSIM800::checkIncoming(int* pbytes)
{
char *p = strstr(m_buffer, "RECV FROM:");
if (p) p = strchr(p, '\r');
if (!p) return 0;
while (*(++p) == '\r' || *p =='\n');
int len = strlen(p);
if (len > 2) {
if (pbytes) *pbytes = len;
return p;
} else {
if (sendCommand("AT\r", 1000, "OK\r\n", true)) {
p = m_buffer;
while (*p && (*p == '\r' || *p == '\n')) p++;
if (pbytes) *pbytes = strlen(p);
return p;
}
}
return 0;
}
String UDPClientSIM800::queryIP(const char* host)
{
sprintf_P(m_buffer, PSTR("AT+CDNSGIP=\"%s\"\r"), host);
if (sendCommand(m_buffer, 10000)) {
char *p = strstr(m_buffer, host);
if (p) {
p = strstr(p, ",\"");
if (p) {
char *ip = p + 2;
p = strchr(ip, '\"');
if (p) *p = 0;
return ip;
}
}
}
return "";
}
bool UDPClientSIM800::sendCommand(const char* cmd, unsigned int timeout, const char* expected, bool terminated)
{
if (cmd) {
m_device->xbWrite(cmd);
}
m_buffer[0] = 0;
byte ret = m_device->xbReceive(m_buffer, sizeof(m_buffer), timeout, &expected, 1);
if (ret) {
if (terminated) {
char *p = strstr(m_buffer, expected);
if (p) *p = 0;
}
return true;
} else {
return false;
}
}
GPS_DATA* UDPClientSIM800::getLocation()
{
if (sendCommand("AT+CIPGSMLOC=1,1\r", 3000)) do {
if (!m_gps) m_gps = new GPS_DATA;
char *p;
if (!(p = strchr(m_buffer, ':'))) break;
if (!(p = strchr(p, ','))) break;
float lng = atof(++p);
if (!(p = strchr(p, ','))) break;
float lat = atof(++p);
if (!(p = strchr(p, ','))) break;
int year = atoi(++p);
if (year < 2019) break;
if (!(p = strchr(p, '/'))) break;
int month = atoi(++p);
if (!(p = strchr(p, '/'))) break;
int day = atoi(++p);
if (!m_gps) m_gps = new GPS_DATA;
m_gps->lng = (int32_t)(lng * 1000000);
m_gps->lat = (int32_t)(lat * 1000000);
m_gps->date = (uint32_t)day * 10000 + month * 100 + (year - 2000);
if (!(p = strchr(p, ','))) break;
int hour = atoi(++p);
if (!(p = strchr(p, ':'))) break;
int minute = atoi(++p);
if (!(p = strchr(p, ':'))) break;
int second = atoi(++p);
m_gps->time = (uint32_t)hour * 10000 + minute * 100 + second;
m_gps->speed = 0;
m_gps->alt = 0;
m_gps->heading = 0;
return m_gps;
} while(0);
return 0;
}
/*******************************************************************************
Implementation for SIM5360
*******************************************************************************/
bool UDPClientSIM5360::begin(CFreematics* device, bool nocheck)
{
m_device = device;
if (m_stage == 0) {
device->xbBegin(XBEE_BAUDRATE);
m_stage = 1;
}
if (nocheck) {
device->xbTogglePower();
return true;
}
unsigned long t = millis();
for (;;) {
if (!sendCommand("AT\r")) sendCommand(0, 5000, "START");
if (sendCommand("ATE0\r") && sendCommand("ATI\r")) {
m_stage = 2;
return true;
}
if (millis() - t >= 30000) break;
device->xbTogglePower();
}
return false;
}
void UDPClientSIM5360::end()
{
sendCommand("AT+CPOF\r");
m_stage = 1;
}
bool UDPClientSIM5360::setup(const char* apn, unsigned int timeout, const char* pin)
{
uint32_t t = millis();
bool success = false;
//sendCommand("AT+CNMP=14\r"); // use WCDMA only
if (pin) {
sprintf_P(m_buffer, PSTR("AT+CPIN=\"%s\"\r"), pin);
sendCommand(m_buffer);
}
do {
do {
Serial.print('.');
delay(1000);
success = sendCommand("AT+CPSI?\r", 1000, "Online");
if (success) {
if (!strstr_P(m_buffer, PSTR("NO SERVICE")))
break;
success = false;
if (strstr_P(m_buffer, PSTR("ERROR"))) break;
} else {
if (strstr_P(m_buffer, PSTR("Off"))) break;
}
} while (millis() - t < timeout);
if (!success) break;
success = false;
do {
if (sendCommand("AT+CREG?\r", 1000, "+CREG: 0,")) {
char *p = strstr(m_buffer, "+CREG: 0,");
success = (p && (*(p + 9) == '1' || *(p + 9) == '5'));
}
} while (!success && millis() - t < timeout);
if (!success) break;
success = false;
do {
if (sendCommand("AT+CGREG?\r",1000, "+CGREG: 0,")) {
char *p = strstr(m_buffer, "+CGREG: 0,");
success = (p && (*(p + 10) == '1' || *(p + 10) == '5'));
}
} while (!success && millis() - t < timeout);
if (!success) break;
if (*apn) {
sprintf_P(m_buffer, PSTR("AT+CGSOCKCONT=1,\"IP\",\"%s\"\r"), apn);
sendCommand(m_buffer);
}
sendCommand("AT+CSOCKSETPN=1\r");
//sendCommand("AT+CSOCKAUTH=,,\"password\",\"password\"\r");
sendCommand("AT+CIPMODE=0\r");
sendCommand("AT+NETOPEN\r");
} while(0);
if (!success) Serial.println(m_buffer);
return success;
}
bool UDPClientSIM5360::startGPS()
{
// set GPS antenna voltage
sendCommand("AT+CVAUXV=61\r");
sendCommand("AT+CVAUXS=1\r");
if (sendCommand("AT+CGPS=1\r") || sendCommand("AT+CGPS?\r", 100, "+CGPS: 1")) {
if (!m_gps) m_gps = new GPS_DATA;
memset(m_gps, 0, sizeof(GPS_DATA));
return true;
}
return false;
}
void UDPClientSIM5360::stopGPS()
{
sendCommand("AT+CGPS=0\r");
if (m_gps) {
delete m_gps;
m_gps = 0;
}
}
String UDPClientSIM5360::getIP()
{
uint32_t t = millis();
do {
if (sendCommand("AT+IPADDR\r", 2000)) {
char *p = strstr_P(m_buffer, PSTR("+IPADDR:"));
if (p) {
char *ip = p + 9;
if (*ip != '0') {
char *q = strchr(ip, '\r');
if (q) *q = 0;
return ip;
}
}
}
delay(500);
} while (millis() - t < 10000);
return "";
}
int UDPClientSIM5360::getSignal()
{
if (sendCommand("AT+CSQ\r", 500)) {
char *p = strchr(m_buffer, ':');
if (p) {
int csq = atoi(p + 2);
if (csq != 99) {
return csq * 2 - 113;
}
}
}
return 0;
}
String UDPClientSIM5360::getOperatorName()
{
// display operator name
if (sendCommand("AT+COPS?\r") == 1) {
char *p = strstr(m_buffer, ",\"");
if (p) {
p += 2;
char *s = strchr(p, '\"');
if (s) *s = 0;
return p;
}
}
return "";
}
bool UDPClientSIM5360::checkSIM()
{
bool success;
for (byte n = 0; n < 10 && !(success = sendCommand("AT+CPIN?\r", 500, ": READY")); n++);
return success;
}
bool UDPClientSIM5360::open(const char* host, uint16_t port)
{
if (host) {
sprintf_P(m_buffer, PSTR("AT+CDNSGIP=\"%s\"\r"), host);
if (sendCommand(m_buffer, 10000, "+CDNSGIP:")) {
char *p = strstr(m_buffer, host);
if (p) {
if ((p = strchr(p, ','))) {
p += 2;
do {
udpIP[0] = atoi(p);
if (!(p = strchr(p, '.'))) break;
udpIP[1] = atoi(++p);
if (!(p = strchr(p, '.'))) break;
udpIP[2] = atoi(++p);
if (!(p = strchr(p, '.'))) break;
udpIP[3] = atoi(++p);
} while(0);
}
}
}
if (!udpIP[0]) {
return false;
}
udpPort = port;
}
sprintf_P(m_buffer, PSTR("AT+CIPOPEN=0,\"UDP\",\"%u.%u.%u.%u\",%u,8000\r"),
udpIP[0], udpIP[1], udpIP[2], udpIP[3], udpPort);
if (sendCommand(m_buffer, 3000)) return true;
close();
return false;
}
void UDPClientSIM5360::close()
{
sendCommand("AT+CIPCLOSE=0\r");
}
bool UDPClientSIM5360::send(const char* data, unsigned int len)
{
sprintf_P(m_buffer, PSTR("AT+CIPSEND=0,%u,\"%u.%u.%u.%u\",%u\r"),
len, udpIP[0], udpIP[1], udpIP[2], udpIP[3], udpPort);
m_device->xbWrite(m_buffer);
delay(10);
return sendCommand(data, 1000);
}
char* UDPClientSIM5360::receive(int* pbytes, unsigned int timeout)
{
for (;;) {
checkGPS();
char *ipd = strstr(m_buffer, "+IPD");
if (ipd) {
char *payload = checkIncoming(ipd, pbytes);
if (payload) {
*ipd = '-';
return payload;
}
}
if (!sendCommand("AT+CGPSINFO\r", timeout, "+IPD")) {
checkGPS();
break;
}
}
return 0;
}
char* UDPClientSIM5360::checkIncoming(char* ipd, int* pbytes)
{
unsigned int len = atoi(ipd + 4);
if (len == 0) return 0;
if (pbytes) *pbytes = len;
char *p = strchr(ipd, '\n');
if (p) {
if (strlen(++p) > len) *(p + len) = 0;
return p;
}
return 0;
}
long UDPClientSIM5360::parseDegree(const char* s)
{
char *p;
unsigned long left = atol(s);
unsigned long tenk_minutes = (left % 100UL) * 100000UL;
if ((p = strchr(s, '.')))
{
unsigned long mult = 10000;
while (isdigit(*++p))
{
tenk_minutes += mult * (*p - '0');
mult /= 10;
}
}
return (left / 100) * 1000000 + tenk_minutes / 6;
}
void UDPClientSIM5360::checkGPS()
{
char *p;
if (m_gps && (p = strstr_P(m_buffer, PSTR("+CGPSINFO:")))) do {
GPS_DATA gl;
if (!(p = strchr(p, ':'))) break;
if (*(++p) == ',') break;
gl.lat = parseDegree(p);
if (!(p = strchr(p, ','))) break;
if (*(++p) == 'S') gl.lat = -gl.lat;
if (!(p = strchr(p, ','))) break;
gl.lng = parseDegree(++p);
if (!(p = strchr(p, ','))) break;
if (*(++p) == 'W') gl.lng = -gl.lng;
if (!(p = strchr(p, ','))) break;
gl.date = atol(++p);
if (!(p = strchr(p, ','))) break;
gl.time = atol(++p) * 100;
if (!(p = strchr(p, ','))) break;
gl.alt = atoi(++p);
if (!(p = strchr(p, ','))) break;
gl.speed = atof(++p) * 100;
if (!(p = strchr(p, ','))) break;
gl.heading = atoi(++p);
memcpy(m_gps, &gl, sizeof(gl));
} while (0);
}
bool UDPClientSIM5360::sendCommand(const char* cmd, unsigned int timeout, const char* expected)
{
if (cmd) {
m_device->xbWrite(cmd);
}
memset(m_buffer, 0, sizeof(m_buffer));
byte ret = m_device->xbReceive(m_buffer, sizeof(m_buffer), timeout, &expected, 1);
if (ret) {
return true;
} else {
return false;
}
}