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

IMG_20160228_123938496 (1)

Now i can switch on my coffee machine with a simple phone call from anywhere in the world!

IMG_20160228_124335888

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);
}

 

11 thoughts on “Remote control by Arduino phone DTMF (with SIM800)

  • 25/05/2016 at 17:40
    Permalink

    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…

    Reply
  • 26/05/2016 at 20:46
    Permalink

    Ah thanks, that better!
    I was looking at the 1.01 Version…

    Reply
  • Pingback: GoTo Goat – A GPS goat tracker – Framtida bruk

  • 28/01/2017 at 20:23
    Permalink

    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..

    Reply
  • 28/06/2017 at 01:43
    Permalink

    I want to connect 4 relays…can you give me a code please

    Reply
  • 12/07/2017 at 01:05
    Permalink

    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 !

    Reply
    • 17/09/2017 at 20:25
      Permalink

      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 😉

      Reply
  • 15/09/2017 at 18:54
    Permalink

    Hi TOM this is simply great project…I am gonna try this. I will let you know about the progress.

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *