The RAK5860 failed to register to the network successfully

During the development process, I used the 4G and 5G compatible card provided by China Unicom. After debugging the code and analyzing the output results, I found that when sending the AT+CSQ command to query the signal quality, the response was +CSQ: 99, 99. By sending the AT+CREG command to query the network registration status, the response is always +CREG:0,2. Could you please tell me what the reason is that prevents me from registering on the network?I will provide my test code.

/**
   @file RAK5860_Network_Registration_Test.ino
   @brief RAK5860 BG77 网络注册专项测试
   @note 专门解决 +CEREG:0,2 网络注册问题
*/
#include <Arduino.h>
#include <Adafruit_USBD_CDC.h>
#include <SoftwareSerial.h>

#define BG77_POWER_KEY WB_IO1

// 联通APN列表 - 按优先级排序
const String APN_LIST[] = {
  "3GNET",     // 联通3G/4G通用APN(推荐)
  "UNINET",    // 联通原始APN
  "LTENET",    // 联通LTE专用APN
  "WONET",     // 联通物联网APN
  "CMNET"      // 如果是移动卡的话
};
const int APN_COUNT = 5;

// 测试配置
#define REGISTRATION_TIMEOUT 180000  // 3分钟注册超时
#define AT_TIMEOUT 5000
#define CHECK_INTERVAL 10000         // 每10秒检查一次状态

int currentAPNIndex = 0;
unsigned long testStartTime = 0;

// 简化的调试输出
void debugLog(String message) {
  Serial.print("[");
  Serial.print(millis()/1000);
  Serial.print("s] ");
  Serial.println(message);
}

// 读取BG77响应
String readBG77Response(int timeout) {
  String response = "";
  unsigned long startTime = millis();
  
  while (millis() - startTime < timeout) {
    if (Serial1.available()) {
      char c = Serial1.read();
      response += c;
      Serial.print(c);  // 实时显示响应
    }
  }
  return response;
}

// 发送AT命令并等待OK
bool sendATCommand(String command, int timeout = AT_TIMEOUT) {
  debugLog("发送: " + command);
  
  // 清空接收缓冲区
  while (Serial1.available()) Serial1.read();
  
  Serial1.println(command);
  String response = readBG77Response(timeout);
  
  return response.indexOf("OK") >= 0;
}

// 发送AT命令并等待特定响应
String sendATCommandGetResponse(String command, int timeout = AT_TIMEOUT) {
  debugLog("查询: " + command);
  
  while (Serial1.available()) Serial1.read();
  
  Serial1.println(command);
  return readBG77Response(timeout);
}

// 初始化BG77模块
bool initializeBG77() {
  debugLog("=== BG77模块初始化 ===");
  
  // 配置电源引脚
  pinMode(BG77_POWER_KEY, OUTPUT);
  digitalWrite(BG77_POWER_KEY, HIGH);
  
  // 初始化串口
  Serial1.begin(115200);
  delay(2000);
  
  // 检查模块是否响应
  debugLog("检查模块响应...");
  Serial1.println("ATI");
  String response = readBG77Response(3000);
  
  if (response.length() < 10) {
    debugLog("模块无响应,尝试唤醒...");
    
    // 执行唤醒序列
    digitalWrite(BG77_POWER_KEY, LOW);
    delay(1000);
    digitalWrite(BG77_POWER_KEY, HIGH);
    delay(2000);
    digitalWrite(BG77_POWER_KEY, LOW);
    delay(3000);
    
    // 再次检查
    Serial1.println("ATI");
    response = readBG77Response(5000);
    
    if (response.length() < 10) {
      debugLog("模块唤醒失败!");
      return false;
    }
  }
  
  debugLog("模块响应正常");
  return true;
}

// 基本配置
bool basicConfiguration() {
  debugLog("=== 基本配置 ===");
  
  // 基本AT测试
  if (!sendATCommand("AT")) {
    debugLog("基本AT命令失败!");
    return false;
  }
  
  // 关闭回显
  sendATCommand("ATE0");
  
  // 设置错误报告格式
  sendATCommand("AT+CMEE=2");
  
  // 检查SIM卡状态
  debugLog("检查SIM卡...");
  String simResponse = sendATCommandGetResponse("AT+CPIN?");
  if (simResponse.indexOf("+CPIN: READY") < 0) {
    debugLog("SIM卡未就绪! 请检查SIM卡插入和状态");
    debugLog("SIM卡响应: ");
    Serial.println(simResponse);
    return false;
  }
  debugLog("SIM卡状态正常");
  
  return true;
}

// 查询基本信息
void queryBasicInfo() {
  debugLog("=== 查询基本信息 ===");
  
  // IMEI
  debugLog("IMEI:");
  sendATCommandGetResponse("AT+CGSN");
  
  // IMSI
  debugLog("IMSI:");
  sendATCommandGetResponse("AT+CIMI");
  
  // 信号质量
  debugLog("信号质量:");
  String csqResponse = sendATCommandGetResponse("AT+CSQ");
  
  // 解析信号质量
  int rssiStart = csqResponse.indexOf("+CSQ: ") + 6;
  if (rssiStart > 5) {
    int rssi = csqResponse.substring(rssiStart, csqResponse.indexOf(",", rssiStart)).toInt();
    debugLog("RSSI值: " + String(rssi) + " (" + String(-113 + rssi * 2) + " dBm)");
    
    if (rssi < 10) {
      debugLog("警告: 信号质量较差,可能影响网络注册");
    }
  }
}

// 网络配置
bool configureNetwork(String apn) {
  debugLog("=== 配置网络 (APN: " + apn + ") ===");
  
  // 设置全功能模式
  debugLog("设置全功能模式...");
  if (!sendATCommand("AT+CFUN=1", 15000)) {
    debugLog("设置全功能模式失败");
    return false;
  }
  delay(3000);
  
  // 查询当前运营商
  debugLog("查询运营商:");
  sendATCommandGetResponse("AT+COPS?", 5000);
  
  // 设置网络选择为自动
  debugLog("设置网络选择模式...");
  sendATCommand("AT+COPS=0", 30000);  // 自动选择网络,可能需要较长时间
  
  // 配置APN
  debugLog("配置APN: " + apn);
  String apnCommand = "AT+CGDCONT=1,\"IP\",\"" + apn + "\"";
  if (!sendATCommand(apnCommand)) {
    debugLog("APN配置失败");
    return false;
  }
  
  // 附着到GPRS网络
  debugLog("附着到GPRS网络...");
  sendATCommand("AT+CGATT=1", 10000);
  
  return true;
}

// 检查网络注册状态
int checkRegistrationStatus() {
  // 检查CS域注册
  String creg = sendATCommandGetResponse("AT+CREG?");
  
  // 检查PS域注册  
  String cgreg = sendATCommandGetResponse("AT+CGREG?");
  
  // 检查EPS注册
  String cereg = sendATCommandGetResponse("AT+CEREG?");
  
  // 解析状态
  int csStatus = parseRegStatus(creg, "+CREG:");
  int psStatus = parseRegStatus(cgreg, "+CGREG:");
  int epsStatus = parseRegStatus(cereg, "+CEREG:");
  
  debugLog("注册状态 - CS:" + getStatusText(csStatus) + 
           " PS:" + getStatusText(psStatus) + 
           " EPS:" + getStatusText(epsStatus));
  
  // 返回最好的注册状态
  if (csStatus == 1 || psStatus == 1 || epsStatus == 1) return 1;  // 已注册
  if (csStatus == 5 || psStatus == 5 || epsStatus == 5) return 5;  // 漫游注册
  if (csStatus == 2 || psStatus == 2 || epsStatus == 2) return 2;  // 正在搜索
  if (csStatus == 3 || psStatus == 3 || epsStatus == 3) return 3;  // 注册被拒绝
  
  return 0;  // 未知状态
}

// 解析注册状态
int parseRegStatus(String response, String prefix) {
  int prefixPos = response.indexOf(prefix);
  if (prefixPos < 0) return -1;
  
  int commaPos = response.indexOf(",", prefixPos);
  if (commaPos < 0) return -1;
  
  int status = response.substring(commaPos + 1, commaPos + 2).toInt();
  return status;
}

// 状态文本转换
String getStatusText(int status) {
  switch (status) {
    case 0: return "未搜索";
    case 1: return "已注册";
    case 2: return "搜索中";
    case 3: return "被拒绝";
    case 4: return "未知";
    case 5: return "漫游";
    default: return "错误";
  }
}

// 网络注册测试主流程
bool testNetworkRegistration() {
  debugLog("=== 开始网络注册测试 ===");
  
  for (currentAPNIndex = 0; currentAPNIndex < APN_COUNT; currentAPNIndex++) {
    String currentAPN = APN_LIST[currentAPNIndex];
    debugLog("测试APN " + String(currentAPNIndex + 1) + "/" + String(APN_COUNT) + ": " + currentAPN);
    
    // 配置网络
    if (!configureNetwork(currentAPN)) {
      debugLog("APN " + currentAPN + " 配置失败,尝试下一个");
      continue;
    }
    
    // 等待网络注册
    debugLog("等待网络注册 (最长3分钟)...");
    unsigned long startTime = millis();
    
    while (millis() - startTime < REGISTRATION_TIMEOUT) {
      int status = checkRegistrationStatus();
      
      if (status == 1 || status == 5) {
        debugLog("✓ 网络注册成功! APN: " + currentAPN + " 状态: " + getStatusText(status));
        
        // 验证网络连接
        debugLog("验证网络连接...");
        sendATCommandGetResponse("AT+CGPADDR=1");
        
        return true;
      } else if (status == 3) {
        debugLog("✗ 网络注册被拒绝,APN可能不正确");
        break;  // 尝试下一个APN
      }
      
      debugLog("等待中... 状态: " + getStatusText(status));
      delay(CHECK_INTERVAL);
    }
    
    debugLog("✗ APN " + currentAPN + " 注册超时");
  }
  
  debugLog("✗ 所有APN测试失败");
  return false;
}

void setup() {
  // 初始化串口
  Serial.begin(115200);
  while (!Serial && millis() < 5000) delay(100);
  
  Serial.println("==========================================");
  Serial.println("RAK5860 BG77 网络注册专项测试");
  Serial.println("目标: 解决 +CEREG:0,2 网络注册问题");
  Serial.println("==========================================");
  
  testStartTime = millis();
  
  // 1. 初始化模块
  if (!initializeBG77()) {
    Serial.println("✗ 模块初始化失败,测试终止");
    while (1) delay(1000);
  }
  
  // 2. 基本配置
  if (!basicConfiguration()) {
    Serial.println("✗ 基本配置失败,测试终止");
    while (1) delay(1000);
  }
  
  // 3. 查询基本信息
  queryBasicInfo();
  
  // 4. 网络注册测试
  bool success = testNetworkRegistration();
  
  // 测试结果
  Serial.println("==========================================");
  if (success) {
    Serial.println("✓ 网络注册测试成功!");
    Serial.println("成功APN: " + APN_LIST[currentAPNIndex]);
  } else {
    Serial.println("✗ 网络注册测试失败");
    Serial.println("建议:");
    Serial.println("1. 检查SIM卡是否支持数据业务");
    Serial.println("2. 确认所在地区有足够的信号覆盖");
    Serial.println("3. 联系运营商确认正确的APN设置");
    Serial.println("4. 尝试在不同位置测试");
  }
  
  unsigned long testDuration = (millis() - testStartTime) / 1000;
  Serial.println("测试耗时: " + String(testDuration) + " 秒");
  Serial.println("==========================================");
}

void loop() {
  // 如果注册成功,定期检查状态
  static unsigned long lastCheck = 0;
  
  if (millis() - lastCheck > 30000) {  // 每30秒检查一次
    debugLog("定期状态检查:");
    checkRegistrationStatus();
    
    // 查询信号质量
    sendATCommandGetResponse("AT+CSQ");
    
    lastCheck = millis();
  }
  
  // 处理用户输入的AT命令
  if (Serial.available()) {
    String userInput = Serial.readStringUntil('\n');
    userInput.trim();
    
    if (userInput.length() > 0) {
      if (userInput.startsWith("AT")) {
        // 直接转发AT命令
        Serial1.println(userInput);
        readBG77Response(5000);
      } else if (userInput.equals("STATUS")) {
        // 快速状态检查
        checkRegistrationStatus();
        sendATCommandGetResponse("AT+CSQ");
      } else if (userInput.equals("RESTART")) {
        // 重启测试
        Serial.println("重启网络注册测试...");
        testNetworkRegistration();
      } else {
        Serial.println("可用命令: STATUS, RESTART, 或任何AT命令");
      }
    }
  }
  
  delay(1000);
}

Welcome to RAK forum @SAYHELLO ,

Looking on your code, it seems you are scanning APN but I am not sure if you really get connection.

Some ideas:

  1. Secure the correct APN to use.
  2. Check on the operator how you can connect to its NB-IoT or LTE-M service.
  3. Ensure that the 4G/5G SIM card you have works on the NB-IoT or LTE-M service of your network provider.

Btw, you can check this example code how to initialize the connection. However, you have to check your APN setting (you have to change the example code for your own APN) and if your telco provider can support your needs for NB-IoT or LTE-M connection.