simple example of scanning wifi AP and sending to geolocation resolver on cloud
Dependencies: mbed-http lr1110 sx12xx_hal
test Wifi geolocation with resolving using HTTP POST.
Use with mbed board with internet access and arduino form factor.
With HTTPS, the RAM requirement is minimum 128Kbytes.
and, use with radio shield for europe
or radio shield for USA
which is programmed with trx
firmware from updater tool.
This project presents to user mbed STDIO serial port at 115200bps, which lets you perform wifi access point scan on LR1110 and send the resulting access point list to a geolocation provider on the cloud.
Use the project by itself to run wifi scan on the serial terminal (at 115200bps), or use with lr1110_wifi_geolocation_device to receive wifi list from remote device (over LoRa) to resolve location of that device using gelocation provider on cloud.
On serial terminal, use ?
question mark to see list of commands. ws
to run wifi scan, or ws p
to wifi scan and resolve location with cloud provider via HTTP POST.
project setup
Edit main.h
to uncomment which geolocation provider you wish to use, and get API key from them:
- google geolocation
- combain geolocation
TODO
others?
(choose one) Put your API key for geolocation provider into the URL in geolocation source file (such asgoogle_geolocation.cpp
orcombain.cpp
)
notice
This project is not using LoRaWAN, instead just LoRa transceiver directly to geolocation provider
Revision 1:4a05f91c9c38, committed 2021-02-09
- Comitter:
- Wayne Roberts
- Date:
- Tue Feb 09 10:49:02 2021 -0800
- Parent:
- 0:fd6707e25c57
- Commit message:
- add source files
Changed in this revision
diff -r fd6707e25c57 -r 4a05f91c9c38 combain.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/combain.cpp Tue Feb 09 10:49:02 2021 -0800 @@ -0,0 +1,127 @@ +#include "main.h" +#if (GEOLOCATION_PROVIDER == COMBAIN) +#include "radio.h" + +char my_copy[128]; + +const char COMBAIN_SSL_CA_PEM[] = "-----BEGIN CERTIFICATE-----\n" +"MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/\n" +"MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT\n" +"DkRTVCBSb290IENBIFgzMB4XDTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVow\n" +"PzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRcwFQYDVQQD\n" +"Ew5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB\n" +"AN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmTrE4O\n" +"rz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEq\n" +"OLl5CjH9UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9b\n" +"xiqKqy69cK3FCxolkHRyxXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw\n" +"7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40dutolucbY38EVAjqr2m7xPi71XAicPNaD\n" +"aeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV\n" +"HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQMA0GCSqG\n" +"SIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69\n" +"ikugdB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXr\n" +"AvHRAosZy5Q6XkjEGB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZz\n" +"R8srzJmwN0jP41ZL9c8PDHIyh8bwRLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5\n" +"JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubSfZGL+T0yjWW06XyxV3bqxbYo\n" +"Ob8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ\n" +"-----END CERTIFICATE-----\n"; + +/* parse_json: get result sent from server */ +void parse_json(const char *body, float *lat, float *lng, int *accuracy) +{ + bool in_location = false; + bool get_lat = false; + bool get_lng = false; + bool get_accuracy = false; + unsigned n; + strncpy(my_copy, body, sizeof(my_copy)); + strtok(my_copy, "\""); + for (n = 0; n < 10; n++) { + char *t = strtok(NULL, "\":"); + if (!t) + break; + if (in_location) { + if (get_lat) { + sscanf(t, "%f", lat); + get_lat = false; + } else if (get_lng) { + sscanf(t, "%f", lng); + get_lng = false; + } + if (strchr(t, '}')) + in_location = false; + else if (strcmp(t, "lat") == 0) + get_lat = true; + else if (strcmp(t, "lng") == 0) + get_lng = true; + } else { + if (get_accuracy) { + sscanf(t, "%d", accuracy); + get_accuracy = false; + } + if (strcmp(t, "location") == 0) + in_location = true; + else if (strcmp(t, "accuracy") == 0) + get_accuracy = true; + } + } +} + +int post_scan_result(const char *body, float *lat, float *lng, int *accuracy) +{ + HttpsRequest* post_req = new HttpsRequest(network, COMBAIN_SSL_CA_PEM, HTTP_POST, "https://apiv2.combain.com?key=YOUR_API_KEY"); + + post_req->set_header("Content-Type", "application/json"); + + HttpResponse* post_res = post_req->send(body, strlen(body)); + if (!post_res) { + printf("HttpRequest failed (error code %d)\n", post_req->get_error()); + return -1; + } + + dump_response(post_res); + parse_json(post_res->get_body_as_string().c_str(), lat, lng, accuracy); + delete post_req; + + return 0; +} + +/* + * https://combain.com/api/#request-body + */ + +void wifi_result_to_json(bool first, const uint8_t *result, unsigned macStart, unsigned rssi_idx) +{ + char str[8]; + unsigned i; + + if (!first) + strcat(json, ","); // end previous wifiAccessPoint + + strcat(json, "{\"macAddress\": \""); + for (i = 0; i < 6; i++) { + sprintf(str, "%02x", result[i + macStart]); + strcat(json, str); + if (i < 5) + strcat(json, ":"); + } + strcat(json, "\",\"signalStrength\": "); + sprintf(str, "%d", (int8_t)result[rssi_idx]); + strcat(json, str); + /* TODO: combain takes channel and/or frequency of access point */ + + strcat(json, "}"); +} + +void json_start() +{ + strcpy(json, "{\"considerIp\": \"false\","); + strcat(json, "\"wifiAccessPoints\": ["); +} + +void json_end() +{ + strcat(json, "]"); + strcat(json, "}"); +} + +#endif /* GEOLOCATION_PROVIDER == COMBAIN */
diff -r fd6707e25c57 -r 4a05f91c9c38 google_geolocation.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/google_geolocation.cpp Tue Feb 09 10:49:02 2021 -0800 @@ -0,0 +1,128 @@ +#include "main.h" +#if (GEOLOCATION_PROVIDER == GOOGLE) +#include "radio.h" + +const char GOOGLE_SSL_CA_PEM[] = "-----BEGIN CERTIFICATE-----\n" +"MIIDujCCAqKgAwIBAgILBAAAAAABD4Ym5g0wDQYJKoZIhvcNAQEFBQAwTDEgMB4G\n" +"A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjIxEzARBgNVBAoTCkdsb2JhbFNp\n" +"Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDYxMjE1MDgwMDAwWhcNMjExMjE1\n" +"MDgwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMjETMBEG\n" +"A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI\n" +"hvcNAQEBBQADggEPADCCAQoCggEBAKbPJA6+Lm8omUVCxKs+IVSbC9N/hHD6ErPL\n" +"v4dfxn+G07IwXNb9rfF73OX4YJYJkhD10FPe+3t+c4isUoh7SqbKSaZeqKeMWhG8\n" +"eoLrvozps6yWJQeXSpkqBy+0Hne/ig+1AnwblrjFuTosvNYSuetZfeLQBoZfXklq\n" +"tTleiDTsvHgMCJiEbKjNS7SgfQx5TfC4LcshytVsW33hoCmEofnTlEnLJGKRILzd\n" +"C9XZzPnqJworc5HGnRusyMvo4KD0L5CLTfuwNhv2GXqF4G3yYROIXJ/gkwpRl4pa\n" +"zq+r1feqCapgvdzZX99yqWATXgAByUr6P6TqBwMhAo6CygPCm48CAwEAAaOBnDCB\n" +"mTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUm+IH\n" +"V2ccHsBqBt5ZtJot39wZhi4wNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5n\n" +"bG9iYWxzaWduLm5ldC9yb290LXIyLmNybDAfBgNVHSMEGDAWgBSb4gdXZxwewGoG\n" +"3lm0mi3f3BmGLjANBgkqhkiG9w0BAQUFAAOCAQEAmYFThxxol4aR7OBKuEQLq4Gs\n" +"J0/WwbgcQ3izDJr86iw8bmEbTUsp9Z8FHSbBuOmDAGJFtqkIk7mpM0sYmsL4h4hO\n" +"291xNBrBVNpGP+DTKqttVCL1OmLNIG+6KYnX3ZHu01yiPqFbQfXf5WRDLenVOavS\n" +"ot+3i9DAgBkcRcAtjOj4LaR0VknFBbVPFd5uRHg5h6h+u/N5GJG79G+dwfCMNYxd\n" +"AfvDbbnvRG15RjF+Cv6pgsH/76tuIMRQyV+dTZsXjAzlAcmgQWpzU/qlULRuJQ/7\n" +"TBj0/VLZjmmx6BEP3ojY+x1J96relc8geMJgEtslQIxq/H5COEBkEveegeGTLg==\n" +"-----END CERTIFICATE-----\n"; + +char my_copy[128]; + +/* parse_json: get result sent from server */ +void parse_json(const char *body, float *lat, float *lng, int *accuracy) +{ + bool in_location = false; + bool get_lat = false; + bool get_lng = false; + bool get_accuracy = false; + unsigned n; + strncpy(my_copy, body, sizeof(my_copy)); + strtok(my_copy, "\""); + for (n = 0; n < 10; n++) { + char *t = strtok(NULL, "\":"); + if (!t) + break; + if (in_location) { + if (get_lat) { + sscanf(t, "%f", lat); + get_lat = false; + } else if (get_lng) { + sscanf(t, "%f", lng); + get_lng = false; + } + if (strchr(t, '}')) + in_location = false; + else if (strcmp(t, "lat") == 0) + get_lat = true; + else if (strcmp(t, "lng") == 0) + get_lng = true; + } else { + if (get_accuracy) { + sscanf(t, "%d", accuracy); + get_accuracy = false; + } + if (strcmp(t, "location") == 0) + in_location = true; + else if (strcmp(t, "accuracy") == 0) + get_accuracy = true; + } + } +} + +/* + * https://developers.google.com/maps/documentation/geolocation/overview + */ +int post_scan_result(const char *body, float *lat, float *lng, int *accuracy) +{ + HttpsRequest* post_req = new HttpsRequest(network, GOOGLE_SSL_CA_PEM, HTTP_POST, "https://www.googleapis.com/geolocation/v1/geolocate?key=YOUR-API-KEY"); + + post_req->set_header("Content-Type", "application/json"); + + HttpResponse* post_res = post_req->send(body, strlen(body)); + if (!post_res) { + printf("HttpRequest failed (error code %d)\n", post_req->get_error()); + return -1; + } + + dump_response(post_res); + parse_json(post_res->get_body_as_string().c_str(), lat, lng, accuracy); + delete post_req; + + return 0; +} + +void json_start() +{ + strcpy(json, "{\"considerIp\": \"false\","); + strcat(json, "\"wifiAccessPoints\": ["); +} + +void json_end() +{ + strcat(json, "]"); + strcat(json, "}"); +} + +void wifi_result_to_json(bool first, const uint8_t *result, unsigned macStart, unsigned rssi_idx) +{ + char str[8]; + unsigned i; + + if (!first) + strcat(json, ","); // end previous wifiAccessPoint + + strcat(json, "{\"macAddress\": \""); + for (i = 0; i < 6; i++) { + sprintf(str, "%02x", result[i + macStart]); + strcat(json, str); + if (i < 5) + strcat(json, ":"); + } + strcat(json, "\",\"signalStrength\": "); + sprintf(str, "%d", (int8_t)result[rssi_idx]); + strcat(json, str); + strcat(json, ",\"signalToNoiseRatio\": 0"); + + strcat(json, "}"); +} + +#endif /* GEOLOCATION_PROVIDER == GOOGLE */
diff -r fd6707e25c57 -r 4a05f91c9c38 main.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.cpp Tue Feb 09 10:49:02 2021 -0800 @@ -0,0 +1,555 @@ +#include "main.h" +#include "network-helper.h" +#include "radio.h" + +#define TX_DBM 20 +#define BW_KHZ 500 +#define SPREADING_FACTOR 11 +#define CF_HZ 919000000 + +/* geolocation provider wont operate with less than 3 wifi access points */ +#define MINIMUM_REQUIRED_ACCESS_POINTS 2 + +bool wifiResultFormatBasic; + +struct location { + float lat, lng; + int accuracy; +}; + +typedef struct { + const char* const cmd; + void (*handler)(uint8_t args_at); + const char* const arg_descr; + const char* const description; +} menu_item_t; + +EventQueue queue(4 * EVENTS_EVENT_SIZE); + +RawSerial pc(USBTX, USBRX, MBED_CONF_PLATFORM_STDIO_BAUD_RATE); // speed from mbed_app.json +char pcbuf[64]; +int pcbuf_len; + +NetworkInterface* network; + +event_callback_t serialEventCb; + +uint8_t wifiScan_buf[9]; +uint64_t wifi_start_at, wifi_scan_dur; +bool post_enable; +bool send_reply; + +void cmd_help(uint8_t); + +struct location geoloc_result; + +uint8_t remote_chip_eui[8]; + +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}, +}; + +char json[1536]; + +void dump_response(HttpResponse* res) +{ + printf("Status: %d - %s\n", res->get_status_code(), res->get_status_message().c_str()); + + printf("Headers:\n"); + for (size_t ix = 0; ix < res->get_headers_length(); ix++) { + printf("\t%s: %s\n", res->get_headers_fields()[ix]->c_str(), res->get_headers_values()[ix]->c_str()); + } + printf("\nBody (%lu bytes):\n\n%s\n", res->get_body_length(), res->get_body_as_string().c_str()); +} + +void cfg_lora() +{ + 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 cmd_wifi_scan(uint8_t idx) +{ + Radio::radio.xfer(OPCODE_WIFI_SCAN, 9, 0, wifiScan_buf); + wifi_start_at = Kernel::get_ms_count(); + printf("wifiScan...\r\n"); + + post_enable = pcbuf[idx] == 'p'; + send_reply = false; +} + +/* List of trusted root CA certificates + * currently two: Amazon, the CA for os.mbed.com and Let's Encrypt, the CA for httpbin.org + * + * To add more root certificates, just concatenate them. + */ +const char HTTBIN_ORG_SSL_CA_PEM[] = "-----BEGIN CERTIFICATE-----\n" + "MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF\n" + "ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6\n" + "b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL\n" + "MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv\n" + "b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj\n" + "ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM\n" + "9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw\n" + "IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6\n" + "VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L\n" + "93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm\n" + "jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC\n" + "AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA\n" + "A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI\n" + "U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs\n" + "N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv\n" + "o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU\n" + "5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy\n" + "rqXRfboQnoZsG4q5WTP468SQvvG5\n" + "-----END CERTIFICATE-----\n" + "-----BEGIN CERTIFICATE-----\n" + "MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/\n" + "MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT\n" + "DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow\n" + "SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT\n" + "GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC\n" + "AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF\n" + "q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8\n" + "SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0\n" + "Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA\n" + "a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj\n" + "/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T\n" + "AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG\n" + "CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv\n" + "bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k\n" + "c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw\n" + "VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC\n" + "ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz\n" + "MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu\n" + "Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF\n" + "AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo\n" + "uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/\n" + "wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu\n" + "X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG\n" + "PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6\n" + "KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg==\n" + "-----END CERTIFICATE-----\n"; + + +void cmd_httpbin_post(uint8_t idx) +{ + const char body[] = "{\"hello\":\"world\"}"; + +#if DEMO == DEMO_HTTPS + printf("\n----- HTTPS POST request -----\n"); + HttpsRequest* post_req = new HttpsRequest(network, HTTBIN_ORG_SSL_CA_PEM, HTTP_POST, "https://httpbin.org/post"); +#elif DEMO == DEMO_HTTP + printf("\n----- HTTP POST request -----\n"); + HttpRequest* post_req = new HttpRequest(network, HTTP_POST, "http://httpbin.org/post"); +#endif + + post_req->set_header("Content-Type", "application/json"); + + HttpResponse* post_res = post_req->send(body, strlen(body)); + if (!post_res) { + printf("HttpRequest failed (error code %d)\n", post_req->get_error()); + return; + } + + dump_response(post_res); + delete post_req; +} + + +void cmd_print_status(uint8_t idx) +{ + stat_t stat; + uint8_t buf[4]; + printf("[NWKH] IP address: %s\n", network->get_ip_address()); + + 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_STATUS, 4, 0, buf); + printf("chipMode:%d, cmdStatus:%d\r\n", stat.bits.chipMode, stat.bits.cmdStatus); +} + +const menu_item_t menu_items[] = +{ + { "phb", cmd_httpbin_post, "","test post to httpbin.org"}, + { "ws", cmd_wifi_scan, "","local wifi scan"}, + { ".", cmd_print_status, "","print status"}, + { "?", cmd_help, "","this list of commands"}, + { NULL, NULL, NULL, NULL } +}; + +void +console() +{ + int i; + uint8_t user_cmd_len; + + if (pcbuf_len < 0) { + printf("abort\r\n"); + pcbuf_len = 0; + return; + } + + printf("\r\n"); + + if (pcbuf_len > 0) { + /* get end of user-entered command */ + user_cmd_len = 1; // first character can be any character + for (i = 1; i <= pcbuf_len; i++) { + if (pcbuf[i] < 'A' || (pcbuf[i] > 'Z' && pcbuf[i] < 'a') || pcbuf[i] > 'z') { + user_cmd_len = i; + break; + } + } + + + for (i = 0; menu_items[i].cmd != NULL ; i++) { + int mi_len = strlen(menu_items[i].cmd); + if (menu_items[i].handler && user_cmd_len == mi_len && (strncmp(pcbuf, menu_items[i].cmd, mi_len) == 0)) { + while (pcbuf[mi_len] == ' ') // skip past spaces + mi_len++; + menu_items[i].handler(mi_len); + break; + } + } + } + + pcbuf_len = 0; + printf("> "); + fflush(stdout); +} + +void echo(char c) +{ + if (c == 8) { + pc.putc(8); + pc.putc(' '); + pc.putc(8); + } else + pc.putc(c); +} + +uint8_t serial_rx_buf; + +void serialCb(int events) +{ + if (events & SERIAL_EVENT_RX_COMPLETE) { + char c = serial_rx_buf; + static uint8_t pcbuf_idx = 0; + static uint8_t prev_len = 0;; + if (c == 8) { + if (pcbuf_idx > 0) { + queue.call(echo, 8); + pcbuf_idx--; + } + } else if (c == 3) { // ctrl-C + pcbuf_len = -1; + queue.call(console); + } else if (c == '\r') { + if (pcbuf_idx == 0) { + pcbuf_len = prev_len; + } else { + pcbuf[pcbuf_idx] = 0; // null terminate + prev_len = pcbuf_idx; + pcbuf_idx = 0; + pcbuf_len = prev_len; + } + queue.call(console); + } else if (pcbuf_idx < sizeof(pcbuf)) { + pcbuf[pcbuf_idx++] = c; + queue.call(echo, c); + } + } + + if (pc.read(&serial_rx_buf, 1, serialCb) != 0) + printf("Serial-Read-Fail\r\n"); +} + +void cmd_help(uint8_t args_at) +{ + int i; + + for (i = 0; menu_items[i].cmd != NULL ; i++) { + printf("%s%s\t%s\r\n", menu_items[i].cmd, menu_items[i].arg_descr, menu_items[i].description); + } +} + + +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); +} + +void take_result() +{ + printf("result %f, %f, %d\r\n", + geoloc_result.lat, + geoloc_result.lng, + geoloc_result.accuracy + ); + + /* TODO: store result to database and show on map */ + + if (send_reply) { + unsigned len; + memcpy(Radio::radio.tx_buf, remote_chip_eui, 8); + Radio::radio.tx_buf[8] = 0; // rfu + Radio::radio.tx_buf[9] = 0; // rfu + len = sprintf((char*)(Radio::radio.tx_buf + HEADER_LENGTH), "%f, %f, %u", + geoloc_result.lat, + geoloc_result.lng, + geoloc_result.accuracy + ); + Radio::Send(len + HEADER_LENGTH, 0, 0, 0); /* begin transmission */ + send_reply = false; // sent + } +} + +void service() +{ + irq_t irq; + irq.dword = Radio::radio.service(); + if (irq.bits.WifiDone) { + stat_t stat; + uint8_t nbResults; + json_start(); + 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) { + unsigned n; + 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) { + wifiChanInfo_t ci; + print_wifi_result(resultBuf); + ci.octet = resultBuf[1]; + if (ci.bits.macValidationID == 1) // 1 is AP + wifi_result_to_json(n == 0, resultBuf, wifiResultFormatBasic ? 3 : 4, 2); + } else + printf("readResult:%s\r\n", Radio::radio.cmdStatus_toString(stat.bits.cmdStatus)); + } + } + json_end(); + //printf("JSON %s\r\n", json); + if (post_enable) { + printf("post_enabled\r\n"); + if (nbResults > MINIMUM_REQUIRED_ACCESS_POINTS) { + post_scan_result(json, &geoloc_result.lat, &geoloc_result.lng, &geoloc_result.accuracy); + queue.call(take_result); + } else + printf("only %u access points\r\n", nbResults); + } + + cfg_lora(); + Radio::Rx(0); + } // ..if (irq.bits.WifiDone) +} + +void radio_irq_handler() +{ + wifi_scan_dur = Kernel::get_ms_count() - wifi_start_at; + queue.call(service); +} + +void txDoneCB() +{ + Radio::Rx(0); +} + +void parse_remote_wifi_scan(uint8_t pktLen) +{ + uint8_t ap_cnt = 0; + uint8_t pkt_idx = HEADER_LENGTH; + json_start(); + while (pkt_idx < pktLen) { + wifi_result_to_json(pkt_idx == HEADER_LENGTH, Radio::radio.rx_buf + pkt_idx, 0, 6); + if (strlen(json) >= sizeof(json)) { + printf("json-overrun\r\n"); + return; + } + pkt_idx += 7; + ap_cnt++; + } + json_end(); + + if (ap_cnt > MINIMUM_REQUIRED_ACCESS_POINTS) { + post_scan_result(json, &geoloc_result.lat, &geoloc_result.lng, &geoloc_result.accuracy); + queue.call(take_result); + } else + printf("only %u access points\r\n", ap_cnt); + + send_reply = true; +} + +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"); + + for (i = 0; i < 8; i++) + remote_chip_eui[i] = Radio::radio.rx_buf[i]; + + parse_remote_wifi_scan(size); +} + +const RadioEvents_t rev = { + /* Dio0_top_half */ radio_irq_handler, + /* TxDone_topHalf */ NULL, + /* TxDone_botHalf */ txDoneCB, + /* TxTimeout */ NULL, + /* RxDone */ rxDoneCB, + /* RxTimeout */ NULL, + /* RxError */ NULL, + /* FhssChangeChannel */NULL, + /* CadDone */ NULL +}; + +int main() +{ + { /* 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] = 0x0a; // 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 + } + + serialEventCb = serialCb; + + if (pc.read(&serial_rx_buf, 1, serialCb) != 0) + printf("serial-read-fail\r\n"); + + // Connect to the network with the default networking interface + // if you use WiFi: see mbed_app.json for the credentials + network = connect_to_default_network_interface(); + if (!network) { + printf("Cannot connect to the network, see serial output\n"); + return 1; + } + + Radio::Init(&rev); + + Radio::Standby(); + cfg_lora(); + Radio::Rx(0); + + queue.dispatch(); +} +
diff -r fd6707e25c57 -r 4a05f91c9c38 main.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.h Tue Feb 09 10:49:02 2021 -0800 @@ -0,0 +1,25 @@ +#include "select-demo.h" + +#include "mbed.h" +#if DEMO == DEMO_HTTPS + #include "https_request.h" +#elif DEMO == DEMO_HTTP + #include "http_request.h" +#endif + +#define GOOGLE 1 +#define COMBAIN 2 +//#define GEOLOCATION_PROVIDER GOOGLE +//#define GEOLOCATION_PROVIDER COMBAIN + +#define HEADER_LENGTH 10 /* for chipEUI and extra reserved */ + +int post_scan_result(const char*, float*, float*, int*); +void wifi_result_to_json(bool first, const uint8_t *result, unsigned macStart, unsigned rssi_idx); +void json_start(void); +void json_end(void); +extern char json[]; +extern bool wifiResultFormatBasic; +extern NetworkInterface* network; +void dump_response(HttpResponse* res); +
diff -r fd6707e25c57 -r 4a05f91c9c38 mbed_app.json --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed_app.json Tue Feb 09 10:49:02 2021 -0800 @@ -0,0 +1,31 @@ +{ + "config": { + "main-stack-size": { + "value": 8192 + } + }, + "macros": [ + "MBEDTLS_MPI_MAX_SIZE=1024", + "MBEDTLS_MPI_WINDOW_SIZE=1", + "MBEDTLS_USER_CONFIG_FILE=\"mbedtls_entropy_config.h\"", + "MBEDTLS_TEST_NULL_ENTROPY", + "MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES", + "MBED_HEAP_STATS_ENABLED=1" + ], + "target_overrides": { + "*": { + "platform.stdio-baud-rate": 115200, + "platform.stdio-convert-newlines": true, + "mbed-mesh-api.6lowpan-nd-channel-page": 0, + "mbed-mesh-api.6lowpan-nd-channel": 12, + "mbed-trace.enable": null, + "mbed-http.http-buffer-size": 2048, + "nsapi.default-wifi-security": "WPA_WPA2", + "nsapi.default-wifi-ssid": "\"SSID\"", + "nsapi.default-wifi-password": "\"Password\"" + }, + "DISCO_L475VG_IOT01A": { + "target.network-default-interface-type" : "WIFI" + } + } +}
diff -r fd6707e25c57 -r 4a05f91c9c38 mbedtls_entropy_config.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbedtls_entropy_config.h Tue Feb 09 10:49:02 2021 -0800 @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2006-2016, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of mbed TLS (https://tls.mbed.org) + */ + +#include "select-demo.h" + +/* Enable entropy for devices with TRNG. This means entropy is disabled for all other targets. */ +/* Do **NOT** deploy this code in production on other targets! */ +/* See https://tls.mbed.org/kb/how-to/add-entropy-sources-to-entropy-pool */ +#if defined(DEVICE_TRNG) +#undef MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES +#undef MBEDTLS_TEST_NULL_ENTROPY +#endif
diff -r fd6707e25c57 -r 4a05f91c9c38 network-helper.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/network-helper.h Tue Feb 09 10:49:02 2021 -0800 @@ -0,0 +1,34 @@ +#ifndef _MBED_HTTP_EXAMPLE_H_ +#define _MBED_HTTP_EXAMPLE_H_ + +#include "mbed.h" +#include "NetworkInterface.h" + +/** + * Connect to the network using the default networking interface, + * you can also swap this out with a driver for a different networking interface + * if you use WiFi: see mbed_app.json for the credentials + */ +NetworkInterface *connect_to_default_network_interface() { + printf("[NWKH] Connecting to network...\n"); + + NetworkInterface* network = NetworkInterface::get_default_instance(); + + if (!network) { + printf("[NWKH] No network interface found, select an interface in 'network-helper.h'\n"); + return NULL; + } + + nsapi_error_t connect_status = network->connect(); + + if (connect_status != NSAPI_ERROR_OK) { + printf("[NWKH] Failed to connect to network (%d)\n", connect_status); + return NULL; + } + + printf("[NWKH] Connected to the network\n"); + printf("[NWKH] IP address: %s\n", network->get_ip_address()); + return network; +} + +#endif // _MBED_HTTP_EXAMPLE_H_
diff -r fd6707e25c57 -r 4a05f91c9c38 source/select-demo.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/source/select-demo.h Tue Feb 09 10:49:02 2021 -0800 @@ -0,0 +1,14 @@ +#ifndef _SELECT_METHOD_H_ +#define _SELECT_METHOD_H_ + +#define DEMO_HTTP 1 +#define DEMO_HTTP_SOCKET_REUSE 2 +#define DEMO_HTTP_IPV6 3 +#define DEMO_HTTPS 4 +#define DEMO_HTTPS_SOCKET_REUSE 5 +#define DEMO_HTTPS_CHUNKED_REQUEST 6 +#define DEMO_TESTS 7 + +#define DEMO DEMO_HTTPS + +#endif // _SELECT_METHOD_H_