Community project

Wearable Distress SMS Alarm

Photo of Wearable Distress SMS Alarm
Generated with AI

Anton Myburgh

Published November 6, 2026

This wearable distress alarm uses an ESP32 microcontroller and 4G LTE modem to send emergency SMS alerts when a button is pressed. The device runs on a rechargeable 18650 battery with integrated charging and protection, boosted to 5V to power all electronics. A piezo buzzer and LED provide immediate audio and visual feedback while the SMS is being transmitted to a pre-configured helper's phone number.

The guide provides a complete parts list, wiring diagram, and step-by-step assembly instructions for building a portable emergency device. Firmware code is included and ready to customize with a recipient phone number. The project covers battery management, 4G modem initialization, button debouncing, and SMS transmission logic, making it suitable for personal safety applications in remote areas or situations where voice calls may not be possible.

Wiring diagram

Loading diagram…

Gather all the parts

QtyComponent
1

SIM7600G-H 4G LTE UART development board

The SIM-card radio board that sends the distress text through a South African mobile network.

1

18650 Li-ion Cell

Samsung INR18650-30Q, 3.6 V 3000 mAh, 15 A high-drain cell

18650 lithium-ion cell, nominal 3.7 V, ~2500 mAh. Common for higher-capacity portable / battery-bank style projects; needs a holder and protection / charger circuit.

1

TP4056 Li-Ion/LiPo charger module with protection

TP4056 single-cell Li-Ion/LiPo linear charger module, 5V USB input, 1A charge current (programmable). Common variants ship with DW01 protection. Pair with battery_lipo_storage for the cell.

1

30 W adjustable 3.7 V-to-5 V boost converter (DD03AJTA type)

This converter raises the battery voltage to a stable 5 V rail with enough headroom for cellular transmit bursts.

1

Push Button

Momentary push button switch

1

Piezo Buzzer

Small passive piezo buzzer (TDK PS1240) for beeps, tones and alerts. It has no built-in oscillator, so drive one lead with a 3-30 V square wave from a microcontroller pin using tone() or PWM and connect the other lead to ground. It is loudest around 4 kHz and works from about 2 to 10 kHz; driving both leads in antiphase roughly doubles the volume.

1

LED

red 5 mm

Standard 3mm/5mm through-hole LED. A current-limiting series resistor is added automatically.

1

Resistor

220 Ω, 1/4 W

Through-hole resistor (current-limiting in series with an LED)

1

Breadboard-friendly SPDT Slide Switch

Compact through-hole SPDT slide switch with three pins for direct breadboard insertion.

Assemble it in 6 steps

1. Gather the right hardware

Buy an ESP32 DevKit v1, a SIM7600G-H UART development board that explicitly states it has a SIM-card holder, 5 V input, and 3.3 V TTL UART pins, its LTE antenna, a genuine Samsung INR18650-30Q cell, a TP4056 charger module with protection, a 30 W adjustable boost converter, a momentary push button, passive piezo buzzer, red LED, 220 Ω resistor, small slide switch, USB charging lead, insulated wire, and a non-metal project box. Also get an activated South African nano-SIM with SMS airtime.

  • DIYElectronics lists a SIM7600G-H board; Netram lists TP4056 chargers; Electronic Pro lists a 30 W boost module; Robotics.org.za and NDE3D list related battery power boards. Check the actual item description before paying.
  • Choose a SIM7600G-H board with an external antenna connector and a clearly labelled 5V, GND, TXD, RXD, and PWRKEY header.
  • Do not buy a 2G-only SIM800L for this build: it is not the planned South African 4G design.
  • Do not use damaged, unbranded, or loose lithium-ion cells; a short circuit can cause a fire.

2. Make the protected battery section

With all power disconnected, place the 18650 cell in a suitable single-cell holder or connect it to the TP4056 only through its intended B+ and B- pads: cell positive to B+ (battery positive), and cell negative to B- (battery negative). Connect the TP4056 OUT+ to the centre COM pin of the slide switch (switched power), and connect TP4056 OUT- to boost_1 VIN- (ground). Connect slide-switch pin A to boost_1 VIN+ (switched battery power). Leave slide-switch pin B unused.

  • Keep the battery wires short and use thicker wire than the small signal wires.
  • The TP4056 USB input is for charging only; do not try to power the 4G board straight from its tiny USB connector.
  • Reversed B+ and B- can destroy the charger or heat the battery.
  • Never solder directly to an 18650 cell; use a holder or a cell with factory-fitted leads.

3. Set the 5 V output before connecting electronics

Turn the slide switch on with no ESP32 or SIM7600 connected. Use a multimeter between boost_1 VOUT+ and VOUT- and turn the small adjustment screw until it reads 5.0 V. Turn the switch off. Connect VOUT+ to the ESP32 5V/VIN pin and to SIM7600 5V (power). Connect VOUT- to an ESP32 GND pin and SIM7600 GND (ground).

  • Put the SIM7600 close to the boost converter and use short, thicker positive and ground wires so radio transmit bursts do not cause a reset.
  • Fit the supplied cellular antenna before turning on the SIM7600.
  • More than 5 V can damage the ESP32 or SIM7600 board.
  • Never run the SIM7600 without its antenna attached; its radio can be damaged or work very poorly.

4. Wire the button, sounder, and light

Connect panic_button GND to ESP32 GND (ground) and panic_button SIGNAL to GPIO32 (signal). Connect buzzer_1 SIG to GPIO25 (alarm tone) and buzzer_1 GND to ESP32 GND (ground). Put one resistor lead, led_resistor P1, into GPIO26 (light signal), and connect its other lead, P2, to the long LED leg, status_led ANODE (current-limited light positive). Connect the LED short leg, status_led GND, to ESP32 GND (ground).

  • The LED long leg is positive; the flat side beside the short leg marks the negative side.
  • Mount the button where it cannot be pressed accidentally inside a pocket; a recessed opening or flip cover helps.
  • Do not connect the LED directly to GPIO26; the 220 Ω resistor limits current and protects both the LED and the ESP32.

5. Wire the cellular data leads

Connect SIM7600 TXD to ESP32 GPIO16 (data from the cellular board), SIM7600 RXD to ESP32 GPIO17 (data to the cellular board), and SIM7600 PWRKEY to ESP32 GPIO27 (radio start signal). Keep the shared ground from step 3 connected.

  • TX and RX deliberately cross: a pin labelled TX sends data, so it goes to the other board's RX pin.
  • Only use a SIM7600 development board whose TXD and RXD are labelled 3.3 V TTL compatible; do not connect an unlevel-shifted 5 V serial output to the ESP32.
  • Do not swap 5V and GND; swapped power can permanently damage the cellular board.

6. Fit, test, and close the alarm

Insert the activated nano-SIM into the SIM7600 slot while power is off. Put the antenna where it is not pressed against the battery, then switch on and use a temporary open enclosure for the first test. The device needs the recipient phone number set in its firmware before its first real test. Once it sends a test text and the buzzer works, secure every board with standoffs or foam tape and close the non-metal enclosure without pinching wires.

  • Test with a real helper contact in a place with mobile signal. Hold the button for two seconds; a steady light means the modem accepted the text, while three flashes means it did not.
  • Charge only while supervised, on a non-flammable surface.
  • This is a personal alert device, not a replacement for calling emergency services. Mobile coverage, SIM activation, airtime, battery charge, and the recipient number all affect whether a text arrives.

Review all connections

1. Connections between "cell_1" and "ESP32"

Functioncell_1ESP32
power+V → TP4056 Li-Ion/LiPo charger module with protection B+EXT
groundGND → TP4056 Li-Ion/LiPo charger module with protection B-EXT

2. Connections between "charger_1" and "ESP32"

Functioncharger_1ESP32
powerIN+ → 5 V USB charging socket positiveEXT
groundIN- → 5 V USB charging socket negativeEXT
powerOUT+ → Breadboard-friendly SPDT Slide Switch COMEXT
groundOUT- → 30 W adjustable 3.7 V-to-5 V boost converter (DD03AJTA type) VIN-EXT

3. Connections between "power_switch" and "ESP32"

Functionpower_switchESP32
digitalA → 30 W adjustable 3.7 V-to-5 V boost converter (DD03AJTA type) VIN+EXT

4. Connections between "boost_1" and "ESP32"

Functionboost_1ESP32
powerVOUT+ → regulated 5 V power rail5V
groundVOUT- → common ground railGND

5. Connections between "sim7600_1" and "ESP32"

Functionsim7600_1ESP32
power5V → radio power from regulated 5 V5V
groundGND → radio ground returnGND
uartTXD → cellular-board data to ESP32GPIO 16
uartRXD → ESP32 data to cellular-boardGPIO 17
digitalPWRKEY → ESP32 holds this low to start the radioGPIO 27

6. Connections between "panic_button" and "ESP32"

Functionpanic_buttonESP32
groundGND → button groundGND
digitalSIGNAL → distress-button signalGPIO 32

7. Connections between "buzzer_1" and "ESP32"

Functionbuzzer_1ESP32
pwmSIG → alarm tone signalGPIO 25
groundGND → buzzer groundGND

8. Connections between "led_resistor" and "ESP32"

Functionled_resistorESP32
digitalP1 → status-light signal through current limiterGPIO 26
digitalP2 → LED ANODEEXT

9. Connections between "status_led" and "ESP32"

Functionstatus_ledESP32
groundGND → status-light groundGND

Deploy the firmware

#include <Arduino.h>

constexpr int MODEM_RX_PIN = 16;
constexpr int MODEM_TX_PIN = 17;
constexpr int MODEM_PWRKEY_PIN = 27;
constexpr int BUTTON_PIN = 32;
constexpr int BUZZER_PIN = 25;
constexpr int LED_PIN = 26;

// Replace this with the helper's number before deployment, e.g. "+27821234567".
const char RECIPIENT_NUMBER[] = "+27XXXXXXXXX";
const char ALERT_TEXT[] = "DISTRESS ALERT: I need help. Please call me and check on me.";

HardwareSerial modem(2);

bool alarmActive = false;
bool buttonWasDown = false;
unsigned long buttonDownAt = 0;
unsigned long lastAlarmToggleAt = 0;
bool toneOn = false;

void setAlarmSound(bool enabled) {
  if (enabled) {
    ledcAttach(BUZZER_PIN, 4000, 8);
    ledcWriteTone(BUZZER_PIN, 4000);
  } else {
    ledcWriteTone(BUZZER_PIN, 0);
    ledcDetach(BUZZER_PIN);
    digitalWrite(BUZZER_PIN, LOW);
  }
}

bool waitForModem(const char *expected, unsigned long timeoutMs) {
  String response;
  unsigned long startedAt = millis();
  while (millis() - startedAt < timeoutMs) {
    while (modem.available()) {
      char c = static_cast<char>(modem.read());
      response += c;
      if (response.indexOf(expected) >= 0) {
        return true;
      }
      if (response.indexOf("ERROR") >= 0) {
        return false;
      }
    }
    delay(10);
  }
  return false;
}

bool sendCommand(const char *command, const char *expected, unsigned long timeoutMs) {
  while (modem.available()) {
    modem.read();
  }
  modem.println(command);
  return waitForModem(expected, timeoutMs);
}

bool sendSms() {
  if (String(RECIPIENT_NUMBER).indexOf('X') >= 0) {
    return false;
  }

  // Most SIM7600 development boards start after this active-low one-second pulse.
  digitalWrite(MODEM_PWRKEY_PIN, LOW);
  delay(1100);
  digitalWrite(MODEM_PWRKEY_PIN, HIGH);
  delay(3500);

  if (!sendCommand("AT", "OK", 5000)) return false;
  if (!sendCommand("AT+CMGF=1", "OK", 3000)) return false;

  while (modem.available()) {
    modem.read();
  }
  modem.print("AT+CMGS=\"");
  modem.print(RECIPIENT_NUMBER);
  modem.println("\"");
  if (!waitForModem(">", 5000)) return false;

  modem.print(ALERT_TEXT);
  modem.write(26); // Ctrl+Z sends the SMS.
  return waitForModem("+CMGS:", 30000);
}

void startDistressAlert() {
  alarmActive = true;
  digitalWrite(LED_PIN, HIGH);
  setAlarmSound(true);
  bool sent = sendSms();

  // A steady LED means the modem accepted the SMS. Three quick flashes means it did not.
  if (sent) {
    digitalWrite(LED_PIN, HIGH);
  } else {
    for (int i = 0; i < 3; ++i) {
      digitalWrite(LED_PIN, LOW);
      delay(180);
      digitalWrite(LED_PIN, HIGH);
      delay(180);
    }
  }
}

void setup() {
  pinMode(BUTTON_PIN, INPUT_PULLUP);
  pinMode(LED_PIN, OUTPUT);
  pinMode(BUZZER_PIN, OUTPUT);
  pinMode(MODEM_PWRKEY_PIN, OUTPUT);
  digitalWrite(LED_PIN, LOW);
  digitalWrite(BUZZER_PIN, LOW);
  digitalWrite(MODEM_PWRKEY_PIN, HIGH);
  modem.begin(115200, SERIAL_8N1, MODEM_RX_PIN, MODEM_TX_PIN);
}

void loop() {
  bool buttonDown = digitalRead(BUTTON_PIN) == LOW;
  unsigned long now = millis();

  if (!alarmActive) {
    if (buttonDown && !buttonWasDown) {
      buttonDownAt = now;
    }
    if (!buttonDown) {
      buttonDownAt = 0;
    }
    if (buttonDown && buttonDownAt != 0 && now - buttonDownAt >= 2000) {
      startDistressAlert();
    }
  }
  buttonWasDown = buttonDown;

  if (alarmActive && now - lastAlarmToggleAt >= 500) {
    lastAlarmToggleAt = now;
    toneOn = !toneOn;
    setAlarmSound(toneOn);
  }
  delay(10);
}

Remix this project

Make it yours in one click

Open a full copy of this project in your own Schematik workspace, diagram, code, parts, and assembly steps included. Swap the sensor, add features, or redesign the whole thing with AI. The author's original stays untouched.

Open in Schematik