You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

能否通过SIM800模块经GPRS实现SIP呼叫?ESP32对接Asterisk咨询

问题描述

我有一台云端Asterisk服务器,希望ESP32设备通过SIM800模块经GPRS网络注册为SIP客户端并发起VoIP呼叫。目前已通过SIM800的AT指令建立GPRS连接,可发送HTTP请求和MQTT消息,也能通过GSM发起通话,但尚未找到连接到VoIP服务器的可行方法。以下是我当前的ESP32代码,已实现GPRS下的MQTT连接及GSM通话功能,但无法对接Asterisk:

当前代码
// ESP32 Configuration
#define TINY_GSM_MODEM_SIM800
#define SerialMon Serial 
#define SerialAT Serial2 
#define TINY_GSM_DEBUG SerialMon
#define GSM_AUTOBAUD_MIN 9600  
#define GSM_AUTOBAUD_MAX 38400 
#define TINY_GSM_USE_GPRS true


#define PhoneNumber "xxxxxxxxxxxxxxx" 


#define rcSwitchInterruptPin 35
#define ENABLE_SIM800C 22   
#define BUTTON_PIN 33
#define SOS_BUTTON_ID0 4236140
#define SOS_BUTTON_ID1 4236132
#define SOS_BUTTON_PIN 27

// GPRS internet settings, specific to your SIM card 
const char apn[] = "internet";
const char gprsUser[] = "";
const char gprsPass[] = "";

// MQTT settings
const char *broker = "broker.emqx.io";
const int mqttPort = 1883;
const char * clientID = "esp32-client";
const char * mqttUser = "acido";
const char * mqttPassword = "12345678";


#include <TinyGsmClient.h>
#include <PubSubClient.h>
#include <SPI.h>
#include <Wire.h>
#include <PubSubClient.h>
#include <elapsedMillis.h>
#include <RCSwitch.h>
RCSwitch mySwitch = RCSwitch();

TinyGsm modem(SerialAT);
TinyGsmClient client(modem);
PubSubClient mqtt(client);

uint32_t lastReconnectAttempt = 0;
void mqttCallback(char *, byte *, unsigned int);
boolean mqttConnect();
void DRE_switch();
elapsedSeconds serialLogSeconds;
unsigned int serialLog_Interval = 3;
elapsedSeconds sinyalBekletme;
unsigned int sinyalBekletme_Interval = 3;
elapsedSeconds sosDelay;
unsigned int sosDelay_Interval = 3;
elapsedSeconds kapiacikDelay;
unsigned int kapiacikDelay_Interval = 3;
elapsedSeconds kapiKapaliDelay;
unsigned int kapiKapaliDelay_Interval = 3;
elapsedSeconds gazDelay;
unsigned int gazDelay_Interval = 3;
elapsedSeconds pirDelay;
unsigned int pirDelay_Interval = 3;
elapsedSeconds dumanDelay;
unsigned int dumanDelay_Interval = 3;

void setup()
{
    mqtt.setKeepAlive(60);

    // Set console baud rate
    SerialMon.begin(9600);
    while (!SerialMon)

    delay(10);

    // !!!!!!!!!!!
    mySwitch.enableReceive(35);
    pinMode(BUTTON_PIN, INPUT_PULLDOWN); 
    pinMode(ENABLE_SIM800C, OUTPUT);

    digitalWrite(ENABLE_SIM800C, HIGH);
    delay(3000);
    digitalWrite(ENABLE_SIM800C, LOW);
    // !!!!!!!!!!!

    TinyGsmAutoBaud(SerialAT, GSM_AUTOBAUD_MIN, GSM_AUTOBAUD_MAX);

    delay(6000);

    // Restart takes quite some time
    // To skip it, call init() instead of restart()

    // if (!modem.restart()) {
    if (!modem.init()) {
        DBG("Failed to restart modem, delaying 10s and retrying");
        // restart autobaud in case GSM just rebooted
        TinyGsmAutoBaud(SerialAT, GSM_AUTOBAUD_MIN, GSM_AUTOBAUD_MAX);
        return;
    }

    String modemInfo = modem.getModemInfo();
    SerialMon.print("setup: Modem Info: ");
    SerialMon.println(modemInfo);

    SerialMon.print("setup: Waiting for network...");
    if (!modem.waitForNetwork())
    {
        SerialMon.println("setup:  fail, wait 5 seconds");
        delay(5000);
        return;
    }
    SerialMon.println("setup:  success");

    if (modem.isNetworkConnected())
        SerialMon.println("setup: Network connected");

#if TINY_GSM_USE_GPRS
    // GPRS connection parameters are usually set after network registration
    SerialMon.print(F("setup: Connecting to apn: "));
    SerialMon.print(apn);
    if (!modem.gprsConnect(apn, gprsUser, gprsPass))
    {
        SerialMon.println("setup:  GPRS Connecting fail, wait 5 seconds");
        delay(5000);
        return;
    }
    SerialMon.println("setup:  GPRS success");

    if (modem.isGprsConnected())
    {
        SerialMon.println("setup: GPRS connected");
    }
#endif

    // MQTT Broker setup
    mqtt.setServer(broker, mqttPort);
    mqtt.setCallback(mqttCallback); 
}

void loop()
{
    bool result;

    // check if we're connected to network
    if (!modem.isNetworkConnected())
    {
        SerialMon.println("loop: modem disconnected");
        if (!modem.waitForNetwork(180000L, true))
        {
            SerialMon.println("loop: modem fail, wait 10 sec");
            delay(10000);
            return;
        }

        if (modem.isNetworkConnected())
        {
            SerialMon.println("loop: Modem re-connected");
        }

        // And make sure GPRS/EPS is still connected
        if (!modem.isGprsConnected())
        {
            SerialMon.println("loop: GPRS disconnected!");
            SerialMon.print(F("loop: Connecting to apn:"));
            SerialMon.print(apn);
            if (!modem.gprsConnect(apn, gprsUser, gprsPass))
            {
                SerialMon.println("loop:  GPRS fail, wait 10 sec");
                delay(10000);
                return;
            }
            if (modem.isGprsConnected())
            {
                SerialMon.println("loop: GPRS reconnected");
            }
        }
    }

    // check if we're connected to MQTT
    if (!mqtt.connected())
    {
        SerialMon.println("loop: === mqtt not connected ===");
        // Reconnect every 10 seconds
        uint32_t t = millis();
        if (t - lastReconnectAttempt > 10000L)
        {
            lastReconnectAttempt = t;
            if (mqttConnect())
            {
                lastReconnectAttempt = 0;
            }
        }
        return;
    }

    if (digitalRead(BUTTON_PIN) == HIGH)
    {
        Serial.println("BUTONA BASILDI");
        result = modem.callNumber(PhoneNumber);
        DBG("Call:", result ? "OK" : "fail");
    }

    // Check RF signals and publish to MQTT

    if (mySwitch.available())
    {
        int num = mySwitch.getReceivedValue();
        Serial.print("Num: ");
        Serial.print(num);
        Serial.print("  Protocol: ");
        Serial.println(mySwitch.getReceivedProtocol());

        if (sosDelay >= sosDelay_Interval)
        {
            if (num == 4236132) // SOS
            {
                Serial.println("SOS Butonuna Basıldı.");
                DRE_switch();
                mqtt.publish("CMR/ESP32s/SENSOR/SOS", "SOS"); // Publish message.
                result = modem.callNumber(PhoneNumber);
                DBG("Call:", result ? "OK" : "fail");
            }
            else
            {
                mqtt.publish("CMR/ESP32s/SENSOR/SOS", "0"); // Publish message.
            }
            sosDelay = 0;
        }

        if (pirDelay >= pirDelay_Interval)
        {
            if (num == 8330345)
            {
                DRE_switch();
                Serial.println("HAREKET ALGILANDI.");
                mqtt.publish("CMR/ESP32s/SENSOR/PIR", "HAREKET ALGILANDI"); // Publish message.
            }
            else
            {
                mqtt.publish("CMR/ESP32s/SENSOR/PIR", "0"); // Publish message
            }
            pirDelay = 0;
        }

        if (kapiacikDelay >= kapiacikDelay_Interval)
        {

            if (num == 5729285) // Kapi acilinca gelen sinyal
            {
                DRE_switch();
                Serial.println("KAPI AÇILDI.");
                mqtt.publish("CMR/ESP32s/SENSOR/KAPIACIK", "1"); // Publish message.
            }
            else
            {
                mqtt.publish("CMR/ESP32s/SENSOR/KAPIACIK", "0"); // Publish message
            }
            kapiacikDelay = 0;
        }

        if (kapiKapaliDelay >= kapiKapaliDelay_Interval)
        {

            if (num == 5729294) // Kapi kapaninca gelen sinyal
            {
                DRE_switch();
                Serial.println("KAPI KAPANDI.");
                mqtt.publish("CMR/ESP32s/SENSOR/KAPIKAPALI", "1"); // Publish message.
            }
            else
            {
                mqtt.publish("CMR/ESP32s/SENSOR/KAPIKAPALI", "0"); // Publish message
            }
            kapiKapaliDelay = 0;
        }

        if (gazDelay >= gazDelay_Interval)
        {
            if (num == 12607491)
            {
                DRE_switch();
                Serial.println("Gas Algılandı.");
                mqtt.publish("CMR/ESP32s/SENSOR/GAS", "GAS ALGILANDI"); // Publish message.
            }
            else
            {
                mqtt.publish("CMR/ESP32s/SENSOR/GAS", "0"); // Publish message
            }
            gazDelay = 0;
        }

        if (dumanDelay >= dumanDelay_Interval)
        {
            if (num == 79974)
            {
                DRE_switch();
                Serial.println("Duman Algılandı .");
                mqtt.publish("CMR/ESP32s/SENSOR/DUMAN", "DUMAN ALGILANDI"); // Publish message.
            }
            else
            {
                mqtt.publish("CMR/ESP32s/SENSOR/DUMAN", "0"); // Publish message
            }
            dumanDelay = 0;
        }
    } // end if mySwitch.available

    modem.maintain();
    mqtt.loop(); // Handling MQTT on the background.
} // end of loop


boolean mqttConnect()
{
    // Connect to MQTT Broker
    SerialMon.print("mqttConnect:  MQTT trying to connect: ");
    SerialMon.print(broker);

    // Connect to MQTT Broker with password protected
    boolean status = mqtt.connect(clientID, mqttUser, mqttPassword);

    // boolean status = mqtt.connect(clientID); // without password

    if (status == false)
    {
        SerialMon.println("mqttConnect:  MQTT Connection Fail");
        return false;
    }
    SerialMon.println("mqttConnect:  *** MQTT CONNECTED *** ");

    mqtt.publish("topic/akes", "Client connection started");
    mqtt.subscribe("topic/akes"); // subscribe to topic
    
    return mqtt.connected();
}


void mqttCallback(char *topic, byte *payload, unsigned int len)
{
    // Handle message arrived
    SerialMon.print("mqttCallback:  Message arrived [");
    SerialMon.print(topic);
    SerialMon.print("]: ");
    SerialMon.write(payload, len);
    SerialMon.println();
}


void DRE_switch()
{
    // Disable Reset Enable function for RF module
    mySwitch.disableReceive();
    mySwitch.resetAvailable();
    mySwitch.enableReceive(rcSwitchInterruptPin);
}
解决方案

要实现ESP32通过SIM800+GPRS对接Asterisk SIP服务器,核心是在ESP32上运行轻量SIP客户端栈,利用已有的GPRS网络连接与Asterisk通信,具体步骤如下:

1. 选择合适的SIP库

ESP32可用的轻量SIP库推荐:

  • ESP32-SIP-Client:专为ESP32设计的轻量SIP客户端库,配置简单,资源占用低,适合当前场景。
  • PJSIP:功能完整的开源SIP栈,支持复杂VoIP功能,但资源占用较高,适合对功能要求高的场景。

以下以ESP32-SIP-Client为例说明集成流程:

2. 集成SIP库到现有代码

步骤1:安装库

通过Arduino Library Manager搜索并安装ESP32-SIP-Client库。

步骤2:添加SIP配置参数

在代码开头新增Asterisk服务器的SIP配置:

// SIP配置
const char* sip_server = "你的Asterisk服务器IP/域名";
const int sip_port = 5060; // 默认SIP端口
const char* sip_username = "Asterisk上创建的SIP账号";
const char* sip_password = "SIP账号密码";
const char* sip_display_name = "ESP32_VoIP";

步骤3:初始化SIP客户端

在setup()函数中,完成GPRS连接后添加SIP初始化与注册逻辑:

#include <SIPClient.h>

SIPClient sipClient;

void setup() {
    // ... 保留原有初始化代码 ...

#if TINY_GSM_USE_GPRS
    // ... 原有GPRS连接代码 ...

    // 初始化SIP客户端,传入已建立的GPRS客户端
    sipClient.begin(sip_server, sip_port, sip_username, sip_password, sip_display_name, &client);
    // 注册到Asterisk服务器
    if(sipClient.registerSIP()){
        SerialMon.println("SIP注册成功");
    } else {
        SerialMon.println("SIP注册失败");
    }
#endif
}

步骤4:维护SIP连接并发起呼叫

在loop()函数中添加SIP连接维护逻辑,替换原有GSM呼叫为VoIP呼叫:

void loop() {
    // ... 原有网络、MQTT维护代码 ...

    // 维护SIP连接,处理服务器消息
    sipClient.loop();

    // 示例:SOS触发时发起VoIP呼叫
    if (mySwitch.available()) {
        int num = mySwitch.getReceivedValue();
        // ... 原有其他传感器逻辑 ...

        if (sosDelay >= sosDelay_Interval) {
            if (num == 4236132) // SOS
            {
                Serial.println("SOS Butonuna Basıldı.");
                DRE_switch();
                mqtt.publish("CMR/ESP32s/SENSOR/SOS", "SOS");
                
                // 发起SIP呼叫,目标为Asterisk上的账号或号码
                if(sipClient.call("目标SIP账号/号码")){
                    SerialMon.println("VoIP呼叫发起成功");
                } else {
                    SerialMon.println("VoIP呼叫发起失败");
                }
            }
            sosDelay = 0;
        }
    }
}

3. Asterisk服务器配置注意事项

  • 在Asterisk的sip.conf中创建对应SIP账号,设置type=friend、host=dynamic(允许动态IP注册),并确保allow字段包含G.711等低带宽音频编码。
  • 开启NAT穿透支持:若Asterisk部署在公网,ESP32处于GPRS内网,需在账号配置中添加nat=yes、externip=你的公网IP,或配置STUN服务器。
  • 开放服务器端口:确保Asterisk的5060端口(UDP/TCP)对外开放,允许GPRS网络的流量接入。

4. 常见问题排查

  • SIP注册失败:用modem.ping(sip_server)测试GPRS是否能连通服务器,检查账号密码、服务器地址是否正确,Asterisk是否开启了对应账号的注册权限。
  • 呼叫无声音:确认ESP32连接的音频输入输出设备接线正确,SIP库与Asterisk的音频编码设置一致(优先
相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 15:30:44