Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: aconno_I2C Lis2dh12 WatchdogTimer
modem.cpp
- Committer:
- pathfindr
- Date:
- 2019-01-21
- Revision:
- 47:cc6d4d0bf897
- Parent:
- 43:7b232f03628f
- Child:
- 48:64b1613941d5
File content as of revision 47:cc6d4d0bf897:
#include "modem.h"
char ATinBuffer[200];
Modem::Modem(PinName pwrkey, PinName vreg_en, PinName w_disable): _pwrkey(pwrkey), _vreg_en(vreg_en), _w_disable(w_disable)
{
}
void Modem::ATsendCMD(char* cmd)
{
NRFuart_flush();
NRFuart_puts(cmd);
NRFuart_puts("\r");
}
bool Modem::ATwaitForWord(char* word, uint32_t timeout)
{
int targetIndex = 0;
bool havefullmatch = false;
char captured[32];
memset(captured,0,sizeof(captured));
Timer t;
t.start();
uint32_t startmillis = t.read_ms();
uint32_t runtime = 0;
while(!havefullmatch && runtime < timeout) {
runtime = (t.read_ms() - startmillis);
if(NRFuart_readable()) {
char c = NRFuart_getc();
if (c != word[targetIndex]) { //no match, reset
targetIndex = 0;
}
if (c == word[targetIndex]) { //we have a match
captured[targetIndex] = c;
targetIndex ++;
//check for full match
if ( strcmp(word, captured) == 0 ) {
havefullmatch = true;
}
}
}
}
t.stop();
t.reset();
if (havefullmatch) {
return true;
} else {
return false;
}
}
bool Modem::ATgetResponse(char terminator, uint32_t timeout)
{
memset(ATinBuffer,0x00,sizeof(ATinBuffer));
int charindex = 0;
bool gotTerminator = false;
Timer t;
t.start();
uint32_t startmillis = t.read_ms();
uint32_t runtime = 0;
while(!gotTerminator && runtime < timeout) {
runtime = (t.read_ms() - startmillis);
if(NRFuart_readable()) {
char c = NRFuart_getc();
if (c == terminator) {
gotTerminator = true;
} else {
ATinBuffer[charindex] = c;
charindex++;
}
}
}
t.stop();
t.reset();
ATinBuffer[charindex] = '\n'; //make sure we end with whitespace lf
return gotTerminator;
}
bool Modem::on(bool force2G)
{
NRFuart_init_nohwfc();
if (!GLOBAL_modemOn) {
_w_disable = 0; // this sets the modem to airplane mode
_vreg_en = 1;
ThisThread::sleep_for(200);
_pwrkey = 0;
ThisThread::sleep_for(200);
_pwrkey = 1;
GLOBAL_modemOn = true;
//CONFIGURE
if (ATwaitForWord("RDY",ATTIMEOUT_MED)) {
//TURN OFF ECHO
ATsendCMD("ATE0");
ATwaitForWord("OK",ATTIMEOUT_SHORT);
//ENABLE AIRPLANE MODE CONTROL WITH PIN
ATsendCMD("AT+QCFG=\"airplanecontrol\",1");
ATwaitForWord("OK",ATTIMEOUT_SHORT);
//CONNECTION TYPE
//ATsendCMD("AT+QCFG=\"nwscanmode\",1"); //2G only connection
//ATsendCMD("AT+QCFG=\"nwscanmode\",2"); //3G only connection
//ATsendCMD("AT+QCFG=\"nwscanmode\",0"); //AUTO
if (force2G) {
ATsendCMD("AT+QCFG=\"nwscanmode\",1"); //2G only connection
ATwaitForWord("OK",ATTIMEOUT_SHORT);
} else {
ATsendCMD("AT+QCFG=\"nwscanmode\",0"); //AUTO
ATwaitForWord("OK",ATTIMEOUT_SHORT);
//PRIORITISE 2G connection (reason being uses less power in some instances and can get cell tower tirangulation)
ATsendCMD("AT+QCFG=\"nwscanseq\",1"); //2G priority
//ATsendCMD("AT+QCFG=\"nwscanseq\",2"); //3G priority
//ATsendCMD("AT+QCFG=\"nwscanseq\",0"); //AUTO - default
ATwaitForWord("OK",ATTIMEOUT_SHORT);
}
return true;
} else {
return false;
}
} else {
return true;
}
}
void Modem::off(bool soft)
{
if (GLOBAL_modemOn) {
if (soft) {
//ATsendCMD("AT+QPOWD=0");
//ATwaitForWord("POWERED DOWN",ATTIMEOUT_VERYSHORT);
}
GLOBAL_modemOn = false;
}
GLOBAL_registeredOnNetwork = false;
_w_disable = 0; //enable airplane mode
_pwrkey = 0; //set power key low
_vreg_en = 0; //kill power to module
}
long long Modem::getIMEI()
{
long long imei = 0;
ATsendCMD("AT+GSN");
if (ATwaitForWord("\r",ATTIMEOUT_SHORT)) {
if (ATgetResponse('\r',ATTIMEOUT_SHORT)) {
imei = atoll(ATinBuffer);
}
};
NRFuart_flush();
return imei;
}
char* Modem::getModemModel()
{
char* modemModel;
ATsendCMD("AT+GMM");
if (ATwaitForWord("\r",ATTIMEOUT_SHORT)) {
if (ATgetResponse('\r',ATTIMEOUT_SHORT)) {
sscanf(ATinBuffer,"%s", modemModel);
}
};
NRFuart_flush();
return modemModel;
}
bool Modem::registerOnNetwork(int maxAttempts, uint32_t timeout)
{
//CHECK WE ARE NOT ALREADY ON NETOWRK
if (!GLOBAL_registeredOnNetwork) {
int attempt = 0;
Timer t;
t.start();
//DISABLE AIRPLANE MODE
_w_disable = 1;
NRFuart_flush();
while (attempt < maxAttempts) {
watchdogKick();
t.reset();
uint32_t startmillis = t.read_ms();
uint32_t runtime = 0;
while(!GLOBAL_registeredOnNetwork && runtime < timeout) {
runtime = (t.read_ms() - startmillis);
ThisThread::sleep_for(1000);
ATsendCMD("AT+CREG?");
if (ATwaitForWord("+CREG: 0,5",ATTIMEOUT_VERYSHORT)) {
NRFuart_flush();
GLOBAL_registeredOnNetwork = true;
};
}
if (!GLOBAL_registeredOnNetwork) {
off(false);
ThisThread::sleep_for(1000);
on(RET_force2G);
}
attempt ++;
}
t.stop();
}
NRFuart_flush();
if (GLOBAL_registeredOnNetwork) {
return true;
} else {
return false;
}
}
bool Modem::USSDsend(char* message, int maxAttempts)
{
bool sent = false;
int attempt = 0;
//TRY X NUMBER OF TIMES
while (!sent && attempt < maxAttempts) {
char bytestosend[160];
snprintf(bytestosend, sizeof(bytestosend), "AT+CUSD=1,\"#469*%s#\"", message);
ATsendCMD(bytestosend);
if (ATwaitForWord("+CUSD: 0",ATTIMEOUT_MED)) {
sent = true;
};
NRFuart_flush();
attempt ++;
}
if (sent) {
return true;
} else {
return false;
}
}
char* Modem::USSDreceive(int messageIndex)
{
bool received = false;
uint32_t timeout = ATTIMEOUT_LONG;
int USSDmessageIndex = 0;
int matchCount = 0;
Timer t;
t.start();
//TRY UNTIL TIMEOUT
uint32_t startmillis = t.read_ms();
uint32_t runtime = 0;
while(!received && runtime < timeout) {
runtime = (t.read_ms() - startmillis);
if (ATwaitForWord("+CUSD: 0",ATTIMEOUT_SHORT)) {
led1 = 0;
if (ATgetResponse('\r',ATTIMEOUT_SHORT)) {
if ( (matchCount = sscanf(ATinBuffer,",\"%d#%[^#]",USSDmessageIndex,ATinBuffer) ) > 0 ) {
if (USSDmessageIndex == messageIndex) {
//NEED TO GET THIS WORKING SO WE KNOW WE ARE DEALING WITH THE RIGHT MESSAGE
//MOVE THE BELOW INTO THIS IF STAEMEBNTS WHEN DONE
}
led1 = 1;
received = true;
}
}
}
}
NRFuart_flush();
if (received) {
return ATinBuffer;
} else {
return "err";
}
}
char* Modem::USSDmessage(char* message, bool needResponse, int maxAttempts, char* api)
{
uint8_t messageIndex = 1;
bool result;
int messageLength = strlen(message);
if (messageLength > USSD_MAXLENGTH) {
char message_failsafe[100];
snprintf(message_failsafe,sizeof(message_failsafe),"(%s,a:error,z:TOOBIG,s:1,c:%d)\0",api,messageIndex);
result = USSDsend(message_failsafe, maxAttempts);
} else {
result = USSDsend(message, maxAttempts);
}
if (result) {
if (needResponse) {
char* response = USSDreceive(messageIndex);
if (strcmp(response, "err") != 0) {
return response;
} else {
return "sendonly";
}
} else {
return "ok";
}
} else {
return "err";
}
}
char* Modem::getLocation(uint8_t accuracy, uint16_t timeout_seconds)
{
NRFuart_flush();
bool haveGPSFix = false;
bool haveCellFix = false;
static char locDataOut[100];
memset(locDataOut,0x00,sizeof(locDataOut));
Timer t;
t.start();
uint32_t startmillis;
uint32_t runtime;
if (accuracy >= 2) {
//Enable External LNA power - IS DISABLED BY DEFAULT
ATsendCMD("AT+QGPSCFG=\"lnacontrol\",1");
ATwaitForWord("OK",5000);
//TURN ON GPS
ATsendCMD("AT+QGPS=1");
ATwaitForWord("OK",ATTIMEOUT_SHORT);
//TRY UNTIL TIMEOUT
uint8_t GPS_fixstage = 0;
uint8_t GPS_fixcount = 0;
startmillis = t.read_ms();
runtime = 0;
while(!haveGPSFix && runtime < (timeout_seconds*1000)) {
watchdogKick();
ThisThread::sleep_for(5000); //this goes first
runtime = (t.read_ms() - startmillis);
ATsendCMD("AT+QGPSLOC=2");
if (ATwaitForWord("+QGPSLOC: ",ATTIMEOUT_SHORT)) {
GPS_fixstage = 1;
GPS_fixcount ++;
if (GPS_fixcount == 2) { //wait 10 seconds to get a better fix // need to improve this logic
haveGPSFix = true;
} else {
NRFuart_flush();
}
} else {
NRFuart_flush();
}
if (haveGPSFix) {
//+QGPSLOC: 233510.0,52.55415,1.24021,1.2,59.2,2,0.00,0.0,0.0,201218,05
int matchCount = 0;
float utc;
float lat;
float lng;
float hdp;
float alt;
uint8_t fix;
float cog;
float spkm;
float spkn;
uint32_t date;
uint8_t sat;
//Example data
//115757.0,52.62091,1.29536,0.8,58.2,2,0.00,0.0,0.0,211218,07
if (ATgetResponse('\r',ATTIMEOUT_SHORT)) {
if ((matchCount = sscanf(ATinBuffer,"%f,%f,%f,%f,%f,%d,%f,%f,%f,%d,%d",&utc,&lat,&lng,&hdp,&alt,&fix,&cog,&spkm,&spkn,&date,&sat)) == 11 ) {
//{“fix”:“GPS”,“sat”:“9",“lat”:“52.913254",“lng”:“-1.455289",“hdp”:“89.0",“spd”:“0.0"}
sprintf(locDataOut,",g:(fix:GPS,sat:%d,lat:%.6f,lng:%.6f,hdp:%.1f,spd:%.1f)\0",sat,lat,lng,hdp,spkm);
}
}
}
}
//TURN OFF GPS
ATsendCMD("AT+QGPSEND");
ATwaitForWord("OK",ATTIMEOUT_SHORT);
}
//SHALL WE GET CELL LOCATION
if (!haveGPSFix && accuracy >= 1) {
registerOnNetwork(2, 90000);
int matchCount;
char type[6];
char cellID[6];
char lac[6];
int mcc;
int mnc;
startmillis = t.read_ms();
runtime = 0;
while(!haveCellFix && runtime < 15000) {
runtime = (t.read_ms() - startmillis);
ATsendCMD("AT+QENG=\"servingcell\"");
if (ATwaitForWord("+QENG: \"servingcell\",\"NOCONN\",",ATTIMEOUT_VERYSHORT)) {
if (ATgetResponse('\r',ATTIMEOUT_SHORT)) {
if ((matchCount = sscanf(ATinBuffer,"\"%[^\"]\",%d,%d,%[^,],%[^,]",&type,&mcc,&mnc,&lac,&cellID)) == 5 ) {
sprintf(locDataOut,",h:%s.%s.%d.%d\0",cellID,lac,mcc,mnc);
//sprintf(locDataOut,",h:41806.2252.234.30\0");
haveCellFix = true;
}
}
}
}
//example from mulbs
/*
2g
+QENG: "servingcell","NOCONN","2G",234,30,8CC,A34E,20,668,0,-80,0,5,4,26,26,1,-,-,-,-,-,-,-,-,-,"-"
3g
+QENG: "servingcell","NOCONN","3G",234,20,8A,CE735F,10588,52,-97,-99,11,25,16,128,-,-,-,-,-,"-",-
+QENG: "neighbourcell","2G",234,30,8CC,A34D,20,656,-89,17,17,0,0
+QENG: "neighbourcell","2G",234,30,8CC,678,61,686,-104,2,2,0,0
+QENG: "neighbourcell","2G",234,30,8CC,4303,32,676,-104,2,2,0,0
+QENG: "neighbourcell","2G",234,30,8CC,B2B2,16,692,-107,-1,-1,0,0
https://www.neilson.co.za/mobile-network-geolocation-obtaining-the-cell-ids-the-signal-strength-of-surrounding-towers-from-a-gsm-modem/
*/
//ATsendCMD("AT+QENG=\"neighbourcell\"");
//ATwaitForWord("OK",ATTIMEOUT_LONG);
}
//RETURN
if (accuracy == 0) {
sprintf(locDataOut,"\0");
} else if (!haveGPSFix && !haveCellFix) {
sprintf(locDataOut,"\0");
}
return locDataOut;
}
Modem::~Modem(){};