From a5ad4c4e1ddcf798d8d1e5a3ed6fa9a4df40a9d2 Mon Sep 17 00:00:00 2001 From: Pavel Brychta Date: Sat, 1 Jun 2019 11:09:49 +0200 Subject: [PATCH] Added battery status, SMS are sent/received in Text format instead of PDU --- DTE.h | 129 ++++++++ README_original.md | 79 +++++ ThreadedGSM.h | 690 ++++++++++++++++++++++++++++++++++++++++++ examples/example1.cpp | 71 +++++ library.json | 29 ++ 5 files changed, 998 insertions(+) create mode 100644 DTE.h create mode 100644 README_original.md create mode 100644 ThreadedGSM.h create mode 100644 examples/example1.cpp create mode 100644 library.json diff --git a/DTE.h b/DTE.h new file mode 100644 index 0000000..e5fd453 --- /dev/null +++ b/DTE.h @@ -0,0 +1,129 @@ +/* +* DTE.h +* +* Created: 20/09/2016 15:40:51 +* Author: Neta Yahav +*/ + +#ifndef __DTE_H__ +#define __DTE_H__ + +#include + +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__ diff --git a/README_original.md b/README_original.md new file mode 100644 index 0000000..42a500a --- /dev/null +++ b/README_original.md @@ -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). diff --git a/ThreadedGSM.h b/ThreadedGSM.h new file mode 100644 index 0000000..68516f6 --- /dev/null +++ b/ThreadedGSM.h @@ -0,0 +1,690 @@ +/* +* ThreadedGSM.h +* +* Created: 20/09/2016 11:14:02 +* Author: Neta Yahav +*/ + + +#ifndef __THREADEDGSM_H__ +#define __THREADEDGSM_H__ + +#include +#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;iconfiguration = 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= 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;iconfiguration.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__ diff --git a/examples/example1.cpp b/examples/example1.cpp new file mode 100644 index 0000000..c6c6c3d --- /dev/null +++ b/examples/example1.cpp @@ -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 +#define SerialDebug Serial1 +#define SerialModem Serial +#define THREADEDGSM_DEBUG SerialDebug +#include + +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(); +} diff --git a/library.json b/library.json new file mode 100644 index 0000000..d8a2cfb --- /dev/null +++ b/library.json @@ -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" + ] +}