transfer files from mbed-os block device (i.e. SD card) over LoRa radio.

Dependencies:   sx12xx_hal

radio chip selection

Radio chip driver is not included, allowing choice of radio device.
If you're using SX1272 or SX1276, then import sx127x driver into your program.
if you're using SX1261 or SX1262, then import sx126x driver into your program.
if you're using SX1280, then import sx1280 driver into your program.
If you're using NAmote72 or Murata discovery, then you must import only sx127x driver.

This program uses mbed serial terminal at 115200 8N1.
Files (on SD Card) are transferred between two LoRa devices.
Use ? question mark to see available commands on serial terminal.
Send file to other side using send /fs/somefile, then use cmp /fs/somefile to compare the file on the other side of radio link with local file with same name.
Use cmp to test radio link.

rm command to delete files must not include /fs/ directory prefix, filename only.
send and cmp require /fs/ directory.

sx1280 thruput

bytes/secsf12sf11sf10sf9sf8sf7sf6sf5
1600KHz2764821081188732775365640012000
800KHz5299701720280047666500
400KHz146522654033

sx126x thruput

bytes/secsf12sf11sf10sf9sf8sf7sf6sf5
500KHz2073817061257215435705608
250KHz103193344641111418873064
125KHz971733245479721607

sx127x thruput

sx126x recommended at 500KHz bandwidth.

bytes/secsf12sf11sf10sf9sf8sf7
500KHz11120738569712202050
250KHz48.71031923456321115
125KHz24.24497151313552
62.5KHz12.222.139.486157276

SD Card

SD card driver must be enabled in mbed_app.json with the line "target.components_add": ["SD"]
Pins for SD card connection can be overridden as shown in mbed_app.json.
The default pin configuration for SD card is found in mbed_lib.json in mbed-os/components/storage/blockdevice/COMPONENT_SD/.
If no pin configuration exists in this mbed_lib.json, or needs to be changed, the included mbed_app.json is example pin declaration for SD card.

FAT filesystem is only used in this project for demonstration purpose; for easily copying files to SD card.
However, for production projects, LittleFileSystem needs to be used instead for reliability and wear leveling.

Platforms such as K64F have SD card slot already.
See all boards with arduino shield connector and SD card here. (arduino connector needed for LoRa radio board)
...but if you want to use nucleo board: /media/uploads/dudmuck/nucleo_sdcardqtr.png

lorachip_sx128x.cpp

Committer:
Wayne Roberts
Date:
2019-06-05
Revision:
1:319ef808aaa4
Parent:
0:c3ecf7b252a3

File content as of revision 1:319ef808aaa4:

#include "lorachip.h"
#ifdef SX128x_H

#define TX_PWR_OFFSET           18

static uint8_t tx_param_buf[2];
static ModulationParams_t mpLORA;
static PacketParams_t ppLORA;
static LoRaPktPar0_t LoRaPktPar0;

void tx_dbm_print()
{
    PaPwrCtrl_t PaPwrCtrl;

    PaPwrCtrl.octet = Radio::radio.readReg(REG_ADDR_PA_PWR_CTRL, 1);
    pc.printf("%ddBm ", PaPwrCtrl.bits.tx_pwr - TX_PWR_OFFSET);

    tx_param_buf[0] = PaPwrCtrl.bits.tx_pwr;
}

static const unsigned lora_bws[] = {
      50, // 0
     100, // 1
     200, // 2
     400, // 3
     800, // 4
    1600  // 5
};

static const unsigned loraBWs[] = {
    LORA_BW_50,  // 0   50
    LORA_BW_100, // 1   100
    LORA_BW_200, // 2   200
    LORA_BW_400, // 3   400
    LORA_BW_800, // 4   800
    LORA_BW_1600 // 5   1600
};

void print_lora_status()
{
    float MHz;
    status_t status;

    tx_dbm_print();

    status.octet = Radio::radio.xfer(OPCODE_GET_STATUS, 0, 0, NULL);
    switch (status.bits.chipMode) {
        case 2: pc.printf("STDBY_RC"); break;    // STDBY_RC
        case 3: pc.printf("STDBY_XOSC"); break;  //STDBY_XOSC
        case 4: pc.printf("FS"); break;  //FS
        case 5: pc.printf("RX"); break;  // RX
        case 6: pc.printf("TX"); break;  // TX
        default: pc.printf("<%u>", status.bits.chipMode); break;
    }
    pc.printf("\t");

    {
        IrqFlags_t irqFlags;
        uint8_t buf[6];
        Radio::radio.xfer(OPCODE_GET_IRQ_STATUS, 0, 3, buf);
        status.octet = buf[0];
        irqFlags.word = buf[1] << 8;
        irqFlags.word |= buf[2];
        pc.printf(" irq:%04x ", irqFlags.word);
    }

    LoRaPktPar0.octet = Radio::radio.readReg(REG_ADDR_LORA_PKTPAR0, 1);

    pc.printf("%uKHz ", lora_bws[LoRaPktPar0.bits.modem_bw]);
    pc.printf("sf%u", LoRaPktPar0.bits.modem_sf);

    MHz = Radio::radio.getMHz();
    pc.printf(" %.3fMHz\r\n", MHz);
}

void cmd_sf(uint8_t argsAt)
{
    unsigned sf;
    if (sscanf(pcbuf + argsAt, "%u", &sf) == 1) {
        mpLORA.lora.spreadingFactor = sf << 4;
        Radio::radio.xfer(OPCODE_SET_MODULATION_PARAMS, 3, 0, mpLORA.buf);
        set_symb_timeout();
    }

    LoRaPktPar0.octet = Radio::radio.readReg(REG_ADDR_LORA_PKTPAR0, 1);
    pc.printf("sf%u\r\n", LoRaPktPar0.bits.modem_sf);
}

void cmd_frf(uint8_t argsAt)
{
    float MHz;

    if (sscanf(pcbuf + argsAt, "%f", &MHz) == 1) {
        Radio::radio.setMHz(MHz);
    }
    MHz = Radio::radio.getMHz();
    pc.printf("%.3fMHz\r\n", MHz);
}

void cmd_bw(uint8_t argsAt)
{
    unsigned n;
    float khz;

    if (sscanf(pcbuf + argsAt, "%f", &khz) == 1) {
        int sidx = -1;
        float min_diff = 9999;
        Radio::Standby();
        wait(0.02);

        for (n = 0; lora_bws[n] > 0; n++) {
            float diff = fabs(lora_bws[n] - khz);
            if (diff < min_diff) {
                sidx = n;
                min_diff = diff;
            }
        }
        if (sidx == -1) {
            pc.printf("bw not found\r\n");
            return;
        }

        mpLORA.lora.bandwidth = loraBWs[sidx];
        Radio::radio.xfer(OPCODE_SET_MODULATION_PARAMS, 3, 0, mpLORA.buf);
        
        wait(0.02);
        set_symb_timeout();
        Radio::Rx(0);

        current.bwKHz = khz;
    }

    LoRaPktPar0.octet = Radio::radio.readReg(REG_ADDR_LORA_PKTPAR0, 1);
    pc.printf("%uKHz\r\n", lora_bws[LoRaPktPar0.bits.modem_bw]);
}

void radio_readChip()
{
    //uint8_t reg8;

#if 0
    reg8 = Radio::radio.readReg(REG_ADDR_PKTCTRL0, 1);
    ppGFSK.gfskFLRC.HeaderType = reg8 & 0x20;
    ppFLRC.gfskFLRC.HeaderType = reg8 & 0x20;

    reg8 = Radio::radio.readReg(REG_ADDR_PKTCTRL1, 1);
    ppGFSK.gfskFLRC.PreambleLength = reg8 & 0x70;
    ppFLRC.gfskFLRC.PreambleLength = reg8 & 0x70;
    ppGFSK.gfskFLRC.SyncWordLength = reg8 & 0x0e;
    ppFLRC.gfskFLRC.SyncWordLength = reg8 & 0x06;
    if (ppFLRC.gfskFLRC.SyncWordLength == 0x06)
        ppFLRC.gfskFLRC.SyncWordLength = FLRC_SYNC_WORD_LEN_P32S;

    reg8 = Radio::radio.readReg(REG_ADDR_PKT_SYNC_ADRS_CTRL, 1);
    ppGFSK.gfskFLRC.SyncWordMatch = reg8 & 0x70;
    ppFLRC.gfskFLRC.SyncWordMatch = reg8 & 0x70;

    reg8 = Radio::radio.readReg(REG_ADDR_PAYLOAD_LEN, 1);
    ppGFSK.gfskFLRC.PayloadLength = reg8;
    ppFLRC.gfskFLRC.PayloadLength = reg8;

    reg8 = Radio::radio.readReg(REG_ADDR_PKT_TX_HEADER, 1);    // TODO hi bit of payload length
    ppBLE.ble.ConnectionState = reg8 & 0xe0;
    ppBLE.ble.BleTestPayload = reg8 & 0x1c;

    reg8 = Radio::radio.readReg(REG_ADDR_PKT_BITSTREAM_CTRL, 1);
    ppBLE.ble.CrcLength = reg8 & 0x30;
    ppBLE.ble.Whitening = reg8 & 0x08;
    ppGFSK.gfskFLRC.CRCLength = reg8 & 0x30;
    ppFLRC.gfskFLRC.CRCLength = reg8 & 0x30;
    ppGFSK.gfskFLRC.Whitening = reg8 & 0x08;
    ppFLRC.gfskFLRC.Whitening = reg8 & 0x08;
#endif /* if 0 */

    LoRaPktPar0.octet = Radio::radio.readReg(REG_ADDR_LORA_PKTPAR0, 1);
    switch (LoRaPktPar0.bits.modem_bw) {
        case 2: mpLORA.lora.bandwidth = LORA_BW_200; break;
        case 3: mpLORA.lora.bandwidth = LORA_BW_400; break;
        case 4: mpLORA.lora.bandwidth = LORA_BW_800; break;
        case 5: mpLORA.lora.bandwidth = LORA_BW_1600; break;
    }
    mpLORA.lora.spreadingFactor = LoRaPktPar0.bits.modem_sf << 4;

    {
        LoRaPktPar1_t LoRaPktPar1;
        LoRaPktPar1.octet = Radio::radio.readReg(REG_ADDR_LORA_PKTPAR1, 1);
        mpLORA.lora.codingRate = LoRaPktPar1.bits.coding_rate;
        ppLORA.lora.InvertIQ = LoRaPktPar1.bits.rxinvert_iq ? LORA_IQ_INVERTED : LORA_IQ_STD;
        ppLORA.lora.HeaderType = LoRaPktPar1.bits.implicit_header ? IMPLICIT_HEADER : EXPLICIT_HEADER;
        // LoRaPktPar1.bits.ppm_offset
    }

    {
        LoRaPreambleReg_t LoRaPreambleReg;
        LoRaPreambleReg.octet = Radio::radio.readReg(REG_ADDR_LORA_PREAMBLE, 1);
        ppLORA.lora.PreambleLength = LoRaPreambleReg.bits.preamble_symb1_nb * (1 << LoRaPreambleReg.bits.preamble_symb_nb_exp);
    }
    ppLORA.lora.PayloadLength = Radio::radio.readReg(REG_ADDR_LORA_TX_PAYLOAD_LENGTH, 1);

    {
        LoRaLrCtl_t LoRaLrCtl;
        LoRaLrCtl.octet = Radio::radio.readReg(REG_ADDR_LORA_LRCTL, 1);
        ppLORA.lora.crc = LoRaLrCtl.octet & 0x20; // LoRaLrCtl.bits.crc_en
    }

#if 0
    {
        RegRxBw_t RegRxBw;
        unsigned bps;
        FloraPreambleHi_t FloraPreambleHi;
        float mi, fdev_hz;
        unsigned freqDev;
        FskModDfH_t FskModDfH;
        FskModDfH.octet = radio.readReg(REG_ADDR_FSK_MODDFH, 1);
        freqDev = FskModDfH.bits.freqDev;
        freqDev <<= 8;
        freqDev |= radio.readReg(REG_ADDR_FSK_MODDFL, 1);
        //pc.printf("freqDev %x, %x\r\n", freqDev, freqDev);
        fdev_hz = freqDev * PLL_STEP_HZ;
        //pc.printf("fdev hz:%f\r\n", fdev_hz);

        FloraPreambleHi.octet = radio.readReg(REG_ADDR_FLORA_PREAMBLE_HI, 1);
        switch (FloraPreambleHi.bits.data_rate) {
            case 0:
                bps = 2.0e6;
                //mpFLRC.flrc.bitrateBandwidth = ??; // 2.6
                break;
            case 1:
                bps = 1.6e6;
                //mpFLRC.flrc.bitrateBandwidth = ??; // 2.08
                break;
            case 2:
                bps = 1.0e6;
                mpFLRC.flrc.bitrateBandwidth = FLRC_BR_1_300_BW_1_2; // 1.3
                break;
            case 3:
                bps = 0.8e6;
                mpFLRC.flrc.bitrateBandwidth = FLRC_BR_1_000_BW_1_2; // 1.04
                break;
            case 4:
                bps = 0.5e6;
                mpFLRC.flrc.bitrateBandwidth = FLRC_BR_0_650_BW_0_6; // 0.65
                break;
            case 5:
                bps = 0.4e6;
                mpFLRC.flrc.bitrateBandwidth = FLRC_BR_0_520_BW_0_6; // 0.52
                break;
            case 6:
                bps = 0.25e6;
                mpFLRC.flrc.bitrateBandwidth = FLRC_BR_0_325_BW_0_3; // 0.325
                break;
            case 7:
                bps = 0.125e6;
                mpFLRC.flrc.bitrateBandwidth = FLRC_BR_0_260_BW_0_3; // 0.26
                break;
        }

        mi = (fdev_hz * 2.0) / bps;
        if (mi > 0.35) {
            mi -= 0.5;
            mi /= 0.25;
            mpBLE_GFSK.gfskBle.ModulationIndex = ((uint8_t)mi) + 1;
        } else
            mpBLE_GFSK.gfskBle.ModulationIndex = 0;

        RegRxBw.octet = radio.readReg(REG_ADDR_RXBW, 1);

        //pc.printf("rx ");
        switch (RegRxBw.bits.bw) {
            case 0:
                //pc.printf("2.4");
                if (FloraPreambleHi.bits.data_rate == 0)
                    mpBLE_GFSK.gfskBle.bitrateBandwidth = GFSK_BLE_BR_2_000_BW_2_4;
                if (FloraPreambleHi.bits.data_rate == 1)
                    mpBLE_GFSK.gfskBle.bitrateBandwidth = GFSK_BLE_BR_1_600_BW_2_4;
                if (FloraPreambleHi.bits.data_rate == 2)
                    mpBLE_GFSK.gfskBle.bitrateBandwidth = GFSK_BLE_BR_1_000_BW_2_4;
                if (FloraPreambleHi.bits.data_rate == 3)
                    mpBLE_GFSK.gfskBle.bitrateBandwidth = GFSK_BLE_BR_0_800_BW_2_4;
                break;
            case 1:
                //pc.printf("1.2");
                if (FloraPreambleHi.bits.data_rate == 2)
                    mpBLE_GFSK.gfskBle.bitrateBandwidth = GFSK_BLE_BR_1_000_BW_1_2;
                if (FloraPreambleHi.bits.data_rate == 3)
                    mpBLE_GFSK.gfskBle.bitrateBandwidth = GFSK_BLE_BR_0_800_BW_1_2;
                if (FloraPreambleHi.bits.data_rate == 4)
                    mpBLE_GFSK.gfskBle.bitrateBandwidth = GFSK_BLE_BR_0_500_BW_1_2;
                if (FloraPreambleHi.bits.data_rate == 5)
                    mpBLE_GFSK.gfskBle.bitrateBandwidth = GFSK_BLE_BR_0_400_BW_1_2;
                break;
            case 2:
                //pc.printf("0.6");
                if (FloraPreambleHi.bits.data_rate == 4)
                    mpBLE_GFSK.gfskBle.bitrateBandwidth = GFSK_BLE_BR_0_500_BW_0_6;
                if (FloraPreambleHi.bits.data_rate == 5)
                    mpBLE_GFSK.gfskBle.bitrateBandwidth = GFSK_BLE_BR_0_400_BW_0_6;
                if (FloraPreambleHi.bits.data_rate == 6)
                    mpBLE_GFSK.gfskBle.bitrateBandwidth = GFSK_BLE_BR_0_250_BW_0_6;
                break;
            case 3:
                //pc.printf("0.3");
                if (FloraPreambleHi.bits.data_rate == 6)
                    mpBLE_GFSK.gfskBle.bitrateBandwidth = GFSK_BLE_BR_0_250_BW_0_3;
                if (FloraPreambleHi.bits.data_rate == 7)
                    mpBLE_GFSK.gfskBle.bitrateBandwidth = GFSK_BLE_BR_0_125_BW_0_3;
                break;
        }
        //pc.printf("MHz bw:%u\r\n", RegRxBw.bits.bw);
        mpBLE_GFSK.gfskBle.bitrateBandwidth = reg8;
    }

    {
        FskCfg_t FskCfg;
        FskCfg.octet = radio.readReg(REG_ADDR_FSK_CFG, 1);
        //pc.printf("gf_bt:%u\r\n", FskCfg.bits.gf_bt);
        mpBLE_GFSK.gfskBle.ModulationShaping = FskCfg.bits.gf_bt << 4;
        mpFLRC.flrc.ModulationShaping = mpBLE_GFSK.gfskBle.ModulationShaping;
    }

    {
        PktBitStreamCtrl_t PktBitStreamCtrl;
        PktBitStreamCtrl.octet = radio.readReg(REG_ADDR_PKT_BITSTREAM_CTRL, 1);
        mpFLRC.flrc.CodingRate = PktBitStreamCtrl.octet & 0x06; // PktBitStreamCtrl.bits.flora_coding_rate 
    }
#endif /* if 0 */

}

#endif /* SX128x_H */