The SIM800 module is great for making a simple phone and listening to the radio, but i noticed it also has a DTMF decoder onboard which allows us to control our Arduino remotely! DTMF are the ‘touchtone’ signals you can send over the phone line using the dialpad on your phone.
Below project uses an Arduino Uno and a SIM800 module to control some LED’s, a buzzer, and a 220V relay by calling the number of the SIM card and entering numbers on my phone’s dialpad.
I needed to configure the SIM module as follows:
- automatically pick up any incoming call after 1 ring: ATS0=1
- enable DTMF: AT+DDET=1,1000,0,0 (see AT commands datasheet for parameters)
- listen for incoming DTMF values on the Serial connection (format: +DTMF: x)
I programmed my sketch (see below) to react as follows when i press these numbers on my phone’s dialpad:
- 1 = toggle the blue LED and the relay
- 2 = move the servo to the open position
- 3 = move the servo to the closed position
- 4 = make a siren sound on the buzzer
- 7 = switch on the yellow LED
- 8 = switch off the yellow LED
The relay is a cheap 5V relay with 3 connections: 5V, GND, and on/off signal that can be driven straight from an Arduino digital output pin. I put it inside a plastic box and added a 220V plug and socket. I also built a bit of Lego for a servo to open a kind of gate. Overview of the setup below:
- Arduino Uno on the top, powered by USB charger (the SIM module takes a lot of power, sometimes a laptop’s USB port cannot supply enough power)
- breadboard in the middle, with OLED screen, LED’s and buzzer
- Lego gate with servo
- 5V relay with plug and socket
Now i can switch on my coffee machine with a simple phone call from anywhere in the world!
A couple of other features of the code:
- i was running out of digital I/O pins so used some analog input pins as digital I/O (A0=D14 etc)
- the sketch also decodes the network name and signal strength and shows it on the OLED
- the servo is only attached (software wise) to its pin while it needs to move; when i left it attached all the time, it was very jittery
- the sketch checks every 5 seconds if the network connection is still OK (it easily drops if the power supply is not sufficient)
- i use a function checkDTMF() to return the integer value of any incoming DTMF message on Serial
- if you don’t have the OLED, the sketch should still work in the same way
Here is the code as described above:
================================================================================
/*
SIM800L gsm module with Arduino UNO on 5V usb
Tom Tobback Feb 2015
SIM800 breakout board has a DIODE to bring Vcc of 5V down to around 4.2V but need to REMOVE shortcut S1 (make it open to enable the diode)
add large capacitor e.g. 220uF
Vcc to 5V (make sure S1 is open)
GND to GND
TX to A0=D14
RX to A1=D15
RST to D7
MIC, MICN to electric mic
SPK, SPKN to speaker
green LED on D2
red LED on D3
actuators:
blue led on D4 >> toggle with DTMF 1
servo on D5 >> open with DTMF 2, close with 3
buzzer on D6 >> siren with DTMF 4
yellow led on D16=A2 >> on with DTMF 7
>> off with DTMF 8
*/
#include <SoftwareSerial.h>
SoftwareSerial sim800(14, 15); // A0 = D14, A1 = D15
#include “U8glib.h”
U8GLIB_SSD1306_128X64 u8g(10, 9); // OLED display: HW SPI CS = 10, A0/DC = 9 (Hardware Pins are SCK/D0 = 13 and MOSI/D1 = 11) + RST to Arduino D8
#include <Servo.h>
Servo simservo;
unsigned long time_checked;
int current_status;
boolean network_status;
int signal_level;
char network_name[10] = ” “; // need to reserve a length, otherwise strange results
char c; // for decoding the sim reply
boolean blue_status = false; // blue LED off
boolean servo_status = false; // servo closed
const int check_interval = 5000;
const int red = 3;
const int green = 2;
const int oled_rst = 8;
const int sim_rst = 7;
const int blue = 4;
const int yellow = 16; // A2 = D16
const int buzzer = 6;
const int servopin = 5;
const int servoclosed = 150;
const int servoopen = 30;
//////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
void setup() {
pinMode(green, OUTPUT); // green LED
pinMode(red, OUTPUT); // red LED
pinMode(blue, OUTPUT); // blue LED
pinMode(yellow, OUTPUT); // yellow LED
pinMode(buzzer, OUTPUT); // buzzer
pinMode(oled_rst, OUTPUT); // reset OLED display
digitalWrite(oled_rst, LOW);
delay(100);
digitalWrite(oled_rst, HIGH);
drawIntro();
digitalWrite(green, HIGH); // 1x blink green and red LED
digitalWrite(red, HIGH);
delay(500);
digitalWrite(green, LOW);
digitalWrite(red, LOW);
digitalWrite(blue, blue_status); // blue off
digitalWrite(yellow, LOW); // yellow off
simservo.attach(servopin); // servo to closed position
simservo.write(servoclosed);
delay(1000);
simservo.detach();
pinMode(sim_rst, OUTPUT); // sim800 RESET pin
Serial.begin(9600);
sim800.begin(9600);
Serial.println();
if (!simOK()) {
drawNoSim();
foreverRed();
}
}
//////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
void loop() {
if (millis() – time_checked > check_interval) {
network_status = checkNetwork(); // check registered or not, and signal level
time_checked = millis();
}
current_status = checkStatus();
if (current_status == 0) {
drawWaiting(); // ready, waiting for call
digitalWrite(red, LOW); // red LED off
}
else {
if (current_status == 4) { // in call after auto answer
drawCallActive();
digitalWrite(red, HIGH); // red LED on
int DTMF = checkDTMF();
if (DTMF > 0) { // -255 if nothing received
Serial.print(“received DTMF: “);
Serial.println(DTMF);
drawDTMFreceived(DTMF);
switch (DTMF) {
case 1: // 1 = toggle blue LED and relay
blue_status = !blue_status;
digitalWrite(blue, blue_status);
beep();
delay(1000);
break;
case 4: // 4 = siren
for (int i = 1000; i < 2000; i++) {
tone(buzzer, i);
delay(1);
}
for (int i = 2000; i > 1000; i–) {
tone(buzzer, i);
delay(1);
}
noTone(buzzer);
break;
case 2: // 2 = servo close > open
beep();
if (!servo_status) {
simservo.attach(servopin);
delay(100);
for (int u = servoclosed; u >= servoopen; u–) {
simservo.write(u);
delay(25);
}
simservo.detach();
servo_status = true;
}
break;
case 3: // 3 = servo open > close
beep();
if (servo_status) {
simservo.attach(servopin);
delay(100);
for (int u = servoopen; u <= servoclosed; u++) {
simservo.write(u);
delay(25);
}
simservo.detach();
servo_status = false;
}
break;
case 7: // 7 = switch on yellow LED
digitalWrite(yellow, HIGH);
beep();
delay(1000);
break;
case 8: // 7 = switch off yellow LED
digitalWrite(yellow, LOW);
beep();
delay(1000);
break;
}
}
}
}
}
//////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
boolean simOK() { // SIM CHECK OK
Serial.println(F(“Checking for sim800 module and SIM card.. “));
digitalWrite(sim_rst, LOW); // hardware reset after sleep RST
delay(300);
digitalWrite(sim_rst, HIGH);
// time to startup 3 sec
for (int i = 0; i < 6; i++) {
digitalWrite(green, HIGH); // green LED blink after RESET
delay(250);
digitalWrite(green, LOW);
delay(250);
}
sim800.println(“AT”); // check if sim800 module responds
delay(100);
if (sim800.find(“OK”)) {
Serial.println(F(“sim800 module found”));
delay(1000); // wait for sim800 to settle a bit
sim800.println(“AT+CSMINS?”); // check if SIM card inserted
delay(100);
if (sim800.find(“CSMINS: 0,0”)) {
Serial.println(F(“no SIM card found, stop here”));
return false;
}
Serial.println(F(“SIM card found”)); // continue if SIM card found
Serial.println(F(“Allow some time for SIM to register on the network..”));
Serial.println();
// SOME HOUSEKEEPING
sim800.println(“AT+CBC”); // battery level
simReply();
sim800.println(“AT+CLVL=80”); // set speaker volume 0-100
simReply();
sim800.println(“AT+CRSL=80”); // set ringer volume 0-100
simReply();
sim800.println(“AT+CMIC=0,10”); // set mic to gain level 0-15
simReply();
// ring tone AT+CALS=5,1 to switch on tone 5 5,0 to switch off
sim800.println(“AT+CALS=19”); // set alert/ring tone
simReply();
sim800.println(“ATS0=1”); // automatically answer call after 1 ring
simReply();
sim800.println(“AT+DDET=1,1000,0,0”); // activate DMTF decoding
simReply();
return true;
}
else
{
Serial.println(F(“sim800 module not found, stop here”));
return false;
}
}
//////////////////////////////////////////////////////////////////////////////////////////
void simReply() { // SIM REPLY
delay(500);
while (sim800.available()) {
char c = sim800.read();
if (c != ‘\n’) Serial.write(c); // replace new line with space
else Serial.print(” “);
delay(5);
}
Serial.println();
}
//////////////////////////////////////////////////////////////////////////////////////////
void simFlush() {
while (sim800.available()) sim800.read();
}
//////////////////////////////////////////////////////////////////////////////////////////
int checkStatus() { // CHECK STATUS FOR RINGING or IN CALL
sim800.println(“AT+CPAS”); // phone activity status: 0= ready, 2= unknown, 3= ringing, 4= in call
delay(100);
if (sim800.find(“+CPAS: “)) { // decode reply
char c = sim800.read(); // gives ascii code for status number
// simReply(); // should give ‘OK’ in Serial Monitor
return c – 48; // return integer value of ascii code
}
else return -1;
}
//////////////////////////////////////////////////////////////////////////////////////////
boolean checkNetwork() { // CHECK REGISTERED OR NOT
// and OPERATOR and SIGNAL STRENGTH
Serial.print(F(“signal strength:”));
sim800.println(“AT+CSQ”); // SIGNAL STRENGTH
delay(100);
if (sim800.find(“:”)) { // decode reply
signal_level = sim800.parseInt();
}
Serial.println(signal_level);
simFlush();
sim800.println(“AT+CREG?”); // possible replies: 0,0=not registered 0,1=registered on home network 0,2=not registered but searching 0,3=denied 0,4=unknown 0,5=roaming
delay(100);
// simReply(); // TESTING
if (sim800.find(“0,1”)) {
// Serial.println(F(“network OK”));
digitalWrite(green, HIGH);
checkOperator();
return true;
}
else {
Serial.println(F(“no network connection..”));
digitalWrite(green, LOW); // 1x blink green LED
delay(250);
digitalWrite(green, HIGH);
delay(250);
digitalWrite(green, LOW);
return false;
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void checkOperator() {
if (network_name[0] == ‘ ‘) {
Serial.print(F(“operator:”));
sim800.println(“AT+COPS?”); // OPERATOR
delay(100);
if (sim800.find(“\””)) { // find operator name between two double quotes
c = sim800.read();
int u = 0;
while (c != ‘”‘ && u < 10) {
network_name[u] = c;
c = sim800.read();
u++;
}
}
Serial.print(network_name);
}
}
/////////////////////////////////////////////////////////////////////////////////////////
void foreverRed() {
for (;;) { // blink red LED forever
digitalWrite(red, HIGH);
delay(100);
digitalWrite(red, LOW);
delay(100);
}
}
//////////////////////////////////////////////////////////////////////////////////////////
int checkDTMF() {
if (sim800.find(“+DTMF:”)) {
return sim800.parseInt();
simFlush();
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////// SCREENS ////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////
void drawIntro() {
u8g.firstPage(); // intro splash
do {
u8g.setFont(u8g_font_fub14r);
u8g.drawStr(2, 30, “R/C PHONE”);
u8g.drawStr(2, 55, “BuffaloLabs”);
} while ( u8g.nextPage() );
}
void drawNoSim() {
u8g.firstPage(); // no SIM
do {
u8g.setFont(u8g_font_fub14r);
u8g.drawStr(2, 30, “SIM card”);
u8g.drawStr(2, 55, “not found”);
} while ( u8g.nextPage() );
}
void drawWaiting() {
u8g.firstPage(); // waiting
do {
u8g.setFont(u8g_font_7x14B);
u8g.setPrintPos(2, 10);
u8g.print(“network:”);
u8g.print(network_name);
u8g.setPrintPos(2, 25);
u8g.print(“signal:”);
u8g.print(map(signal_level, 0, 31, 0, 100));
u8g.print(“%”);
u8g.setPrintPos(2, 45);
u8g.print(“waiting for call”);
} while ( u8g.nextPage() );
}
void drawCallActive() {
u8g.firstPage(); // in call
do {
u8g.setFont(u8g_font_fub14r);
u8g.drawStr(2, 20, “in call”);
} while ( u8g.nextPage() );
}
void drawDTMFreceived(int d) {
u8g.firstPage(); // got DTMF
do {
u8g.setFont(u8g_font_fub14r);
u8g.drawStr(2, 20, “in call”);
u8g.setPrintPos(2, 40);
u8g.print(“DTMF: “);
u8g.print(d);
} while ( u8g.nextPage() );
}
void beep() {
tone(buzzer, 500);
delay(200);
noTone(buzzer);
}
Hi!
Thanks a lot for your code, I’m working on a SIM800 with a raspberry and this was very helpful.
I just don’t understand this command:
“AT+DDET=1,1000,0,0”
The datasheet says that there is only one agument:
“disable or enable DTMF detection control
0 disable
1 enable”
I dont understand what the 3 other arguments do…
Hi Max, thanks for your feedback. You are probably looking at an older datasheet; if you check https://cdn-shop.adafruit.com/product-files/2637/SIM800+Series_AT+Command+Manual_V1.09.pdf you will find (p333) the function has 4 arguments, adding an interval, report mode, and key.
Good luck with it- Tom
Ah thanks, that better!
I was looking at the 1.01 Version…
Pingback:GoTo Goat – A GPS goat tracker – Framtida bruk
how to use AT+CPAS command to findout phone activity in simple arduino code.. can you please help me syntax that how to find the phone status..
Hi, you can check my code at the bottom of https://cassiopeia.hk/arduinokidsphone/
I want to connect 4 relays…can you give me a code please
Hi, that should be as easy as adjusting a few lines, if you’re new to Arduino, have a look at my introduction slides at https://www.slideshare.net/tomtobback/cassiopeia-ltd-standard-arduino-workshop
The whole program is awsome but can u please help me out of getting even the character’s like * and # also … please help me out . thanks in advance !
Hi, sorry i’m aware of the mess WordPress makes out of copy/paste code, i don’t have a way to avoid it at the moment. please try to debug it; that will also help to understand the code 😉
Hi TOM this is simply great project…I am gonna try this. I will let you know about the progress.
Dear Colleague ,
Really well done of proof of concept. Only problem is how you can enjoy drinking coffee from your remote coffee machine?
Bottom line i liked it the idea. I just working on that in order to learn DTMF commands.
Thank you
Can you control the DOL starter of 3 phase motor using dtmf, please tell me how to program the gsm starter using sim 800l and Arduino nano
After much searching I found and liked your website
yes that should be possible, using a 3phase contactor. from a quick google search it seems most people use an Arduino-style relay to switch the contactor, as an Arduino pin cannot deliver enough current to move the contactor.
Sir,
If I wanted to do a project like the one below, I need your help
1. Make a call to the device, press “1”, LED 1 lights up. An SMS is sent from your device to your phone, that the command has been completed.
2. The same with LED 2.
3. Pressing “1” or “2” again will turn off the LEDs and an SMS message will also be sent.
4. And so on.
yes that’s all possible, you just have to combine a few sketches
Thanks for sharing the code I too am working on similar project.
Can we make this whole thing password protected also password could be change using dtmf. If so please help.
Hi thanks for this great tutorial, I’ve a question, when i tried to make a normal call between 2 cellphones and testing DTMF tones I found no tones sent or received as DTMF signals sent digitally with no tones and this caused by operator network not a devices issue as I tried many phones and tested with IVR all working well.
Now Will do this SIM800 or DTMF decoder MT8870 modules work properly with that DTMF digital signals while no tones transmitted?
sorry i have no idea