pdf-icon

Arduino入門

2. デバイス&サンプル

6. アプリケーション

Cap LoRa868 Arduino 使用チュートリアル

1. 準備作業

注意
TinyGPSPlus ライブラリは GitHub 上から M5Stack デバイスに適合したバージョンをダウンロードしてください。ライブラリページ: TinyGPSPlus - M5Stack GitHub。Arduino Library からはダウンロードしないでください。(不明点がある場合は こちらのチュートリアル を参照してください)

2. コンパイルと書き込み

  • Cardputer-Adv 側面の電源スイッチを OFF にし、起動前に G0 ボタンを押しながら、背面から給電後にボタンを離すとデバイスはダウンロードモードに入り、書き込み待機します。

  • Arduino IDE 左上のコンパイル&書き込みボタンをクリックし、プログラムがコンパイル・アップロードされるのを待ちます。

3. サンプルプログラム

  • 本チュートリアルで使用するメインコントローラは Cardputer-Adv と Cap LoRa868 の組み合わせです。Cap LoRa868 の LoRa 部分は SPI 通信方式を採用し、GPS 部分はシリアル通信方式を採用しています。実際の回路接続に応じてプログラム内のピン定義を修正してください。接続後の SPI IO は G5 (NSS)G4 (TRQ)G3 (RST)G6 (BUSY)、シリアル IO は G15 (RX)G13 (TX) です。

実機の接続組み立て例は以下の図のとおりです:

3.1 LoRa 使用例

送信例

cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
#include <M5Unified.h>
#include <RadioLib.h>

#define LORA_BW           125.0f // SX1262 bandwidth (kHz) - 125kHz is common for long-range
#define LORA_SF           12     // Spreading factor (6-12) - higher = longer range, slower data rate
#define LORA_CR           5      // Coding rate (5-8, represents 4/5 to 4/8) - higher = more error correction
#define LORA_FREQ         868.0  // Carrier frequency (MHz) - 868MHz is EU ISM band for SX1262
#define LORA_SYNC_WORD    0x34   // Sync word for packet recognition - must match between transmitter/receiver
#define LORA_TX_POWER     22     // Transmission power (dBm) - 22dBm is maximum for many regions
#define LORA_PREAMBLE_LEN 20     // Preamble length (symbols) - ensures receiver can detect packet start

//         SX1262 PIN         NSS,       IRQ,         RST,        BUSY 
SX1262 radio = new Module(GPIO_NUM_5, GPIO_NUM_4, GPIO_NUM_3, GPIO_NUM_6);

// Tracks the result of the last transmission attempt (error code from RadioLib)
int transmissionState = RADIOLIB_ERR_NONE;

// Special attribute for ESP8266/ESP32 to place ISR in RAM (faster interrupt response)
#if defined(ESP8266) || defined(ESP32)
ICACHE_RAM_ATTR
#endif
// Packet sent flag
volatile bool transmittedFlag = false;
// This function is called when a complete packet is transmitted by the module
// IMPORTANT: this function MUST be 'void' type and MUST NOT have any arguments!
void setFlag(void)
{
    // we sent a packet, set the flag
    transmittedFlag = true;
}

void setup()
{
    M5.begin();
    Serial.begin(115200);
    M5.Display.setFont(&fonts::FreeMonoBold9pt7b);

    // Init SX1262
    Serial.print(F("[LoRa] Initializing ... "));
    int state =
        radio.begin(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR, LORA_SYNC_WORD, LORA_TX_POWER, LORA_PREAMBLE_LEN, 3.0, true);
    if (state == RADIOLIB_ERR_NONE) {
        Serial.println(F("Init success!"));
    } else {
        Serial.print(F("Init failed, code: "));
        Serial.println(state);
        while (true) { delay(10); }
    }
    radio.setCurrentLimit(140); radio.setCurrentLimit(140);// Current range: 0-140mA , step 2.5mA

    // Register callback function after sending packet successfully
    radio.setPacketSentAction(setFlag);

    // Send first packet to enable flag
    Serial.print(F("[LoRa] Sending first packet... "));
    transmissionState = radio.startTransmit("Hello World!");
    M5.Display.setCursor(5,0);
    M5.Display.printf("Hello World!\n");
}

// Counter to keep track of transmitted packets
int count = 0;

void loop()
{
    if (transmittedFlag) {
        // reset flag
        transmittedFlag = false;

        if (transmissionState == RADIOLIB_ERR_NONE) {
            // packet was successfully sent
            Serial.println(F("Transmission finished!"));
            M5.Display.println("Send sucessfully!");

            // NOTE: when using interrupt-driven transmit method,
            //       it is not possible to automatically measure
            //       transmission data rate using getDataRate()

        } else {
            Serial.print(F("Send failed, code: "));
            Serial.println(transmissionState);
            M5.Display.print("\nSend failed\ncode:");
            M5.Display.println(transmissionState);
        }

        // Clean up after transmission is finished
        // This will ensure transmitter is disabled,
        // RF switch is powered down etc.
        radio.finishTransmit();

        // Wait a second before transmitting again
        delay(1000);

        Serial.printf("[LoRa] Sending #%d packet ... ", count);
        // You can transmit C-string or Arduino string up to 256 characters long
        String str        = "Cap LoRa868#" + String(count++);
        transmissionState = radio.startTransmit(str);
        M5.Display.clear();
        M5.Display.setCursor(0,5);
        M5.Display.printf("[LoRa]\nSending #%d packet\n......\n", count);

        // You can also transmit byte array up to 256 bytes long
        /*
          byte byteArr[] = {0x01, 0x23, 0x45, 0x67,
                            0x89, 0xAB, 0xCD, 0xEF};
          transmissionState = radio.startTransmit(byteArr, 8);
        */
    }
}

受信例

cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
#include <M5Unified.h>
#include <RadioLib.h>

#define LORA_BW           125.0f // LoRa bandwidth (kHz) - 125kHz is common for long-range
#define LORA_SF           12     // Spreading factor (6-12) - higher = longer range, slower data rate
#define LORA_CR           5      // Coding rate (5-8, represents 4/5 to 4/8) - higher = more error correction
#define LORA_FREQ         868.0  // Carrier frequency (MHz) - 868MHz is EU ISM band for LoRa
#define LORA_SYNC_WORD    0x34   // Sync word for packet recognition - must match between transmitter/receiver
#define LORA_TX_POWER     22     // Transmission power (dBm) - 22dBm is maximum for many regions
#define LORA_PREAMBLE_LEN 20     // Preamble length (symbols) - ensures receiver can detect packet start

//         SX1262 PIN         NSS,       IRQ,         RST,        BUSY 
SX1262 radio = new Module(GPIO_NUM_5, GPIO_NUM_4, GPIO_NUM_3, GPIO_NUM_6);

#if defined(ESP8266) || defined(ESP32)
ICACHE_RAM_ATTR
#endif
// Packet received flag
volatile bool receivedFlag = false;
// This function is called when a complete packet is received by the module
// IMPORTANT: This function MUST be 'void' type and MUST NOT have any arguments!
void setFlag(void)
{
    receivedFlag = true;
}

void setup()
{
    M5.begin();
    Serial.begin(115200);
    M5.Display.setFont(&fonts::FreeMonoBold9pt7b);

    // Init SX1262
    Serial.print(F("[LoRa] Initializing ... "));
    int state = radio.begin(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR, LORA_SYNC_WORD, LORA_TX_POWER, LORA_PREAMBLE_LEN, 3.0, true);
    if (state == RADIOLIB_ERR_NONE) {
        Serial.println(F("Init success!"));
    } else {
        Serial.print(F("Init failed, code: "));
        Serial.println(state);
        while (true) { delay(10); }
    }
    radio.setCurrentLimit(140);// Current range: 0-140mA , step 2.5mA

    // Register callback function after receiving packet successfully
    radio.setPacketReceivedAction(setFlag);

    // Start listening for LoRa packets
    Serial.print(F("[LoRa] Starting to listen ... "));
    state = radio.startReceive();
    if (state == RADIOLIB_ERR_NONE) {
        Serial.println(F("Listen successfully!"));
    } else {
        Serial.print(F("Listen failed, code: "));
        Serial.println(state);
        while (true) { delay(10); }
    }

    // If needed, 'listen' mode can be disabled by calling any of the following methods:
    // radio.standby()
    // radio.sleep()
    // radio.transmit();
    // radio.receive();
    // radio.scanChannel();
}

void loop()
{
    if (receivedFlag) {
        // reset flag
        receivedFlag = false;

        // Read received data as an Arduino String
        String str;
        int state = radio.readData(str);

        // Read received data as byte array
        /*
          byte byteArr[8];
          int numBytes = radio.getPacketLength();
          int state = radio.readData(byteArr, numBytes);
        */

        if (state == RADIOLIB_ERR_NONE) {
            // Packet was successfully received
            Serial.println(F("[LoRa] Received packet:"));
            M5.Display.clear();
            M5.Display.setCursor(0,5);
            M5.Display.printf("[LoRa]\nReceived packet:\n");

            // Data of the packet
            Serial.print(F("[LoRa] Data:\t\t"));
            Serial.println(str);
            M5.Display.printf("Data: %s\n", str.c_str());

            // RSSI (Received Signal Strength Indicator)
            Serial.print(F("[LoRa] RSSI:\t\t"));
            Serial.print(radio.getRSSI());
            Serial.println(F(" dBm"));
            M5.Display.printf("RSSI: %0.2f dBm\n", radio.getRSSI());

            // SNR (Signal-to-Noise Ratio)
            Serial.print(F("[LoRa] SNR:\t\t"));
            Serial.print(radio.getSNR());
            Serial.println(F(" dB"));
            M5.Display.printf("SNR:  %0.2f dB\n", radio.getSNR());

            // Frequency error
            Serial.print(F("[LoRa] Frequency error:\t"));
            Serial.print(radio.getFrequencyError());
            Serial.println(F(" Hz"));
            M5.Display.printf("Freq err: %0.2f Hz\n", radio.getFrequencyError());

        } else if (state == RADIOLIB_ERR_CRC_MISMATCH) {
            // Packet was received, but is malformed
            Serial.println(F("CRC error!"));

        } else {
            Serial.print(F("Receive failed, code: "));
            Serial.println(state);
        }
    }
}

上記の例の動作は、送信側で 1 秒ごとにカウント付きの文字列を送信し、受信側で受信した文字列と RSSI、SNR、周波数誤差などの情報を表示します。

  • 送信側シリアル出力例:
[LoRa] Sending #39 packet ... Transmission finished!
  • 受信側シリアル出力例:
[LoRa] Received packet:
[LoRa] Data:            Cap LoRa868#38
[LoRa] RSSI:            -7.00 dBm
[LoRa] SNR:             4.50 dB
[LoRa] Frequency error: -779.60 Hz

3.2 GPS 使用例

  • 本デバイスの GPS モジュールは GPS、GLONASS、GALILEO、BDS、QZSS など複数の衛星システムをサポートします。例では setSatelliteMode() 関数で衛星システムを変更できます(デフォルトは GLONASS)。変更すると画面のプレフィックスも更新され、システムの判別に役立ちます。
cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
#include "M5Unified.h"
#include "M5GFX.h"
#include "MultipleSatellite.h"

static const int RXPin = 15, TXPin = 13;
static const uint32_t GPSBaud = 115200;
MultipleSatellite gps(Serial1, GPSBaud, SERIAL_8N1, RXPin, TXPin);

// Variable to track current satellite system mode (default: GLONASS)
satellite_mode_t currentMode = SATELLITE_MODE_GLONASS;

// Function prototype for displaying information
void displayInfo(void);

/**
 * Get the prefix string for different satellite systems
 * @param mode Current satellite system mode
 * @return Corresponding prefix string for the satellite system
 */
const char* getSatPrefix(satellite_mode_t mode) {
    switch (mode) {
        case SATELLITE_MODE_GPS:      return "GPS_Sat";   // GPS satellite prefix
        case SATELLITE_MODE_BDS:      return "BDS_Sat";   // BeiDou satellite prefix
        case SATELLITE_MODE_GLONASS:  return "GLN_Sat";   // GLONASS satellite prefix
        case SATELLITE_MODE_GALILEO:  return "GAL_Sat";   // Galileo satellite prefix
        case SATELLITE_MODE_QZSS:     return "QZS_Sat";   // QZSS satellite prefix
        default:                      return "Unknown";   // Default for unknown systems
    }
}

void setup() {
    M5.begin();               
    Serial.begin(115200);    
    M5.Display.setFont(&fonts::FreeMonoBold9pt7b);
    gps.begin();              // Initialize GPS module
    // Set GPS to factory start mode
    gps.setSystemBootMode(BOOT_FACTORY_START);
    
    // Print initialization information to serial monitor
    Serial.println(F("<----------Cap LoRa868 Example---------->"));
    Serial.print(F("Testing TinyGPSPlus library v. "));
    Serial.println(TinyGPSPlus::libraryVersion());
    String version = gps.getGNSSVersion();
    Serial.printf("GNSS SW=%s\r\n", version.c_str());
    delay(1000);  // Short delay for initialization

    displayInfo();  // Initial display of information
}

void loop() {
    gps.updateGPS();  // Update GPS data
    displayInfo();    // Update display with new data
    delay(100);       
}

void displayInfo(void) {
    Serial.println("=========================================");
    Serial.print(F("Location: "));

    Serial.printf("satellites:%d\n", gps.satellites.value());
    String gps_mode = gps.getSatelliteMode(); 
    Serial.printf("GNSS Mode:%s\r\n", gps_mode.c_str());

    // Get appropriate satellite prefix based on current mode
    const char* satPrefix = getSatPrefix(currentMode);

    // Check if location data has been updated
    if (gps.location.isUpdated()) {
        auto latitude = gps.location.lat();
        auto longitude = gps.location.lng();
        // Print latitude and longitude to serial with 6 decimal places
        Serial.print(latitude, 6); Serial.print(F(","));
        Serial.print(longitude, 6);Serial.print(F("\n"));

        M5.Display.fillRect(0, 0, 240, 135, TFT_BLACK);  
        M5.Display.setCursor(0, 0);                   
        M5.Display.printf("%s: \nSat: %d\nLat: %.6f\nlng: %.6f\n", satPrefix,
                          (uint8_t)gps.satellites.value(),
                          latitude,
                          longitude);
    } else {
        // If no valid location data, display placeholders
        M5.Display.fillRect(0, 0, 240, 135, TFT_BLACK);
        M5.Display.setCursor(0, 0);
        M5.Display.printf("%s\n", satPrefix);
        M5.Display.print("Sat: ----\nLat: ----\nLng: ----\n");
        Serial.print(F("LOCATION INVALID\n"));  // Indicate invalid data on serial
    }

    Serial.print(F("Date/Time: "));
    // Check if date data has been updated
    if (gps.date.isUpdated()) {
        auto month = gps.date.month();
        auto day = gps.date.day();
        auto year = gps.date.year();
        // Print date to serial (month/day/year)
        Serial.printf("%d/%d/%d ", month, day, year);

        M5.Display.fillRect(0, 80, 128, 128, TFT_BLACK);
        M5.Display.setCursor(0, 80);
        M5.Display.printf("Date: %d/%d/%d\n", month, day, year);
    } else {
        Serial.print(F("DATE INVALID"));  // Indicate invalid date
    }

    // Check if time data has been updated
    if (gps.time.isUpdated()) {
        auto hour = gps.time.hour();
        auto minute = gps.time.minute();
        auto sec = gps.time.second();
        auto centisec = gps.time.centisecond();
        // Print time to serial with leading zeros where necessary (HH:MM:SS.CS)
        if (hour < 10) Serial.print(F("0"));
        Serial.print(hour);Serial.print(F(":"));
        if (minute < 10) Serial.print(F("0"));
        Serial.print(minute);Serial.print(F(":"));
        if (sec < 10) Serial.print(F("0"));
        Serial.print(sec);Serial.print(F("."));
        if (centisec < 10) Serial.print(F("0"));
        Serial.print(centisec);

        M5.Display.fillRect(0, 128, 128, 60, TFT_BLACK);
        M5.Display.setCursor(0, 96);
        M5.Display.printf("Time: %02d:%02d:%02d.%02d\n", hour, minute, sec, centisec);
    } else {
        Serial.print(F("TIME INVALID"));  // Indicate invalid time
    }
    Serial.println();
    delay(1000);  // Delay to stabilize display updates
}

本製品の GPS は内蔵アンテナを使用し、外部アンテナはありません。できるだけ屋外の開けた場所(運動場、屋上など)で使用してください。位置特定には数分かかる場合があります。

  • シリアル出力例:
=========================================
Location: satellites:22
GNSS Mode:GLN_Sat
22.687541,113.772193
Date/Time: 9/5/2025 03:00:24.00

3.3 LoRa 通信で GPS 情報を送信

送信側

cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
#include <M5Unified.h>
#include <RadioLib.h>
#include "M5GFX.h"
#include "MultipleSatellite.h"

// LoRa configuration parameters
#define LORA_BW           125.0f // Bandwidth (kHz)
#define LORA_SF           12     // Spreading factor (6-12)
#define LORA_CR           5      // Coding rate (5-8)
#define LORA_FREQ         868.0  // Frequency (MHz)
#define LORA_SYNC_WORD    0x34   // Sync word
#define LORA_TX_POWER     22     // Transmission power (dBm)
#define LORA_PREAMBLE_LEN 20     // Preamble length

// LoRa pin definition
SX1262 radio = new Module(GPIO_NUM_5, GPIO_NUM_4, GPIO_NUM_3, GPIO_NUM_6);

// GPS configuration parameters
static const int RXPin = 15, TXPin = 13;
static const uint32_t GPSBaud = 115200;
MultipleSatellite gps(Serial1, GPSBaud, SERIAL_8N1, RXPin, TXPin);

// Global variables
int transmissionState = RADIOLIB_ERR_NONE;
volatile bool transmittedFlag = false;
int count = 0;
satellite_mode_t currentMode = SATELLITE_MODE_GLONASS;
float lastLat = 0.0f, lastLng = 0.0f;
int lastSatellites = 0;
String lastDate = "", lastTime = "";

// LoRa transmission completion callback function
#if defined(ESP8266) || defined(ESP32)
ICACHE_RAM_ATTR
#endif
void setFlag(void) {
    transmittedFlag = true;
}

// Function to get satellite system prefix
const char* getSatPrefix(satellite_mode_t mode) {
    switch (mode) {
        case SATELLITE_MODE_GPS:      return "GPS_Sat";
        case SATELLITE_MODE_BDS:      return "BDS_Sat";
        case SATELLITE_MODE_GLONASS:  return "GLN_Sat";
        case SATELLITE_MODE_GALILEO:  return "GAL_Sat";
        case SATELLITE_MODE_QZSS:     return "QZS_Sat";
        default:                      return "Unknown";
    }
}

// Function to update display information
String displayInfo(void) {
    M5.Display.setTextColor(TFT_WHITE);
    Serial.println("\n=========================================");
    Serial.print(F("Location: "));
    // Update satellite count
    lastSatellites = gps.satellites.value();
    Serial.printf("satellites:%d\n", lastSatellites);
    String gps_mode = gps.getSatelliteMode(); 
    Serial.printf("GNSS Mode:%s\r\n", gps_mode.c_str());

    const char* satPrefix = getSatPrefix(currentMode);

    // Update location information
    if (gps.location.isUpdated()) {
        lastLat = gps.location.lat();
        lastLng = gps.location.lng();
        Serial.print(lastLat, 6); Serial.print(F(","));
        Serial.print(lastLng, 6);Serial.print(F("\n"));

        // Display GPS information
        M5.Display.fillRect(0, 0, 240, 135, TFT_BLACK);  
        M5.Display.setCursor(0, 0);                   
        M5.Display.printf("%s: \nSat: %d\nLat: %.6f\nLng: %.6f\n", 
                          satPrefix, lastSatellites, lastLat, lastLng);
    } else {
        M5.Display.fillRect(0, 0, 240, 135, TFT_BLACK);
        M5.Display.setCursor(0, 0);
        M5.Display.printf("%s\nSat: ----\nLat: ----\nLng: ----\n", satPrefix);
        Serial.print(F("LOCATION INVALID\n"));
    }

    // Update date information
    Serial.print(F("Date/Time: "));
    if (gps.date.isUpdated()) {
        lastDate = String(gps.date.month()) + "/" + 
                  String(gps.date.day()) + "/" + 
                  String(gps.date.year());
        Serial.print(lastDate);
        Serial.print(F(" "));

        M5.Display.setCursor(0, 75);
        M5.Display.printf("Date: %s\n", lastDate.c_str());
    } else {
        lastDate = "DATE INVALID";
        Serial.print(F("DATE INVALID "));
    }

    // Update time information
    if (gps.time.isUpdated()) {
        lastTime = (gps.time.hour() < 10 ? "0" : "") + String(gps.time.hour()) + ":" +
           (gps.time.minute() < 10 ? "0" : "") + String(gps.time.minute()) + ":" +
           (gps.time.second() < 10 ? "0" : "") + String(gps.time.second()) + "." +
           (gps.time.centisecond() < 10 ? "0" : "") + String(gps.time.centisecond());
        Serial.print(lastTime);

        M5.Display.printf("Time: %s\n", lastTime.c_str());
    } else {
        lastTime = "TIME INVALID";
        Serial.print(F("TIME INVALID"));
    }
    Serial.println();

    return String("Type:") + satPrefix + 
           " Count:" + String(count) + 
           " Sat:" + String(lastSatellites) + 
           " Lat:" + String(lastLat, 6) + 
           " Lng:" + String(lastLng, 6) + 
           " Date:" + lastDate + 
           " Time:" + lastTime;
}

void setup() {
    // Initialize M5 device
    M5.begin();
    Serial.begin(115200);
    M5.Display.setFont(&fonts::FreeMonoBold9pt7b);

    // Initialize LoRa module
    Serial.print(F("[LoRa] Initializing ... "));
    int state = radio.begin(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR, 
                           LORA_SYNC_WORD, LORA_TX_POWER, LORA_PREAMBLE_LEN, 3.0, true);
    if (state == RADIOLIB_ERR_NONE) {
        Serial.println(F("Init success!"));
    } else {
        Serial.print(F("Init failed, code: "));
        Serial.println(state);
        while (true) { delay(10); }
    }
    radio.setCurrentLimit(140);
    radio.setPacketSentAction(setFlag);
    // Send first data packet
    Serial.print(F("[LoRa] Sending first packet... \n"));
    transmissionState = radio.startTransmit("GPS Initializing...");

    M5.Display.setCursor(5,0);
    M5.Display.printf("GPS Initializing...\n");
    // Initialize GPS module
    gps.begin();
    gps.setSystemBootMode(BOOT_FACTORY_START);
    Serial.println(F("<----------Cap LoRa868 Example---------->"));
    Serial.print(F("Testing TinyGPSPlus library v. "));
    Serial.println(TinyGPSPlus::libraryVersion());
    String version = gps.getGNSSVersion();
    Serial.printf("GNSS SW=%s\r\n", version.c_str());
    delay(1000);
}

void loop() {
    // Handle LoRa transmission completion event
    if (transmittedFlag) {
        transmittedFlag = false;

        if (transmissionState == RADIOLIB_ERR_NONE) {
            // Update GPS data and display
            Serial.println(F("      Transmission finished!"));
            M5.Display.setTextColor(TFT_GREEN);
            M5.Display.println("  OK!");
        } else {
            Serial.print(F("Send failed, code: "));
            Serial.println(transmissionState);
            M5.Display.print("\nSend failed\ncode:");
            M5.Display.println(transmissionState);
        }

        radio.finishTransmit();
        delay(1000);
        gps.updateGPS();
        String gpsData = displayInfo();
        // Send GPS data
        Serial.printf("[LoRa] Sending GPS data:\n %s\n", gpsData.c_str());
        transmissionState = radio.startTransmit(gpsData);
        
        // Update display
        M5.Display.fillRect(0, 115, 240, 20, TFT_BLACK);
        M5.Display.setCursor(0,115);
        M5.Display.setTextColor(TFT_YELLOW);
        M5.Display.printf("Sending #%d... ", count++);
    }
}

受信側

cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
#include <M5Unified.h>
#include <RadioLib.h>

#define LORA_BW           125.0f // LoRa bandwidth (kHz) - 125kHz is common for long-range
#define LORA_SF           12     // Spreading factor (6-12) - higher = longer range, slower data rate
#define LORA_CR           5      // Coding rate (5-8, represents 4/5 to 4/8) - higher = more error correction
#define LORA_FREQ         868.0  // Carrier frequency (MHz) - 868MHz is EU ISM band for LoRa
#define LORA_SYNC_WORD    0x34   // Sync word for packet recognition - must match between transmitter/receiver
#define LORA_TX_POWER     22     // Transmission power (dBm) - 22dBm is maximum for many regions
#define LORA_PREAMBLE_LEN 20     // Preamble length (symbols) - ensures receiver can detect packet start

//         SX1262 PIN         NSS,       IRQ,         RST,        BUSY 
SX1262 radio = new Module(GPIO_NUM_5, GPIO_NUM_4, GPIO_NUM_3, GPIO_NUM_6);

#if defined(ESP8266) || defined(ESP32)
ICACHE_RAM_ATTR
#endif
// Packet received flag
volatile bool receivedFlag = false;
// This function is called when a complete packet is received by the module
// IMPORTANT: This function MUST be 'void' type and MUST NOT have any arguments!
void setFlag(void)
{
    receivedFlag = true;
}

String Type = "Unknown";
String Count = "--";
String Sat = "--";
String Lat = "--";
String Lng = "--";
String Date = "Invalid";
String Time = "Invalid";

void gpsInfoParse(String str) {
   
    int pos = 0;
    while (pos < str.length()) {
        int newlinePos = str.indexOf(' ', pos);
        if (newlinePos == -1) newlinePos = str.length();
        
        String line = str.substring(pos, newlinePos);
        pos = newlinePos + 1;
        
        int fieldType = -1;
        if (line.startsWith("Type:")) fieldType = 0;
        else if (line.startsWith("Count:")) fieldType = 1;
        else if (line.startsWith("Sat:")) fieldType = 2;
        else if (line.startsWith("Lat:")) fieldType = 3;
        else if (line.startsWith("Lng:")) fieldType = 4;
        else if (line.startsWith("Date:")) fieldType = 5;
        else if (line.startsWith("Time:")) fieldType = 6;
        
        switch (fieldType) {
            case 0: Type = line.substring(5); break;// Type                
            case 1: Count = line.substring(6); break; // Count
            case 2: Sat = line.substring(4); break; // Sat
            case 3: Lat = line.substring(4); break; // Lat
            case 4: Lng = line.substring(4); break; // Lng
            case 5: Date = line.substring(5); break; // Date
            case 6: Time = line.substring(5); break; // Time
            default: break;
        }
    }
}

void setup()
{
    M5.begin();
    Serial.begin(115200);
    M5.Display.setFont(&fonts::FreeMonoBold9pt7b);

    // Init SX1262
    Serial.print(F("[LoRa] Initializing ... "));
    int state = radio.begin(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR, LORA_SYNC_WORD, LORA_TX_POWER, LORA_PREAMBLE_LEN, 3.0, true);
    if (state == RADIOLIB_ERR_NONE) {
        Serial.println(F("Init success!"));
    } else {
        Serial.print(F("Init failed, code: "));
        Serial.println(state);
        while (true) { delay(10); }
    }
    radio.setCurrentLimit(140);// Current range: 0-140mA , step 2.5mA

    // Register callback function after receiving packet successfully
    radio.setPacketReceivedAction(setFlag);

    // Start listening for LoRa packets
    Serial.print(F("[LoRa] Starting to listen ... "));
    state = radio.startReceive();
    if (state == RADIOLIB_ERR_NONE) {
        Serial.println(F("Listen successfully!"));
    } else {
        Serial.print(F("Listen failed, code: "));
        Serial.println(state);
        while (true) { delay(10); }
    }
}

void loop()
{
    if (receivedFlag) {
        // reset flag
        receivedFlag = false;

        // Read received data as an Arduino String
        String gpsDataStr;
        int state = radio.readData(gpsDataStr);

        if (state == RADIOLIB_ERR_NONE) {
            gpsInfoParse(gpsDataStr);
            // Packet was successfully received
            Serial.println(F("[LoRa] Received packet:"));
            M5.Display.clear();

            // Data of the packet
            Serial.print(F("[LoRa] Data:\t\t"));
            Serial.println(gpsDataStr);

            // RSSI (Received Signal Strength Indicator)
            Serial.print(F("[LoRa] RSSI:\t\t"));
            Serial.print(radio.getRSSI());
            Serial.println(F(" dBm"));

            // SNR (Signal-to-Noise Ratio)
            Serial.print(F("[LoRa] SNR:\t\t"));
            Serial.print(radio.getSNR());
            Serial.println(F(" dB"));

            // Frequency error
            Serial.print(F("[LoRa] Frequency error:\t"));
            Serial.print(radio.getFrequencyError());
            Serial.println(F(" Hz"));

            // Display GPS Information
            M5.Display.setCursor(0,5);
            M5.Display.setTextColor(TFT_BLUE);
            M5.Display.printf("Received packet: ");
            M5.Display.setTextColor(TFT_WHITE);
            M5.Display.printf("#%s\n", Count);
            M5.Display.setTextColor(TFT_YELLOW);
            M5.Display.printf("Type: %s\n", Type);
            M5.Display.printf("Sat: %s\n", Sat);
            M5.Display.printf("Lat: %s\n", Lat);
            M5.Display.printf("Lng: %s\n", Lng);
            M5.Display.printf("Date: %s\n", Date);
            M5.Display.printf("Time: %s\n", Time);
        } else if (state == RADIOLIB_ERR_CRC_MISMATCH) {
            // Packet was received, but is malformed
            Serial.println(F("CRC error!"));

        } else {
            Serial.print(F("Receive failed, code: "));
            Serial.println(state);
        }
    }
}

本製品の GPS は内蔵アンテナを使用するため、送信デバイスはできるだけ屋外の開けた場所(運動場、屋上など)で使用してください。初回の位置特定には数分かかる場合があります。

上記のサンプルが正常に動作した場合の結果は以下です。

  • 送信側シリアル出力例:
=========================================
Location: satellites:17
GNSS Mode:GLN_Sat
22.687502,113.772232
Date/Time: 9/5/2025 01:31:06.00
[LoRa] Sending GPS data:
 Type:GLN_Sat Count:120 Sat:17 Lat:22.687502 Lng:113.772232 Date:9/5/2025 Time:01:31:06.00
      Transmission finished!
  • 受信側シリアル出力例:
[LoRa] Received packet:
[LoRa] Data:            Type:GLN_Sat Count:120 Sat:17 Lat:22.687502 Lng:113.772232 Date:9/5/2025 Time:01:31:06.00
[LoRa] RSSI:            -2.00 dBm
[LoRa] SNR:             6.50 dB
[LoRa] Frequency error: -789.77 Hz
On This Page