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
|
||||
#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);
|
||||
}
|
||||
}
|
||||
+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 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
|
||||
|
||||
Reference in new issue
Block a user