diff --git a/OpenMQTTGateway.ino b/OpenMQTTGateway.ino
index bfefb01..c970e53 100644
--- a/OpenMQTTGateway.ino
+++ b/OpenMQTTGateway.ino
@@ -73,6 +73,9 @@
#ifdef ZgatewayIR
#include "config_IR.h"
#endif
+#ifdef Zgateway2G
+ #include "config_2G.h"
+#endif
#ifdef ZactuatorONOFF
#include "config_ONOFF.h"
#endif
@@ -329,6 +332,9 @@ void setup()
#ifdef ZactuatorONOFF
setupONOFF();
#endif
+ #ifdef Zgateway2G
+ setup2G();
+ #endif
#ifdef ZgatewayIR
setupIR();
#endif
@@ -625,6 +631,11 @@ void loop()
trc(F("BTtoMQTT OK"));
#endif
#endif
+ #ifdef Zgateway2G
+ if(_2GtoMQTT()){
+ trc(F("2GtoMQTT OK"));
+ }
+ #endif
#ifdef ZgatewayRFM69
if(RFM69toMQTT())
trc(F("RFM69toMQTT OK"));
@@ -705,6 +716,9 @@ digitalWrite(led_send, LOW);
#ifdef ZgatewayRF2
MQTTtoRF2(topicOri, datacallback);
#endif
+#ifdef Zgateway2G
+ MQTTto2G(topicOri, datacallback);
+#endif
#ifdef ZgatewaySRFB
MQTTtoSRFB(topicOri, datacallback);
#endif
@@ -761,3 +775,22 @@ void trc(String msg){
Serial.println(msg);
}
}
+
+
+void trc(int msg){
+ if (TRACE) {
+ Serial.println(msg);
+ }
+}
+
+void trc(uint32_t msg){
+ if (TRACE) {
+ Serial.println(msg);
+ }
+}
+
+void trc(float msg){
+ if (TRACE) {
+ Serial.println(msg);
+ }
+}
diff --git a/Zgateway2G.ino b/Zgateway2G.ino
new file mode 100644
index 0000000..ff4de9f
--- /dev/null
+++ b/Zgateway2G.ino
@@ -0,0 +1,133 @@
+/*
+ OpenMQTTGateway - ESP8266 or Arduino program for home automation
+
+ Act as a wifi or ethernet gateway between your 433mhz/infrared IR signal and a MQTT broker
+ Send and receiving command by MQTT
+
+ This gateway enables to:
+ - receive MQTT data from a topic and send SMS corresponding to the received MQTT data
+ - publish MQTT data to a different topic related to received SMS
+
+ Copyright: (c)Florian ROBERT
+
+ This file is part of OpenMQTTGateway.
+
+ OpenMQTTGateway is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ OpenMQTTGateway is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+#ifdef Zgateway2G
+
+#include // library for controling A6 or A7 module
+
+// Instantiate the library with TxPin, RxPin.
+A6lib A6l(_2G_TX_PIN, _2G_RX_PIN); //D6 to A6 RX, D7 to A6 TX
+
+int unreadSMSLocs[50] = {0};
+int unreadSMSNum = 0;
+SMSmessage sms;
+
+void setup2G(){
+
+ trc(F("_2G_TX_PIN "));
+ trc(String(_2G_TX_PIN));
+ trc(F("_2G_RX_PIN "));
+ trc(String(_2G_RX_PIN));
+ setupGSM(false);
+ trc(F("Zgateway2G setup done "));
+}
+
+void setupGSM(boolean deleteSMS){
+ trc("Init 2G module: ");
+ trc(_2G_PWR_PIN);
+ delay(1000);
+ // Power-cycle the module to reset it.
+ A6l.powerCycle(_2G_PWR_PIN);
+ trc("waiting for network connection");
+ A6l.blockUntilReady(_2G_MODULE_BAUDRATE);
+ trc("A6/A7 gsm ready");
+ signalStrengthAnalysis();
+ delay(1000);
+ // deleting all sms
+ if (deleteSMS){
+ if (A6l.deleteSMS(1,4) == A6_OK ) {
+ trc("delete SMS OK");
+ }else{
+ trc("delete SMS KO");
+ }
+ }
+}
+
+void signalStrengthAnalysis(){
+ int signalStrength = 0;
+ signalStrength = A6l.getSignalStrength();
+ trc("Signal strength: ");
+ trc(signalStrength);
+ if (signalStrength < _2G_MIN_SIGNAL || signalStrength > _2G_MAX_SIGNAL ) {
+ trc("Signal too low restart the module");
+ setupGSM(false); // if we are below or above a threshold signal we relaunch the setup of GSM module
+ }
+}
+
+boolean _2GtoMQTT(){
+ // Get the memory locations of unread SMS messages.
+ unreadSMSNum = A6l.getUnreadSMSLocs(unreadSMSLocs, 512);
+ for (int i = 0; i < unreadSMSNum; i++) {
+ trc("New message at index: ");
+ trc(unreadSMSNum);
+ sms = A6l.readSMS(unreadSMSLocs[i]);
+ trc(sms.number);
+ trc(sms.date);
+ trc(sms.message);
+ A6l.deleteSMS(unreadSMSLocs[i]); // we delete the SMS received
+ trc(F("data 2GtoMQTT"));
+ client.publish(subject2GtoMQTTphone,(char *)sms.number.c_str());
+ client.publish(subject2GtoMQTTdate,(char *)sms.date.c_str());
+ client.publish(subject2GtoMQTTmessage,(char *)sms.message.c_str());
+ return true;
+ }
+ return false;
+}
+
+void MQTTto2G(char * topicOri, char * datacallback) {
+
+ String data = datacallback; // we will not be able to pass values > 4294967295
+
+ // 2G DATA ANALYSIS
+ //We look into the subject to see if a special RF protocol is defined
+ String topic = topicOri;
+ String phone_number = "";
+ int pos0 = topic.lastIndexOf(_2GPhoneKey);
+ if (pos0 != -1) {
+ pos0 = pos0 + strlen(_2GPhoneKey);
+ phone_number = topic.substring(pos0);
+ trc(F("Phone number to send SMS:"));
+ trc(phone_number);
+ }
+ if((strstr(topicOri, subjectMQTTto2G) != NULL) && (pos0 != -1)){
+ trc(F("MQTTto2G"));
+ trc(data);
+ if (A6l.sendSMS(phone_number,data) == A6_OK ) {
+ trc("SMS OK");
+ }else{
+ trc("SMS KO");
+ }
+ // Acknowledgement to the GTW2G topic
+ boolean result = client.publish(subjectGTW2GtoMQTT, datacallback);// we acknowledge the sending by publishing the value to an acknowledgement topic, for the moment even if it is a signal repetition we acknowledge also
+ if (result){
+ trc(F("MQTTto2G ack pub."));
+ trc(String(data));
+ }
+ }
+
+}
+#endif
diff --git a/config_2G.h b/config_2G.h
new file mode 100644
index 0000000..f62aad6
--- /dev/null
+++ b/config_2G.h
@@ -0,0 +1,46 @@
+/*
+ OpenMQTTGateway - ESP8266 or Arduino program for home automation
+
+ Act as a wifi or ethernet gateway between your 433mhz/infrared IR signal and a MQTT broker
+ Send and receiving command by MQTT
+
+ This gateway enables to set your parameters for the 2G gateway
+
+ Copyright: (c)Florian ROBERT
+
+ This file is part of OpenMQTTGateway.
+
+ OpenMQTTGateway is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ OpenMQTTGateway is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+/*-------------------2G topics & parameters----------------------*/
+//433Mhz MQTT Subjects and keys
+#define subjectMQTTto2G Base_Topic Gateway_Name "/commands/MQTTto2G"
+#define subject2GtoMQTT Base_Topic Gateway_Name "/2GtoMQTT"
+#define subjectGTW2GtoMQTT Base_Topic Gateway_Name "/2GtoMQTT"
+#define subject2GtoMQTTphone Base_Topic Gateway_Name "/2GtoMQTT/phone"
+#define subject2GtoMQTTdate Base_Topic Gateway_Name "/2GtoMQTT/date"
+#define subject2GtoMQTTmessage Base_Topic Gateway_Name "/2GtoMQTT/message"
+#define _2GPhoneKey "PHO_" // phone number define the phone number to send the SMS MQTT->2G
+
+#define _2G_MODULE_BAUDRATE 9600
+#define _2G_MIN_SIGNAL 30
+#define _2G_MAX_SIGNAL 1000
+
+/*-------------------PIN DEFINITIONS----------------------*/
+#define _2G_TX_PIN D6 //D6 to A6 RX,
+#define _2G_RX_PIN D7 //D7 to A6 TX
+#define _2G_PWR_PIN D5
+#define _2G_RST_PIN D0
+
diff --git a/img/OpenMQTTgateway_ESP8266_Addon_2G.fzz b/img/OpenMQTTgateway_ESP8266_Addon_2G.fzz
new file mode 100644
index 0000000..fbfd5fb
Binary files /dev/null and b/img/OpenMQTTgateway_ESP8266_Addon_2G.fzz differ
diff --git a/img/OpenMQTTgateway_ESP8266_Addon_2G.png b/img/OpenMQTTgateway_ESP8266_Addon_2G.png
new file mode 100644
index 0000000..f324209
Binary files /dev/null and b/img/OpenMQTTgateway_ESP8266_Addon_2G.png differ
diff --git a/lib/A6lib/.editorconfig b/lib/A6lib/.editorconfig
new file mode 100644
index 0000000..384f9b2
--- /dev/null
+++ b/lib/A6lib/.editorconfig
@@ -0,0 +1,21 @@
+# EditorConfig file: http://EditorConfig.org
+
+# top-most EditorConfig file
+root = true
+
+# Unix-style newlines with a newline ending every file
+[*]
+end_of_line = lf
+insert_final_newline = false
+
+# 4 space indentation
+[*.{cpp,h}]
+indent_style = space
+indent_size = 4
+trim_trailing_whitespace = true
+
+# 2 space indentation
+[*.json]
+indent_style = space
+indent_size = 2
+trim_trailing_whitespace = true
diff --git a/lib/A6lib/A6lib.cpp b/lib/A6lib/A6lib.cpp
new file mode 100644
index 0000000..a1ecfa5
--- /dev/null
+++ b/lib/A6lib/A6lib.cpp
@@ -0,0 +1,504 @@
+#include
+#include
+#include "A6lib.h"
+
+
+/////////////////////////////////////////////
+// Public methods.
+//
+
+A6lib::A6lib(int transmitPin, int receivePin) {
+#ifdef ESP8266
+ A6conn = new SoftwareSerial(receivePin, transmitPin, false, 1024);
+#else
+ A6conn = new SoftwareSerial(receivePin, transmitPin, false);
+#endif
+A6conn->setTimeout(100);
+}
+
+
+A6lib::~A6lib() {
+ delete A6conn;
+}
+
+
+// Block until the module is ready.
+byte A6lib::blockUntilReady(long baudRate) {
+
+ byte response = A6_NOTOK;
+ while (A6_OK != response) {
+ response = begin(baudRate);
+ // This means the modem has failed to initialize and we need to reboot
+ // it.
+ if (A6_FAILURE == response) {
+ return A6_FAILURE;
+ }
+ delay(1000);
+ logln("Waiting for module to be ready...");
+ }
+ return A6_OK;
+}
+
+
+// Initialize the software serial connection and change the baud rate from the
+// default (autodetected) to the desired speed.
+byte A6lib::begin(long baudRate) {
+
+ A6conn->flush();
+
+ if (A6_OK != setRate(baudRate)) {
+ return A6_NOTOK;
+ }
+
+ // Factory reset.
+ A6command("AT&F", "OK", "yy", A6_CMD_TIMEOUT, 2, NULL);
+
+ // Echo off.
+ A6command("ATE0", "OK", "yy", A6_CMD_TIMEOUT, 2, NULL);
+
+ // Switch audio to headset.
+ enableSpeaker(0);
+
+ // Set caller ID on.
+ A6command("AT+CLIP=1", "OK", "yy", A6_CMD_TIMEOUT, 2, NULL);
+
+ // Set SMS to text mode.
+ A6command("AT+CMGF=1", "OK", "yy", A6_CMD_TIMEOUT, 2, NULL);
+
+ // Turn SMS indicators off.
+ A6command("AT+CNMI=1,0", "OK", "yy", A6_CMD_TIMEOUT, 2, NULL);
+
+ // Set SMS storage to the GSM modem.
+ if (A6_OK != A6command("AT+CPMS=SM,ME,SM", "OK", "yy", A6_CMD_TIMEOUT, 2, NULL))
+ // This may sometimes fail, in which case the modem needs to be
+ // rebooted.
+ {
+ return A6_FAILURE;
+ }
+
+ // Set SMS character set.
+ setSMScharset("UCS2");
+
+ return A6_OK;
+}
+
+
+// Reboot the module by setting the specified pin HIGH, then LOW. The pin should
+// be connected to a P-MOSFET, not the A6's POWER pin.
+void A6lib::powerCycle(int pin) {
+ logln("Power-cycling module...");
+
+ powerOff(pin);
+ delay(2000);
+
+ powerOn(pin);
+ delay(4000);
+
+ powerOff(pin);
+ // Give the module some time to settle.
+ logln("Done, waiting for the module to initialize...");
+ delay(20000);
+ logln("Done.");
+
+ A6conn->flush();
+}
+
+
+// Turn the modem power completely off.
+void A6lib::powerOff(int pin) {
+ pinMode(pin, OUTPUT);
+ digitalWrite(pin, LOW);
+}
+
+
+// Turn the modem power on.
+void A6lib::powerOn(int pin) {
+ pinMode(pin, OUTPUT);
+ digitalWrite(pin, HIGH);
+}
+
+
+// Dial a number.
+void A6lib::dial(String number) {
+ char buffer[50];
+
+ logln("Dialing number...");
+
+ sprintf(buffer, "ATD%s;", number.c_str());
+ A6command(buffer, "OK", "yy", A6_CMD_TIMEOUT, 2, NULL);
+}
+
+
+// Redial the last number.
+void A6lib::redial() {
+ logln("Redialing last number...");
+ A6command("AT+DLST", "OK", "CONNECT", A6_CMD_TIMEOUT, 2, NULL);
+}
+
+
+// Answer a call.
+void A6lib::answer() {
+ A6command("ATA", "OK", "yy", A6_CMD_TIMEOUT, 2, NULL);
+}
+
+
+// Hang up the phone.
+void A6lib::hangUp() {
+ A6command("ATH", "OK", "yy", A6_CMD_TIMEOUT, 2, NULL);
+}
+
+
+// Check whether there is an active call.
+callInfo A6lib::checkCallStatus() {
+ char number[50];
+ String response = "";
+ uint32_t respStart = 0, matched = 0;
+ callInfo cinfo = (const struct callInfo) {
+ 0
+ };
+
+ // Issue the command and wait for the response.
+ A6command("AT+CLCC", "OK", "+CLCC", A6_CMD_TIMEOUT, 2, &response);
+
+ // Parse the response if it contains a valid +CLCC.
+ respStart = response.indexOf("+CLCC");
+ if (respStart >= 0) {
+ matched = sscanf(response.substring(respStart).c_str(), "+CLCC: %d,%d,%d,%d,%d,\"%s\",%d", &cinfo.index, &cinfo.direction, &cinfo.state, &cinfo.mode, &cinfo.multiparty, number, &cinfo.type);
+ cinfo.number = String(number);
+ }
+
+ uint8_t comma_index = cinfo.number.indexOf('"');
+ if (comma_index != -1) {
+ logln("Extra comma found.");
+ cinfo.number = cinfo.number.substring(0, comma_index);
+ }
+
+ return cinfo;
+}
+
+
+// Get the strength of the GSM signal.
+int A6lib::getSignalStrength() {
+ String response = "";
+ uint32_t respStart = 0;
+ int strength, error = 0;
+
+ // Issue the command and wait for the response.
+ A6command("AT+CSQ", "OK", "+CSQ", A6_CMD_TIMEOUT, 2, &response);
+
+ respStart = response.indexOf("+CSQ");
+ if (respStart < 0) {
+ return 0;
+ }
+
+ sscanf(response.substring(respStart).c_str(), "+CSQ: %d,%d",
+ &strength, &error);
+
+ // Bring value range 0..31 to 0..100%, don't mind rounding..
+ strength = (strength * 100) / 31;
+ return strength;
+}
+
+
+// Get the real time from the modem. Time will be returned as yy/MM/dd,hh:mm:ss+XX
+String A6lib::getRealTimeClock() {
+ String response = "";
+
+ // Issue the command and wait for the response.
+ A6command("AT+CCLK?", "OK", "yy", A6_CMD_TIMEOUT, 1, &response);
+ int respStart = response.indexOf("+CCLK: \"") + 8;
+ response.setCharAt(respStart - 1, '-');
+
+ return response.substring(respStart, response.indexOf("\""));
+}
+
+
+// Send an SMS.
+byte A6lib::sendSMS(String number, String text) {
+ char ctrlZ[2] = { 0x1a, 0x00 };
+ char buffer[100];
+
+ if (text.length() > 159) {
+ // We can't send messages longer than 160 characters.
+ return A6_NOTOK;
+ }
+
+ log("Sending SMS to ");
+ log(number);
+ logln("...");
+
+ sprintf(buffer, "AT+CMGS=\"%s\"", number.c_str());
+ A6command(buffer, ">", "yy", A6_CMD_TIMEOUT, 2, NULL);
+ delay(100);
+ A6conn->println(text.c_str());
+ A6conn->println(ctrlZ);
+ A6conn->println();
+
+ return A6_OK;
+}
+
+
+// Retrieve the number and locations of unread SMS messages.
+int A6lib::getUnreadSMSLocs(int* buf, int maxItems) {
+ return getSMSLocsOfType(buf, maxItems, "REC UNREAD");
+}
+
+// Retrieve the number and locations of all SMS messages.
+int A6lib::getSMSLocs(int* buf, int maxItems) {
+ return getSMSLocsOfType(buf, maxItems, "ALL");
+}
+
+// Retrieve the number and locations of all SMS messages.
+int A6lib::getSMSLocsOfType(int* buf, int maxItems, String type) {
+ String seqStart = "+CMGL: ";
+ String response = "";
+
+ String command = "AT+CMGL=\"";
+ command += type;
+ command += "\"";
+
+ // Issue the command and wait for the response.
+ byte status = A6command(command.c_str(), "\xff\r\nOK\r\n", "\r\nOK\r\n", A6_CMD_TIMEOUT, 2, &response);
+
+ int seqStartLen = seqStart.length();
+ int responseLen = response.length();
+ int index, occurrences = 0;
+
+ // Start looking for the +CMGL string.
+ for (int i = 0; i < (responseLen - seqStartLen); i++) {
+ // If we found a response and it's less than occurrences, add it.
+ if (response.substring(i, i + seqStartLen) == seqStart && occurrences < maxItems) {
+ // Parse the position out of the reply.
+ sscanf(response.substring(i, i + 12).c_str(), "+CMGL: %u,%*s", &index);
+
+ buf[occurrences] = index;
+ occurrences++;
+ }
+ }
+ return occurrences;
+}
+
+// Return the SMS at index.
+SMSmessage A6lib::readSMS(int index) {
+ String response = "";
+ char buffer[30];
+
+ // Issue the command and wait for the response.
+ sprintf(buffer, "AT+CMGR=%d", index);
+ A6command(buffer, "\xff\r\nOK\r\n", "\r\nOK\r\n", A6_CMD_TIMEOUT, 2, &response);
+
+ char message[200];
+ char number[50];
+ char date[50];
+ char type[10];
+ int respStart = 0, matched = 0;
+ SMSmessage sms = (const struct SMSmessage) {
+ "", "", ""
+ };
+
+ // Parse the response if it contains a valid +CLCC.
+ respStart = response.indexOf("+CMGR");
+ if (respStart >= 0) {
+ // Parse the message header.
+ matched = sscanf(response.substring(respStart).c_str(), "+CMGR: \"REC %s\",\"%s\",,\"%s\"\r\n", type, number, date);
+ sms.number = String(number);
+ sms.date = String(date);
+ // The rest is the message, extract it.
+ sms.message = response.substring(strlen(type) + strlen(number) + strlen(date) + 24, response.length() - 8);
+ }
+ return sms;
+}
+
+// Delete the SMS at index.
+byte A6lib::deleteSMS(int index) {
+ char buffer[20];
+ sprintf(buffer, "AT+CMGD=%d", index);
+ return A6command(buffer, "OK", "yy", A6_CMD_TIMEOUT, 2, NULL);
+}
+
+// Delete SMS with special flags
+byte A6lib::deleteSMS(int index, int flag) {
+ char buffer[20];
+ String command = "AT+CMGD=";
+ command += String(index);
+ command += ",";
+ command += String(flag);
+ return A6command(command.c_str(), "OK", "yy", A6_CMD_TIMEOUT, 2, NULL);
+} // AT+CMGD=1,4 delete all SMS from the storage area
+
+
+// Set the SMS charset.
+byte A6lib::setSMScharset(String charset) {
+ char buffer[30];
+
+ sprintf(buffer, "AT+CSCS=\"%s\"", charset.c_str());
+ return A6command(buffer, "OK", "yy", A6_CMD_TIMEOUT, 2, NULL);
+}
+
+
+// Set the volume for the speaker. level should be a number between 5 and
+// 8 inclusive.
+void A6lib::setVol(byte level) {
+ char buffer[30];
+
+ // level should be between 5 and 8.
+ level = _min(_max(level, 5), 8);
+ sprintf(buffer, "AT+CLVL=%d", level);
+ A6command(buffer, "OK", "yy", A6_CMD_TIMEOUT, 2, NULL);
+}
+
+
+// Enable the speaker, rather than the headphones. Pass 0 to route audio through
+// headphones, 1 through speaker.
+void A6lib::enableSpeaker(byte enable) {
+ char buffer[30];
+
+ // enable should be between 0 and 1.
+ enable = _min(_max(enable, 0), 1);
+ sprintf(buffer, "AT+SNFS=%d", enable);
+ A6command(buffer, "OK", "yy", A6_CMD_TIMEOUT, 2, NULL);
+}
+
+
+
+/////////////////////////////////////////////
+// Private methods.
+//
+
+
+// Autodetect the connection rate.
+
+long A6lib::detectRate() {
+ unsigned long rate = 0;
+ unsigned long rates[] = {9600, 115200};
+
+ // Try to autodetect the rate.
+ logln("Autodetecting connection rate...");
+ for (int i = 0; i < countof(rates); i++) {
+ rate = rates[i];
+ A6conn->begin(rate);
+ log("Trying rate ");
+ log(rate);
+ logln("...");
+
+ delay(100);
+ if (A6command("\rAT", "OK", "+CME", 2000, 2, NULL) == A6_OK) {
+ return rate;
+ }
+ }
+
+ logln("Couldn't detect the rate.");
+
+ return A6_NOTOK;
+}
+
+
+// Set the A6 baud rate.
+char A6lib::setRate(long baudRate) {
+ int rate = 0;
+
+ rate = detectRate();
+ if (rate == A6_NOTOK) {
+ return A6_NOTOK;
+ }
+
+ // The rate is already the desired rate, return.
+ //if (rate == baudRate) return OK;
+
+ logln("Setting baud rate on the module...");
+
+ // Change the rate to the requested.
+ char buffer[30];
+ sprintf(buffer, "AT+IPR=%d", baudRate);
+ A6command(buffer, "OK", "+IPR=", A6_CMD_TIMEOUT, 3, NULL);
+
+ logln("Switching to the new rate...");
+ // Begin the connection again at the requested rate.
+ A6conn->begin(baudRate);
+ logln("Rate set.");
+
+ return A6_OK;
+}
+
+
+// Read some data from the A6 in a non-blocking manner.
+String A6lib::read() {
+ String reply = "";
+ if (A6conn->available()) {
+ reply = A6conn->readString();
+ }
+
+ // XXX: Replace NULs with \xff so we can match on them.
+ for (int x = 0; x < reply.length(); x++) {
+ if (reply.charAt(x) == 0) {
+ reply.setCharAt(x, 255);
+ }
+ }
+ return reply;
+}
+
+
+// Issue a command.
+byte A6lib::A6command(const char *command, const char *resp1, const char *resp2, int timeout, int repetitions, String *response) {
+ byte returnValue = A6_NOTOK;
+ byte count = 0;
+
+ // Get rid of any buffered output.
+ A6conn->flush();
+
+ while (count < repetitions && returnValue != A6_OK) {
+ log("Issuing command: ");
+ logln(command);
+
+ A6conn->write(command);
+ A6conn->write('\r');
+
+ if (A6waitFor(resp1, resp2, timeout, response) == A6_OK) {
+ returnValue = A6_OK;
+ } else {
+ returnValue = A6_NOTOK;
+ }
+ count++;
+ }
+ return returnValue;
+}
+
+
+// Wait for responses.
+byte A6lib::A6waitFor(const char *resp1, const char *resp2, int timeout, String *response) {
+ unsigned long entry = millis();
+ int count = 0;
+ String reply = "";
+ byte retVal = 99;
+ do {
+ reply += read();
+ #ifdef ESP8266
+ yield();
+ #endif
+ } while (((reply.indexOf(resp1) + reply.indexOf(resp2)) == -2) && ((millis() - entry) < timeout));
+
+ if (reply != "") {
+ log("Reply in ");
+ log((millis() - entry));
+ log(" ms: ");
+ logln(reply);
+ }
+
+ if (response != NULL) {
+ *response = reply;
+ }
+
+ if ((millis() - entry) >= timeout) {
+ retVal = A6_TIMEOUT;
+ logln("Timed out.");
+ } else {
+ if (reply.indexOf(resp1) + reply.indexOf(resp2) > -2) {
+ logln("Reply OK.");
+ retVal = A6_OK;
+ } else {
+ logln("Reply NOT OK.");
+ retVal = A6_NOTOK;
+ }
+ }
+ return retVal;
+}
diff --git a/lib/A6lib/A6lib.h b/lib/A6lib/A6lib.h
new file mode 100644
index 0000000..b4a6cfe
--- /dev/null
+++ b/lib/A6lib/A6lib.h
@@ -0,0 +1,111 @@
+#ifndef A6lib_h
+#define A6lib_h
+
+#include
+#include "SoftwareSerial.h"
+
+#ifdef DEBUG
+#define log(msg) Serial.print(msg)
+#define logln(msg) Serial.println(msg)
+#else
+#define log(msg)
+#define logln(msg)
+#endif
+
+#define countof(a) (sizeof(a) / sizeof(a[0]))
+
+#define A6_OK 0
+#define A6_NOTOK 1
+#define A6_TIMEOUT 2
+#define A6_FAILURE 3
+
+#define A6_CMD_TIMEOUT 5000
+
+
+enum call_direction {
+ DIR_OUTGOING = 0,
+ DIR_INCOMING = 1
+};
+
+enum call_state {
+ CALL_ACTIVE = 0,
+ CALL_HELD = 1,
+ CALL_DIALING = 2,
+ CALL_ALERTING = 3,
+ CALL_INCOMING = 4,
+ CALL_WAITING = 5,
+ CALL_RELEASE = 7
+};
+
+enum call_mode {
+ MODE_VOICE = 0,
+ MODE_DATA = 1,
+ MODE_FAX = 2,
+ MODE_VOICE_THEN_DATA_VMODE = 3,
+ MODE_VOICE_AND_DATA_VMODE = 4,
+ MODE_VOICE_AND_FAX_VMODE = 5,
+ MODE_VOICE_THEN_DATA_DMODE = 6,
+ MODE_VOICE_AND_DATA_DMODE = 7,
+ MODE_VOICE_AND_FAX_FMODE = 8,
+ MODE_UNKNOWN = 9
+};
+
+struct SMSmessage {
+ String number;
+ String date;
+ String message;
+};
+
+struct callInfo {
+ int index;
+ call_direction direction;
+ call_state state;
+ call_mode mode;
+ int multiparty;
+ String number;
+ int type;
+};
+
+
+class A6lib {
+public:
+ A6lib(int transmitPin, int receivePin);
+ ~A6lib();
+
+ byte begin(long baudRate);
+ byte blockUntilReady(long baudRate);
+
+ void powerCycle(int pin);
+ void powerOn(int pin);
+ void powerOff(int pin);
+
+ void dial(String number);
+ void redial();
+ void answer();
+ void hangUp();
+ callInfo checkCallStatus();
+ int getSignalStrength();
+
+ byte sendSMS(String number, String text);
+ int getUnreadSMSLocs(int* buf, int maxItems);
+ int getSMSLocs(int* buf, int maxItems);
+ int getSMSLocsOfType(int* buf, int maxItems, String type);
+ SMSmessage readSMS(int index);
+ byte deleteSMS(int index);
+ byte deleteSMS(int index, int flag);
+ byte setSMScharset(String charset);
+
+ void setVol(byte level);
+ void enableSpeaker(byte enable);
+
+ String getRealTimeClock();
+
+ SoftwareSerial *A6conn;
+private:
+ String read();
+ byte A6command(const char *command, const char *resp1, const char *resp2, int timeout, int repetitions, String *response);
+ byte A6waitFor(const char *resp1, const char *resp2, int timeout, String *response);
+ long detectRate();
+ char setRate(long baudRate);
+};
+#endif
diff --git a/lib/A6lib/LICENSE.md b/lib/A6lib/LICENSE.md
new file mode 100644
index 0000000..87c0133
--- /dev/null
+++ b/lib/A6lib/LICENSE.md
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2016 Stavros Korokithakis
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/lib/A6lib/README.md b/lib/A6lib/README.md
new file mode 100644
index 0000000..83a00fc
--- /dev/null
+++ b/lib/A6lib/README.md
@@ -0,0 +1,65 @@
+# A6lib
+
+An ESP8266/Arduino library for communicating with the AI-Thinker A6 GSM module.
+It will probably also work with other GSM modules that use the AT command set,
+like the SIM900.
+
+## Details
+
+This library is an adaptation of
+[this sample module](https://github.com/SensorsIot/A6-GSM-Module). It is still
+very incomplete, but at least it works somewhat. Please feel free to issue pull
+requests to improve it.
+
+## Usage
+
+If you have the A6 breakout board, connect the Tx and Rx pins to your ESP8266.
+The example assumes the TX pin is connected to D5 and the RX to D6. A6lib can
+power-cycle the module if you connect a MOSFET to a pin and control the A6's
+power supply with it.
+
+For a sample circuit that uses this library, have a look at [the A6/ESP8266
+breakout board](https://gitlab.com/stavros/A6-ESP8266-breakout/) I designed.
+
+The A6's PWR pin should be permanently connected to Vcc (if you think that's
+wrong or know a better way, please open an issue).
+
+The code looks something like this:
+
+~~~
+// Instantiate the class with Tx, Rx (remember to swap them when connecting to
+the A6, i.e. connect the A6's Rx pin to D6).
+A6lib A6c(D6, D5);
+
+// Initialize the modem, rebooting it if it fails to become ready.
+do {
+ // Power-cycle the module to reset it.
+ A6c.powerCycle(D0);
+} while (A6c.blockUntilReady(9600) != A6_OK);
+
+// Start and place a call.
+A6c.dial("1234567890");
+delay(8000);
+
+A6c.hangUp();
+delay(8000);
+
+A6c.redial();
+delay(8000);
+
+// Send a message.
+A6c.sendSMS("+1234567890", "Hello there!");
+
+// Get an SMS message from memory.
+SMSmessage sms = A6c.readSMS(3);
+
+// Delete an SMS message.
+A6c.deleteSMS(3);
+
+callInfo cinfo = A6c.checkCallStatus();
+// This will be the calling number, "1234567890".
+cinfo.number;
+~~~
+
+This library doesn't currently include any code to connect to the internet, but
+a PR adding that would be very welcome.
diff --git a/lib/A6lib/examples/dial/dial.ino b/lib/A6lib/examples/dial/dial.ino
new file mode 100644
index 0000000..0a96541
--- /dev/null
+++ b/lib/A6lib/examples/dial/dial.ino
@@ -0,0 +1,44 @@
+#include
+
+#ifndef ESP8266
+#define D0 0
+#define D5 8
+#define D6 7
+#endif
+
+// Instantiate the library with TxPin, RxPin.
+A6lib A6l(D6, D5);
+
+void setup() {
+ Serial.begin(115200);
+
+ delay(1000);
+
+ // Power-cycle the module to reset it.
+ A6l.powerCycle(D0);
+ A6l.blockUntilReady(9600);
+}
+
+void loop() {
+ Serial.println("Checking call status...");
+ callInfo cinfo = A6l.checkCallStatus();
+ Serial.println("Call status checked.");
+
+ int sigStrength = A6l.getSignalStrength();
+ Serial.print("Signal strength percentage: ");
+ Serial.println(sigStrength);
+
+ delay(5000);
+
+ if (cinfo.number != NULL) {
+ if (cinfo.direction == DIR_INCOMING && cinfo.number == "919999999999") {
+ A6l.answer();
+ } else {
+ A6l.hangUp();
+ }
+ delay(1000);
+ } else {
+ Serial.println("No number yet.");
+ delay(1000);
+ }
+}
diff --git a/lib/A6lib/examples/serial_relay/serial_relay.ino b/lib/A6lib/examples/serial_relay/serial_relay.ino
new file mode 100644
index 0000000..535aa34
--- /dev/null
+++ b/lib/A6lib/examples/serial_relay/serial_relay.ino
@@ -0,0 +1,30 @@
+#include
+
+#ifndef ESP8266
+#define D0 0
+#define D5 8
+#define D6 7
+#endif
+
+// Instantiate the library with TxPin, RxPin.
+A6lib A6l(D6, D5);
+
+void setup() {
+ Serial.begin(115200);
+
+ delay(1000);
+
+ // Power-cycle the module to reset it.
+ A6l.powerCycle(D0);
+ A6l.blockUntilReady(9600);
+}
+
+void loop() {
+ // Relay things between Serial and the module's SoftSerial.
+ while (A6l.A6conn->available() > 0) {
+ Serial.write(A6l.A6conn->read());
+ }
+ while (Serial.available() > 0) {
+ A6l.A6conn->write(Serial.read());
+ }
+}
diff --git a/lib/A6lib/examples/sms/sms.ino b/lib/A6lib/examples/sms/sms.ino
new file mode 100644
index 0000000..cd483de
--- /dev/null
+++ b/lib/A6lib/examples/sms/sms.ino
@@ -0,0 +1,51 @@
+#include
+
+#ifdef ESP8266
+#define D0 0
+#define D5 8
+#define D6 7
+#endif
+
+// Instantiate the library with TxPin, RxPin.
+A6lib A6l(D6, D5);
+
+int unreadSMSLocs[30] = {0};
+int unreadSMSNum = 0;
+SMSmessage sms;
+
+void setup() {
+ Serial.begin(115200);
+
+ delay(1000);
+
+ // Power-cycle the module to reset it.
+ A6l.powerCycle(D0);
+ A6l.blockUntilReady(9600);
+}
+
+void loop() {
+ String myNumber = "+1132352890";
+
+ callInfo cinfo = A6l.checkCallStatus();
+ if (cinfo.direction == DIR_INCOMING) {
+ if (myNumber.endsWith(cinfo.number)) {
+ // If the number that sent the SMS is ours, reply.
+ A6l.sendSMS(myNumber, "I can't come to the phone right now, I'm a machine.");
+ A6l.hangUp();
+ }
+
+ // Get the memory locations of unread SMS messages.
+ unreadSMSNum = A6l.getUnreadSMSLocs(unreadSMSLocs, 30);
+
+ for (int i = 0; i < unreadSMSNum; i++) {
+ Serial.print("New message at index: ");
+ Serial.println(unreadSMSLocs[i], DEC);
+
+ sms = A6l.readSMS(unreadSMSLocs[i]);
+ Serial.println(sms.number);
+ Serial.println(sms.date);
+ Serial.println(sms.message);
+ }
+ delay(1000);
+ }
+}
diff --git a/lib/A6lib/keywords.txt b/lib/A6lib/keywords.txt
new file mode 100644
index 0000000..1076a88
--- /dev/null
+++ b/lib/A6lib/keywords.txt
@@ -0,0 +1,35 @@
+#######################################
+# A6 GSM communication module
+# (esp8266)
+#######################################
+
+#######################################
+# Datatypes (KEYWORD1)
+#######################################
+
+A6lib KEYWORD1
+callInfo KEYWORD1
+
+#######################################
+# Methods and Functions (KEYWORD2)
+#######################################
+
+begin KEYWORD2
+powerCycle KEYWORD2
+
+dial KEYWORD2
+redial KEYWORD2
+hangUp KEYWORD2
+checkCallStatus KEYWORD2
+getSignalStrength KEYWORD2
+
+sendSMS KEYWORD2
+
+setVol KEYWORD2
+enableSpeaker KEYWORD2
+
+A6conn KEYWORD2
+
+#######################################
+# Constants (LITERAL1)
+#######################################
diff --git a/lib/A6lib/library.json b/lib/A6lib/library.json
new file mode 100644
index 0000000..9023366
--- /dev/null
+++ b/lib/A6lib/library.json
@@ -0,0 +1,14 @@
+{
+ "name": "A6lib",
+ "keywords": [
+ "A6", "GSM", "mobile"
+ ],
+ "description": "An ESP8266/Arduino library for communicating with the A6 GSM module.",
+ "repository":
+ {
+ "type": "git",
+ "url": "https://github.com/skorokithakis/A6lib"
+ },
+ "frameworks": "arduino",
+ "platforms": "*"
+}
diff --git a/lib/A6lib/library.properties b/lib/A6lib/library.properties
new file mode 100644
index 0000000..e7b6598
--- /dev/null
+++ b/lib/A6lib/library.properties
@@ -0,0 +1,9 @@
+name=A6lib
+version=0.1.3
+author=Stavros Korokithakis
+maintainer=Stavros Korokithakis (hi@stavros.io)
+sentence=An ESP8266/Arduino library for communicating with the A6 GSM module.
+paragraph=
+category=Communication
+url=https://github.com/skorokithakis/A6lib
+architectures=*
diff --git a/lib/A6lib/sscanf.cpp b/lib/A6lib/sscanf.cpp
new file mode 100644
index 0000000..bafaeea
--- /dev/null
+++ b/lib/A6lib/sscanf.cpp
@@ -0,0 +1,262 @@
+/*
+ * Copyright (c) 2000-2002 Opsycon AB (www.opsycon.se)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Opsycon AB.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include
+#include
+#include
+#include
+#include
+
+#define MAXLN 200
+#define ISSPACE " \t\n\r\f\v\""
+
+size_t strcspn(const char *p, const char *s) {
+ int i, j;
+
+ for (i = 0; p[i]; i++) {
+ for (j = 0; s[j]; j++) {
+ if (s[j] == p[i]) {
+ break;
+ }
+ }
+ if (s[j]) {
+ break;
+ }
+ }
+ return (i);
+}
+
+char * _getbase(char *p, int *basep) {
+ if (p[0] == '0') {
+ switch (p[1]) {
+ case 'x':
+ *basep = 16;
+ break;
+ case 't':
+ case 'n':
+ *basep = 10;
+ break;
+ case 'o':
+ *basep = 8;
+ break;
+ default:
+ *basep = 10;
+ return (p);
+ }
+ return (p + 2);
+ }
+ *basep = 10;
+ return (p);
+}
+
+/*
+ * _atob(vp,p,base)
+ */
+int _atob(uint32_t *vp, char *p, int base) {
+ uint32_t value, v1, v2;
+ char *q, tmp[20];
+ int digit;
+
+ if (p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) {
+ base = 16;
+ p += 2;
+ }
+
+ if (base == 16 && (q = strchr(p, '.')) != 0) {
+ if (q - p > sizeof(tmp) - 1) {
+ return (0);
+ }
+
+ strncpy(tmp, p, q - p);
+ tmp[q - p] = '\0';
+ if (!_atob(&v1, tmp, 16)) {
+ return (0);
+ }
+
+ q++;
+ if (strchr(q, '.')) {
+ return (0);
+ }
+
+ if (!_atob(&v2, q, 16)) {
+ return (0);
+ }
+ *vp = (v1 << 16) + v2;
+ return (1);
+ }
+
+ value = *vp = 0;
+ for (; *p; p++) {
+ if (*p >= '0' && *p <= '9') {
+ digit = *p - '0';
+ } else if (*p >= 'a' && *p <= 'f') {
+ digit = *p - 'a' + 10;
+ } else if (*p >= 'A' && *p <= 'F') {
+ digit = *p - 'A' + 10;
+ } else {
+ return (0);
+ }
+
+ if (digit >= base) {
+ return (0);
+ }
+ value *= base;
+ value += digit;
+ }
+ *vp = value;
+ return (1);
+}
+
+/*
+ * atob(vp,p,base)
+ * converts p to binary result in vp, rtn 1 on success
+ */
+int atob(uint32_t *vp, char *p, int base) {
+ uint32_t v;
+ if (base == 0) {
+ p = _getbase(p, &base);
+ }
+ if (_atob(&v, p, base)) {
+ *vp = v;
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * vsscanf(buf,fmt,ap)
+ */
+
+int vsscanf(const char *buf, const char *s, va_list ap) {
+ uint32_t count, noassign, width, base, lflag;
+ const char *tc;
+ char *t, tmp[MAXLN];
+
+ count = noassign = width = lflag = 0;
+ while (*s && *buf) {
+ while (isspace(*s)) {
+ s++;
+ }
+ if (*s == '%') {
+ s++;
+ for (; *s; s++) {
+ if (strchr("dibouxcsefg%", *s)) {
+ break;
+ }
+ if (*s == '*') {
+ noassign = 1;
+ } else if (*s == 'l' || *s == 'L') {
+ lflag = 1;
+ } else if (*s >= '1' && *s <= '9') {
+ for (tc = s; isdigit(*s); s++);
+ strncpy(tmp, tc, s - tc);
+ tmp[s - tc] = '\0';
+ atob(&width, tmp, 10);
+ s--;
+ }
+ }
+ if (*s == 's') {
+ while (isspace(*buf)) {
+ buf++;
+ }
+ if (!width) {
+ width = strcspn(buf, ISSPACE);
+ }
+ if (!noassign) {
+ strncpy(t = va_arg(ap, char *), buf, width);
+ t[width] = '\0';
+ }
+ buf += width;
+ } else if (*s == 'c') {
+ if (!width) {
+ width = 1;
+ }
+ if (!noassign) {
+ strncpy(t = va_arg(ap, char *), buf, width);
+ t[width] = '\0';
+ }
+ buf += width;
+ } else if (strchr("dobxu", *s)) {
+ while (isspace(*buf)) {
+ buf++;
+ }
+ if (*s == 'd' || *s == 'u') {
+ base = 10;
+ } else if (*s == 'x') {
+ base = 16;
+ } else if (*s == 'o') {
+ base = 8;
+ } else if (*s == 'b') {
+ base = 2;
+ }
+ if (!width) {
+ if (isspace(*(s + 1)) || *(s + 1) == 0) {
+ width = strcspn(buf, ISSPACE);
+ } else {
+ width = strchr(buf, *(s + 1)) - buf;
+ }
+ }
+ strncpy(tmp, buf, width);
+ tmp[width] = '\0';
+ buf += width;
+ if (!noassign) {
+ atob(va_arg(ap, uint32_t *), tmp, base);
+ }
+ }
+ if (!noassign) {
+ count++;
+ }
+ width = noassign = lflag = 0;
+ s++;
+ } else {
+ while (isspace(*buf)) {
+ buf++;
+ }
+ if (*s != *buf) {
+ break;
+ } else {
+ s++, buf++;
+ }
+ }
+ }
+ return (count);
+}
+
+
+int __attribute__((used)) sscanf(const char *buf, const char *fmt, ...) {
+ int count;
+ va_list ap;
+
+ va_start(ap, fmt);
+ count = vsscanf(buf, fmt, ap);
+ va_end(ap);
+ return (count);
+}
diff --git a/tests/Test_config.h b/tests/Test_config.h
index df1d4f5..07477f3 100644
--- a/tests/Test_config.h
+++ b/tests/Test_config.h
@@ -90,6 +90,7 @@ const byte subnet[] = { 255, 255, 255, 0 }; //ip adress
#define ZgatewayRF2
#define ZgatewayIR
#define ZgatewayBT
+ #define Zgateway2G
#define ZactuatorONOFF
#define ZsensorINA226
#define ZsensorHCSR501
@@ -102,6 +103,7 @@ const byte subnet[] = { 255, 255, 255, 0 }; //ip adress
#elif ESP32
#define ZgatewayRF
#define ZgatewayRF2
+ //#define Zgateway2G (not tested yet)
//#define ZgatewayIR
#define ZgatewayBT
#define ZactuatorONOFF
@@ -114,6 +116,7 @@ const byte subnet[] = { 255, 255, 255, 0 }; //ip adress
//#define ZgatewayRFM69 // If you uncomment this you can't use RF and BT due to the fact that RF use also D8 and BT use also D6/D7
#else // for arduino mega + W5100
#define ZgatewayRF
+ //#define Zgateway2G (not tested yet)
//#define ZgatewayRF2 // too big for UNO
//#define ZgatewayRFM69 not tested
//#define ZgatewayIR