/************************************************************************* * 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 #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; } }