Build an ESP32 GSM Calling Device with Auto Answer Using GeoLinker

Published  June 3, 2026   0
User Avatar Anand D
Author
ESP32 GSM Calling Device using GeoLinker GL868

In this project, we build a complete ESP32 GSM voice calling device using the GeoLinker GL868 board. The main idea of this project is very simple. When we press a push button connected to the ESP32 board, it will automatically call a fixed phone number. Also, if someone calls the SIM card inside the module, the call will be automatically attended, and we can talk through the connected microphone and speaker.

For this project, we are using the GeoLinker GL868 board because it already has an ESP32 controller and a SIM868 GSM module on the same board. This makes it much easier since we do not need to separately connect a GSM module and ESP32. GL868 can be useful for making simple communication systems, emergency calling devices, wireless intercom systems, security systems and many other GSM-based applications. The GeoLinker GL868  development board has a well-documented Wiki Page where you can find out the whole functionalities and specs of the board.

Quick Overview

Parameters Used                         Details
Main BoardGeoLinker GL868 (ESP32-S3 + SIM868)
GSM ModuleSIM868 (2G GSM/GPRS + GPS)
TriggerPush button on GPIO 4 
Audio Output4Ω Speaker on SP+ / SP– pins (max 1 W at 8Ω)
Audio InputCondenser microphone on M+ / M– pins
Power Supply3.7V Li-ion battery via JST connector
SIM Type2G GSM SIM with active voice plan
Auto-AnswerYes - detects RING, sends ATA command

Introduction to GeoLinker GL868

The GeoLinker GL868_ESP32 is a production-ready, open-source development board that combines an ESP32-S3 and SIM868 GSM modem, with GPS Tracking, motion detection, battery management, wireless connectivity (Bluetooth/Wifi) and cellular monitoring (GPRS/SMS/Calls) on a compact PCB. It targets asset tracking, vehicle monitoring, anti-theft systems, and any IoT application that needs cellular monitoring or control, along with location tracking. This kind of GSM-based communication system project is highly practical and can serve as the foundation for a GSM-based emergency calling device for elderly care, a wireless calling device using GSM for industrial alerts, or a GSM auto-dialer for alarm systems.

The board ships with an Airtel M2M IoT SIM card (optional) and readily supports the GeoLinker GPS tracking dashboard, allowing you to track and visualise data on a map right out of the box for quick prototyping. For further development, you can use the open-source GL868_ESP32 Arduino library, which provides a high-level, non-blocking API for GPS tracking, HTTP data upload, SMS command processing, voice call handling, and deep-sleep power management. To give you a further understanding of the Geolinker GL868 Development Board, here is another tutorial on how we can use GL868 to make an IVR System.

How the ESP32 GSM Calling System Works

The working of this project is very easy. We have a button connected to GPIO 4 of GL868. Once this button is pressed, the if condition in loop() turns true, and it sends AT commands to place a call to the configured number. The SIM868 GSM Modem takes care of the call and gives audio output through the SP+ and SP- pins to which we have connected our 4Ω speaker. The condenser mic is connected to the M+ and M- pins, which give audio input to the modem.

For incoming calls, the Modem sends a message “RING” to ESP32, which keeps on repeating until the call is automatically answered by the AT command sent by ESP32 to the Modem. The call goes on until disconnected from the other end. Both incoming and outgoing calls function normally as we call normally using cell phones. We can add multiple buttons as well, which point the call to different end points/users.

This tutorial introduces you to the SIM868 GSM Modem. If you like to get started with another version, do check-out our GSM based calling and texting project using SIM900A. If you need to make use of a Raspberry Pi in your project while taking advantage of a GSM Module, here is our Call and Text using Raspberry Pi and GSM Module project, where we showcased how a GSM module can be integrated with a RPI4.

Circuit Diagram

Below is the detailed circuit diagram for this project.

 

Circuit Diagram ESP32 GSM Calling Device

Circuit Connections at a Glance

The circuit diagram is very simple. We have a small speaker connected to the SP+ and SP- pins of the board. The condenser mic is connected to the M+ and M- pins of the board. The push button is connected to the pulled-up GPIO 4 of the board. When the button is pressed. It connects GPIO 4 to GND, and it places the call to the assigned number. The antennas are GSM and GPS antennas are kept in an upward position. The speaker pins give an output of max 1Watt with 8Ω impedance. Here in this tutorial, I’ve used a mini enclosed speaker with 4Ω impedance. The 3.7V lithium battery is connected to the board using the JST connector provided on the backside of the board.

 

Hardware setup of the ESP32 GSM calling device inside a 3D-printed enclosure showing the GeoLinker GL868 board, speaker, condenser mic, and push button

Above is the real hardware setup of the project, kept in a 3D-Printed enclosure. The enclosure is designed to properly keep the battery and wires in a well-organised way.

SIM868 AT Commands Used in This Project

The SIM868 modem is controlled by the ESP32 through Serial2 using standard Hayes AT commands, which allows for communication between the two devices. A complete list of the AT commands that are being utilised in this project is provided in the table below. It is recommended that you use this table as a reference for all your GSM voice communication projects using the ESP32.

AT Command                               Purpose
AT+CHFA=1Select audio channel 1 (external speaker/mic on GPIO pins)
AT+CLVL=90Set speaker/loudspeaker volume level
AT+CMIC=0,12Set microphone gain for channel 0
AT+FMMUTE=0 Unmute the audio path
AT+SIDET=1Enable sidetone (hear your own voice feedback)
ATD+[number];Dial a voice call (semicolon ensures voice, not data)
ATAAnswer an incoming call (auto-answer on RING detection)
ATHHang up / end the current call

Components Required

Below is the list of components required to build this project with their description.

S NoComponentDescription
1.GeoLinker GL868 dev boardThe main controller for the project
2.4Ω SpeakerTo get the audio output
3.Condenser micTo give audio input to the device
4.3.7 V lithium batteryTo power the whole project
5.Push ButtonTo trigger a call on button press
6.SIM Card2G SIM card for connectivity
7.Connecting wiresTo make the whole circuit

Code Explanation

Below is a detailed breakdown of the whole code in detail, so you can learn the functionalities this code gives to our project.

Configuration Section

#define DEVICE_ID "GL868_ESP32"
#define API_KEY "cd_har_270526_LUOh63"
#define BUTTON_PIN 4
const char* phoneNumber = "+919744041489";

DEVICE_ID & API_KEY: Credentials to connect to the GeoLinker cloud service. You can give the DEVICE_ID a name that you can easily identify. Here, I have given GL868_ESP32.
For API_KEY, you need to go to CircuitDigest Cloud > Login/Create an Account > API Keys > Copy the API Key displayed in the dashboard and paste it here
BUTTON_PIN 4: GPIO pin where the push button is connected (with a pull-up resistor)
Phone Number: The emergency number that will be dialled when the button is pressed

Global Variables

bool lastButtonState = HIGH;
bool callActive = false;
bool audioConfigured = false;

lastButtonState: Tracks the previous button state to detect button press.
callActive: Tracks whether a call is currently in progress.
AudioConfigured: Prevents the audio setup commands from running repeatedly during an active call (prevents endless command loop). This helps in preventing broken audio outputs.

Setup Function

void setup() {
 Serial.begin(115200);
 pinMode(BUTTON_PIN, INPUT_PULLUP);
 Serial.println("GL868 Voice System");
 // Initialize library
 GeoLinker.begin(DEVICE_ID, API_KEY);
 // Power modem
 Serial.println("Powering modem...");
 if (!GeoLinker.modem.powerOn()) {
   Serial.println("Modem power failed!");
   while (1) delay(1000);
 }
 Serial.println("Waiting for network...");
 while (!GeoLinker.gsm.waitNetworkRegistration(10000)) {
   Serial.println("Searching network...");
 }
 Serial.println("Network connected");
 // Global Audio Setup
 Serial2.println("AT+CHFA=1");
 delay(100);
 Serial2.println("AT+CLVL=90");
 delay(100);
 Serial2.println("AT+CMIC=0,12");
 delay(100);
 Serial2.println("AT+FMMUTE=0");
 delay(100);
 Serial2.println("AT+SIDET=1");
 delay(100);
 Serial.println("Ready");
}

This section starts with Serial at 115200 baud rate. Then it connects to the GeoLinker cloud with device credentials, powers on the GSM modem, waits up to 10 seconds for cellular network registration and configures the audio path for voice calls (speaker + mic).

Loop Function

void loop() {
 // ======================================
 // BUTTON PRESS -> VOICE CALL
 // ======================================
 bool currentState = digitalRead(BUTTON_PIN);
 if (lastButtonState == HIGH && currentState == LOW && !callActive) {
   delay(50);
   if (digitalRead(BUTTON_PIN) == LOW) {
     Serial.println("Executing Voice Dial Sequence...");
     Serial2.println("AT+CHFA=1");
     delay(100);
     Serial2.println("AT+CLVL=90");
     delay(100);
     Serial2.println("AT+CMIC=0,12");
     delay(100);
     Serial2.println("AT+FMMUTE=0");
     delay(100);
     // Start voice call
     Serial2.print("ATD");
     Serial2.print(phoneNumber);
     Serial2.println(";");
     Serial.print("Dialing: ATD");
     Serial.print(phoneNumber);
     Serial.println(";");
     callActive = true;
     audioConfigured = false; // Reset for the new outgoing call
   }
 }
 lastButtonState = currentState;

The loop starts by detecting button presses from High to Low and checks if there is a call currently in progress. It then sends the standard GSM AT commands to the GSM modem to dial a number.

Modem Response Handling

while (Serial2.available()) {
   String response = Serial2.readStringUntil('\n');
   response.trim();
   // ... process responses
}

Read AT command responses from the GSM modem.

Incoming Call Detection:

if (response.indexOf("RING") >= 0) {
   Serial2.println("ATA");  // Answer the call
   callActive = true;
   audioConfigured = false;
}

When the modem sends "RING", it auto-answers with the ATA command.

Call Connected - Audio Setup

if (callActive && !audioConfigured && 
   (response.indexOf("MO CONNECTED") >= 0 || response.indexOf("CONNECT") >= 0)) {
   audioConfigured = true;  // Lock to prevent re-running
   // Re-apply audio AT commands
}

Run once when the call connects (prevents command spam). Re-applies audio configuration after call is established -  this helps to prevent broken audio, as if we check call connected multiple times in a loop, the audio output will be broken.

Call Disconnected

if (response.indexOf("NO CARRIER") >= 0 ||
   response.indexOf("BUSY") >= 0 ||
   response.indexOf("NO ANSWER") >= 0) {
   callActive = false;
   audioConfigured = false;
}

Resets flags when call ends (any reason: disconnect, busy, no answer)

GeoLinker Cloud Update

if (!callActive) {
   GeoLinker.update();
}

Only runs cloud updates when no call is active (prevents interference with voice data)

Live Project Showcase: GSM Communication and GPS Monitoring

Watch the complete working demonstration of the ESP32 GSM voice calling device below, including an outgoing call triggered by the push button and an incoming call with automatic answering:


Troubleshooting Guide

Problem          Likely Cause                                         Solution
Outgoing call fails, but incoming call worksInsufficient current during dial - modem resetsUse a fully charged 3.7V Li-ion battery via JST. Do not rely on USB Type-C power alone for outgoing calls.
Button press does not trigger a callWrong GPIO pin configured in codeVerify the button is wired to GPIO 4 and that BUTTON_PIN 4 matches in the sketch. Use a multimeter to confirm the pin reads LOW when pressed.
Serial Monitor shows nothingWrong baud rate or USB CDC On Boot disabledSet Serial Monitor to 115200 baud. In Arduino IDE → Tools, enable USB CDC On Boot for the ESP32-S3 board.
Call drops within seconds of connectingWeak 2G signal or insufficient SIM creditCheck the 2G signal strength with AT+CSQ (17+ is good). Top up voice call credits on the SIM card.
Audio is broken, choppy, or silentAudio AT commands running in a loop during callEnsure the audioConfigured flag is checked so commands run only once per call connection. Re-verify speaker/mic wiring to SP+/SP– and M+/M–.
Modem does not power onNo battery connected or flat batteryConnect and charge the 3.7V Li-ion via the JST connector before powering on the board.
Network registration stuck at "Searching"SIM not 2G-compatible, or no 2G coverage in areaConfirm the SIM supports 2G GSM (SIM868 does not support 4G/LTE). Move to an area with 2G coverage or try a different carrier.

Conclusion

In this project, we successfully built a complete ESP32 GSM voice calling device and GSM auto-answer system using the GeoLinker GL868 development board.  With just a push button, the device can automatically call a predefined phone number, while incoming calls are answered automatically, which can be edited in the code. We learned how to interface an external speaker and mic to the Geolinked GL868 development board.

Since both ESP32 and the SIM868 were available on the same board, we didn’t have to spend too much time wiring up the whole circuit. This project can be further expanded into an emergency calling device, a GSM-based emergency calling device for elderly people, a wireless intercom,  a GSM auto-dialer for alarm systems, or a cellular alert unit for industrial automation. It also serves as a good starting point for learning GSM voice communication using ESP32. If you enjoyed this project, you can explore more interesting ESP32-based builds and tutorials from our collection of ESP32 projects. If you are interested in exploring more IoT based projects, we have a dedicated collection of IOT projects that make use of a wide range of sensors and microcontrollers.

ESP32 GSM Calling Device GitHub Repository

This GitHub repository contains the source code and documentation for an ESP32-based GSM calling device capable of making and managing phone calls through a GSM module. It serves as a practical reference for implementing wireless communication and telephony features in IoT applications.

ESP32 GSM calling device code and schematicsESP32 GSM Calling Device Download Zip

Frequently Asked Questions

⇥ Do I need to disconnect the battery and use external charging modules?
The GL868 has an inbuilt battery charger. So there is no need to charge the battery externally. Just plug in the Type-C; it will charge the battery.

⇥ Can I change the destination number?
Absolutely, the destination number can be changed by updating the variable “phoneNumber”

⇥ How many buttons can I add?
You can add multiple buttons by defining more GPIO pins and adding similar press detection logic for each. Each button can dial different numbers.

⇥ What happens if I press the button during an active call?
Nothing happens, as the code checks for !callActive and the button press is ignored during a call to prevent accidental redials.

⇥ What is an ESP32-based GSM Calling Device?
An ESP32-based GSM calling device is a combination of an ESP32 microcontroller and a GSM modem (like the SIM868). The ESP32 sends AT commands to the GSM modem to attempt to make phone calls or to auto-answer incoming calls. Therefore, a GSM-based calling device will work well for applications that need to send emergency alerts or for intercom-type applications.

⇥ How does the auto-answer feature work in a GSM Auto-Answer System?
When the SIM868 detects an incoming call, it sends a RING string back to the ESP32 (over UART) in a repeated manner. The ESP32 firmware continuously listens for this RING response and immediately sends the ATA (Answer) AT command to the GSM modem to answer the call without any user interaction. This way, the setup of the two-way audio (being able to hear and be heard) occurs automatically as soon as the GSM modem receives an incoming phone call.

⇥ What is the GeoLinker GL868 development board and why is it used?
The GeoLinker GL868 development board is an open-source, production-ready development board that incorporates an ESP32-S3 microcontroller and a SIM868 GSM/GPS modem into one PCB. It is designed to eliminate the complicated wiring between the ESP32 microcontroller and the SIM868 GSM/GPS modem. Also, the GeoLinker GL868 Development Board ships with an optional Airtel M2M SIM for use with the board and includes an easy-to-use Arduino library for making and receiving voice calls, SMS, GPS, and uploading to cloud data.

IoT-Based GSM Communication Projects

These projects demonstrate GSM-based calling, messaging, and GPS location tracking using Arduino and NodeMCU. It enables real-time communication, alert notifications, and remote monitoring for IoT and security applications.

 Call and Message using Arduino and GSM Module

Call and Message using Arduino and GSM Module

So here we are going to build a Simple Mobile Phone using Arduino, in which a GSM Module is used to make calls, answer calls, send SMS, and read SMS, and also this Arduino phone has a mic and Speaker to talk over this Phone. This project will also serve as a proper interface of the GSM Module with Arduino, with all the Code needed to operate any Phone’s basic functions.

How to Send SMS from NodeMCU without Using GSM Module?

How to Send SMS from NodeMCU without Using GSM Module?

By learning how to send SMS from NodeMCU, we can build interesting IoT projects, where we can easily monitor our sensors and also get device-critical SMS like battery low, device tampering alert, etc. 

Arduino Location Tracker using SIM800L GSM Module and NEO-6M GPS Module

Arduino Location Tracker using SIM800L GSM Module and NEO-6M GPS Module

This comprehensive project shows you how to create a fully functional GPS tracking system using Arduino UNO R3, SIM800L GSM module, and NEO-6M GPS module, a perfect low-cost DIY combination for vehicle monitoring, asset protection, or personal safety applications

Complete Project Code

#include <GL868_ESP32.h>
// ======================================
// CONFIG
// ======================================
#define DEVICE_ID "your_device_id"
#define API_KEY "your_api_key"
#define BUTTON_PIN 4
const char* phoneNumber = "+91----------";
// ======================================
bool lastButtonState = HIGH;
bool callActive = false;
bool audioConfigured = false; // FIX: Prevents the endless command loop
void setup() {
 Serial.begin(115200);
 pinMode(BUTTON_PIN, INPUT_PULLUP);
 Serial.println("GL868 Voice System");
 // Initialize library
 GeoLinker.begin(DEVICE_ID, API_KEY);
 // Power modem
 Serial.println("Powering modem...");
 if (!GeoLinker.modem.powerOn()) {
   Serial.println("Modem power failed!");
   while (1) delay(1000);
 }
 Serial.println("Waiting for network...");
 while (!GeoLinker.gsm.waitNetworkRegistration(10000)) {
   Serial.println("Searching network...");
 }
 Serial.println("Network connected");
 // Global Audio Setup
 Serial2.println("AT+CHFA=1");
 delay(100);
 Serial2.println("AT+CLVL=90");
 delay(100);
 Serial2.println("AT+CMIC=0,12");
 delay(100);
 Serial2.println("AT+FMMUTE=0");
 delay(100);
 Serial2.println("AT+SIDET=1");
 delay(100);
 Serial.println("Ready");
}
void loop() {
 // ======================================
 // BUTTON PRESS -> VOICE CALL
 // ======================================
 bool currentState = digitalRead(BUTTON_PIN);
 if (lastButtonState == HIGH && currentState == LOW && !callActive) {
   delay(50);
   if (digitalRead(BUTTON_PIN) == LOW) {
     Serial.println("Executing Voice Dial Sequence...");
     Serial2.println("AT+CHFA=1");
     delay(100);
     Serial2.println("AT+CLVL=90");
     delay(100);
     Serial2.println("AT+CMIC=0,12");
     delay(100);
     Serial2.println("AT+FMMUTE=0");
     delay(100);
     // Start voice call
     Serial2.print("ATD");
     Serial2.print(phoneNumber);
     Serial2.println(";");
     Serial.print("Dialing: ATD");
     Serial.print(phoneNumber);
     Serial.println(";");
     callActive = true;
     audioConfigured = false; // Reset for the new outgoing call
   }
 } 
 lastButtonState = currentState;
 // ======================================
 // MODEM RESPONSE HANDLING
 // ======================================
 while (Serial2.available()) {
   String response = Serial2.readStringUntil('\n');
   response.trim();
   if (response.length() > 0) {
     Serial.println(response);
     // --- INCOMING CALL ---
     if (response.indexOf("RING") >= 0) {
       Serial.println("Incoming Call... Answering");
       delay(1000);
       Serial2.println("ATA");
       
       callActive = true;
       audioConfigured = false; // Reset for the incoming call
     }
     // --- OUTGOING/INCOMING CALL CONNECTED (RUNS ONLY ONCE) ---
     // We look explicitly for connection confirmations and ensure it hasn't run yet
     if (callActive && !audioConfigured && 
         (response.indexOf("MO CONNECTED") >= 0 || response.indexOf("CONNECT") >= 0)) {
       Serial.println("[Audio Patch] Call connected. Configuring audio routing ONCE.");
       audioConfigured = true; // Lock the block so it cannot loop
       delay(300);
       Serial2.println("AT+CHFA=1");
       delay(100);
       Serial2.println("AT+CLVL=90");
       delay(100);
       Serial2.println("AT+CMIC=0,12");
       delay(100);
       Serial2.println("AT+FMMUTE=0");
       delay(100);
       Serial2.println("AT+SIDET=1");
       delay(100);
     }
     // --- CALL TERMINATED ---
     if (response.indexOf("NO CARRIER") >= 0 ||
         response.indexOf("BUSY") >= 0 ||
         response.indexOf("NO ANSWER") >= 0) {
       Serial.println("Call Disconnected.");
       callActive = false;
       audioConfigured = false;
     }
   }
 }
 // Only run updates when idle
 if (!callActive) {
   GeoLinker.update();
 }
 delay(20);
}
Have any question related to this Article?

Add New Comment

Login to Comment Sign in with Google Log in with Facebook Sign in with GitHub