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:
Florian authored and GitHub committed 2018-04-21 19:22:36 +02:00
1 parent 0412461d66
commit 513d71c544
18 files changed
+1382

No files matched your search

+21
View File
@@ -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
+504
View File
@@ -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;
}
+111
View File
@@ -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
+21
View File
@@ -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.
+65
View File
@@ -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.
+44
View File
@@ -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());
}
}
+51
View File
@@ -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);
}
}
+35
View File
@@ -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)
#######################################
+14
View File
@@ -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": "*"
}
+9
View File
@@ -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=*
+262
View File
@@ -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);
}