Sending SMS Alerts with a SIM800L

Draft. This tutorial has not been checked against the physical product yet. Confirm wiring, pin assignments and voltage limits against the product documentation before building it.
DifficultyAdvanced
CategoryCommunication
Supported boardsArduino Uno R3 and compatible ATmega328P boards
Time needed80 minutes
PrerequisitesRegulators, Batteries and Solar
Tested boardNot yet verified
Tested onNot yet verified
Arduino IDE versionNot yet verified
LibraryNot yet verified

What it does

A mobile phone module. Put a SIM in it and an Arduino can send a text message from anywhere with coverage, with no Wi-Fi, no router and no internet.

For a water tank in a village, a generator in a yard, or a cold store that must not fail unnoticed, that is a capability nothing else in this library has. An alert arrives on a phone that is already in someone’s pocket.

It is also the module most likely to arrive, be wired up correctly, and do nothing at all. There are two reasons for that, and both come before any code.

Read this first

1. The power supply, which is where most of these fail

This is the single most common reason a SIM800L appears faulty.

It needs 3.4 to 4.4 volts. Not 5. An Arduino’s 5V pin is above the maximum and can damage it; the 3.3V pin is below the minimum and it will not register on the network.

And it draws up to 2 amps in bursts while transmitting. Not continuously: in short peaks each time it talks to the tower, especially while first registering.

So the supply has to do two things at once: sit in a narrow voltage window, and deliver a 2 A burst without sagging. An Arduino’s regulator can do neither.

What works:

  • A dedicated buck converter set to about 4 V, fed from a supply that can give 2 A.
  • A single lithium cell, which is 3.0 to 4.2 V and happily supplies the burst. This is the neatest answer and why so many SIM800L projects are battery powered.
  • Four NiMH cells at 4.8 V is slightly too high; three at 3.6 V is slightly low but often works.

Add a large capacitor across the module’s supply pins, 1000 µF or more. Same reasoning as the NRF24L01’s 10 µF, scaled up: it supplies the peak so the rail does not dip.

The symptom of getting this wrong is specific and worth recognising: the module powers up, the LED blinks, and it never registers on the network, or it resets every time it tries to send.

2. 2G, which may not exist where you are

The SIM800L is a 2G-only module. It cannot use 3G, 4G or 5G.

Many operators around the world have switched their 2G networks off, and others have announced dates. Before buying this module, check that your operator still runs 2G in the place the project will live. If it does not, the module is unusable there and no amount of wiring will change that.

Where 2G is still running, as it is across much of West Africa, the module works well and its coverage is often better than data coverage.

3. The SIM, and its logic level

  • The SIM holder takes a micro SIM, in a slot on the underside.
  • Disable the PIN on the SIM first, using a phone. A module cannot answer a PIN prompt, and a locked SIM simply never registers.
  • Make sure there is credit.
  • The module’s logic is 2.8 V. That is below the Uno’s 3.0 V HIGH threshold, so the module’s TXD may be read unreliably, and the Uno’s 5 V TXD is too much for the module’s RXD.

So: a divider on the Arduino’s transmit line is required, and if the module’s replies come back garbled, a proper level converter is the fix. A 3.3 V board such as an ESP32 is a better partner for this module than an Uno.

Before you begin

Required parts and tools

  • An Arduino Uno or compatible board.
  • One SIM800L module. This is Siratek product 2671, supplied with a helical antenna.
  • A 4 V supply capable of 2 A, and a 1000 µF capacitor.
  • A micro SIM with credit and no PIN.
  • Resistors for a divider, or a level converter.
  • A multimeter.

The antenna

The module comes with a small helical spring antenna and has a u.FL connector for a better one.

Fit the supplied antenna before powering up. A GSM module transmitting into no antenna can damage its output stage, and will certainly not register.

Indoors, near a window is better than the middle of a room. If registration is unreliable, an external antenna on the u.FL connector makes more difference than anything else.

Reading the status LED

The LED tells you the state, and knowing the pattern saves hours:

  • Blinking about once a second: searching for a network.
  • Blinking about once every three seconds: registered. This is what you want.
  • Rapid blinking: usually GPRS activity.
  • Nothing: no power, or the supply is out of range.

Do not send a single AT command until the LED is on the slow three-second blink. Everything before that will fail, and it will look like a wiring problem.

Pin reference

Module pin Connect to Purpose Notes
VCC 4 V supply, 2 A capable Power Never the Arduino’s 5V
GND Supply negative and Uno GND Common ground Both
SIM_TXD Uno D10 Module out 2.8 V; marginal for an Uno
SIM_RXD Uno D11 through a divider Module in 2.8 V input
RST Not used here Reset
A SIM800L powered from a separate 4 volt supply with a large capacitor across its supply pins, its grounds joined to the Arduino, and a divider on its receive line
4 volts, 2 amps of burst, and a big capacitor. The Arduino’s 5V pin can supply none of that.

Wiring steps

  1. Unplug everything.
  2. Fit the antenna and insert the SIM.
  3. Set your buck converter to 4.0 V and confirm it with a meter before connecting the module.
  4. Supply positive to VCC, supply negative to GND.
  5. Fit the 1000 µF capacitor across VCC and GND, at the module. Watch the polarity.
  6. Join the supply’s ground to the Uno’s GND.
  7. Module SIM_TXD to D10.
  8. From D11, a 1 k resistor to a free row, and a 2 k from that row to ground. Module SIM_RXD to that row.
  9. Power up and watch the LED before doing anything else.

Software and libraries

No library is required, and that is deliberate. The module speaks AT commands: plain text lines you send and plain text replies you read. Seeing that directly is more useful than hiding it, because every failure shows up as a reply you can read.

SoftwareSerial comes with the IDE.

Minimal working example

Talk to it by hand first. Nothing else is worth trying until this works.

// Siratek: a pass-through, so you can type AT commands yourself.

#include <SoftwareSerial.h>

SoftwareSerial sim(10, 11);      // Arduino RX, Arduino TX

void setup() {
  Serial.begin(9600);
  sim.begin(9600);
  Serial.println("Siratek: wait for the slow blink, then type AT and press Enter.");
}

void loop() {
  if (sim.available()) {
    Serial.write(sim.read());
  }
  if (Serial.available()) {
    sim.write(Serial.read());
  }
}

Set the serial monitor’s line ending to Both NL & CR, or the module will ignore everything you type.

Then work through these, in order. Each one tells you something:

Type Expect Means
AT OK The module is listening
AT+CPIN? +CPIN: READY The SIM is in and unlocked
AT+CSQ +CSQ: 15,0 Signal. Under 10 is poor, over 20 is good
AT+CREG? +CREG: 0,1 or 0,5 Registered. 0,2 means still searching
AT+COPS? Your operator’s name It found a network

If AT gives nothing, the problem is power, wiring or baud rate, in that order. If it gives garbled characters, it is the logic levels or the baud rate.

Do not go further until AT+CREG? returns 1 or 5. Everything after this depends on it.

Sending a message

// Siratek: send one SMS, with the replies checked rather than assumed.

#include <SoftwareSerial.h>

SoftwareSerial sim(10, 11);

// International format, including the country code.
const char* recipient = "+233XXXXXXXXX";

bool waitFor(const char* expected, unsigned long timeout) {
  unsigned long start = millis();
  String buffer = "";

  while (millis() - start < timeout) {
    while (sim.available()) {
      buffer += (char) sim.read();
      if (buffer.indexOf(expected) >= 0) {
        return true;
      }
      if (buffer.indexOf("ERROR") >= 0) {
        Serial.println("  module replied ERROR");
        return false;
      }
    }
  }

  Serial.print("  timed out waiting for ");
  Serial.println(expected);
  return false;
}

bool sendSms(const char* number, const char* text) {
  sim.println("AT+CMGF=1");                 // text mode, not PDU
  if (!waitFor("OK", 2000)) {
    return false;
  }

  sim.print("AT+CMGS=\"");
  sim.print(number);
  sim.println("\"");
  if (!waitFor(">", 5000)) {                // the module asks for the text
    return false;
  }

  sim.print(text);
  sim.write(26);                            // Ctrl-Z ends the message

  // Sending can genuinely take this long.
  return waitFor("+CMGS", 30000);
}

void setup() {
  Serial.begin(9600);
  sim.begin(9600);

  Serial.println("Siratek: waiting 20 seconds for the network.");
  delay(20000);

  if (sendSms(recipient, "Siratek: tank level low.")) {
    Serial.println("sent");
  } else {
    Serial.println("failed");
  }
}

void loop() {
}

Four things in there matter more than the rest.

Every step waits for the expected reply. The common version of this sketch is a series of println() calls with delay(1000) between them, which works on the bench and fails in the field with no indication of where. Waiting for OK, then >, then +CMGS means a failure tells you which step failed.

The > prompt. After AT+CMGS the module replies with a bare > and waits for the message text. Sending the text before that prompt arrives loses it.

Ctrl-Z, which is byte 26, ends the message. Nothing is sent until that byte arrives.

Thirty seconds for the final reply. Sending an SMS genuinely takes that long sometimes, especially on a weak signal. A two-second timeout here reports failure on messages that are actually sent.

An alert that does not flood you

A sensor on a threshold can send fifty messages in an hour, which costs money and teaches the recipient to ignore them.

const unsigned long minimumGap = 3600000UL;   // one hour
unsigned long lastAlert = 0;
bool alerting = false;

void checkAndAlert(bool condition, const char* text) {
  if (condition && !alerting) {
    if (millis() - lastAlert > minimumGap || lastAlert == 0) {
      alerting = true;
      lastAlert = millis();
      sendSms(recipient, text);
    }
  } else if (!condition) {
    alerting = false;          // armed again, but still rate-limited
  }
}

One message per event, and no more than one an hour whatever happens. For anything that costs money per message, this is not optional.

Expected result

The LED settling to a slow three-second blink within a minute of power-up. AT returning OK. AT+CSQ returning a signal figure above 10. Then a text message arriving on the recipient’s phone within a few seconds.

Troubleshooting

Symptom Likely cause Check or fix
LED blinks fast and never slows Power, almost always 4 V, 2 A capable, with a big capacitor
LED does nothing Supply out of range Measure it: 3.4 to 4.4 V
Module resets when sending Supply sags under the burst Bigger capacitor; better supply
AT returns nothing Power, wiring or baud Check in that order
AT returns garbled text Logic levels, or baud rate Fit the divider; try 9600
+CPIN: SIM PIN SIM is locked Disable the PIN with a phone
+CREG: 0,2 for ever Searching, no coverage Antenna; move near a window; check 2G exists
+CSQ: 99,99 No signal at all Antenna not fitted, or no coverage
AT+CMGS returns ERROR Not registered, or no credit Check AT+CREG? first
Message never arrives Number not in international format +233..., including the country code
Worked once, then stopped Out of credit Check the SIM
Nothing works anywhere Operator has switched 2G off This module cannot be made to work

Next steps

  • Easier first: Regulators, Batteries and Solar, which is what this module’s supply needs.
  • Related: Sending Sensor Data with MQTT, where Wi-Fi exists and is cheaper per message.
  • Next project: a tank-level alarm that sends one text when the level drops and no more than one an hour.

Sources and attribution

All text, wiring guidance and code in this tutorial are original Siratek work.

  • SIM800L documentation for the supply voltage range, the peak current during transmission, the 2.8 V logic level and the status LED patterns.
  • The AT commands used are from the standard GSM AT command set.
  • The helical antenna, the u.FL connector, the micro SIM holder and the loose header strip were read from the Siratek product photographs for item 2671.

Whether 2G service exists in a given place is an operator matter that changes over time, and the reader is told to check rather than given an answer. Current and voltage figures are from the module documentation. No third-party tutorial text, diagrams, photographs or code has been reproduced here.

Product used in this tutorial: SIM800L GSM GPRS Module
Shop
Sidebar
0 Wishlist
0 Cart