Skip to content

Instantly share code, notes, and snippets.

@technobly
Last active May 7, 2019 19:10
Show Gist options
  • Select an option

  • Save technobly/fb164a7012c64eed7a775f4f7ba91662 to your computer and use it in GitHub Desktop.

Select an option

Save technobly/fb164a7012c64eed7a775f4f7ba91662 to your computer and use it in GitHub Desktop.
SMS Test App (with Tinker)
Press a key to run a command:
[l] list unread SMS
[L] list Read SMS
[d] delete SMS (not implemented, but the function is in the code)
[s] send SMS (not implemented, but the function is in the code)
[r] read unread SMS, do not delete or reply
[R] read already read SMS, delete and reply
[a] send an AT command
[h] show this help menu

The electron will check for new messages every 1 second
If it finds d7 or D7, it will toggle the D7 LED and reply with the new state.
If it finds "who are you" it will reply with "Particle Electron of course!".
/*
******************************************************************************
Copyright (c) 2015 Particle Industries, Inc. All rights reserved.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation, either
version 3 of the License, or (at your option) any later version.
This program 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this program; if not, see <http://www.gnu.org/licenses/>.
******************************************************************************
*/
/* Includes ------------------------------------------------------------------*/
#include "application.h"
PRODUCT_ID(PLATFORM_ID);
PRODUCT_VERSION(2);
// ALL_LEVEL, TRACE_LEVEL, DEBUG_LEVEL, INFO_LEVEL, WARN_LEVEL, ERROR_LEVEL, PANIC_LEVEL, NO_LOG_LEVEL
SerialDebugOutput debugOutput(9600, ALL_LEVEL);
STARTUP(cellular_credentials_set("wireless.twilio.com", "", "", NULL)); // TWILIO
//STARTUP(cellular_credentials_set("broadband", "", "", NULL)); // AT&T
/* Function prototypes -------------------------------------------------------*/
int tinkerDigitalRead(String pin);
int tinkerDigitalWrite(String command);
int tinkerAnalogRead(String pin);
int tinkerAnalogWrite(String command);
SYSTEM_MODE(AUTOMATIC);
uint32_t lastUpdate = 0;
bool D7state = false;
typedef struct { char* buf; char* num; } CMGRparam;
//static int _cbCUSD(int type, const char* buf, int len, char* resp);
typedef struct { int* ix; int num; } CMGLparam;
int _cbCMGL(int type, const char* buf, int len, CMGLparam* param);
int _cbCMGR(int type, const char* buf, int len, CMGRparam* param);
int smsList(const char* stat /*= "ALL"*/, int* ix /*=NULL*/, int num /*= 0*/);
bool smsSend(const char* num, const char* buf);
bool smsDelete(int ix);
bool smsRead(int ix, char* num, char* buf, int len);
void checkUnreadSMS();
void checkReadSMS();
void processATcommand();
void showHelp();
int _cbCMGL(int type, const char* buf, int len, CMGLparam* param)
{
if ((type == TYPE_PLUS) && param && param->num) {
// +CMGL: <ix>,...
int ix;
if (sscanf(buf, "\r\n+CMGL: %d,", &ix) == 1)
{
*param->ix++ = ix;
param->num--;
}
}
return WAIT;
}
int smsList(const char* stat /*= "ALL"*/, int* ix /*=NULL*/, int num /*= 0*/) {
int ret = -1;
CMGLparam param;
param.ix = ix;
param.num = num;
if (RESP_OK == Cellular.command(_cbCMGL, &param, "AT+CMGL=\"%s\"\r\n", stat))
ret = num - param.num;
return ret;
}
bool smsSend(const char* num, const char* buf)
{
bool ok = false;
if (RESP_PROMPT == Cellular.command(150*1000, "AT+CMGS=\"%s\"\r\n", num)) {
Cellular.command(10, "%s", buf);
ok = (RESP_OK == Cellular.command(3*60*1000, "%c", 0x1A)); // ctrl-Z
}
return ok;
}
bool smsDelete(int ix)
{
/*
* AT+CMGD=<index>[,<flag>]
*
* <index> = Storage position
*
* <flag> = Deletion flag. If present, and different from 0, <index> is ignored:
* • 0 (default value): delete the message specified in <index>
* • 1: delete all the read messages from the preferred message storage, leaving unread messages
* and stored mobile originated messages (whether sent or not) untouched
* • 2: delete all the read messages from the preferred message storage and sent mobile originated
* messages, leaving unread messages and unsent mobile originated messages untouched
* • 3: delete all the read messages from the preferred message storage, sent and unsent mobile
* originated messages leaving unread messages untouched
* • 4: delete all the messages from the preferred message storage including unread messages
*/
bool ok = false;
ok = (RESP_OK == Cellular.command("AT+CMGD=%d\r\n", ix));
return ok;
}
int _cbCMGR(int type, const char* buf, int len, CMGRparam* param)
{
if (param) {
if (type == TYPE_PLUS) {
if (sscanf(buf, "\r\n+CMGR: \"%*[^\"]\",\"%[^\"]", param->num) == 1) {
}
} else if ((type == TYPE_UNKNOWN) && (buf[len-2] == '\r') && (buf[len-1] == '\n')) {
memcpy(param->buf, buf, len-2);
param->buf[len-2] = '\0';
}
}
return WAIT;
}
bool smsRead(int ix, char* num, char* buf, int len)
{
bool ok = false;
CMGRparam param;
param.num = num;
param.buf = buf;
ok = (RESP_OK == Cellular.command(_cbCMGR, &param, "AT+CMGR=%d\r\n", ix));
return ok;
}
void checkUnreadSMS() {
// checking unread sms, looking for matching for D7 or special messages, send reply, then delete the message.
char buf[512] = "";
int ix[8];
int n = smsList("REC UNREAD", ix, 8);
if (n > 8) n = 8;
while (n-- > 0)
{
char num[32];
Serial.printf("Unread SMS at index %d\r\n", ix[n]);
if (smsRead(ix[n], num, buf, sizeof(buf))) {
Serial.printf("Got SMS from \"%s\" with text \"%s\"\r\n", num, buf);
// provide a generic reply
const char* reply = "Hello my friend";
// If message contains D7 or d7
if (strstr(buf, "d7") || strstr(buf, "D7")) {
if (getPinMode(D7) != OUTPUT) D7state = false; // assume D7 was not just an output, make sure the LED turns ON.
D7state = !D7state; // toggle the D7 LED
pinMode(D7, OUTPUT);
digitalWrite(D7, D7state);
if (D7state) reply = "D7 LED toggled on";
else reply = "D7 LED toggled off";
}
// If the message contains "Who are you" it will reply with something specific
// we don't check the W in case it's upper/lower case.
if (strstr(buf, /*w*/"ho are you"))
reply = "Particle Electron of course!";
Serial.printf("Send SMS reply \"%s\" to \"%s\"\r\n", reply, num);
smsSend(num, reply);
// All done with this message, let's delete it
Serial.printf("Delete SMS at index %d\r\n", ix[n]);
smsDelete(ix[n]);
}
}
}
void checkReadSMS() {
// checking read sms, and deleting
char buf[512] = "";
int ix[8];
int n = smsList("REC READ", ix, 8);
if (n > 8) n = 8;
while (n-- > 0)
{
char num[32];
Serial.printf("Read SMS at index %d\r\n", ix[n]);
if (smsRead(ix[n], num, buf, sizeof(buf))) {
Serial.printf("Got SMS from \"%s\" with text \"%s\"\r\n", num, buf);
Serial.printf("Delete SMS at index %d\r\n", ix[n]);
smsDelete(ix[n]);
}
}
}
/* This function is called once at start up ----------------------------------*/
void setup()
{
Particle.keepAlive(2 * 30); // send a ping every 1 minute
Serial.begin(9600);
//Setup the Tinker application here
//Register all the Tinker functions
Particle.function("digitalread", tinkerDigitalRead);
Particle.function("digitalwrite", tinkerDigitalWrite);
Particle.function("analogread", tinkerAnalogRead);
Particle.function("analogwrite", tinkerAnalogWrite);
}
/* This function loops forever --------------------------------------------*/
void loop()
{
// Check for new SMS every 1 second, process if one is received.
if (millis() - lastUpdate > 1000UL) {
lastUpdate = millis();
checkUnreadSMS();
}
if (Serial.available() > 0)
{
char c = Serial.read();
if (c == 'a') {
processATcommand();
}
else if (c == 'l') {
int ix[8];
int n = smsList("REC UNREAD", ix, 8);
Serial.printf("UNREAD SMS WAITING: %d\r\n", n);
}
else if (c == 'L') {
int ix[8];
int n = smsList("REC READ", ix, 8);
Serial.printf("READ SMS WAITING: %d\r\n", n);
}
else if (c == 'r') {
//smsRead();
checkUnreadSMS();
}
else if (c == 'R') {
//smsRead();
checkReadSMS();
}
else if (c == 'd') {
//smsDelete();
}
else if (c == 's') {
//smsSend();
}
else if (c == 'h') {
showHelp();
}
else {
Serial.println("Bad command!");
}
while (Serial.available()) Serial.read(); // Flush the input buffer
}
}
/*******************************************************************************
* Function Name : tinkerDigitalRead
* Description : Reads the digital value of a given pin
* Input : Pin
* Output : None.
* Return : Value of the pin (0 or 1) in INT type
Returns a negative number on failure
*******************************************************************************/
int tinkerDigitalRead(String pin)
{
//convert ascii to integer
int pinNumber = pin.charAt(1) - '0';
//Sanity check to see if the pin numbers are within limits
if (pinNumber < 0 || pinNumber > 7) return -1;
if(pin.startsWith("D"))
{
pinMode(pinNumber, INPUT_PULLDOWN);
return digitalRead(pinNumber);
}
else if (pin.startsWith("A"))
{
pinMode(pinNumber+10, INPUT_PULLDOWN);
return digitalRead(pinNumber+10);
}
#if Wiring_Cellular
else if (pin.startsWith("B"))
{
if (pinNumber > 5) return -3;
pinMode(pinNumber+24, INPUT_PULLDOWN);
return digitalRead(pinNumber+24);
}
else if (pin.startsWith("C"))
{
if (pinNumber > 5) return -4;
pinMode(pinNumber+30, INPUT_PULLDOWN);
return digitalRead(pinNumber+30);
}
#endif
return -2;
}
/*******************************************************************************
* Function Name : tinkerDigitalWrite
* Description : Sets the specified pin HIGH or LOW
* Input : Pin and value
* Output : None.
* Return : 1 on success and a negative number on failure
*******************************************************************************/
int tinkerDigitalWrite(String command)
{
bool value = 0;
//convert ascii to integer
int pinNumber = command.charAt(1) - '0';
//Sanity check to see if the pin numbers are within limits
if (pinNumber < 0 || pinNumber > 7) return -1;
if(command.substring(3,7) == "HIGH") value = 1;
else if(command.substring(3,6) == "LOW") value = 0;
else return -2;
if(command.startsWith("D"))
{
pinMode(pinNumber, OUTPUT);
digitalWrite(pinNumber, value);
return 1;
}
else if(command.startsWith("A"))
{
pinMode(pinNumber+10, OUTPUT);
digitalWrite(pinNumber+10, value);
return 1;
}
#if Wiring_Cellular
else if(command.startsWith("B"))
{
if (pinNumber > 5) return -4;
pinMode(pinNumber+24, OUTPUT);
digitalWrite(pinNumber+24, value);
return 1;
}
else if(command.startsWith("C"))
{
if (pinNumber > 5) return -5;
pinMode(pinNumber+30, OUTPUT);
digitalWrite(pinNumber+30, value);
return 1;
}
#endif
else return -3;
}
/*******************************************************************************
* Function Name : tinkerAnalogRead
* Description : Reads the analog value of a pin
* Input : Pin
* Output : None.
* Return : Returns the analog value in INT type (0 to 4095)
Returns a negative number on failure
*******************************************************************************/
int tinkerAnalogRead(String pin)
{
//convert ascii to integer
int pinNumber = pin.charAt(1) - '0';
//Sanity check to see if the pin numbers are within limits
if (pinNumber < 0 || pinNumber > 7) return -1;
if(pin.startsWith("D"))
{
return -3;
}
else if (pin.startsWith("A"))
{
return analogRead(pinNumber+10);
}
#if Wiring_Cellular
else if (pin.startsWith("B"))
{
if (pinNumber < 2 || pinNumber > 5) return -3;
return analogRead(pinNumber+24);
}
#endif
return -2;
}
/*******************************************************************************
* Function Name : tinkerAnalogWrite
* Description : Writes an analog value (PWM) to the specified pin
* Input : Pin and Value (0 to 255)
* Output : None.
* Return : 1 on success and a negative number on failure
*******************************************************************************/
int tinkerAnalogWrite(String command)
{
String value = command.substring(3);
if(command.substring(0,2) == "TX")
{
pinMode(TX, OUTPUT);
analogWrite(TX, value.toInt());
return 1;
}
else if(command.substring(0,2) == "RX")
{
pinMode(RX, OUTPUT);
analogWrite(RX, value.toInt());
return 1;
}
//convert ascii to integer
int pinNumber = command.charAt(1) - '0';
//Sanity check to see if the pin numbers are within limits
if (pinNumber < 0 || pinNumber > 7) return -1;
if(command.startsWith("D"))
{
pinMode(pinNumber, OUTPUT);
analogWrite(pinNumber, value.toInt());
return 1;
}
else if(command.startsWith("A"))
{
pinMode(pinNumber+10, OUTPUT);
analogWrite(pinNumber+10, value.toInt());
return 1;
}
else if(command.substring(0,2) == "TX")
{
pinMode(TX, OUTPUT);
analogWrite(TX, value.toInt());
return 1;
}
else if(command.substring(0,2) == "RX")
{
pinMode(RX, OUTPUT);
analogWrite(RX, value.toInt());
return 1;
}
#if Wiring_Cellular
else if (command.startsWith("B"))
{
if (pinNumber > 3) return -3;
pinMode(pinNumber+24, OUTPUT);
analogWrite(pinNumber+24, value.toInt());
return 1;
}
else if (command.startsWith("C"))
{
if (pinNumber < 4 || pinNumber > 5) return -4;
pinMode(pinNumber+30, OUTPUT);
analogWrite(pinNumber+30, value.toInt());
return 1;
}
#endif
else return -2;
}
void processATcommand() {
static bool once = false;
if (!once) {
once = true;
Serial.println("Please be careful with AT commands. BACKSPACE and"
"\r\nESCAPE keys can be used to cancel and modify commands.");
}
Serial.print("Enter an AT command: ");
int done = false;
String cmd = "";
while (!done) {
if (Serial.available() > 0) {
char c = Serial.read();
if (c == '\r') {
Serial.println();
if(cmd != "") {
if (RESP_OK == Cellular.command("%s\r\n", cmd.c_str())) {
Serial.printlnf("%s :command request sent!", cmd.c_str());
}
else {
Serial.printlnf("%s :command request was not recognized by the modem!", cmd.c_str());
}
}
cmd = "";
done = true;
}
else if (c == 27) { // ESC
if (cmd.length() > 0) {
for (uint32_t x=0; x<cmd.length(); x++) {
Serial.print('\b');
}
for (uint32_t x=0; x<cmd.length(); x++) {
Serial.print(' ');
}
for (uint32_t x=0; x<cmd.length(); x++) {
Serial.print('\b');
}
}
Serial.println("command aborted!");
cmd = "";
done = true;
}
else if ((c == 8 || c == 127)) { // BACKSPACE
if (cmd.length() > 0) {
cmd.remove(cmd.length()-1, 1);
Serial.print("\b \b");
}
}
else {
cmd += c;
Serial.print(c);
}
}
Particle.process();
}
}
void showHelp() {
Serial.println("\r\nPress a key to run a command:"
"\r\n[l] list unread SMS"
"\r\n[L] list Read SMS"
"\r\n[d] delete SMS (not implemented)"
"\r\n[s] send SMS (not implemented)"
"\r\n[r] read unread SMS, do not delete or reply"
"\r\n[R] read already read SMS, delete and reply"
"\r\n[a] send an AT command"
"\r\n[h] show this help menu\r\n");
}
@18637
Copy link

18637 commented Aug 15, 2017

Hey mate, Code looks good but when sending a SMS containing d7 ot D7 it just reply's back "hello my friend" I cant seem to get it working to switch off a LED on D7. any ideas.? Cheers Ric

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment