SIM800L 发送第一条短信:Arduino 接线、AT+CMGS、读取回复与人体感应报警

Cartoon of a bright red SIM800L module with a silver shield and gold spring antenna sending a white envelope along teal signal arcs to a navy smartphone showing a message bubble

想让 SIM800L GSM 模块发出第一条短信,步骤是:装上一张已注册的 micro-SIM 卡,用一节 18650 电池给模块供电(它要求 3.4–4.4V,发射时最高会猛抽 2A),用两颗 10k 电阻把 Uno 的 5V TX 信号降到约 2.5V,然后发送 AT+CMGF=1 和 AT+CMGS="+60…"——只有当你的程序写出 Ctrl+Z,也就是字节 26,这条短信才会真正发出去。

SIM800L 要发短信,需要先准备什么?

SIM800L 是一块只支持 2G 的 GSM 模块,所以它需要两样任何商店都没法放进包装盒里的东西:一张 SIM 卡和一个 2G 网络。SIM 卡用的是 micro-SIM,插进电路板背面的卡槽——任何已注册、有短信余额的马来西亚 SIM 卡都可以,预付卡也行。插卡时金色触点朝向电路板,缺角对准卡槽旁边的缺角标记(nano-SIM 需要套一个 micro 卡托)。如果卡上设了 PIN 码,先放进手机里把 PIN 码关掉;锁着 PIN 的卡会让模块一直停在 +CPIN: SIM PIN。网络方面,马来西亚的 2G 仍在运行,不过 4G 专营电信商的 SIM 卡没有 2G 网络可连,永远注册不上——详细说明见常见问题。

接线之前先说一句:SIM800L 模块出货时两条 6 针排针是散装放在袋子里的,所以要先焊好 12 个焊点,它才能插上面包板——焊接技巧见我们的排针焊接教程,或者在下面加购代焊服务,模块到手就能直接插上。

可选:人体感应报警、代焊服务和工具

ItemPriceQty
PIR Motion Sensor HC-SR501 MN-SR501 MINI Human Sensor Human Detector Motion Detector Arduino HCSR501 MNSR501 - HC-SR501 PIR MOTION SENSORPIR Motion Sensor HC-SR501 MN-SR501 MINI Human Sensor Human Detector Motion Detector Arduino HCSR501 MNSR501 - HC-SR501 PIR MOTION SENSORHCSR501RM4.90
40pcs Dupont Wire 10cm 20cm 30cm for Breadboard DIY Experiment Jumper Wire Breadboard wire - DUPONT WIRE M-F 30CM40pcs Dupont Wire 10cm 20cm 30cm for Breadboard DIY Experiment Jumper Wire Breadboard wire - DUPONT WIRE M-F 30CMDPWMF30RM4.60
Soldering Service for Circuit Boards & Electronics - 10 Pins Soldering ServiceSoldering Service for Circuit Boards & Electronics - 10 Pins Soldering ServiceSOLSE10RM1.40
Soldering Service for Circuit Boards & Electronics - 1 Pin Soldering ServiceSoldering Service for Circuit Boards & Electronics - 1 Pin Soldering ServiceSOLSE01RM0.20
18650 Battery Charger 1/2 Slot Li-on Battery Charger USB A Connector 3.7V Universal Charger - 18651 BATTERY CHARGER (2 SLOT)18650 Battery Charger 1/2 Slot Li-on Battery Charger USB A Connector 3.7V Universal Charger - 18651 BATTERY CHARGER (2 SLOT)18UCHA2RM13.95
XL830L Digital Multimeter with backlight Portable Multimeter Electronics ProjectXL830L Digital Multimeter with backlight Portable Multimeter Electronics ProjectMULTIMERM25.95

PIR 传感器和公对母杜邦线用于最后一节的人体感应报警。代焊服务(10 针 + 2 个单针 = 正好是 SIM800L 的 12 根排针)让模块到手时已经焊好。充电器在两次使用之间给 18650 充满电,万用表则让分压电路和电池电压的检查轻松很多。

SIM800L 怎么供电,怎么接到 Uno?

SIM800L 的供电窗口很严格——根据 SIMCom 的 SIM800 硬件设计文档,是 3.4–4.4V,发射时的突发电流最高 2A——而这块红色小板上没有任何稳压器:VCC 引脚的电直接送进射频部分。一节锂电池正是 SIMCom 自己建议的供电方式——同一份文档写明,3.7V 锂电池可以直接接到 VBAT 引脚。一节充好电的 18650 在电量的上半段维持在 3.7–4.2V,正好落在窗口里,扛 2A 的突发电流也毫不吃力;这就是我们 SIM800L 掉电重启教程里的方案 1。如果你的模块一发射就重启,那篇教程有完整的电源排查方法(NETLIGHT 闪烁含义、AT+CSQ、AT+CREG?)。

单节电池盒自带红黑两根引线:把每根线的裸露线头紧紧缠在一根公对公杜邦线的针脚上,就能像普通跳线一样插进面包板。装电池时负极顶着弹簧,正极对着铜片按钮(也就是红线那一端):这块板没有防反接保护,电池装反可能直接烧掉模块。连到模块 VCC 和 GND 那两列的线要短而直接——SIMCom 要求电池到模块之间的电阻低于 150mΩ,因为在 2A 下,每 0.1Ω 就会吃掉 0.2V(V = I × R)。即使走满 150mΩ,一节刚充满的 4.2V 电池仍能送出 3.9V;而一节用掉一半的 3.7V 电池只剩 3.4V,正好踩在窗口的最低线上。所以每次开工前,都先换上一节刚充满的电池。想用 7–12V 的台式电源?把 LM2596 降压模块调到 4.0V 也能达到同样效果——调电位器的方法见我们的 LM2596 调压教程。一定要在接上模块之前先调好并量准 4.0V:没调过的降压模块,输出可能几乎等于输入电压。

卡通图:黑色单节电池盒里装着一节粉色 18650 电池,通过短短的红黑引线给带金色弹簧天线的亮红色 SIM800L 模块供电,引线旁画着青绿色的电流突发闪光
一节粉色 18650 直接接模块:又短又直接的引线才扛得住 2A 的发射突发电流,长长的杜邦线和 Uno 的 5V 引脚都做不到。

接下来是串口部分。SIM800L 的逻辑引脚工作在它内部的 2.8V 电源轨(VDD_EXT)上,同一份 SIMCom 文档规定串口输入的有效高电平在 2.1V 到 3.0V 之间——Uno 的 5V TX 信号会让 RXD 远远超过这个上限:一旦超过,引脚的保护二极管就会导通,把电流灌进模块自己的 2.8V 电源轨。两颗阻值相同的电阻就能解决:10k 从 Uno 的 D3 接到 RXD,另一颗 10k 从 RXD 接到 GND,流过两颗电阻的电流相同,所以 5V 被平分成两半,RXD 看到的约是 2.5V——稳稳落在窗口之内。两颗 10k 都可以从电阻包的同一条里取(这种蓝色五色环金属膜电阻上,10k 的色环是棕-黑-黑-红,接着一条棕色误差环;从哪一端开始读,见我们的电阻色环教程)。别用网上流行的 10k/20k 组合:它得出约 3.3V,刚好超过 3.0V 的上限。反方向则不需要任何元件。2.8V 的信号没法把电流推进一个 5V 供电的输入脚,所以伤不到 Uno;虽然它比 ATmega328P 保证识别为高电平的 3.0V(0.6 × 5V)低了一点点,但实际芯片的翻转门槛更低,在实践中能稳定读到模块的 TX 信号。TXD 直接接到 D2,和掉电重启教程的接法一脚不差。

概念卡通图:蓝色 Arduino Uno 发出一个高高的深蓝色方波,经过白色面包板上两颗相同的蓝色 10k 电阻,从两颗电阻中间的节点出来的是一半高度的青绿色方波,进入红色 SIM800L 模块
一张图看懂分压电路:两颗相同的电阻把 Uno 高高的 5V 摆幅对半分,SIM800L 工作在 2.8V 电源轨上的输入脚就能承受得住。
SIM800L 引脚 连接到 说明
VCC 18650 电池盒红线 绝对不要接 Uno 的 5V 引脚:5V 超过 4.5V 的绝对最大值,而且 Uno 提供不了 2A 的突发电流
GND 18650 电池盒黑线,同时接 Uno GND 必须共地:两块板的高低电平都以地为基准
TXD Uno D2 直接连线——2.8V 在实践中能读成高电平
RXD 经分压电路接 Uno D3 10k 从 D3 接到 RXD,10k 从 RXD 接到 GND(≈2.5V)
天线 金色弹簧天线,焊脚焊在 NET 焊盘上 没装天线绝对不要发射
RST 悬空不接 基本使用不需要
RING、DTR、MIC±、SPK± 悬空不接 通话、音频和休眠引脚,发短信用不到

焊排针的同一次,把金色弹簧天线弯折的那只脚焊到 NET 焊盘上(左上角,紧挨着那个小小的天线座),然后再让模块靠近面包板。接着按这个顺序在面包板上搭电路:先放模块,再把分压电阻插在 RXD 旁边,然后用信号线接到 Uno,电池最后才接。这根弹簧天线不是装饰——没装天线就发射,发射功率会反射回模块里。整个电路都是低压直流,但 18650 是一节没有保护板的电池:千万别让电池盒两根引线的裸露线头碰在一起,也只能用正规的 18650 充电器充电——相关的好习惯见我们的 18650 充电教程。

怎样用 AT 指令和 SIM800L 对话?

AT 指令就是 SIM800L 的全部操作界面:你的程序发给它的每一行都是一条指令,它传回来的每一行都是它的回答。下面这段程序先跑完发短信最关键的三项检查,然后把串口监视器变成一个直通模块的实时终端——把串口监视器设为 9600 波特率,行结束符选“Both NL & CR”,因为模块要收到回车符才会执行指令。

#include <SoftwareSerial.h>

// SIM800L TXD -> Uno D2(直接连线)
// SIM800L RXD <- Uno D3,经过 10k/10k 分压电路
SoftwareSerial sim800(2, 3);  // RX, TX

// 回复一到就立刻打印:SoftwareSerial 只能缓存 64 个字节
void waitAndEcho(unsigned long ms) {
  unsigned long start = millis();
  while (millis() - start < ms) {
    if (sim800.available()) Serial.write(sim800.read());
  }
}

void setup() {
  Serial.begin(9600);
  sim800.begin(9600);
  delay(3000);                 // 等模块启动

  sim800.println("AT");        // 应回复:OK
  waitAndEcho(500);
  sim800.println("AT+CPIN?");  // 应回复:+CPIN: READY(SIM 卡已识别)
  waitAndEcho(500);
  sim800.println("AT+CREG?");  // 应回复:+CREG: 0,1 或 0,5(已注册网络)
  waitAndEcho(500);
  sim800.println("AT+CMGF=1"); // 文本模式:直接用普通字符,不用 PDU
  waitAndEcho(500);
}

void loop() {
  // 直通模式:在串口监视器里输入 AT 指令
  // (9600 波特率,行结束符选 "Both NL & CR")
  if (sim800.available()) Serial.write(sim800.read());
  if (Serial.available()) sim800.write(Serial.read());
}

第一次运行时要给模块一点时间:装上电池后,等 NETLIGHT 指示灯从每秒闪一次(正在搜网)变成每三秒闪一次(已注册),再打开串口监视器。打开串口监视器会让 Uno 复位,于是这几项检查会在模块准备好之后重新跑一遍。每条回复前面都会先回显一遍对应的指令,这是正常的。

指令 正常回复 其他回复代表什么
AT OK 毫无反应:TXD 和 RXD 接反、分压电路接错、没有共地,或者没通电
AT+CPIN? +CPIN: READY +CPIN: SIM PIN:PIN 锁还没关。ERROR:没检测到 SIM 卡,重新插一下
AT+CREG? +CREG: 0,1(本地网络)或 0,5(漫游) 0,2:还在搜网,再等等。0,3:网络拒绝了这张 SIM 卡

最后一条指令 AT+CMGF 决定你和模块用哪种“语言”沟通。在 PDU 模式(AT+CMGF=0)下,模块要求你把整条短信预先打包成网络实际传输的二进制数据包——号码、编码和内容全部压成十六进制数字。在文本模式(AT+CMGF=1)下,模块会用普通字符替你打包好。这个设置默认不会永久保存,所以这里的程序每次开机都在 setup() 里重新设置一次——这样你就可以完全忘掉 PDU 的存在。本文以外的指令细节,请查 SIMCom 的 SIM800 AT 指令手册。

为什么 AT+CMGS 要等到 Ctrl+Z 才发送?

AT+CMGS 指令是一段分两幕的对话。第一幕:你发送 AT+CMGS="+60123456789" 加一个回车符。模块回你一个孤零零的 >——这就是提示符。从这一刻起,它不再把你发来的字节当成指令,而是开始把它们收集起来,作为短信内容。

第二幕才是陷阱:模块会一直收集下去,直到收到一个特定的字节——0x1A,也就是十进制的 26,Ctrl+Z 字符——意思是“短信写完了,交给网络”。(字节 27,也就是 ESC,作用正好相反:把草稿丢掉。)为什么不能直接按回车?因为短信内容本身可以换行:按照 3GPP TS 27.005 短信标准,在提示符之后输入的回车符会成为短信内容的一部分,模块还会再给你一个新的 >。只有永远不可能出现在短信里的字节才能用来结束它,所以 Ctrl+Z 是发送,ESC 是取消。这也是为什么短信内容要用 print() 发出,而不是 println()——后者只会多加一个换行,什么也结束不了。Arduino 串口监视器只能发送可打印的文字,所以你没法在里面输入 Ctrl+Z——网上一半“CMGS 发不出去”的讨论帖,卡住的正是这一步。程序会自己发出这个字节:sim800.write(26);。几秒钟后,模块会回报 +CMGS: 1(一个短信参考编号)和 OK——网络已经收下了。一条普通短信最多可以装 160 个 GSM 字母表字符,英文和马来文都没问题;中文字符和表情符号需要 UCS2 编码,那是发第一条短信之后的进阶步骤了——所以下面程序里的短信内容保持英文。

#include <SoftwareSerial.h>

SoftwareSerial sim800(2, 3);  // RX, TX

// 回复一到就立刻打印:SoftwareSerial 只能缓存 64 个字节
void waitAndEcho(unsigned long ms) {
  unsigned long start = millis();
  while (millis() - start < ms) {
    if (sim800.available()) Serial.write(sim800.read());
  }
}

void sendSMS(const char *number, const char *text) {
  sim800.println("AT+CMGF=1");   // 文本模式,每次开机重新设置
  waitAndEcho(500);
  sim800.print("AT+CMGS=\"");    // 引号是指令的一部分
  sim800.print(number);
  sim800.println("\"");
  waitAndEcho(500);              // 模块回复 > 提示符
  sim800.print(text);            // 短信内容——这里不要用 println
  sim800.write(26);              // Ctrl+Z(字节 26):交给网络发送
  waitAndEcho(5000);             // 发送需要几秒钟
}

void setup() {
  Serial.begin(9600);
  sim800.begin(9600);
  delay(8000);                   // 启动 + 注册网络

  // 填你自己的号码,国际格式
  sendSMS("+60123456789", "Hello from my Arduino");
  Serial.println("Done - a +CMGS: line above means the network took it");
}

void loop() {
  // 回显模块的回复:+CMGS: 1 之后接 OK,代表已交给网络
  if (sim800.available()) Serial.write(sim800.read());
}

把号码改成你自己的手机号码,用国际格式(+60…),等 NETLIGHT 进入每三秒闪一次的慢闪之后再上传。上传只会让 Uno 复位,用电池供电的模块会保持在已注册状态,接着你的手机一震——这就是回报。想看完整的对话过程(> 提示符、你的短信内容,然后是 +CMGS: 和 OK)就打开串口监视器,但要知道:打开它会让 Uno 复位、重新跑一遍 setup(),所以看这一眼的代价是再发一条要收费的短信。

SIM800L 怎么读取回复的短信?

在 SIM800L 上接收短信,就是发送的镜像操作。用手机回一条短信给模块,它会主动报告:+CMTI: "SM",1——意思是“收到新短信,存在第 1 号位置”。在直通程序里输入 AT+CMGR=1,就能打印出发件人、时间戳和内容;读完用 AT+CMGD=1 删除——SIM 卡的存储空间只够放几十条短信。想要实时推送?AT+CNMI=2,2,0,0,0 会把每条新短信直接从串口推出来,格式是一行带完整内容的 +CMT:,不做任何存储——如果想让程序解析短信、按短信指令执行动作,用的就是这个模式。

怎样把 SIM800L 做成会发短信通知你的人体感应报警?

HC-SR501 PIR 传感器能搭出最经典的成果作品:有东西在动,你的手机就震。HC-SR501 支持 5–20V 供电,所以直接用 Uno 的 5V 引脚供电;它的 OUT 引脚输出 3.3V 逻辑电平,高于 Uno 保证识别为高电平的 3.0V——这一侧不需要分压,因为信号是流进 Uno,而不是流进一个工作在 2.8V 电源轨上的引脚。用公对母杜邦线(传感器上是公排针)接线:VCC → 5V,GND → GND,OUT → D7。灵敏度、感应距离和那两个橙色电位器的调法,见我们的 PIR 人体感应灯教程——相信这个报警之前,先把它读一遍。

有一条规则一定要遵守,因为它会花掉真金白银:没有限制发送频率的 PIR 报警,一个下午就能把你的预付卡余额烧光。只要还看到有东西在动,HC-SR501 就会不断重新触发;如果不加限制,loop 大约每六秒就会再发一条——也就是一次 sendSMS() 调用所需的时间:一小时就是几百条收费短信。下面的程序发一条之后,会在五分钟内无视传感器,把最坏情况限制在每小时 12 条(人来人往的房间就把 LOCKOUT_MS 调大);它在开机后还会先等一分钟,因为 HC-SR501 在刚上电的大约一分钟里稳定下来之前,可能会误触发几次。

#include <SoftwareSerial.h>

SoftwareSerial sim800(2, 3);  // RX, TX

const int PIR_PIN = 7;        // HC-SR501 OUT:3.3V 逻辑电平,Uno 可以直接读取
const unsigned long LOCKOUT_MS = 300000UL;  // 两条短信之间隔 5 分钟——保护你的话费余额

unsigned long lastSMS = 0;
bool sentBefore = false;

// 回复一到就立刻打印:SoftwareSerial 只能缓存 64 个字节
void waitAndEcho(unsigned long ms) {
  unsigned long start = millis();
  while (millis() - start < ms) {
    if (sim800.available()) Serial.write(sim800.read());
  }
}

void sendSMS(const char *number, const char *text) {
  sim800.println("AT+CMGF=1");
  waitAndEcho(500);
  sim800.print("AT+CMGS=\"");
  sim800.print(number);
  sim800.println("\"");
  waitAndEcho(500);
  sim800.print(text);
  sim800.write(26);            // Ctrl+Z 把短信发出去
  waitAndEcho(5000);
}

void setup() {
  pinMode(PIR_PIN, INPUT);
  Serial.begin(9600);
  sim800.begin(9600);
  delay(8000);                 // 模块启动 + 注册网络
  delay(60000);                // HC-SR501 预热:约 1 分钟内可能误触发
  Serial.println("Armed");
}

void loop() {
  bool lockedOut = sentBefore && (millis() - lastSMS < LOCKOUT_MS);

  if (digitalRead(PIR_PIN) == HIGH && !lockedOut) {
    Serial.println("Motion - sending SMS");
    sendSMS("+60123456789", "Motion detected at home");
    lastSMS = millis();
    sentBefore = true;
  }

  if (sim800.available()) Serial.write(sim800.read());
}

长期值守时,Uno 和 PIR 用 USB 供电(笔记本拿走后就换成手机充电器),SIM800L 则用它自己的电池。按 SIMCom 的数据,模块在网络上待机时耗电约 22mA,所以一节 2200mAh 的电池最多撑大约 100 小时。电压降到 3.49V 时,它会报告 UNDER-VOLTAGE WARNNING(拼写错误是 SIMCom 原文如此),低于 3.39V 就会自动关机:这时要么给电池充电,要么把长期安装的方案改成 LM2596 加墙插电源适配器。

老实说清楚它是什么:这是一个告诉你“有东西动了”的爱好级报警器。它不能替代真正的防盗系统——要保护真正值钱的东西,就该用正规的安防设备。

卡通工作台场景:白色圆顶 PIR 传感器侦测到一只走过的猫,经白色面包板接线,面包板上插着红色 SIM800L 模块和两颗蓝色电阻,再连到只露出一部分的蓝色 Arduino Uno;SIM800L 由黑色电池盒里一节独立的粉色 18650 电池供电,旁边一部深蓝色智能手机显示一个收到新短信的气泡
成果作品:PIR 圆顶侦测到动静,Uno 让 SIM800L 发短信,手机随即一震——最多每五分钟一条,绝不会短信轰炸。

我们常见的错误

> 提示符出现了,然后什么都发不出去。模块还在等字节 26。输入“26”或按回车都产生不了这个字节——只有程序里的 sim800.write(26); 才行。

AT+CMGS 之后马上报 ERROR。通常是这次开机还没设文本模式——AT+CMGF=1 应该写在 setup() 里,而不是靠你记得去发——或者发送电话号码时漏了引号。

用本地格式写号码,短信始终收不到。目标号码要写成国际格式 +60123456789——像 0123456789 这样的本地写法,可能在一个网络上能用,换到另一个网络就无声无息地消失了。

常见问题

SIM800L 在马来西亚的网络上还能用吗?

能——SIM800L 只支持 2G,而马来西亚的 2G 仍在运行:主要电信商保留 2G 网络,作为语音通话的后备以及机器对机器(M2M)设备的连接,而且没有一家公布过关闭日期。计划可能会改变,所以如果你的设备要连续运行好几年,正式投入之前先向你的电信商确认 2G 支持情况。

我平常用的 4G 或 5G 预付卡能插进 SIM800L 用吗?

通常可以。现代 SIM 卡本身不分网络制式——关键在于卡背后的网络是否仍接受 2G 连接,而马来西亚的主要网络都接受;4G 专营电信商的 SIM 卡则没有 2G 网络可连,永远注册不上。SIM 卡必须已注册、是 micro-SIM 尺寸、有短信余额,并且已关闭 PIN 锁。

为什么我的 SIM800L 一发短信就重启?

发射的时候正是模块抽取 2A 突发电流的时候,所以供电不够强,电压就会在这一刻垮掉,模块因掉电而复位。用一节充好电的 18650,或调到 4.0V 的降压模块,配上又短又粗的引线供电,绝对不要用 Arduino 的 5V 引脚——完整的排查方法见我们的 SIM800L 电源教程。

SIM800L 的排针一定要自己焊吗?

要——模块出货时两条 6 针排针是散装的,要插面包板,12 根针都得焊好。作为第一次焊接练习,这个难度很友好(技巧见我们的排针焊接教程);或者加购 12 针代焊服务,模块到手就能直接插上。

为什么我的 SIM800L 对 AT 指令没有反应?

按顺序检查:电源(NETLIGHT 在闪)、TXD 接 D2、RXD 经分压电路接 D3 而没有接反、两块板共地,以及串口监视器设为 9600 波特率并选了“Both NL & CR”。SIM800L 出厂时是自动波特率,会锁定上电后听到的第一个 AT 的速度,所以如果第一条没有回应,就再发一次 AT。

最后更新:2026 年 11 月。有问题?WhatsApp 联系我们。

Leave a Reply

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