A6 A7 gateway (GSM GPRS) (#195)
* init 2G gateway with SMS sending and receiving * A6lib add * add 2Ggateway call from setup and loop * add schematic and image
This commit is contained in:
1 parent
0412461d66
commit
513d71c544
18 files changed
+1382
No files matched your search
@@ -73,6 +73,9 @@
|
|||||||
#ifdef ZgatewayIR
|
#ifdef ZgatewayIR
|
||||||
#include "config_IR.h"
|
#include "config_IR.h"
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef Zgateway2G
|
||||||
|
#include "config_2G.h"
|
||||||
|
#endif
|
||||||
#ifdef ZactuatorONOFF
|
#ifdef ZactuatorONOFF
|
||||||
#include "config_ONOFF.h"
|
#include "config_ONOFF.h"
|
||||||
#endif
|
#endif
|
||||||
@@ -329,6 +332,9 @@ void setup()
|
|||||||
#ifdef ZactuatorONOFF
|
#ifdef ZactuatorONOFF
|
||||||
setupONOFF();
|
setupONOFF();
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef Zgateway2G
|
||||||
|
setup2G();
|
||||||
|
#endif
|
||||||
#ifdef ZgatewayIR
|
#ifdef ZgatewayIR
|
||||||
setupIR();
|
setupIR();
|
||||||
#endif
|
#endif
|
||||||
@@ -625,6 +631,11 @@ void loop()
|
|||||||
trc(F("BTtoMQTT OK"));
|
trc(F("BTtoMQTT OK"));
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef Zgateway2G
|
||||||
|
if(_2GtoMQTT()){
|
||||||
|
trc(F("2GtoMQTT OK"));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
#ifdef ZgatewayRFM69
|
#ifdef ZgatewayRFM69
|
||||||
if(RFM69toMQTT())
|
if(RFM69toMQTT())
|
||||||
trc(F("RFM69toMQTT OK"));
|
trc(F("RFM69toMQTT OK"));
|
||||||
@@ -705,6 +716,9 @@ digitalWrite(led_send, LOW);
|
|||||||
#ifdef ZgatewayRF2
|
#ifdef ZgatewayRF2
|
||||||
MQTTtoRF2(topicOri, datacallback);
|
MQTTtoRF2(topicOri, datacallback);
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef Zgateway2G
|
||||||
|
MQTTto2G(topicOri, datacallback);
|
||||||
|
#endif
|
||||||
#ifdef ZgatewaySRFB
|
#ifdef ZgatewaySRFB
|
||||||
MQTTtoSRFB(topicOri, datacallback);
|
MQTTtoSRFB(topicOri, datacallback);
|
||||||
#endif
|
#endif
|
||||||
@@ -761,3 +775,22 @@ void trc(String msg){
|
|||||||
Serial.println(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);
|
||||||
|
}
|
||||||
|
}
|
||||||
+133
@@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#ifdef Zgateway2G
|
||||||
|
|
||||||
|
#include <A6lib.h> // 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
|
||||||
+46
@@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*-------------------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
|
||||||
|
|
||||||
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 127 KiB |
@@ -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
|
||||||
@@ -0,0 +1,504 @@
|
|||||||
|
#include <Arduino.h>
|
||||||
|
#include <SoftwareSerial.h>
|
||||||
|
#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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
#ifndef A6lib_h
|
||||||
|
#define A6lib_h
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
#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
|
||||||
@@ -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.
|
||||||
@@ -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.
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
#include <A6lib.h>
|
||||||
|
|
||||||
|
#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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
#include <A6lib.h>
|
||||||
|
|
||||||
|
#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());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
#include <A6lib.h>
|
||||||
|
|
||||||
|
#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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
#######################################
|
||||||
@@ -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": "*"
|
||||||
|
}
|
||||||
@@ -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=*
|
||||||
@@ -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 <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
#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);
|
||||||
|
}
|
||||||
@@ -90,6 +90,7 @@ const byte subnet[] = { 255, 255, 255, 0 }; //ip adress
|
|||||||
#define ZgatewayRF2
|
#define ZgatewayRF2
|
||||||
#define ZgatewayIR
|
#define ZgatewayIR
|
||||||
#define ZgatewayBT
|
#define ZgatewayBT
|
||||||
|
#define Zgateway2G
|
||||||
#define ZactuatorONOFF
|
#define ZactuatorONOFF
|
||||||
#define ZsensorINA226
|
#define ZsensorINA226
|
||||||
#define ZsensorHCSR501
|
#define ZsensorHCSR501
|
||||||
@@ -102,6 +103,7 @@ const byte subnet[] = { 255, 255, 255, 0 }; //ip adress
|
|||||||
#elif ESP32
|
#elif ESP32
|
||||||
#define ZgatewayRF
|
#define ZgatewayRF
|
||||||
#define ZgatewayRF2
|
#define ZgatewayRF2
|
||||||
|
//#define Zgateway2G (not tested yet)
|
||||||
//#define ZgatewayIR
|
//#define ZgatewayIR
|
||||||
#define ZgatewayBT
|
#define ZgatewayBT
|
||||||
#define ZactuatorONOFF
|
#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
|
//#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
|
#else // for arduino mega + W5100
|
||||||
#define ZgatewayRF
|
#define ZgatewayRF
|
||||||
|
//#define Zgateway2G (not tested yet)
|
||||||
//#define ZgatewayRF2 // too big for UNO
|
//#define ZgatewayRF2 // too big for UNO
|
||||||
//#define ZgatewayRFM69 not tested
|
//#define ZgatewayRFM69 not tested
|
||||||
//#define ZgatewayIR
|
//#define ZgatewayIR
|
||||||
|
|||||||
Reference in new issue
Block a user