diff --git a/src/adcread.cpp b/src/adcread.cpp index 1dc46776..84b1313d 100644 --- a/src/adcread.cpp +++ b/src/adcread.cpp @@ -5,52 +5,57 @@ #include #include -#define DEFAULT_VREF 1100 // to be done: use adc2_vref_to_gpio() to obtain a better estimate -#define NO_OF_SAMPLES 64 // we do multisampling +#define DEFAULT_VREF \ + 1100 // to be done: use adc2_vref_to_gpio() to obtain a better estimate +#define NO_OF_SAMPLES 64 // we do multisampling // Local logging tag static const char TAG[] = "main"; -static void print_char_val_type(esp_adc_cal_value_t val_type) -{ - if (val_type == ESP_ADC_CAL_VAL_EFUSE_TP) { - ESP_LOGI(TAG,"ADC characterization based on Two Point values stored in eFuse"); - } else if (val_type == ESP_ADC_CAL_VAL_EFUSE_VREF) { - ESP_LOGI(TAG,"ADC characterization based on reference voltage stored in eFuse"); - } else { - ESP_LOGI(TAG,"ADC characterization based on default reference voltage"); - } +static void print_char_val_type(esp_adc_cal_value_t val_type) { + if (val_type == ESP_ADC_CAL_VAL_EFUSE_TP) { + ESP_LOGI(TAG, + "ADC characterization based on Two Point values stored in eFuse"); + } else if (val_type == ESP_ADC_CAL_VAL_EFUSE_VREF) { + ESP_LOGI(TAG, + "ADC characterization based on reference voltage stored in eFuse"); + } else { + ESP_LOGI(TAG, "ADC characterization based on default reference voltage"); + } } -uint16_t read_voltage(void) -{ - static const adc1_channel_t channel = HAS_BATTERY_PROBE; - static const adc_atten_t atten = ADC_ATTEN_DB_11; - static const adc_unit_t unit = ADC_UNIT_1; +uint16_t read_voltage(void) { + static const adc1_channel_t channel = HAS_BATTERY_PROBE; + static const adc_atten_t atten = ADC_ATTEN_DB_11; + static const adc_unit_t unit = ADC_UNIT_1; - //configure ADC1 - ESP_ERROR_CHECK(adc1_config_width(ADC_WIDTH_BIT_12)); - ESP_ERROR_CHECK(adc1_config_channel_atten(channel, atten)); + // configure ADC1 + ESP_ERROR_CHECK(adc1_config_width(ADC_WIDTH_BIT_12)); + ESP_ERROR_CHECK(adc1_config_channel_atten(channel, atten)); - //calibrate ADC1 - esp_adc_cal_characteristics_t *adc_chars = (esp_adc_cal_characteristics_t *) calloc(1, sizeof(esp_adc_cal_characteristics_t)); - esp_adc_cal_value_t val_type = esp_adc_cal_characterize(unit, atten, ADC_WIDTH_BIT_12, DEFAULT_VREF, adc_chars); - print_char_val_type(val_type); + // calibrate ADC1 + esp_adc_cal_characteristics_t *adc_chars = + (esp_adc_cal_characteristics_t *)calloc( + 1, sizeof(esp_adc_cal_characteristics_t)); + esp_adc_cal_value_t val_type = esp_adc_cal_characterize( + unit, atten, ADC_WIDTH_BIT_12, DEFAULT_VREF, adc_chars); + print_char_val_type(val_type); - //multisample ADC1 - uint32_t adc_reading = 0; - for (int i = 0; i < NO_OF_SAMPLES; i++) { - adc_reading += adc1_get_raw(channel); - } + // multisample ADC1 + uint32_t adc_reading = 0; + for (int i = 0; i < NO_OF_SAMPLES; i++) { + adc_reading += adc1_get_raw(channel); + } - adc_reading /= NO_OF_SAMPLES; + adc_reading /= NO_OF_SAMPLES; - //Convert adc_reading to voltage in mV - uint16_t voltage = (uint16_t) esp_adc_cal_raw_to_voltage(adc_reading, adc_chars); - #ifdef BATT_FACTOR - voltage *= BATT_FACTOR; - #endif - ESP_LOGI(TAG,"Raw: %d / Voltage: %dmV", adc_reading, voltage); - return voltage; + // Convert adc_reading to voltage in mV + uint16_t voltage = + (uint16_t)esp_adc_cal_raw_to_voltage(adc_reading, adc_chars); +#ifdef BATT_FACTOR + voltage *= BATT_FACTOR; +#endif + ESP_LOGI(TAG, "Raw: %d / Voltage: %dmV", adc_reading, voltage); + return voltage; } #endif // HAS_BATTERY_PROBE \ No newline at end of file diff --git a/src/antenna.cpp b/src/antenna.cpp index 402a313b..b8d044dd 100644 --- a/src/antenna.cpp +++ b/src/antenna.cpp @@ -1,4 +1,5 @@ -/* switches wifi antenna, if board has switch to select internal and external antenna */ +/* switches wifi antenna, if board has switch to select internal and external + * antenna */ #ifdef HAS_ANTENNA_SWITCH @@ -7,35 +8,32 @@ // Local logging tag static const char TAG[] = "wifi"; -typedef enum { - ANTENNA_INT = 0, - ANTENNA_EXT -} antenna_type_t; +typedef enum { ANTENNA_INT = 0, ANTENNA_EXT } antenna_type_t; void antenna_init(void) { - gpio_config_t gpioconf = {.pin_bit_mask = 1ull << HAS_ANTENNA_SWITCH, - .mode = GPIO_MODE_OUTPUT, - .pull_up_en = GPIO_PULLUP_DISABLE, - .pull_down_en = GPIO_PULLDOWN_DISABLE, - .intr_type = GPIO_INTR_DISABLE}; - gpio_config(&gpioconf); + gpio_config_t gpioconf = {.pin_bit_mask = 1ull << HAS_ANTENNA_SWITCH, + .mode = GPIO_MODE_OUTPUT, + .pull_up_en = GPIO_PULLUP_DISABLE, + .pull_down_en = GPIO_PULLDOWN_DISABLE, + .intr_type = GPIO_INTR_DISABLE}; + gpio_config(&gpioconf); } -void antenna_select (const uint8_t _ant) { - if (HAS_ANTENNA_SWITCH < 32) { - if (_ant == ANTENNA_EXT) { - GPIO_REG_WRITE(GPIO_OUT_W1TS_REG, 1 << HAS_ANTENNA_SWITCH); - } else { - GPIO_REG_WRITE(GPIO_OUT_W1TC_REG, 1 << HAS_ANTENNA_SWITCH); - } - } else { - if (_ant == ANTENNA_EXT) { - GPIO_REG_WRITE(GPIO_OUT1_W1TS_REG, 1 << (HAS_ANTENNA_SWITCH & 31)); - } else { - GPIO_REG_WRITE(GPIO_OUT1_W1TC_REG, 1 << (HAS_ANTENNA_SWITCH & 31)); - } - } - ESP_LOGI(TAG, "Wifi Antenna switched to %s", _ant ? "external" : "internal"); +void antenna_select(const uint8_t _ant) { + if (HAS_ANTENNA_SWITCH < 32) { + if (_ant == ANTENNA_EXT) { + GPIO_REG_WRITE(GPIO_OUT_W1TS_REG, 1 << HAS_ANTENNA_SWITCH); + } else { + GPIO_REG_WRITE(GPIO_OUT_W1TC_REG, 1 << HAS_ANTENNA_SWITCH); + } + } else { + if (_ant == ANTENNA_EXT) { + GPIO_REG_WRITE(GPIO_OUT1_W1TS_REG, 1 << (HAS_ANTENNA_SWITCH & 31)); + } else { + GPIO_REG_WRITE(GPIO_OUT1_W1TC_REG, 1 << (HAS_ANTENNA_SWITCH & 31)); + } + } + ESP_LOGI(TAG, "Wifi Antenna switched to %s", _ant ? "external" : "internal"); } #endif \ No newline at end of file diff --git a/src/blecsan.cpp b/src/blecsan.cpp index c7f4f9b4..af3e725d 100644 --- a/src/blecsan.cpp +++ b/src/blecsan.cpp @@ -8,13 +8,14 @@ https://github.com/nkolban/esp32-snippets/tree/master/BLE/scanner #include "globals.h" // Bluetooth specific includes +#include +#include // needed for BLE_ADDR types, do not remove #include #include #include -#include // needed for BLE_ADDR types, do not remove -#include -#define BT_BD_ADDR_HEX(addr) addr[0], addr[1], addr[2], addr[3], addr[4], addr[5] +#define BT_BD_ADDR_HEX(addr) \ + addr[0], addr[1], addr[2], addr[3], addr[4], addr[5] // local Tag for logging static const char TAG[] = "bluetooth"; @@ -23,208 +24,256 @@ static const char TAG[] = "bluetooth"; bool mac_add(uint8_t *paddr, int8_t rssi, bool sniff_type); const char *bt_addr_t_to_string(esp_ble_addr_type_t type) { - switch(type) { - case BLE_ADDR_TYPE_PUBLIC: - return "BLE_ADDR_TYPE_PUBLIC"; - case BLE_ADDR_TYPE_RANDOM: - return "BLE_ADDR_TYPE_RANDOM"; - case BLE_ADDR_TYPE_RPA_PUBLIC: - return "BLE_ADDR_TYPE_RPA_PUBLIC"; - case BLE_ADDR_TYPE_RPA_RANDOM: - return "BLE_ADDR_TYPE_RPA_RANDOM"; - default: - return "Unknown addr_t"; - } + switch (type) { + case BLE_ADDR_TYPE_PUBLIC: + return "BLE_ADDR_TYPE_PUBLIC"; + case BLE_ADDR_TYPE_RANDOM: + return "BLE_ADDR_TYPE_RANDOM"; + case BLE_ADDR_TYPE_RPA_PUBLIC: + return "BLE_ADDR_TYPE_RPA_PUBLIC"; + case BLE_ADDR_TYPE_RPA_RANDOM: + return "BLE_ADDR_TYPE_RPA_RANDOM"; + default: + return "Unknown addr_t"; + } } // bt_addr_t_to_string const char *btsig_gap_type(uint32_t gap_type) { - switch (gap_type) - { - case 0x01: return "Flags"; - case 0x02: return "Incomplete List of 16-bit Service Class UUIDs"; - case 0x03: return "Complete List of 16-bit Service Class UUIDs"; - case 0x04: return "Incomplete List of 32-bit Service Class UUIDs"; - case 0x05: return "Complete List of 32-bit Service Class UUIDs"; - case 0x06: return "Incomplete List of 128-bit Service Class UUIDs"; - case 0x07: return "Complete List of 128-bit Service Class UUIDs"; - case 0x08: return "Shortened Local Name"; - case 0x09: return "Complete Local Name"; - case 0x0A: return "Tx Power Level"; - case 0x0D: return "Class of Device"; - case 0x0E: return "Simple Pairing Hash C/C-192"; - case 0x0F: return "Simple Pairing Randomizer R/R-192"; - case 0x10: return "Device ID/Security Manager TK Value"; - case 0x11: return "Security Manager Out of Band Flags"; - case 0x12: return "Slave Connection Interval Range"; - case 0x14: return "List of 16-bit Service Solicitation UUIDs"; - case 0x1F: return "List of 32-bit Service Solicitation UUIDs"; - case 0x15: return "List of 128-bit Service Solicitation UUIDs"; - case 0x16: return "Service Data - 16-bit UUID"; - case 0x20: return "Service Data - 32-bit UUID"; - case 0x21: return "Service Data - 128-bit UUID"; - case 0x22: return "LE Secure Connections Confirmation Value"; - case 0x23: return "LE Secure Connections Random Value"; - case 0x24: return "URI"; - case 0x25: return "Indoor Positioning"; - case 0x26: return "Transport Discovery Data"; - case 0x17: return "Public Target Address"; - case 0x18: return "Random Target Address"; - case 0x19: return "Appearance"; - case 0x1A: return "Advertising Interval"; - case 0x1B: return "LE Bluetooth Device Address"; - case 0x1C: return "LE Role"; - case 0x1D: return "Simple Pairing Hash C-256"; - case 0x1E: return "Simple Pairing Randomizer R-256"; - case 0x3D: return "3D Information Data"; - case 0xFF: return "Manufacturer Specific Data"; - - default: - return "Unknown type"; - } + switch (gap_type) { + case 0x01: + return "Flags"; + case 0x02: + return "Incomplete List of 16-bit Service Class UUIDs"; + case 0x03: + return "Complete List of 16-bit Service Class UUIDs"; + case 0x04: + return "Incomplete List of 32-bit Service Class UUIDs"; + case 0x05: + return "Complete List of 32-bit Service Class UUIDs"; + case 0x06: + return "Incomplete List of 128-bit Service Class UUIDs"; + case 0x07: + return "Complete List of 128-bit Service Class UUIDs"; + case 0x08: + return "Shortened Local Name"; + case 0x09: + return "Complete Local Name"; + case 0x0A: + return "Tx Power Level"; + case 0x0D: + return "Class of Device"; + case 0x0E: + return "Simple Pairing Hash C/C-192"; + case 0x0F: + return "Simple Pairing Randomizer R/R-192"; + case 0x10: + return "Device ID/Security Manager TK Value"; + case 0x11: + return "Security Manager Out of Band Flags"; + case 0x12: + return "Slave Connection Interval Range"; + case 0x14: + return "List of 16-bit Service Solicitation UUIDs"; + case 0x1F: + return "List of 32-bit Service Solicitation UUIDs"; + case 0x15: + return "List of 128-bit Service Solicitation UUIDs"; + case 0x16: + return "Service Data - 16-bit UUID"; + case 0x20: + return "Service Data - 32-bit UUID"; + case 0x21: + return "Service Data - 128-bit UUID"; + case 0x22: + return "LE Secure Connections Confirmation Value"; + case 0x23: + return "LE Secure Connections Random Value"; + case 0x24: + return "URI"; + case 0x25: + return "Indoor Positioning"; + case 0x26: + return "Transport Discovery Data"; + case 0x17: + return "Public Target Address"; + case 0x18: + return "Random Target Address"; + case 0x19: + return "Appearance"; + case 0x1A: + return "Advertising Interval"; + case 0x1B: + return "LE Bluetooth Device Address"; + case 0x1C: + return "LE Role"; + case 0x1D: + return "Simple Pairing Hash C-256"; + case 0x1E: + return "Simple Pairing Randomizer R-256"; + case 0x3D: + return "3D Information Data"; + case 0xFF: + return "Manufacturer Specific Data"; + + default: + return "Unknown type"; + } } // btsig_gap_type // using IRAM_:ATTR here to speed up callback function -IRAM_ATTR void gap_callback_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) -{ - esp_ble_gap_cb_param_t *p = (esp_ble_gap_cb_param_t *)param; - - ESP_LOGD(TAG, "BT payload rcvd -> type: 0x%.2x -> %s", *p->scan_rst.ble_adv, btsig_gap_type(*p->scan_rst.ble_adv)); +IRAM_ATTR void gap_callback_handler(esp_gap_ble_cb_event_t event, + esp_ble_gap_cb_param_t *param) { + esp_ble_gap_cb_param_t *p = (esp_ble_gap_cb_param_t *)param; - switch (event) - { - case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT: - // restart scan - ESP_ERROR_CHECK(esp_ble_gap_start_scanning(BLESCANTIME)); - break; - - case ESP_GAP_BLE_SCAN_RESULT_EVT: - // evaluate scan results - if ( p->scan_rst.search_evt == ESP_GAP_SEARCH_INQ_CMPL_EVT) // Inquiry complete, scan is done - { // restart scan - ESP_ERROR_CHECK(esp_ble_gap_start_scanning(BLESCANTIME)); - return; - } - - if (p->scan_rst.search_evt == ESP_GAP_SEARCH_INQ_RES_EVT) // Inquiry result for a peer device - { // evaluate sniffed packet - ESP_LOGD(TAG, "Device address (bda): %02x:%02x:%02x:%02x:%02x:%02x", BT_BD_ADDR_HEX(p->scan_rst.bda)); - ESP_LOGD(TAG, "Addr_type : %s", bt_addr_t_to_string(p->scan_rst.ble_addr_type)); - ESP_LOGD(TAG, "RSSI : %d", p->scan_rst.rssi); + ESP_LOGD(TAG, "BT payload rcvd -> type: 0x%.2x -> %s", *p->scan_rst.ble_adv, + btsig_gap_type(*p->scan_rst.ble_adv)); - if ((cfg.rssilimit) && (p->scan_rst.rssi < cfg.rssilimit )) { // rssi is negative value - ESP_LOGI(TAG, "BLTH RSSI %d -> ignoring (limit: %d)", p->scan_rst.rssi, cfg.rssilimit); - break; - } + switch (event) { + case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT: + // restart scan + ESP_ERROR_CHECK(esp_ble_gap_start_scanning(BLESCANTIME)); + break; - #ifdef VENDORFILTER - - if ((p->scan_rst.ble_addr_type == BLE_ADDR_TYPE_RANDOM) || (p->scan_rst.ble_addr_type == BLE_ADDR_TYPE_RPA_RANDOM)) { - ESP_LOGD(TAG, "BT device filtered"); - break; - } + case ESP_GAP_BLE_SCAN_RESULT_EVT: + // evaluate scan results + if (p->scan_rst.search_evt == + ESP_GAP_SEARCH_INQ_CMPL_EVT) // Inquiry complete, scan is done + { // restart scan + ESP_ERROR_CHECK(esp_ble_gap_start_scanning(BLESCANTIME)); + return; + } - #endif + if (p->scan_rst.search_evt == + ESP_GAP_SEARCH_INQ_RES_EVT) // Inquiry result for a peer device + { // evaluate sniffed packet + ESP_LOGD(TAG, "Device address (bda): %02x:%02x:%02x:%02x:%02x:%02x", + BT_BD_ADDR_HEX(p->scan_rst.bda)); + ESP_LOGD(TAG, "Addr_type : %s", + bt_addr_t_to_string(p->scan_rst.ble_addr_type)); + ESP_LOGD(TAG, "RSSI : %d", p->scan_rst.rssi); - // add this device and show new count total if it was not previously added - mac_add((uint8_t *) p->scan_rst.bda, p->scan_rst.rssi, MAC_SNIFF_BLE); - - /* to be improved in vendorfilter if: - - // you can search for elements in the payload using the - // function esp_ble_resolve_adv_data() - // - // Like this, that scans for the "Complete name" (looking inside the payload buffer) - // uint8_t len; - // uint8_t *data = esp_ble_resolve_adv_data(p->scan_rst.ble_adv, ESP_BLE_AD_TYPE_NAME_CMPL, &len); - - filter BLE devices using their advertisements to get filter alternative to vendor OUI - if vendorfiltering is on, we ... - - want to count: mobile phones and tablets - - don't want to count: beacons, peripherals (earphones, headsets, printers), cars and machines - see - https://github.com/nkolban/ESP32_BLE_Arduino/blob/master/src/BLEAdvertisedDevice.cpp - - http://www.libelium.com/products/meshlium/smartphone-detection/ - - https://www.question-defense.com/2013/01/12/bluetooth-cod-bluetooth-class-of-deviceclass-of-service-explained - - https://www.bluetooth.com/specifications/assigned-numbers/baseband - - "The Class of Device (CoD) in case of Bluetooth which allows us to differentiate the type of - device (smartphone, handsfree, computer, LAN/network AP). With this parameter we can - differentiate among pedestrians and vehicles." - - */ - - } // evaluate sniffed packet - break; - - default: + if ((cfg.rssilimit) && + (p->scan_rst.rssi < cfg.rssilimit)) { // rssi is negative value + ESP_LOGI(TAG, "BLTH RSSI %d -> ignoring (limit: %d)", p->scan_rst.rssi, + cfg.rssilimit); break; - } + } + +#ifdef VENDORFILTER + + if ((p->scan_rst.ble_addr_type == BLE_ADDR_TYPE_RANDOM) || + (p->scan_rst.ble_addr_type == BLE_ADDR_TYPE_RPA_RANDOM)) { + ESP_LOGD(TAG, "BT device filtered"); + break; + } + +#endif + + // add this device and show new count total if it was not previously added + mac_add((uint8_t *)p->scan_rst.bda, p->scan_rst.rssi, MAC_SNIFF_BLE); + + /* to be improved in vendorfilter if: + + // you can search for elements in the payload using the + // function esp_ble_resolve_adv_data() + // + // Like this, that scans for the "Complete name" (looking inside the + payload buffer) + // uint8_t len; + // uint8_t *data = esp_ble_resolve_adv_data(p->scan_rst.ble_adv, + ESP_BLE_AD_TYPE_NAME_CMPL, &len); + + filter BLE devices using their advertisements to get filter alternative to + vendor OUI if vendorfiltering is on, we ... + - want to count: mobile phones and tablets + - don't want to count: beacons, peripherals (earphones, headsets, + printers), cars and machines see + https://github.com/nkolban/ESP32_BLE_Arduino/blob/master/src/BLEAdvertisedDevice.cpp + + http://www.libelium.com/products/meshlium/smartphone-detection/ + + https://www.question-defense.com/2013/01/12/bluetooth-cod-bluetooth-class-of-deviceclass-of-service-explained + + https://www.bluetooth.com/specifications/assigned-numbers/baseband + + "The Class of Device (CoD) in case of Bluetooth which allows us to + differentiate the type of device (smartphone, handsfree, computer, + LAN/network AP). With this parameter we can differentiate among + pedestrians and vehicles." + + */ + + } // evaluate sniffed packet + break; + + default: + break; + } } // gap_callback_handler - esp_err_t register_ble_callback(void) { - ESP_LOGI(TAG, "Register GAP callback"); - - // This function is called to occur gap event, such as scan result. - //register the scan callback function to the gap module - ESP_ERROR_CHECK(esp_ble_gap_register_callback(&gap_callback_handler)); + ESP_LOGI(TAG, "Register GAP callback"); - static esp_ble_scan_params_t ble_scan_params = - { - .scan_type = BLE_SCAN_TYPE_PASSIVE, - .own_addr_type = BLE_ADDR_TYPE_RANDOM, - - #ifdef VENDORFILTER - .scan_filter_policy = BLE_SCAN_FILTER_ALLOW_WLIST_PRA_DIR, - // ADV_IND, ADV_NONCONN_IND, ADV_SCAN_IND packets are used for broadcasting - // data in broadcast applications (e.g., Beacons), so we don't want them in vendorfilter mode - #else - .scan_filter_policy = BLE_SCAN_FILTER_ALLOW_ALL, - #endif + // This function is called to occur gap event, such as scan result. + // register the scan callback function to the gap module + ESP_ERROR_CHECK(esp_ble_gap_register_callback(&gap_callback_handler)); - .scan_interval = (uint16_t) (cfg.blescantime * 10 / 0.625), // Time = N * 0.625 msec - .scan_window = (uint16_t) (BLESCANWINDOW / 0.625) // Time = N * 0.625 msec - }; + static esp_ble_scan_params_t ble_scan_params = { + .scan_type = BLE_SCAN_TYPE_PASSIVE, + .own_addr_type = BLE_ADDR_TYPE_RANDOM, - ESP_LOGI(TAG, "Set GAP scan parameters"); +#ifdef VENDORFILTER + .scan_filter_policy = BLE_SCAN_FILTER_ALLOW_WLIST_PRA_DIR, + // ADV_IND, ADV_NONCONN_IND, ADV_SCAN_IND packets are used for broadcasting + // data in broadcast applications (e.g., Beacons), so we don't want them in + // vendorfilter mode +#else + .scan_filter_policy = BLE_SCAN_FILTER_ALLOW_ALL, +#endif - // This function is called to set scan parameters. - ESP_ERROR_CHECK(esp_ble_gap_set_scan_params(&ble_scan_params)); - - return ESP_OK; + .scan_interval = + (uint16_t)(cfg.blescantime * 10 / 0.625), // Time = N * 0.625 msec + .scan_window = (uint16_t)(BLESCANWINDOW / 0.625) // Time = N * 0.625 msec + }; + + ESP_LOGI(TAG, "Set GAP scan parameters"); + + // This function is called to set scan parameters. + ESP_ERROR_CHECK(esp_ble_gap_set_scan_params(&ble_scan_params)); + + return ESP_OK; } // register_ble_callback -void start_BLEscan(void){ - ESP_LOGI(TAG, "Initializing bluetooth scanner ..."); +void start_BLEscan(void) { + ESP_LOGI(TAG, "Initializing bluetooth scanner ..."); - // Initialize BT controller to allocate task and other resource. - esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); - bt_cfg.controller_task_stack_size = BLESTACKSIZE; // set BT stack size to value configured in paxcounter.conf - ESP_ERROR_CHECK(esp_bt_controller_init(&bt_cfg)); - ESP_ERROR_CHECK(esp_bt_controller_enable(ESP_BT_MODE_BTDM)); + // Initialize BT controller to allocate task and other resource. + esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); + bt_cfg.controller_task_stack_size = + BLESTACKSIZE; // set BT stack size to value configured in paxcounter.conf + ESP_ERROR_CHECK(esp_bt_controller_init(&bt_cfg)); + ESP_ERROR_CHECK(esp_bt_controller_enable(ESP_BT_MODE_BTDM)); - // Init and alloc the resource for bluetooth stack, must be done prior to every bluetooth stuff - ESP_ERROR_CHECK(esp_bluedroid_init()); - ESP_ERROR_CHECK(esp_bluedroid_enable()); + // Init and alloc the resource for bluetooth stack, must be done prior to + // every bluetooth stuff + ESP_ERROR_CHECK(esp_bluedroid_init()); + ESP_ERROR_CHECK(esp_bluedroid_enable()); - // Register callback function for capturing bluetooth packets - ESP_ERROR_CHECK(register_ble_callback()); + // Register callback function for capturing bluetooth packets + ESP_ERROR_CHECK(register_ble_callback()); - ESP_LOGI(TAG, "Bluetooth scanner started"); + ESP_LOGI(TAG, "Bluetooth scanner started"); } // start_BLEscan -void stop_BLEscan(void){ - ESP_LOGI(TAG, "Shutting down bluetooth scanner ..."); - ESP_ERROR_CHECK(esp_ble_gap_register_callback(NULL)); - ESP_ERROR_CHECK(esp_bluedroid_disable()); - ESP_ERROR_CHECK(esp_bluedroid_deinit()); - ESP_ERROR_CHECK(esp_bt_controller_disable()); - ESP_ERROR_CHECK(esp_bt_controller_deinit()); - ESP_LOGI(TAG, "Bluetooth scanner stopped"); +void stop_BLEscan(void) { + ESP_LOGI(TAG, "Shutting down bluetooth scanner ..."); + ESP_ERROR_CHECK(esp_ble_gap_register_callback(NULL)); + ESP_ERROR_CHECK(esp_bluedroid_disable()); + ESP_ERROR_CHECK(esp_bluedroid_deinit()); + ESP_ERROR_CHECK(esp_bt_controller_disable()); + ESP_ERROR_CHECK(esp_bt_controller_deinit()); + ESP_LOGI(TAG, "Bluetooth scanner stopped"); } // stop_BLEscan #endif // BLECOUNTER \ No newline at end of file diff --git a/src/configmanager.cpp b/src/configmanager.cpp index 867a071f..39cd707e 100644 --- a/src/configmanager.cpp +++ b/src/configmanager.cpp @@ -13,130 +13,152 @@ esp_err_t err; // defined in antenna.cpp #ifdef HAS_ANTENNA_SWITCH - void antenna_select(const uint8_t _ant); +void antenna_select(const uint8_t _ant); #endif // populate cfg vars with factory settings void defaultConfig() { - cfg.lorasf = LORASFDEFAULT; // 7-12, initial lora spreadfactor defined in paxcounter.conf - cfg.txpower = 15; // 2-15, lora tx power - cfg.adrmode = 1; // 0=disabled, 1=enabled - cfg.screensaver = 0; // 0=disabled, 1=enabled - cfg.screenon = 1; // 0=disabled, 1=enabled - cfg.countermode = 0; // 0=cyclic, 1=cumulative, 2=cyclic confirmed - cfg.rssilimit = 0; // threshold for rssilimiter, negative value! - cfg.sendcycle = SEND_SECS; // payload send cycle [seconds/2] - cfg.wifichancycle = WIFI_CHANNEL_SWITCH_INTERVAL; // wifi channel switch cycle [seconds/100] - cfg.blescantime = BLESCANINTERVAL / 10; // BT channel scan cycle duration [seconds/100], default 1 (= 10ms) - cfg.blescan = 1; // 0=disabled, 1=enabled - cfg.wifiant = 0; // 0=internal, 1=external (for LoPy/LoPy4) - cfg.vendorfilter = 1; // 0=disabled, 1=enabled - cfg.rgblum = RGBLUMINOSITY; // RGB Led luminosity (0..100%) - cfg.gpsmode = 1; // 0=disabled, 1=enabled + cfg.lorasf = LORASFDEFAULT; // 7-12, initial lora spreadfactor defined in + // paxcounter.conf + cfg.txpower = 15; // 2-15, lora tx power + cfg.adrmode = 1; // 0=disabled, 1=enabled + cfg.screensaver = 0; // 0=disabled, 1=enabled + cfg.screenon = 1; // 0=disabled, 1=enabled + cfg.countermode = 0; // 0=cyclic, 1=cumulative, 2=cyclic confirmed + cfg.rssilimit = 0; // threshold for rssilimiter, negative value! + cfg.sendcycle = SEND_SECS; // payload send cycle [seconds/2] + cfg.wifichancycle = + WIFI_CHANNEL_SWITCH_INTERVAL; // wifi channel switch cycle [seconds/100] + cfg.blescantime = + BLESCANINTERVAL / + 10; // BT channel scan cycle duration [seconds/100], default 1 (= 10ms) + cfg.blescan = 1; // 0=disabled, 1=enabled + cfg.wifiant = 0; // 0=internal, 1=external (for LoPy/LoPy4) + cfg.vendorfilter = 1; // 0=disabled, 1=enabled + cfg.rgblum = RGBLUMINOSITY; // RGB Led luminosity (0..100%) + cfg.gpsmode = 1; // 0=disabled, 1=enabled - strncpy( cfg.version, PROGVERSION, sizeof(cfg.version)-1 ); + strncpy(cfg.version, PROGVERSION, sizeof(cfg.version) - 1); } void open_storage() { + err = nvs_flash_init(); + if (err == ESP_ERR_NVS_NO_FREE_PAGES) { + // NVS partition was truncated and needs to be erased + // Retry nvs_flash_init + ESP_ERROR_CHECK(nvs_flash_erase()); err = nvs_flash_init(); - if (err == ESP_ERR_NVS_NO_FREE_PAGES) { - // NVS partition was truncated and needs to be erased - // Retry nvs_flash_init - ESP_ERROR_CHECK(nvs_flash_erase()); - err = nvs_flash_init(); - } - ESP_ERROR_CHECK( err ); + } + ESP_ERROR_CHECK(err); - // Open - ESP_LOGI(TAG, "Opening NVS"); - err = nvs_open("config", NVS_READWRITE, &my_handle); - if (err != ESP_OK) - ESP_LOGI(TAG, "Error (%d) opening NVS handle", err); - else - ESP_LOGI(TAG, "Done"); + // Open + ESP_LOGI(TAG, "Opening NVS"); + err = nvs_open("config", NVS_READWRITE, &my_handle); + if (err != ESP_OK) + ESP_LOGI(TAG, "Error (%d) opening NVS handle", err); + else + ESP_LOGI(TAG, "Done"); } // erase all keys and values in NVRAM void eraseConfig() { - ESP_LOGI(TAG, "Clearing settings in NVS"); - open_storage(); - if (err == ESP_OK) { - nvs_erase_all(my_handle); - nvs_commit(my_handle); - nvs_close(my_handle); - ESP_LOGI(TAG, "Done");} - else { - ESP_LOGW(TAG, "NVS erase failed"); } + ESP_LOGI(TAG, "Clearing settings in NVS"); + open_storage(); + if (err == ESP_OK) { + nvs_erase_all(my_handle); + nvs_commit(my_handle); + nvs_close(my_handle); + ESP_LOGI(TAG, "Done"); + } else { + ESP_LOGW(TAG, "NVS erase failed"); + } } // save current configuration from RAM to NVRAM void saveConfig() { - ESP_LOGI(TAG, "Storing settings in NVS"); - open_storage(); - if (err == ESP_OK) { - int8_t flash8 = 0; - int16_t flash16 = 0; - size_t required_size; - char storedversion[10]; + ESP_LOGI(TAG, "Storing settings in NVS"); + open_storage(); + if (err == ESP_OK) { + int8_t flash8 = 0; + int16_t flash16 = 0; + size_t required_size; + char storedversion[10]; - if( nvs_get_str(my_handle, "version", storedversion, &required_size) != ESP_OK || strcmp(storedversion, cfg.version) != 0 ) - nvs_set_str(my_handle, "version", cfg.version); + if (nvs_get_str(my_handle, "version", storedversion, &required_size) != + ESP_OK || + strcmp(storedversion, cfg.version) != 0) + nvs_set_str(my_handle, "version", cfg.version); - if( nvs_get_i8(my_handle, "lorasf", &flash8) != ESP_OK || flash8 != cfg.lorasf ) - nvs_set_i8(my_handle, "lorasf", cfg.lorasf); + if (nvs_get_i8(my_handle, "lorasf", &flash8) != ESP_OK || + flash8 != cfg.lorasf) + nvs_set_i8(my_handle, "lorasf", cfg.lorasf); - if( nvs_get_i8(my_handle, "txpower", &flash8) != ESP_OK || flash8 != cfg.txpower ) - nvs_set_i8(my_handle, "txpower", cfg.txpower); + if (nvs_get_i8(my_handle, "txpower", &flash8) != ESP_OK || + flash8 != cfg.txpower) + nvs_set_i8(my_handle, "txpower", cfg.txpower); - if( nvs_get_i8(my_handle, "adrmode", &flash8) != ESP_OK || flash8 != cfg.adrmode ) - nvs_set_i8(my_handle, "adrmode", cfg.adrmode); + if (nvs_get_i8(my_handle, "adrmode", &flash8) != ESP_OK || + flash8 != cfg.adrmode) + nvs_set_i8(my_handle, "adrmode", cfg.adrmode); - if( nvs_get_i8(my_handle, "screensaver", &flash8) != ESP_OK || flash8 != cfg.screensaver ) - nvs_set_i8(my_handle, "screensaver", cfg.screensaver); + if (nvs_get_i8(my_handle, "screensaver", &flash8) != ESP_OK || + flash8 != cfg.screensaver) + nvs_set_i8(my_handle, "screensaver", cfg.screensaver); - if( nvs_get_i8(my_handle, "screenon", &flash8) != ESP_OK || flash8 != cfg.screenon ) - nvs_set_i8(my_handle, "screenon", cfg.screenon); + if (nvs_get_i8(my_handle, "screenon", &flash8) != ESP_OK || + flash8 != cfg.screenon) + nvs_set_i8(my_handle, "screenon", cfg.screenon); - if( nvs_get_i8(my_handle, "countermode", &flash8) != ESP_OK || flash8 != cfg.countermode ) - nvs_set_i8(my_handle, "countermode", cfg.countermode); + if (nvs_get_i8(my_handle, "countermode", &flash8) != ESP_OK || + flash8 != cfg.countermode) + nvs_set_i8(my_handle, "countermode", cfg.countermode); - if( nvs_get_i8(my_handle, "sendcycle", &flash8) != ESP_OK || flash8 != cfg.sendcycle ) - nvs_set_i8(my_handle, "sendcycle", cfg.sendcycle); + if (nvs_get_i8(my_handle, "sendcycle", &flash8) != ESP_OK || + flash8 != cfg.sendcycle) + nvs_set_i8(my_handle, "sendcycle", cfg.sendcycle); - if( nvs_get_i8(my_handle, "wifichancycle", &flash8) != ESP_OK || flash8 != cfg.wifichancycle ) - nvs_set_i8(my_handle, "wifichancycle", cfg.wifichancycle); + if (nvs_get_i8(my_handle, "wifichancycle", &flash8) != ESP_OK || + flash8 != cfg.wifichancycle) + nvs_set_i8(my_handle, "wifichancycle", cfg.wifichancycle); - if( nvs_get_i8(my_handle, "blescantime", &flash8) != ESP_OK || flash8 != cfg.blescantime ) - nvs_set_i8(my_handle, "blescantime", cfg.blescantime); + if (nvs_get_i8(my_handle, "blescantime", &flash8) != ESP_OK || + flash8 != cfg.blescantime) + nvs_set_i8(my_handle, "blescantime", cfg.blescantime); - if( nvs_get_i8(my_handle, "blescanmode", &flash8) != ESP_OK || flash8 != cfg.blescan ) - nvs_set_i8(my_handle, "blescanmode", cfg.blescan); + if (nvs_get_i8(my_handle, "blescanmode", &flash8) != ESP_OK || + flash8 != cfg.blescan) + nvs_set_i8(my_handle, "blescanmode", cfg.blescan); - if( nvs_get_i8(my_handle, "wifiant", &flash8) != ESP_OK || flash8 != cfg.wifiant ) - nvs_set_i8(my_handle, "wifiant", cfg.wifiant); + if (nvs_get_i8(my_handle, "wifiant", &flash8) != ESP_OK || + flash8 != cfg.wifiant) + nvs_set_i8(my_handle, "wifiant", cfg.wifiant); - if( nvs_get_i8(my_handle, "vendorfilter", &flash8) != ESP_OK || flash8 != cfg.vendorfilter ) - nvs_set_i8(my_handle, "vendorfilter", cfg.vendorfilter); + if (nvs_get_i8(my_handle, "vendorfilter", &flash8) != ESP_OK || + flash8 != cfg.vendorfilter) + nvs_set_i8(my_handle, "vendorfilter", cfg.vendorfilter); - if( nvs_get_i8(my_handle, "rgblum", &flash8) != ESP_OK || flash8 != cfg.rgblum ) - nvs_set_i8(my_handle, "rgblum", cfg.rgblum); + if (nvs_get_i8(my_handle, "rgblum", &flash8) != ESP_OK || + flash8 != cfg.rgblum) + nvs_set_i8(my_handle, "rgblum", cfg.rgblum); - if( nvs_get_i8(my_handle, "gpsmode", &flash8) != ESP_OK || flash8 != cfg.gpsmode ) + if (nvs_get_i8(my_handle, "gpsmode", &flash8) != ESP_OK || + flash8 != cfg.gpsmode) nvs_set_i8(my_handle, "gpsmode", cfg.gpsmode); - if( nvs_get_i16(my_handle, "rssilimit", &flash16) != ESP_OK || flash16 != cfg.rssilimit ) - nvs_set_i16(my_handle, "rssilimit", cfg.rssilimit); + if (nvs_get_i16(my_handle, "rssilimit", &flash16) != ESP_OK || + flash16 != cfg.rssilimit) + nvs_set_i16(my_handle, "rssilimit", cfg.rssilimit); - err = nvs_commit(my_handle); - nvs_close(my_handle); - if ( err == ESP_OK ) { - ESP_LOGI(TAG, "Done"); - } else { - ESP_LOGW(TAG, "NVS config write failed"); - } + err = nvs_commit(my_handle); + nvs_close(my_handle); + if (err == ESP_OK) { + ESP_LOGI(TAG, "Done"); } else { - ESP_LOGW(TAG, "Error (%d) opening NVS handle", err); + ESP_LOGW(TAG, "NVS config write failed"); } + } else { + ESP_LOGW(TAG, "Error (%d) opening NVS handle", err); + } } // set and save cfg.version @@ -152,31 +174,33 @@ void loadConfig() { ESP_LOGI(TAG, "Reading settings from NVS"); open_storage(); if (err != ESP_OK) { - ESP_LOGW(TAG,"Error (%d) opening NVS handle, storing defaults", err); - saveConfig(); } // saves factory settings to NVRAM + ESP_LOGW(TAG, "Error (%d) opening NVS handle, storing defaults", err); + saveConfig(); + } // saves factory settings to NVRAM else { - int8_t flash8 = 0; + int8_t flash8 = 0; int16_t flash16 = 0; size_t required_size; // check if configuration stored in NVRAM matches PROGVERSION - if( nvs_get_str(my_handle, "version", NULL, &required_size) == ESP_OK ) { + if (nvs_get_str(my_handle, "version", NULL, &required_size) == ESP_OK) { nvs_get_str(my_handle, "version", cfg.version, &required_size); ESP_LOGI(TAG, "NVRAM settings version = %s", cfg.version); if (strcmp(cfg.version, PROGVERSION)) { - ESP_LOGI(TAG, "migrating NVRAM settings to new version %s", PROGVERSION); + ESP_LOGI(TAG, "migrating NVRAM settings to new version %s", + PROGVERSION); nvs_close(my_handle); migrateVersion(); } } else { - ESP_LOGI(TAG, "new version %s, deleting NVRAM settings", PROGVERSION); - nvs_close(my_handle); - eraseConfig(); - migrateVersion(); + ESP_LOGI(TAG, "new version %s, deleting NVRAM settings", PROGVERSION); + nvs_close(my_handle); + eraseConfig(); + migrateVersion(); } // overwrite defaults with valid values from NVRAM - if( nvs_get_i8(my_handle, "lorasf", &flash8) == ESP_OK ) { + if (nvs_get_i8(my_handle, "lorasf", &flash8) == ESP_OK) { cfg.lorasf = flash8; ESP_LOGI(TAG, "lorasf = %d", flash8); } else { @@ -184,7 +208,7 @@ void loadConfig() { saveConfig(); } - if( nvs_get_i8(my_handle, "txpower", &flash8) == ESP_OK ) { + if (nvs_get_i8(my_handle, "txpower", &flash8) == ESP_OK) { cfg.txpower = flash8; ESP_LOGI(TAG, "txpower = %d", flash8); } else { @@ -192,7 +216,7 @@ void loadConfig() { saveConfig(); } - if( nvs_get_i8(my_handle, "adrmode", &flash8) == ESP_OK ) { + if (nvs_get_i8(my_handle, "adrmode", &flash8) == ESP_OK) { cfg.adrmode = flash8; ESP_LOGI(TAG, "adrmode = %d", flash8); } else { @@ -200,7 +224,7 @@ void loadConfig() { saveConfig(); } - if( nvs_get_i8(my_handle, "screensaver", &flash8) == ESP_OK ) { + if (nvs_get_i8(my_handle, "screensaver", &flash8) == ESP_OK) { cfg.screensaver = flash8; ESP_LOGI(TAG, "screensaver = %d", flash8); } else { @@ -208,7 +232,7 @@ void loadConfig() { saveConfig(); } - if( nvs_get_i8(my_handle, "screenon", &flash8) == ESP_OK ) { + if (nvs_get_i8(my_handle, "screenon", &flash8) == ESP_OK) { cfg.screenon = flash8; ESP_LOGI(TAG, "screenon = %d", flash8); } else { @@ -216,7 +240,7 @@ void loadConfig() { saveConfig(); } - if( nvs_get_i8(my_handle, "countermode", &flash8) == ESP_OK ) { + if (nvs_get_i8(my_handle, "countermode", &flash8) == ESP_OK) { cfg.countermode = flash8; ESP_LOGI(TAG, "countermode = %d", flash8); } else { @@ -224,7 +248,7 @@ void loadConfig() { saveConfig(); } - if( nvs_get_i8(my_handle, "sendcycle", &flash8) == ESP_OK ) { + if (nvs_get_i8(my_handle, "sendcycle", &flash8) == ESP_OK) { cfg.sendcycle = flash8; ESP_LOGI(TAG, "sendcycle = %d", flash8); } else { @@ -232,7 +256,7 @@ void loadConfig() { saveConfig(); } - if( nvs_get_i8(my_handle, "wifichancycle", &flash8) == ESP_OK ) { + if (nvs_get_i8(my_handle, "wifichancycle", &flash8) == ESP_OK) { cfg.wifichancycle = flash8; ESP_LOGI(TAG, "wifichancycle = %d", flash8); } else { @@ -240,7 +264,7 @@ void loadConfig() { saveConfig(); } - if( nvs_get_i8(my_handle, "wifiant", &flash8) == ESP_OK ) { + if (nvs_get_i8(my_handle, "wifiant", &flash8) == ESP_OK) { cfg.wifiant = flash8; ESP_LOGI(TAG, "wifiantenna = %d", flash8); } else { @@ -248,7 +272,7 @@ void loadConfig() { saveConfig(); } - if( nvs_get_i8(my_handle, "vendorfilter", &flash8) == ESP_OK ) { + if (nvs_get_i8(my_handle, "vendorfilter", &flash8) == ESP_OK) { cfg.vendorfilter = flash8; ESP_LOGI(TAG, "vendorfilter = %d", flash8); } else { @@ -256,7 +280,7 @@ void loadConfig() { saveConfig(); } - if( nvs_get_i8(my_handle, "rgblum", &flash8) == ESP_OK ) { + if (nvs_get_i8(my_handle, "rgblum", &flash8) == ESP_OK) { cfg.rgblum = flash8; ESP_LOGI(TAG, "rgbluminosity = %d", flash8); } else { @@ -264,7 +288,7 @@ void loadConfig() { saveConfig(); } - if( nvs_get_i8(my_handle, "blescantime", &flash8) == ESP_OK ) { + if (nvs_get_i8(my_handle, "blescantime", &flash8) == ESP_OK) { cfg.blescantime = flash8; ESP_LOGI(TAG, "blescantime = %d", flash8); } else { @@ -272,7 +296,7 @@ void loadConfig() { saveConfig(); } - if( nvs_get_i8(my_handle, "blescanmode", &flash8) == ESP_OK ) { + if (nvs_get_i8(my_handle, "blescanmode", &flash8) == ESP_OK) { cfg.blescan = flash8; ESP_LOGI(TAG, "BLEscanmode = %d", flash8); } else { @@ -280,7 +304,7 @@ void loadConfig() { saveConfig(); } - if( nvs_get_i16(my_handle, "rssilimit", &flash16) == ESP_OK ) { + if (nvs_get_i16(my_handle, "rssilimit", &flash16) == ESP_OK) { cfg.rssilimit = flash16; ESP_LOGI(TAG, "rssilimit = %d", flash16); } else { @@ -288,7 +312,7 @@ void loadConfig() { saveConfig(); } - if( nvs_get_i8(my_handle, "gpsmode", &flash8) == ESP_OK ) { + if (nvs_get_i8(my_handle, "gpsmode", &flash8) == ESP_OK) { cfg.gpsmode = flash8; ESP_LOGI(TAG, "GPSmode = %d", flash8); } else { @@ -300,9 +324,9 @@ void loadConfig() { ESP_LOGI(TAG, "Done"); // put actions to be triggered after config loaded here - - #ifdef HAS_ANTENNA_SWITCH // set antenna type, if device has one - antenna_select(cfg.wifiant); - #endif - } + +#ifdef HAS_ANTENNA_SWITCH // set antenna type, if device has one + antenna_select(cfg.wifiant); +#endif + } } diff --git a/src/globals.h b/src/globals.h index 04ef15d7..72b1c735 100644 --- a/src/globals.h +++ b/src/globals.h @@ -2,63 +2,63 @@ #include // std::set for unified array functions -#include -#include #include +#include +#include // OLED Display #ifdef HAS_DISPLAY - #include +#include #endif -//GPS +// GPS #ifdef HAS_GPS - #include +#include #endif // LMIC-Arduino LoRaWAN Stack -#include #include +#include // LED controls #ifdef HAS_RGB_LED - #include +#include #endif -#include "rgb_led.h" #include "macsniff.h" #include "main.h" +#include "rgb_led.h" // Struct holding devices's runtime configuration typedef struct { - uint8_t lorasf; // 7-12, lora spreadfactor - uint8_t txpower; // 2-15, lora tx power - uint8_t adrmode; // 0=disabled, 1=enabled - uint8_t screensaver; // 0=disabled, 1=enabled - uint8_t screenon; // 0=disabled, 1=enabled - uint8_t countermode; // 0=cyclic unconfirmed, 1=cumulative, 2=cyclic confirmed - int16_t rssilimit; // threshold for rssilimiter, negative value! - uint8_t sendcycle; // payload send cycle [seconds/2] - uint8_t wifichancycle; // wifi channel switch cycle [seconds/100] - uint8_t blescantime; // BLE scan cycle duration [seconds] - uint8_t blescan; // 0=disabled, 1=enabled - uint8_t wifiant; // 0=internal, 1=external (for LoPy/LoPy4) - uint8_t vendorfilter; // 0=disabled, 1=enabled - uint8_t rgblum; // RGB Led luminosity (0..100%) - uint8_t gpsmode; // 0=disabled, 1=enabled - char version[10]; // Firmware version - } configData_t; + uint8_t lorasf; // 7-12, lora spreadfactor + uint8_t txpower; // 2-15, lora tx power + uint8_t adrmode; // 0=disabled, 1=enabled + uint8_t screensaver; // 0=disabled, 1=enabled + uint8_t screenon; // 0=disabled, 1=enabled + uint8_t countermode; // 0=cyclic unconfirmed, 1=cumulative, 2=cyclic confirmed + int16_t rssilimit; // threshold for rssilimiter, negative value! + uint8_t sendcycle; // payload send cycle [seconds/2] + uint8_t wifichancycle; // wifi channel switch cycle [seconds/100] + uint8_t blescantime; // BLE scan cycle duration [seconds] + uint8_t blescan; // 0=disabled, 1=enabled + uint8_t wifiant; // 0=internal, 1=external (for LoPy/LoPy4) + uint8_t vendorfilter; // 0=disabled, 1=enabled + uint8_t rgblum; // RGB Led luminosity (0..100%) + uint8_t gpsmode; // 0=disabled, 1=enabled + char version[10]; // Firmware version +} configData_t; #ifdef HAS_GPS - typedef struct { - uint32_t latitude; - uint32_t longitude; - uint8_t satellites; - uint16_t hdop; - uint16_t altitude; - } gpsStatus_t; - extern gpsStatus_t gps_status; // struct for storing gps data - extern TinyGPSPlus gps; // Make TinyGPS++ instance globally availabe +typedef struct { + uint32_t latitude; + uint32_t longitude; + uint8_t satellites; + uint16_t hdop; + uint16_t altitude; +} gpsStatus_t; +extern gpsStatus_t gps_status; // struct for storing gps data +extern TinyGPSPlus gps; // Make TinyGPS++ instance globally availabe #endif extern configData_t cfg; @@ -68,6 +68,7 @@ extern char display_lora[], display_lmic[]; extern int countermode, screensaver, adrmode, lorasf, txpower, rlim; extern uint16_t macs_total, macs_wifi, macs_ble; // MAC counters extern std::set macs; -extern hw_timer_t * channelSwitch; // hardware timer used for wifi channel switching -extern xref2u1_t rcmd_data; // buffer for rcommand results size -extern u1_t rcmd_data_size; // buffer for rcommand results size +extern hw_timer_t + *channelSwitch; // hardware timer used for wifi channel switching +extern xref2u1_t rcmd_data; // buffer for rcommand results size +extern u1_t rcmd_data_size; // buffer for rcommand results size diff --git a/src/gpsread.cpp b/src/gpsread.cpp index eb38eb73..ebe16999 100644 --- a/src/gpsread.cpp +++ b/src/gpsread.cpp @@ -7,74 +7,73 @@ static const char TAG[] = "main"; // read GPS data and cast to global struct void gps_read() { - gps_status.latitude = (uint32_t) (gps.location.lat() * 1000000); - gps_status.longitude = (uint32_t) (gps.location.lng() * 1000000); - gps_status.satellites = (uint8_t) gps.satellites.value(); - gps_status.hdop = (uint16_t) gps.hdop.value(); - gps_status.altitude = (uint16_t) gps.altitude.meters(); + gps_status.latitude = (uint32_t)(gps.location.lat() * 1000000); + gps_status.longitude = (uint32_t)(gps.location.lng() * 1000000); + gps_status.satellites = (uint8_t)gps.satellites.value(); + gps_status.hdop = (uint16_t)gps.hdop.value(); + gps_status.altitude = (uint16_t)gps.altitude.meters(); } // GPS serial feed FreeRTos Task -void gps_loop(void * pvParameters) { +void gps_loop(void *pvParameters) { - configASSERT( ( ( uint32_t ) pvParameters ) == 1 ); // FreeRTOS check + configASSERT(((uint32_t)pvParameters) == 1); // FreeRTOS check - // initialize and, if needed, configure, GPS - #if defined GPS_SERIAL - HardwareSerial GPS_Serial(1); - #elif defined GPS_I2C - // to be done - #endif +// initialize and, if needed, configure, GPS +#if defined GPS_SERIAL + HardwareSerial GPS_Serial(1); +#elif defined GPS_I2C + // to be done +#endif - while(1) { + while (1) { - if (cfg.gpsmode) - { - #if defined GPS_SERIAL - - // serial connect to GPS device - GPS_Serial.begin(GPS_SERIAL); - - while(cfg.gpsmode) { - // feed GPS decoder with serial NMEA data from GPS device - while (GPS_Serial.available()) { - gps.encode(GPS_Serial.read()); - } - vTaskDelay(1/portTICK_PERIOD_MS); // reset watchdog - } - // after GPS function was disabled, close connect to GPS device - GPS_Serial.end(); + if (cfg.gpsmode) { +#if defined GPS_SERIAL - #elif defined GPS_I2C - - // I2C connect to GPS device with 100 kHz - Wire.begin(GPS_I2C_PINS, 100000); - Wire.beginTransmission(GPS_I2C_ADDRESS_WRITE); - Wire.write(0x00); + // serial connect to GPS device + GPS_Serial.begin(GPS_SERIAL); - i2c_ret == Wire.beginTransmission(GPS_I2C_ADDRESS_READ); - if (i2c_ret == 0) { // check if device seen on i2c bus - while(cfg.gpsmode) { - // feed GPS decoder with serial NMEA data from GPS device - while (Wire.available()) { - Wire.requestFrom(GPS_I2C_ADDRESS_READ, 255); - gps.encode(Wire.read()); - vTaskDelay(1/portTICK_PERIOD_MS); // reset watchdog - } - } - // after GPS function was disabled, close connect to GPS device - - Wire.endTransmission(); - Wire.setClock(400000); // Set back to 400KHz to speed up OLED - } - - #endif + while (cfg.gpsmode) { + // feed GPS decoder with serial NMEA data from GPS device + while (GPS_Serial.available()) { + gps.encode(GPS_Serial.read()); } - - vTaskDelay(1/portTICK_PERIOD_MS); // reset watchdog + vTaskDelay(1 / portTICK_PERIOD_MS); // reset watchdog + } + // after GPS function was disabled, close connect to GPS device + GPS_Serial.end(); - } // end of infinite loop +#elif defined GPS_I2C + + // I2C connect to GPS device with 100 kHz + Wire.begin(GPS_I2C_PINS, 100000); + Wire.beginTransmission(GPS_I2C_ADDRESS_WRITE); + Wire.write(0x00); + + i2c_ret == Wire.beginTransmission(GPS_I2C_ADDRESS_READ); + if (i2c_ret == 0) { // check if device seen on i2c bus + while (cfg.gpsmode) { + // feed GPS decoder with serial NMEA data from GPS device + while (Wire.available()) { + Wire.requestFrom(GPS_I2C_ADDRESS_READ, 255); + gps.encode(Wire.read()); + vTaskDelay(1 / portTICK_PERIOD_MS); // reset watchdog + } + } + // after GPS function was disabled, close connect to GPS device + + Wire.endTransmission(); + Wire.setClock(400000); // Set back to 400KHz to speed up OLED + } + +#endif + } + + vTaskDelay(1 / portTICK_PERIOD_MS); // reset watchdog + + } // end of infinite loop } // gps_loop() - + #endif // HAS_GPS \ No newline at end of file diff --git a/src/hal/fipy.h b/src/hal/fipy.h index 9f88928d..25128d2b 100644 --- a/src/hal/fipy.h +++ b/src/hal/fipy.h @@ -1,19 +1,19 @@ // Hardware related definitions for Pycom FiPy Board #define CFG_sx1272_radio 1 -#define HAS_LED NOT_A_PIN // FiPy has no on board LED, so we use RGB LED -#define HAS_RGB_LED GPIO_NUM_0 // WS2812B RGB LED on GPIO0 +#define HAS_LED NOT_A_PIN // FiPy has no on board LED, so we use RGB LED +#define HAS_RGB_LED GPIO_NUM_0 // WS2812B RGB LED on GPIO0 // Hardware pin definitions for Pycom FiPy board -#define PIN_SPI_SS GPIO_NUM_18 -#define PIN_SPI_MOSI GPIO_NUM_27 -#define PIN_SPI_MISO GPIO_NUM_19 -#define PIN_SPI_SCK GPIO_NUM_5 -#define RST LMIC_UNUSED_PIN -#define DIO0 GPIO_NUM_23 // LoRa IRQ -#define DIO1 GPIO_NUM_23 // workaround -#define DIO2 LMIC_UNUSED_PIN +#define PIN_SPI_SS GPIO_NUM_18 +#define PIN_SPI_MOSI GPIO_NUM_27 +#define PIN_SPI_MISO GPIO_NUM_19 +#define PIN_SPI_SCK GPIO_NUM_5 +#define RST LMIC_UNUSED_PIN +#define DIO0 GPIO_NUM_23 // LoRa IRQ +#define DIO1 GPIO_NUM_23 // workaround +#define DIO2 LMIC_UNUSED_PIN // select WIFI antenna (internal = onboard / external = u.fl socket) -#define HAS_ANTENNA_SWITCH GPIO_NUM_21 // pin for switching wifi antenna -#define WIFI_ANTENNA 0 // 0 = internal, 1 = external +#define HAS_ANTENNA_SWITCH GPIO_NUM_21 // pin for switching wifi antenna +#define WIFI_ANTENNA 0 // 0 = internal, 1 = external diff --git a/src/hal/heltec.h b/src/hal/heltec.h index 74bdb89a..15c43c95 100644 --- a/src/hal/heltec.h +++ b/src/hal/heltec.h @@ -4,22 +4,36 @@ #define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C // OLED-Display on board //#define DISPLAY_FLIP 1 // uncomment this for rotated display -#define HAS_LED GPIO_NUM_25 // white LED on board +#define HAS_LED GPIO_NUM_25 // white LED on board #define HAS_BUTTON GPIO_NUM_0 // button "PROG" on board // re-define pin definitions of pins_arduino.h -#define PIN_SPI_SS GPIO_NUM_18 // ESP32 GPIO18 (Pin18) -- SX1276 NSS (Pin19) SPI Chip Select Input -#define PIN_SPI_MOSI GPIO_NUM_27 // ESP32 GPIO27 (Pin27) -- SX1276 MOSI (Pin18) SPI Data Input -#define PIN_SPI_MISO GPIO_NUM_19 // ESP32 GPIO19 (Pin19) -- SX1276 MISO (Pin17) SPI Data Output -#define PIN_SPI_SCK GPIO_NUM_5 // ESP32 GPIO5 (Pin5) -- SX1276 SCK (Pin16) SPI Clock Input +#define PIN_SPI_SS \ + GPIO_NUM_18 // ESP32 GPIO18 (Pin18) -- SX1276 NSS (Pin19) SPI Chip Select + // Input +#define PIN_SPI_MOSI \ + GPIO_NUM_27 // ESP32 GPIO27 (Pin27) -- SX1276 MOSI (Pin18) SPI Data Input +#define PIN_SPI_MISO \ + GPIO_NUM_19 // ESP32 GPIO19 (Pin19) -- SX1276 MISO (Pin17) SPI Data Output +#define PIN_SPI_SCK \ + GPIO_NUM_5 // ESP32 GPIO5 (Pin5) -- SX1276 SCK (Pin16) SPI Clock Input // non arduino pin definitions -#define RST GPIO_NUM_14 // ESP32 GPIO14 (Pin14) -- SX1276 NRESET (Pin7) Reset Trigger Input -#define DIO0 GPIO_NUM_26 // ESP32 GPIO26 (Pin15) -- SX1276 DIO0 (Pin8) used by LMIC for detecting LoRa RX_Done & TX_Done -#define DIO1 GPIO_NUM_33 // ESP32 GPIO33 (Pin13) -- SX1276 DIO1 (Pin9) used by LMIC for detecting LoRa RX_Timeout -#define DIO2 LMIC_UNUSED_PIN // 32 ESP32 GPIO32 (Pin12) -- SX1276 DIO2 (Pin10) not used by LMIC for LoRa (Timeout for FSK only) +#define RST \ + GPIO_NUM_14 // ESP32 GPIO14 (Pin14) -- SX1276 NRESET (Pin7) Reset Trigger + // Input +#define DIO0 \ + GPIO_NUM_26 // ESP32 GPIO26 (Pin15) -- SX1276 DIO0 (Pin8) used by LMIC for + // detecting LoRa RX_Done & TX_Done +#define DIO1 \ + GPIO_NUM_33 // ESP32 GPIO33 (Pin13) -- SX1276 DIO1 (Pin9) used by LMIC for + // detecting LoRa RX_Timeout +#define DIO2 \ + LMIC_UNUSED_PIN // 32 ESP32 GPIO32 (Pin12) -- SX1276 DIO2 (Pin10) not used by + // LMIC for LoRa (Timeout for FSK only) -// Hardware pin definitions for Heltec LoRa-32 Board with OLED SSD1306 I2C Display +// Hardware pin definitions for Heltec LoRa-32 Board with OLED SSD1306 I2C +// Display #define OLED_RST GPIO_NUM_16 // ESP32 GPIO16 (Pin16) -- SD1306 RST #define OLED_SDA GPIO_NUM_4 // ESP32 GPIO4 (Pin4) -- SD1306 D1+D2 #define OLED_SCL GPIO_NUM_15 // ESP32 GPIO15 (Pin15) -- SD1306 D0 diff --git a/src/hal/lolin32_lora.h b/src/hal/lolin32_lora.h index ca5929af..1c3e7676 100644 --- a/src/hal/lolin32_lora.h +++ b/src/hal/lolin32_lora.h @@ -6,33 +6,50 @@ #define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C // OLED-Display on board //#define DISPLAY_FLIP 1 // uncomment this for rotated display -#define HAS_LED NOT_A_PIN // Led os on same pin as Lora SS pin, to avoid problems, we don't use it -#define LED_ACTIVE_LOW 1 // Onboard LED is active when pin is LOW - // Anyway shield is on over the LoLin32 board, so we won't be able to see this LED -#define HAS_RGB_LED 13 // ESP32 GPIO13 (pin13) On Board Shield WS2812B RGB LED -#define HAS_BUTTON 15 // ESP32 GPIO15 (pin15) Button is on the LoraNode32 shield -#define BUTTON_PULLUP 1 // Button need pullup instead of default pulldown +#define HAS_LED \ + NOT_A_PIN // Led os on same pin as Lora SS pin, to avoid problems, we don't + // use it +#define LED_ACTIVE_LOW \ + 1 // Onboard LED is active when pin is LOW + // Anyway shield is on over the LoLin32 board, so we won't be able to see + // this LED +#define HAS_RGB_LED 13 // ESP32 GPIO13 (pin13) On Board Shield WS2812B RGB LED +#define HAS_BUTTON 15 // ESP32 GPIO15 (pin15) Button is on the LoraNode32 shield +#define BUTTON_PULLUP 1 // Button need pullup instead of default pulldown #define CFG_sx1276_radio 1 // RFM95 module // re-define pin definitions of pins_arduino.h -#define PIN_SPI_SS 5 // ESP32 GPIO5 (Pin5) -- SX1276 NSS (Pin19) SPI Chip Select Input -#define PIN_SPI_MOSI 23 // ESP32 GPIO23 (Pin23) -- SX1276 MOSI (Pin18) SPI Data Input -#define PIN_SPI_MISO 19 // ESP32 GPIO19 (Pin19) -- SX1276 MISO (Pin17) SPI Data Output -#define PIN_SPI_SCK 18 // ESP32 GPIO18 (Pin18 -- SX1276 SCK (Pin16) SPI Clock Input +#define PIN_SPI_SS \ + 5 // ESP32 GPIO5 (Pin5) -- SX1276 NSS (Pin19) SPI Chip Select Input +#define PIN_SPI_MOSI \ + 23 // ESP32 GPIO23 (Pin23) -- SX1276 MOSI (Pin18) SPI Data Input +#define PIN_SPI_MISO \ + 19 // ESP32 GPIO19 (Pin19) -- SX1276 MISO (Pin17) SPI Data Output +#define PIN_SPI_SCK \ + 18 // ESP32 GPIO18 (Pin18 -- SX1276 SCK (Pin16) SPI Clock Input // non arduino pin definitions -#define RST 25 // ESP32 GPIO25 (Pin25) -- SX1276 NRESET (Pin7) Reset Trigger Input -#define DIO0 27 // ESP32 GPIO27 (Pin27) -- SX1276 DIO0 (Pin8) used by LMIC for detecting LoRa RX_Done & TX_Done -#define DIO1 26 // ESP32 GPIO26 (Pin26) -- SX1276 DIO1 (Pin9) used by LMIC for detecting LoRa RX_Timeout -#define DIO2 LMIC_UNUSED_PIN // 4 ESP32 GPIO4 (Pin4) -- SX1276 DIO2 (Pin10) not used by LMIC for LoRa (Timeout for FSK only) -#define DIO5 LMIC_UNUSED_PIN // 35 ESP32 GPIO35 (Pin35) -- SX1276 DIO5 not used by LMIC for LoRa (Timeout for FSK only) +#define RST \ + 25 // ESP32 GPIO25 (Pin25) -- SX1276 NRESET (Pin7) Reset Trigger Input +#define DIO0 \ + 27 // ESP32 GPIO27 (Pin27) -- SX1276 DIO0 (Pin8) used by LMIC for detecting + // LoRa RX_Done & TX_Done +#define DIO1 \ + 26 // ESP32 GPIO26 (Pin26) -- SX1276 DIO1 (Pin9) used by LMIC for detecting + // LoRa RX_Timeout +#define DIO2 \ + LMIC_UNUSED_PIN // 4 ESP32 GPIO4 (Pin4) -- SX1276 DIO2 (Pin10) not used by + // LMIC for LoRa (Timeout for FSK only) +#define DIO5 \ + LMIC_UNUSED_PIN // 35 ESP32 GPIO35 (Pin35) -- SX1276 DIO5 not used by LMIC + // for LoRa (Timeout for FSK only) // Hardware pin definitions for LoRaNode32 Board with OLED I2C Display -#define OLED_RST U8X8_PIN_NONE // Not reset pin -#define OLED_SDA 21 // ESP32 GPIO21 (Pin21) -- OLED SDA -#define OLED_SCL 22 // ESP32 GPIO22 (Pin22) -- OLED SCL +#define OLED_RST U8X8_PIN_NONE // Not reset pin +#define OLED_SDA 21 // ESP32 GPIO21 (Pin21) -- OLED SDA +#define OLED_SCL 22 // ESP32 GPIO22 (Pin22) -- OLED SCL // I2C config for Microchip 24AA02E64 DEVEUI unique address -#define MCP_24AA02E64_I2C_ADDRESS 0x50 // I2C address for the 24AA02E64 +#define MCP_24AA02E64_I2C_ADDRESS 0x50 // I2C address for the 24AA02E64 #define MCP_24AA02E64_MAC_ADDRESS 0xF8 // Memory adress of unique deveui 64 bits diff --git a/src/hal/lolin32lite_lora.h b/src/hal/lolin32lite_lora.h index 51cd6f10..99bf740e 100644 --- a/src/hal/lolin32lite_lora.h +++ b/src/hal/lolin32lite_lora.h @@ -6,32 +6,45 @@ #define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C // OLED-Display on board //#define DISPLAY_FLIP 1 // uncomment this for rotated display -#define HAS_LED 22 // ESP32 GPIO12 (pin22) On Board LED -#define LED_ACTIVE_LOW 1 // Onboard LED is active when pin is LOW -#define HAS_RGB_LED 13 // ESP32 GPIO13 (pin13) On Board Shield WS2812B RGB LED -#define HAS_BUTTON 15 // ESP32 GPIO15 (pin15) Button is on the LoraNode32 shield -#define BUTTON_PULLUP 1 // Button need pullup instead of default pulldown +#define HAS_LED 22 // ESP32 GPIO12 (pin22) On Board LED +#define LED_ACTIVE_LOW 1 // Onboard LED is active when pin is LOW +#define HAS_RGB_LED 13 // ESP32 GPIO13 (pin13) On Board Shield WS2812B RGB LED +#define HAS_BUTTON 15 // ESP32 GPIO15 (pin15) Button is on the LoraNode32 shield +#define BUTTON_PULLUP 1 // Button need pullup instead of default pulldown #define CFG_sx1276_radio 1 // RFM95 module // re-define pin definitions of pins_arduino.h -#define PIN_SPI_SS 5 // ESP32 GPIO5 (Pin5) -- SX1276 NSS (Pin19) SPI Chip Select Input -#define PIN_SPI_MOSI 23 // ESP32 GPIO23 (Pin23) -- SX1276 MOSI (Pin18) SPI Data Input -#define PIN_SPI_MISO 19 // ESP32 GPIO19 (Pin19) -- SX1276 MISO (Pin17) SPI Data Output -#define PIN_SPI_SCK 18 // ESP32 GPIO18 (Pin18 -- SX1276 SCK (Pin16) SPI Clock Input +#define PIN_SPI_SS \ + 5 // ESP32 GPIO5 (Pin5) -- SX1276 NSS (Pin19) SPI Chip Select Input +#define PIN_SPI_MOSI \ + 23 // ESP32 GPIO23 (Pin23) -- SX1276 MOSI (Pin18) SPI Data Input +#define PIN_SPI_MISO \ + 19 // ESP32 GPIO19 (Pin19) -- SX1276 MISO (Pin17) SPI Data Output +#define PIN_SPI_SCK \ + 18 // ESP32 GPIO18 (Pin18 -- SX1276 SCK (Pin16) SPI Clock Input // non arduino pin definitions -#define RST 25 // ESP32 GPIO25 (Pin25) -- SX1276 NRESET (Pin7) Reset Trigger Input -#define DIO0 27 // ESP32 GPIO27 (Pin27) -- SX1276 DIO0 (Pin8) used by LMIC for detecting LoRa RX_Done & TX_Done -#define DIO1 26 // ESP32 GPIO26 (Pin26) -- SX1276 DIO1 (Pin9) used by LMIC for detecting LoRa RX_Timeout -#define DIO2 LMIC_UNUSED_PIN // 4 ESP32 GPIO4 (Pin4) -- SX1276 DIO2 (Pin10) not used by LMIC for LoRa (Timeout for FSK only) -#define DIO5 LMIC_UNUSED_PIN // 35 ESP32 GPIO35 (Pin35) -- SX1276 DIO5 not used by LMIC for LoRa (Timeout for FSK only) +#define RST \ + 25 // ESP32 GPIO25 (Pin25) -- SX1276 NRESET (Pin7) Reset Trigger Input +#define DIO0 \ + 27 // ESP32 GPIO27 (Pin27) -- SX1276 DIO0 (Pin8) used by LMIC for detecting + // LoRa RX_Done & TX_Done +#define DIO1 \ + 26 // ESP32 GPIO26 (Pin26) -- SX1276 DIO1 (Pin9) used by LMIC for detecting + // LoRa RX_Timeout +#define DIO2 \ + LMIC_UNUSED_PIN // 4 ESP32 GPIO4 (Pin4) -- SX1276 DIO2 (Pin10) not used by + // LMIC for LoRa (Timeout for FSK only) +#define DIO5 \ + LMIC_UNUSED_PIN // 35 ESP32 GPIO35 (Pin35) -- SX1276 DIO5 not used by LMIC + // for LoRa (Timeout for FSK only) // Hardware pin definitions for LoRaNode32 Board with OLED I2C Display -#define OLED_RST U8X8_PIN_NONE // Not reset pin -#define OLED_SDA 14 // ESP32 GPIO14 (Pin14) -- OLED SDA -#define OLED_SCL 12 // ESP32 GPIO12 (Pin12) -- OLED SCL +#define OLED_RST U8X8_PIN_NONE // Not reset pin +#define OLED_SDA 14 // ESP32 GPIO14 (Pin14) -- OLED SDA +#define OLED_SCL 12 // ESP32 GPIO12 (Pin12) -- OLED SCL // I2C config for Microchip 24AA02E64 DEVEUI unique address -#define MCP_24AA02E64_I2C_ADDRESS 0x50 // I2C address for the 24AA02E64 +#define MCP_24AA02E64_I2C_ADDRESS 0x50 // I2C address for the 24AA02E64 #define MCP_24AA02E64_MAC_ADDRESS 0xF8 // Memory adress of unique deveui 64 bits diff --git a/src/hal/lopy.h b/src/hal/lopy.h index d54004f6..0f64fa94 100644 --- a/src/hal/lopy.h +++ b/src/hal/lopy.h @@ -1,12 +1,14 @@ // Hardware related definitions for Pycom LoPy Board (not: LoPy4) #define CFG_sx1272_radio 1 -#define HAS_LED NOT_A_PIN // LoPy4 has no on board LED, so we use RGB LED on LoPy4 -#define HAS_RGB_LED GPIO_NUM_0 // WS2812B RGB LED on GPIO0 +#define HAS_LED \ + NOT_A_PIN // LoPy4 has no on board LED, so we use RGB LED on LoPy4 +#define HAS_RGB_LED GPIO_NUM_0 // WS2812B RGB LED on GPIO0 // !!EXPERIMENTAL - not tested yet!! // uncomment this only if your LoPy lives on a Pytrack expansion board with GPS -// see http://www.quectel.com/UploadImage/Downlad/Quectel_L76-L_I2C_Application_Note_V1.0.pdf +// see +// http://www.quectel.com/UploadImage/Downlad/Quectel_L76-L_I2C_Application_Note_V1.0.pdf //#define HAS_GPS 1 //#define GPS_I2C_PINS GPIO_NUM_9, GPIO_NUM_8 // SDA, SCL //#define GPS_I2C_ADDRESS_READ 0x21 @@ -14,15 +16,15 @@ //#define HAS_BUTTON GPIO_NUM_4 // Hardware pin definitions for Pycom LoPy board -#define PIN_SPI_SS GPIO_NUM_17 -#define PIN_SPI_MOSI GPIO_NUM_27 -#define PIN_SPI_MISO GPIO_NUM_19 -#define PIN_SPI_SCK GPIO_NUM_5 -#define RST GPIO_NUM_18 -#define DIO0 GPIO_NUM_23 // LoRa IRQ -#define DIO1 GPIO_NUM_23 // workaround -#define DIO2 LMIC_UNUSED_PIN +#define PIN_SPI_SS GPIO_NUM_17 +#define PIN_SPI_MOSI GPIO_NUM_27 +#define PIN_SPI_MISO GPIO_NUM_19 +#define PIN_SPI_SCK GPIO_NUM_5 +#define RST GPIO_NUM_18 +#define DIO0 GPIO_NUM_23 // LoRa IRQ +#define DIO1 GPIO_NUM_23 // workaround +#define DIO2 LMIC_UNUSED_PIN // select WIFI antenna (internal = onboard / external = u.fl socket) -#define HAS_ANTENNA_SWITCH 16 // pin for switching wifi antenna -#define WIFI_ANTENNA 0 // 0 = internal, 1 = external +#define HAS_ANTENNA_SWITCH 16 // pin for switching wifi antenna +#define WIFI_ANTENNA 0 // 0 = internal, 1 = external diff --git a/src/hal/lopy4.h b/src/hal/lopy4.h index 708a861d..8e3acc6e 100644 --- a/src/hal/lopy4.h +++ b/src/hal/lopy4.h @@ -1,12 +1,14 @@ // Hardware related definitions for Pycom LoPy Board (not: LoPy4) #define CFG_sx1276_radio 1 -#define HAS_LED NOT_A_PIN // LoPy4 has no on board LED, so we use RGB LED on LoPy4 -#define HAS_RGB_LED GPIO_NUM_0 // WS2812B RGB LED on GPIO0 +#define HAS_LED \ + NOT_A_PIN // LoPy4 has no on board LED, so we use RGB LED on LoPy4 +#define HAS_RGB_LED GPIO_NUM_0 // WS2812B RGB LED on GPIO0 // !!EXPERIMENTAL - not tested yet!!f // uncomment this only if your LoPy lives on a Pytrack expansion board with GPS -// see http://www.quectel.com/UploadImage/Downlad/Quectel_L76-L_I2C_Application_Note_V1.0.pdf +// see +// http://www.quectel.com/UploadImage/Downlad/Quectel_L76-L_I2C_Application_Note_V1.0.pdf //#define HAS_GPS 1 //#define GPS_I2C_PINS GPIO_NUM_9, GPIO_NUM_8 // SDA, SCL //#define GPS_I2C_ADDRESS_READ 0x21 @@ -14,15 +16,15 @@ //#define HAS_BUTTON GPIO_NUM_4 // Hardware pin definitions for Pycom LoPy4 board -#define PIN_SPI_SS GPIO_NUM_18 -#define PIN_SPI_MOSI GPIO_NUM_27 -#define PIN_SPI_MISO GPIO_NUM_19 -#define PIN_SPI_SCK GPIO_NUM_5 -#define RST LMIC_UNUSED_PIN -#define DIO0 GPIO_NUM_23 // LoRa IRQ -#define DIO1 GPIO_NUM_23 // workaround -#define DIO2 LMIC_UNUSED_PIN +#define PIN_SPI_SS GPIO_NUM_18 +#define PIN_SPI_MOSI GPIO_NUM_27 +#define PIN_SPI_MISO GPIO_NUM_19 +#define PIN_SPI_SCK GPIO_NUM_5 +#define RST LMIC_UNUSED_PIN +#define DIO0 GPIO_NUM_23 // LoRa IRQ +#define DIO1 GPIO_NUM_23 // workaround +#define DIO2 LMIC_UNUSED_PIN // select WIFI antenna (internal = onboard / external = u.fl socket) -#define HAS_ANTENNA_SWITCH 21 // pin for switching wifi antenna -#define WIFI_ANTENNA 0 // 0 = internal, 1 = external \ No newline at end of file +#define HAS_ANTENNA_SWITCH 21 // pin for switching wifi antenna +#define WIFI_ANTENNA 0 // 0 = internal, 1 = external \ No newline at end of file diff --git a/src/hal/ttgobeam.h b/src/hal/ttgobeam.h index aafbb3a9..f1d24830 100644 --- a/src/hal/ttgobeam.h +++ b/src/hal/ttgobeam.h @@ -3,20 +3,31 @@ #define CFG_sx1276_radio 1 // HPD13A LoRa SoC #define HAS_LED GPIO_NUM_21 // on board green LED_G1 -//#define HAS_BUTTON GPIO_NUM_39 // on board button "BOOT" (next to reset button) !! seems not to work!! -#define HAS_BATTERY_PROBE ADC1_GPIO35_CHANNEL // battery probe GPIO pin -> ADC1_CHANNEL_7 +//#define HAS_BUTTON GPIO_NUM_39 // on board button "BOOT" (next to reset +// button) !! seems not to work!! +#define HAS_BATTERY_PROBE \ + ADC1_GPIO35_CHANNEL // battery probe GPIO pin -> ADC1_CHANNEL_7 #define BATT_FACTOR 2 // voltage divider 100k/100k on board -#define HAS_GPS 1 // use on board GPS -#define GPS_SERIAL 9600, SERIAL_8N1, GPIO_NUM_12, GPIO_NUM_15 // UBlox NEO 6M or 7M with default configuration +#define HAS_GPS 1 // use on board GPS +#define GPS_SERIAL \ + 9600, SERIAL_8N1, GPIO_NUM_12, \ + GPIO_NUM_15 // UBlox NEO 6M or 7M with default configuration // re-define pin definitions of pins_arduino.h -#define PIN_SPI_SS GPIO_NUM_18 // ESP32 GPIO18 (Pin18) -- HPD13A NSS/SEL (Pin4) SPI Chip Select Input -#define PIN_SPI_MOSI GPIO_NUM_27 // ESP32 GPIO27 (Pin27) -- HPD13A MOSI/DSI (Pin6) SPI Data Input -#define PIN_SPI_MISO GPIO_NUM_19 // ESP32 GPIO19 (Pin19) -- HPD13A MISO/DSO (Pin7) SPI Data Output -#define PIN_SPI_SCK GPIO_NUM_5 // ESP32 GPIO5 (Pin5) -- HPD13A SCK (Pin5) SPI Clock Input +#define PIN_SPI_SS \ + GPIO_NUM_18 // ESP32 GPIO18 (Pin18) -- HPD13A NSS/SEL (Pin4) SPI Chip Select + // Input +#define PIN_SPI_MOSI \ + GPIO_NUM_27 // ESP32 GPIO27 (Pin27) -- HPD13A MOSI/DSI (Pin6) SPI Data Input +#define PIN_SPI_MISO \ + GPIO_NUM_19 // ESP32 GPIO19 (Pin19) -- HPD13A MISO/DSO (Pin7) SPI Data Output +#define PIN_SPI_SCK \ + GPIO_NUM_5 // ESP32 GPIO5 (Pin5) -- HPD13A SCK (Pin5) SPI Clock Input // non arduino pin definitions -#define RST LMIC_UNUSED_PIN // connected to ESP32 RST/EN -#define DIO0 GPIO_NUM_26 // ESP32 GPIO26 <-> HPD13A IO0 -#define DIO1 GPIO_NUM_33 // Lora1 <-> HPD13A IO1 // !! NEEDS EXTERNAL WIRING !! -#define DIO2 LMIC_UNUSED_PIN // Lora2 <-> HPD13A IO2 // needs external wiring, but not necessary for LoRa, only FSK +#define RST LMIC_UNUSED_PIN // connected to ESP32 RST/EN +#define DIO0 GPIO_NUM_26 // ESP32 GPIO26 <-> HPD13A IO0 +#define DIO1 GPIO_NUM_33 // Lora1 <-> HPD13A IO1 // !! NEEDS EXTERNAL WIRING !! +#define DIO2 \ + LMIC_UNUSED_PIN // Lora2 <-> HPD13A IO2 // needs external wiring, but not + // necessary for LoRa, only FSK diff --git a/src/hal/ttgov1.h b/src/hal/ttgov1.h index df5735b5..0d175957 100644 --- a/src/hal/ttgov1.h +++ b/src/hal/ttgov1.h @@ -4,21 +4,34 @@ #define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C // OLED-Display on board //#define DISPLAY_FLIP 1 // uncomment this for rotated display -#define HAS_LED GPIO_NUM_2 // white LED on board -#define LED_ACTIVE_LOW 1 // Onboard LED is active when pin is LOW +#define HAS_LED GPIO_NUM_2 // white LED on board +#define LED_ACTIVE_LOW 1 // Onboard LED is active when pin is LOW #define HAS_BUTTON GPIO_NUM_0 // button "PRG" on board // re-define pin definitions of pins_arduino.h -#define PIN_SPI_SS GPIO_NUM_18 // ESP32 GPIO18 (Pin18) -- SX1276 NSS (Pin19) SPI Chip Select Input -#define PIN_SPI_MOSI GPIO_NUM_27 // ESP32 GPIO27 (Pin27) -- SX1276 MOSI (Pin18) SPI Data Input -#define PIN_SPI_MISO GPIO_NUM_19 // ESP32 GPIO19 (Pin19) -- SX1276 MISO (Pin17) SPI Data Output -#define PIN_SPI_SCK GPIO_NUM_5 // ESP32 GPIO5 (Pin5) -- SX1276 SCK (Pin16) SPI Clock Input +#define PIN_SPI_SS \ + GPIO_NUM_18 // ESP32 GPIO18 (Pin18) -- SX1276 NSS (Pin19) SPI Chip Select + // Input +#define PIN_SPI_MOSI \ + GPIO_NUM_27 // ESP32 GPIO27 (Pin27) -- SX1276 MOSI (Pin18) SPI Data Input +#define PIN_SPI_MISO \ + GPIO_NUM_19 // ESP32 GPIO19 (Pin19) -- SX1276 MISO (Pin17) SPI Data Output +#define PIN_SPI_SCK \ + GPIO_NUM_5 // ESP32 GPIO5 (Pin5) -- SX1276 SCK (Pin16) SPI Clock Input // non arduino pin definitions -#define RST GPIO_NUM_14 // ESP32 GPIO14 (Pin14) -- SX1276 NRESET (Pin7) Reset Trigger Input -#define DIO0 GPIO_NUM_26 // ESP32 GPIO26 (Pin15) -- SX1276 DIO0 (Pin8) used by LMIC for detecting LoRa RX_Done & TX_Done -#define DIO1 GPIO_NUM_33 // ESP32 GPIO33 (Pin13) -- SX1276 DIO1 (Pin9) used by LMIC for detecting LoRa RX_Timeout -#define DIO2 LMIC_UNUSED_PIN // 32 ESP32 GPIO32 (Pin12) -- SX1276 DIO2 (Pin10) not used by LMIC for LoRa (Timeout for FSK only) +#define RST \ + GPIO_NUM_14 // ESP32 GPIO14 (Pin14) -- SX1276 NRESET (Pin7) Reset Trigger + // Input +#define DIO0 \ + GPIO_NUM_26 // ESP32 GPIO26 (Pin15) -- SX1276 DIO0 (Pin8) used by LMIC for + // detecting LoRa RX_Done & TX_Done +#define DIO1 \ + GPIO_NUM_33 // ESP32 GPIO33 (Pin13) -- SX1276 DIO1 (Pin9) used by LMIC for + // detecting LoRa RX_Timeout +#define DIO2 \ + LMIC_UNUSED_PIN // 32 ESP32 GPIO32 (Pin12) -- SX1276 DIO2 (Pin10) not used by + // LMIC for LoRa (Timeout for FSK only) // Hardware pin definitions for TTGOv1 Board with OLED SSD1306 I2C Display #define OLED_RST GPIO_NUM_16 // ESP32 GPIO16 (Pin16) -- SD1306 Reset diff --git a/src/hal/ttgov2.h b/src/hal/ttgov2.h index e4bb49b4..38c313fc 100644 --- a/src/hal/ttgov2.h +++ b/src/hal/ttgov2.h @@ -4,28 +4,37 @@ #define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C //#define DISPLAY_FLIP 1 // uncomment this for rotated display -#define HAS_LED NOT_A_PIN // on-board LED is wired to SCL (used by display) therefore totally useless +#define HAS_LED \ + NOT_A_PIN // on-board LED is wired to SCL (used by display) therefore totally + // useless // disable brownout detection (needed on TTGOv2 for battery powered operation) #define DISABLE_BROWNOUT 1 // comment out if you want to keep brownout feature // re-define pin definitions of pins_arduino.h -#define PIN_SPI_SS GPIO_NUM_18 // ESP32 GPIO18 (Pin18) -- HPD13A NSS/SEL (Pin4) SPI Chip Select Input -#define PIN_SPI_MOSI GPIO_NUM_27 // ESP32 GPIO27 (Pin27) -- HPD13A MOSI/DSI (Pin6) SPI Data Input -#define PIN_SPI_MISO GPIO_NUM_19 // ESP32 GPIO19 (Pin19) -- HPD13A MISO/DSO (Pin7) SPI Data Output -#define PIN_SPI_SCK GPIO_NUM_5 // ESP32 GPIO5 (Pin5) -- HPD13A SCK (Pin5) SPI Clock Input +#define PIN_SPI_SS \ + GPIO_NUM_18 // ESP32 GPIO18 (Pin18) -- HPD13A NSS/SEL (Pin4) SPI Chip Select + // Input +#define PIN_SPI_MOSI \ + GPIO_NUM_27 // ESP32 GPIO27 (Pin27) -- HPD13A MOSI/DSI (Pin6) SPI Data Input +#define PIN_SPI_MISO \ + GPIO_NUM_19 // ESP32 GPIO19 (Pin19) -- HPD13A MISO/DSO (Pin7) SPI Data Output +#define PIN_SPI_SCK \ + GPIO_NUM_5 // ESP32 GPIO5 (Pin5) -- HPD13A SCK (Pin5) SPI Clock Input // non arduino pin definitions -#define RST LMIC_UNUSED_PIN // connected to ESP32 RST/EN -#define DIO0 GPIO_NUM_26 // ESP32 GPIO26 wired on PCB to HPD13A -#define DIO1 GPIO_NUM_33 // HPDIO1 on pcb, needs to be wired external to GPIO33 -#define DIO2 LMIC_UNUSED_PIN // 32 HPDIO2 on pcb, needs to be wired external to GPIO32 (not necessary for LoRa, only FSK) +#define RST LMIC_UNUSED_PIN // connected to ESP32 RST/EN +#define DIO0 GPIO_NUM_26 // ESP32 GPIO26 wired on PCB to HPD13A +#define DIO1 GPIO_NUM_33 // HPDIO1 on pcb, needs to be wired external to GPIO33 +#define DIO2 \ + LMIC_UNUSED_PIN // 32 HPDIO2 on pcb, needs to be wired external to GPIO32 (not + // necessary for LoRa, only FSK) -// Hardware pin definitions for TTGO V2 Board with OLED SSD1306 0,96" I2C Display +// Hardware pin definitions for TTGO V2 Board with OLED SSD1306 0,96" I2C +// Display #define OLED_RST U8X8_PIN_NONE // connected to CPU RST/EN -#define OLED_SDA GPIO_NUM_21 // ESP32 GPIO4 (Pin4) -- SD1306 D1+D2 -#define OLED_SCL GPIO_NUM_22 // ESP32 GPIO15 (Pin15) -- SD1306 D0 - +#define OLED_SDA GPIO_NUM_21 // ESP32 GPIO4 (Pin4) -- SD1306 D1+D2 +#define OLED_SCL GPIO_NUM_22 // ESP32 GPIO15 (Pin15) -- SD1306 D0 /* ESP32 LoRa module (SPI) OLED display (I2C) @@ -48,6 +57,6 @@ {2} Must be manually wired! DIO2 is wired to a separate pin but is not wired on-board to pin/GPIO32. Explicitly wire board pin labeled DIO2 to pin 32 (see TTGO V2.0 pinout). - {3} The on-board LED is wired to SCL (used by display) therefore totally useless! + {3} The on-board LED is wired to SCL (used by display) therefore totally + useless! */ - diff --git a/src/hal/ttgov21.h b/src/hal/ttgov21.h index 1e3b38ec..59aaf41d 100644 --- a/src/hal/ttgov21.h +++ b/src/hal/ttgov21.h @@ -3,24 +3,32 @@ #define CFG_sx1276_radio 1 // HPD13A LoRa SoC #define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C -#define DISPLAY_FLIP 1 // rotated display -#define HAS_LED GPIO_NUM_23 // green on board LED_G3 (not in initial board version) -#define HAS_BATTERY_PROBE ADC1_GPIO35_CHANNEL // battery probe GPIO pin -> ADC1_CHANNEL_7 +#define DISPLAY_FLIP 1 // rotated display +#define HAS_LED \ + GPIO_NUM_23 // green on board LED_G3 (not in initial board version) +#define HAS_BATTERY_PROBE \ + ADC1_GPIO35_CHANNEL // battery probe GPIO pin -> ADC1_CHANNEL_7 #define BATT_FACTOR 2 // voltage divider 100k/100k on board // re-define pin definitions of pins_arduino.h -#define PIN_SPI_SS GPIO_NUM_18 // ESP32 GPIO18 (Pin18) -- HPD13A NSS/SEL (Pin4) SPI Chip Select Input -#define PIN_SPI_MOSI GPIO_NUM_27 // ESP32 GPIO27 (Pin27) -- HPD13A MOSI/DSI (Pin6) SPI Data Input -#define PIN_SPI_MISO GPIO_NUM_19 // ESP32 GPIO19 (Pin19) -- HPD13A MISO/DSO (Pin7) SPI Data Output -#define PIN_SPI_SCK GPIO_NUM_5 // ESP32 GPIO5 (Pin5) -- HPD13A SCK (Pin5) SPI Clock Input +#define PIN_SPI_SS \ + GPIO_NUM_18 // ESP32 GPIO18 (Pin18) -- HPD13A NSS/SEL (Pin4) SPI Chip Select + // Input +#define PIN_SPI_MOSI \ + GPIO_NUM_27 // ESP32 GPIO27 (Pin27) -- HPD13A MOSI/DSI (Pin6) SPI Data Input +#define PIN_SPI_MISO \ + GPIO_NUM_19 // ESP32 GPIO19 (Pin19) -- HPD13A MISO/DSO (Pin7) SPI Data Output +#define PIN_SPI_SCK \ + GPIO_NUM_5 // ESP32 GPIO5 (Pin5) -- HPD13A SCK (Pin5) SPI Clock Input // non arduino pin definitions -#define RST LMIC_UNUSED_PIN // connected to ESP32 RST/EN -#define DIO0 GPIO_NUM_26 // ESP32 GPIO26 <-> HPD13A IO0 -#define DIO1 GPIO_NUM_33 // ESP32 GPIO33 <-> HPDIO1 <-> HPD13A IO1 -#define DIO2 GPIO_NUM_32 // ESP32 GPIO32 <-> HPDIO2 <-> HPD13A IO2 +#define RST LMIC_UNUSED_PIN // connected to ESP32 RST/EN +#define DIO0 GPIO_NUM_26 // ESP32 GPIO26 <-> HPD13A IO0 +#define DIO1 GPIO_NUM_33 // ESP32 GPIO33 <-> HPDIO1 <-> HPD13A IO1 +#define DIO2 GPIO_NUM_32 // ESP32 GPIO32 <-> HPDIO2 <-> HPD13A IO2 -// Hardware pin definitions for TTGO V2 Board with OLED SSD1306 0,96" I2C Display +// Hardware pin definitions for TTGO V2 Board with OLED SSD1306 0,96" I2C +// Display #define OLED_RST U8X8_PIN_NONE // connected to CPU RST/EN -#define OLED_SDA GPIO_NUM_21 // ESP32 GPIO4 (Pin4) -- SD1306 D1+D2 -#define OLED_SCL GPIO_NUM_22 // ESP32 GPIO15 (Pin15) -- SD1306 D0 \ No newline at end of file +#define OLED_SDA GPIO_NUM_21 // ESP32 GPIO4 (Pin4) -- SD1306 D1+D2 +#define OLED_SCL GPIO_NUM_22 // ESP32 GPIO15 (Pin15) -- SD1306 D0 \ No newline at end of file diff --git a/src/loraconf.sample.h b/src/loraconf.sample.h index ec816959..9cb04154 100644 --- a/src/loraconf.sample.h +++ b/src/loraconf.sample.h @@ -1,28 +1,31 @@ /************************************************************ * LMIC LoRaWAN configuration - * + * * Read the values from TTN console (or whatever applies), insert them here, * and rename this file to src/loraconf.h - * + * * Note that DEVEUI, APPEUI and APPKEY should all be specified in MSB format. - * (This is different from standard LMIC-Arduino which expects DEVEUI and APPEUI in LSB format.) + * (This is different from standard LMIC-Arduino which expects DEVEUI and APPEUI + in LSB format.) - * Set your DEVEUI here, if you have one. You can leave this untouched, + * Set your DEVEUI here, if you have one. You can leave this untouched, * then the DEVEUI will be generated during runtime from device's MAC adress * and will be displayed on device's screen as well as on serial console. - * - * NOTE: Use MSB format (as displayed in TTN console, so you can cut & paste from there) + * + * NOTE: Use MSB format (as displayed in TTN console, so you can cut & paste + from there) * For TTN, APPEUI in MSB format always starts with 0x70, 0xB3, 0xD5 * * Note: If using a board with Microchip 24AA02E64 Uinique ID for deveui, * the DEVEUI will be overwriten by the one contained in the Microchip module - * + * ************************************************************/ #include -static const u1_t DEVEUI[8]={ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +static const u1_t DEVEUI[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; -static const u1_t APPEUI[8]={ 0x70, 0xB3, 0xD5, 0x00, 0x00, 0x00, 0x00, 0x00 }; +static const u1_t APPEUI[8] = {0x70, 0xB3, 0xD5, 0x00, 0x00, 0x00, 0x00, 0x00}; -static const u1_t APPKEY[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; \ No newline at end of file +static const u1_t APPKEY[16] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; \ No newline at end of file diff --git a/src/lorawan.cpp b/src/lorawan.cpp index b832b311..a9260bc9 100644 --- a/src/lorawan.cpp +++ b/src/lorawan.cpp @@ -3,11 +3,11 @@ // LMIC-Arduino LoRaWAN Stack #include "loraconf.h" -#include #include +#include #ifdef MCP_24AA02E64_I2C_ADDRESS - #include // Needed for 24AA02E64, does not hurt anything if included and not used +#include // Needed for 24AA02E64, does not hurt anything if included and not used #endif // Local logging Tag @@ -19,219 +19,257 @@ void switch_lora(uint8_t sf, uint8_t tx); // DevEUI generator using devices's MAC address void gen_lora_deveui(uint8_t *pdeveui) { - uint8_t *p = pdeveui, dmac[6]; - int i = 0; - esp_efuse_mac_get_default(dmac); - // deveui is LSB, we reverse it so TTN DEVEUI display - // will remain the same as MAC address - // MAC is 6 bytes, devEUI 8, set first 2 ones - // with an arbitrary value - *p++ = 0xFF; - *p++ = 0xFE; - // Then next 6 bytes are mac address reversed - for ( i=0; i<6 ; i++) { - *p++ = dmac[5-i]; - } + uint8_t *p = pdeveui, dmac[6]; + int i = 0; + esp_efuse_mac_get_default(dmac); + // deveui is LSB, we reverse it so TTN DEVEUI display + // will remain the same as MAC address + // MAC is 6 bytes, devEUI 8, set first 2 ones + // with an arbitrary value + *p++ = 0xFF; + *p++ = 0xFE; + // Then next 6 bytes are mac address reversed + for (i = 0; i < 6; i++) { + *p++ = dmac[5 - i]; + } } // Function to do a byte swap in a byte array -void RevBytes(unsigned char* b, size_t c) -{ +void RevBytes(unsigned char *b, size_t c) { u1_t i; - for (i = 0; i < c / 2; i++) - { unsigned char t = b[i]; + for (i = 0; i < c / 2; i++) { + unsigned char t = b[i]; b[i] = b[c - 1 - i]; - b[c - 1 - i] = t; } + b[c - 1 - i] = t; + } } void get_hard_deveui(uint8_t *pdeveui) { - // read DEVEUI from Microchip 24AA02E64 2Kb serial eeprom if present +// read DEVEUI from Microchip 24AA02E64 2Kb serial eeprom if present #ifdef MCP_24AA02E64_I2C_ADDRESS - uint8_t i2c_ret; - // Init this just in case, no more to 100KHz - Wire.begin(OLED_SDA, OLED_SCL, 100000); + uint8_t i2c_ret; + // Init this just in case, no more to 100KHz + Wire.begin(OLED_SDA, OLED_SCL, 100000); + Wire.beginTransmission(MCP_24AA02E64_I2C_ADDRESS); + Wire.write(MCP_24AA02E64_MAC_ADDRESS); + i2c_ret = Wire.endTransmission(); + // check if device seen on i2c bus + if (i2c_ret == 0) { + char deveui[32] = ""; + uint8_t data; Wire.beginTransmission(MCP_24AA02E64_I2C_ADDRESS); - Wire.write(MCP_24AA02E64_MAC_ADDRESS); - i2c_ret = Wire.endTransmission(); - // check if device seen on i2c bus - if (i2c_ret == 0) { - char deveui[32]=""; - uint8_t data; - Wire.beginTransmission(MCP_24AA02E64_I2C_ADDRESS); - Wire.write(MCP_24AA02E64_MAC_ADDRESS); - Wire.requestFrom(MCP_24AA02E64_I2C_ADDRESS, 8); - while (Wire.available()) { - data = Wire.read(); - sprintf(deveui+strlen(deveui), "%02X ", data); - *pdeveui++ = data; - } - i2c_ret = Wire.endTransmission(); - ESP_LOGI(TAG, "Serial EEPROM 24AA02E64 found, read DEVEUI %s", deveui); - } else { - ESP_LOGI(TAG, "Serial EEPROM 24AA02E64 not found ret=%d", i2c_ret); + Wire.write(MCP_24AA02E64_MAC_ADDRESS); + Wire.requestFrom(MCP_24AA02E64_I2C_ADDRESS, 8); + while (Wire.available()) { + data = Wire.read(); + sprintf(deveui + strlen(deveui), "%02X ", data); + *pdeveui++ = data; } - // Set back to 400KHz to speed up OLED - Wire.setClock(400000); -#endif // MCP 24AA02E64 + i2c_ret = Wire.endTransmission(); + ESP_LOGI(TAG, "Serial EEPROM 24AA02E64 found, read DEVEUI %s", deveui); + } else { + ESP_LOGI(TAG, "Serial EEPROM 24AA02E64 not found ret=%d", i2c_ret); + } + // Set back to 400KHz to speed up OLED + Wire.setClock(400000); +#endif // MCP 24AA02E64 } #ifdef VERBOSE // Display a key -void printKey(const char * name, const uint8_t * key, uint8_t len, bool lsb) { - const uint8_t * p ; - char keystring[len+1] = "", keybyte[3]; - for (uint8_t i=0; i> 8; - mydata[1] = macs_wifi & 0xff; - - if (cfg.blescan) { - // append sum of unique BLE MACs seen to payload - mydata[2] = (macs_ble & 0xff00) >> 8; - mydata[3] = macs_ble & 0xff; - } else { - mydata[2] = 0; - mydata[3] = 0; - } + mydata[0] = (macs_wifi & 0xff00) >> 8; + mydata[1] = macs_wifi & 0xff; - #ifdef HAS_GPS - static uint8_t gpsdata[18]; - if (cfg.gpsmode && gps.location.isValid()) { - gps_read(); - memcpy (gpsdata, mydata, 4); - memcpy (gpsdata+4, &gps_status, sizeof(gps_status)); - ESP_LOGI(TAG, "lat=%.6f / lon=%.6f | %u Sats | HDOP=%.1f | Altitude=%u m", \ - gps_status.latitude / (float) 1000000, \ - gps_status.longitude / (float) 1000000, \ - gps_status.satellites, \ - gps_status.hdop / (float) 100, \ - gps_status.altitude); - LMIC_setTxData2(COUNTERPORT, gpsdata, sizeof(gpsdata), (cfg.countermode & 0x02)); - ESP_LOGI(TAG, "%d bytes queued to send", sizeof(gpsdata)); - } - else { - #endif - LMIC_setTxData2(COUNTERPORT, mydata, sizeof(mydata), (cfg.countermode & 0x02)); - ESP_LOGI(TAG, "%d bytes queued to send", sizeof(mydata)); - sprintf(display_lmic, "PACKET QUEUED"); + if (cfg.blescan) { + // append sum of unique BLE MACs seen to payload + mydata[2] = (macs_ble & 0xff00) >> 8; + mydata[3] = macs_ble & 0xff; + } else { + mydata[2] = 0; + mydata[3] = 0; + } - #ifdef HAS_GPS - } - #endif - - // clear counter if not in cumulative counter mode - if (cfg.countermode != 1) { - reset_counters(); // clear macs container and reset all counters - reset_salt(); // get new salt for salting hashes - ESP_LOGI(TAG, "Counter cleared (countermode = %d)", cfg.countermode); - } +#ifdef HAS_GPS + static uint8_t gpsdata[18]; + if (cfg.gpsmode && gps.location.isValid()) { + gps_read(); + memcpy(gpsdata, mydata, 4); + memcpy(gpsdata + 4, &gps_status, sizeof(gps_status)); + ESP_LOGI(TAG, "lat=%.6f / lon=%.6f | %u Sats | HDOP=%.1f | Altitude=%u m", + gps_status.latitude / (float)1000000, + gps_status.longitude / (float)1000000, gps_status.satellites, + gps_status.hdop / (float)100, gps_status.altitude); + LMIC_setTxData2(COUNTERPORT, gpsdata, sizeof(gpsdata), + (cfg.countermode & 0x02)); + ESP_LOGI(TAG, "%d bytes queued to send", sizeof(gpsdata)); + } else { +#endif + LMIC_setTxData2(COUNTERPORT, mydata, sizeof(mydata), + (cfg.countermode & 0x02)); + ESP_LOGI(TAG, "%d bytes queued to send", sizeof(mydata)); + sprintf(display_lmic, "PACKET QUEUED"); - // Schedule next transmission - end: - os_setTimedCallback(&sendjob, os_getTime()+sec2osticks(cfg.sendcycle * 2), do_send); +#ifdef HAS_GPS + } +#endif + + // clear counter if not in cumulative counter mode + if (cfg.countermode != 1) { + reset_counters(); // clear macs container and reset all counters + reset_salt(); // get new salt for salting hashes + ESP_LOGI(TAG, "Counter cleared (countermode = %d)", cfg.countermode); + } + +// Schedule next transmission +end: + os_setTimedCallback(&sendjob, os_getTime() + sec2osticks(cfg.sendcycle * 2), + do_send); } // do_send() -void onEvent (ev_t ev) { - char buff[24]=""; +void onEvent(ev_t ev) { + char buff[24] = ""; - switch(ev) { - case EV_SCAN_TIMEOUT: strcpy_P(buff, PSTR("SCAN TIMEOUT")); break; - case EV_BEACON_FOUND: strcpy_P(buff, PSTR("BEACON FOUND")); break; - case EV_BEACON_MISSED: strcpy_P(buff, PSTR("BEACON MISSED")); break; - case EV_BEACON_TRACKED: strcpy_P(buff, PSTR("BEACON TRACKED")); break; - case EV_JOINING: strcpy_P(buff, PSTR("JOINING")); break; - case EV_LOST_TSYNC: strcpy_P(buff, PSTR("LOST TSYNC")); break; - case EV_RESET: strcpy_P(buff, PSTR("RESET")); break; - case EV_RXCOMPLETE: strcpy_P(buff, PSTR("RX COMPLETE")); break; - case EV_LINK_DEAD: strcpy_P(buff, PSTR("LINK DEAD")); break; - case EV_LINK_ALIVE: strcpy_P(buff, PSTR("LINK ALIVE")); break; - case EV_RFU1: strcpy_P(buff, PSTR("RFUI")); break; - case EV_JOIN_FAILED: strcpy_P(buff, PSTR("JOIN FAILED")); break; - case EV_REJOIN_FAILED: strcpy_P(buff, PSTR("REJOIN FAILED")); break; - - case EV_JOINED: + switch (ev) { + case EV_SCAN_TIMEOUT: + strcpy_P(buff, PSTR("SCAN TIMEOUT")); + break; + case EV_BEACON_FOUND: + strcpy_P(buff, PSTR("BEACON FOUND")); + break; + case EV_BEACON_MISSED: + strcpy_P(buff, PSTR("BEACON MISSED")); + break; + case EV_BEACON_TRACKED: + strcpy_P(buff, PSTR("BEACON TRACKED")); + break; + case EV_JOINING: + strcpy_P(buff, PSTR("JOINING")); + break; + case EV_LOST_TSYNC: + strcpy_P(buff, PSTR("LOST TSYNC")); + break; + case EV_RESET: + strcpy_P(buff, PSTR("RESET")); + break; + case EV_RXCOMPLETE: + strcpy_P(buff, PSTR("RX COMPLETE")); + break; + case EV_LINK_DEAD: + strcpy_P(buff, PSTR("LINK DEAD")); + break; + case EV_LINK_ALIVE: + strcpy_P(buff, PSTR("LINK ALIVE")); + break; + case EV_RFU1: + strcpy_P(buff, PSTR("RFUI")); + break; + case EV_JOIN_FAILED: + strcpy_P(buff, PSTR("JOIN FAILED")); + break; + case EV_REJOIN_FAILED: + strcpy_P(buff, PSTR("REJOIN FAILED")); + break; - strcpy_P(buff, PSTR("JOINED")); - sprintf(display_lora, " "); // clear previous lmic status message from display + case EV_JOINED: - // set data rate adaptation - LMIC_setAdrMode(cfg.adrmode); - // Set data rate and transmit power (note: txpower seems to be ignored by the library) - switch_lora(cfg.lorasf,cfg.txpower); - - // show effective LoRa parameters after join - ESP_LOGI(TAG, "ADR=%d, SF=%d, TXPOWER=%d", cfg.adrmode, cfg.lorasf, cfg.txpower); - break; + strcpy_P(buff, PSTR("JOINED")); + sprintf(display_lora, + " "); // clear previous lmic status message from display - case EV_TXCOMPLETE: + // set data rate adaptation + LMIC_setAdrMode(cfg.adrmode); + // Set data rate and transmit power (note: txpower seems to be ignored by + // the library) + switch_lora(cfg.lorasf, cfg.txpower); - strcpy_P(buff, (LMIC.txrxFlags & TXRX_ACK) ? PSTR("RECEIVED ACK") : PSTR("TX COMPLETE")); - sprintf(display_lora, " "); // clear previous lmic status message from display - - if (LMIC.dataLen) { - ESP_LOGI(TAG, "Received %d bytes of payload, RSSI %d SNR %d", LMIC.dataLen, LMIC.rssi, (signed char)LMIC.snr / 4); - // LMIC.snr = SNR twos compliment [dB] * 4 - // LMIC.rssi = RSSI [dBm] (-196...+63) - sprintf(display_lora, "RSSI %d SNR %d", LMIC.rssi, (signed char)LMIC.snr / 4 ); + // show effective LoRa parameters after join + ESP_LOGI(TAG, "ADR=%d, SF=%d, TXPOWER=%d", cfg.adrmode, cfg.lorasf, + cfg.txpower); + break; - // check if payload received on command port, then call remote command interpreter - if ( (LMIC.txrxFlags & TXRX_PORT) && (LMIC.frame[LMIC.dataBeg-1] == RCMDPORT ) ) { - // caution: buffering LMIC values here because rcommand() can modify LMIC.frame - unsigned char* buffer = new unsigned char[MAX_LEN_FRAME]; - memcpy(buffer, LMIC.frame, MAX_LEN_FRAME); //Copy data from cfg to char* - int i, k = LMIC.dataBeg, l = LMIC.dataBeg+LMIC.dataLen-2; - for (i=k; i<=l; i+=2) { - rcommand(buffer[i], buffer[i+1]); - } - delete[] buffer; //free memory - } - } - break; + case EV_TXCOMPLETE: - default: sprintf_P(buff, PSTR("UNKNOWN EVENT %d"), ev); break; + strcpy_P(buff, (LMIC.txrxFlags & TXRX_ACK) ? PSTR("RECEIVED ACK") + : PSTR("TX COMPLETE")); + sprintf(display_lora, + " "); // clear previous lmic status message from display + + if (LMIC.dataLen) { + ESP_LOGI(TAG, "Received %d bytes of payload, RSSI %d SNR %d", + LMIC.dataLen, LMIC.rssi, (signed char)LMIC.snr / 4); + // LMIC.snr = SNR twos compliment [dB] * 4 + // LMIC.rssi = RSSI [dBm] (-196...+63) + sprintf(display_lora, "RSSI %d SNR %d", LMIC.rssi, + (signed char)LMIC.snr / 4); + + // check if payload received on command port, then call remote command + // interpreter + if ((LMIC.txrxFlags & TXRX_PORT) && + (LMIC.frame[LMIC.dataBeg - 1] == RCMDPORT)) { + // caution: buffering LMIC values here because rcommand() can modify + // LMIC.frame + unsigned char *buffer = new unsigned char[MAX_LEN_FRAME]; + memcpy(buffer, LMIC.frame, MAX_LEN_FRAME); // Copy data from cfg to + // char* + int i, k = LMIC.dataBeg, l = LMIC.dataBeg + LMIC.dataLen - 2; + for (i = k; i <= l; i += 2) { + rcommand(buffer[i], buffer[i + 1]); + } + delete[] buffer; // free memory + } } + break; + + default: + sprintf_P(buff, PSTR("UNKNOWN EVENT %d"), ev); + break; + } + + // Log & Display if asked + if (*buff) { + ESP_LOGI(TAG, "EV_%s", buff); + sprintf(display_lmic, buff); + } - // Log & Display if asked - if (*buff) { - ESP_LOGI(TAG, "EV_%s", buff); - sprintf(display_lmic, buff); - } - } // onEvent() - diff --git a/src/lorawan.h b/src/lorawan.h index de54b6e8..eda04be7 100644 --- a/src/lorawan.h +++ b/src/lorawan.h @@ -2,9 +2,9 @@ #define LORAWAN_H void onEvent(ev_t ev); -void do_send(osjob_t* j); -void gen_lora_deveui(uint8_t * pdeveui); -void RevBytes(unsigned char* b, size_t c); +void do_send(osjob_t *j); +void gen_lora_deveui(uint8_t *pdeveui); +void RevBytes(unsigned char *b, size_t c); void get_hard_deveui(uint8_t *pdeveui); #endif \ No newline at end of file diff --git a/src/macsniff.cpp b/src/macsniff.cpp index 8403e701..0e681d94 100644 --- a/src/macsniff.cpp +++ b/src/macsniff.cpp @@ -3,114 +3,136 @@ #include "globals.h" #ifdef VENDORFILTER - #include "vendor_array.h" +#include "vendor_array.h" #endif // Local logging tag static const char TAG[] = "wifi"; -static wifi_country_t wifi_country = {.cc=WIFI_MY_COUNTRY, .schan=WIFI_CHANNEL_MIN, .nchan=WIFI_CHANNEL_MAX, .policy=WIFI_COUNTRY_POLICY_MANUAL}; +static wifi_country_t wifi_country = {.cc = WIFI_MY_COUNTRY, + .schan = WIFI_CHANNEL_MIN, + .nchan = WIFI_CHANNEL_MAX, + .policy = WIFI_COUNTRY_POLICY_MANUAL}; // globals uint16_t salt; uint16_t reset_salt(void) { - salt = random(65536); // get new 16bit random for salting hashes and set global salt var - return salt; + salt = random( + 65536); // get new 16bit random for salting hashes and set global salt var + return salt; } bool mac_add(uint8_t *paddr, int8_t rssi, bool sniff_type) { - char buff[16]; // temporary buffer for printf - bool added = false; - uint32_t addr2int, vendor2int; // temporary buffer for MAC and Vendor OUI - uint16_t hashedmac; // temporary buffer for generated hash value + char buff[16]; // temporary buffer for printf + bool added = false; + uint32_t addr2int, vendor2int; // temporary buffer for MAC and Vendor OUI + uint16_t hashedmac; // temporary buffer for generated hash value - // only last 3 MAC Address bytes are used for MAC address anonymization - // but since it's uint32 we take 4 bytes to avoid 1st value to be 0 - addr2int = ( (uint32_t)paddr[2] ) | ( (uint32_t)paddr[3] << 8 ) | ( (uint32_t)paddr[4] << 16 ) | ( (uint32_t)paddr[5] << 24 ); + // only last 3 MAC Address bytes are used for MAC address anonymization + // but since it's uint32 we take 4 bytes to avoid 1st value to be 0 + addr2int = ((uint32_t)paddr[2]) | ((uint32_t)paddr[3] << 8) | + ((uint32_t)paddr[4] << 16) | ((uint32_t)paddr[5] << 24); - #ifdef VENDORFILTER - vendor2int = ( (uint32_t)paddr[2] ) | ( (uint32_t)paddr[1] << 8 ) | ( (uint32_t)paddr[0] << 16 ); - // use OUI vendor filter list only on Wifi, not on BLE - if ( (sniff_type==MAC_SNIFF_BLE) || std::find(vendors.begin(), vendors.end(), vendor2int) != vendors.end() ) - { - #endif +#ifdef VENDORFILTER + vendor2int = ((uint32_t)paddr[2]) | ((uint32_t)paddr[1] << 8) | + ((uint32_t)paddr[0] << 16); + // use OUI vendor filter list only on Wifi, not on BLE + if ((sniff_type == MAC_SNIFF_BLE) || + std::find(vendors.begin(), vendors.end(), vendor2int) != vendors.end()) { +#endif - // salt and hash MAC, and if new unique one, store identifier in container and increment counter on display - // https://en.wikipedia.org/wiki/MAC_Address_Anonymization - - addr2int += (uint32_t)salt; // add 16-bit salt to pseudo MAC - snprintf(buff, sizeof(buff), "%08X", addr2int); // convert unsigned 32-bit salted MAC to 8 digit hex string - hashedmac = rokkit(&buff[3], 5); // hash MAC last string value, use 5 chars to fit hash in uint16_t container - auto newmac = macs.insert(hashedmac); // add hashed MAC to total container if new unique - added = newmac.second ? true:false; // true if hashed MAC is unique in container + // salt and hash MAC, and if new unique one, store identifier in container + // and increment counter on display + // https://en.wikipedia.org/wiki/MAC_Address_Anonymization + + addr2int += (uint32_t)salt; // add 16-bit salt to pseudo MAC + snprintf( + buff, sizeof(buff), "%08X", + addr2int); // convert unsigned 32-bit salted MAC to 8 digit hex string + hashedmac = rokkit(&buff[3], 5); // hash MAC last string value, use 5 chars + // to fit hash in uint16_t container + auto newmac = macs.insert( + hashedmac); // add hashed MAC to total container if new unique + added = newmac.second ? true + : false; // true if hashed MAC is unique in container // Count only if MAC was not yet seen if (added) { - // increment counter and one blink led - if (sniff_type == MAC_SNIFF_WIFI ) { - macs_wifi++; // increment Wifi MACs counter - #if (HAS_LED != NOT_A_PIN) || defined (HAS_RGB_LED) - blink_LED(COLOR_GREEN, 50); - #endif - } - #ifdef BLECOUNTER - else if (sniff_type == MAC_SNIFF_BLE ) { - macs_ble++; // increment BLE Macs counter - #if (HAS_LED != NOT_A_PIN) || defined (HAS_RGB_LED) - blink_LED(COLOR_MAGENTA, 50); - #endif - } - #endif - } + // increment counter and one blink led + if (sniff_type == MAC_SNIFF_WIFI) { + macs_wifi++; // increment Wifi MACs counter +#if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED) + blink_LED(COLOR_GREEN, 50); +#endif + } +#ifdef BLECOUNTER + else if (sniff_type == MAC_SNIFF_BLE) { + macs_ble++; // increment BLE Macs counter +#if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED) + blink_LED(COLOR_MAGENTA, 50); +#endif + } +#endif + } // Log scan result - ESP_LOGI(TAG, "%s %s RSSI %ddBi -> MAC %s -> Hash %04X -> WiFi:%d BLTH:%d -> %d Bytes left", - added ? "new " : "known", - sniff_type==MAC_SNIFF_WIFI ? "WiFi":"BLTH", - rssi, buff, hashedmac, macs_wifi, macs_ble, - ESP.getFreeHeap()); + ESP_LOGI(TAG, + "%s %s RSSI %ddBi -> MAC %s -> Hash %04X -> WiFi:%d BLTH:%d -> " + "%d Bytes left", + added ? "new " : "known", + sniff_type == MAC_SNIFF_WIFI ? "WiFi" : "BLTH", rssi, buff, + hashedmac, macs_wifi, macs_ble, ESP.getFreeHeap()); - #ifdef VENDORFILTER - } else { - // Very noisy - // ESP_LOGD(TAG, "Filtered MAC %02X:%02X:%02X:%02X:%02X:%02X", paddr[0],paddr[1],paddr[2],paddr[3],paddr[5],paddr[5]); - } - #endif +#ifdef VENDORFILTER + } else { + // Very noisy + // ESP_LOGD(TAG, "Filtered MAC %02X:%02X:%02X:%02X:%02X:%02X", + // paddr[0],paddr[1],paddr[2],paddr[3],paddr[5],paddr[5]); + } +#endif - // True if MAC WiFi/BLE was new - return added; // function returns bool if a new and unique Wifi or BLE mac was counted (true) or not (false) + // True if MAC WiFi/BLE was new + return added; // function returns bool if a new and unique Wifi or BLE mac was + // counted (true) or not (false) } void wifi_sniffer_init(void) { - wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); - cfg.nvs_enable = 0; // we don't need any wifi settings from NVRAM - wifi_promiscuous_filter_t filter = {.filter_mask = WIFI_PROMIS_FILTER_MASK_MGMT}; // we need only MGMT frames - ESP_ERROR_CHECK(esp_wifi_init(&cfg)); // configure Wifi with cfg - ESP_ERROR_CHECK(esp_wifi_set_country(&wifi_country)); // set locales for RF and channels - ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM)); // we don't need NVRAM - //ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_NULL)); - ESP_ERROR_CHECK(esp_wifi_set_promiscuous_filter(&filter)); // set MAC frame filter - ESP_ERROR_CHECK(esp_wifi_set_promiscuous_rx_cb(&wifi_sniffer_packet_handler)); - ESP_ERROR_CHECK(esp_wifi_set_promiscuous(true)); // now switch on monitor mode + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + cfg.nvs_enable = 0; // we don't need any wifi settings from NVRAM + wifi_promiscuous_filter_t filter = { + .filter_mask = WIFI_PROMIS_FILTER_MASK_MGMT}; // we need only MGMT frames + ESP_ERROR_CHECK(esp_wifi_init(&cfg)); // configure Wifi with cfg + ESP_ERROR_CHECK( + esp_wifi_set_country(&wifi_country)); // set locales for RF and channels + ESP_ERROR_CHECK( + esp_wifi_set_storage(WIFI_STORAGE_RAM)); // we don't need NVRAM + // ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_NULL)); + ESP_ERROR_CHECK( + esp_wifi_set_promiscuous_filter(&filter)); // set MAC frame filter + ESP_ERROR_CHECK(esp_wifi_set_promiscuous_rx_cb(&wifi_sniffer_packet_handler)); + ESP_ERROR_CHECK(esp_wifi_set_promiscuous(true)); // now switch on monitor mode } void wifi_sniffer_set_channel(uint8_t channel) { - esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE); + esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE); } // using IRAM_:ATTR here to speed up callback function -IRAM_ATTR void wifi_sniffer_packet_handler(void* buff, wifi_promiscuous_pkt_type_t type) { - const wifi_promiscuous_pkt_t *ppkt = (wifi_promiscuous_pkt_t *)buff; - const wifi_ieee80211_packet_t *ipkt = (wifi_ieee80211_packet_t *)ppkt->payload; - const wifi_ieee80211_mac_hdr_t *hdr = &ipkt->hdr; - - if ((cfg.rssilimit) && (ppkt->rx_ctrl.rssi < cfg.rssilimit )) { // rssi is negative value - ESP_LOGI(TAG, "WiFi RSSI %d -> ignoring (limit: %d)", ppkt->rx_ctrl.rssi, cfg.rssilimit); - } else { - uint8_t *p = (uint8_t *) hdr->addr2; - mac_add(p, ppkt->rx_ctrl.rssi, MAC_SNIFF_WIFI) ; - } -} +IRAM_ATTR void wifi_sniffer_packet_handler(void *buff, + wifi_promiscuous_pkt_type_t type) { + const wifi_promiscuous_pkt_t *ppkt = (wifi_promiscuous_pkt_t *)buff; + const wifi_ieee80211_packet_t *ipkt = + (wifi_ieee80211_packet_t *)ppkt->payload; + const wifi_ieee80211_mac_hdr_t *hdr = &ipkt->hdr; + if ((cfg.rssilimit) && + (ppkt->rx_ctrl.rssi < cfg.rssilimit)) { // rssi is negative value + ESP_LOGI(TAG, "WiFi RSSI %d -> ignoring (limit: %d)", ppkt->rx_ctrl.rssi, + cfg.rssilimit); + } else { + uint8_t *p = (uint8_t *)hdr->addr2; + mac_add(p, ppkt->rx_ctrl.rssi, MAC_SNIFF_WIFI); + } +} diff --git a/src/macsniff.h b/src/macsniff.h index 5eb07f30..5e7a0c96 100644 --- a/src/macsniff.h +++ b/src/macsniff.h @@ -5,21 +5,21 @@ #include #define MAC_SNIFF_WIFI 0 -#define MAC_SNIFF_BLE 1 +#define MAC_SNIFF_BLE 1 typedef struct { - unsigned frame_ctrl:16; - unsigned duration_id:16; - uint8_t addr1[6]; /* receiver address */ - uint8_t addr2[6]; /* sender address */ - uint8_t addr3[6]; /* filtering address */ - unsigned sequence_ctrl:16; - uint8_t addr4[6]; /* optional */ + unsigned frame_ctrl : 16; + unsigned duration_id : 16; + uint8_t addr1[6]; /* receiver address */ + uint8_t addr2[6]; /* sender address */ + uint8_t addr3[6]; /* filtering address */ + unsigned sequence_ctrl : 16; + uint8_t addr4[6]; /* optional */ } wifi_ieee80211_mac_hdr_t; typedef struct { - wifi_ieee80211_mac_hdr_t hdr; - uint8_t payload[0]; /* network data ended with 4 bytes csum (CRC32) */ + wifi_ieee80211_mac_hdr_t hdr; + uint8_t payload[0]; /* network data ended with 4 bytes csum (CRC32) */ } wifi_ieee80211_packet_t; uint16_t reset_salt(void); @@ -28,6 +28,6 @@ void wifi_sniffer_set_channel(uint8_t channel); void wifi_sniffer_packet_handler(void *buff, wifi_promiscuous_pkt_type_t type); // function defined in rokkithash.cpp -uint32_t rokkit(const char * , int ); +uint32_t rokkit(const char *, int); #endif \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index a6216a3c..a83f9ca3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,5 +1,5 @@ /* - + Copyright 2018 Oliver Brandmueller Copyright 2018 Klaus Wilting @@ -15,9 +15,9 @@ Copyright 2018 Klaus Wilting See the License for the specific language governing permissions and limitations under the License. -NOTICE: -Parts of the source files in this repository are made available under different licenses. -Refer to LICENSE.txt file in repository for more details. +NOTICE: +Parts of the source files in this repository are made available under different +licenses. Refer to LICENSE.txt file in repository for more details. */ @@ -25,619 +25,654 @@ Refer to LICENSE.txt file in repository for more details. #include "globals.h" // Does nothing and avoid any compilation error with I2C -#include +#include // LMIC-Arduino LoRaWAN Stack #include "loraconf.h" -#include #include +#include // ESP32 lib Functions -#include // needed for Wifi event handler -#include // needed for reading ESP32 chip attributes -#include // needed for ESP_LOGx on arduino framework +#include // needed for ESP_LOGx on arduino framework +#include // needed for Wifi event handler +#include // needed for reading ESP32 chip attributes // Initialize global variables -configData_t cfg; // struct holds current device configuration -osjob_t sendjob, rcmdjob; // LMIC job handler -uint64_t uptimecounter = 0; // timer global for uptime counter -uint8_t DisplayState = 0; // globals for state machine -uint16_t macs_total = 0, macs_wifi = 0, macs_ble = 0; // MAC counters globals for display -uint8_t channel = 0; // wifi channel rotation counter global for display +configData_t cfg; // struct holds current device configuration +osjob_t sendjob, rcmdjob; // LMIC job handler +uint64_t uptimecounter = 0; // timer global for uptime counter +uint8_t DisplayState = 0; // globals for state machine +uint16_t macs_total = 0, macs_wifi = 0, + macs_ble = 0; // MAC counters globals for display +uint8_t channel = 0; // wifi channel rotation counter global for display char display_lora[16], display_lmic[16]; // display buffers -led_states LEDState = LED_OFF; // LED state global for state machine -led_states previousLEDState = LED_ON; // This will force LED to be off at boot since State is OFF -unsigned long LEDBlinkStarted = 0; // When (in millis() led blink started) -uint16_t LEDBlinkDuration = 0; // How long the blink need to be -uint16_t LEDColor = COLOR_NONE; // state machine variable to set RGB LED color -hw_timer_t * displaytimer = NULL; // configure hardware timer used for cyclic display refresh -hw_timer_t * channelSwitch = NULL; // configure hardware timer used for wifi channel switching -xref2u1_t rcmd_data; // buffer for rcommand results size -u1_t rcmd_data_size; // buffer for rcommand results size +led_states LEDState = LED_OFF; // LED state global for state machine +led_states previousLEDState = + LED_ON; // This will force LED to be off at boot since State is OFF +unsigned long LEDBlinkStarted = 0; // When (in millis() led blink started) +uint16_t LEDBlinkDuration = 0; // How long the blink need to be +uint16_t LEDColor = COLOR_NONE; // state machine variable to set RGB LED color +hw_timer_t *displaytimer = + NULL; // configure hardware timer used for cyclic display refresh +hw_timer_t *channelSwitch = + NULL; // configure hardware timer used for wifi channel switching +xref2u1_t rcmd_data; // buffer for rcommand results size +u1_t rcmd_data_size; // buffer for rcommand results size #ifdef HAS_GPS - gpsStatus_t gps_status; // struct for storing gps data - TinyGPSPlus gps; // create TinyGPS++ instance +gpsStatus_t gps_status; // struct for storing gps data +TinyGPSPlus gps; // create TinyGPS++ instance #endif -portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED; // sync main loop and ISR when modifying IRQ handler shared variables +portMUX_TYPE timerMux = + portMUX_INITIALIZER_UNLOCKED; // sync main loop and ISR when modifying IRQ + // handler shared variables -std::set macs; // associative container holds total of unique MAC adress hashes (Wifi + BLE) +std::set macs; // associative container holds total of unique MAC + // adress hashes (Wifi + BLE) // this variables will be changed in the ISR, and read in main loop -static volatile int ButtonPressedIRQ = 0, DisplayTimerIRQ = 0, ChannelTimerIRQ = 0; +static volatile int ButtonPressedIRQ = 0, DisplayTimerIRQ = 0, + ChannelTimerIRQ = 0; // local Tag for logging static const char TAG[] = "main"; #ifndef VERBOSE -int redirect_log(const char * fmt, va_list args) { - //do nothing - return 0; +int redirect_log(const char *fmt, va_list args) { + // do nothing + return 0; } #endif void reset_counters() { - macs.clear(); // clear all macs container - macs_total = 0; // reset all counters - macs_wifi = 0; - macs_ble = 0; + macs.clear(); // clear all macs container + macs_total = 0; // reset all counters + macs_wifi = 0; + macs_ble = 0; } -/* begin LMIC specific parts ------------------------------------------------------------ */ - + /* begin LMIC specific parts + * ------------------------------------------------------------ */ #ifdef VERBOSE - void printKeys(void); +void printKeys(void); #endif // VERBOSE // LMIC callback functions -void os_getDevKey (u1_t *buf) { - memcpy(buf, APPKEY, 16); +void os_getDevKey(u1_t *buf) { memcpy(buf, APPKEY, 16); } + +void os_getArtEui(u1_t *buf) { + memcpy(buf, APPEUI, 8); + RevBytes(buf, 8); // TTN requires it in LSB First order, so we swap bytes } -void os_getArtEui (u1_t *buf) { - memcpy(buf, APPEUI, 8); - RevBytes(buf, 8); // TTN requires it in LSB First order, so we swap bytes -} +void os_getDevEui(u1_t *buf) { + int i = 0, k = 0; + memcpy(buf, DEVEUI, 8); // get fixed DEVEUI from loraconf.h + for (i = 0; i < 8; i++) { + k += buf[i]; + } + if (k) { + RevBytes(buf, 8); // use fixed DEVEUI and swap bytes to LSB format + } else { + gen_lora_deveui(buf); // generate DEVEUI from device's MAC + } -void os_getDevEui (u1_t* buf) { - int i=0, k=0; - memcpy(buf, DEVEUI, 8); // get fixed DEVEUI from loraconf.h - for (i=0; i<8 ; i++) { - k += buf[i]; - } - if (k) { - RevBytes(buf, 8); // use fixed DEVEUI and swap bytes to LSB format - } else { - gen_lora_deveui(buf); // generate DEVEUI from device's MAC - } - - // Get MCP 24AA02E64 hardware DEVEUI (override default settings if found) - #ifdef MCP_24AA02E64_I2C_ADDRESS - get_hard_deveui(buf); - RevBytes(buf, 8); // swap bytes to LSB format - #endif +// Get MCP 24AA02E64 hardware DEVEUI (override default settings if found) +#ifdef MCP_24AA02E64_I2C_ADDRESS + get_hard_deveui(buf); + RevBytes(buf, 8); // swap bytes to LSB format +#endif } // LMIC enhanced Pin mapping -const lmic_pinmap lmic_pins = { - .mosi = PIN_SPI_MOSI, - .miso = PIN_SPI_MISO, - .sck = PIN_SPI_SCK, - .nss = PIN_SPI_SS, - .rxtx = LMIC_UNUSED_PIN, - .rst = RST, - .dio = {DIO0, DIO1, DIO2} -}; - +const lmic_pinmap lmic_pins = {.mosi = PIN_SPI_MOSI, + .miso = PIN_SPI_MISO, + .sck = PIN_SPI_SCK, + .nss = PIN_SPI_SS, + .rxtx = LMIC_UNUSED_PIN, + .rst = RST, + .dio = {DIO0, DIO1, DIO2}}; // LMIC FreeRTos Task -void lorawan_loop(void * pvParameters) { +void lorawan_loop(void *pvParameters) { - configASSERT( ( ( uint32_t ) pvParameters ) == 1 ); // FreeRTOS check + configASSERT(((uint32_t)pvParameters) == 1); // FreeRTOS check - while(1) { - os_runloop_once(); // execute LMIC jobs - vTaskDelay(1/portTICK_PERIOD_MS); // reset watchdog - } + while (1) { + os_runloop_once(); // execute LMIC jobs + vTaskDelay(1 / portTICK_PERIOD_MS); // reset watchdog + } } -/* end LMIC specific parts --------------------------------------------------------------- */ + /* end LMIC specific parts + * --------------------------------------------------------------- */ -/* beginn hardware specific parts -------------------------------------------------------- */ + /* beginn hardware specific parts + * -------------------------------------------------------- */ #ifdef HAS_DISPLAY - HAS_DISPLAY u8x8(OLED_RST, OLED_SCL, OLED_SDA); - // Display Refresh IRQ - void IRAM_ATTR DisplayIRQ() { - portENTER_CRITICAL_ISR(&timerMux); - DisplayTimerIRQ++; - portEXIT_CRITICAL_ISR(&timerMux); - } +HAS_DISPLAY u8x8(OLED_RST, OLED_SCL, OLED_SDA); +// Display Refresh IRQ +void IRAM_ATTR DisplayIRQ() { + portENTER_CRITICAL_ISR(&timerMux); + DisplayTimerIRQ++; + portEXIT_CRITICAL_ISR(&timerMux); +} #endif #ifdef HAS_ANTENNA_SWITCH - // defined in antenna.cpp - void antenna_init(); - void antenna_select(const uint8_t _ant); +// defined in antenna.cpp +void antenna_init(); +void antenna_select(const uint8_t _ant); #endif #ifndef BLECOUNTER - bool btstop = btStop(); +bool btstop = btStop(); #endif -// Button IRQ Handler Routine, IRAM_ATTR necessary here, see https://github.com/espressif/arduino-esp32/issues/855 +// Button IRQ Handler Routine, IRAM_ATTR necessary here, see +// https://github.com/espressif/arduino-esp32/issues/855 #ifdef HAS_BUTTON - void IRAM_ATTR ButtonIRQ() { - ButtonPressedIRQ++; - } +void IRAM_ATTR ButtonIRQ() { ButtonPressedIRQ++; } #endif // Wifi Channel Rotation Timer IRQ Handler Routine void IRAM_ATTR ChannelSwitchIRQ() { - portENTER_CRITICAL(&timerMux); - ChannelTimerIRQ++; - portEXIT_CRITICAL(&timerMux); + portENTER_CRITICAL(&timerMux); + ChannelTimerIRQ++; + portEXIT_CRITICAL(&timerMux); } -/* end hardware specific parts -------------------------------------------------------- */ +/* end hardware specific parts + * -------------------------------------------------------- */ - -/* begin wifi specific parts ---------------------------------------------------------- */ +/* begin wifi specific parts + * ---------------------------------------------------------- */ // Sniffer Task -void sniffer_loop(void * pvParameters) { +void sniffer_loop(void *pvParameters) { - configASSERT( ( ( uint32_t ) pvParameters ) == 1 ); // FreeRTOS check + configASSERT(((uint32_t)pvParameters) == 1); // FreeRTOS check - while (1) { + while (1) { - if (ChannelTimerIRQ) { - portENTER_CRITICAL(&timerMux); - ChannelTimerIRQ--; - portEXIT_CRITICAL(&timerMux); - // rotates variable channel 1..WIFI_CHANNEL_MAX - channel = (channel % WIFI_CHANNEL_MAX) + 1; - wifi_sniffer_set_channel(channel); - ESP_LOGD(TAG, "Wifi set channel %d", channel); + if (ChannelTimerIRQ) { + portENTER_CRITICAL(&timerMux); + ChannelTimerIRQ--; + portEXIT_CRITICAL(&timerMux); + // rotates variable channel 1..WIFI_CHANNEL_MAX + channel = (channel % WIFI_CHANNEL_MAX) + 1; + wifi_sniffer_set_channel(channel); + ESP_LOGD(TAG, "Wifi set channel %d", channel); - vTaskDelay(1/portTICK_PERIOD_MS); // reset watchdog - } + vTaskDelay(1 / portTICK_PERIOD_MS); // reset watchdog + } - } // end of infinite wifi channel rotation loop + } // end of infinite wifi channel rotation loop } -/* end wifi specific parts ------------------------------------------------------------ */ +/* end wifi specific parts + * ------------------------------------------------------------ */ // uptime counter 64bit to prevent millis() rollover after 49 days uint64_t uptime() { - static uint32_t low32, high32; - uint32_t new_low32 = millis(); - if (new_low32 < low32) high32++; - low32 = new_low32; - return (uint64_t) high32 << 32 | low32; + static uint32_t low32, high32; + uint32_t new_low32 = millis(); + if (new_low32 < low32) + high32++; + low32 = new_low32; + return (uint64_t)high32 << 32 | low32; } #ifdef HAS_DISPLAY - // Print a key on display - void DisplayKey(const uint8_t * key, uint8_t len, bool lsb) { - const uint8_t * p ; - for (uint8_t i=0; i= LEDBlinkDuration) { + // Led becomes off, and stop blink + LEDState = LED_OFF; + LEDBlinkStarted = 0; + LEDBlinkDuration = 0; + LEDColor = COLOR_NONE; + } else { + // In case of LoRaWAN led management blinked off + LEDState = LED_ON; } - void led_loop() { - // Custom blink running always have priority other LoRaWAN led management - if ( LEDBlinkStarted && LEDBlinkDuration) { + // No custom blink, check LoRaWAN state + } else { - //ESP_LOGI(TAG, "Start=%ld for %g",LEDBlinkStarted, LEDBlinkDuration ); + // LED indicators for viusalizing LoRaWAN state + if (LMIC.opmode & (OP_JOINING | OP_REJOIN)) { + LEDColor = COLOR_YELLOW; + // quick blink 20ms on each 1/5 second + LEDState = ((millis() % 200) < 20) ? LED_ON : LED_OFF; // TX data pending + } else if (LMIC.opmode & (OP_TXDATA | OP_TXRXPEND)) { + LEDColor = COLOR_BLUE; + // small blink 10ms on each 1/2sec (not when joining) + LEDState = ((millis() % 500) < 20) ? LED_ON : LED_OFF; + // This should not happen so indicate a problem + } else if (LMIC.opmode & + ((OP_TXDATA | OP_TXRXPEND | OP_JOINING | OP_REJOIN) == 0)) { + LEDColor = COLOR_RED; + // heartbeat long blink 200ms on each 2 seconds + LEDState = ((millis() % 2000) < 200) ? LED_ON : LED_OFF; + } else { + // led off + LEDColor = COLOR_NONE; + LEDState = LED_OFF; + } + } - // Custom blink is finished, let this order, avoid millis() overflow - if ( (millis() - LEDBlinkStarted) >= LEDBlinkDuration) { - // Led becomes off, and stop blink - LEDState = LED_OFF; - LEDBlinkStarted = 0; - LEDBlinkDuration = 0; - LEDColor = COLOR_NONE ; - } else { - // In case of LoRaWAN led management blinked off - LEDState = LED_ON; - } - - // No custom blink, check LoRaWAN state - } else { + // ESP_LOGI(TAG, "state=%d previous=%d Color=%d",LEDState, previousLEDState, + // LEDColor ); + // led need to change state? avoid digitalWrite() for nothing + if (LEDState != previousLEDState) { + if (LEDState == LED_ON) { + rgb_set_color(LEDColor); +#ifdef LED_ACTIVE_LOW + digitalWrite(HAS_LED, LOW); +#else + digitalWrite(HAS_LED, HIGH); +#endif + } else { + rgb_set_color(COLOR_NONE); +#ifdef LED_ACTIVE_LOW + digitalWrite(HAS_LED, HIGH); +#else + digitalWrite(HAS_LED, LOW); +#endif + } + previousLEDState = LEDState; + } +}; // led_loop() - // LED indicators for viusalizing LoRaWAN state - if ( LMIC.opmode & (OP_JOINING | OP_REJOIN) ) { - LEDColor = COLOR_YELLOW; - // quick blink 20ms on each 1/5 second - LEDState = ((millis() % 200) < 20) ? LED_ON : LED_OFF; // TX data pending - } else if (LMIC.opmode & (OP_TXDATA | OP_TXRXPEND)) { - LEDColor = COLOR_BLUE; - // small blink 10ms on each 1/2sec (not when joining) - LEDState = ((millis() % 500) < 20) ? LED_ON : LED_OFF; - // This should not happen so indicate a problem - } else if ( LMIC.opmode & ((OP_TXDATA | OP_TXRXPEND | OP_JOINING | OP_REJOIN) == 0 ) ) { - LEDColor = COLOR_RED; - // heartbeat long blink 200ms on each 2 seconds - LEDState = ((millis() % 2000) < 200) ? LED_ON : LED_OFF; - } else { - // led off - LEDColor = COLOR_NONE; - LEDState = LED_OFF; - } - } +#endif - //ESP_LOGI(TAG, "state=%d previous=%d Color=%d",LEDState, previousLEDState, LEDColor ); - // led need to change state? avoid digitalWrite() for nothing - if (LEDState != previousLEDState) { - if (LEDState == LED_ON) { - rgb_set_color(LEDColor); - #ifdef LED_ACTIVE_LOW - digitalWrite(HAS_LED, LOW); - #else - digitalWrite(HAS_LED, HIGH); - #endif - } else { - rgb_set_color(COLOR_NONE); - #ifdef LED_ACTIVE_LOW - digitalWrite(HAS_LED, HIGH); - #else - digitalWrite(HAS_LED, LOW); - #endif - } - previousLEDState = LEDState; - } - }; // led_loop() - - #endif - -/* begin Aruino SETUP ------------------------------------------------------------ */ +/* begin Aruino SETUP + * ------------------------------------------------------------ */ void setup() { - char features[64] = ""; + char features[64] = ""; - // disable brownout detection + // disable brownout detection #ifdef DISABLE_BROWNOUT - // register with brownout is at address DR_REG_RTCCNTL_BASE + 0xd4 - (*((volatile uint32_t *)ETS_UNCACHED_ADDR((DR_REG_RTCCNTL_BASE+0xd4)))) = 0; + // register with brownout is at address DR_REG_RTCCNTL_BASE + 0xd4 + (*((volatile uint32_t *)ETS_UNCACHED_ADDR((DR_REG_RTCCNTL_BASE + 0xd4)))) = 0; #endif - // setup debug output or silence device + // setup debug output or silence device #ifdef VERBOSE - Serial.begin(115200); - esp_log_level_set("*", ESP_LOG_VERBOSE); + Serial.begin(115200); + esp_log_level_set("*", ESP_LOG_VERBOSE); #else - // mute logs completely by redirecting them to silence function - esp_log_level_set("*", ESP_LOG_NONE); - esp_log_set_vprintf(redirect_log); + // mute logs completely by redirecting them to silence function + esp_log_level_set("*", ESP_LOG_NONE); + esp_log_set_vprintf(redirect_log); #endif - - ESP_LOGI(TAG, "Starting %s %s", PROGNAME, PROGVERSION); - - // initialize system event handler for wifi task, needed for wifi_sniffer_init() - esp_event_loop_init(NULL, NULL); - // print chip information on startup if in verbose mode + ESP_LOGI(TAG, "Starting %s %s", PROGNAME, PROGVERSION); + + // initialize system event handler for wifi task, needed for + // wifi_sniffer_init() + esp_event_loop_init(NULL, NULL); + + // print chip information on startup if in verbose mode #ifdef VERBOSE - esp_chip_info_t chip_info; - esp_chip_info(&chip_info); - ESP_LOGI(TAG, "This is ESP32 chip with %d CPU cores, WiFi%s%s, silicon revision %d, %dMB %s Flash", - chip_info.cores, - (chip_info.features & CHIP_FEATURE_BT) ? "/BT" : "", - (chip_info.features & CHIP_FEATURE_BLE) ? "/BLE" : "", - chip_info.revision, spi_flash_get_chip_size() / (1024 * 1024), - (chip_info.features & CHIP_FEATURE_EMB_FLASH) ? "embedded" : "external"); - ESP_LOGI(TAG, "ESP32 SDK: %s", ESP.getSdkVersion()); + esp_chip_info_t chip_info; + esp_chip_info(&chip_info); + ESP_LOGI(TAG, + "This is ESP32 chip with %d CPU cores, WiFi%s%s, silicon revision " + "%d, %dMB %s Flash", + chip_info.cores, (chip_info.features & CHIP_FEATURE_BT) ? "/BT" : "", + (chip_info.features & CHIP_FEATURE_BLE) ? "/BLE" : "", + chip_info.revision, spi_flash_get_chip_size() / (1024 * 1024), + (chip_info.features & CHIP_FEATURE_EMB_FLASH) ? "embedded" + : "external"); + ESP_LOGI(TAG, "ESP32 SDK: %s", ESP.getSdkVersion()); #endif #ifdef HAS_GPS - ESP_LOGI(TAG, "TinyGPS+ version %s", TinyGPSPlus::libraryVersion()); + ESP_LOGI(TAG, "TinyGPS+ version %s", TinyGPSPlus::libraryVersion()); #endif - // read settings from NVRAM - loadConfig(); // includes initialize if necessary + // read settings from NVRAM + loadConfig(); // includes initialize if necessary - // initialize led if needed -#if (HAS_LED != NOT_A_PIN) - pinMode(HAS_LED, OUTPUT); - strcat(features, " LED"); + // initialize led if needed +#if (HAS_LED != NOT_A_PIN) + pinMode(HAS_LED, OUTPUT); + strcat(features, " LED"); #endif #ifdef HAS_RGB_LED - rgb_set_color(COLOR_PINK); - strcat(features, " RGB"); + rgb_set_color(COLOR_PINK); + strcat(features, " RGB"); #endif - // initialize button handling if needed + // initialize button handling if needed #ifdef HAS_BUTTON - strcat(features, " BTN_"); - #ifdef BUTTON_PULLUP - strcat(features, "PU"); - // install button interrupt (pullup mode) - pinMode(HAS_BUTTON, INPUT_PULLUP); - attachInterrupt(digitalPinToInterrupt(HAS_BUTTON), ButtonIRQ, RISING); - #else - strcat(features, "PD"); - // install button interrupt (pulldown mode) - pinMode(HAS_BUTTON, INPUT_PULLDOWN); - attachInterrupt(digitalPinToInterrupt(HAS_BUTTON), ButtonIRQ, FALLING); - #endif + strcat(features, " BTN_"); +#ifdef BUTTON_PULLUP + strcat(features, "PU"); + // install button interrupt (pullup mode) + pinMode(HAS_BUTTON, INPUT_PULLUP); + attachInterrupt(digitalPinToInterrupt(HAS_BUTTON), ButtonIRQ, RISING); +#else + strcat(features, "PD"); + // install button interrupt (pulldown mode) + pinMode(HAS_BUTTON, INPUT_PULLDOWN); + attachInterrupt(digitalPinToInterrupt(HAS_BUTTON), ButtonIRQ, FALLING); +#endif #endif - // initialize wifi antenna if needed + // initialize wifi antenna if needed #ifdef HAS_ANTENNA_SWITCH - strcat(features, " ANT"); - antenna_init(); + strcat(features, " ANT"); + antenna_init(); #endif // initialize gps if present #ifdef HAS_GPS - strcat(features, " GPS"); + strcat(features, " GPS"); #endif #ifdef HAS_DISPLAY - strcat(features, " OLED"); - // initialize display - init_display(PROGNAME, PROGVERSION); - DisplayState = cfg.screenon; - u8x8.setPowerSave(!cfg.screenon); // set display off if disabled - u8x8.draw2x2String(0, 0, "PAX:0"); - u8x8.setCursor(0,4); - u8x8.printf("WIFI:0"); - #ifdef BLECOUNTER - u8x8.setCursor(0,3); - u8x8.printf("BLTH:0"); - #endif - u8x8.setCursor(0,5); - u8x8.printf(!cfg.rssilimit ? "RLIM:off " : "RLIM:%d", cfg.rssilimit); - - sprintf(display_lora, "Join wait"); + strcat(features, " OLED"); + // initialize display + init_display(PROGNAME, PROGVERSION); + DisplayState = cfg.screenon; + u8x8.setPowerSave(!cfg.screenon); // set display off if disabled + u8x8.draw2x2String(0, 0, "PAX:0"); + u8x8.setCursor(0, 4); + u8x8.printf("WIFI:0"); +#ifdef BLECOUNTER + u8x8.setCursor(0, 3); + u8x8.printf("BLTH:0"); +#endif + u8x8.setCursor(0, 5); + u8x8.printf(!cfg.rssilimit ? "RLIM:off " : "RLIM:%d", cfg.rssilimit); - // setup display refresh trigger IRQ using esp32 hardware timer 0 - // for explanation see https://techtutorialsx.com/2017/10/07/esp32-arduino-timer-interrupts/ - displaytimer = timerBegin(0, 80, true); // prescaler 80 -> divides 80 MHz CPU freq to 1 MHz, timer 0, count up - timerAttachInterrupt(displaytimer, &DisplayIRQ, true); // interrupt handler DisplayIRQ, triggered by edge - timerAlarmWrite(displaytimer, DISPLAYREFRESH_MS * 1000, true); // reload interrupt after each trigger of display refresh cycle - timerAlarmEnable(displaytimer); // enable display interrupt + sprintf(display_lora, "Join wait"); + + // setup display refresh trigger IRQ using esp32 hardware timer 0 + // for explanation see + // https://techtutorialsx.com/2017/10/07/esp32-arduino-timer-interrupts/ + displaytimer = timerBegin(0, 80, true); // prescaler 80 -> divides 80 MHz CPU + // freq to 1 MHz, timer 0, count up + timerAttachInterrupt(displaytimer, &DisplayIRQ, + true); // interrupt handler DisplayIRQ, triggered by edge + timerAlarmWrite( + displaytimer, DISPLAYREFRESH_MS * 1000, + true); // reload interrupt after each trigger of display refresh cycle + timerAlarmEnable(displaytimer); // enable display interrupt #endif -// setup channel rotation trigger IRQ using esp32 hardware timer 1 -channelSwitch = timerBegin(1, 80, true); -timerAttachInterrupt(channelSwitch, &ChannelSwitchIRQ, true); -timerAlarmWrite(channelSwitch, cfg.wifichancycle * 10000, true); -timerAlarmEnable(channelSwitch); + // setup channel rotation trigger IRQ using esp32 hardware timer 1 + channelSwitch = timerBegin(1, 80, true); + timerAttachInterrupt(channelSwitch, &ChannelSwitchIRQ, true); + timerAlarmWrite(channelSwitch, cfg.wifichancycle * 10000, true); + timerAlarmEnable(channelSwitch); -// show compiled features -ESP_LOGI(TAG, "Features %s", features); + // show compiled features + ESP_LOGI(TAG, "Features %s", features); // output LoRaWAN keys to console #ifdef VERBOSE - printKeys(); + printKeys(); #endif -// initialize LoRaWAN LMIC run-time environment -os_init(); -// reset LMIC MAC state -LMIC_reset(); -// This tells LMIC to make the receive windows bigger, in case your clock is 1% faster or slower. -LMIC_setClockError(MAX_CLOCK_ERROR * 1 / 100); + // initialize LoRaWAN LMIC run-time environment + os_init(); + // reset LMIC MAC state + LMIC_reset(); + // This tells LMIC to make the receive windows bigger, in case your clock is + // 1% faster or slower. + LMIC_setClockError(MAX_CLOCK_ERROR * 1 / 100); -// start lmic runloop in rtos task on core 1 (note: arduino main loop runs on core 1, too) -// https://techtutorialsx.com/2017/05/09/esp32-get-task-execution-core/ + // start lmic runloop in rtos task on core 1 (note: arduino main loop runs on + // core 1, too) + // https://techtutorialsx.com/2017/05/09/esp32-get-task-execution-core/ -ESP_LOGI(TAG, "Starting Lora task on core 1"); -xTaskCreatePinnedToCore(lorawan_loop, "loratask", 2048, ( void * ) 1, ( 5 | portPRIVILEGE_BIT ), NULL, 1); + ESP_LOGI(TAG, "Starting Lora task on core 1"); + xTaskCreatePinnedToCore(lorawan_loop, "loratask", 2048, (void *)1, + (5 | portPRIVILEGE_BIT), NULL, 1); -// start wifi in monitor mode and start channel rotation task on core 0 -ESP_LOGI(TAG, "Starting Wifi task on core 0"); -wifi_sniffer_init(); -// initialize salt value using esp_random() called by random() in arduino-esp32 core -// note: do this *after* wifi has started, since function gets it's seed from RF noise -reset_salt(); // get new 16bit for salting hashes -xTaskCreatePinnedToCore(sniffer_loop, "wifisniffer", 2048, ( void * ) 1, 1, NULL, 0); + // start wifi in monitor mode and start channel rotation task on core 0 + ESP_LOGI(TAG, "Starting Wifi task on core 0"); + wifi_sniffer_init(); + // initialize salt value using esp_random() called by random() in + // arduino-esp32 core note: do this *after* wifi has started, since function + // gets it's seed from RF noise + reset_salt(); // get new 16bit for salting hashes + xTaskCreatePinnedToCore(sniffer_loop, "wifisniffer", 2048, (void *)1, 1, NULL, + 0); // start BLE scan callback if BLE function is enabled in NVRAM configuration #ifdef BLECOUNTER - if (cfg.blescan) { - start_BLEscan(); - } + if (cfg.blescan) { + start_BLEscan(); + } #endif -// if device has GPS and GPS function is enabled, start GPS reader task on core 0 +// if device has GPS and GPS function is enabled, start GPS reader task on core +// 0 #ifdef HAS_GPS - if (cfg.gpsmode) { - ESP_LOGI(TAG, "Starting GPS task on core 0"); - xTaskCreatePinnedToCore(gps_loop, "gpsfeed", 2048, ( void * ) 1, 1, NULL, 0); - } + if (cfg.gpsmode) { + ESP_LOGI(TAG, "Starting GPS task on core 0"); + xTaskCreatePinnedToCore(gps_loop, "gpsfeed", 2048, (void *)1, 1, NULL, 0); + } #endif -// kickoff sendjob -> joins network and rescedules sendjob for cyclic transmitting payload -do_send(&sendjob); - + // kickoff sendjob -> joins network and rescedules sendjob for cyclic + // transmitting payload + do_send(&sendjob); } -/* end Arduino SETUP ------------------------------------------------------------ */ +/* end Arduino SETUP + * ------------------------------------------------------------ */ -/* begin Arduino main loop ------------------------------------------------------ */ +/* begin Arduino main loop + * ------------------------------------------------------ */ void loop() { - while (1) { + while (1) { - // simple state machine for controlling uptime, display, LED, button, memory. + // simple state machine for controlling uptime, display, LED, button, + // memory. - uptimecounter = uptime() / 1000; // counts uptime in seconds (64bit) + uptimecounter = uptime() / 1000; // counts uptime in seconds (64bit) - #if (HAS_LED != NOT_A_PIN) || defined (HAS_RGB_LED) - led_loop(); - #endif - - #ifdef HAS_BUTTON - readButton(); - #endif +#if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED) + led_loop(); +#endif - #ifdef HAS_DISPLAY - updateDisplay(); - #endif +#ifdef HAS_BUTTON + readButton(); +#endif - // check free memory - if (esp_get_minimum_free_heap_size() <= MEM_LOW) { - ESP_LOGI(TAG, "Memory full, counter cleared (heap low water mark = %d Bytes / free heap = %d bytes)", \ - esp_get_minimum_free_heap_size(), ESP.getFreeHeap()); - do_send(&sendjob); // send count - reset_counters(); // clear macs container and reset all counters - reset_salt(); // get new salt for salting hashes - } +#ifdef HAS_DISPLAY + updateDisplay(); +#endif - #ifdef HAS_GPS - // log NMEA status every 30 seconds, useful for debugging GPS connection - if ( (uptime() % 30000) == 0 ) - ESP_LOGD(TAG, "GPS NMEA data: passed %d / failed: %d / with fix: %d", gps.passedChecksum(), gps.failedChecksum(), gps.sentencesWithFix()); - #endif + // check free memory + if (esp_get_minimum_free_heap_size() <= MEM_LOW) { + ESP_LOGI(TAG, + "Memory full, counter cleared (heap low water mark = %d Bytes / " + "free heap = %d bytes)", + esp_get_minimum_free_heap_size(), ESP.getFreeHeap()); + do_send(&sendjob); // send count + reset_counters(); // clear macs container and reset all counters + reset_salt(); // get new salt for salting hashes + } - vTaskDelay(1/portTICK_PERIOD_MS); // reset watchdog +#ifdef HAS_GPS + // log NMEA status every 30 seconds, useful for debugging GPS connection + if ((uptime() % 30000) == 0) + ESP_LOGD(TAG, "GPS NMEA data: passed %d / failed: %d / with fix: %d", + gps.passedChecksum(), gps.failedChecksum(), + gps.sentencesWithFix()); +#endif - } // end of infinite main loop + vTaskDelay(1 / portTICK_PERIOD_MS); // reset watchdog + + } // end of infinite main loop } -/* end Arduino main loop ------------------------------------------------------------ */ +/* end Arduino main loop + * ------------------------------------------------------------ */ diff --git a/src/main.h b/src/main.h index a2e0eeb0..2d8c9dce 100644 --- a/src/main.h +++ b/src/main.h @@ -3,21 +3,19 @@ #include "lorawan.h" #include "macsniff.h" -// program version - note: increment version after modifications to configData_t struct!! -#define PROGVERSION "1.3.8" // use max 10 chars here! -#define PROGNAME "PAXCNT" +// program version - note: increment version after modifications to configData_t +// struct!! +#define PROGVERSION "1.3.8" // use max 10 chars here! +#define PROGNAME "PAXCNT" //--- Declarations --- -enum led_states { - LED_OFF, - LED_ON -}; +enum led_states { LED_OFF, LED_ON }; #if defined(CFG_eu868) - const char lora_datarate[] = {"1211100908077BFSNA"}; -#elif defined(CFG_us915) - const char lora_datarate[] = {"100908078CNA121110090807"}; +const char lora_datarate[] = {"1211100908077BFSNA"}; +#elif defined(CFG_us915) +const char lora_datarate[] = {"100908078CNA121110090807"}; #endif //--- Prototypes --- @@ -27,15 +25,14 @@ void reset_counters(void); void blink_LED(uint16_t set_color, uint16_t set_blinkduration); void led_loop(void); - // defined in blescan.cpp #ifdef BLECOUNTER - void start_BLEscan(void); - void stop_BLEscan(void); +void start_BLEscan(void); +void stop_BLEscan(void); #endif -//defined in gpsread.cpp +// defined in gpsread.cpp #ifdef HAS_GPS - void gps_read(void); - void gps_loop(void * pvParameters); +void gps_read(void); +void gps_loop(void *pvParameters); #endif \ No newline at end of file diff --git a/src/rcommand.cpp b/src/rcommand.cpp index fa061f9f..f1bfea94 100644 --- a/src/rcommand.cpp +++ b/src/rcommand.cpp @@ -1,310 +1,365 @@ // remote command interpreter -// parses multiple number of command / value pairs from LoRaWAN remote command port (RCMDPORT) -// checks commands and executes each command with 1 argument per command +// parses multiple number of command / value pairs from LoRaWAN remote command +// port (RCMDPORT) checks commands and executes each command with 1 argument per +// command // Basic Config #include "globals.h" // LMIC-Arduino LoRaWAN Stack -#include #include +#include // Local logging tag static const char TAG[] = "main"; // table of remote commands and assigned functions typedef struct { - const uint8_t nam; - void (*func)(uint8_t); - const bool store; + const uint8_t nam; + void (*func)(uint8_t); + const bool store; } cmd_t; // function defined in antenna.cpp #ifdef HAS_ANTENNA_SWITCH - void antenna_select(const uint8_t _ant); +void antenna_select(const uint8_t _ant); #endif // function defined in adcread.cpp #ifdef HAS_BATTERY_PROBE - uint32_t read_voltage(void); +uint32_t read_voltage(void); #endif // function sends result of get commands to LoRaWAN network -void do_transmit(osjob_t* j){ - // check if there is a pending TX/RX job running, if yes then reschedule transmission - if (LMIC.opmode & OP_TXRXPEND) { - ESP_LOGI(TAG, "LoRa busy, rescheduling"); - sprintf(display_lmic, "LORA BUSY"); - os_setTimedCallback(&rcmdjob, os_getTime()+sec2osticks(RETRANSMIT_RCMD), do_transmit); - } - LMIC_setTxData2(RCMDPORT, rcmd_data, rcmd_data_size, 0); // send data unconfirmed on RCMD Port - ESP_LOGI(TAG, "%d bytes queued to send", rcmd_data_size); - sprintf(display_lmic, "PACKET QUEUED"); +void do_transmit(osjob_t *j) { + // check if there is a pending TX/RX job running, if yes then reschedule + // transmission + if (LMIC.opmode & OP_TXRXPEND) { + ESP_LOGI(TAG, "LoRa busy, rescheduling"); + sprintf(display_lmic, "LORA BUSY"); + os_setTimedCallback(&rcmdjob, os_getTime() + sec2osticks(RETRANSMIT_RCMD), + do_transmit); + } + LMIC_setTxData2(RCMDPORT, rcmd_data, rcmd_data_size, + 0); // send data unconfirmed on RCMD Port + ESP_LOGI(TAG, "%d bytes queued to send", rcmd_data_size); + sprintf(display_lmic, "PACKET QUEUED"); } -// help function to transmit result of get commands, since callback function do_transmit() cannot have params -void transmit(xref2u1_t mydata, u1_t mydata_size){ - rcmd_data = mydata; - rcmd_data_size = mydata_size; - do_transmit(&rcmdjob); +// help function to transmit result of get commands, since callback function +// do_transmit() cannot have params +void transmit(xref2u1_t mydata, u1_t mydata_size) { + rcmd_data = mydata; + rcmd_data_size = mydata_size; + do_transmit(&rcmdjob); } // help function to assign LoRa datarates to numeric spreadfactor values -void switch_lora (uint8_t sf, uint8_t tx) { - if ( tx > 20 ) return; - cfg.txpower = tx; - switch (sf) { - case 7: LMIC_setDrTxpow(DR_SF7,tx); cfg.lorasf=sf; break; - case 8: LMIC_setDrTxpow(DR_SF8,tx); cfg.lorasf=sf; break; - case 9: LMIC_setDrTxpow(DR_SF9,tx); cfg.lorasf=sf; break; - case 10: LMIC_setDrTxpow(DR_SF10,tx); cfg.lorasf=sf; break; - case 11: - #if defined(CFG_eu868) - LMIC_setDrTxpow(DR_SF11,tx); cfg.lorasf=sf; break; - #elif defined(CFG_us915) - LMIC_setDrTxpow(DR_SF11CR,tx); cfg.lorasf=sf; break; - #endif - case 12: - #if defined(CFG_eu868) - LMIC_setDrTxpow(DR_SF12,tx); cfg.lorasf=sf; break; - #elif defined(CFG_us915) - LMIC_setDrTxpow(DR_SF12CR,tx); cfg.lorasf=sf; break; - #endif - default: break; - } +void switch_lora(uint8_t sf, uint8_t tx) { + if (tx > 20) + return; + cfg.txpower = tx; + switch (sf) { + case 7: + LMIC_setDrTxpow(DR_SF7, tx); + cfg.lorasf = sf; + break; + case 8: + LMIC_setDrTxpow(DR_SF8, tx); + cfg.lorasf = sf; + break; + case 9: + LMIC_setDrTxpow(DR_SF9, tx); + cfg.lorasf = sf; + break; + case 10: + LMIC_setDrTxpow(DR_SF10, tx); + cfg.lorasf = sf; + break; + case 11: +#if defined(CFG_eu868) + LMIC_setDrTxpow(DR_SF11, tx); + cfg.lorasf = sf; + break; +#elif defined(CFG_us915) + LMIC_setDrTxpow(DR_SF11CR, tx); + cfg.lorasf = sf; + break; +#endif + case 12: +#if defined(CFG_eu868) + LMIC_setDrTxpow(DR_SF12, tx); + cfg.lorasf = sf; + break; +#elif defined(CFG_us915) + LMIC_setDrTxpow(DR_SF12CR, tx); + cfg.lorasf = sf; + break; +#endif + default: + break; + } } // set of functions that can be triggered by remote commands void set_reset(uint8_t val) { - switch (val) { - case 0: // restart device - ESP_LOGI(TAG, "Remote command: restart device"); - sprintf(display_lora, "Reset pending"); - vTaskDelay(10000/portTICK_PERIOD_MS); // wait for LMIC to confirm LoRa downlink to server - esp_restart(); - break; - case 1: // reset MAC counter - ESP_LOGI(TAG, "Remote command: reset MAC counter"); - reset_counters(); // clear macs - reset_salt(); // get new salt - sprintf(display_lora, "Reset counter"); - break; - case 2: // reset device to factory settings - ESP_LOGI(TAG, "Remote command: reset device to factory settings"); - sprintf(display_lora, "Factory reset"); - eraseConfig(); - break; - } + switch (val) { + case 0: // restart device + ESP_LOGI(TAG, "Remote command: restart device"); + sprintf(display_lora, "Reset pending"); + vTaskDelay( + 10000 / + portTICK_PERIOD_MS); // wait for LMIC to confirm LoRa downlink to server + esp_restart(); + break; + case 1: // reset MAC counter + ESP_LOGI(TAG, "Remote command: reset MAC counter"); + reset_counters(); // clear macs + reset_salt(); // get new salt + sprintf(display_lora, "Reset counter"); + break; + case 2: // reset device to factory settings + ESP_LOGI(TAG, "Remote command: reset device to factory settings"); + sprintf(display_lora, "Factory reset"); + eraseConfig(); + break; + } }; void set_rssi(uint8_t val) { - cfg.rssilimit = val * -1; - ESP_LOGI(TAG, "Remote command: set RSSI limit to %d", cfg.rssilimit); -}; + cfg.rssilimit = val * -1; + ESP_LOGI(TAG, "Remote command: set RSSI limit to %d", cfg.rssilimit); +}; void set_sendcycle(uint8_t val) { - cfg.sendcycle = val; - ESP_LOGI(TAG, "Remote command: set payload send cycle to %d seconds", cfg.sendcycle*2); -}; + cfg.sendcycle = val; + ESP_LOGI(TAG, "Remote command: set payload send cycle to %d seconds", + cfg.sendcycle * 2); +}; void set_wifichancycle(uint8_t val) { - cfg.wifichancycle = val; - // modify wifi channel rotation IRQ - timerAlarmWrite(channelSwitch, cfg.wifichancycle * 10000, true); // reload interrupt after each trigger of channel switch cycle - ESP_LOGI(TAG, "Remote command: set Wifi channel switch interval to %.1f seconds", cfg.wifichancycle/float(100)); -}; + cfg.wifichancycle = val; + // modify wifi channel rotation IRQ + timerAlarmWrite( + channelSwitch, cfg.wifichancycle * 10000, + true); // reload interrupt after each trigger of channel switch cycle + ESP_LOGI(TAG, + "Remote command: set Wifi channel switch interval to %.1f seconds", + cfg.wifichancycle / float(100)); +}; void set_blescantime(uint8_t val) { - cfg.blescantime = val; - ESP_LOGI(TAG, "Remote command: set BLE scan time to %.1f seconds", cfg.blescantime/float(100)); - #ifdef BLECOUNTER - // stop & restart BLE scan task to apply new parameter - if (cfg.blescan) - { - stop_BLEscan(); - start_BLEscan(); - } - #endif + cfg.blescantime = val; + ESP_LOGI(TAG, "Remote command: set BLE scan time to %.1f seconds", + cfg.blescantime / float(100)); +#ifdef BLECOUNTER + // stop & restart BLE scan task to apply new parameter + if (cfg.blescan) { + stop_BLEscan(); + start_BLEscan(); + } +#endif }; void set_countmode(uint8_t val) { - switch (val) { - case 0: // cyclic unconfirmed - cfg.countermode = 0; - ESP_LOGI(TAG, "Remote command: set counter mode to cyclic unconfirmed"); - break; - case 1: // cumulative - cfg.countermode = 1; - ESP_LOGI(TAG, "Remote command: set counter mode to cumulative"); - break; - default: // cyclic confirmed - cfg.countermode = 2; - ESP_LOGI(TAG, "Remote command: set counter mode to cyclic confirmed"); - break; - } + switch (val) { + case 0: // cyclic unconfirmed + cfg.countermode = 0; + ESP_LOGI(TAG, "Remote command: set counter mode to cyclic unconfirmed"); + break; + case 1: // cumulative + cfg.countermode = 1; + ESP_LOGI(TAG, "Remote command: set counter mode to cumulative"); + break; + default: // cyclic confirmed + cfg.countermode = 2; + ESP_LOGI(TAG, "Remote command: set counter mode to cyclic confirmed"); + break; + } }; void set_screensaver(uint8_t val) { - ESP_LOGI(TAG, "Remote command: set screen saver to %s ", val ? "on" : "off"); - switch (val) { - case 1: cfg.screensaver = val; break; - default: cfg.screensaver = 0; break; - } + ESP_LOGI(TAG, "Remote command: set screen saver to %s ", val ? "on" : "off"); + switch (val) { + case 1: + cfg.screensaver = val; + break; + default: + cfg.screensaver = 0; + break; + } }; void set_display(uint8_t val) { - ESP_LOGI(TAG, "Remote command: set screen to %s", val ? "on" : "off"); - switch (val) { - case 1: cfg.screenon = val; break; - default: cfg.screenon = 0; break; - } + ESP_LOGI(TAG, "Remote command: set screen to %s", val ? "on" : "off"); + switch (val) { + case 1: + cfg.screenon = val; + break; + default: + cfg.screenon = 0; + break; + } }; void set_gps(uint8_t val) { - ESP_LOGI(TAG, "Remote command: set GPS to %s", val ? "on" : "off"); - switch (val) { - case 1: cfg.gpsmode = val; break; - default: cfg.gpsmode = 0; break; - } + ESP_LOGI(TAG, "Remote command: set GPS to %s", val ? "on" : "off"); + switch (val) { + case 1: + cfg.gpsmode = val; + break; + default: + cfg.gpsmode = 0; + break; + } }; void set_lorasf(uint8_t val) { - ESP_LOGI(TAG, "Remote command: set LoRa SF to %d", val); - switch_lora(val, cfg.txpower); + ESP_LOGI(TAG, "Remote command: set LoRa SF to %d", val); + switch_lora(val, cfg.txpower); }; void set_loraadr(uint8_t val) { - ESP_LOGI(TAG, "Remote command: set LoRa ADR mode to %s", val ? "on" : "off"); - switch (val) { - case 1: cfg.adrmode = val; break; - default: cfg.adrmode = 0; break; - } - LMIC_setAdrMode(cfg.adrmode); + ESP_LOGI(TAG, "Remote command: set LoRa ADR mode to %s", val ? "on" : "off"); + switch (val) { + case 1: + cfg.adrmode = val; + break; + default: + cfg.adrmode = 0; + break; + } + LMIC_setAdrMode(cfg.adrmode); }; void set_blescan(uint8_t val) { - ESP_LOGI(TAG, "Remote command: set BLE scanner to %s", val ? "on" : "off"); - switch (val) { - case 0: - cfg.blescan = 0; - macs_ble = 0; // clear BLE counter - #ifdef BLECOUNTER - stop_BLEscan(); - #endif - break; - default: - cfg.blescan = 1; - #ifdef BLECOUNTER - start_BLEscan(); - #endif - break; - } + ESP_LOGI(TAG, "Remote command: set BLE scanner to %s", val ? "on" : "off"); + switch (val) { + case 0: + cfg.blescan = 0; + macs_ble = 0; // clear BLE counter +#ifdef BLECOUNTER + stop_BLEscan(); +#endif + break; + default: + cfg.blescan = 1; +#ifdef BLECOUNTER + start_BLEscan(); +#endif + break; + } }; void set_wifiant(uint8_t val) { - ESP_LOGI(TAG, "Remote command: set Wifi antenna to %s", val ? "external" : "internal"); - switch (val) { - case 1: cfg.wifiant = val; break; - default: cfg.wifiant = 0; break; - } - #ifdef HAS_ANTENNA_SWITCH - antenna_select(cfg.wifiant); - #endif + ESP_LOGI(TAG, "Remote command: set Wifi antenna to %s", + val ? "external" : "internal"); + switch (val) { + case 1: + cfg.wifiant = val; + break; + default: + cfg.wifiant = 0; + break; + } +#ifdef HAS_ANTENNA_SWITCH + antenna_select(cfg.wifiant); +#endif }; void set_vendorfilter(uint8_t val) { - ESP_LOGI(TAG, "Remote command: set vendorfilter mode to %s", val ? "on" : "off"); - switch (val) { - case 1: cfg.vendorfilter = val; break; - default: cfg.vendorfilter = 0; break; - } + ESP_LOGI(TAG, "Remote command: set vendorfilter mode to %s", + val ? "on" : "off"); + switch (val) { + case 1: + cfg.vendorfilter = val; + break; + default: + cfg.vendorfilter = 0; + break; + } }; void set_rgblum(uint8_t val) { - // Avoid wrong parameters - cfg.rgblum = (val>=0 && val<=100) ? (uint8_t) val : RGBLUMINOSITY; - ESP_LOGI(TAG, "Remote command: set RGB Led luminosity %d", cfg.rgblum); + // Avoid wrong parameters + cfg.rgblum = (val >= 0 && val <= 100) ? (uint8_t)val : RGBLUMINOSITY; + ESP_LOGI(TAG, "Remote command: set RGB Led luminosity %d", cfg.rgblum); }; void set_lorapower(uint8_t val) { - ESP_LOGI(TAG, "Remote command: set LoRa TXPOWER to %d", val); - switch_lora(cfg.lorasf, val); + ESP_LOGI(TAG, "Remote command: set LoRa TXPOWER to %d", val); + switch_lora(cfg.lorasf, val); }; -void get_config (uint8_t val) { - ESP_LOGI(TAG, "Remote command: get configuration"); - transmit((byte*)&cfg, sizeof(cfg)); +void get_config(uint8_t val) { + ESP_LOGI(TAG, "Remote command: get configuration"); + transmit((byte *)&cfg, sizeof(cfg)); }; -void get_uptime (uint8_t val) { - ESP_LOGI(TAG, "Remote command: get uptime"); - transmit((byte*)&uptimecounter, sizeof(uptimecounter)); +void get_uptime(uint8_t val) { + ESP_LOGI(TAG, "Remote command: get uptime"); + transmit((byte *)&uptimecounter, sizeof(uptimecounter)); }; -void get_cputemp (uint8_t val) { - ESP_LOGI(TAG, "Remote command: get cpu temperature"); - float temp = temperatureRead(); - transmit((byte*)&temp, sizeof(temp)); +void get_cputemp(uint8_t val) { + ESP_LOGI(TAG, "Remote command: get cpu temperature"); + float temp = temperatureRead(); + transmit((byte *)&temp, sizeof(temp)); }; -void get_voltage (uint8_t val) { - ESP_LOGI(TAG, "Remote command: get battery voltage"); - #ifdef HAS_BATTERY_PROBE - uint16_t voltage = read_voltage(); - #else - uint16_t voltage = 0; - #endif - transmit((byte*)&voltage, sizeof(voltage)); +void get_voltage(uint8_t val) { + ESP_LOGI(TAG, "Remote command: get battery voltage"); +#ifdef HAS_BATTERY_PROBE + uint16_t voltage = read_voltage(); +#else + uint16_t voltage = 0; +#endif + transmit((byte *)&voltage, sizeof(voltage)); }; -void get_gps (uint8_t val) { - ESP_LOGI(TAG, "Remote command: get gps status"); - #ifdef HAS_GPS - gps_read(); - transmit((byte*)&gps_status, sizeof(gps_status)); - ESP_LOGI(TAG, "lat=%f / lon=%f | Sats=%u | HDOP=%u | Alti=%u", gps_status.latitude / 1000000, gps_status.longitude / 1000000, gps_status.satellites, gps_status.hdop, gps_status.altitude); - #else - ESP_LOGE(TAG, "GPS not present"); - #endif +void get_gps(uint8_t val) { + ESP_LOGI(TAG, "Remote command: get gps status"); +#ifdef HAS_GPS + gps_read(); + transmit((byte *)&gps_status, sizeof(gps_status)); + ESP_LOGI(TAG, "lat=%f / lon=%f | Sats=%u | HDOP=%u | Alti=%u", + gps_status.latitude / 1000000, gps_status.longitude / 1000000, + gps_status.satellites, gps_status.hdop, gps_status.altitude); +#else + ESP_LOGE(TAG, "GPS not present"); +#endif }; // assign previously defined functions to set of numeric remote commands // format: opcode, function, flag (1 = do make settings persistent / 0 = don't) -// -cmd_t table[] = { - {0x01, set_rssi, true}, - {0x02, set_countmode, true}, - {0x03, set_gps, true}, - {0x04, set_display, true}, - {0x05, set_lorasf, true}, - {0x06, set_lorapower, true}, - {0x07, set_loraadr, true}, - {0x08, set_screensaver, true}, - {0x09, set_reset, false}, - {0x0a, set_sendcycle, true}, - {0x0b, set_wifichancycle, true}, - {0x0c, set_blescantime, true}, - {0x0d, set_vendorfilter, false}, - {0x0e, set_blescan, true}, - {0x0f, set_wifiant, true}, - {0x10, set_rgblum, true}, - {0x80, get_config, false}, - {0x81, get_uptime, false}, - {0x82, get_cputemp, false}, - {0x83, get_voltage, false}, - {0x84, get_gps, false} - }; +// +cmd_t table[] = {{0x01, set_rssi, true}, {0x02, set_countmode, true}, + {0x03, set_gps, true}, {0x04, set_display, true}, + {0x05, set_lorasf, true}, {0x06, set_lorapower, true}, + {0x07, set_loraadr, true}, {0x08, set_screensaver, true}, + {0x09, set_reset, false}, {0x0a, set_sendcycle, true}, + {0x0b, set_wifichancycle, true}, {0x0c, set_blescantime, true}, + {0x0d, set_vendorfilter, false}, {0x0e, set_blescan, true}, + {0x0f, set_wifiant, true}, {0x10, set_rgblum, true}, + {0x80, get_config, false}, {0x81, get_uptime, false}, + {0x82, get_cputemp, false}, {0x83, get_voltage, false}, + {0x84, get_gps, false}}; // check and execute remote command void rcommand(uint8_t cmd, uint8_t arg) { - int i = sizeof(table) / sizeof(table[0]); // number of commands in command table - bool store_flag = false; - while(i--) { - if(cmd == table[i].nam) { // check if valid command - table[i].func(arg); // then execute assigned function - if ( table[i].store ) store_flag = true; // set save flag if function needs to store configuration - break; // exit check loop, since command was found - } + int i = + sizeof(table) / sizeof(table[0]); // number of commands in command table + bool store_flag = false; + while (i--) { + if (cmd == table[i].nam) { // check if valid command + table[i].func(arg); // then execute assigned function + if (table[i].store) + store_flag = + true; // set save flag if function needs to store configuration + break; // exit check loop, since command was found } - if (store_flag) saveConfig(); // if save flag is set: store new configuration in NVS to make it persistent + } + if (store_flag) + saveConfig(); // if save flag is set: store new configuration in NVS to make + // it persistent } \ No newline at end of file diff --git a/src/rgb_led.cpp b/src/rgb_led.cpp index 899ded9e..772f0a0b 100644 --- a/src/rgb_led.cpp +++ b/src/rgb_led.cpp @@ -6,23 +6,22 @@ // RGB Led instance SmartLed rgb_led(LED_WS2812, 1, HAS_RGB_LED); -float rgb_CalcColor(float p, float q, float t) -{ - if (t < 0.0f) - t += 1.0f; - if (t > 1.0f) - t -= 1.0f; +float rgb_CalcColor(float p, float q, float t) { + if (t < 0.0f) + t += 1.0f; + if (t > 1.0f) + t -= 1.0f; - if (t < 1.0f / 6.0f) - return p + (q - p) * 6.0f * t; + if (t < 1.0f / 6.0f) + return p + (q - p) * 6.0f * t; - if (t < 0.5f) - return q; + if (t < 0.5f) + return q; - if (t < 2.0f / 3.0f) - return p + ((q - p) * (2.0f / 3.0f - t) * 6.0f); + if (t < 2.0f / 3.0f) + return p + ((q - p) * (2.0f / 3.0f - t) * 6.0f); - return p; + return p; } // ------------------------------------------------------------------------ @@ -30,45 +29,41 @@ float rgb_CalcColor(float p, float q, float t) // HslColor using H, S, L values (0.0 - 1.0) // L should be limited to between (0.0 - 0.5) // ------------------------------------------------------------------------ -RGBColor rgb_hsl2rgb(float h, float s, float l) -{ - RGBColor RGB_color; - float r; - float g; - float b; +RGBColor rgb_hsl2rgb(float h, float s, float l) { + RGBColor RGB_color; + float r; + float g; + float b; - if (s == 0.0f || l == 0.0f) - { - r = g = b = l; // achromatic or black - } - else - { - float q = l < 0.5f ? l * (1.0f + s) : l + s - (l * s); - float p = 2.0f * l - q; - r = rgb_CalcColor(p, q, h + 1.0f / 3.0f); - g = rgb_CalcColor(p, q, h); - b = rgb_CalcColor(p, q, h - 1.0f / 3.0f); - } + if (s == 0.0f || l == 0.0f) { + r = g = b = l; // achromatic or black + } else { + float q = l < 0.5f ? l * (1.0f + s) : l + s - (l * s); + float p = 2.0f * l - q; + r = rgb_CalcColor(p, q, h + 1.0f / 3.0f); + g = rgb_CalcColor(p, q, h); + b = rgb_CalcColor(p, q, h - 1.0f / 3.0f); + } - RGB_color.R = (uint8_t)(r * 255.0f); - RGB_color.G = (uint8_t)(g * 255.0f); - RGB_color.B = (uint8_t)(b * 255.0f); + RGB_color.R = (uint8_t)(r * 255.0f); + RGB_color.G = (uint8_t)(g * 255.0f); + RGB_color.B = (uint8_t)(b * 255.0f); - return RGB_color; + return RGB_color; } void rgb_set_color(uint16_t hue) { if (hue == COLOR_NONE) { // Off - rgb_led[0] = Rgb(0,0,0); + rgb_led[0] = Rgb(0, 0, 0); } else { // see http://www.workwithcolor.com/blue-color-hue-range-01.htm // H (is color from 0..360) should be between 0.0 and 1.0 // S is saturation keep it to 1 // L is brightness should be between 0.0 and 0.5 // cfg.rgblum is between 0 and 100 (percent) - RGBColor target = rgb_hsl2rgb( hue / 360.0f, 1.0f, 0.005f * cfg.rgblum); - //uint32_t color = target.R<<16 | target.G<<8 | target.B; + RGBColor target = rgb_hsl2rgb(hue / 360.0f, 1.0f, 0.005f * cfg.rgblum); + // uint32_t color = target.R<<16 | target.G<<8 | target.B; rgb_led[0] = Rgb(target.R, target.G, target.B); } // Show diff --git a/src/rgb_led.h b/src/rgb_led.h index 589b3f94..49aa5f76 100644 --- a/src/rgb_led.h +++ b/src/rgb_led.h @@ -3,27 +3,26 @@ // value for HSL color // see http://www.workwithcolor.com/blue-color-hue-range-01.htm -#define COLOR_RED 0 -#define COLOR_ORANGE 30 -#define COLOR_ORANGE_YELLOW 45 -#define COLOR_YELLOW 60 -#define COLOR_YELLOW_GREEN 90 -#define COLOR_GREEN 120 -#define COLOR_GREEN_CYAN 165 -#define COLOR_CYAN 180 -#define COLOR_CYAN_BLUE 210 -#define COLOR_BLUE 240 -#define COLOR_BLUE_MAGENTA 275 -#define COLOR_MAGENTA 300 -#define COLOR_PINK 350 -#define COLOR_WHITE 360 -#define COLOR_NONE 999 +#define COLOR_RED 0 +#define COLOR_ORANGE 30 +#define COLOR_ORANGE_YELLOW 45 +#define COLOR_YELLOW 60 +#define COLOR_YELLOW_GREEN 90 +#define COLOR_GREEN 120 +#define COLOR_GREEN_CYAN 165 +#define COLOR_CYAN 180 +#define COLOR_CYAN_BLUE 210 +#define COLOR_BLUE 240 +#define COLOR_BLUE_MAGENTA 275 +#define COLOR_MAGENTA 300 +#define COLOR_PINK 350 +#define COLOR_WHITE 360 +#define COLOR_NONE 999 -struct RGBColor -{ - uint8_t R; - uint8_t G; - uint8_t B; +struct RGBColor { + uint8_t R; + uint8_t G; + uint8_t B; }; // Exported Functions diff --git a/src/rokkithash.cpp b/src/rokkithash.cpp index 98b6c5c3..62e12656 100644 --- a/src/rokkithash.cpp +++ b/src/rokkithash.cpp @@ -36,48 +36,52 @@ #include -uint32_t rokkit(const char * data, int len) { +uint32_t rokkit(const char *data, int len) { uint32_t hash, tmp; int rem; - if (len <= 0 || data == 0) return 0; - hash = len; - rem = len & 3; - len >>= 2; + if (len <= 0 || data == 0) + return 0; + hash = len; + rem = len & 3; + len >>= 2; - /* Main loop */ - while (len > 0) { - hash += *((uint16_t*)data); - tmp = (*((uint16_t*)(data+2)) << 11) ^ hash; - hash = (hash << 16) ^ tmp; - data += 2*2; - hash += hash >> 11; - len--; - } + /* Main loop */ + while (len > 0) { + hash += *((uint16_t *)data); + tmp = (*((uint16_t *)(data + 2)) << 11) ^ hash; + hash = (hash << 16) ^ tmp; + data += 2 * 2; + hash += hash >> 11; + len--; + } - /* Handle end cases */ - switch (rem) { - case 3: hash += *((uint16_t*)data); - hash ^= hash << 16; - hash ^= ((signed char)data[2]) << 18; - hash += hash >> 11; - break; - case 2: hash += *((uint16_t*)data); - hash ^= hash << 11; - hash += hash >> 17; - break; - case 1: hash += (signed char)*data; - hash ^= hash << 10; - hash += hash >> 1; - } - - /* Force "avalanching" of final 127 bits */ - hash ^= hash << 3; - hash += hash >> 5; - hash ^= hash << 4; + /* Handle end cases */ + switch (rem) { + case 3: + hash += *((uint16_t *)data); + hash ^= hash << 16; + hash ^= ((signed char)data[2]) << 18; + hash += hash >> 11; + break; + case 2: + hash += *((uint16_t *)data); + hash ^= hash << 11; hash += hash >> 17; - hash ^= hash << 25; - hash += hash >> 6; + break; + case 1: + hash += (signed char)*data; + hash ^= hash << 10; + hash += hash >> 1; + } - return hash; + /* Force "avalanching" of final 127 bits */ + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + + return hash; } diff --git a/src/vendor_array.h b/src/vendor_array.h index 9b610782..fd555495 100644 --- a/src/vendor_array.h +++ b/src/vendor_array.h @@ -1,1435 +1,206 @@ std::array vendors = { - 0x38f23e, - 0x807abf, - 0x90e7c4, - 0x7c6193, - 0x485073, - 0x74e28c, - 0x8463d6, - 0xd48f33, - 0x2c8a72, - 0x980d2e, - 0xa826d9, - 0xd4206d, - 0x00155d, - 0x806c1b, - 0xa470d6, - 0x985fd3, - 0x1c69a5, - 0x382de8, - 0xd087e2, - 0x205531, - 0x5440ad, - 0x842e27, - 0x50f0d3, - 0x84119e, - 0x08eca9, - 0x10d38a, - 0x382dd1, - 0xe0cbee, - 0x64b853, - 0xf4428f, - 0x188331, - 0x8455a5, - 0xa87c01, - 0xc01173, - 0xbce63f, - 0xb857d8, - 0x94b10a, - 0xe458b8, - 0x088c2c, - 0xb86ce8, - 0x9c65b0, - 0xc8a823, - 0xc44202, - 0xd059e4, - 0x64b310, - 0x9476b7, - 0x8c1abf, - 0xb47443, - 0x30cbf8, - 0x182195, - 0xa88195, - 0x88add2, - 0xd0fccc, - 0x14c913, - 0x4c6641, - 0x3cbdd8, - 0x38256b, - 0x849866, - 0xe89309, - 0x0016db, - 0x5c3c27, - 0x10d542, - 0xa0821f, - 0xc45006, - 0x88329b, - 0xbc8ccd, - 0x400e85, - 0xec9bf3, - 0xf8042e, - 0x843838, - 0x54880e, - 0xbc79ad, - 0x30d6c9, - 0xb0df3a, - 0x805719, - 0x78a873, - 0x041bba, - 0x08fd0e, - 0x08d42b, - 0x00e3b2, - 0xc81479, - 0xf0728c, - 0x94350a, - 0x001fcd, - 0xd0dfc7, - 0x1c62b8, - 0x18e2c2, - 0x001a8a, - 0x002567, - 0xa8f274, - 0x001599, - 0x0012fb, - 0x7cf854, - 0x8cc8cd, - 0xe81132, - 0xa02195, - 0x8c71f8, - 0x04180f, - 0x9463d1, - 0x0cdfa4, - 0xcc051b, - 0x68ebae, - 0x60d0a9, - 0x60a10a, - 0xa07591, - 0x001fcc, - 0xec107b, - 0xa01081, - 0xf4f524, - 0x9c99a0, - 0x185936, - 0x98fae3, - 0x640980, - 0x8cbebe, - 0xf8a45f, - 0xbc8385, - 0x900628, - 0xd4ae05, - 0x3c0518, - 0xc40bcb, - 0xe8bba8, - 0xbc3aea, - 0x8c0ee3, - 0x6c5c14, - 0x78abbb, - 0x1816c9, - 0xfc8f90, - 0x244b03, - 0x988389, - 0x14bb6e, - 0x1c3ade, - 0xf83f51, - 0xd8e0e1, - 0xecf342, - 0x5092b9, - 0xb4bff6, - 0xc8d7b0, - 0x982d68, - 0xecd09f, - 0xe446da, - 0xf4f5db, - 0xd80831, - 0xdc5583, - 0x2c54cf, - 0x001fe3, - 0x0026e2, - 0x001e75, - 0x6cd68a, - 0x2021a5, - 0x0c4885, - 0xdc0b34, - 0xac0d1b, - 0x60e3ac, - 0xf895c7, - 0xc4438f, - 0xa816b2, - 0xe892a4, - 0x700514, - 0x88c9d0, - 0x2c598a, - 0x18f0e4, - 0xec8350, - 0x4cdd31, - 0x703eac, - 0x6c94f8, - 0x84788b, - 0x2cf0ee, - 0x68d93c, - 0x24e314, - 0x28f076, - 0x285aeb, - 0x087402, - 0xccc760, - 0x705aac, - 0xfc643a, - 0xd4e6b7, - 0x2802d8, - 0x9c2ea1, - 0x68967b, - 0xbc6778, - 0xa82066, - 0xb065bd, - 0xf0dce2, - 0x7cd1c3, - 0x705681, - 0x2cbe08, - 0x783a84, - 0x84b153, - 0x6476ba, - 0x54ae27, - 0xf437b7, - 0xf0d1a9, - 0x34c059, - 0x04f7e4, - 0x10ddb1, - 0xb4f0ab, - 0x848506, - 0x7831c1, - 0x8c7c92, - 0xd0e140, - 0xacfdec, - 0xf82793, - 0x40a6d9, - 0x109add, - 0xf0b479, - 0x58b035, - 0x34159e, - 0x286aba, - 0xec852f, - 0x44d884, - 0x003ee1, - 0x7c11be, - 0x04e536, - 0x881fa1, - 0x04db56, - 0x9cfc01, - 0xc81ee7, - 0x34363b, - 0xc01ada, - 0xacbc32, - 0x70700d, - 0x7c5049, - 0x503237, - 0xd4619d, - 0xb0481a, - 0x989e63, - 0xdca904, - 0x48a195, - 0x6cab31, - 0x6c96cf, - 0x3035ad, - 0xa8be27, - 0xb8634d, - 0x9ce33f, - 0xf0989d, - 0xace4b5, - 0xe42b34, - 0x1c36bb, - 0x3c2eff, - 0xf0766f, - 0x40cbc0, - 0x4098ad, - 0x6c4d73, - 0xc48466, - 0xd02b20, - 0x3010e4, - 0x380f4a, - 0x685b35, - 0xc86f1d, - 0x701124, - 0x38484c, - 0x041552, - 0x786c1c, - 0xbc3baf, - 0x00a040, - 0x60fec5, - 0xcc4463, - 0x6c72e7, - 0x18af61, - 0x00cdfe, - 0xac61ea, - 0x38b54d, - 0x60c547, - 0x28e02c, - 0x50ead6, - 0x4860bc, - 0x403004, - 0x0c74c2, - 0xa4b197, - 0x7cf05f, - 0xa4f1e8, - 0x70a2b3, - 0x4c57ca, - 0x68fb7e, - 0x90c1c6, - 0x9cf48e, - 0xfcd848, - 0x64b9e8, - 0x001cb3, - 0x000d93, - 0x30d9d9, - 0x6030d4, - 0x94bf2d, - 0xc49880, - 0xe0338e, - 0x68fef7, - 0xbce143, - 0x645aed, - 0xc0b658, - 0x881908, - 0xfc2a9c, - 0x48605f, - 0x188796, - 0x002376, - 0x84100d, - 0x04c23e, - 0x5c5188, - 0xe89120, - 0x9c6c15, - 0x4886e8, - 0x2c2997, - 0x102f6b, - 0x00eebd, - 0x281878, - 0x6045bd, - 0x7ced8d, - 0xe85b5b, - 0x000d3a, - 0xe09861, - 0xf4f1e1, - 0x60beb5, - 0xb4e1c4, - 0x70aab2, - 0x0026ff, - 0x406f2a, - 0x002557, - 0xf05a09, - 0x503275, - 0x28cc01, - 0xb46293, - 0x04fe31, - 0x845181, - 0xd831cf, - 0xf8d0bd, - 0xfcc734, - 0xe4b021, - 0xb0ec71, - 0x3cbbfd, - 0x2cae2b, - 0xc488e5, - 0x7c9122, - 0xe8b4c8, - 0x18895b, - 0xe0db10, - 0xe09971, - 0x6077e2, - 0x680571, - 0x6c2f2c, - 0x300d43, - 0x6c2779, - 0x607edd, - 0x9c2a83, - 0xe45d75, - 0xe4faed, - 0xc83f26, - 0x54f201, - 0xa06090, - 0xac3743, - 0x141f78, - 0x006f64, - 0xdc6672, - 0x001e7d, - 0x3c6200, - 0x0024e9, - 0x002399, - 0xe4e0c5, - 0xe8039a, - 0xc4731e, - 0x8c7712, - 0x2013e0, - 0x0007ab, - 0x0021d2, - 0xbc4760, - 0xd0176a, - 0x2cbaba, - 0x24920e, - 0x40d3ae, - 0xf01dbc, - 0x24dbed, - 0xac3613, - 0x1449e0, - 0xc0bdd1, - 0xe8508b, - 0xf025b7, - 0xc8ba94, - 0xec1f72, - 0x9852b1, - 0x1489fd, - 0xccfe3c, - 0x789ed0, - 0xe440e2, - 0x1caf05, - 0xe492fb, - 0x0073e0, - 0xbc4486, - 0x380b40, - 0x002490, - 0x0023d7, - 0xfca13e, - 0xa00798, - 0x945103, - 0xc819f7, - 0x2c4401, - 0x28e31f, - 0x0c1daf, - 0x14f65a, - 0x742344, - 0xf0b429, - 0xec51bc, - 0xf079e8, - 0x887598, - 0xd0b128, - 0xd00401, - 0xf06d78, - 0x10683f, - 0x74a722, - 0x58a2b5, - 0x64899a, - 0x88074b, - 0x64bc0c, - 0xa039f7, - 0x041b6d, - 0x001f6b, - 0x30b4b8, - 0x503cea, - 0x0c9838, - 0x54fcf0, - 0x08aed6, - 0xa816d0, - 0x88bd45, - 0x544e90, - 0xd03311, - 0x70e72c, - 0xc0cecd, - 0x98e0d9, - 0xe0accb, - 0x78d75f, - 0x2078f0, - 0x985aeb, - 0xa45e60, - 0x006d52, - 0x5cadcf, - 0xb8e856, - 0x90b21f, - 0xa8bbcf, - 0xc8b5b7, - 0x18af8f, - 0xf4f951, - 0xf0c1f1, - 0xec3586, - 0x88cb87, - 0xac3c0b, - 0xcc785f, - 0xe88d28, - 0x3ce072, - 0xf41ba1, - 0xa85b78, - 0x9cf387, - 0x34a395, - 0x48437c, - 0xac87a3, - 0x00f76f, - 0xc0f2fb, - 0x1caba7, - 0x60facd, - 0x680927, - 0x78a3e4, - 0x68a86d, - 0xb817c2, - 0xb88d12, - 0x5c8d4e, - 0xe06678, - 0x1c1ac0, - 0xc8f650, - 0x60d9c7, - 0x44fb42, - 0x64a3cb, - 0xd8d1cb, - 0x542696, - 0x14109f, - 0xbc52b7, - 0xe0c97a, - 0x5c95ae, - 0x8cfaba, - 0x0cbc9f, - 0xbc4cc4, - 0x0c1539, - 0x908d6c, - 0x80006e, - 0xb418d1, - 0x1499e2, - 0xe0c767, - 0xa860b6, - 0x24f094, - 0x90b0ed, - 0xc4b301, - 0xe05f45, - 0x483b38, - 0x88e87f, - 0xb853ac, - 0x2c3361, - 0x784f43, - 0x404d7f, - 0xbc926b, - 0x0452f3, - 0x241eeb, - 0xf431c3, - 0x64a5c3, - 0x606944, - 0xe498d6, - 0x0cd746, - 0x440010, - 0x1c9e46, - 0x7c04d0, - 0xbc9fef, - 0x8866a5, - 0x70f087, - 0x886b6e, - 0x4c74bf, - 0x844167, - 0xb4f61c, - 0xe49adc, - 0xb8c111, - 0x3408bc, - 0xd0817a, - 0xc4618b, - 0x68ab1e, - 0x2c61f6, - 0x0026bb, - 0x00254b, - 0x002436, - 0x002332, - 0x002312, - 0x0019e3, - 0x001451, - 0x000a27, - 0x003065, - 0x0050e4, - 0xd023db, - 0xe0b9ba, - 0x3451c9, - 0x8c5877, - 0x9803d8, - 0xc82a14, - 0x88c663, - 0x8c7b9d, - 0x5855ca, - 0xcc08e0, - 0xe80688, - 0x641cb0, - 0x3cdcbc, - 0xf47190, - 0x587a6a, - 0xe4c483, - 0x8cf5a3, - 0x14568e, - 0x8058f8, - 0xf0d7aa, - 0xc49ded, - 0xb0aa36, - 0x2c5bb8, - 0x1c48ce, - 0x24f5aa, - 0xf877b8, - 0x682737, - 0x5056bf, - 0x9097f3, - 0x58c5cb, - 0xacafb9, - 0x30074d, - 0x5c5181, - 0x389af6, - 0xe0aa96, - 0x507705, - 0x2c4053, - 0x084acf, - 0x1cddea, - 0x08152f, - 0xd461da, - 0xc8d083, - 0x88e9fe, - 0x88ae07, - 0x5c0947, - 0x38892c, - 0x40831d, - 0x50bc96, - 0x9ce65e, - 0x90dd5d, - 0x08f69c, - 0x00092d, - 0xf8db7f, - 0xe899c4, - 0x24da9b, - 0x1c56fe, - 0xe4907e, - 0x80c5e6, - 0x800184, - 0xf8cfc5, - 0xc808e9, - 0x206274, - 0x30d587, - 0xc0eefb, - 0x502e5c, - 0x847a88, - 0x0025ae, - 0x002538, - 0x0022a1, - 0x00125a, - 0x9cd917, - 0x9068c3, - 0x408805, - 0xf8f1b6, - 0x001ccc, - 0x94ebcd, - 0xa4e4b8, - 0x389496, - 0x0cb319, - 0x08ee8b, - 0xa89fba, - 0xfc1910, - 0x083d88, - 0x5c2e59, - 0x646cb2, - 0xf884f2, - 0x14b484, - 0x608f5c, - 0x4cbca5, - 0x78595e, - 0xb0d09c, - 0x4ca56d, - 0xa48431, - 0xe4f8ef, - 0x1432d1, - 0xe458e7, - 0x8cbfa6, - 0x7840e4, - 0x9000db, - 0x183a2d, - 0x08373d, - 0x50f520, - 0xa4ebd3, - 0x28987b, - 0xf40e22, - 0x9c3aaf, - 0x0821ef, - 0xa0cbfd, - 0x34145f, - 0x6c8fb5, - 0xac5f3e, - 0x509ea7, - 0xdccf96, - 0x6c2483, - 0xc09727, - 0xd85b2a, - 0xacc33a, - 0x88797e, - 0x00e091, - 0x6cd032, - 0xc041f6, - 0x0017d5, - 0x001247, - 0xe4121d, - 0x684898, - 0xf409d8, - 0xb479a7, - 0x002339, - 0xd487d8, - 0x184617, - 0x5001bb, - 0x380a94, - 0xd857ef, - 0x1c66aa, - 0x58c38b, - 0x001ee2, - 0x001c43, - 0x001d25, - 0x3c5a37, - 0x549b12, - 0x3c8bfe, - 0x00265d, - 0xd4e8b2, - 0x0808c2, - 0xb0c4e7, - 0xd890e8, - 0x34aa8b, - 0x24c696, - 0x181eb0, - 0x20d390, - 0x343111, - 0x34be00, - 0x78521a, - 0x7825ad, - 0xf4d9fb, - 0x0017c9, - 0x00166b, - 0x00166c, - 0xe47cf9, - 0x002454, - 0x20d5bf, - 0x30cda7, - 0xc87e75, - 0x00233a, - 0x60a4d0, - 0x2c0e3d, - 0xd4970b, - 0x64cc2e, - 0xb0e235, - 0x38a4ed, - 0xf48b32, - 0x7c787e, - 0xc0d3c0, - 0x440444, - 0xc09f05, - 0xcc2d83, - 0x38295a, - 0x4c1a3d, - 0xa81b5a, - 0xdc6dcd, - 0x54fa3e, - 0x0c8910, - 0xfcf136, - 0x981dfa, - 0x84a466, - 0x1867b0, - 0xccb11a, - 0xb8bbaf, - 0x60c5ad, - 0x28395e, - 0xc4ae12, - 0xdc74a8, - 0xc087eb, - 0x74f61c, - 0x986f60, - 0x4c189a, - 0x3cf591, - 0x602101, - 0xa89675, - 0x608e08, - 0x7c2edd, - 0x3cf7a4, - 0x342d0d, - 0x94d029, - 0x308454, - 0x4c49e3, - 0x087808, - 0xd03169, - 0xbc5451, - 0x00bf61, - 0xf80cf3, - 0x30766f, - 0x8c3ae3, - 0x78f882, - 0xb4f1da, - 0x0021fb, - 0xd013fd, - 0xa8b86e, - 0x04b167, - 0xd86375, - 0xdcbfe9, - 0x306a85, - 0x2047da, - 0x8035c1, - 0xd02598, - 0xa8667f, - 0x7014a6, - 0x10417f, - 0xac293a, - 0x94e96a, - 0x0c4de9, - 0x907240, - 0xa88808, - 0xc8e0eb, - 0x54e43a, - 0x28e14c, - 0x848e0c, - 0xb03495, - 0xf0f61c, - 0x0c3021, - 0xd89695, - 0x649abe, - 0x5cf5da, - 0x20a2e4, - 0xf02475, - 0x24a074, - 0x8863df, - 0x609217, - 0x34e2fd, - 0x0c3e9f, - 0x6c709f, - 0x6c4008, - 0x5c97f3, - 0x90fd61, - 0x006171, - 0x80e650, - 0xdc2b2a, - 0xb844d9, - 0xe0f5c6, - 0x949426, - 0xcc29f5, - 0x58404e, - 0xdc0c5c, - 0x2c200b, - 0xdca4ca, - 0x8c8fe9, - 0x9810e8, - 0xb49cdf, - 0xa4e975, - 0xc0a53e, - 0x9800c6, - 0x787b8a, - 0x3866f0, - 0x20ee28, - 0x08f4ab, - 0x8c8590, - 0xb48b19, - 0xe49a79, - 0x28a02b, - 0xb44bd2, - 0x2cf0a2, - 0xecadb8, - 0x9801a7, - 0x609ac1, - 0xf07960, - 0x9c8ba0, - 0x4c3275, - 0xe4e4ab, - 0xc8334b, - 0x00f4b9, - 0x0c771a, - 0x74e1b6, - 0x64200c, - 0xc0847a, - 0x183451, - 0xfc253f, - 0x1040f3, - 0x6cc26b, - 0x182032, - 0x70dee2, - 0x00c610, - 0x101c0c, - 0x7cfadf, - 0x5cf938, - 0x3871de, - 0xbc5436, - 0x9c4fda, - 0x1c5cf2, - 0x60fb42, - 0x002500, - 0x00236c, - 0x0021e9, - 0x001ff3, - 0x001f5b, - 0x001e52, - 0x001d4f, - 0x001124, - 0xa8fad8, - 0x5c969d, - 0xe48b7f, - 0x84fcfe, - 0x444c0c, - 0x8c2daa, - 0x6c3e6d, - 0x189efc, - 0xc09f42, - 0xb8f6b1, - 0x406c8f, - 0xa4d1d2, - 0x040cce, - 0xd89e3f, - 0x28e7cf, - 0xc8bcc8, - 0xd8a25e, - 0x90840d, - 0xf81edf, - 0xb0ca68, - 0x98ca33, - 0x68ef43, - 0xcc2db7, - 0xd4a33d, - 0xe4e0a6, - 0x70ef00, - 0x80ad16, - 0x641cae, - 0x14205e, - 0x5c1dd9, - 0x18f1d8, - 0xf86fc1, - 0xf099b6, - 0xdcd3a2, - 0x38e7d8, - 0xd8b377, - 0xb4cef6, - 0xd40b1a, - 0x5882a8, - 0xb4ae2b, - 0x0c413e, - 0xd0929e, - 0x4480eb, - 0xb84fd5, - 0xec59e7, - 0x3059b7, - 0x501ac5, - 0x1cb094, - 0xa0f450, - 0x002248, - 0xec8892, - 0xb07994, - 0x141aa3, - 0xccc3ea, - 0x34bb26, - 0x40786a, - 0xf40b93, - 0x68ed43, - 0x34bb1f, - 0x489d24, - 0x000f86, - 0xacee9e, - 0xc08997, - 0x2827bf, - 0xf05b7b, - 0x7cf90e, - 0xac5a14, - 0xb0c559, - 0xbcd11f, - 0xa0b4a5, - 0x80656d, - 0x48137e, - 0xe83a12, - 0x9c0298, - 0x6c8336, - 0xb8c68e, - 0x74458a, - 0xa49a58, - 0xb4ef39, - 0x14a364, - 0x3ca10d, - 0x206e9c, - 0x183f47, - 0x0c715d, - 0x0c1420, - 0xa80600, - 0x6cf373, - 0x78c3e9, - 0xc83870, - 0x288335, - 0x44783e, - 0x202d07, - 0x98398e, - 0x348a7b, - 0xbc765e, - 0x78009e, - 0x68c44d, - 0xf8e61a, - 0x888322, - 0x84b541, - 0x0015b9, - 0x001df6, - 0xece09b, - 0x606bbd, - 0x0000f0, - 0x4844f7, - 0x1c5a3e, - 0xf47b5e, - 0x008701, - 0xfc4203, - 0x1c232c, - 0xcc61e5, - 0x404e36, - 0x009ec8, - 0xacf7f3, - 0x102ab3, - 0x584498, - 0xa086c6, - 0x7c1dd9, - 0x9893cc, - 0x3ccd93, - 0xf06bca, - 0x3423ba, - 0xd022be, - 0xd02544, - 0xbc20a4, - 0x14f42a, - 0xbc851f, - 0xb85e7b, - 0xc462ea, - 0x0023d6, - 0x002491, - 0x001b98, - 0x44f459, - 0x34c3ac, - 0x94d771, - 0x4c3c16, - 0x9401c2, - 0xb43a28, - 0xd0c1b1, - 0xf008f1, - 0x78471d, - 0x3816d1, - 0xd48890, - 0x002566, - 0x00265f, - 0xacc1ee, - 0x5cba37, - 0x7802f8, - 0x3096fb, - 0xf0ee10, - 0xa43d78, - 0xec01ee, - 0xb83765, - 0xc4576e, - 0x90f1aa, - 0x78bdbc, - 0xd47ae2, - 0x84c0ef, - 0x7c1c68, - 0xd463c6, - 0x508f4c, - 0x7c6456, - 0x448f17, - 0x04d6aa, - 0x9ce063, - 0xf06e0b, - 0x5c865c, - 0xf0b0e7, - 0x209bcd, - 0xcc20e8, - 0x04d13a, - 0x0cf346, - 0x003de8, - 0x485929, - 0x34fcef, - 0x002483, - 0x001c62, - 0x583f54, - 0x40b0fa, - 0xa8922c, - 0x98d6f7, - 0x505527, - 0x0034da, - 0xa09169, - 0x88365f, - 0x9c8c6e, - 0xbcffeb, - 0x685acf, - 0x48746e, - 0x54724f, - 0x04f13e, - 0x600308, - 0x80ea96, - 0x24a2e1, - 0x90b931, - 0x280b5c, - 0xa8968a, - 0x9c04eb, - 0x885395, - 0x80929f, - 0x98b8e3, - 0xd8004d, - 0x98fe94, - 0x68644b, - 0xf099bf, - 0xfce998, - 0x48e9f1, - 0x4c7c5f, - 0x60f81d, - 0x689c70, - 0x2cb43a, - 0x042665, - 0xf4f15a, - 0x207d74, - 0x4c8d79, - 0xfcfc48, - 0x38c986, - 0x70ece4, - 0xd81d72, - 0x94f6a3, - 0x78fd94, - 0x48d705, - 0x7c6df8, - 0x3cab8e, - 0x787e61, - 0xd4f46f, - 0xc88550, - 0xac7f3e, - 0xa4c361, - 0x087045, - 0x40331a, - 0xdc3714, - 0x789f70, - 0x64b0a6, - 0x84fcac, - 0x6c19c0, - 0x20ab37, - 0xc0d012, - 0xd4dccd, - 0x484baa, - 0xf80377, - 0x14bd61, - 0x78886d, - 0xa85c2c, - 0x00db70, - 0xbcec5d, - 0xdc415f, - 0x30636b, - 0x0c5101, - 0x086d41, - 0x04d3cf, - 0x203cae, - 0x748d08, - 0xa03be3, - 0x186590, - 0x0010fa, - 0x000502, - 0xb8782e, - 0xa4d18c, - 0xcc25ef, - 0x68dbca, - 0x044bed, - 0x6c8dc1, - 0x38cada, - 0xf45c89, - 0x581faa, - 0x24ab81, - 0x70cd60, - 0x7cc537, - 0xc42c03, - 0xd83062, - 0x40d32d, - 0x7c6d62, - 0x286ab8, - 0x403cfc, - 0xb8c75d, - 0xe8040b, - 0xe4ce8f, - 0x3c0754, - 0xa46706, - 0x80b03d, - 0xc83c85, - 0xa04ea7, - 0x409c28, - 0x08e689, - 0x4cb199, - 0x98d6bb, - 0x3cd0f8, - 0x7cc3a1, - 0x002608, - 0x001ec2, - 0x001b63, - 0x0017f2, - 0x0016cb, - 0x000393, - 0x804971, - 0x64e682, - 0xb4f7a1, - 0x785dc8, - 0x48c796, - 0x804e70, - 0x3880df, - 0x1094bb, - 0xf01898, - 0x48a91c, - 0xa056f3, - 0x549963, - 0x28ff3c, - 0x902155, - 0x64a769, - 0xbccfcc, - 0xa4516f, - 0x3c8375, - 0x149a10, - 0x0ce725, - 0xc0335e, - 0x20a99b, - 0x4c0bbe, - 0x7c1e52, - 0xdcb4c4, - 0x001dd8, - 0x0017fa, - 0x0003ff, - 0xf8e079, - 0x1430c6, - 0xe0757d, - 0x9cd35b, - 0x60af6d, - 0xb85a73, - 0x103047, - 0x109266, - 0xb047bf, - 0x7c0bc6, - 0x804e81, - 0x244b81, - 0x50a4c8, - 0x8425db, - 0xd8c4e9, - 0x50c8e5, - 0x446d6c, - 0x38d40b, - 0x647791, - 0x781fdb, - 0x08fc88, - 0x30c7ae, - 0x18227e, - 0x00f46f, - 0x9ce6e7, - 0xe498d1, - 0x5cca1a, - 0x70288b, - 0x4849c7, - 0x205ef7, - 0x182666, - 0xc06599, - 0xcc07ab, - 0xe84e84, - 0x50fc9f, - 0xe432cb, - 0x889b39, - 0xbcb1f3, - 0x38ece4, - 0xccf9e8, - 0xf0e77e, - 0x5ce8eb, - 0xb8d9ce, - 0x70f927, - 0x301966, - 0x28bab5, - 0x103b59, - 0x6cb7f4, - 0x001ee1, - 0x0018af, - 0xbc72b1, - 0x78f7be, - 0xf49f54, - 0x00214c, - 0x001632, - 0xd0667b, - 0x001377, - 0x50b7c3, - 0x8018a7, - 0x444e1a, - 0xe8e5d6, - 0x5492be, - 0x101dc0, - 0x0021d1, - 0x68dfdd, - 0xc46ab7, - 0xfc64ba, - 0x2082c0, - 0x3480b3, - 0x7451ba, - 0x64b473, - 0xcc2d8c, - 0x949aa9, - 0x20dbab, - 0x5c9960, - 0x948bc1, - 0x4827ea, - 0x388c50, - 0xa09347, - 0xc8f230, - 0x1c77f6, - 0xe44790, - 0xd4503f, - 0x40163b, - 0x5c497d, - 0xe47dbd, - 0x503da1, - 0x508569, - 0x1077b1, - 0x5cf6dc, - 0x380195, - 0xbc1485, - 0x88d50c, - 0x947be7, - 0x00ec0a, - 0x54bd79, - 0xdc44b6, - 0x1007b6, - 0xc0174d, - 0xa407b6, - 0x149f3c, - 0x88b4a6, - 0x2c5491, - 0x5c70a3, - 0x10f96f, - 0xf01c13, - 0x00aa70, - 0xbcf5ac, - 0xccfa00, - 0xf8a9d0, - 0x805a04, - 0x5caf06, - 0xb81daa, - 0x10f1f2, - 0x0025e5, - 0x0022a9, - 0xc49a02, - 0x344df7, - 0xd41a3f, - 0xcc6ea4, - 0xa46cf1, - 0x0ca8a7, - 0x54b802, - 0x0469f8, - 0xbc6c21, - 0xc869cd, - 0x80d605, - 0x587f57, - 0xa4b805, - 0x70480f, - 0x18f643, - 0x748114, - 0x18ee69, - 0xf0dbe2, - 0xb8098a, - 0x549f13, - 0x2c1f23, - 0x507a55, - 0x9c35eb, - 0xa43135, - 0xd0034b, - 0xa01828, - 0xd0a637, - 0xd04f7e, - 0xd8bb2c, - 0x80be05, - 0xe0b52d, - 0x68ae20, - 0xe8802e, - 0x7c0191, - 0x9c293f, - 0x341298, - 0x903c92, - 0x24240e, - 0xa0999b, - 0xe0f847, - 0x442a60, - 0x1093e9, - 0xdc2b61, - 0xb8ff61, - 0x18e7f4, - 0x78ca39, - 0x5c5948, - 0x60334b, - 0x9027e4, - 0xd49a20, - 0xb09fba, - 0x8c006d, - 0xc06394, - 0x843835, - 0xe4c63d, - 0x54eaa8, - 0xa886dd, - 0xaccf5c, - 0xf0dbf8, - 0x98f0ab, - 0xdc9b9c, - 0x8c2937, - 0xdc86d8, - 0xa88e24, - 0xd8cf9c, - 0x04489a, - 0x3c15c2, - 0x20c9d0, - 0x74e2f5, - 0x842999, - 0x9c207b, - 0x283737, - 0x148fc6, - 0x28cfda, - 0x145a05, - 0xa0edcd, - 0x1ce62b, - 0x3090ab, - 0x7073cb, - 0xf0cba1, - 0x045453, - 0x40b395, - 0x008865, - 0x30f7c5, - 0x20768f, - 0xc0ccf8, - 0x80ed2c, - 0xe8b2ac, - 0x8489ad, - 0x8c8ef2, - 0xf40f24, - 0x84a134, - 0x1c9148, - 0x5cf7e6, - 0xa0d795, - 0xcc088d, - 0x00b362, - 0xf86214, - 0xb0702d, - 0xd0c5f3, - 0x60f445, - 0x5082d5, - 0x9c84bf, - 0x48bf6b, - 0x245ba7, - 0xbca920, - 0xb019c6, - 0x58e28f, - 0xac1f74, - 0x080007, - 0xe425e7, - 0x28cfe9, - 0x9060f1, - 0x741bb2, - 0x28ed6a, - 0x34ab37, - 0x60a37d, - 0x0056cd, - 0x7081eb, - 0x086698, - 0x24f677, - 0x7867d7, - 0x5433cb, - 0xd0d2b0, - 0xd88f76, - 0x3c2ef9, - 0xdc56e7, - 0x347c25, - 0xd4909c, - 0x041e64, - 0x0026b0, - 0x00264a, - 0x0025bc, - 0x0023df, - 0x002241, - 0x000a95, - 0x38e60a, - 0x24181d, - 0xf4c248, - 0xa8515b, - 0xc048e6, - 0xd07714, - 0x749eaf, - 0xb841a4, - 0xf895ea, - 0x50a67f, - 0x647033, - 0x846878 -}; + 0x38f23e, 0x807abf, 0x90e7c4, 0x7c6193, 0x485073, 0x74e28c, 0x8463d6, + 0xd48f33, 0x2c8a72, 0x980d2e, 0xa826d9, 0xd4206d, 0x00155d, 0x806c1b, + 0xa470d6, 0x985fd3, 0x1c69a5, 0x382de8, 0xd087e2, 0x205531, 0x5440ad, + 0x842e27, 0x50f0d3, 0x84119e, 0x08eca9, 0x10d38a, 0x382dd1, 0xe0cbee, + 0x64b853, 0xf4428f, 0x188331, 0x8455a5, 0xa87c01, 0xc01173, 0xbce63f, + 0xb857d8, 0x94b10a, 0xe458b8, 0x088c2c, 0xb86ce8, 0x9c65b0, 0xc8a823, + 0xc44202, 0xd059e4, 0x64b310, 0x9476b7, 0x8c1abf, 0xb47443, 0x30cbf8, + 0x182195, 0xa88195, 0x88add2, 0xd0fccc, 0x14c913, 0x4c6641, 0x3cbdd8, + 0x38256b, 0x849866, 0xe89309, 0x0016db, 0x5c3c27, 0x10d542, 0xa0821f, + 0xc45006, 0x88329b, 0xbc8ccd, 0x400e85, 0xec9bf3, 0xf8042e, 0x843838, + 0x54880e, 0xbc79ad, 0x30d6c9, 0xb0df3a, 0x805719, 0x78a873, 0x041bba, + 0x08fd0e, 0x08d42b, 0x00e3b2, 0xc81479, 0xf0728c, 0x94350a, 0x001fcd, + 0xd0dfc7, 0x1c62b8, 0x18e2c2, 0x001a8a, 0x002567, 0xa8f274, 0x001599, + 0x0012fb, 0x7cf854, 0x8cc8cd, 0xe81132, 0xa02195, 0x8c71f8, 0x04180f, + 0x9463d1, 0x0cdfa4, 0xcc051b, 0x68ebae, 0x60d0a9, 0x60a10a, 0xa07591, + 0x001fcc, 0xec107b, 0xa01081, 0xf4f524, 0x9c99a0, 0x185936, 0x98fae3, + 0x640980, 0x8cbebe, 0xf8a45f, 0xbc8385, 0x900628, 0xd4ae05, 0x3c0518, + 0xc40bcb, 0xe8bba8, 0xbc3aea, 0x8c0ee3, 0x6c5c14, 0x78abbb, 0x1816c9, + 0xfc8f90, 0x244b03, 0x988389, 0x14bb6e, 0x1c3ade, 0xf83f51, 0xd8e0e1, + 0xecf342, 0x5092b9, 0xb4bff6, 0xc8d7b0, 0x982d68, 0xecd09f, 0xe446da, + 0xf4f5db, 0xd80831, 0xdc5583, 0x2c54cf, 0x001fe3, 0x0026e2, 0x001e75, + 0x6cd68a, 0x2021a5, 0x0c4885, 0xdc0b34, 0xac0d1b, 0x60e3ac, 0xf895c7, + 0xc4438f, 0xa816b2, 0xe892a4, 0x700514, 0x88c9d0, 0x2c598a, 0x18f0e4, + 0xec8350, 0x4cdd31, 0x703eac, 0x6c94f8, 0x84788b, 0x2cf0ee, 0x68d93c, + 0x24e314, 0x28f076, 0x285aeb, 0x087402, 0xccc760, 0x705aac, 0xfc643a, + 0xd4e6b7, 0x2802d8, 0x9c2ea1, 0x68967b, 0xbc6778, 0xa82066, 0xb065bd, + 0xf0dce2, 0x7cd1c3, 0x705681, 0x2cbe08, 0x783a84, 0x84b153, 0x6476ba, + 0x54ae27, 0xf437b7, 0xf0d1a9, 0x34c059, 0x04f7e4, 0x10ddb1, 0xb4f0ab, + 0x848506, 0x7831c1, 0x8c7c92, 0xd0e140, 0xacfdec, 0xf82793, 0x40a6d9, + 0x109add, 0xf0b479, 0x58b035, 0x34159e, 0x286aba, 0xec852f, 0x44d884, + 0x003ee1, 0x7c11be, 0x04e536, 0x881fa1, 0x04db56, 0x9cfc01, 0xc81ee7, + 0x34363b, 0xc01ada, 0xacbc32, 0x70700d, 0x7c5049, 0x503237, 0xd4619d, + 0xb0481a, 0x989e63, 0xdca904, 0x48a195, 0x6cab31, 0x6c96cf, 0x3035ad, + 0xa8be27, 0xb8634d, 0x9ce33f, 0xf0989d, 0xace4b5, 0xe42b34, 0x1c36bb, + 0x3c2eff, 0xf0766f, 0x40cbc0, 0x4098ad, 0x6c4d73, 0xc48466, 0xd02b20, + 0x3010e4, 0x380f4a, 0x685b35, 0xc86f1d, 0x701124, 0x38484c, 0x041552, + 0x786c1c, 0xbc3baf, 0x00a040, 0x60fec5, 0xcc4463, 0x6c72e7, 0x18af61, + 0x00cdfe, 0xac61ea, 0x38b54d, 0x60c547, 0x28e02c, 0x50ead6, 0x4860bc, + 0x403004, 0x0c74c2, 0xa4b197, 0x7cf05f, 0xa4f1e8, 0x70a2b3, 0x4c57ca, + 0x68fb7e, 0x90c1c6, 0x9cf48e, 0xfcd848, 0x64b9e8, 0x001cb3, 0x000d93, + 0x30d9d9, 0x6030d4, 0x94bf2d, 0xc49880, 0xe0338e, 0x68fef7, 0xbce143, + 0x645aed, 0xc0b658, 0x881908, 0xfc2a9c, 0x48605f, 0x188796, 0x002376, + 0x84100d, 0x04c23e, 0x5c5188, 0xe89120, 0x9c6c15, 0x4886e8, 0x2c2997, + 0x102f6b, 0x00eebd, 0x281878, 0x6045bd, 0x7ced8d, 0xe85b5b, 0x000d3a, + 0xe09861, 0xf4f1e1, 0x60beb5, 0xb4e1c4, 0x70aab2, 0x0026ff, 0x406f2a, + 0x002557, 0xf05a09, 0x503275, 0x28cc01, 0xb46293, 0x04fe31, 0x845181, + 0xd831cf, 0xf8d0bd, 0xfcc734, 0xe4b021, 0xb0ec71, 0x3cbbfd, 0x2cae2b, + 0xc488e5, 0x7c9122, 0xe8b4c8, 0x18895b, 0xe0db10, 0xe09971, 0x6077e2, + 0x680571, 0x6c2f2c, 0x300d43, 0x6c2779, 0x607edd, 0x9c2a83, 0xe45d75, + 0xe4faed, 0xc83f26, 0x54f201, 0xa06090, 0xac3743, 0x141f78, 0x006f64, + 0xdc6672, 0x001e7d, 0x3c6200, 0x0024e9, 0x002399, 0xe4e0c5, 0xe8039a, + 0xc4731e, 0x8c7712, 0x2013e0, 0x0007ab, 0x0021d2, 0xbc4760, 0xd0176a, + 0x2cbaba, 0x24920e, 0x40d3ae, 0xf01dbc, 0x24dbed, 0xac3613, 0x1449e0, + 0xc0bdd1, 0xe8508b, 0xf025b7, 0xc8ba94, 0xec1f72, 0x9852b1, 0x1489fd, + 0xccfe3c, 0x789ed0, 0xe440e2, 0x1caf05, 0xe492fb, 0x0073e0, 0xbc4486, + 0x380b40, 0x002490, 0x0023d7, 0xfca13e, 0xa00798, 0x945103, 0xc819f7, + 0x2c4401, 0x28e31f, 0x0c1daf, 0x14f65a, 0x742344, 0xf0b429, 0xec51bc, + 0xf079e8, 0x887598, 0xd0b128, 0xd00401, 0xf06d78, 0x10683f, 0x74a722, + 0x58a2b5, 0x64899a, 0x88074b, 0x64bc0c, 0xa039f7, 0x041b6d, 0x001f6b, + 0x30b4b8, 0x503cea, 0x0c9838, 0x54fcf0, 0x08aed6, 0xa816d0, 0x88bd45, + 0x544e90, 0xd03311, 0x70e72c, 0xc0cecd, 0x98e0d9, 0xe0accb, 0x78d75f, + 0x2078f0, 0x985aeb, 0xa45e60, 0x006d52, 0x5cadcf, 0xb8e856, 0x90b21f, + 0xa8bbcf, 0xc8b5b7, 0x18af8f, 0xf4f951, 0xf0c1f1, 0xec3586, 0x88cb87, + 0xac3c0b, 0xcc785f, 0xe88d28, 0x3ce072, 0xf41ba1, 0xa85b78, 0x9cf387, + 0x34a395, 0x48437c, 0xac87a3, 0x00f76f, 0xc0f2fb, 0x1caba7, 0x60facd, + 0x680927, 0x78a3e4, 0x68a86d, 0xb817c2, 0xb88d12, 0x5c8d4e, 0xe06678, + 0x1c1ac0, 0xc8f650, 0x60d9c7, 0x44fb42, 0x64a3cb, 0xd8d1cb, 0x542696, + 0x14109f, 0xbc52b7, 0xe0c97a, 0x5c95ae, 0x8cfaba, 0x0cbc9f, 0xbc4cc4, + 0x0c1539, 0x908d6c, 0x80006e, 0xb418d1, 0x1499e2, 0xe0c767, 0xa860b6, + 0x24f094, 0x90b0ed, 0xc4b301, 0xe05f45, 0x483b38, 0x88e87f, 0xb853ac, + 0x2c3361, 0x784f43, 0x404d7f, 0xbc926b, 0x0452f3, 0x241eeb, 0xf431c3, + 0x64a5c3, 0x606944, 0xe498d6, 0x0cd746, 0x440010, 0x1c9e46, 0x7c04d0, + 0xbc9fef, 0x8866a5, 0x70f087, 0x886b6e, 0x4c74bf, 0x844167, 0xb4f61c, + 0xe49adc, 0xb8c111, 0x3408bc, 0xd0817a, 0xc4618b, 0x68ab1e, 0x2c61f6, + 0x0026bb, 0x00254b, 0x002436, 0x002332, 0x002312, 0x0019e3, 0x001451, + 0x000a27, 0x003065, 0x0050e4, 0xd023db, 0xe0b9ba, 0x3451c9, 0x8c5877, + 0x9803d8, 0xc82a14, 0x88c663, 0x8c7b9d, 0x5855ca, 0xcc08e0, 0xe80688, + 0x641cb0, 0x3cdcbc, 0xf47190, 0x587a6a, 0xe4c483, 0x8cf5a3, 0x14568e, + 0x8058f8, 0xf0d7aa, 0xc49ded, 0xb0aa36, 0x2c5bb8, 0x1c48ce, 0x24f5aa, + 0xf877b8, 0x682737, 0x5056bf, 0x9097f3, 0x58c5cb, 0xacafb9, 0x30074d, + 0x5c5181, 0x389af6, 0xe0aa96, 0x507705, 0x2c4053, 0x084acf, 0x1cddea, + 0x08152f, 0xd461da, 0xc8d083, 0x88e9fe, 0x88ae07, 0x5c0947, 0x38892c, + 0x40831d, 0x50bc96, 0x9ce65e, 0x90dd5d, 0x08f69c, 0x00092d, 0xf8db7f, + 0xe899c4, 0x24da9b, 0x1c56fe, 0xe4907e, 0x80c5e6, 0x800184, 0xf8cfc5, + 0xc808e9, 0x206274, 0x30d587, 0xc0eefb, 0x502e5c, 0x847a88, 0x0025ae, + 0x002538, 0x0022a1, 0x00125a, 0x9cd917, 0x9068c3, 0x408805, 0xf8f1b6, + 0x001ccc, 0x94ebcd, 0xa4e4b8, 0x389496, 0x0cb319, 0x08ee8b, 0xa89fba, + 0xfc1910, 0x083d88, 0x5c2e59, 0x646cb2, 0xf884f2, 0x14b484, 0x608f5c, + 0x4cbca5, 0x78595e, 0xb0d09c, 0x4ca56d, 0xa48431, 0xe4f8ef, 0x1432d1, + 0xe458e7, 0x8cbfa6, 0x7840e4, 0x9000db, 0x183a2d, 0x08373d, 0x50f520, + 0xa4ebd3, 0x28987b, 0xf40e22, 0x9c3aaf, 0x0821ef, 0xa0cbfd, 0x34145f, + 0x6c8fb5, 0xac5f3e, 0x509ea7, 0xdccf96, 0x6c2483, 0xc09727, 0xd85b2a, + 0xacc33a, 0x88797e, 0x00e091, 0x6cd032, 0xc041f6, 0x0017d5, 0x001247, + 0xe4121d, 0x684898, 0xf409d8, 0xb479a7, 0x002339, 0xd487d8, 0x184617, + 0x5001bb, 0x380a94, 0xd857ef, 0x1c66aa, 0x58c38b, 0x001ee2, 0x001c43, + 0x001d25, 0x3c5a37, 0x549b12, 0x3c8bfe, 0x00265d, 0xd4e8b2, 0x0808c2, + 0xb0c4e7, 0xd890e8, 0x34aa8b, 0x24c696, 0x181eb0, 0x20d390, 0x343111, + 0x34be00, 0x78521a, 0x7825ad, 0xf4d9fb, 0x0017c9, 0x00166b, 0x00166c, + 0xe47cf9, 0x002454, 0x20d5bf, 0x30cda7, 0xc87e75, 0x00233a, 0x60a4d0, + 0x2c0e3d, 0xd4970b, 0x64cc2e, 0xb0e235, 0x38a4ed, 0xf48b32, 0x7c787e, + 0xc0d3c0, 0x440444, 0xc09f05, 0xcc2d83, 0x38295a, 0x4c1a3d, 0xa81b5a, + 0xdc6dcd, 0x54fa3e, 0x0c8910, 0xfcf136, 0x981dfa, 0x84a466, 0x1867b0, + 0xccb11a, 0xb8bbaf, 0x60c5ad, 0x28395e, 0xc4ae12, 0xdc74a8, 0xc087eb, + 0x74f61c, 0x986f60, 0x4c189a, 0x3cf591, 0x602101, 0xa89675, 0x608e08, + 0x7c2edd, 0x3cf7a4, 0x342d0d, 0x94d029, 0x308454, 0x4c49e3, 0x087808, + 0xd03169, 0xbc5451, 0x00bf61, 0xf80cf3, 0x30766f, 0x8c3ae3, 0x78f882, + 0xb4f1da, 0x0021fb, 0xd013fd, 0xa8b86e, 0x04b167, 0xd86375, 0xdcbfe9, + 0x306a85, 0x2047da, 0x8035c1, 0xd02598, 0xa8667f, 0x7014a6, 0x10417f, + 0xac293a, 0x94e96a, 0x0c4de9, 0x907240, 0xa88808, 0xc8e0eb, 0x54e43a, + 0x28e14c, 0x848e0c, 0xb03495, 0xf0f61c, 0x0c3021, 0xd89695, 0x649abe, + 0x5cf5da, 0x20a2e4, 0xf02475, 0x24a074, 0x8863df, 0x609217, 0x34e2fd, + 0x0c3e9f, 0x6c709f, 0x6c4008, 0x5c97f3, 0x90fd61, 0x006171, 0x80e650, + 0xdc2b2a, 0xb844d9, 0xe0f5c6, 0x949426, 0xcc29f5, 0x58404e, 0xdc0c5c, + 0x2c200b, 0xdca4ca, 0x8c8fe9, 0x9810e8, 0xb49cdf, 0xa4e975, 0xc0a53e, + 0x9800c6, 0x787b8a, 0x3866f0, 0x20ee28, 0x08f4ab, 0x8c8590, 0xb48b19, + 0xe49a79, 0x28a02b, 0xb44bd2, 0x2cf0a2, 0xecadb8, 0x9801a7, 0x609ac1, + 0xf07960, 0x9c8ba0, 0x4c3275, 0xe4e4ab, 0xc8334b, 0x00f4b9, 0x0c771a, + 0x74e1b6, 0x64200c, 0xc0847a, 0x183451, 0xfc253f, 0x1040f3, 0x6cc26b, + 0x182032, 0x70dee2, 0x00c610, 0x101c0c, 0x7cfadf, 0x5cf938, 0x3871de, + 0xbc5436, 0x9c4fda, 0x1c5cf2, 0x60fb42, 0x002500, 0x00236c, 0x0021e9, + 0x001ff3, 0x001f5b, 0x001e52, 0x001d4f, 0x001124, 0xa8fad8, 0x5c969d, + 0xe48b7f, 0x84fcfe, 0x444c0c, 0x8c2daa, 0x6c3e6d, 0x189efc, 0xc09f42, + 0xb8f6b1, 0x406c8f, 0xa4d1d2, 0x040cce, 0xd89e3f, 0x28e7cf, 0xc8bcc8, + 0xd8a25e, 0x90840d, 0xf81edf, 0xb0ca68, 0x98ca33, 0x68ef43, 0xcc2db7, + 0xd4a33d, 0xe4e0a6, 0x70ef00, 0x80ad16, 0x641cae, 0x14205e, 0x5c1dd9, + 0x18f1d8, 0xf86fc1, 0xf099b6, 0xdcd3a2, 0x38e7d8, 0xd8b377, 0xb4cef6, + 0xd40b1a, 0x5882a8, 0xb4ae2b, 0x0c413e, 0xd0929e, 0x4480eb, 0xb84fd5, + 0xec59e7, 0x3059b7, 0x501ac5, 0x1cb094, 0xa0f450, 0x002248, 0xec8892, + 0xb07994, 0x141aa3, 0xccc3ea, 0x34bb26, 0x40786a, 0xf40b93, 0x68ed43, + 0x34bb1f, 0x489d24, 0x000f86, 0xacee9e, 0xc08997, 0x2827bf, 0xf05b7b, + 0x7cf90e, 0xac5a14, 0xb0c559, 0xbcd11f, 0xa0b4a5, 0x80656d, 0x48137e, + 0xe83a12, 0x9c0298, 0x6c8336, 0xb8c68e, 0x74458a, 0xa49a58, 0xb4ef39, + 0x14a364, 0x3ca10d, 0x206e9c, 0x183f47, 0x0c715d, 0x0c1420, 0xa80600, + 0x6cf373, 0x78c3e9, 0xc83870, 0x288335, 0x44783e, 0x202d07, 0x98398e, + 0x348a7b, 0xbc765e, 0x78009e, 0x68c44d, 0xf8e61a, 0x888322, 0x84b541, + 0x0015b9, 0x001df6, 0xece09b, 0x606bbd, 0x0000f0, 0x4844f7, 0x1c5a3e, + 0xf47b5e, 0x008701, 0xfc4203, 0x1c232c, 0xcc61e5, 0x404e36, 0x009ec8, + 0xacf7f3, 0x102ab3, 0x584498, 0xa086c6, 0x7c1dd9, 0x9893cc, 0x3ccd93, + 0xf06bca, 0x3423ba, 0xd022be, 0xd02544, 0xbc20a4, 0x14f42a, 0xbc851f, + 0xb85e7b, 0xc462ea, 0x0023d6, 0x002491, 0x001b98, 0x44f459, 0x34c3ac, + 0x94d771, 0x4c3c16, 0x9401c2, 0xb43a28, 0xd0c1b1, 0xf008f1, 0x78471d, + 0x3816d1, 0xd48890, 0x002566, 0x00265f, 0xacc1ee, 0x5cba37, 0x7802f8, + 0x3096fb, 0xf0ee10, 0xa43d78, 0xec01ee, 0xb83765, 0xc4576e, 0x90f1aa, + 0x78bdbc, 0xd47ae2, 0x84c0ef, 0x7c1c68, 0xd463c6, 0x508f4c, 0x7c6456, + 0x448f17, 0x04d6aa, 0x9ce063, 0xf06e0b, 0x5c865c, 0xf0b0e7, 0x209bcd, + 0xcc20e8, 0x04d13a, 0x0cf346, 0x003de8, 0x485929, 0x34fcef, 0x002483, + 0x001c62, 0x583f54, 0x40b0fa, 0xa8922c, 0x98d6f7, 0x505527, 0x0034da, + 0xa09169, 0x88365f, 0x9c8c6e, 0xbcffeb, 0x685acf, 0x48746e, 0x54724f, + 0x04f13e, 0x600308, 0x80ea96, 0x24a2e1, 0x90b931, 0x280b5c, 0xa8968a, + 0x9c04eb, 0x885395, 0x80929f, 0x98b8e3, 0xd8004d, 0x98fe94, 0x68644b, + 0xf099bf, 0xfce998, 0x48e9f1, 0x4c7c5f, 0x60f81d, 0x689c70, 0x2cb43a, + 0x042665, 0xf4f15a, 0x207d74, 0x4c8d79, 0xfcfc48, 0x38c986, 0x70ece4, + 0xd81d72, 0x94f6a3, 0x78fd94, 0x48d705, 0x7c6df8, 0x3cab8e, 0x787e61, + 0xd4f46f, 0xc88550, 0xac7f3e, 0xa4c361, 0x087045, 0x40331a, 0xdc3714, + 0x789f70, 0x64b0a6, 0x84fcac, 0x6c19c0, 0x20ab37, 0xc0d012, 0xd4dccd, + 0x484baa, 0xf80377, 0x14bd61, 0x78886d, 0xa85c2c, 0x00db70, 0xbcec5d, + 0xdc415f, 0x30636b, 0x0c5101, 0x086d41, 0x04d3cf, 0x203cae, 0x748d08, + 0xa03be3, 0x186590, 0x0010fa, 0x000502, 0xb8782e, 0xa4d18c, 0xcc25ef, + 0x68dbca, 0x044bed, 0x6c8dc1, 0x38cada, 0xf45c89, 0x581faa, 0x24ab81, + 0x70cd60, 0x7cc537, 0xc42c03, 0xd83062, 0x40d32d, 0x7c6d62, 0x286ab8, + 0x403cfc, 0xb8c75d, 0xe8040b, 0xe4ce8f, 0x3c0754, 0xa46706, 0x80b03d, + 0xc83c85, 0xa04ea7, 0x409c28, 0x08e689, 0x4cb199, 0x98d6bb, 0x3cd0f8, + 0x7cc3a1, 0x002608, 0x001ec2, 0x001b63, 0x0017f2, 0x0016cb, 0x000393, + 0x804971, 0x64e682, 0xb4f7a1, 0x785dc8, 0x48c796, 0x804e70, 0x3880df, + 0x1094bb, 0xf01898, 0x48a91c, 0xa056f3, 0x549963, 0x28ff3c, 0x902155, + 0x64a769, 0xbccfcc, 0xa4516f, 0x3c8375, 0x149a10, 0x0ce725, 0xc0335e, + 0x20a99b, 0x4c0bbe, 0x7c1e52, 0xdcb4c4, 0x001dd8, 0x0017fa, 0x0003ff, + 0xf8e079, 0x1430c6, 0xe0757d, 0x9cd35b, 0x60af6d, 0xb85a73, 0x103047, + 0x109266, 0xb047bf, 0x7c0bc6, 0x804e81, 0x244b81, 0x50a4c8, 0x8425db, + 0xd8c4e9, 0x50c8e5, 0x446d6c, 0x38d40b, 0x647791, 0x781fdb, 0x08fc88, + 0x30c7ae, 0x18227e, 0x00f46f, 0x9ce6e7, 0xe498d1, 0x5cca1a, 0x70288b, + 0x4849c7, 0x205ef7, 0x182666, 0xc06599, 0xcc07ab, 0xe84e84, 0x50fc9f, + 0xe432cb, 0x889b39, 0xbcb1f3, 0x38ece4, 0xccf9e8, 0xf0e77e, 0x5ce8eb, + 0xb8d9ce, 0x70f927, 0x301966, 0x28bab5, 0x103b59, 0x6cb7f4, 0x001ee1, + 0x0018af, 0xbc72b1, 0x78f7be, 0xf49f54, 0x00214c, 0x001632, 0xd0667b, + 0x001377, 0x50b7c3, 0x8018a7, 0x444e1a, 0xe8e5d6, 0x5492be, 0x101dc0, + 0x0021d1, 0x68dfdd, 0xc46ab7, 0xfc64ba, 0x2082c0, 0x3480b3, 0x7451ba, + 0x64b473, 0xcc2d8c, 0x949aa9, 0x20dbab, 0x5c9960, 0x948bc1, 0x4827ea, + 0x388c50, 0xa09347, 0xc8f230, 0x1c77f6, 0xe44790, 0xd4503f, 0x40163b, + 0x5c497d, 0xe47dbd, 0x503da1, 0x508569, 0x1077b1, 0x5cf6dc, 0x380195, + 0xbc1485, 0x88d50c, 0x947be7, 0x00ec0a, 0x54bd79, 0xdc44b6, 0x1007b6, + 0xc0174d, 0xa407b6, 0x149f3c, 0x88b4a6, 0x2c5491, 0x5c70a3, 0x10f96f, + 0xf01c13, 0x00aa70, 0xbcf5ac, 0xccfa00, 0xf8a9d0, 0x805a04, 0x5caf06, + 0xb81daa, 0x10f1f2, 0x0025e5, 0x0022a9, 0xc49a02, 0x344df7, 0xd41a3f, + 0xcc6ea4, 0xa46cf1, 0x0ca8a7, 0x54b802, 0x0469f8, 0xbc6c21, 0xc869cd, + 0x80d605, 0x587f57, 0xa4b805, 0x70480f, 0x18f643, 0x748114, 0x18ee69, + 0xf0dbe2, 0xb8098a, 0x549f13, 0x2c1f23, 0x507a55, 0x9c35eb, 0xa43135, + 0xd0034b, 0xa01828, 0xd0a637, 0xd04f7e, 0xd8bb2c, 0x80be05, 0xe0b52d, + 0x68ae20, 0xe8802e, 0x7c0191, 0x9c293f, 0x341298, 0x903c92, 0x24240e, + 0xa0999b, 0xe0f847, 0x442a60, 0x1093e9, 0xdc2b61, 0xb8ff61, 0x18e7f4, + 0x78ca39, 0x5c5948, 0x60334b, 0x9027e4, 0xd49a20, 0xb09fba, 0x8c006d, + 0xc06394, 0x843835, 0xe4c63d, 0x54eaa8, 0xa886dd, 0xaccf5c, 0xf0dbf8, + 0x98f0ab, 0xdc9b9c, 0x8c2937, 0xdc86d8, 0xa88e24, 0xd8cf9c, 0x04489a, + 0x3c15c2, 0x20c9d0, 0x74e2f5, 0x842999, 0x9c207b, 0x283737, 0x148fc6, + 0x28cfda, 0x145a05, 0xa0edcd, 0x1ce62b, 0x3090ab, 0x7073cb, 0xf0cba1, + 0x045453, 0x40b395, 0x008865, 0x30f7c5, 0x20768f, 0xc0ccf8, 0x80ed2c, + 0xe8b2ac, 0x8489ad, 0x8c8ef2, 0xf40f24, 0x84a134, 0x1c9148, 0x5cf7e6, + 0xa0d795, 0xcc088d, 0x00b362, 0xf86214, 0xb0702d, 0xd0c5f3, 0x60f445, + 0x5082d5, 0x9c84bf, 0x48bf6b, 0x245ba7, 0xbca920, 0xb019c6, 0x58e28f, + 0xac1f74, 0x080007, 0xe425e7, 0x28cfe9, 0x9060f1, 0x741bb2, 0x28ed6a, + 0x34ab37, 0x60a37d, 0x0056cd, 0x7081eb, 0x086698, 0x24f677, 0x7867d7, + 0x5433cb, 0xd0d2b0, 0xd88f76, 0x3c2ef9, 0xdc56e7, 0x347c25, 0xd4909c, + 0x041e64, 0x0026b0, 0x00264a, 0x0025bc, 0x0023df, 0x002241, 0x000a95, + 0x38e60a, 0x24181d, 0xf4c248, 0xa8515b, 0xc048e6, 0xd07714, 0x749eaf, + 0xb841a4, 0xf895ea, 0x50a67f, 0x647033, 0x846878};