Added battery status, SMS are sent/received in Text format instead of PDU
This commit is contained in:
parent
83e50daf36
commit
a5ad4c4e1d
129
DTE.h
Normal file
129
DTE.h
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
/*
|
||||||
|
* DTE.h
|
||||||
|
*
|
||||||
|
* Created: 20/09/2016 15:40:51
|
||||||
|
* Author: Neta Yahav
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __DTE_H__
|
||||||
|
#define __DTE_H__
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
class DTE
|
||||||
|
{
|
||||||
|
//variables
|
||||||
|
public:
|
||||||
|
enum CommandResult
|
||||||
|
{
|
||||||
|
EXPECT_BUSY,
|
||||||
|
EXPECT_TIMEOUT,
|
||||||
|
EXPECT_DELAY,
|
||||||
|
EXPECT_RESULT
|
||||||
|
};
|
||||||
|
|
||||||
|
protected:
|
||||||
|
private:
|
||||||
|
String buffer;
|
||||||
|
Stream& stream;
|
||||||
|
unsigned int bufferIndex;
|
||||||
|
unsigned int bufferSize;
|
||||||
|
String response[3];
|
||||||
|
unsigned long timeout;
|
||||||
|
unsigned long tick;
|
||||||
|
unsigned int match;
|
||||||
|
CommandResult result;
|
||||||
|
//functions
|
||||||
|
public:
|
||||||
|
DTE(Stream& stream, unsigned int size) : stream(stream), bufferSize(size), result(EXPECT_RESULT) { buffer.reserve(size); }
|
||||||
|
~DTE() {};
|
||||||
|
void SendCommand(const char* command, unsigned long timeout, const char* response1, const char* response2 = 0, const char* response3 = 0)
|
||||||
|
{
|
||||||
|
match = 0;
|
||||||
|
result = EXPECT_BUSY;
|
||||||
|
response[0] = response1;
|
||||||
|
response[1] = response2;
|
||||||
|
response[2] = response3;
|
||||||
|
this->timeout = timeout;
|
||||||
|
// clear rx buffer
|
||||||
|
while (stream.available())
|
||||||
|
{
|
||||||
|
stream.read();
|
||||||
|
}
|
||||||
|
// send command
|
||||||
|
stream.print(command);
|
||||||
|
buffer = "";
|
||||||
|
tick = millis();
|
||||||
|
proccess();
|
||||||
|
}
|
||||||
|
void Delay(unsigned long delay)
|
||||||
|
{
|
||||||
|
timeout = delay;
|
||||||
|
result = EXPECT_DELAY;
|
||||||
|
tick = millis();
|
||||||
|
proccess();
|
||||||
|
}
|
||||||
|
bool getIsBusy()
|
||||||
|
{
|
||||||
|
proccess();
|
||||||
|
return (result == EXPECT_BUSY) || (result == EXPECT_DELAY);
|
||||||
|
}
|
||||||
|
CommandResult getResult() { return result ; }
|
||||||
|
unsigned int getMatch() { return match; }
|
||||||
|
String& getBuffer() { return buffer; }
|
||||||
|
protected:
|
||||||
|
private:
|
||||||
|
void proccess()
|
||||||
|
{
|
||||||
|
if(result == EXPECT_DELAY)
|
||||||
|
{
|
||||||
|
if(millis() - tick >= timeout)
|
||||||
|
result = EXPECT_RESULT;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(result != EXPECT_BUSY) return;
|
||||||
|
|
||||||
|
char c;
|
||||||
|
unsigned long now = millis();
|
||||||
|
|
||||||
|
while(millis() - tick < timeout)
|
||||||
|
{
|
||||||
|
while(stream.available() && (buffer.length() < (bufferSize)))
|
||||||
|
{
|
||||||
|
c = stream.read();
|
||||||
|
buffer += c;
|
||||||
|
if(buffer.endsWith(response[0]))
|
||||||
|
{
|
||||||
|
match = 0;
|
||||||
|
result = EXPECT_RESULT;
|
||||||
|
return;
|
||||||
|
}else if(response[1].length() != 0)
|
||||||
|
{
|
||||||
|
if(buffer.endsWith(response[1]))
|
||||||
|
{
|
||||||
|
match = 1;
|
||||||
|
result = EXPECT_RESULT;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}else if(response[2].length() != 0)
|
||||||
|
{
|
||||||
|
if(buffer.endsWith(response[2]))
|
||||||
|
{
|
||||||
|
match = 2;
|
||||||
|
result = EXPECT_RESULT;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(millis() - now > 5)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// time out
|
||||||
|
result = EXPECT_TIMEOUT;
|
||||||
|
}
|
||||||
|
}; //DTE
|
||||||
|
|
||||||
|
#endif //__DTE_H__
|
79
README_original.md
Normal file
79
README_original.md
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
# ArduinoThreadedGSM
|
||||||
|
|
||||||
|
|
||||||
|
Use this library to:
|
||||||
|
|
||||||
|
* Send/Read SMS
|
||||||
|
* Read Network Time
|
||||||
|
* Read GSM signal level
|
||||||
|
|
||||||
|
### Quick-start
|
||||||
|
|
||||||
|
Initialize modem:
|
||||||
|
|
||||||
|
```c++
|
||||||
|
ThreadedGSM modem(Serial1);
|
||||||
|
...
|
||||||
|
void setup()
|
||||||
|
{
|
||||||
|
modem.begin();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Set automatic intervals for syncing clock and signal levels:
|
||||||
|
```c++
|
||||||
|
modem.setInterval(ThreadedGSM::INTERVAL_CLOCK, 60000);
|
||||||
|
modem.setInterval(ThreadedGSM::INTERVAL_SIGNAL, 60000);
|
||||||
|
modem.setInterval(ThreadedGSM::INTERVAL_INBOX, 30000);
|
||||||
|
```
|
||||||
|
Create your event handlers
|
||||||
|
```c++
|
||||||
|
void clock(ThreadedGSM& modem, ThreadedGSM::NetworkTime& Time)
|
||||||
|
{
|
||||||
|
/* Network time */
|
||||||
|
}
|
||||||
|
|
||||||
|
void signal(ThreadedGSM& modem, ThreadedGSM::SignalLevel& Signal)
|
||||||
|
{
|
||||||
|
/* Signal */
|
||||||
|
}
|
||||||
|
|
||||||
|
void sms(ThreadedGSM& modem, String& Msg)
|
||||||
|
{
|
||||||
|
/* Message received in PDU */
|
||||||
|
}
|
||||||
|
|
||||||
|
void startup(ThreadedGSM& modem)
|
||||||
|
{
|
||||||
|
/* READY */
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Set event handlers for modem events as you like:
|
||||||
|
```c++
|
||||||
|
modem.setHandlers({
|
||||||
|
.signal = signal,
|
||||||
|
.clock = clock,
|
||||||
|
.incoming = sms,
|
||||||
|
.ready = startup,
|
||||||
|
.outgoing = NULL,
|
||||||
|
.power = NULL
|
||||||
|
});
|
||||||
|
```
|
||||||
|
and finally, let the modem loop as a thread in your Arduino loop()
|
||||||
|
```c++
|
||||||
|
void loop()
|
||||||
|
{
|
||||||
|
// "Non-blocking" loop
|
||||||
|
modem.loop();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
To send SMS messages, request to send them using sendSMS:
|
||||||
|
```c++
|
||||||
|
modem.sendSMS(PDU); // PDU as String, hexadecimal
|
||||||
|
```
|
||||||
|
*Don't forget to check whether your messages were sent successfuly, before sending more.*
|
||||||
|
|
||||||
|
*Use power event callbacks to set modem pins (EN/RST) on power on/off when needed*
|
||||||
|
|
||||||
|
|
||||||
|
Project hosted on GitHub: [https://github.com/neta540/ArduinoThreadedGSM](https://github.com/neta540/ArduinoThreadedGSM).
|
690
ThreadedGSM.h
Normal file
690
ThreadedGSM.h
Normal file
@ -0,0 +1,690 @@
|
|||||||
|
/*
|
||||||
|
* ThreadedGSM.h
|
||||||
|
*
|
||||||
|
* Created: 20/09/2016 11:14:02
|
||||||
|
* Author: Neta Yahav
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef __THREADEDGSM_H__
|
||||||
|
#define __THREADEDGSM_H__
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include "DTE.h"
|
||||||
|
|
||||||
|
// Defaults
|
||||||
|
#define THREADEDGSM_DEF_DTE_BUF_SIZ 512
|
||||||
|
#define THREADEDGSM_DEF_AT_TIMEOUT 5000
|
||||||
|
#define THREADEDGSM_DEF_STA_PON 10000
|
||||||
|
#define THREADEDGSM_DEF_STA_POF 1000
|
||||||
|
|
||||||
|
// Use custom values or default ones
|
||||||
|
#ifndef THREADEDGSM_DTE_BUFFER_SIZE
|
||||||
|
#define THREADEDGSM_DTE_BUFFER_SIZE THREADEDGSM_DEF_DTE_BUF_SIZ
|
||||||
|
#endif
|
||||||
|
#ifndef THREADEDGSM_AT_TIMEOUT
|
||||||
|
#define THREADEDGSM_AT_TIMEOUT THREADEDGSM_DEF_AT_TIMEOUT
|
||||||
|
#endif
|
||||||
|
#ifndef THREADEDGSM_STARTUP_DELAY
|
||||||
|
#define THREADEDGSM_STARTUP_DELAY THREADEDGSM_DEF_STA_PON
|
||||||
|
#endif
|
||||||
|
#ifndef THREADEDGSM_STARTUP_POWER_OFF_DELAY
|
||||||
|
#define THREADEDGSM_STARTUP_POWER_OFF_DELAY THREADEDGSM_DEF_STA_POF
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define THREADEDGSM_INTERVAL_COUNT 4
|
||||||
|
|
||||||
|
#ifdef THREADEDGSM_DEBUG
|
||||||
|
#define DEBUG_PRINT(x) THREADEDGSM_DEBUG.print (x)
|
||||||
|
#define DEBUG_PRINTLN(x) THREADEDGSM_DEBUG.println (x)
|
||||||
|
#else
|
||||||
|
#define DEBUG_PRINT(x)
|
||||||
|
#define DEBUG_PRINTLN(x)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class ThreadedGSM
|
||||||
|
{
|
||||||
|
//variables
|
||||||
|
public:
|
||||||
|
struct NetworkTime
|
||||||
|
{
|
||||||
|
int year;
|
||||||
|
int month;
|
||||||
|
int day;
|
||||||
|
int hour;
|
||||||
|
int minute;
|
||||||
|
int second;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ReadMessagesListTypeE {
|
||||||
|
READ_TYPE_UNREAD = 0,
|
||||||
|
READ_TYPE_READ = 1,
|
||||||
|
READ_TYPE_UNSENT = 2,
|
||||||
|
READ_TYPE_SENT = 3,
|
||||||
|
READ_TYPE_ALL = 4
|
||||||
|
};
|
||||||
|
|
||||||
|
enum IntervalSourceE
|
||||||
|
{
|
||||||
|
INTERVAL_CLOCK,
|
||||||
|
INTERVAL_INBOX,
|
||||||
|
INTERVAL_SIGNAL,
|
||||||
|
INTERVAL_BATTERY
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SignalLevel
|
||||||
|
{
|
||||||
|
int Dbm;
|
||||||
|
int Value;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BatteryInfo
|
||||||
|
{
|
||||||
|
int Percent;
|
||||||
|
int Voltage;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SMSInfo
|
||||||
|
{
|
||||||
|
String Number;
|
||||||
|
String Text;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef void (*ThreadedGSMCallbackSignal)(ThreadedGSM&, SignalLevel&);
|
||||||
|
typedef void (*ThreadedGSMCallbackClock)(ThreadedGSM&, NetworkTime&);
|
||||||
|
typedef void (*ThreadedGSMCallbackBattery)(ThreadedGSM&, BatteryInfo&);
|
||||||
|
typedef void (*ThreadedGSMCallbackIncomingSMS)(ThreadedGSM&, SMSInfo&);
|
||||||
|
typedef void (*ThreadedGSMCallbackBool)(ThreadedGSM&, bool);
|
||||||
|
typedef void (*ThreadedGSMCallback)(ThreadedGSM&);
|
||||||
|
struct conf
|
||||||
|
{
|
||||||
|
ThreadedGSMCallbackSignal signal;
|
||||||
|
ThreadedGSMCallbackClock clock;
|
||||||
|
ThreadedGSMCallbackIncomingSMS incoming;
|
||||||
|
ThreadedGSMCallback ready;
|
||||||
|
ThreadedGSMCallback outgoing;
|
||||||
|
ThreadedGSMCallbackBool power;
|
||||||
|
ThreadedGSMCallbackBattery battery;
|
||||||
|
};
|
||||||
|
protected:
|
||||||
|
private:
|
||||||
|
enum StatesStartup
|
||||||
|
{
|
||||||
|
STARTUP_POWER_OFF,
|
||||||
|
STARTUP_POWER_OFF_DELAY,
|
||||||
|
STARTUP_POWER_ON,
|
||||||
|
STARTUP_DELAY,
|
||||||
|
STARTUP_ENTER_AT,
|
||||||
|
STARTUP_CHK_CPIN,
|
||||||
|
STARTUP_CHK_CREG,
|
||||||
|
STARTUP_CHK_CLTS,
|
||||||
|
STARTUP_CHK_CENG
|
||||||
|
};
|
||||||
|
|
||||||
|
enum StatesClock
|
||||||
|
{
|
||||||
|
CLOCK_REQ,
|
||||||
|
CLOCK_VERIFY
|
||||||
|
};
|
||||||
|
|
||||||
|
enum StatesSignal
|
||||||
|
{
|
||||||
|
SIGNAL_REQ,
|
||||||
|
SIGNAL_VERIFY
|
||||||
|
};
|
||||||
|
|
||||||
|
enum StatesBattry
|
||||||
|
{
|
||||||
|
BATTERY_REQ,
|
||||||
|
BATTERY_VERIFY
|
||||||
|
};
|
||||||
|
|
||||||
|
enum StatesInbox
|
||||||
|
{
|
||||||
|
READ_REQ,
|
||||||
|
READ_CHK_CMGF,
|
||||||
|
READ_CHK_CPMS,
|
||||||
|
READ_CHK_CMGL,
|
||||||
|
READ_DELAY_CLEAR_BUFF,
|
||||||
|
READ_TEXT_CMGR,
|
||||||
|
READ_CHK_CMGR,
|
||||||
|
READ_CHK_CMGD
|
||||||
|
};
|
||||||
|
|
||||||
|
enum StatesOutbox
|
||||||
|
{
|
||||||
|
SEND_REQ,
|
||||||
|
SEND_CHK_CMGF,
|
||||||
|
SEND_CHK_RDY,
|
||||||
|
SEND_CHK_OK
|
||||||
|
};
|
||||||
|
|
||||||
|
unsigned long tick;
|
||||||
|
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
int Index; // Index of readed message
|
||||||
|
}Message;
|
||||||
|
|
||||||
|
SMSInfo SMSi; // Inbox SMS (incoming)
|
||||||
|
SMSInfo SMSo; // Outbox SMS (outgoing)
|
||||||
|
|
||||||
|
Stream& stream;
|
||||||
|
DTE dte;
|
||||||
|
|
||||||
|
unsigned long tickSync[THREADEDGSM_INTERVAL_COUNT];
|
||||||
|
unsigned long Intervals[THREADEDGSM_INTERVAL_COUNT];
|
||||||
|
|
||||||
|
// callbacks
|
||||||
|
conf configuration = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
|
||||||
|
|
||||||
|
enum ReqTypes
|
||||||
|
{
|
||||||
|
REQ_CLOCK = 1,
|
||||||
|
REQ_SIG = 2,
|
||||||
|
REQ_INBOX = 4,
|
||||||
|
REQ_OUTBOX = 8,
|
||||||
|
REQ_STARTUP = 16,
|
||||||
|
REQ_BATTERY = 32,
|
||||||
|
};
|
||||||
|
int requests;
|
||||||
|
int state;
|
||||||
|
int job;
|
||||||
|
//functions
|
||||||
|
public:
|
||||||
|
ThreadedGSM(Stream& stream) : stream(stream), dte(stream, THREADEDGSM_DTE_BUFFER_SIZE)
|
||||||
|
{
|
||||||
|
for(int i=0;i<THREADEDGSM_INTERVAL_COUNT;i++)
|
||||||
|
Intervals[i] = 0;
|
||||||
|
|
||||||
|
job = state = requests = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
~ThreadedGSM(){};
|
||||||
|
|
||||||
|
void nextJob()
|
||||||
|
{
|
||||||
|
job = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setHandlers(conf config)
|
||||||
|
{
|
||||||
|
this->configuration = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setInterval(IntervalSourceE source, unsigned long interval)
|
||||||
|
{
|
||||||
|
Intervals[source] = interval;
|
||||||
|
tickSync[source] = millis();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialization
|
||||||
|
void begin()
|
||||||
|
{
|
||||||
|
requests = (REQ_STARTUP);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call this function for executing thread
|
||||||
|
void loop()
|
||||||
|
{
|
||||||
|
if(dte.getIsBusy()) return;
|
||||||
|
|
||||||
|
// intervals
|
||||||
|
for(int i=0;i<THREADEDGSM_INTERVAL_COUNT;i++)
|
||||||
|
{
|
||||||
|
if(Intervals[i])
|
||||||
|
{
|
||||||
|
if(millis() - tickSync[i] >= Intervals[i])
|
||||||
|
{
|
||||||
|
switch (i) {
|
||||||
|
case INTERVAL_CLOCK:
|
||||||
|
requests |= REQ_CLOCK;
|
||||||
|
break;
|
||||||
|
case INTERVAL_INBOX:
|
||||||
|
requests |= REQ_INBOX;
|
||||||
|
break;
|
||||||
|
case INTERVAL_SIGNAL:
|
||||||
|
requests |= REQ_SIG;
|
||||||
|
break;
|
||||||
|
case INTERVAL_BATTERY:
|
||||||
|
requests |= REQ_BATTERY;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
tickSync[i] = millis();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(job == 0)
|
||||||
|
{
|
||||||
|
// no assigned job, assign it
|
||||||
|
if(requests & REQ_CLOCK)
|
||||||
|
job = REQ_CLOCK;
|
||||||
|
else if(requests & REQ_SIG)
|
||||||
|
job = REQ_SIG;
|
||||||
|
else if(requests & REQ_INBOX)
|
||||||
|
job = REQ_INBOX;
|
||||||
|
else if(requests & REQ_OUTBOX)
|
||||||
|
job = REQ_OUTBOX;
|
||||||
|
else if(requests & REQ_STARTUP)
|
||||||
|
job = REQ_STARTUP;
|
||||||
|
else if(requests & REQ_BATTERY)
|
||||||
|
job = REQ_BATTERY;
|
||||||
|
|
||||||
|
if(job)
|
||||||
|
{
|
||||||
|
state = 0;
|
||||||
|
DEBUG_PRINT(F("Job ID: "));
|
||||||
|
DEBUG_PRINTLN(job);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// execute current job
|
||||||
|
if(job == REQ_STARTUP)
|
||||||
|
Startup();
|
||||||
|
else if(job == REQ_CLOCK)
|
||||||
|
Clock();
|
||||||
|
else if(job == REQ_SIG)
|
||||||
|
Signal();
|
||||||
|
else if(job == REQ_INBOX)
|
||||||
|
Inbox();
|
||||||
|
else if(job == REQ_OUTBOX)
|
||||||
|
Outbox();
|
||||||
|
else if(job == REQ_BATTERY)
|
||||||
|
Battery();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Requests
|
||||||
|
void sendSMS(String& Number, String& Text)
|
||||||
|
{
|
||||||
|
requests |= (REQ_OUTBOX);
|
||||||
|
SMSo.Number = Number;
|
||||||
|
SMSo.Text = Text;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
private:
|
||||||
|
|
||||||
|
// States
|
||||||
|
void Startup()
|
||||||
|
{
|
||||||
|
int lastState = state;
|
||||||
|
switch(state)
|
||||||
|
{
|
||||||
|
case STARTUP_POWER_OFF:
|
||||||
|
if(this->configuration.power != NULL)
|
||||||
|
this->configuration.power(*this, false);
|
||||||
|
tick = millis();
|
||||||
|
state = STARTUP_POWER_OFF_DELAY;
|
||||||
|
break;
|
||||||
|
case STARTUP_POWER_OFF_DELAY:
|
||||||
|
if(millis() - tick >= THREADEDGSM_STARTUP_POWER_OFF_DELAY)
|
||||||
|
state = STARTUP_POWER_ON;
|
||||||
|
break;
|
||||||
|
case STARTUP_POWER_ON:
|
||||||
|
if(this->configuration.power != NULL)
|
||||||
|
this->configuration.power(*this, true);
|
||||||
|
// begin delay
|
||||||
|
tick = millis();
|
||||||
|
state = STARTUP_DELAY;
|
||||||
|
break;
|
||||||
|
case STARTUP_DELAY:
|
||||||
|
if(millis() - tick >= THREADEDGSM_STARTUP_DELAY)
|
||||||
|
{
|
||||||
|
dte.SendCommand("AT\r", THREADEDGSM_AT_TIMEOUT, "OK\r");
|
||||||
|
state = STARTUP_ENTER_AT;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case STARTUP_ENTER_AT:
|
||||||
|
if(dte.getResult() == DTE::EXPECT_RESULT)
|
||||||
|
{
|
||||||
|
dte.SendCommand("AT+CPIN?\r", 10000, "OK\r");
|
||||||
|
state = STARTUP_CHK_CPIN;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
state = STARTUP_POWER_OFF;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case STARTUP_CHK_CPIN:
|
||||||
|
if(dte.getResult() == DTE::EXPECT_RESULT)
|
||||||
|
{
|
||||||
|
if(dte.getBuffer().indexOf("+CPIN: READY") != -1)
|
||||||
|
{
|
||||||
|
dte.SendCommand("AT+CREG?\r", THREADEDGSM_AT_TIMEOUT, "OK\r");
|
||||||
|
state = STARTUP_CHK_CREG;
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
state = STARTUP_POWER_OFF;
|
||||||
|
}
|
||||||
|
}else
|
||||||
|
state = STARTUP_POWER_OFF;
|
||||||
|
break;
|
||||||
|
case STARTUP_CHK_CREG:
|
||||||
|
if(dte.getResult() == DTE::EXPECT_RESULT)
|
||||||
|
{
|
||||||
|
if((dte.getBuffer().indexOf(",1") >= 0) || (dte.getBuffer().indexOf(",5") >= 0))
|
||||||
|
{
|
||||||
|
dte.SendCommand("AT+CLTS=1\r", THREADEDGSM_AT_TIMEOUT, "OK\r");
|
||||||
|
state = STARTUP_CHK_CLTS;
|
||||||
|
}else
|
||||||
|
state = STARTUP_POWER_OFF;
|
||||||
|
}else
|
||||||
|
state = STARTUP_POWER_OFF;
|
||||||
|
break;
|
||||||
|
case STARTUP_CHK_CLTS:
|
||||||
|
if(dte.getResult() == DTE::EXPECT_RESULT)
|
||||||
|
{
|
||||||
|
dte.SendCommand("AT+CENG=3\r", THREADEDGSM_AT_TIMEOUT, "OK\r");
|
||||||
|
state = STARTUP_CHK_CENG;
|
||||||
|
}else
|
||||||
|
state = STARTUP_POWER_OFF;
|
||||||
|
break;
|
||||||
|
case STARTUP_CHK_CENG:
|
||||||
|
if(dte.getResult() == DTE::EXPECT_RESULT)
|
||||||
|
{
|
||||||
|
requests |= ((REQ_CLOCK)|(REQ_SIG)|(REQ_BATTERY));
|
||||||
|
clearReq(REQ_STARTUP);
|
||||||
|
for(int i=0;i<THREADEDGSM_INTERVAL_COUNT;i++)
|
||||||
|
tickSync[i] = millis();
|
||||||
|
if(this->configuration.ready != NULL)
|
||||||
|
this->configuration.ready(*this);
|
||||||
|
}else
|
||||||
|
state = STARTUP_POWER_OFF;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(state != lastState)
|
||||||
|
{
|
||||||
|
DEBUG_PRINT(F("STARTUP_STATE: "));
|
||||||
|
DEBUG_PRINTLN(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Threads
|
||||||
|
void Clock()
|
||||||
|
{
|
||||||
|
String clockTime;
|
||||||
|
int lastState = state;
|
||||||
|
switch(state)
|
||||||
|
{
|
||||||
|
case CLOCK_REQ:
|
||||||
|
dte.SendCommand("AT+CCLK?\r", THREADEDGSM_AT_TIMEOUT, "OK\r");
|
||||||
|
state = CLOCK_VERIFY;
|
||||||
|
break;
|
||||||
|
case CLOCK_VERIFY:
|
||||||
|
int index = dte.getBuffer().indexOf("+CCLK: ");
|
||||||
|
if(index >= 0)
|
||||||
|
{
|
||||||
|
// parse clock
|
||||||
|
index+=8;
|
||||||
|
int endindex;
|
||||||
|
endindex = dte.getBuffer().indexOf("+", index);
|
||||||
|
if(endindex >= 0)
|
||||||
|
clockTime = dte.getBuffer().substring(index, endindex);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
endindex = dte.getBuffer().indexOf("-", index);
|
||||||
|
if(endindex >= 0)
|
||||||
|
clockTime = dte.getBuffer().substring(index, endindex);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(endindex >= 0)
|
||||||
|
{
|
||||||
|
NetworkTime ClockTime;
|
||||||
|
ClockTime.year = 2000+clockTime.substring(0,2).toInt();
|
||||||
|
ClockTime.month = clockTime.substring(3,5).toInt();
|
||||||
|
ClockTime.day = clockTime.substring(6,8).toInt();
|
||||||
|
ClockTime.hour = clockTime.substring(9,11).toInt();
|
||||||
|
ClockTime.minute = clockTime.substring(12,14).toInt();
|
||||||
|
ClockTime.second = clockTime.substring(15,17).toInt();
|
||||||
|
if(this->configuration.clock != NULL)
|
||||||
|
this->configuration.clock(*this, ClockTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
clearReq(REQ_CLOCK);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(state != lastState)
|
||||||
|
{
|
||||||
|
DEBUG_PRINT(F("CLOCK_STATE: "));
|
||||||
|
DEBUG_PRINTLN(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void Signal()
|
||||||
|
{
|
||||||
|
int lastState = state;
|
||||||
|
switch(state)
|
||||||
|
{
|
||||||
|
case SIGNAL_REQ:
|
||||||
|
dte.SendCommand("AT+CSQ\r", THREADEDGSM_AT_TIMEOUT, "OK\r");
|
||||||
|
state = SIGNAL_VERIFY;
|
||||||
|
break;
|
||||||
|
case SIGNAL_VERIFY:
|
||||||
|
int index = dte.getBuffer().indexOf("+CSQ: ");
|
||||||
|
if(index >= 0)
|
||||||
|
{
|
||||||
|
// parse signal
|
||||||
|
index+=6;
|
||||||
|
SignalLevel GsmSignal;
|
||||||
|
GsmSignal.Value = dte.getBuffer().substring(index,index+2).toInt();
|
||||||
|
GsmSignal.Dbm = dte.getBuffer().substring(index+3,index+5).toInt();
|
||||||
|
if(GsmSignal.Value != 0)
|
||||||
|
{
|
||||||
|
if(this->configuration.signal != NULL)
|
||||||
|
this->configuration.signal(*this, GsmSignal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
clearReq(REQ_SIG);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(state != lastState)
|
||||||
|
{
|
||||||
|
DEBUG_PRINT(F("SIGNAL_STATE: "));
|
||||||
|
DEBUG_PRINTLN(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Battery()
|
||||||
|
{
|
||||||
|
int lastState = state;
|
||||||
|
switch(state)
|
||||||
|
{
|
||||||
|
case BATTERY_REQ:
|
||||||
|
dte.SendCommand("AT+CBC\r", THREADEDGSM_AT_TIMEOUT, "OK\r");
|
||||||
|
state = BATTERY_VERIFY;
|
||||||
|
break;
|
||||||
|
case BATTERY_VERIFY:
|
||||||
|
int index = dte.getBuffer().indexOf("+CBC:");
|
||||||
|
if(index >= 0)
|
||||||
|
{
|
||||||
|
BatteryInfo BattInfo;
|
||||||
|
// parse battery level
|
||||||
|
String buffer = dte.getBuffer().substring(index);
|
||||||
|
String buffer2 = buffer.substring(buffer.indexOf(",") + 1);
|
||||||
|
buffer = buffer2;
|
||||||
|
BattInfo.Percent = buffer2.substring(0, buffer2.indexOf(",")).toInt(); // converts the result to interger
|
||||||
|
buffer2 = buffer.substring(buffer.indexOf(",") + 1);
|
||||||
|
BattInfo.Voltage = buffer2.substring(0, buffer2.indexOf("\r")).toInt(); // converts the result to interger
|
||||||
|
if(this->configuration.battery != NULL)
|
||||||
|
this->configuration.battery(*this, BattInfo);
|
||||||
|
}
|
||||||
|
clearReq(REQ_BATTERY);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(state != lastState)
|
||||||
|
{
|
||||||
|
DEBUG_PRINT(F("BATTERY_STATE: "));
|
||||||
|
DEBUG_PRINTLN(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void clearReq(int req)
|
||||||
|
{
|
||||||
|
requests &= ~(req);
|
||||||
|
nextJob();
|
||||||
|
}
|
||||||
|
void Inbox()
|
||||||
|
{
|
||||||
|
String CMD;
|
||||||
|
int lastState = state;
|
||||||
|
switch(state)
|
||||||
|
{
|
||||||
|
case READ_REQ:
|
||||||
|
SMSi.Text = "";
|
||||||
|
SMSi.Number = "";
|
||||||
|
dte.SendCommand("AT+CMGF=0\r", THREADEDGSM_AT_TIMEOUT, "OK\r"); // SMS Message format set as PDU
|
||||||
|
state = READ_CHK_CMGF;
|
||||||
|
break;
|
||||||
|
case READ_CHK_CMGF:
|
||||||
|
if(dte.getResult() == DTE::EXPECT_RESULT)
|
||||||
|
{
|
||||||
|
dte.SendCommand("AT+CPMS=\"SM\"\r", THREADEDGSM_AT_TIMEOUT, "OK\r"); // SIM Message storage
|
||||||
|
state = READ_CHK_CPMS;
|
||||||
|
}
|
||||||
|
else clearReq(REQ_INBOX);
|
||||||
|
break;
|
||||||
|
case READ_CHK_CPMS:
|
||||||
|
if(dte.getResult() == DTE::EXPECT_RESULT)
|
||||||
|
{
|
||||||
|
CMD = F("AT+CMGL="); // Read all SMS messages
|
||||||
|
CMD += (int) READ_TYPE_ALL;
|
||||||
|
CMD += "\r";
|
||||||
|
dte.SendCommand(CMD.c_str(), THREADEDGSM_AT_TIMEOUT, ",");
|
||||||
|
state = READ_CHK_CMGL;
|
||||||
|
}
|
||||||
|
else clearReq(REQ_INBOX);
|
||||||
|
break;
|
||||||
|
case READ_CHK_CMGL:
|
||||||
|
if(dte.getResult() == DTE::EXPECT_RESULT)
|
||||||
|
{
|
||||||
|
//fetch index
|
||||||
|
int indexStart = dte.getBuffer().indexOf("+CMGL: ");
|
||||||
|
if (indexStart >= 0)
|
||||||
|
{
|
||||||
|
Message.Index = dte.getBuffer().substring(indexStart + 7, dte.getBuffer().indexOf(",")).toInt();
|
||||||
|
if(Message.Index != 0)
|
||||||
|
{
|
||||||
|
dte.Delay(2000);
|
||||||
|
state = READ_DELAY_CLEAR_BUFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(state != READ_DELAY_CLEAR_BUFF)
|
||||||
|
clearReq(REQ_INBOX);
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case READ_DELAY_CLEAR_BUFF:
|
||||||
|
dte.SendCommand("AT+CMGF=1\r", THREADEDGSM_AT_TIMEOUT, "OK\r"); // SMS Message format set as TEXT
|
||||||
|
state = READ_TEXT_CMGR;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case READ_TEXT_CMGR:
|
||||||
|
if (dte.getResult() == DTE::EXPECT_RESULT)
|
||||||
|
{
|
||||||
|
CMD = F("AT+CMGR="); // Read the SMS message
|
||||||
|
CMD += Message.Index;
|
||||||
|
CMD += "\r";
|
||||||
|
dte.SendCommand(CMD.c_str(), THREADEDGSM_AT_TIMEOUT, "OK\r");
|
||||||
|
state = READ_CHK_CMGR;
|
||||||
|
}else
|
||||||
|
clearReq(REQ_INBOX);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case READ_CHK_CMGR:
|
||||||
|
if(dte.getResult() == DTE::EXPECT_RESULT)
|
||||||
|
{
|
||||||
|
int indexStart = dte.getBuffer().indexOf("+CMGR: ");
|
||||||
|
if(indexStart >= 0)
|
||||||
|
{
|
||||||
|
int indexStartPDU = dte.getBuffer().indexOf("\r\n", indexStart);
|
||||||
|
if (indexStartPDU >= 0)
|
||||||
|
{
|
||||||
|
indexStartPDU+=2;
|
||||||
|
int indexEndPDU = dte.getBuffer().indexOf("\r", indexStartPDU);
|
||||||
|
if(indexEndPDU >= 0)
|
||||||
|
SMSi.Text = dte.getBuffer().substring(indexStartPDU, indexEndPDU);
|
||||||
|
}
|
||||||
|
indexStartPDU = dte.getBuffer().indexOf(",\"", indexStart);
|
||||||
|
if (indexStartPDU >= 0)
|
||||||
|
{
|
||||||
|
indexStartPDU += 2;
|
||||||
|
int indexEndPDU = dte.getBuffer().indexOf("\",", indexStartPDU);
|
||||||
|
if (indexEndPDU >= 0)
|
||||||
|
SMSi.Number = dte.getBuffer().substring(indexStartPDU, indexEndPDU);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CMD = F("AT+CMGD="); // Delete the SMS message
|
||||||
|
CMD += Message.Index;
|
||||||
|
CMD += "\r";
|
||||||
|
dte.SendCommand(CMD.c_str(), THREADEDGSM_AT_TIMEOUT, "OK\r");
|
||||||
|
state = READ_CHK_CMGD;
|
||||||
|
}else
|
||||||
|
clearReq(REQ_INBOX);
|
||||||
|
break;
|
||||||
|
case READ_CHK_CMGD:
|
||||||
|
//if( (dte.getResult() == DTE::EXPECT_RESULT) && (SMS.InboxMsgContents != ""))
|
||||||
|
if(dte.getResult() == DTE::EXPECT_RESULT)
|
||||||
|
{
|
||||||
|
if(this->configuration.incoming != NULL)
|
||||||
|
this->configuration.incoming(*this, SMSi);
|
||||||
|
}
|
||||||
|
clearReq(REQ_INBOX);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(state != lastState)
|
||||||
|
{
|
||||||
|
DEBUG_PRINT(F("INBOX_STATE: "));
|
||||||
|
DEBUG_PRINTLN(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Outbox()
|
||||||
|
{
|
||||||
|
String CMD;
|
||||||
|
int lastState = state;
|
||||||
|
switch(state)
|
||||||
|
{
|
||||||
|
case SEND_REQ:
|
||||||
|
dte.SendCommand("AT+CMGF=1\r", THREADEDGSM_AT_TIMEOUT, "OK\r"); // SMS Text mode
|
||||||
|
state = SEND_CHK_CMGF;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SEND_CHK_CMGF:
|
||||||
|
if(dte.getResult() == DTE::EXPECT_RESULT)
|
||||||
|
{
|
||||||
|
CMD = F("AT+CMGS=\"");
|
||||||
|
CMD += SMSo.Number;
|
||||||
|
CMD += "\"\r";
|
||||||
|
dte.SendCommand(CMD.c_str(), 15000, "> ");
|
||||||
|
state = SEND_CHK_RDY;
|
||||||
|
}
|
||||||
|
else clearReq(REQ_OUTBOX);
|
||||||
|
break;
|
||||||
|
case SEND_CHK_RDY:
|
||||||
|
if(dte.getResult() == DTE::EXPECT_RESULT)
|
||||||
|
{
|
||||||
|
CMD = SMSo.Text;
|
||||||
|
CMD += (char)26;
|
||||||
|
dte.SendCommand(CMD.c_str(), 10000, "OK\r");
|
||||||
|
state = SEND_CHK_OK;
|
||||||
|
}else clearReq(REQ_OUTBOX);
|
||||||
|
break;
|
||||||
|
case SEND_CHK_OK:
|
||||||
|
if(dte.getResult() == DTE::EXPECT_RESULT)
|
||||||
|
{
|
||||||
|
if(this->configuration.outgoing != NULL)
|
||||||
|
this->configuration.outgoing(*this);
|
||||||
|
}
|
||||||
|
clearReq(REQ_OUTBOX);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(state != lastState)
|
||||||
|
{
|
||||||
|
DEBUG_PRINT(F("OUTBOX_STATE: "));
|
||||||
|
DEBUG_PRINTLN(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}; //ThreadedGSM
|
||||||
|
|
||||||
|
#endif //__THREADEDGSM_H__
|
71
examples/example1.cpp
Normal file
71
examples/example1.cpp
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
/**
|
||||||
|
* ThreadedGSM Example: Read SMS, Network Time and Signal level
|
||||||
|
* Reads SMS messages in PDU mode and prints them to debug serial
|
||||||
|
*/
|
||||||
|
#include <Arduino.h>
|
||||||
|
#define SerialDebug Serial1
|
||||||
|
#define SerialModem Serial
|
||||||
|
#define THREADEDGSM_DEBUG SerialDebug
|
||||||
|
#include <ThreadedGSM.h>
|
||||||
|
|
||||||
|
ThreadedGSM SIM900(SerialModem);
|
||||||
|
|
||||||
|
void clock(ThreadedGSM& modem, ThreadedGSM::NetworkTime& Time)
|
||||||
|
{
|
||||||
|
SerialDebug.print("Modem Time: ");
|
||||||
|
SerialDebug.print(Time.day);
|
||||||
|
SerialDebug.print("/");
|
||||||
|
SerialDebug.print(Time.month);
|
||||||
|
SerialDebug.print("/");
|
||||||
|
SerialDebug.print(Time.year);
|
||||||
|
SerialDebug.print(" ");
|
||||||
|
SerialDebug.print(Time.hour);
|
||||||
|
SerialDebug.print(":");
|
||||||
|
SerialDebug.print(Time.minute);
|
||||||
|
SerialDebug.print(":");
|
||||||
|
SerialDebug.println(Time.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
void signal(ThreadedGSM& modem, ThreadedGSM::SignalLevel& Signal)
|
||||||
|
{
|
||||||
|
SerialDebug.print("Modem signal: Dbm:");
|
||||||
|
SerialDebug.print(Signal.Dbm);
|
||||||
|
SerialDebug.print(" value: ");
|
||||||
|
SerialDebug.println(Signal.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sms(ThreadedGSM& modem, String& Msg)
|
||||||
|
{
|
||||||
|
SerialDebug.print("Received SMS: ");
|
||||||
|
SerialDebug.println(Msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void startup(ThreadedGSM& modem)
|
||||||
|
{
|
||||||
|
SerialDebug.print("Ready");
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup()
|
||||||
|
{
|
||||||
|
SerialDebug.begin(115200);
|
||||||
|
SerialModem.begin(115200);
|
||||||
|
SerialDebug.println("STARTUP\r\n");
|
||||||
|
|
||||||
|
SIM900.begin();
|
||||||
|
SIM900.setInterval(ThreadedGSM::INTERVAL_CLOCK, 60000);
|
||||||
|
SIM900.setInterval(ThreadedGSM::INTERVAL_SIGNAL, 60000);
|
||||||
|
SIM900.setInterval(ThreadedGSM::INTERVAL_INBOX, 30000);
|
||||||
|
SIM900.setHandlers({
|
||||||
|
.signal = signal,
|
||||||
|
.clock = clock,
|
||||||
|
.incoming = sms,
|
||||||
|
.ready = startup,
|
||||||
|
.outgoing = NULL,
|
||||||
|
.power = NULL
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop()
|
||||||
|
{
|
||||||
|
SIM900.loop();
|
||||||
|
}
|
29
library.json
Normal file
29
library.json
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"name": "ThreadedGSM",
|
||||||
|
"keywords": "gsm, sim800, sim900, sms, sim800l",
|
||||||
|
"description": "Non-blocking Arduino GSM Library with SMS support",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"repository":
|
||||||
|
{
|
||||||
|
"type": "git",
|
||||||
|
"url": "http://git.xpablo.cz/xPablo.cz/ThreadedGSM.git"
|
||||||
|
},
|
||||||
|
"authors":
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "Neta Yahav",
|
||||||
|
"email": "neta540@gmail.com",
|
||||||
|
"maintainer": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Pavel Brychta",
|
||||||
|
"email": "pablo@xpablo.cz",
|
||||||
|
"maintainer": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"frameworks": "arduino",
|
||||||
|
"platforms": "*",
|
||||||
|
"examples": [
|
||||||
|
"[Ee]xamples/*.cpp"
|
||||||
|
]
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user