ublox-cellular-driver-gen Fork
Fork of ublox-cellular-driver-gen by
Diff: UbloxCellularDriverGen.cpp
- Revision:
- 0:bb5fabac67ab
- Child:
- 2:08302b9cd519
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/UbloxCellularDriverGen.cpp Mon Jun 05 13:00:31 2017 +0000 @@ -0,0 +1,730 @@ +/* Copyright (c) 2017 ublox Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "UbloxCellularDriverGen.h" +#include "string.h" +#if defined(FEATURE_COMMON_PAL) +#include "mbed_trace.h" +#define TRACE_GROUP "UCID" +#else +#define debug_if(_debug_trace_on, ...) (void(0)) // dummies if feature common pal is not added +#define tr_info(...) (void(0)) // dummies if feature common pal is not added +#define tr_error(...) (void(0)) // dummies if feature common pal is not added +#endif + +/********************************************************************** + * PROTECTED METHODS: Short Message Service + **********************************************************************/ + +// URC for Short Message listing. +void UbloxCellularDriverGen::CMGL_URC() +{ + char buf[64]; + int index; + + // Note: not calling _at->recv() from here as we're + // already in an _at->recv() + // +CMGL: <ix>,... + *buf = 0; + if (read_at_to_char(buf, sizeof(buf), '\n') > 0) { + // Now also read out the text message, so that we don't + // accidentally trigger URCs or the like on any of + // its contents + *_smsBuf = 0; + read_at_to_char(_smsBuf, sizeof(_smsBuf), '\n'); + // Note: don't put any debug in here, this URC is being + // called multiple times and debug may cause it to + // miss characters + if (sscanf(buf, ": %d,", &index) == 1) { + _smsCount++; + if ((_userSmsIndex != NULL) && (_userSmsNum > 0)) { + *_userSmsIndex = index; + _userSmsIndex++; + _userSmsNum--; + } + } + } +} + +// URC for new SMS messages. +void UbloxCellularDriverGen::CMTI_URC() +{ + char buf[32]; + + // Note: not calling _at->recv() from here as we're + // already in an _at->recv() + *buf = 0; + if (read_at_to_char(buf, sizeof (buf), '\n') > 0) { + // No need to parse, any content is good + tr_info("New SMS received"); + } +} + +/********************************************************************** + * PROTECTED METHODS: Unstructured Supplementary Service Data + **********************************************************************/ + +// URC for call waiting. +void UbloxCellularDriverGen::CCWA_URC() +{ + char buf[32]; + int numChars; + int a; + int b = 0; + + // Note: not calling _at->recv() from here as we're + // already in an _at->recv() + // +CCWA: <status>[, <class>] + numChars = read_at_to_char(buf, sizeof (buf), '\n'); + if (numChars > 0) { + if (sscanf(buf, ": %d, %d", &a, &b) > 0) { + if (_ssUrcBuf == NULL) { + _ssUrcBuf = (char *) malloc(numChars + 5 + 1); + if (_ssUrcBuf != NULL) { + memcpy (_ssUrcBuf, "+CCWA", 5); + memcpy (_ssUrcBuf + 5, buf, numChars); + *(_ssUrcBuf + numChars + 5) = 0; + if (a > 0) { + debug_if(_debug_trace_on, "Calling Waiting is active"); + } else { + debug_if(_debug_trace_on, "Calling Waiting is not active"); + } + if (b > 0) { + if (b & 0x01) { + debug_if(_debug_trace_on, " for voice\n"); + } + if (b & 0x02) { + debug_if(_debug_trace_on, " for data\n"); + } + if (b & 0x04) { + debug_if(_debug_trace_on, " for fax\n"); + } + if (b & 0x08) { + debug_if(_debug_trace_on, " for SMS\n"); + } + if (b & 0x10) { + debug_if(_debug_trace_on, " for data circuit sync\n"); + } + if (b & 0x20) { + debug_if(_debug_trace_on, " for data circuit async\n"); + } + if (b & 0x40) { + debug_if(_debug_trace_on, " for dedicated packet access\n"); + } + if (b & 0x80) { + debug_if(_debug_trace_on, " for dedicated PAD access\n"); + } + } + } + } + } + } +} + +// URC for call forwarding. +void UbloxCellularDriverGen::CCFC_URC() +{ + char buf[32]; + int numChars; + char num[32]; + int a, b; + int numValues; + + // Note: not calling _at->recv() from here as we're + // already in an _at->recv() + // +CCFC: <status>[, <class>] + numChars = read_at_to_char(buf, sizeof (buf), '\n'); + if (numChars > 0) { + memset (num, 0, sizeof (num)); + numValues = sscanf(buf, ": %d,%d,\"%32[^\"][\"]", &a, &b, num); + if (numValues > 0) { + if (_ssUrcBuf == NULL) { + _ssUrcBuf = (char *) malloc(numChars + 5 + 1); + if (_ssUrcBuf != NULL) { + memcpy (_ssUrcBuf, "+CCFC", 5); + memcpy (_ssUrcBuf + 5, buf, numChars); + *(_ssUrcBuf + numChars + 5) = 0; + if (a > 0) { + debug_if(_debug_trace_on, "Calling Forwarding is active "); + } else { + debug_if(_debug_trace_on, "Calling Forwarding is not active "); + } + if (numValues > 1) { + if (b > 0) { + if (b & 0x01) { + debug_if(_debug_trace_on, " for voice"); + } + if (b & 0x02) { + debug_if(_debug_trace_on, " for data"); + } + if (b & 0x04) { + debug_if(_debug_trace_on, " for fax"); + } + if (b & 0x08) { + debug_if(_debug_trace_on, " for SMS"); + } + if (b & 0x10) { + debug_if(_debug_trace_on, " for data circuit sync"); + } + if (b & 0x20) { + debug_if(_debug_trace_on, " for data circuit async"); + } + if (b & 0x40) { + debug_if(_debug_trace_on, " for dedicated packet access"); + } + if (b & 0x80) { + debug_if(_debug_trace_on, " for dedicated PAD access"); + } + } + } + if (numValues > 2) { + debug_if(_debug_trace_on, " for %s\n", num); + } else { + debug_if(_debug_trace_on, "\n"); + } + } + } + } + } +} + + +// URC for calling line ID restriction. +void UbloxCellularDriverGen::CLIR_URC() +{ + char buf[32]; + int numChars; + int a, b; + int numValues; + + // Note: not calling _at->recv() from here as we're + // already in an _at->recv() + // +CLIR: <n>[, <m>] + numChars = read_at_to_char(buf, sizeof (buf), '\n'); + if (numChars > 0) { + numValues = sscanf(buf, ": %d,%d", &a, &b); + if (numValues > 0) { + if (_ssUrcBuf == NULL) { + _ssUrcBuf = (char *) malloc(numChars + 5 + 1); + if (_ssUrcBuf != NULL) { + memcpy (_ssUrcBuf, "+CLIR", 5); + memcpy (_ssUrcBuf + 5, buf, numChars); + *(_ssUrcBuf + numChars + 5) = 0; + switch (a) { + case 0: + debug_if(_debug_trace_on, "Calling Line ID restriction is as subscribed\n"); + break; + case 1: + debug_if(_debug_trace_on, "Calling Line ID invocation "); + break; + case 2: + debug_if(_debug_trace_on, "Calling Line ID suppression "); + break; + } + if (numValues > 2) { + switch (b) { + case 0: + debug_if(_debug_trace_on, " is not provisioned\n"); + break; + case 1: + debug_if(_debug_trace_on, " is provisioned permanently\n"); + break; + case 2: + debug_if(_debug_trace_on, " is unknown\n"); + break; + case 3: + debug_if(_debug_trace_on, " is in temporary mode, presentation restricted\n"); + break; + case 4: + debug_if(_debug_trace_on, " is in temporary mode, presentation allowed\n"); + break; + } + } + } + } + } + } +} + +// URC for calling line ID presentation. +void UbloxCellularDriverGen::CLIP_URC() +{ + char buf[32]; + int numChars; + int a, b; + int numValues; + + // Note: not calling _at->recv() from here as we're + // already in an _at->recv() + // +CLIP: <n>[, <m>] + numChars = read_at_to_char(buf, sizeof (buf), '\n'); + if (numChars > 0) { + numValues = sscanf(buf, ": %d,%d", &a, &b); + if (numValues > 0) { + if (_ssUrcBuf == NULL) { + _ssUrcBuf = (char *) malloc(numChars + 5 + 1); + if (_ssUrcBuf != NULL) { + memcpy (_ssUrcBuf, "+CLIP", 5); + memcpy (_ssUrcBuf + 5, buf, numChars); + *(_ssUrcBuf + numChars + 5) = 0; + switch (a) { + case 0: + debug_if(_debug_trace_on, "Calling Line ID disable "); + break; + case 1: + debug_if(_debug_trace_on, "Calling Line ID enable "); + break; + } + if (numValues > 1) { + switch (b) { + case 0: + debug_if(_debug_trace_on, " is not provisioned\n"); + break; + case 1: + debug_if(_debug_trace_on, " is provisioned\n"); + break; + case 2: + debug_if(_debug_trace_on, " is unknown\n"); + break; + } + } + } + } + } + } +} + +// URC for connected line ID presentation. +void UbloxCellularDriverGen::COLP_URC() +{ + char buf[32]; + int numChars; + int a, b; + int numValues; + + // Note: not calling _at->recv() from here as we're + // already in an _at->recv() + // +COLP: <n>[, <m>] + numChars = read_at_to_char(buf, sizeof (buf), '\n'); + if (numChars > 0) { + numValues = sscanf(buf, ": %d,%d", &a, &b); + if (numValues > 0) { + if (_ssUrcBuf == NULL) { + _ssUrcBuf = (char *) malloc(numChars + 5 + 1); + if (_ssUrcBuf != NULL) { + memcpy (_ssUrcBuf, "+COLP", 5); + memcpy (_ssUrcBuf + 5, buf, numChars); + *(_ssUrcBuf + numChars + 5) = 0; + switch (a) { + case 0: + debug_if(_debug_trace_on, "Connected Line ID disable "); + break; + case 1: + debug_if(_debug_trace_on, "Connected Line ID enable "); + break; + } + if (numValues > 1) { + switch (b) { + case 0: + debug_if(_debug_trace_on, " is not provisioned\n"); + break; + case 1: + debug_if(_debug_trace_on, " is provisioned\n"); + break; + case 2: + debug_if(_debug_trace_on, " is unknown\n"); + break; + } + } + } + } + } + } +} + +// URC for connected line ID restriction. +void UbloxCellularDriverGen::COLR_URC() +{ + char buf[32]; + int numChars; + int a; + + // Note: not calling _at->recv() from here as we're + // already in an _at->recv() + // +COLR: <status> + numChars = read_at_to_char(buf, sizeof (buf), '\n'); + if (numChars > 0) { + if (sscanf(buf, ": %d", &a) > 0) { + if (_ssUrcBuf == NULL) { + _ssUrcBuf = (char *) malloc(numChars + 5 + 1); + if (_ssUrcBuf != NULL) { + memcpy (_ssUrcBuf, "+COLR", 5); + memcpy (_ssUrcBuf + 5, buf, numChars); + *(_ssUrcBuf + numChars + 5) = 0; + switch (a) { + case 0: + debug_if(_debug_trace_on, "Connected Line ID restriction is not provisioned\n"); + break; + case 1: + debug_if(_debug_trace_on, "Connected Line ID restriction is provisioned\n"); + break; + case 2: + debug_if(_debug_trace_on, "Connected Line ID restriction is unknown\n"); + break; + } + } + } + } + } +} + +/********************************************************************** + * PUBLIC METHODS: Generic + **********************************************************************/ + +// Constructor. +UbloxCellularDriverGen::UbloxCellularDriverGen(PinName tx, PinName rx, + int baud, bool debug_on) +{ + _userSmsIndex = NULL; + _userSmsNum = 0; + _smsCount = 0; + _ssUrcBuf = NULL; + + // Initialise the base class, which starts the AT parser + baseClassInit(tx, rx, baud, debug_on); + + // URCs related to SMS + _at->oob("+CMGL", callback(this, &UbloxCellularDriverGen::CMGL_URC)); + // Include the colon with this one as otherwise it could be found + // by +CMT, should it ever occur + _at->oob("+CMTI:", callback(this, &UbloxCellularDriverGen::CMTI_URC)); + + // URCs relater to supplementary services + _at->oob("+CCWA", callback(this, &UbloxCellularDriverGen::CCWA_URC)); + _at->oob("+CCFC", callback(this, &UbloxCellularDriverGen::CCFC_URC)); + _at->oob("+CLIR", callback(this, &UbloxCellularDriverGen::CLIR_URC)); + _at->oob("+CLIP", callback(this, &UbloxCellularDriverGen::CLIP_URC)); + _at->oob("+COLP", callback(this, &UbloxCellularDriverGen::COLP_URC)); + _at->oob("+COLR", callback(this, &UbloxCellularDriverGen::COLR_URC)); +} + +// Destructor. +UbloxCellularDriverGen::~UbloxCellularDriverGen() +{ +} + +/********************************************************************** + * PUBLIC METHODS: Short Message Service + **********************************************************************/ + +// Count the number of messages on the module. +int UbloxCellularDriverGen::smsList(const char* stat, int* index, int num) +{ + int numMessages = -1; + LOCK(); + + _userSmsIndex = index; + _userSmsNum = num; + _smsCount = 0; + // There is a callback to capture the result + // +CMGL: <ix>,... + _at->debug_on(false); // No time for AT interface debug + // as the list comes out in one long + // stream and we can lose characters if we + // pause to do printfs + if (_at->send("AT+CMGL=\"%s\"", stat) && _at->recv("OK\n")) { + numMessages = _smsCount; + } + _at->debug_on(_debug_trace_on); + + // Set this back to null so that the URC won't trample + _userSmsIndex = NULL; + + UNLOCK(); + return numMessages; +} + +// Send an SMS message. +bool UbloxCellularDriverGen::smsSend(const char* num, const char* buf) +{ + bool success = false; + char typeOfAddress = TYPE_OF_ADDRESS_NATIONAL; + LOCK(); + + if ((strlen (num) > 0) && (*(num) == '+')) { + typeOfAddress = TYPE_OF_ADDRESS_INTERNATIONAL; + } + if (_at->send("AT+CMGS=\"%s\",%d", num, typeOfAddress) && _at->recv(">")) { + if ((_at->write(buf, (int) strlen(buf)) >= (int) strlen(buf)) && + (_at->putc(0x1A) == 0) && // CTRL-Z + _at->recv("OK")) { + success = true; + } + } + + UNLOCK(); + return success; +} + +bool UbloxCellularDriverGen::smsDelete(int index) +{ + bool success; + LOCK(); + + success = _at->send("AT+CMGD=%d", index) && _at->recv("OK"); + + UNLOCK(); + return success; +} + +bool UbloxCellularDriverGen::smsRead(int index, char* num, char* buf, int len) +{ + bool success = false; + char * endOfString; + int smsReadLength = 0; + LOCK(); + + if (len > 0) { + //+CMGR: "REC READ", "+393488535999",,"07/04/05,18:02:28+08",145,4,0,0,"+393492000466",145,93 + // The text of the message. + // OK + memset (_smsBuf, 0, sizeof (SMS_BUFFER_SIZE)); // Ensure terminator + if (_at->send("AT+CMGR=%d", index) && + _at->recv("+CMGR: \"%*[^\"]\",\"%15[^\"]\"%*[^\n]\n", num) && + _at->recv("%" stringify(SMS_BUFFER_SIZE) "[^\n]\n", _smsBuf) && + _at->recv("OK")) { + endOfString = strchr(_smsBuf, 0); + if (endOfString != NULL) { + smsReadLength = endOfString - _smsBuf; + if (smsReadLength + 1 > len) { // +1 for terminator + smsReadLength = len - 1; + } + memcpy(buf, _smsBuf, smsReadLength); + *(buf + smsReadLength) = 0; // Add terminator + success = true; + } + } + } + + UNLOCK(); + return success; +} + +/********************************************************************** + * PUBLIC METHODS: Unstructured Supplementary Service Data + **********************************************************************/ + +// Perform a USSD command. +bool UbloxCellularDriverGen::ussdCommand(const char* cmd, char* buf, int len) +{ + bool success = false; + char * tmpBuf; + int atTimeout; + int x; + Timer timer; + LOCK(); + atTimeout = _at_timeout; // Has to be inside LOCK()s + + if (len > 0) { + *buf = 0; + if (len > USSD_STRING_LENGTH + 1) { + len = USSD_STRING_LENGTH + 1; + } + + tmpBuf = (char *) malloc(USSD_STRING_LENGTH + 1); + + if (tmpBuf != NULL) { + memset (tmpBuf, 0, USSD_STRING_LENGTH + 1); + // +CUSD: \"%*d, \"%128[^\"]\",%*d" + if (_at->send("AT+CUSD=1,\"%s\"", cmd)) { + // Wait for either +CUSD to come back or + // one of the other SS related URCs to trigger + if (_ssUrcBuf != NULL) { + free (_ssUrcBuf); + _ssUrcBuf = NULL; + } + timer.start(); + _at->set_timeout(1000); + while (!success && (timer.read_ms() < atTimeout)) { + if (_at->recv("+CUSD: %*d,\"")) { + // Note: don't wait for "OK" here as the +CUSD response may come + // before or after the OK + // Also, the return string may include newlines so can't just use + // recv() to capture it as recv() will stop capturing at a newline. + if (read_at_to_char(tmpBuf, USSD_STRING_LENGTH, '\"') > 0) { + success = true; + memcpy (buf, tmpBuf, len); + *(buf + len - 1) = 0; + } + } else { + // Some of the return values do not appear as +CUSD but + // instead as the relevant URC for call waiting, call forwarding, + // etc. Test those here. + if (_ssUrcBuf != NULL) { + success = true; + x = strlen (_ssUrcBuf); + if (x > len - 1 ) { + x = len - 1; + } + memcpy (buf, _ssUrcBuf, x); + *(buf + x) = 0; + free (_ssUrcBuf); + _ssUrcBuf = NULL; + } + } + } + at_set_timeout(atTimeout); + timer.stop(); + } + } + } + + UNLOCK(); + return success; +} + +/********************************************************************** + * PUBLIC: Module File System + **********************************************************************/ + +// Delete a file from the module's file system. +bool UbloxCellularDriverGen::delFile(const char* filename) +{ + bool success; + LOCK(); + + success = _at->send("AT+UDELFILE=\"%s\"", filename) && _at->recv("OK"); + + UNLOCK(); + return success; +} + +// Write a buffer of data to a file in the module's file system. +int UbloxCellularDriverGen::writeFile(const char* filename, const char* buf, int len) +{ + int bytesWritten = -1; + LOCK(); + + if (_at->send("AT+UDWNFILE=\"%s\",%d", filename, len) && _at->recv(">")) { + if ((_at->write(buf, len) >= len) && _at->recv("OK")) { + bytesWritten = len; + } + } + + UNLOCK(); + return bytesWritten; +} + +// Read a file from the module's file system +// Note: this is implemented with block reads since UARTSerial +// does not currently allow flow control and there is a danger +// of character loss with large whole-file reads +int UbloxCellularDriverGen::readFile(const char* filename, char* buf, int len) +{ + int countBytes = -1; // Counter for file reading (default value) + int bytesToRead = fileSize(filename); // Retrieve the size of the file + int offset = 0; + int blockSize = FILE_BUFFER_SIZE; + char respFilename[48 + 1]; + int sz, sz_read; + bool success = true; + int ch = 0; + int timeLimit; + Timer timer; + + debug_if(_debug_trace_on, "readFile: filename is %s; size is %d\n", filename, bytesToRead); + + memset(respFilename, 0, sizeof (respFilename)); // Ensure terminator + if (bytesToRead > 0) + { + if (bytesToRead > len) { + bytesToRead = len; + } + + while (success && (bytesToRead > 0)) { + + if (bytesToRead < blockSize) { + blockSize = bytesToRead; + } + LOCK(); + + if (blockSize > 0) { + if (_at->send("AT+URDBLOCK=\"%s\",%d,%d\r\n", filename, offset, blockSize) && + _at->recv("+URDBLOCK: \"%48[^\"]\",%d,\"", respFilename, &sz) && + (strcmp(filename, respFilename) == 0)) { + + // Would use _at->read() here, but if it runs ahead of the + // serial stream it returns -1 instead of the number of characters + // read so far, which is not very helpful so instead use _at->getc() and + // a time limit. The time limit is twice the amount of time it should take to + // read the block at the working baud rate + timer.reset(); + timer.start(); + timeLimit = blockSize * 2 / ((MBED_CONF_UBLOX_CELL_BAUD_RATE / 8) / 1000); + sz_read = 0; + while ((sz_read < blockSize) && (timer.read_ms() < timeLimit)) { + ch = _at->getc(); + if (ch >= 0) { + *buf = ch; + buf++; + sz_read++; + } + } + timer.stop(); + + if (sz_read == blockSize) { + bytesToRead -= sz_read; + offset += sz_read; + _at->recv("OK"); + } else { + debug_if(_debug_trace_on, "blockSize %d but only received %d bytes\n", blockSize, sz_read); + success = false; + } + } else { + success = false; + } + } + + UNLOCK(); + } + + if (success) { + countBytes = offset; + } + } + + return countBytes; +} + +// Return the size of a file. +int UbloxCellularDriverGen::fileSize(const char* filename) +{ + int returnValue = -1; + int fileSize; + LOCK(); + + if (_at->send("AT+ULSTFILE=2,\"%s\"", filename) && + _at->recv("+ULSTFILE: %d\n", &fileSize) && + _at->recv("OK")) { + returnValue = fileSize; + } + + UNLOCK(); + return returnValue; +} + +// End of file +