simple example of wifi AP scanning, sending scan-results over radio link

Dependencies:   lr1110 sx12xx_hal

End device for testing Wifi geolocation.

Use with radio shield for europe
or radio shield for USA
which is programmed with trx firmware from updater tool.


Press USER_BUTTON to scan wifi access points and send results over radio link to gateway, which posts to geolocation provider, which then reply's with coordinates.

Long press user button (half second) to only scan wifi, to print out on 9600bps UART, and not transmit scan results for geolocation resolving. Short press and release (under 500ms) will wifi scan and transmit for geolocation resolving.

Use this project with lr1110_wifi_geolocation_gateway to receive the message from this project and resolve the location from geolocation server

notice

This project is not using LoRaWAN, instead just LoRa transceiver directly to geolocation provider

main.cpp

Committer:
Wayne Roberts
Date:
2021-02-09
Revision:
1:416ed16806fc

File content as of revision 1:416ed16806fc:

#include "radio.h"

#define TX_DBM              20
#define BW_KHZ              500
#define SPREADING_FACTOR    11
#define CF_HZ               919000000

#define HEADER_LENGTH           10  /* for chipEUI and extra reserved */
#define WIFI_MAX_RESULTS        20
#define LORA_RX_TIME_MS         20000       /* how long for cloud to reply with resolved location? */

/**********************************************************************/
bool wifiResultFormatBasic; /* false, TODO get basic results functional */
EventQueue queue(4 * EVENTS_EVENT_SIZE);

unsigned packet_len;
uint8_t chip_eui[8];

InterruptIn ub(USER_BUTTON);  /* released = hi, pressed = lo */
void button_released(void);
int wifi_scan_id, long_press_id;
bool long_pressed;

uint64_t wifi_start_at, wifi_scan_dur;

static void cfg_lora()
{
    /* after wifi scan, lora is gone */
    Radio::LoRaModemConfig(BW_KHZ, SPREADING_FACTOR, 1);
    Radio::SetChannel(CF_HZ);

    Radio::set_tx_dbm(TX_DBM);

               // preambleLen, fixLen, crcOn, invIQ
    Radio::LoRaPacketConfig(8, false, true, false);
}

void wifi_scan()
{
    uint8_t wifiScan_buf[9];

    {   /* wifi scan defaults, see LR1110 user manual section 10.2 */
        unsigned chanmask = 0x0421; // ch1, ch6, ch11
        unsigned timeout = 105; // in milliseconds, 100 wifi TUs (beacon interval)

        wifiScan_buf[0] = 0x01; // wifi type
        wifiScan_buf[2] = chanmask; // chanmask-lo
        chanmask >>= 8;
        wifiScan_buf[1] = chanmask; // chanmask-hi
        wifiScan_buf[3] = 0x02; // acqMode
        wifiScan_buf[4] = WIFI_MAX_RESULTS; // NbMaxRes
        wifiScan_buf[5] = 0x10; // NbScanPerChan
        wifiScan_buf[7] = timeout; // Timeout-lo
        timeout >>= 8;
        wifiScan_buf[6] = timeout; // Timeout-hi
        wifiScan_buf[8] = 0x00; // AbortOnTimeout
    }

    Radio::radio.xfer(OPCODE_WIFI_SCAN, 9, 0, wifiScan_buf);
    wifi_start_at = Kernel::get_ms_count();
    printf("wifiScan...\r\n");
}

void long_press()
{
    long_pressed = true;
    wifi_scan();
}

void button_pressed()
{
    ub.rise(button_released);
    queue.cancel(wifi_scan_id);
    long_pressed = false;
    long_press_id = queue.call_in(500, long_press);
}

void button_released()
{
    ub.fall(button_pressed);
    if (!long_pressed) {
        wifi_scan_id = queue.call_in(20, wifi_scan);
        queue.cancel(long_press_id);
    }
}

void txDoneCB()
{
    Radio::Rx(0);
    queue.call_in(LORA_RX_TIME_MS, Radio::Standby);
}

void rxDoneCB(uint8_t size, float rssi, float snr)
{
    unsigned i;
    printf("%.1fdBm  snr:%.1fdB\t", rssi, snr);

    /*
    for (i = 0; i < size; i++) {
        printf("%02x ", Radio::radio.rx_buf[i]);
    }
    printf("\r\n");*/

    if (memcmp(Radio::radio.rx_buf, chip_eui, 8) == 0) {
        /* print resolved coordinates from cloud */
        printf(">> %s\r\n", Radio::radio.rx_buf + HEADER_LENGTH);
    }
}

struct wifidr {
    const char *txt;
    float Mbps;
};

const struct wifidr wifiDatarates[] = {
    /*   0 */ { NULL, 0},
    /*   1 */ { "DBPSK", 1},
    /*   2 */ { "DQPSK", 2},
    /*   3 */ { "BPSK", 6},
    /*   4 */ { "BPSK", 9},
    /*   5 */ { "QPSK", 12},
    /*   6 */ { "QPSK", 18},
    /*   7 */ { "16-QAM", 24},
    /*   8 */ { "16-QAM", 36},
    /*   9 */ { "(9)", 0},
    /*  10 */ { "(10)", 0},
    /*  11 */ { "BPSK", 6.5},
    /*  12 */ { "QPSK", 13},
    /*  13 */ { "QPSK", 19.5},
    /*  14 */ { "16-QAM", 26},
    /*  15 */ { "16-QAM", 39},
    /*  16 */ { "(16)", 0},
    /*  17 */ { "(17)", 0},
    /*  18 */ { "(18)", 0},
    /*  19 */ { "BPSK", 7.2},
    /*  20 */ { "QPSK", 14.4},
    /*  21 */ { "QPSK", 21.7},
    /*  22 */ { "16-QAM", 28.9},
    /*  23 */ { "16-QAM", 43.3},
};

void print_wifi_result(const uint8_t *result)
{
    char out[96];
    char str[24];
    unsigned n, macStart;
    wifiType_t wt;
    wifiChanInfo_t ci;
    wt.octet = result[0];
    ci.octet = result[1];
    out[0] = 0;
    strcat(out, "802.11");
    switch (wt.bits.signal) {
        case 1: strcat(out, "b"); break;
        case 2: strcat(out, "g"); break;
        case 3: strcat(out, "n"); break;
    }
    sprintf(str, " %s %.1fMbps", wifiDatarates[wt.bits.datarate].txt, wifiDatarates[wt.bits.datarate].Mbps);
    strcat(out, str);
    strcat(out, " ");

    sprintf(str, "ch%u ", ci.bits.channelID);
    strcat(out, str);
    switch (ci.bits.channelID) {
        // table 10-5
    }
    strcat(out, " ");
    sprintf(str, "mv:%u ", ci.bits.macValidationID);
    strcat(out, str);
    switch (ci.bits.macValidationID) {
        case 1: strcat(out, "gateway"); break;
        case 2: strcat(out, "phone"); break;
        case 3: strcat(out, "?"); break;
        // table 10.8
    }

    strcat(out, " ");

    if (wifiResultFormatBasic) {
        macStart = 3;
    } else {
        macStart = 4;
    }
    for (n = 0; n < 6; n++) {
        sprintf(str, "%02x", result[n+macStart]);
        strcat(out, str);
        if (n < 5)
            strcat(out, ":");
    }

    sprintf(str, " rssi:%d ", (int8_t)result[2]);
    strcat(out, str);

    if (!wifiResultFormatBasic) {
        sprintf(str, "frameCtrl:%02x ", result[3]);
        strcat(out, str);
    }
    printf("%s\r\n", out);
}

static void service()
{
    irq_t irq;
    irq.dword = Radio::radio.service();
    if (irq.bits.WifiDone) {
        unsigned n;
        stat_t stat;
        uint8_t nbResults;
        stat.word = Radio::radio.xfer(OPCODE_GET_WIFI_NB_RESULTS, 0, 0, NULL);
        stat.word = Radio::radio.xfer(0x0000, 0, 1, &nbResults);
        if (stat.bits.cmdStatus != CMD_DAT) {
            printf("get-nbResult-fail\r\n");
            return;
        }
        packet_len = HEADER_LENGTH;
        printf("%ums nbResults:%u\r\n", (unsigned)wifi_scan_dur, nbResults);
        for (n = 0; n < nbResults; n++) {
            uint8_t buf[3];
            uint8_t resultBuf[22];
            buf[0] = n;
            buf[1] = 1; // number of results in this read
            buf[2] = wifiResultFormatBasic ? 4 : 1;
            stat.word = Radio::radio.xfer(OPCODE_WIFI_READ_RESULTS, 3, 0, buf);
            // basic =  9byte length
            // full  = 22byte length
            stat.word = Radio::radio.xfer(0x0000, 0, wifiResultFormatBasic ? 9 : 22, resultBuf);

            if (stat.bits.cmdStatus == CMD_DAT) {
                unsigned n, macStart;
                wifiChanInfo_t ci;
                ci.octet = resultBuf[1];
                if (ci.bits.macValidationID == 1) { // gateway (AP)
                    macStart = wifiResultFormatBasic ? 3 : 4;
                    for (n = 0; n < 6; n++) {
                        Radio::radio.tx_buf[packet_len++] = resultBuf[n+macStart];
                        printf("%02x", resultBuf[n+macStart]);
                        if (n < 5)
                            printf(":");
                    }
                    printf(" rssi:%d\r\n", (int8_t)resultBuf[2]);
                    Radio::radio.tx_buf[packet_len++] = resultBuf[2];
                }
            } else
                printf("readResult:%s\r\n", Radio::radio.cmdStatus_toString(stat.bits.cmdStatus));
        }

        if (!long_pressed) {
            unsigned n;
            cfg_lora();
            for (n = 0; n < 8; n++)
                Radio::radio.tx_buf[n] = chip_eui[n];

            Radio::radio.tx_buf[n++] = 0;    // rfu
            Radio::radio.tx_buf[n++] = 0;    // rfu
            printf("pktLen:%u\r\n", packet_len);
            Radio::Send(packet_len, 0, 0, 0);   /* begin transmission */
        }
    } // ..if (irq.bits.WifiDone)

    if (irq.bits.TxDone) {
        printf("main-TxDone\r\n");
    }
}

void radio_irq_callback()
{
    wifi_scan_dur = Kernel::get_ms_count() - wifi_start_at;
    queue.call(service);
}

const RadioEvents_t rev = {
    /* DioPin_top_half */   radio_irq_callback,
    /* TxDone_topHalf */    NULL,
    /* TxDone_botHalf */    txDoneCB,
    /* TxTimeout  */        NULL,
    /* RxDone  */           rxDoneCB,
    /* RxTimeout  */        NULL,
    /* RxError  */          NULL,
    /* FhssChangeChannel  */NULL,
    /* CadDone  */          NULL
};

int main()
{
    Radio::Init(&rev);

    Radio::Standby();
    cfg_lora();

    {
        uint8_t buf[9];
        stat_t stat;
        stat.word = Radio::radio.xfer(OPCODE_GET_VERSION, 0, 0, NULL);
        stat.word = Radio::radio.xfer(0x0000, 0, 4, buf);
        if (stat.bits.cmdStatus == CMD_DAT) {
            printf("LR1110 chip:%02x use:%02x fw-v%u.%u\r\n",
                buf[0], /* silicon rev */
                buf[1], /* use case */
                buf[2], /* firmware major */
                buf[3]  /* firmware minor */
            );
        }

        stat.word = Radio::radio.xfer(OPCODE_GET_DEVEUI, 0, 0, NULL);
        stat.word = Radio::radio.xfer(0x0000, 0, 9, buf);
        memcpy(chip_eui, buf+1, 8);
        for (unsigned i = 0; i < 9; i++)
            printf("%02x ", buf[i]);
        printf("\r\n");
    }

#ifdef AUTO_TX
    queue.call_in(500, tx_test);
#endif /* AUTO_TX */

    if (ub.read())
        ub.fall(button_pressed);
    else
        ub.rise(button_released);

    queue.dispatch();
}