Wio BG770AとSORACOM 入門: LTE接続編
はじめに
概要編で把握したアーキテクチャを前提に、本編では BG770A の初期化、SORACOM への接続、までを一気通貫で整理します。WioCellular/WioNetwork API の呼び出し順を把握しつつ、実機デバッグや省電力の第一歩まで掘り下げていきましょう。
SORACOM に接続する
まずはプログラムを動かす
下記の折りたたみを開いてプログラムをコピーしてください。
cellular_basic.ino
/*
* cellular_basic.ino
* Focused demo that runs only the modem initialization sequence.
*
*
* Steps:
* 1. Configure radio profile (APN / RAT / band)
* 2. Start the WioCellular stack
* 3. Power on the BG770A module
* 4. Bring up WioNetwork (PDP context)
* 5. Wait until the modem reports communication available
*
* Each stage emits detailed Serial logs so you can trace failures.
*/
#include <Adafruit_TinyUSB.h>
#include <WioCellular.h>
#include <cctype>
#include <string>
#include <vector>
static constexpr auto SEARCH_ACCESS_TECHNOLOGY =
WioCellularNetwork::SearchAccessTechnology::LTEM;
static constexpr auto LTEM_BAND = WioCellularNetwork::ALL_LTEM_BAND;
static constexpr int SEARCH_ACCESS_TECHNOLOGY_MODE =
static_cast<int>(SEARCH_ACCESS_TECHNOLOGY);
static const char APN[] = "soracom.io";
static constexpr int PDP_CONTEXT_ID = 1;
static constexpr int PDP_AUTH_MODE = 2; // default to CHAP
static constexpr int PDP_AUTH_MODE_QICSGP = PDP_AUTH_MODE;
static const char PDP_AUTH_USER[] = "sora";
static const char PDP_AUTH_PASSWORD[] = "sora";
static constexpr int POWER_ON_TIMEOUT = 1000 * 20;
static constexpr int NETWORK_TIMEOUT = 1000 * 60 * 3;
static constexpr int LOOP_IDLE_MS = 1000;
static constexpr int CONSOLE_WAIT_TIMEOUT = 1000 * 10;
static void waitForConsole();
static void logResult(const char *label, WioCellularResult result);
static bool logModemAndSimIdentity();
static bool logIdentityField(const char *label, WioCellularResult result,
const std::string &value);
static bool logNetworkAttachDetails();
static void logPowerSavingConfiguration();
static const char *operatorSelectionModeToString(int mode);
static const char *operatorFormatToString(int format);
static const char *accessTechnologyToString(int act);
static std::string trim(const std::string &text);
static std::vector<std::string> parseQnwinfoFields(const std::string &payload);
static bool applyNetworkProfile();
static bool startCellularStack();
static bool powerOnModem();
static bool startNetworkService();
static bool awaitNetworkReady();
static bool configurePdpAuthentication();
void setup() {
Serial.begin(115200);
waitForConsole();
pinMode(LED_BUILTIN, OUTPUT);
Serial.println();
Serial.println("=== Wio BG770A modem initialization ===");
digitalWrite(LED_BUILTIN, HIGH);
const bool success = applyNetworkProfile() && startCellularStack() &&
powerOnModem() && startNetworkService() &&
awaitNetworkReady();
digitalWrite(LED_BUILTIN, LOW);
if (success) {
Serial.println("[INIT] Modem initialization completed successfully.");
} else {
Serial.println("[INIT] Modem initialization failed. See logs above.");
}
}
void loop() {
WioCellular.doWorkUntil(LOOP_IDLE_MS);
}
static void waitForConsole() {
const uint32_t start = millis();
while (!Serial && millis() - start < CONSOLE_WAIT_TIMEOUT) {
delay(10);
}
delay(250); // allow host to settle before printing first logs
}
static void logStepHeader(const char *title, int stepIndex, int totalSteps) {
Serial.printf("[INIT][%d/%d] %s\n", stepIndex, totalSteps, title);
}
static bool applyNetworkProfile() {
logStepHeader("Applying radio profile", 1, 5);
WioNetwork.config.apn = APN;
WioNetwork.config.searchAccessTechnology = SEARCH_ACCESS_TECHNOLOGY;
WioNetwork.config.ltemBand = LTEM_BAND;
WioNetwork.config.pdpContextId = PDP_CONTEXT_ID;
Serial.printf(" APN: %s\n", WioNetwork.config.apn.c_str());
Serial.printf(" Search RAT: %d\n", SEARCH_ACCESS_TECHNOLOGY_MODE);
Serial.printf(" LTE-M band mask: %s\n",
WioNetwork.config.ltemBand.c_str());
Serial.printf(" PDP CID: %d\n", WioNetwork.config.pdpContextId);
const char *authDescription =
(PDP_AUTH_MODE == 0) ? "none"
: (PDP_AUTH_MODE == 1) ? "PAP"
: (PDP_AUTH_MODE == 2) ? "CHAP" : "unknown";
Serial.printf(" Auth mode: %d (%s), user: %s\n", PDP_AUTH_MODE,
authDescription, PDP_AUTH_USER);
const bool validApn = !WioNetwork.config.apn.empty();
Serial.printf(" Check: APN string is %s\n", validApn ? "set" : "empty");
Serial.printf(" Result: %s\n", validApn ? "OK" : "FAILED (APN missing)");
return validApn;
}
static bool startCellularStack() {
logStepHeader("Booting WioCellular stack", 2, 5);
WioCellular.begin();
Serial.println(" WioCellular.begin() completed");
Serial.println(" Check: API has no status; success assumed if it returns");
Serial.println(" Result: OK");
return true;
}
static bool powerOnModem() {
logStepHeader("Powering on BG770A module", 3, 5);
Serial.printf(" Timeout: %d ms\n", POWER_ON_TIMEOUT);
const auto result = WioCellular.powerOn(POWER_ON_TIMEOUT);
logResult(" powerOn", result);
const bool powered = result == WioCellularResult::Ok;
Serial.printf(" Check: WioCellular.powerOn() returned %s\n",
WioCellularResultToString(result));
bool identityOk = false;
if (powered) {
identityOk = logModemAndSimIdentity();
Serial.printf(" Check: identity snapshot %s\n",
identityOk ? "OK" : "FAILED");
}
const bool stepOk = powered && identityOk;
Serial.printf(" Result: %s\n", stepOk ? "OK" : "FAILED");
return stepOk;
}
static bool startNetworkService() {
logStepHeader("Starting WioNetwork service (PDP context)", 4, 5);
WioNetwork.begin();
Serial.println(" WioNetwork.begin() issued");
Serial.println(" Check: API has no status; success assumed if it returns");
const bool authOk = configurePdpAuthentication();
Serial.printf(" Check: PDP authentication %s\n",
authOk ? "OK (at least one command accepted)"
: "FAILED (both commands rejected)");
Serial.printf(" Result: %s\n", authOk ? "OK" : "FAILED");
return authOk;
}
static bool awaitNetworkReady() {
logStepHeader("Waiting for communication availability", 5, 5);
Serial.printf(" Timeout: %d ms\n", NETWORK_TIMEOUT);
if (!WioNetwork.waitUntilCommunicationAvailable(NETWORK_TIMEOUT)) {
Serial.println(
" Check: waitUntilCommunicationAvailable() returned false");
Serial.println(" Result: TIMEOUT (EPS not registered)");
WioCellular.powerOff();
return false;
}
Serial.println(" Check: waitUntilCommunicationAvailable() returned true");
const bool detailOk = logNetworkAttachDetails();
Serial.printf(" Check: attachment detail query %s\n",
detailOk ? "OK" : "FAILED");
logPowerSavingConfiguration();
Serial.println(" Result: READY (EPS registered)");
return true;
}
static void logResult(const char *label, WioCellularResult result) {
Serial.printf("%s -> %s\n", label, WioCellularResultToString(result));
}
static bool logIdentityField(const char *label, WioCellularResult result,
const std::string &value) {
Serial.printf(" %s -> %s\n", label, WioCellularResultToString(result));
if (result == WioCellularResult::Ok) {
Serial.printf(" value: %s\n", value.c_str());
}
return result == WioCellularResult::Ok;
}
static bool logModemAndSimIdentity() {
Serial.println(" Capturing modem/SIM identity...");
bool ok = true;
{
std::string model;
const auto result = WioCellular.queryCommand(
"AT+CGMM", [&model](const std::string &response) -> bool {
model = response;
return true;
},
300);
ok &= logIdentityField("Modem model (AT+CGMM)", result, model);
}
{
std::string revision;
const auto result = WioCellular.getModemInfo(&revision);
ok &= logIdentityField("Firmware revision (AT+QGMR)", result, revision);
}
{
std::string imei;
const auto result = WioCellular.getIMEI(&imei);
ok &= logIdentityField("IMEI (AT+GSN)", result, imei);
}
{
std::string imsi;
const auto result = WioCellular.getIMSI(&imsi);
ok &= logIdentityField("IMSI (AT+CIMI)", result, imsi);
}
{
std::string iccid;
const auto result = WioCellular.getSimCCID(&iccid);
ok &= logIdentityField("ICCID (AT+QCCID)", result, iccid);
}
return ok;
}
static bool logNetworkAttachDetails() {
Serial.println(" Querying operator/band information...");
bool ok = true;
{
int mode = 0;
int format = 0;
std::string oper;
int act = 0;
const auto result =
WioCellular.getOperator(&mode, &format, &oper, &act);
Serial.printf(" getOperator -> %s\n",
WioCellularResultToString(result));
if (result == WioCellularResult::Ok) {
Serial.printf(" operator: %s\n", oper.c_str());
Serial.printf(" mode: %d (%s)\n", mode,
operatorSelectionModeToString(mode));
Serial.printf(" format: %d (%s)\n", format,
operatorFormatToString(format));
Serial.printf(" access tech: %d (%s)\n", act,
accessTechnologyToString(act));
} else {
ok = false;
}
}
{
std::string payload;
const auto result = WioCellular.queryCommand(
"AT+QNWINFO", [&payload](const std::string &response) -> bool {
if (response.rfind("+QNWINFO:", 0) == 0) {
payload = response.substr(10);
return true;
}
return false;
},
500);
Serial.printf(" AT+QNWINFO -> %s\n",
WioCellularResultToString(result));
if (result == WioCellularResult::Ok && !payload.empty()) {
payload = trim(payload);
Serial.printf(" raw: %s\n", payload.c_str());
const auto fields = parseQnwinfoFields(payload);
if (fields.size() >= 1) {
Serial.printf(" RAT: %s\n", fields[0].c_str());
}
if (fields.size() >= 2) {
Serial.printf(" Operator (MCC/MNC): %s\n", fields[1].c_str());
}
if (fields.size() >= 3) {
Serial.printf(" Band: %s\n", fields[2].c_str());
}
if (fields.size() >= 4) {
Serial.printf(" Channel/Frequency: %s\n", fields[3].c_str());
}
} else {
ok = false;
}
}
return ok;
}
static void logPowerSavingConfiguration() {
Serial.println(" Querying power-saving configuration...");
{
std::string response;
const auto result = WioCellular.queryCommand(
"AT+CEDRXS?", [&response](const std::string &res) -> bool {
if (res.rfind("+CEDRXS:", 0) == 0) {
response = res;
return true;
}
return false;
},
500);
Serial.printf(" AT+CEDRXS? -> %s\n",
WioCellularResultToString(result));
if (!response.empty()) {
Serial.printf(" %s\n", response.c_str());
}
}
{
std::string response;
const auto result = WioCellular.queryCommand(
"AT+CEDRXRDP", [&response](const std::string &res) -> bool {
if (res.rfind("+CEDRXRDP:", 0) == 0) {
response = res;
return true;
}
return false;
},
500);
Serial.printf(" AT+CEDRXRDP -> %s\n",
WioCellularResultToString(result));
if (!response.empty()) {
Serial.printf(" %s\n", response.c_str());
}
}
{
std::string response;
const auto result = WioCellular.queryCommand(
"AT+CPSMS?", [&response](const std::string &res) -> bool {
if (res.rfind("+CPSMS:", 0) == 0) {
response = res;
return true;
}
return false;
},
500);
Serial.printf(" AT+CPSMS? -> %s\n",
WioCellularResultToString(result));
if (!response.empty()) {
Serial.printf(" %s\n", response.c_str());
}
}
{
std::string response;
const auto result = WioCellular.queryCommand(
"AT+QPSMS?", [&response](const std::string &res) -> bool {
if (res.rfind("+QPSMS:", 0) == 0) {
response = res;
return true;
}
return false;
},
500);
Serial.printf(" AT+QPSMS? -> %s\n",
WioCellularResultToString(result));
if (!response.empty()) {
Serial.printf(" %s\n", response.c_str());
}
}
}
static const char *operatorSelectionModeToString(int mode) {
switch (mode) {
case 0:
return "automatic";
case 1:
return "manual";
case 2:
return "deregistered";
case 3:
return "set only";
case 4:
return "manual/automatic";
default:
return "unknown";
}
}
static const char *operatorFormatToString(int format) {
switch (format) {
case 0:
return "long alphanumeric";
case 1:
return "short alphanumeric";
case 2:
return "numeric (MCCMNC)";
default:
return "unknown";
}
}
static const char *accessTechnologyToString(int act) {
switch (act) {
case 0:
return "GSM";
case 1:
return "GSM Compact";
case 2:
return "UTRAN";
case 3:
return "GSM w/EGPRS";
case 4:
return "UTRAN w/HSDPA";
case 5:
return "UTRAN w/HSUPA";
case 6:
return "UTRAN w/HSDPA+HSUPA";
case 7:
return "E-UTRAN (LTE)";
case 8:
return "E-UTRAN (NB-IoT)";
case 9:
return "E-UTRAN (LTE-M)";
default:
return "unknown";
}
}
static std::string trim(const std::string &text) {
size_t start = 0;
while (start < text.size() &&
std::isspace(static_cast<unsigned char>(text[start]))) {
++start;
}
size_t end = text.size();
while (end > start &&
std::isspace(static_cast<unsigned char>(text[end - 1]))) {
--end;
}
return text.substr(start, end - start);
}
static std::vector<std::string> parseQnwinfoFields(
const std::string &payload) {
std::vector<std::string> fields;
std::string current;
bool inQuote = false;
for (char ch : payload) {
if (ch == '"') {
inQuote = !inQuote;
continue;
}
if (ch == ',' && !inQuote) {
fields.push_back(trim(current));
current.clear();
} else {
current.push_back(ch);
}
}
if (!current.empty()) {
fields.push_back(trim(current));
}
return fields;
}
static bool configurePdpAuthentication() {
Serial.printf(
" Configuring PDP authentication (cid=%d, mode=%d, user=%s)\n",
PDP_CONTEXT_ID, PDP_AUTH_MODE, PDP_AUTH_USER);
std::string command = "AT+CGAUTH=" + std::to_string(PDP_CONTEXT_ID) + "," +
std::to_string(PDP_AUTH_MODE) + ",\"" +
PDP_AUTH_USER + "\",\"" + PDP_AUTH_PASSWORD + "\"";
Serial.printf(" Command: %s\n", command.c_str());
const auto result = WioCellular.executeCommand(command.c_str(), 500);
Serial.printf(" Check: AT+CGAUTH returned %s\n",
WioCellularResultToString(result));
bool ok = result == WioCellularResult::Ok;
if (!ok) {
Serial.println(" Note: CGAUTH rejected (probably not required by SIM)");
Serial.println(" CGAUTH failed, trying AT+QICSGP fallback");
std::string fallback =
"AT+QICSGP=" + std::to_string(PDP_CONTEXT_ID) + ",1,\"" + APN +
"\",\"" + PDP_AUTH_USER + "\",\"" + PDP_AUTH_PASSWORD + "\"," +
std::to_string(PDP_AUTH_MODE_QICSGP);
Serial.printf(" Command: %s\n", fallback.c_str());
const auto fbResult = WioCellular.executeCommand(fallback.c_str(), 1000);
Serial.printf(" Check: AT+QICSGP returned %s\n",
WioCellularResultToString(fbResult));
ok = fbResult == WioCellularResult::Ok;
}
Serial.printf(" Auth Result: %s\n", ok ? "OK" : "FAILED");
return ok;
}
このサンプルは BG770A の初期化手順を最小限のコードにまとめたもので、後続の節で参照する関数をすべて含んでいます。
書き込み手順はこちらの記事を参照してください。書き込み後、以下の手順でモデム初期化と SORACOM への接続を確認します。
シリアルログの読み方
シリアルモニタでモニタリングをすると、以下のようなログが出力されます。[INIT][1/5] から [5/5] までの流れが EPS Attach 完了 (= waitUntilCommunicationAvailable() が true) へ向かうシーケンスです。
Reconnecting to /dev/cu.usbmodem1101 . Connected!
=== Wio BG770A modem initialization ===
[INIT][1/5] Applying radio profile
APN: soracom.io
Search RAT: 1
LTE-M band mask: 0x2000000000f0e189f
PDP CID: 1
Auth mode: 2 (CHAP), user: sora
Check: APN string is set
Result: OK
[INIT][2/5] Booting WioCellular stack
WioCellular.begin() completed
Check: API has no status; success assumed if it returns
Result: OK
[INIT][3/5] Powering on BG770A module
Timeout: 20000 ms
powerOn -> Ok
Check: WioCellular.powerOn() returned Ok
Capturing modem/SIM identity...
Modem model (AT+CGMM) -> Ok
value: BG770A-GL
Firmware revision (AT+QGMR) -> Ok
value: BG770AGLAAR02A05_JP_01.200.01.200
IMEI (AT+GSN) -> Ok
value: 8655xxxxxxxxxxxx
IMSI (AT+CIMI) -> Ok
value: 295xxxxxxxxxxxxx
ICCID (AT+QCCID) -> Ok
value: 8942310xxxxxxxxxxxxxxxx
Check: identity snapshot OK
Result: OK
[INIT][4/5] Starting WioNetwork service (PDP context)
WioNetwork.begin() issued
Check: API has no status; success assumed if it returns
Configuring PDP authentication (cid=1, mode=2, user=sora)
Command: AT+CGAUTH=1,2,"sora","sora"
Check: AT+CGAUTH returned Ok
Auth Result: OK
Check: PDP authentication OK (at least one command accepted)
Result: OK
[INIT][5/5] Waiting for communication availability
Timeout: 180000 ms
Check: waitUntilCommunicationAvailable() returned true
Querying operator/band information...
getOperator -> Ok
operator: NTT DOCOMO
mode: 0 (automatic)
format: 0 (long alphanumeric)
access tech: 7 (E-UTRAN (LTE))
AT+QNWINFO -> Ok
raw: "eMTC","440","LTE BAND 19",6100
RAT: eMTC
Operator (MCC/MNC): 440
Band: LTE BAND 19
Channel/Frequency: 6100
Check: attachment detail query OK
Querying power-saving configuration...
AT+CEDRXS? -> Ok
+CEDRXS: 0
AT+CEDRXRDP -> Ok
+CEDRXRDP: 0
AT+CPSMS? -> Ok
+CPSMS: 0,,,"00101100","00001010"
AT+QPSMS? -> Ok
+QPSMS: 0,,,"43200","20"
Result: READY (EPS registered)
[INIT] Modem initialization completed successfully.
プログラムは以下のような構成になっています。図は初期化シーケンスを整理したものです。
モデム初期化フロー
モデム初期化フロー
cellular_basic.ino では 5 段階のログを出しながらモデムを初期化しています。以下の表は [INIT][n/5] ログと対応する各ステップの役割を整理したものです。順番を守ることで、BG770A への給電、UART 設定、URC 待受が安定します。
| Step | 関数 | 役割 | 補足 |
|---|---|---|---|
| 1 | applyNetworkProfile() |
WioNetwork.config.apn や searchAccessTechnology(例: LTEM)を設定し、LTE-M 用バンドマスクを ltemBand に適用する |
APN が空なら即座に FAILED を返し、以降の処理へ進ませない |
| 2 | startCellularStack() |
WioCellular.begin() で内部タスク・UART を起動する |
戻り値がないため「戻ってきた=成功」。ログでは API 仕様も併記 |
| 3 | powerOnModem() |
WioCellular.powerOn(POWER_ON_TIMEOUT) を発行して BG770A を給電起動し、WioCellularResult::Ok を確認する |
成功時に logModemAndSimIdentity() を呼び、AT+CGMM や AT+CIMI の結果を記録して SIM 情報のベースラインを残す |
| 4 | startNetworkService() |
WioNetwork.begin() で PDP コンテキストを立ち上げ、configurePdpAuthentication() が AT+CGAUTH→AT+QICSGP を試行する |
SORACOM SIM では認証不要なため CommandRejected が想定内。少なくとも 1 コマンドが受理されたかログで判断する |
| 5 | awaitNetworkReady() |
WioNetwork.waitUntilCommunicationAvailable(NETWORK_TIMEOUT) が true になるまで待機し、失敗時は WioCellular.powerOff() で安全に落とす |
成功後は logNetworkAttachDetails() が getOperator() / AT+QNWINFO を整形して表示し、バンドや RAT を確認する |
初期化全体は以下のような合成条件で評価され、途中で一つでも失敗するとログに「FAILED」と表示されます。
const bool success = applyNetworkProfile() && startCellularStack() &&
powerOnModem() && startNetworkService() &&
awaitNetworkReady();
それではここから各関数の詳細を見ていきましょう。
1. モデム制御フロー関連
初期化時に呼び出されるモデム制御系 API を一覧化しました。次の表を見ながら、どの関数がどの ATコマンドを叩いているかを追うとログ解析がしやすくなります。
| 関数 (呼び出し元) | API | 主な引数 | 役割/注意点 |
|---|---|---|---|
startCellularStack() |
WioCellular.begin() |
なし | WioCellular 内部タスク・UART を初期化。戻り値なしのため「戻って来た=成功」として扱う。 |
powerOnModem() |
WioCellular.powerOn(int timeout) |
timeout (ms) |
PWRKEY 制御と RDY URC 待ち。timeout 内に AT 応答がなければ WioCellularResult::RdyTimeout が返るので、結果を必ずログ化して失敗時は後続処理を止める。 |
awaitNetworkReady() |
WioNetwork.begin() |
なし | PDP コンテキスト起動と URC 購読を開始。 |
WioNetwork.waitUntilCommunicationAvailable(int timeout) |
timeout (ms) |
EPS Attach を待機。false の場合は WioCellular.powerOff() でモデムを安全に落としている。 |
|
WioCellular.powerOff() |
なし | Attach 失敗時のリカバリで呼び出し。PWRKEY 制御と電源断を行う。 |
2. SIM/モデム情報の取得 (logModemAndSimIdentity)
IMSI や ICCID などの識別情報を取得するコマンドを次の表にまとめました。logModemAndSimIdentity() の出力と突き合わせる際に参照してください。
static bool logModemAndSimIdentity() {
Serial.println(" Capturing modem/SIM identity...");
bool ok = true;
{
std::string model;
const auto result = WioCellular.queryCommand(
"AT+CGMM", [&model](const std::string &response) -> bool {
model = response;
return true;
},
300);
ok &= logIdentityField("Modem model (AT+CGMM)", result, model);
}
{
std::string revision;
const auto result = WioCellular.getModemInfo(&revision);
ok &= logIdentityField("Firmware revision (AT+QGMR)", result, revision);
}
{
std::string imei;
const auto result = WioCellular.getIMEI(&imei);
ok &= logIdentityField("IMEI (AT+GSN)", result, imei);
}
{
std::string imsi;
const auto result = WioCellular.getIMSI(&imsi);
ok &= logIdentityField("IMSI (AT+CIMI)", result, imsi);
}
{
std::string iccid;
const auto result = WioCellular.getSimCCID(&iccid);
ok &= logIdentityField("ICCID (AT+QCCID)", result, iccid);
}
return ok;
}
| API | 呼び出し | 返り値 | 補足 |
|---|---|---|---|
WioCellular.queryCommand("AT+CGMM", handler, timeout) |
モデム型番取得 | WioCellularResult |
プレーン AT コマンド呼び出し。ラムダで応答文字列を取り出し、モデル名をシリアルに表示。 |
WioCellular.getModemInfo(std::string *revision) |
FW バージョン取得 | WioCellularResult |
内部で AT+QGMR を実行。nullptr を渡すと格納されない点に注意。 |
WioCellular.getIMEI(std::string *imei) |
IMEI 取得 | WioCellularResult |
AT+GSN をラップ。 |
WioCellular.getIMSI(std::string *imsi) |
SIM IMSI 取得 | WioCellularResult |
AT+CIMI をラップ。PIN ロック時は失敗するのでログを確認する。 |
WioCellular.getSimCCID(std::string *iccid) |
ICCID 取得 | WioCellularResult |
AT+QCCID のパース結果。 |
各 API は WioCellularResult::Ok を成功条件としており、logIdentityField() で結果と値をセットでシリアル表示している (cellular_basic.ino 102-149 行付近)。
3. ネットワーク詳細の確認 (logNetworkAttachDetails)
EPS Attach 後に確認したい項目(キャリア名や利用中バンド)を logNetworkAttachDetails() では一括取得します。表ではコマンドごとの取得内容を整理しています。
static bool logNetworkAttachDetails() {
Serial.println(" Querying operator/band information...");
bool ok = true;
{
int mode = 0;
int format = 0;
std::string oper;
int act = 0;
const auto result =
WioCellular.getOperator(&mode, &format, &oper, &act);
Serial.printf(" getOperator -> %s\n",
WioCellularResultToString(result));
if (result == WioCellularResult::Ok) {
Serial.printf(" operator: %s\n", oper.c_str());
Serial.printf(" mode: %d (%s)\n", mode,
operatorSelectionModeToString(mode));
Serial.printf(" format: %d (%s)\n", format,
operatorFormatToString(format));
Serial.printf(" access tech: %d (%s)\n", act,
accessTechnologyToString(act));
} else {
ok = false;
}
}
{
std::string payload;
const auto result = WioCellular.queryCommand(
"AT+QNWINFO", [&payload](const std::string &response) -> bool {
if (response.rfind("+QNWINFO:", 0) == 0) {
payload = response.substr(10);
return true;
}
return false;
},
500);
Serial.printf(" AT+QNWINFO -> %s\n",
WioCellularResultToString(result));
if (result == WioCellularResult::Ok && !payload.empty()) {
payload = trim(payload);
Serial.printf(" raw: %s\n", payload.c_str());
const auto fields = parseQnwinfoFields(payload);
if (fields.size() >= 1) {
Serial.printf(" RAT: %s\n", fields[0].c_str());
}
if (fields.size() >= 2) {
Serial.printf(" Operator (MCC/MNC): %s\n", fields[1].c_str());
}
if (fields.size() >= 3) {
Serial.printf(" Band: %s\n", fields[2].c_str());
}
if (fields.size() >= 4) {
Serial.printf(" Channel/Frequency: %s\n", fields[3].c_str());
}
} else {
ok = false;
}
}
return ok;
}
| API/コマンド | 取得情報 | メモ |
|---|---|---|
WioCellular.getOperator(&mode, &format, &oper, &act) |
選択キャリア名/PLMN 表示フォーマット/アクセス技術 | mode=0 なら自動選択、format=0 は長いアルファベット表記、act=7/9 などで RAT を判別。 |
WioCellular.queryCommand("AT+QNWINFO", handler, 500) |
RAT、MCC/MNC、利用中バンド、周波数 | 例: "eMTC","440","LTE BAND 19",6100。パース用に parseQnwinfoFields() を実装し、ダブルクォーテーションを除去して表示。 |
WioCellular.queryCommand("AT+CGPADDR=<cid>", handler, 500) |
PDP アドレス (IPv4) |
+CGPADDR: 1,"10.xxx.xxx.xxx" を引用符でパースし、PDP CID n IP: ... とログ出力。レスポンスが空/異常な場合は value: <empty> を出して失敗を明示。 |
AT+CGPADDR は logAssignedIpAddress() として logNetworkAttachDetails() の直後に呼び出され、Attach が成功しても IP が未取得のケースを即座に検出できるようになった。どのコマンドも [INIT][5/5] ログで続けて表示される。
4. 電力制御 (eDRX/PSM) 状態の確認 (logPowerSavingConfiguration)
BG770AはLTE-Mの通信モジュールであるため省電力関連の機能を使ってこそ威力を発揮します。逆に省電力機能を使わないのであれば積極的にwioBG770Aを選ぶ理由はありません。各コマンドの役割を押さえてから logPowerSavingConfiguration() のログを読むと状況を把握しやすくなります。
static void logPowerSavingConfiguration() {
Serial.println(" Querying power-saving configuration...");
{
std::string response;
const auto result = WioCellular.queryCommand(
"AT+CEDRXS?", [&response](const std::string &res) -> bool {
if (res.rfind("+CEDRXS:", 0) == 0) {
response = res;
return true;
}
return false;
},
500);
Serial.printf(" AT+CEDRXS? -> %s\n",
WioCellularResultToString(result));
if (!response.empty()) {
Serial.printf(" %s\n", response.c_str());
}
}
{
std::string response;
const auto result = WioCellular.queryCommand(
"AT+CEDRXRDP", [&response](const std::string &res) -> bool {
if (res.rfind("+CEDRXRDP:", 0) == 0) {
response = res;
return true;
}
return false;
},
500);
Serial.printf(" AT+CEDRXRDP -> %s\n",
WioCellularResultToString(result));
if (!response.empty()) {
Serial.printf(" %s\n", response.c_str());
}
}
{
std::string response;
const auto result = WioCellular.queryCommand(
"AT+CPSMS?", [&response](const std::string &res) -> bool {
if (res.rfind("+CPSMS:", 0) == 0) {
response = res;
return true;
}
return false;
},
500);
Serial.printf(" AT+CPSMS? -> %s\n",
WioCellularResultToString(result));
if (!response.empty()) {
Serial.printf(" %s\n", response.c_str());
}
}
{
std::string response;
const auto result = WioCellular.queryCommand(
"AT+QPSMS?", [&response](const std::string &res) -> bool {
if (res.rfind("+QPSMS:", 0) == 0) {
response = res;
return true;
}
return false;
},
500);
Serial.printf(" AT+QPSMS? -> %s\n",
WioCellularResultToString(result));
if (!response.empty()) {
Serial.printf(" %s\n", response.c_str());
}
}
}
| コマンド | 内容 | 備考 |
|---|---|---|
AT+CEDRXS? |
eDRX 設定 (要求値) |
+CEDRXS: <mode>,<act>,<Requested_eDRX_value>。戻り値 0 なら無効。 |
AT+CEDRXRDP |
eDRX 実施状況 |
+CEDRXRDP: <mode>,<act>,<Paging_time_window>,<eDRX_cycle> 形式。 |
AT+CPSMS? |
3GPP PSM タイマ |
+CPSMS: <mode>,,,<Requested_Periodic_TAU>,<Requested_Active_Time>。mode=0 で PSM 無効。 |
AT+QPSMS? |
Quectel 拡張 PSM 状態 |
+QPSMS: <mode>,,,<Periodic_TAU_sec>,<Active_Time_sec>。 |
すべて WioCellular.queryCommand() を利用しており、応答が存在した場合のみ raw 文字列を記録 (cellular_basic.ino 232-279 行)。
まとめと次回予告
ここまでで、Wio BG770A モデムの初期化手順とシリアルログへの表示ができるようになりました。
いよいよ、データを送受信するための準備が整いました。次回は インターフェースを接続してセンサからのデータ取得と各種プロトコルでのデータ送信を試していきます。
Discussion