├── .github └── workflows │ └── PioBuild.yaml ├── .gitignore ├── .vscode └── extensions.json ├── CMakeLists.txt ├── doc ├── HomeAssistantSnippets.md └── screenshots │ ├── bt_monitor_page.png │ ├── main_menu.jpg │ └── parameter_page.jpg ├── include ├── BluetoothParameter.h ├── BluetoothScanner.h ├── CallbackHelper.h ├── NestedStruct.h ├── TelnetSerial.h ├── WiFiComponent.h ├── led.h ├── mqtt.h ├── parameter.h └── stackDbgHelper.h ├── partitions_custom.csv ├── platformio.ini ├── readme.md ├── sdkconfig.defaults └── src ├── BluetoothScanner.cpp ├── CMakeLists.txt ├── TelnetSerial.cpp ├── WiFiComponent.cpp ├── main.cpp ├── mqtt.cpp └── parameter.cpp /.github/workflows/PioBuild.yaml: -------------------------------------------------------------------------------- 1 | name: Build PlatformIO Project 2 | 3 | on: 4 | push: 5 | branches: 6 | - '**' 7 | workflow_dispatch: 8 | 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@v3 16 | - name: Cache pip 17 | uses: actions/cache@v2 18 | with: 19 | path: ~/.cache/pip 20 | key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} 21 | restore-keys: | 22 | ${{ runner.os }}-pip- 23 | - name: Cache PlatformIO 24 | uses: actions/cache@v2 25 | with: 26 | path: ~/.platformio 27 | key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }} 28 | - name: Set up Python 29 | uses: actions/setup-python@v4 30 | with: 31 | python-version: '3.8' 32 | - name: Install PlatformIO 33 | run: | 34 | python -m pip install --upgrade pip 35 | pip install --upgrade platformio 36 | 37 | 38 | - name: Run PlatformIO 39 | run: pio run -e wemos_d1_mini32 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # These are some examples of commonly ignored file patterns. 2 | # You should customize this list as applicable to your project. 3 | # Learn more about .gitignore: 4 | # https://www.atlassian.com/git/tutorials/saving-changes/gitignore 5 | 6 | 7 | .pio 8 | 9 | # Node artifact files 10 | node_modules/ 11 | dist/ 12 | 13 | # Compiled Java class files 14 | *.class 15 | 16 | # Compiled Python bytecode 17 | *.py[cod] 18 | 19 | # Log files 20 | *.log 21 | 22 | # Package files 23 | *.jar 24 | 25 | # Maven 26 | target/ 27 | dist/ 28 | 29 | # JetBrains IDE 30 | .idea/ 31 | 32 | # Unit test reports 33 | TEST*.xml 34 | 35 | # Generated by MacOS 36 | .DS_Store 37 | 38 | # Generated by Windows 39 | Thumbs.db 40 | 41 | # Applications 42 | *.app 43 | *.exe 44 | *.war 45 | 46 | # Large media files 47 | *.mp4 48 | *.tiff 49 | *.avi 50 | *.flv 51 | *.mov 52 | *.wmv 53 | 54 | /.vscode/launch.json 55 | /.vscode/c_cpp_properties.json 56 | .vscode/settings.json 57 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": [ 5 | "platformio.platformio-ide" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # The following lines of boilerplate have to be in your project's CMakeLists 2 | # in this exact order for cmake to work correctly 3 | cmake_minimum_required(VERSION 3.16.0) 4 | 5 | include($ENV{IDF_PATH}/tools/cmake/project.cmake) 6 | project(Esp32_bluetoothClassicScan) 7 | -------------------------------------------------------------------------------- /doc/HomeAssistantSnippets.md: -------------------------------------------------------------------------------- 1 | 2 | I use the ESP32 bluetooth presence scans to provide presence information to HomeAssistant. 3 | 4 | Here's an example of a Bluetooth presence sensor for a _ESP32-MQTT-Bluetooth-Monitor_ device with Identity ```ESP32-bluetooth-monitor```. 5 | The device that the confidence is reported for has alias ```s10```. When the Bluetooth scanner node goes down, this is also reflected in the presence sensor. 6 | This snippet belongs in the ```sensor:``` section of the HomeAssistant configuration. 7 | ```yaml 8 | - platform: mqtt 9 | state_topic: 'monitor/ESP32-bluetooth-monitor/s10' 10 | value_template: '{{ value_json.confidence }}' 11 | unit_of_measurement: '%' 12 | availability: 13 | - topic: "monitor/ESP32-bluetooth-monitor/status" 14 | payload_available: "online" 15 | payload_not_available: "offline" 16 | name: 'ESP32 S10 bluetooth presence confidence' 17 | ``` 18 | 19 | 20 | The following snippet is an automation used to trigger _Arrival_ / _Departure_ scans based on door open / close triggers. 21 | For my household where 4 devices are in the Known Devices list, precense is generally detected within about __2.5 seconds__ of the door being opened! 22 | 23 | ```yaml 24 | - id: 'bt_presence_scan_on_door_open_close' 25 | alias: Bluetooth Presence scan on Door open/close 26 | description: Scan for bluetooth arrival / departure on door open / close events 27 | trigger: 28 | # Any door sensor to trigger the scan for: 29 | - platform: state 30 | entity_id: binary_sensor.voordeur_contact 31 | - platform: state 32 | entity_id: binary_sensor.achterdeur_contact 33 | condition: [] 34 | action: 35 | # Either scan for departure or arrivals 36 | - choose: 37 | - conditions: 38 | - condition: template 39 | value_template: "{{ trigger.to_state.state == 'off' }}" 40 | # Door close: delayed scan for departing devices 41 | sequence: 42 | - delay: 00:02:00 43 | - service: mqtt.publish 44 | data: 45 | topic: monitor/scan/DEPART 46 | # Door open: scan for new devices 47 | default: 48 | - service: mqtt.publish 49 | data: 50 | topic: monitor/scan/ARRIVE 51 | mode: parallel 52 | max: 3 53 | max_exceeded: silent 54 | ``` 55 | 56 | I've newly added passive ble iBeacon scan support which will also support rssi value reporting. Below is an example to get to that data using an MQTT sensor in HomeAssistant. This example uses an _ESP32-MQTT-Bluetooth-Monitor_ device with Identity ```ESP32-bluetooth-monitor```. 57 | The device that the confidence is reported for has alias ```s10-iBeacon```. 58 | This snippet belongs in the ```sensor:``` section of the HomeAssistant configuration. 59 | ```yaml 60 | - platform: mqtt 61 | state_topic: 'monitor/ESP32-bluetooth-monitor/s10-iBeacon' 62 | value_template: '{{ value_json.rssi }}' 63 | unit_of_measurement: 'dBm' 64 | availability: 65 | - topic: "monitor/ESP32-bluetooth-monitor/status" 66 | payload_available: "online" 67 | payload_not_available: "offline" 68 | name: 'ESP32 S10 iBeacon RSSI' 69 | ``` 70 | 71 | -------------------------------------------------------------------------------- /doc/screenshots/bt_monitor_page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboMagus/ESP32-MQTT-Bluetooth-Monitor/a1b736af58aa088100e51a006ae3a8a09ac60d31/doc/screenshots/bt_monitor_page.png -------------------------------------------------------------------------------- /doc/screenshots/main_menu.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboMagus/ESP32-MQTT-Bluetooth-Monitor/a1b736af58aa088100e51a006ae3a8a09ac60d31/doc/screenshots/main_menu.jpg -------------------------------------------------------------------------------- /doc/screenshots/parameter_page.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RoboMagus/ESP32-MQTT-Bluetooth-Monitor/a1b736af58aa088100e51a006ae3a8a09ac60d31/doc/screenshots/parameter_page.jpg -------------------------------------------------------------------------------- /include/BluetoothParameter.h: -------------------------------------------------------------------------------- 1 | #ifndef BLUETOOTH_PARAMETER_H 2 | #define BLUETOOTH_PARAMETER_H 3 | 4 | #include "WString.h" 5 | #include "parameter.h" 6 | 7 | 8 | static const char MAC_ADDR[] PROGMEM = "MAC address / iBeacon UUID"; 9 | static const char ALIAS [] PROGMEM = "Alias"; 10 | 11 | 12 | class BluetoothParameter { 13 | public: 14 | BluetoothParameter(uint8_t index) : 15 | bm_mac_addr (alias_id+1, MAC_ADDR, "", 40), 16 | bm_mac_alias (alias_id , ALIAS , "", 20), 17 | bm_mac_sep (PSTR("
")) 18 | { 19 | // Write the required ID's to retrieve preference data. 20 | sprintf (alias_id, "abm%02d", index); 21 | // Alias starts at 'a', mac addr starts with 'bm'. This way the same buffer can be used for both!! 22 | } 23 | 24 | const char *getMacAddress() { 25 | return bm_mac_addr.getValue(); 26 | } 27 | 28 | void setMacAddress(const char * val) { 29 | bm_mac_addr.setValue(val); 30 | } 31 | 32 | const char *getAlias() { 33 | return bm_mac_alias.getValue(); 34 | } 35 | 36 | void setAlias(const char * val) { 37 | bm_mac_alias.setValue(val); 38 | } 39 | 40 | private: 41 | /*const*/ char alias_id[8]; 42 | 43 | Parameter bm_mac_addr; 44 | Parameter bm_mac_alias; 45 | Parameter bm_mac_sep; 46 | }; 47 | 48 | #endif // BLUETOOTH_PARAMETER_H -------------------------------------------------------------------------------- /include/BluetoothScanner.h: -------------------------------------------------------------------------------- 1 | #ifndef BLUETOOTH_SCANNER_H 2 | #define BLUETOOTH_SCANNER_H 3 | 4 | // Basic headers 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include // std::sort 11 | #include 12 | #include 13 | #include 14 | 15 | // IDF headers 16 | #include 17 | #include 18 | #include 19 | #include "nvs.h" 20 | #include "nvs_flash.h" 21 | #include "esp_system.h" 22 | #include "esp_log.h" 23 | #include "esp32-hal-bt.h" 24 | #include "esp_bt.h" 25 | #include "esp_bt_main.h" 26 | #include "esp_bt_device.h" 27 | #include "esp_gap_bt_api.h" 28 | 29 | // Tweaked SDK configuration 30 | #include "sdkconfig.h" 31 | #include 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include "BLEEddystoneURL.h" 38 | #include "BLEEddystoneTLM.h" 39 | #include "BLEBeacon.h" 40 | 41 | // monitor/setup/ADD STATIC DEVICE 42 | // monitor/setup/add known device 43 | // monitor/setup/delete known device 44 | 45 | char *bda2str(const esp_bd_addr_t bda, char *str, size_t size); 46 | bool str2bda(const char* str, esp_bd_addr_t& mac); 47 | 48 | enum class ScanType{ 49 | None, 50 | Arrival, 51 | Departure, 52 | Either 53 | }; 54 | 55 | class BluetoothScanner : public BLEAdvertisedDeviceCallbacks 56 | { 57 | public: 58 | BluetoothScanner(Stream& serialStream) : mSerial(serialStream) { 59 | } 60 | virtual ~BluetoothScanner() { 61 | 62 | } 63 | 64 | void init(); 65 | 66 | void setup(); 67 | 68 | void loop(); 69 | 70 | void stop(); 71 | 72 | void setNumArrivalScans (uint8_t val ); 73 | void setNumDepartureScans (uint8_t val ); 74 | void setSecondsBetweenScanIters (unsigned long val ); 75 | void setBeaconExpiration (uint32_t val ); 76 | void setMinTimeBetweenScans (uint32_t val ); 77 | void setPeriodicScanInterval (uint32_t val ); 78 | void setScanDurationTimeout (uint32_t val ); 79 | void setMqttTopic (const std::string& topic ); 80 | void setScannerIdentity (const char* identity); 81 | void setRetainFlag (bool flag ); 82 | 83 | void startBluetoothScan(ScanType scanType); 84 | 85 | void addKnownDevice (const std::string& input); 86 | void addKnownDevice (const esp_bd_addr_t mac, const char* alias); 87 | void deleteKnownDevice(const std::string& mac); 88 | void deleteKnownDevice(const esp_bd_addr_t mac); 89 | void clearKnownDevices(); 90 | 91 | void addKnownIBeacon (const std::string& input); 92 | void addKnownIBeacon (const BLEUUID uuid, const char* alias); 93 | void deleteKnownIBeacon(const BLEUUID uuid); 94 | 95 | // BLE advertisement results: 96 | void onResult(BLEAdvertisedDevice advertisedDevice); 97 | // BLE scan done callback: 98 | void bleScanCompleted(BLEScanResults); 99 | 100 | void HandleBleAdvertisementResult(BLEAdvertisedDevice& bleAdvertisedDeviceResult); 101 | 102 | private: 103 | void removeFromBtDevices(const esp_bd_addr_t mac); 104 | 105 | uint8_t getNumScans(ScanType scanType); 106 | unsigned long getLastScanTime(ScanType scanType); 107 | void setLastScanTime(ScanType scanType, unsigned long time); 108 | 109 | bool scanForNextDevice(); 110 | 111 | void SetReadRemoteNameResult(const esp_bt_gap_cb_param_t::read_rmt_name_param& remoteNameParam); 112 | void HandleReadRemoteNameResult(esp_bt_gap_cb_param_t::read_rmt_name_param& remoteNameParam); 113 | 114 | void gap_init(); 115 | void startupGap(); 116 | void gap_callback(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param); 117 | 118 | void update_device_info(esp_bt_gap_cb_param_t *param); 119 | 120 | struct btDeviceId_t { 121 | esp_bd_addr_t mac; 122 | String name; 123 | uint8_t confidence; 124 | uint8_t state; 125 | uint8_t scansLeft; 126 | 127 | btDeviceId_t(const esp_bd_addr_t MAC, String Name) : name(std::move(Name)), confidence(0), state(0) 128 | { 129 | memcpy(mac, MAC, sizeof(esp_bd_addr_t)); 130 | } 131 | }; 132 | 133 | struct iBeaconDeviceId_t { 134 | iBeaconDeviceId_t(const BLEUUID uuid, String Name) : uuid(uuid), name(std::move(Name)), power(0), lastSentRssi(0), confidence(0), state(0), last_update_millis(0) 135 | { 136 | 137 | } 138 | 139 | bool isVirgin () { 140 | return !filled_once; 141 | } 142 | 143 | void reset() { 144 | power = 0; 145 | confidence = 0; 146 | state = 0; 147 | memset(rssi_array, 0, sizeof(rssi_array)); 148 | filled_once=false; 149 | } 150 | 151 | void addRSSI(int rssi) { 152 | if(!filled_once && rssi_idx == 0) { 153 | memset(rssi_array, rssi, sizeof(rssi_array)); // Fill all with first value to get immediate 'find' 154 | } 155 | rssi_array[rssi_idx] = rssi; 156 | rssi_idx = (rssi_idx + 1)%rssi_array_size; 157 | if (!filled_once && rssi_idx == 0) { 158 | filled_once = true; 159 | } 160 | } 161 | 162 | int getFilteredRSSI() { 163 | if(!filled_once) { 164 | return 0; 165 | } 166 | else { 167 | int copy[rssi_array_size]; 168 | std::copy(std::begin(rssi_array), std::end(rssi_array), std::begin(copy)); 169 | std::sort(copy, copy + rssi_array_size); 170 | return copy[2]; 171 | } 172 | } 173 | 174 | BLEUUID uuid; 175 | String name; 176 | int power; 177 | int lastSentRssi; 178 | 179 | uint8_t confidence; 180 | uint8_t state; 181 | 182 | unsigned long last_update_millis; 183 | 184 | private: 185 | static const int rssi_array_size = 5; 186 | int rssi_array[rssi_array_size]; 187 | int rssi_idx = 0; 188 | bool filled_once = false; 189 | }; 190 | public: 191 | const std::vector& getBtDeviceStates(); 192 | 193 | private: 194 | 195 | Stream& mSerial; 196 | 197 | typedef enum { 198 | APP_GAP_STATE_IDLE = 0, 199 | APP_GAP_STATE_DEVICE_DISCOVERING, 200 | APP_GAP_STATE_DEVICE_DISCOVER_COMPLETE, 201 | APP_GAP_STATE_SERVICE_DISCOVERING, 202 | APP_GAP_STATE_SERVICE_DISCOVER_COMPLETE, 203 | } app_gap_state_t; 204 | 205 | typedef struct { 206 | bool dev_found; 207 | uint8_t bdname_len; 208 | uint8_t eir_len; 209 | uint8_t rssi; 210 | uint32_t cod; 211 | uint8_t eir[ESP_BT_GAP_EIR_DATA_LEN]; 212 | uint8_t bdname[ESP_BT_GAP_MAX_BDNAME_LEN + 1]; 213 | esp_bd_addr_t bda; 214 | app_gap_state_t state; 215 | } app_gap_cb_t; 216 | 217 | app_gap_cb_t m_dev_info; 218 | std::vector btDevices; 219 | std::mutex btDevicesMutex; 220 | 221 | std::vector iBeaconDevices; 222 | std::mutex iBeaconDevicesMutex; 223 | 224 | volatile uint8_t scanInProgress = 0; 225 | int scanIndex = -1; // loops over devices 226 | int iterationScansLeft = -1; // Loops over iterations 227 | ScanType scanMode; 228 | 229 | 230 | int scanTime = 4; //1; //In seconds 231 | BLEScan *pBLEScan = nullptr; 232 | bool bleScan_shouldStart = false; 233 | uint8_t scanContinueCount = 0; 234 | const uint8_t scanContinueWraparound = 4; 235 | const size_t maxBleProcessPerIteration = 3; 236 | 237 | std::queue readRemoteNameResultQueue; 238 | 239 | std::queue bleAdvertisedDeviceResultQueue; 240 | 241 | // Scanner parameters from storage: 242 | uint8_t num_arrival_scans; 243 | uint8_t num_departure_scans; 244 | unsigned long scan_iter_interval; 245 | uint32_t beacon_expiration_seconds; 246 | uint32_t min_seconds_between_scans; 247 | uint32_t periodic_scan_interval; 248 | uint32_t scan_duration_timeout; 249 | std::string m_mqtt_topic; 250 | const char* m_scanner_identity; 251 | bool m_retain = false; 252 | 253 | unsigned long last_arrival_scan_time = 0; 254 | unsigned long last_departure_scan_time = 0; 255 | 256 | unsigned long last_scan_iter_millis = 0; 257 | }; 258 | 259 | #endif // BLUETOOTH_SCANNER_H 260 | -------------------------------------------------------------------------------- /include/CallbackHelper.h: -------------------------------------------------------------------------------- 1 | #ifndef CALLBACK_HELPER_H 2 | #define CALLBACK_HELPER_H 3 | 4 | // Callback helper class / definitions based on the bounce implementation by embeddedartistry: 5 | // https://github.com/embeddedartistry/embedded-resources/blob/master/examples/cpp/bounce.cpp 6 | // 7 | // Better lead: 8 | //https://stackoverflow.com/questions/1000663/using-a-c-class-member-function-as-a-c-callback-function 9 | 10 | #include 11 | 12 | 13 | template 14 | struct Callback; 15 | 16 | template 17 | struct Callback { 18 | template 19 | static Ret callback(Args... args) { 20 | return func(args...); 21 | } 22 | static std::function func; 23 | }; 24 | 25 | template 26 | std::function Callback::func; 27 | 28 | 29 | 30 | #endif // CALLBACK_HELPER_H 31 | -------------------------------------------------------------------------------- /include/NestedStruct.h: -------------------------------------------------------------------------------- 1 | #ifndef NESTED_STRUCT_H 2 | #define NESTED_STRUCT_H 3 | 4 | template 5 | struct NestedStruct 6 | { 7 | NestedStruct contained; 8 | T t; 9 | // Constructor 10 | NestedStruct() : t(N) {} 11 | }; 12 | 13 | template 14 | struct NestedStruct 15 | { 16 | T t; 17 | // Constructor 18 | NestedStruct() : t(0) {} 19 | }; 20 | 21 | template 22 | class NestWrapper 23 | { 24 | public: 25 | T (&data)[N] = reinterpret_cast(nest); 26 | static const int size = N; 27 | private: 28 | NestedStruct nest; 29 | }; 30 | 31 | #endif // NESTED_STRUCT_H -------------------------------------------------------------------------------- /include/TelnetSerial.h: -------------------------------------------------------------------------------- 1 | /* 2 | TelnetSerial.h - Arduino like serial library for printing over Telnet for ESP8266 & ESP32 3 | */ 4 | 5 | #ifndef TelnetSerial_h 6 | #define TelnetSerial_h 7 | 8 | #include 9 | #include <../include/time.h> // See issue #6714 10 | #include "Stream.h" 11 | #include "WiFiServer.h" 12 | #include "WiFiClient.h" 13 | 14 | #include "stackDbgHelper.h" 15 | 16 | #define MAX_TELNET_CLIENTS 2 17 | 18 | class Telnet: public Stream 19 | { 20 | public: 21 | Telnet(uint16_t port = 23); 22 | virtual ~Telnet() {} 23 | 24 | void begin(); 25 | 26 | void end(); 27 | 28 | void handleConnections(); 29 | 30 | int getConnectionCount(); 31 | 32 | int available(void) override; 33 | 34 | void flush(void) override; 35 | 36 | int peek(void) override 37 | { 38 | int return_value = -1; 39 | for(uint8_t i = 0; i < MAX_TELNET_CLIENTS; i++){ 40 | if (serverClients[i] && serverClients[i].connected()){ 41 | return_value = serverClients[i].peek(); 42 | if (return_value >= 0) { 43 | break; 44 | } 45 | } 46 | } 47 | return return_value; 48 | } 49 | 50 | int read(void) override 51 | { 52 | int return_value = -1; 53 | for(uint8_t i = 0; i < MAX_TELNET_CLIENTS; i++){ 54 | if (serverClients[i] && serverClients[i].connected()){ 55 | return_value = serverClients[i].read(); 56 | if (return_value >= 0) { 57 | break; 58 | } 59 | } 60 | } 61 | return return_value; 62 | } 63 | 64 | size_t read(char* buffer, size_t size) 65 | { 66 | size_t return_value = 0; 67 | for(uint8_t i = 0; i < MAX_TELNET_CLIENTS; i++){ 68 | if (serverClients[i] && serverClients[i].connected()){ 69 | return_value = serverClients[i].read((uint8_t*)buffer, size); 70 | if (return_value > 0) { 71 | break; 72 | } 73 | } 74 | } 75 | return return_value; 76 | } 77 | size_t readBytes(char* buffer, size_t size) override 78 | { 79 | size_t return_value = 0; 80 | for(uint8_t i = 0; i < MAX_TELNET_CLIENTS; i++){ 81 | if (serverClients[i] && serverClients[i].connected()){ 82 | return_value = serverClients[i].read((uint8_t*)buffer, size); 83 | if (return_value > 0) { 84 | break; 85 | } 86 | } 87 | } 88 | return return_value; 89 | } 90 | size_t readBytes(uint8_t* buffer, size_t size) override 91 | { 92 | size_t return_value = 0; 93 | for(uint8_t i = 0; i < MAX_TELNET_CLIENTS; i++){ 94 | if (serverClients[i] && serverClients[i].connected()){ 95 | return_value = serverClients[i].read(buffer, size); 96 | if (return_value > 0) { 97 | break; 98 | } 99 | } 100 | } 101 | return return_value; 102 | } 103 | 104 | size_t write(uint8_t c) override 105 | { 106 | size_t last_return_value = 0; 107 | //push UART data to all connected telnet clients 108 | for(uint8_t i = 0; i < MAX_TELNET_CLIENTS; i++) { 109 | if (serverClients[i] && serverClients[i].connected()){ 110 | last_return_value = serverClients[i].write(c); 111 | feedLoopWDT(); // Must be done <= 360 mS 112 | auto _millis = millis(); 113 | if(_millis > _last_yield_ms + _yield_timeout) { 114 | //yield(); 115 | delay(10); 116 | _last_yield_ms=_millis; 117 | } 118 | } 119 | } 120 | return last_return_value; 121 | } 122 | size_t write(const uint8_t *buffer, size_t size) override 123 | { 124 | size_t last_return_value = 0; 125 | //push UART data to all connected telnet clients 126 | for(uint8_t i = 0; i < MAX_TELNET_CLIENTS; i++){ 127 | if (serverClients[i] && serverClients[i].connected()){ 128 | last_return_value = serverClients[i].write(buffer, size); 129 | feedLoopWDT(); // Must be done <= 360 mS 130 | auto _millis = millis(); 131 | if(_millis > _last_yield_ms + _yield_timeout) { 132 | //yield(); 133 | delay(10); 134 | _last_yield_ms=_millis; 135 | } 136 | } 137 | } 138 | return last_return_value; 139 | } 140 | using Print::write; // Import other write() methods to support things like write(0) properly 141 | 142 | protected: 143 | WiFiServer server; 144 | WiFiClient serverClients[MAX_TELNET_CLIENTS]; 145 | size_t _rx_size; 146 | unsigned long _last_yield_ms; 147 | const unsigned long _yield_timeout = 500; 148 | }; 149 | 150 | class TelnetSerial: public Stream 151 | { 152 | public: 153 | TelnetSerial(uint16_t telnetPort = 23) : _telnet(telnetPort) { 154 | 155 | } 156 | virtual ~TelnetSerial() {} 157 | 158 | void begin() { 159 | _telnet.begin(); 160 | _begun = true; 161 | } 162 | 163 | void end() { 164 | _telnet.end(); 165 | _begun = false; 166 | } 167 | 168 | void loop() { 169 | SCOPED_STACK_ENTRY; 170 | _telnet.handleConnections(); 171 | } 172 | 173 | int getConnectionCount() { 174 | return _telnet.getConnectionCount(); 175 | } 176 | 177 | int available(void) 178 | { 179 | int last_return_value = 0; 180 | last_return_value = Serial.available(); 181 | if(_begun && !last_return_value) { 182 | last_return_value = _telnet.available(); 183 | } 184 | return last_return_value; 185 | } 186 | 187 | void flush(void) 188 | { 189 | Serial.flush(); 190 | if(_begun) { 191 | _telnet.flush(); 192 | } 193 | } 194 | 195 | int peek(void) override 196 | { 197 | int last_return_value = -1; 198 | last_return_value = Serial.peek(); 199 | if(_begun && last_return_value < 0) { 200 | last_return_value = _telnet.peek(); 201 | } 202 | return last_return_value; 203 | } 204 | 205 | int read(void) override 206 | { 207 | int last_return_value = -1; 208 | last_return_value = Serial.read(); 209 | if(_begun && last_return_value < 0) { 210 | last_return_value = _telnet.read(); 211 | } 212 | return last_return_value; 213 | } 214 | 215 | size_t read(char* buffer, size_t size) 216 | { 217 | size_t last_return_value = 0; 218 | last_return_value = Serial.read(buffer, size); 219 | if(_begun && !last_return_value) { 220 | last_return_value = _telnet.read(buffer, size); 221 | } 222 | return last_return_value; 223 | } 224 | size_t readBytes(char* buffer, size_t size) override 225 | { 226 | size_t last_return_value = 0; 227 | last_return_value = Serial.read(buffer, size); 228 | if(_begun && !last_return_value) { 229 | last_return_value = _telnet.read(buffer, size); 230 | } 231 | return last_return_value; 232 | } 233 | 234 | size_t write(uint8_t c) override 235 | { 236 | size_t last_return_value = 0; 237 | //. Test if using single char writing... 238 | //if(_begun) { 239 | // last_return_value = _telnet.write(c); 240 | //} 241 | last_return_value = Serial.write(c); 242 | return last_return_value; 243 | } 244 | 245 | size_t write(const uint8_t *buffer, size_t size) override 246 | { 247 | size_t last_return_value = 0; 248 | if(_begun) { 249 | last_return_value = _telnet.write(buffer, size); 250 | } 251 | last_return_value = Serial.write(buffer, size); 252 | return last_return_value; 253 | } 254 | 255 | using Print::write; // Import other write() methods to support things like write(0) properly 256 | 257 | protected: 258 | Telnet _telnet; 259 | bool _begun = false; 260 | 261 | public: 262 | Telnet& TelNet = _telnet; 263 | }; 264 | 265 | 266 | #endif 267 | -------------------------------------------------------------------------------- /include/WiFiComponent.h: -------------------------------------------------------------------------------- 1 | #ifndef WIFICOMPONENT_h 2 | #define WIFICOMPONENT_h 3 | 4 | // Basic headers 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | // Tweaked SDK configuration 12 | #include "sdkconfig.h" 13 | 14 | // Arduino includes 15 | #include 16 | #include 17 | #include // https://github.com/tzapu/WiFiManager 18 | 19 | // Configuration access point password: 20 | #ifndef AP_PASSWD 21 | #define AP_PASSWD "abc45678" 22 | #endif 23 | 24 | // Config portal httpd auth: 25 | #ifndef HTTPD_USER 26 | #define HTTPD_USER "admin" 27 | #endif 28 | #ifndef HTTPD_PASSWD 29 | #define HTTPD_PASSWD "1234" 30 | #endif 31 | 32 | class WiFiComponent { 33 | public: 34 | typedef std::function customHtmlHandler_t; 35 | 36 | WiFiComponent(Stream& serialStream) : mSerial(serialStream) 37 | { 38 | // Nothing 39 | } 40 | 41 | void setup(); 42 | void loop(); 43 | 44 | void registerOtaStartCallback(std::function callback); 45 | 46 | void registerParamSaveCallback(std::function callback); 47 | 48 | // Note: This must be called before 'setup()'!! 49 | void addCustomHtmlPage(const char* path, customHtmlHandler_t handler); 50 | 51 | private: 52 | void wifiInfo(); 53 | 54 | // Setup functions 55 | void setupArduinoOTA(); 56 | void setupConfigPortal(); 57 | 58 | void stopArduinoOTA(); 59 | 60 | // Parameter handling functions 61 | void loadParams(); 62 | void storeParams(); 63 | 64 | // Callbacks 65 | void saveWifiCallback(); 66 | void configModeCallback (WiFiManager *myWiFiManager); 67 | void saveParamCallback(); 68 | void handleRoute(); 69 | void bindServerCallback(); 70 | 71 | Stream& mSerial; 72 | 73 | uint8_t otaBusy = 0; 74 | std::vector> ota_callback_functions; 75 | 76 | std::vector> paramSaved_callback_functions; 77 | 78 | struct customPage_t { 79 | const char* path; 80 | customHtmlHandler_t handler; 81 | 82 | customPage_t(const char* Path, customHtmlHandler_t Handler) : path(Path), handler(Handler) { 83 | } 84 | }; 85 | std::vector custom_html_page_handlers; 86 | String _hostname; 87 | }; 88 | 89 | #endif // WIFICOMPONENT_h -------------------------------------------------------------------------------- /include/led.h: -------------------------------------------------------------------------------- 1 | #ifndef LED_H 2 | #define LED_H 3 | 4 | #include 5 | 6 | #define BLINK_GPIO (gpio_num_t)CONFIG_BLINK_GPIO 7 | 8 | #define ON 255 9 | #define OFF 0 10 | 11 | class LED { 12 | public: 13 | LED(const gpio_num_t pin = BLINK_GPIO) : _pin(pin){ 14 | 15 | } 16 | 17 | void setup(){ 18 | gpio_reset_pin(_pin); 19 | /* Set the GPIO as a push/pull output */ 20 | gpio_set_direction(_pin, GPIO_MODE_OUTPUT); 21 | 22 | ledcAttachPin(_pin, 0); // Assign ledpin to PWM channel 23 | // Initialize channels 24 | // channels 0-15, resolution 1-16 bits, freq limits depend on resolution 25 | // ledcSetup(uint8_t channel, uint32_t freq, uint8_t resolution_bits); 26 | ledcSetup(0, 4000, 8); // 12 kHz PWM, 8-bit resolution 27 | } 28 | 29 | // void blink_task(void *pvParameter) 30 | // { 31 | // // Configure the IOMUX register for pad BLINK_GPIO (some pads are 32 | // // muxed to GPIO on reset already, but some default to other 33 | // // functions and need to be switched to GPIO. Consult the 34 | // // Technical Reference for a list of pads and their default 35 | // // functions.) 36 | // // 37 | // gpio_pad_select_gpio(BLINK_GPIO); 38 | // // Set the GPIO as a push/pull output 39 | // gpio_set_direction(BLINK_GPIO, GPIO_MODE_OUTPUT); 40 | // while(1) { 41 | // // Blink off (output low) 42 | // gpio_set_level(BLINK_GPIO, 0); 43 | // vTaskDelay(1000 / portTICK_PERIOD_MS); 44 | // // Blink on (output high) 45 | // gpio_set_level(BLINK_GPIO, 1); 46 | // vTaskDelay(1000 / portTICK_PERIOD_MS); 47 | // } 48 | // } 49 | 50 | void toggle() { 51 | // Toggle the LED state 52 | _led_state = (_led_state ? 0 : 255); 53 | 54 | ledcWrite(0, _led_state); // set the brightness of the LED 55 | } 56 | 57 | void set(uint8_t level) { 58 | //gpio_set_level(_pin, level); 59 | 60 | ledcWrite(0, level); // set the brightness of the LED 61 | _led_state = level; 62 | } 63 | 64 | private: 65 | uint8_t _led_state = 0; 66 | const gpio_num_t _pin; 67 | }; 68 | 69 | extern LED led; 70 | 71 | #endif // LED_H -------------------------------------------------------------------------------- /include/mqtt.h: -------------------------------------------------------------------------------- 1 | #ifndef MQTT_H 2 | #define MQTT_H 3 | 4 | // Basic headers 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | // Arduino headers 12 | #include 13 | #include 14 | #include "WiFiClient.h" 15 | #include "Stream.h" 16 | 17 | 18 | class MQTT { 19 | public: 20 | typedef std::function CallbackFunction_t; 21 | 22 | MQTT(Stream& serialStream) : mSerial(serialStream), mqtt_client(espClient) 23 | { 24 | // Nothing.. 25 | } 26 | 27 | void setup( 28 | const char* mqtt_server, 29 | const char* mqtt_port, 30 | const char* mqtt_username, 31 | const char* mqtt_password, 32 | const char* mqtt_client_id); 33 | void loop(); 34 | 35 | void setStateTopic(std::string state_topic); 36 | void setIpTopic(std::string ip_topic); 37 | static std::string trimWildcards(const char* topic); 38 | 39 | void send_message(const char *topic, const char *payload, bool retain = false); 40 | 41 | void add_callback(const char* topic, CallbackFunction_t callbackFunction); 42 | void clear_callbacks(); 43 | 44 | void add_subscription_topic(String topic); 45 | void remove_subscription_topic(String topic); 46 | void clear_subscription_topics(); 47 | 48 | private: 49 | 50 | void mqtt_callback(char* topic, byte* payload, unsigned int length); 51 | bool reconnect(); 52 | 53 | Stream& mSerial; 54 | WiFiClient espClient; 55 | PubSubClient mqtt_client; 56 | 57 | // Stored MQTT parameters 58 | const char* _mqtt_server = nullptr; 59 | const char* _mqtt_port = nullptr; 60 | const char* _mqtt_username = nullptr; 61 | const char* _mqtt_password = nullptr; 62 | const char* _mqtt_clientId = nullptr; 63 | 64 | uint32_t _mqtt_reconnect_retries = 0; 65 | 66 | std::string _mqtt_state_topic_str = ""; 67 | std::string _mqtt_ip_topic_str = ""; 68 | 69 | // MQTT Last reconnection counter 70 | unsigned long last_reconnect_attempt = 0; 71 | 72 | std::vector> callback_functions; 73 | std::vector subscription_topics; 74 | 75 | static const uint8_t mqtt_max_reconnect_tries = 10; 76 | }; 77 | 78 | 79 | extern MQTT mqtt; 80 | 81 | #endif // MQTT_H 82 | -------------------------------------------------------------------------------- /include/parameter.h: -------------------------------------------------------------------------------- 1 | #ifndef Parameter_h 2 | #define Parameter_h 3 | 4 | // Basic headers 5 | #include 6 | #include 7 | #include 8 | 9 | // Tweaked SDK configuration 10 | #include "sdkconfig.h" 11 | 12 | #include // https://github.com/tzapu/WiFiManager 13 | 14 | 15 | #define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x))) 16 | #define MAX(a ,b) ((a) > (b) ? (a) : (b)) 17 | 18 | 19 | class Parameter : public WiFiManagerParameter{ 20 | public: 21 | // Constructors for different Parameter types: 22 | Parameter(const char *custom); 23 | Parameter(const char *id, const char *label, const char* defaultValue = "", int length = 0, const char *custom = nullptr, int labelPlacement = WFM_LABEL_BEFORE); 24 | 25 | void initialize(); 26 | void storeValue(); 27 | 28 | void setValue(const char *value); 29 | 30 | private: 31 | bool _initialized = false; 32 | }; 33 | 34 | bool param2bool(const Parameter& param); 35 | 36 | template::value, int>::type* = nullptr> 37 | constexpr int maxNumDigits(const T maxVal) { 38 | return MAX(6, static_cast(log10(abs(maxVal))) + 2); 39 | } 40 | 41 | template::value, int>::type* = nullptr> 42 | constexpr int maxNumDigits(const T maxVal) { 43 | return 12; 44 | } 45 | 46 | // Numeric parameter: 47 | template::value, T>::type> 48 | class NumericParameter : public Parameter 49 | { 50 | public: 51 | NumericParameter(const char *id, const char *label, T defaultValue, T minValue = std::numeric_limits::min(), T maxValue = std::numeric_limits::max(), int labelPlacement = WFM_LABEL_BEFORE) : 52 | Parameter(id, label, nullptr, maxNumDigits(std::numeric_limits::max()), nullptr, labelPlacement), m_min(minValue), m_max(maxValue) 53 | { 54 | std::string strVal = std::to_string(defaultValue); 55 | WiFiManagerParameter::setValue(strVal.c_str()); 56 | } 57 | 58 | void setValue(const T value) { 59 | std::string strVal = std::to_string(value); 60 | Parameter::setValue(strVal.c_str()); 61 | } 62 | 63 | template 64 | const typename std::enable_if::value, T>::type getValue() { 65 | T val = atof(Parameter::getValue()); 66 | T cVal = CLAMP(val, m_min, m_max); 67 | if (cVal != val) { 68 | setValue(cVal); 69 | } 70 | return cVal; 71 | } 72 | 73 | template 74 | const typename std::enable_if::value, T>::type getValue() { 75 | long long val = atoll(Parameter::getValue()); 76 | T tVal = CLAMP(val, m_min, m_max); 77 | if (tVal != val) { 78 | setValue(tVal); 79 | } 80 | return tVal; 81 | } 82 | 83 | operator T() { 84 | return getValue(); 85 | } 86 | 87 | T& operator=(T value) { 88 | setValue(value); 89 | return value; 90 | } 91 | 92 | private: 93 | const T m_min; 94 | const T m_max; 95 | }; 96 | 97 | typedef NumericParameter BoolParameter; 98 | typedef NumericParameter U8Parameter; 99 | typedef NumericParameter< int8_t> I8Parameter; 100 | typedef NumericParameter U16Parameter; 101 | typedef NumericParameter< int16_t> I16Parameter; 102 | typedef NumericParameter U32Parameter; 103 | typedef NumericParameter< int32_t> I32Parameter; 104 | typedef NumericParameter IntParameter; 105 | typedef NumericParameter LongParameter; 106 | typedef NumericParameter ULongParameter; 107 | typedef NumericParameter FloatParameter; 108 | 109 | #endif // Parameter_h 110 | -------------------------------------------------------------------------------- /include/stackDbgHelper.h: -------------------------------------------------------------------------------- 1 | #ifndef StackDbgHelper_h 2 | #define StackDbgHelper_h 3 | 4 | // Basic headers 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | // Tweaked SDK configuration 11 | #include "sdkconfig.h" 12 | 13 | // Arduino includes 14 | #include 15 | 16 | struct stack_entry { 17 | uint32_t pc; 18 | const char* func; 19 | uint32_t line; 20 | 21 | stack_entry(uint32_t _pc, const char* f, uint32_t l) : pc(_pc), func(f), line(l) { 22 | 23 | } 24 | }; 25 | 26 | extern std::stack _stack; 27 | 28 | class scopedStackEntry { 29 | public: 30 | scopedStackEntry(uint32_t val, const char* f, uint32_t line) : _entry(val, f, line) { 31 | _stack.push(_entry); 32 | } 33 | 34 | ~scopedStackEntry() { 35 | _stack.pop(); 36 | } 37 | 38 | private: 39 | stack_entry _entry; 40 | }; 41 | 42 | #define SCOPED_STACK_ENTRY scopedStackEntry _s(_stack.size(), __PRETTY_FUNCTION__, __LINE__); 43 | 44 | 45 | #endif // StackDbgHelper_h 46 | -------------------------------------------------------------------------------- /partitions_custom.csv: -------------------------------------------------------------------------------- 1 | # Name, Type, SubType, Offset, Size, Flags 2 | nvs, data, nvs, 0x009000, 0x005000, 3 | otadata, data, ota, 0x00e000, 0x002000, 4 | app0, app, ota_0, 0x010000, 0x1F0000, 5 | app1, app, ota_1, 0x200000, 0x1F0000, 6 | eeprom0, data, 0x99, 0x3F0000, 0x002000, 7 | spiffs, data, spiffs, 0x3F2000, 0x00E000, -------------------------------------------------------------------------------- /platformio.ini: -------------------------------------------------------------------------------- 1 | ; PlatformIO Project Configuration File 2 | ; 3 | ; Build options: build flags, source filter 4 | ; Upload options: custom upload port, speed and extra flags 5 | ; Library options: dependencies, extra library storages 6 | ; Advanced options: extra scripting 7 | ; 8 | ; Please visit documentation for the other options and examples 9 | ; https://docs.platformio.org/page/projectconf.html 10 | 11 | [env] 12 | # Use Tasmota platform: It refers to Arduino + Esp-IDF frameworks that work well together!! 13 | platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.4.1/platform-espressif32-2.0.4.1.zip 14 | framework = arduino, espidf 15 | platform_packages = 16 | 17 | lib_deps = 18 | ropg/ezTime @ 0.8.3 19 | https://github.com/RoboMagus/WiFiManager.git#customizations 20 | https://github.com/knolleary/PubSubClient#v2.8 21 | 22 | build_flags = 23 | ; Do Not Modify: 24 | -DARDUINO_STACK_SIZE=4096 25 | -DCORE_DEBUG_LEVEL=3 26 | ; -DCORE_DEBUG_LEVEL=4 27 | -DCONFIG_BLINK_GPIO=2 28 | -DESP32=1 29 | -DMQTT_MAX_PACKET_SIZE=512 30 | -DWM_MDNS=1 31 | -DWM_ERASE_NVS=1 32 | -DWM_DEBUG_LEVEL=DEBUG_NOTIFY 33 | -Wno-missing-field-initializers 34 | -Wno-unused-variable 35 | ; Can Modify: 36 | -DAP_PASSWD=\"abc45678\" 37 | -DHTTPD_USER=\"admin\" 38 | -DHTTPD_PASSWD=\"1234\" 39 | -DMAX_NUM_STORED_BLUETOOTH_DEVICES=8 40 | -DDUMPTIMEMEMINTERVAL=10 41 | ; -DNO_USE_TELNET_SERIAL 42 | 43 | monitor_filters = esp32_exception_decoder 44 | monitor_speed = 115200 45 | 46 | ; >>>>>>>>>>> 47 | ; For OTA update and remote debugging 48 | ; 49 | ; monitor_port = socket://192.168.1.200:23 50 | ; upload_protocol = espota 51 | ; upload_port = 192.168.1.200 52 | ; upload_flags = 53 | ; --port=3232 54 | ; --auth=admin 55 | ; <<<<<<<<<<< 56 | 57 | [env:wemos_d1_mini32] 58 | board = wemos_d1_mini32 59 | board_build.partitions = partitions_custom.csv 60 | board_upload.maximum_size = 2031616 61 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | 2 | ![PlatformIO Build Status](https://img.shields.io/github/workflow/status/RoboMagus/ESP32-MQTT-Bluetooth-Monitor/Build%20PlatformIO%20Project?label=Platformio%20build&logo=github&style=for-the-badge) 3 | ![EspHome Build Status](https://img.shields.io/github/workflow/status/RoboMagus/ESP32-MQTT-Bluetooth-Monitor/Build%20EspHome?label=EspHome%20build&logo=github&style=for-the-badge) 4 | 5 | # ESP32 BT Monitor 6 | ![GitHub release (latest by date including pre-releases)](https://img.shields.io/github/v/release/RoboMagus/ESP32-MQTT-Bluetooth-Monitor?include_prereleases&style=for-the-badge) ![ESP-IDF](https://img.shields.io/badge/ESP--IDF-v4.4.3-red?longCache=true&style=for-the-badge) 7 | ![ESP32](https://img.shields.io/badge/ESP-32-blue?longCache=true&style=for-the-badge) 8 | 9 | __What is it?__ 10 | 11 | This is a (at the moment __partial__) port of [andrewjfreyer/monitor](https://github.com/andrewjfreyer/monitor) for the popular and super cheap ESP32 boards. For a more detailed explanation of how it works, visit the link above. But as a short summary: this software allows you to scan for known Bluetooth devices by MAC address on demand, by requesting their name. The beauty of it is that you don't need any special software running on the devices you'd like to track! 12 | 13 | __iBeacon support:__ 14 | 15 | Currently, support for ble iBeacon devices is also being implemented! 16 | The ESP32 will in the background continually listen passively to broadcasts of iBeacon devices. If a found ble __UUID__ matches one of the configured __UUID__'s then an MQTT message will be broadcast that includes the _RSSI_ of the found service. 17 | _Note_ that this feature is currently still subject to change!! 18 | 19 | 20 | ## Building the project from source 21 | ### Prerequisites 22 | In order to build this project, you need to have __Visual Studio Code__ installed, with the __C/C++__ and __PlatformIO IDE__ extensions. 23 | 24 | ### Libraries 25 | This project relies on the following libraries: 26 | - [ropg/ezTime](https://github.com/ropg/ezTime) 27 | - [knolleary/PubSubClient](https://github.com/knolleary/PubSubClient#v2.8) 28 | - [RoboMagus/WiFiManager](https://github.com/RoboMagus/WiFiManager) (Modified from [tzapu/WiFiManager](https://github.com/tzapu/WiFiManager)) 29 | 30 | ### Build it 31 | 1. Clone this repository 32 | 2. Open the local repository directory in VSCode 33 | 3. Open the PIO _Projects & Configuration_ tab and create an existing project. 34 | 4. Open this project from the PIO interface 35 | 5. In the sidebar, open up the PIO Tab and under _PROJECT TASKS_ / _Default_ hit _Build_ 36 | Note: The first time opening this project, PlatformIO will take some time to fetch all the required modules before the list under _PROJECT TASKS_ shows up!! 37 | 6. Once the build succeeds you can flash your ESP32 with the '_Monitor_' compatible Bluetooth scanner. 38 | 39 | __Modifications:__ 40 | When building the project from sources, there are some things you may want to customize in ```platformio.ini```. 41 | - ```AP_PASSWD```: The AccessPoint password for when the ESP32 is not yet configured to connect to your home WiFi network. 42 | - ```HTTPD_USER```: Username for authorization on the config portal when the ESP32 is connected to your home WiFi network. 43 | - ```HTTPD_PASSWD```: Password for authorization on the config portal when the ESP32 is connected to your home WiFi network. 44 | - ```MAX_NUM_STORED_BLUETOOTH_DEVICES```: Maximum number of _known bluetooth devices_ that can be stored on the ESP32 45 | 46 | By default PlatformIO is set up for flashing devices that are connected to your computer. If you wish to perform remote debugging or over the air updates using ```espota``` you'll need to uncomment the following section and fill in your device-ip: 47 | ``` 48 | monitor_port = socket://[device-ip]:23 49 | upload_protocol = espota 50 | upload_port = [device-ip] 51 | upload_flags = 52 | --port=3232 53 | --auth=admin 54 | ``` 55 | 56 | 57 | ## Configuration 58 | After flasing your ESP32 some initial configuration is required. To begin, connect your phone to the ESP32's WiFi hotspot. This will have a name starting with ```ESP32_bt```, followed by unique characters based on the devices MAC address. The default firmware configuration requires the following credentials in order to connect to the AP: ```abc45678```. When connected navigate with your browser of choice to ```192.168.4.1``` to open the configuration menu. 59 | 60 | 61 | 62 | Now it's best to first enter the __settings__ menu to start configuration there. Performing the WiFi configuration first will cause the device to restart and connect to your home network. You can still configure the device after this, it just requires some more steps to navigate to it's new IP again! 63 | The settings menu contains all settings related to the BT monitor application as shown below. The scan settings and BT devices can be skipped and configured later on. The important bits for now are the MQTT parameters to make sure the application can communicate. 64 | 65 | 66 | 67 | In case you need to revisit the configuration pages you can do so by navigating to the ESP32's IP address on your home network. To prevent others from tinkering with your device any access to it's webpage once it's connected to your WiFi network is password protected! To log on, use username ```admin``` and password ```1234```. 68 | 69 | ## Usage 70 | ### web control 71 | The ESP32 bluetooth scan status can be seen through the devices webpage by following the __BT Monitor__ link in the main menu. This page will show you all configured devices by name and MAC address, as well as their away / present state. From this page you can also trigger various scans and update the page to load the new scan results. 72 | 73 | 74 | 75 | ### Home assistant 76 | Find some examples for sensors and automations in HomeAssistant [here](doc/HomeAssistantSnippets.md). 77 | 78 | ## Features 79 | ### Implemented: 80 | - [andrewjfreyer/monitor](https://github.com/andrewjfreyer/monitor) compatible MQTT scan reporting 81 | - Arrival / Departure scan triggered by ```monitor/scan/arrive``` and ```monitor/scan/depart``` 82 | - 'Any' scan (scan for departure on present devices and arrival for away devices) triggered by ```monitor/scan/any``` 83 | - Periodic scanning 84 | - Setup known bluetooth devices through MQTT using ```monitor/setup/add known device``` and ```monitor/setup/delete known device``` 85 | - Send only MAC when deleting a device 86 | - Send MAC followed by its alias when adding devices 87 | - Over the air update 88 | - through web interface 89 | - Using ```espota``` 90 | - Setup through [modified](https://github.com/RoboMagus/WiFiManager) [WiFiManager](https://github.com/tzapu/WiFiManager) 91 | - Password protected web interface once connected to your home WiFi network 92 | - Bluetooth Scan status + scan controls webpage 93 | - Logging over serial and telnet 94 | - Monitor settings: 95 | - #Arrival scans 96 | - #Departure scans 97 | - seconds between scans within set 98 | - minimal time between scan sets 99 | - periodic scanning interval 100 | 101 | ### ToDo 102 | - [ ] Add passive scanning as is available in [monitor](https://github.com/andrewjfreyer/monitor) 103 | - [ ] Expand configuration options 104 | - [ ] Add BLE iBeacon scanning support for room by room presence detection 105 | - [x] Initial support __(This is subject to change!!)__ 106 | - [ ] Add support for interacting with ble keyfinders 107 | - [ ] Strip away the core BT monitor logic to expose as a library 108 | - [ ] Make an ESPHome component for it 109 | - **Work In Progress** 110 | 111 | -------------------------------------------------------------------------------- /sdkconfig.defaults: -------------------------------------------------------------------------------- 1 | # 2 | # Automatically generated file. DO NOT EDIT. 3 | # Espressif IoT Development Framework (ESP-IDF) Project Configuration 4 | # 5 | CONFIG_IDF_CMAKE=y 6 | CONFIG_IDF_TARGET="esp32" 7 | CONFIG_IDF_TARGET_ESP32=y 8 | CONFIG_IDF_FIRMWARE_CHIP_ID=0x0000 9 | 10 | # 11 | # SDK tool configuration 12 | # 13 | CONFIG_SDK_TOOLPREFIX="xtensa-esp32-elf-" 14 | # CONFIG_SDK_TOOLCHAIN_SUPPORTS_TIME_WIDE_64_BITS is not set 15 | # end of SDK tool configuration 16 | 17 | # 18 | # Build type 19 | # 20 | CONFIG_APP_BUILD_TYPE_APP_2NDBOOT=y 21 | # CONFIG_APP_BUILD_TYPE_ELF_RAM is not set 22 | CONFIG_APP_BUILD_GENERATE_BINARIES=y 23 | CONFIG_APP_BUILD_BOOTLOADER=y 24 | CONFIG_APP_BUILD_USE_FLASH_SECTIONS=y 25 | # end of Build type 26 | 27 | # 28 | # Application manager 29 | # 30 | CONFIG_APP_COMPILE_TIME_DATE=y 31 | # CONFIG_APP_EXCLUDE_PROJECT_VER_VAR is not set 32 | # CONFIG_APP_EXCLUDE_PROJECT_NAME_VAR is not set 33 | # CONFIG_APP_PROJECT_VER_FROM_CONFIG is not set 34 | CONFIG_APP_RETRIEVE_LEN_ELF_SHA=16 35 | # end of Application manager 36 | 37 | # 38 | # Bootloader config 39 | # 40 | CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y 41 | # CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_DEBUG is not set 42 | # CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_PERF is not set 43 | # CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_NONE is not set 44 | # CONFIG_BOOTLOADER_LOG_LEVEL_NONE is not set 45 | # CONFIG_BOOTLOADER_LOG_LEVEL_ERROR is not set 46 | # CONFIG_BOOTLOADER_LOG_LEVEL_WARN is not set 47 | CONFIG_BOOTLOADER_LOG_LEVEL_INFO=y 48 | # CONFIG_BOOTLOADER_LOG_LEVEL_DEBUG is not set 49 | # CONFIG_BOOTLOADER_LOG_LEVEL_VERBOSE is not set 50 | CONFIG_BOOTLOADER_LOG_LEVEL=3 51 | # CONFIG_BOOTLOADER_VDDSDIO_BOOST_1_8V is not set 52 | CONFIG_BOOTLOADER_VDDSDIO_BOOST_1_9V=y 53 | # CONFIG_BOOTLOADER_FACTORY_RESET is not set 54 | # CONFIG_BOOTLOADER_APP_TEST is not set 55 | CONFIG_BOOTLOADER_WDT_ENABLE=y 56 | # CONFIG_BOOTLOADER_WDT_DISABLE_IN_USER_CODE is not set 57 | CONFIG_BOOTLOADER_WDT_TIME_MS=9000 58 | # CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE is not set 59 | # CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP is not set 60 | CONFIG_BOOTLOADER_RESERVE_RTC_SIZE=0 61 | # CONFIG_BOOTLOADER_CUSTOM_RESERVE_RTC is not set 62 | # end of Bootloader config 63 | 64 | # 65 | # Security features 66 | # 67 | # CONFIG_SECURE_SIGNED_APPS_NO_SECURE_BOOT is not set 68 | # CONFIG_SECURE_BOOT is not set 69 | # CONFIG_SECURE_FLASH_ENC_ENABLED is not set 70 | # end of Security features 71 | 72 | # 73 | # Serial flasher config 74 | # 75 | CONFIG_ESPTOOLPY_BAUD_OTHER_VAL=115200 76 | CONFIG_ESPTOOLPY_WITH_STUB=y 77 | # CONFIG_ESPTOOLPY_FLASHMODE_QIO is not set 78 | # CONFIG_ESPTOOLPY_FLASHMODE_QOUT is not set 79 | CONFIG_ESPTOOLPY_FLASHMODE_DIO=y 80 | # CONFIG_ESPTOOLPY_FLASHMODE_DOUT is not set 81 | CONFIG_ESPTOOLPY_FLASHMODE="dio" 82 | # CONFIG_ESPTOOLPY_FLASHFREQ_80M is not set 83 | CONFIG_ESPTOOLPY_FLASHFREQ_40M=y 84 | # CONFIG_ESPTOOLPY_FLASHFREQ_26M is not set 85 | # CONFIG_ESPTOOLPY_FLASHFREQ_20M is not set 86 | CONFIG_ESPTOOLPY_FLASHFREQ="40m" 87 | # CONFIG_ESPTOOLPY_FLASHSIZE_1MB is not set 88 | # CONFIG_ESPTOOLPY_FLASHSIZE_2MB is not set 89 | CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y 90 | # CONFIG_ESPTOOLPY_FLASHSIZE_8MB is not set 91 | # CONFIG_ESPTOOLPY_FLASHSIZE_16MB is not set 92 | CONFIG_ESPTOOLPY_FLASHSIZE="4MB" 93 | CONFIG_ESPTOOLPY_FLASHSIZE_DETECT=y 94 | CONFIG_ESPTOOLPY_BEFORE_RESET=y 95 | # CONFIG_ESPTOOLPY_BEFORE_NORESET is not set 96 | CONFIG_ESPTOOLPY_BEFORE="default_reset" 97 | CONFIG_ESPTOOLPY_AFTER_RESET=y 98 | # CONFIG_ESPTOOLPY_AFTER_NORESET is not set 99 | CONFIG_ESPTOOLPY_AFTER="hard_reset" 100 | # CONFIG_ESPTOOLPY_MONITOR_BAUD_9600B is not set 101 | # CONFIG_ESPTOOLPY_MONITOR_BAUD_57600B is not set 102 | CONFIG_ESPTOOLPY_MONITOR_BAUD_115200B=y 103 | # CONFIG_ESPTOOLPY_MONITOR_BAUD_230400B is not set 104 | # CONFIG_ESPTOOLPY_MONITOR_BAUD_921600B is not set 105 | # CONFIG_ESPTOOLPY_MONITOR_BAUD_2MB is not set 106 | # CONFIG_ESPTOOLPY_MONITOR_BAUD_OTHER is not set 107 | CONFIG_ESPTOOLPY_MONITOR_BAUD_OTHER_VAL=115200 108 | CONFIG_ESPTOOLPY_MONITOR_BAUD=115200 109 | # end of Serial flasher config 110 | 111 | # 112 | # Partition Table 113 | # 114 | # CONFIG_PARTITION_TABLE_SINGLE_APP is not set 115 | # CONFIG_PARTITION_TABLE_TWO_OTA is not set 116 | CONFIG_PARTITION_TABLE_CUSTOM=y 117 | CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" 118 | CONFIG_PARTITION_TABLE_FILENAME="partitions.csv" 119 | CONFIG_PARTITION_TABLE_OFFSET=0x8000 120 | CONFIG_PARTITION_TABLE_MD5=y 121 | # end of Partition Table 122 | 123 | # 124 | # Arduino Configuration 125 | # 126 | CONFIG_ENABLE_ARDUINO_DEPENDS=y 127 | CONFIG_AUTOSTART_ARDUINO=y 128 | # CONFIG_ARDUINO_RUN_CORE0 is not set 129 | CONFIG_ARDUINO_RUN_CORE1=y 130 | # CONFIG_ARDUINO_RUN_NO_AFFINITY is not set 131 | CONFIG_ARDUINO_RUNNING_CORE=1 132 | # CONFIG_ARDUINO_EVENT_RUN_CORE0 is not set 133 | CONFIG_ARDUINO_EVENT_RUN_CORE1=y 134 | # CONFIG_ARDUINO_EVENT_RUN_NO_AFFINITY is not set 135 | CONFIG_ARDUINO_EVENT_RUNNING_CORE=1 136 | # CONFIG_ARDUINO_UDP_RUN_CORE0 is not set 137 | CONFIG_ARDUINO_UDP_RUN_CORE1=y 138 | # CONFIG_ARDUINO_UDP_RUN_NO_AFFINITY is not set 139 | CONFIG_ARDUINO_UDP_TASK_PRIORITY=3 140 | CONFIG_ARDUINO_UDP_RUNNING_CORE=1 141 | # CONFIG_ARDUINO_ISR_IRAM is not set 142 | CONFIG_DISABLE_HAL_LOCKS=y 143 | 144 | # 145 | # Debug Log Configuration 146 | # 147 | # CONFIG_ARDUHAL_LOG_DEFAULT_LEVEL_NONE is not set 148 | # CONFIG_ARDUHAL_LOG_DEFAULT_LEVEL_ERROR is not set 149 | CONFIG_ARDUHAL_LOG_DEFAULT_LEVEL_WARN=y 150 | # CONFIG_ARDUHAL_LOG_DEFAULT_LEVEL_INFO is not set 151 | # CONFIG_ARDUHAL_LOG_DEFAULT_LEVEL_DEBUG is not set 152 | # CONFIG_ARDUHAL_LOG_DEFAULT_LEVEL_VERBOSE is not set 153 | CONFIG_ARDUHAL_LOG_DEFAULT_LEVEL=2 154 | # CONFIG_ARDUHAL_LOG_COLORS is not set 155 | # CONFIG_ARDUHAL_ESP_LOG is not set 156 | # end of Debug Log Configuration 157 | 158 | CONFIG_ARDUHAL_PARTITION_SCHEME_DEFAULT=y 159 | # CONFIG_ARDUHAL_PARTITION_SCHEME_MINIMAL is not set 160 | # CONFIG_ARDUHAL_PARTITION_SCHEME_NO_OTA is not set 161 | # CONFIG_ARDUHAL_PARTITION_SCHEME_HUGE_APP is not set 162 | # CONFIG_ARDUHAL_PARTITION_SCHEME_MIN_SPIFFS is not set 163 | CONFIG_ARDUHAL_PARTITION_SCHEME="default" 164 | CONFIG_AUTOCONNECT_WIFI=y 165 | # CONFIG_ARDUINO_SELECTIVE_COMPILATION is not set 166 | CONFIG_ARDUINO_SELECTIVE_WiFi=y 167 | # end of Arduino Configuration 168 | 169 | # 170 | # Compiler options 171 | # 172 | # CONFIG_COMPILER_OPTIMIZATION_DEFAULT is not set 173 | CONFIG_COMPILER_OPTIMIZATION_SIZE=y 174 | # CONFIG_COMPILER_OPTIMIZATION_PERF is not set 175 | # CONFIG_COMPILER_OPTIMIZATION_NONE is not set 176 | # CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_ENABLE is not set 177 | # CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT is not set 178 | CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_DISABLE=y 179 | # CONFIG_COMPILER_CXX_EXCEPTIONS is not set 180 | # CONFIG_COMPILER_CXX_RTTI is not set 181 | # CONFIG_COMPILER_STACK_CHECK_MODE_NONE is not set 182 | CONFIG_COMPILER_STACK_CHECK_MODE_NORM=y 183 | # CONFIG_COMPILER_STACK_CHECK_MODE_STRONG is not set 184 | # CONFIG_COMPILER_STACK_CHECK_MODE_ALL is not set 185 | CONFIG_COMPILER_STACK_CHECK=y 186 | # CONFIG_COMPILER_WARN_WRITE_STRINGS is not set 187 | # CONFIG_COMPILER_DISABLE_GCC8_WARNINGS is not set 188 | # end of Compiler options 189 | 190 | # 191 | # Component config 192 | # 193 | 194 | # 195 | # Application Level Tracing 196 | # 197 | CONFIG_APPTRACE_DEST_TRAX=y 198 | # CONFIG_APPTRACE_DEST_NONE is not set 199 | CONFIG_APPTRACE_ENABLE=y 200 | CONFIG_APPTRACE_LOCK_ENABLE=y 201 | CONFIG_APPTRACE_ONPANIC_HOST_FLUSH_TMO=-1 202 | CONFIG_APPTRACE_POSTMORTEM_FLUSH_THRESH=0 203 | CONFIG_APPTRACE_PENDING_DATA_SIZE_MAX=0 204 | 205 | # 206 | # FreeRTOS SystemView Tracing 207 | # 208 | # CONFIG_SYSVIEW_ENABLE is not set 209 | # end of FreeRTOS SystemView Tracing 210 | 211 | # CONFIG_APPTRACE_GCOV_ENABLE is not set 212 | # end of Application Level Tracing 213 | 214 | # 215 | # Bluetooth 216 | # 217 | CONFIG_BT_ENABLED=y 218 | 219 | # 220 | # Bluetooth controller 221 | # 222 | # CONFIG_BTDM_CTRL_MODE_BLE_ONLY is not set 223 | # CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY is not set 224 | CONFIG_BTDM_CTRL_MODE_BTDM=y 225 | CONFIG_BTDM_CTRL_BLE_MAX_CONN=2 226 | CONFIG_BTDM_CTRL_BR_EDR_MAX_ACL_CONN=2 227 | CONFIG_BTDM_CTRL_BR_EDR_MAX_SYNC_CONN=1 228 | # CONFIG_BTDM_CTRL_BR_EDR_SCO_DATA_PATH_HCI is not set 229 | CONFIG_BTDM_CTRL_BR_EDR_SCO_DATA_PATH_PCM=y 230 | CONFIG_BTDM_CTRL_BR_EDR_SCO_DATA_PATH_EFF=1 231 | CONFIG_BTDM_CTRL_PCM_ROLE_EDGE_CONFIG=y 232 | CONFIG_BTDM_CTRL_PCM_ROLE_MASTER=y 233 | # CONFIG_BTDM_CTRL_PCM_ROLE_SLAVE is not set 234 | CONFIG_BTDM_CTRL_PCM_POLAR_FALLING_EDGE=y 235 | # CONFIG_BTDM_CTRL_PCM_POLAR_RISING_EDGE is not set 236 | CONFIG_BTDM_CTRL_PCM_ROLE_EFF=0 237 | CONFIG_BTDM_CTRL_PCM_POLAR_EFF=0 238 | CONFIG_BTDM_CTRL_AUTO_LATENCY=y 239 | CONFIG_BTDM_CTRL_AUTO_LATENCY_EFF=y 240 | CONFIG_BTDM_CTRL_LEGACY_AUTH_VENDOR_EVT=y 241 | CONFIG_BTDM_CTRL_LEGACY_AUTH_VENDOR_EVT_EFF=y 242 | CONFIG_BTDM_CTRL_BLE_MAX_CONN_EFF=2 243 | CONFIG_BTDM_CTRL_BR_EDR_MAX_ACL_CONN_EFF=2 244 | CONFIG_BTDM_CTRL_BR_EDR_MAX_SYNC_CONN_EFF=1 245 | CONFIG_BTDM_CTRL_PINNED_TO_CORE_0=y 246 | # CONFIG_BTDM_CTRL_PINNED_TO_CORE_1 is not set 247 | CONFIG_BTDM_CTRL_PINNED_TO_CORE=0 248 | CONFIG_BTDM_CTRL_HCI_MODE_VHCI=y 249 | # CONFIG_BTDM_CTRL_HCI_MODE_UART_H4 is not set 250 | 251 | # 252 | # MODEM SLEEP Options 253 | # 254 | CONFIG_BTDM_MODEM_SLEEP=y 255 | CONFIG_BTDM_MODEM_SLEEP_MODE_ORIG=y 256 | # CONFIG_BTDM_MODEM_SLEEP_MODE_EVED is not set 257 | CONFIG_BTDM_LPCLK_SEL_MAIN_XTAL=y 258 | # end of MODEM SLEEP Options 259 | 260 | CONFIG_BTDM_BLE_DEFAULT_SCA_250PPM=y 261 | CONFIG_BTDM_BLE_SLEEP_CLOCK_ACCURACY_INDEX_EFF=1 262 | # CONFIG_BTDM_BLE_SCAN_DUPL is not set 263 | CONFIG_BTDM_CTRL_FULL_SCAN_SUPPORTED=y 264 | CONFIG_BTDM_BLE_ADV_REPORT_FLOW_CTRL_SUPP=y 265 | CONFIG_BTDM_BLE_ADV_REPORT_FLOW_CTRL_NUM=100 266 | CONFIG_BTDM_BLE_ADV_REPORT_DISCARD_THRSHOLD=20 267 | CONFIG_BTDM_COEX_BT_OPTIONS=y 268 | CONFIG_BTDM_COEX_BLE_ADV_HIGH_PRIORITY=y 269 | # end of Bluetooth controller 270 | 271 | CONFIG_BT_BLUEDROID_ENABLED=y 272 | # CONFIG_BT_NIMBLE_ENABLED is not set 273 | # CONFIG_BT_CONTROLLER_ONLY is not set 274 | 275 | # 276 | # Bluedroid Options 277 | # 278 | CONFIG_BT_BTC_TASK_STACK_SIZE=2048 279 | CONFIG_BT_BLUEDROID_PINNED_TO_CORE_0=y 280 | # CONFIG_BT_BLUEDROID_PINNED_TO_CORE_1 is not set 281 | CONFIG_BT_BLUEDROID_PINNED_TO_CORE=0 282 | CONFIG_BT_BTU_TASK_STACK_SIZE=2048 283 | # CONFIG_BT_BLUEDROID_MEM_DEBUG is not set 284 | CONFIG_BT_CLASSIC_ENABLED=y 285 | # CONFIG_BT_A2DP_ENABLE is not set 286 | # CONFIG_BT_SPP_ENABLED is not set 287 | # CONFIG_BT_HFP_ENABLE is not set 288 | # CONFIG_BT_HID_HOST_ENABLED is not set 289 | CONFIG_BT_SSP_ENABLED=y 290 | CONFIG_BT_BLE_ENABLED=y 291 | CONFIG_BT_GATTS_ENABLE=y 292 | # CONFIG_BT_GATTS_PPCP_CHAR_GAP is not set 293 | # CONFIG_BT_GATTS_SEND_SERVICE_CHANGE_MANUAL is not set 294 | CONFIG_BT_GATTS_SEND_SERVICE_CHANGE_AUTO=y 295 | CONFIG_BT_GATTS_SEND_SERVICE_CHANGE_MODE=0 296 | CONFIG_BT_GATTC_ENABLE=y 297 | CONFIG_BT_GATTC_CACHE_NVS_FLASH=y 298 | CONFIG_BT_BLE_SMP_ENABLE=y 299 | # CONFIG_BT_SMP_SLAVE_CON_PARAMS_UPD_ENABLE is not set 300 | CONFIG_BT_STACK_NO_LOG=y 301 | CONFIG_BT_ACL_CONNECTIONS=2 302 | # CONFIG_BT_ALLOCATION_FROM_SPIRAM_FIRST is not set 303 | CONFIG_BT_BLE_DYNAMIC_ENV_MEMORY=y 304 | # CONFIG_BT_BLE_HOST_QUEUE_CONG_CHECK is not set 305 | CONFIG_BT_SMP_ENABLE=y 306 | # CONFIG_BT_BLE_ACT_SCAN_REP_ADV_SCAN is not set 307 | CONFIG_BT_BLE_ESTAB_LINK_CONN_TOUT=30 308 | CONFIG_BT_RESERVE_DRAM=0xdb5c 309 | # end of Bluedroid Options 310 | # end of Bluetooth 311 | 312 | # CONFIG_BLE_MESH is not set 313 | 314 | # 315 | # CoAP Configuration 316 | # 317 | CONFIG_COAP_MBEDTLS_PSK=y 318 | # CONFIG_COAP_MBEDTLS_PKI is not set 319 | # CONFIG_COAP_MBEDTLS_DEBUG is not set 320 | CONFIG_COAP_LOG_DEFAULT_LEVEL=0 321 | # end of CoAP Configuration 322 | 323 | # 324 | # Driver configurations 325 | # 326 | 327 | # 328 | # ADC configuration 329 | # 330 | # CONFIG_ADC_FORCE_XPD_FSM is not set 331 | CONFIG_ADC_DISABLE_DAC=y 332 | # end of ADC configuration 333 | 334 | # 335 | # SPI configuration 336 | # 337 | # CONFIG_SPI_MASTER_IN_IRAM is not set 338 | CONFIG_SPI_MASTER_ISR_IN_IRAM=y 339 | # CONFIG_SPI_SLAVE_IN_IRAM is not set 340 | CONFIG_SPI_SLAVE_ISR_IN_IRAM=y 341 | # end of SPI configuration 342 | 343 | # 344 | # UART configuration 345 | # 346 | # CONFIG_UART_ISR_IN_IRAM is not set 347 | # end of UART configuration 348 | 349 | # 350 | # RTCIO configuration 351 | # 352 | # CONFIG_RTCIO_SUPPORT_RTC_GPIO_DESC is not set 353 | # end of RTCIO configuration 354 | # end of Driver configurations 355 | 356 | # 357 | # eFuse Bit Manager 358 | # 359 | # CONFIG_EFUSE_CUSTOM_TABLE is not set 360 | # CONFIG_EFUSE_VIRTUAL is not set 361 | # CONFIG_EFUSE_CODE_SCHEME_COMPAT_NONE is not set 362 | CONFIG_EFUSE_CODE_SCHEME_COMPAT_3_4=y 363 | # CONFIG_EFUSE_CODE_SCHEME_COMPAT_REPEAT is not set 364 | CONFIG_EFUSE_MAX_BLK_LEN=192 365 | # end of eFuse Bit Manager 366 | 367 | # 368 | # ESP-TLS 369 | # 370 | CONFIG_ESP_TLS_USING_MBEDTLS=y 371 | # CONFIG_ESP_TLS_USE_SECURE_ELEMENT is not set 372 | # CONFIG_ESP_TLS_SERVER is not set 373 | # CONFIG_ESP_TLS_PSK_VERIFICATION is not set 374 | # end of ESP-TLS 375 | 376 | # 377 | # ESP32-specific 378 | # 379 | CONFIG_ESP32_REV_MIN_0=y 380 | # CONFIG_ESP32_REV_MIN_1 is not set 381 | # CONFIG_ESP32_REV_MIN_2 is not set 382 | # CONFIG_ESP32_REV_MIN_3 is not set 383 | CONFIG_ESP32_REV_MIN=0 384 | CONFIG_ESP32_DPORT_WORKAROUND=y 385 | # CONFIG_ESP32_DEFAULT_CPU_FREQ_80 is not set 386 | # CONFIG_ESP32_DEFAULT_CPU_FREQ_160 is not set 387 | CONFIG_ESP32_DEFAULT_CPU_FREQ_240=y 388 | CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ=240 389 | # CONFIG_ESP32_SPIRAM_SUPPORT is not set 390 | CONFIG_ESP32_MEMMAP_TRACEMEM=y 391 | CONFIG_ESP32_MEMMAP_TRACEMEM_TWOBANKS=y 392 | # CONFIG_ESP32_TRAX is not set 393 | CONFIG_ESP32_TRACEMEM_RESERVE_DRAM=0x8000 394 | # CONFIG_ESP32_UNIVERSAL_MAC_ADDRESSES_TWO is not set 395 | CONFIG_ESP32_UNIVERSAL_MAC_ADDRESSES_FOUR=y 396 | CONFIG_ESP32_UNIVERSAL_MAC_ADDRESSES=4 397 | # CONFIG_ESP32_ULP_COPROC_ENABLED is not set 398 | CONFIG_ESP32_ULP_COPROC_RESERVE_MEM=0 399 | CONFIG_ESP32_DEBUG_OCDAWARE=y 400 | CONFIG_ESP32_BROWNOUT_DET=y 401 | CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_0=y 402 | # CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_1 is not set 403 | # CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_2 is not set 404 | # CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_3 is not set 405 | # CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_4 is not set 406 | # CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_5 is not set 407 | # CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_6 is not set 408 | # CONFIG_ESP32_BROWNOUT_DET_LVL_SEL_7 is not set 409 | CONFIG_ESP32_BROWNOUT_DET_LVL=0 410 | CONFIG_ESP32_REDUCE_PHY_TX_POWER=y 411 | CONFIG_ESP32_TIME_SYSCALL_USE_RTC_FRC1=y 412 | # CONFIG_ESP32_TIME_SYSCALL_USE_RTC is not set 413 | # CONFIG_ESP32_TIME_SYSCALL_USE_FRC1 is not set 414 | # CONFIG_ESP32_TIME_SYSCALL_USE_NONE is not set 415 | CONFIG_ESP32_RTC_CLK_SRC_INT_RC=y 416 | # CONFIG_ESP32_RTC_CLK_SRC_EXT_CRYS is not set 417 | # CONFIG_ESP32_RTC_CLK_SRC_EXT_OSC is not set 418 | # CONFIG_ESP32_RTC_CLK_SRC_INT_8MD256 is not set 419 | CONFIG_ESP32_RTC_CLK_CAL_CYCLES=1024 420 | CONFIG_ESP32_DEEP_SLEEP_WAKEUP_DELAY=2000 421 | CONFIG_ESP32_XTAL_FREQ_40=y 422 | # CONFIG_ESP32_XTAL_FREQ_26 is not set 423 | # CONFIG_ESP32_XTAL_FREQ_AUTO is not set 424 | CONFIG_ESP32_XTAL_FREQ=40 425 | # CONFIG_ESP32_DISABLE_BASIC_ROM_CONSOLE is not set 426 | # CONFIG_ESP32_COMPATIBLE_PRE_V2_1_BOOTLOADERS is not set 427 | # CONFIG_ESP32_USE_FIXED_STATIC_RAM_SIZE is not set 428 | CONFIG_ESP32_DPORT_DIS_INTERRUPT_LVL=5 429 | # end of ESP32-specific 430 | 431 | # 432 | # Power Management 433 | # 434 | # CONFIG_PM_ENABLE is not set 435 | # end of Power Management 436 | 437 | # 438 | # ADC-Calibration 439 | # 440 | CONFIG_ADC_CAL_EFUSE_TP_ENABLE=y 441 | CONFIG_ADC_CAL_EFUSE_VREF_ENABLE=y 442 | CONFIG_ADC_CAL_LUT_ENABLE=y 443 | # end of ADC-Calibration 444 | 445 | # 446 | # Common ESP-related 447 | # 448 | CONFIG_ESP_ERR_TO_NAME_LOOKUP=y 449 | CONFIG_ESP_SYSTEM_EVENT_QUEUE_SIZE=24 450 | CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=1792 451 | CONFIG_ESP_MAIN_TASK_STACK_SIZE=2048 452 | CONFIG_ESP_IPC_TASK_STACK_SIZE=2048 453 | CONFIG_ESP_IPC_USES_CALLERS_PRIORITY=y 454 | CONFIG_ESP_MINIMAL_SHARED_STACK_SIZE=1536 455 | CONFIG_ESP_CONSOLE_UART_DEFAULT=y 456 | # CONFIG_ESP_CONSOLE_UART_CUSTOM is not set 457 | # CONFIG_ESP_CONSOLE_UART_NONE is not set 458 | CONFIG_ESP_CONSOLE_UART_NUM=0 459 | CONFIG_ESP_CONSOLE_UART_TX_GPIO=1 460 | CONFIG_ESP_CONSOLE_UART_RX_GPIO=3 461 | CONFIG_ESP_CONSOLE_UART_BAUDRATE=115200 462 | CONFIG_ESP_INT_WDT=y 463 | CONFIG_ESP_INT_WDT_TIMEOUT_MS=500 464 | CONFIG_ESP_INT_WDT_CHECK_CPU1=y 465 | CONFIG_ESP_TASK_WDT=y 466 | # CONFIG_ESP_TASK_WDT_PANIC is not set 467 | CONFIG_ESP_TASK_WDT_TIMEOUT_S=8 468 | CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0=y 469 | CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU1=y 470 | # CONFIG_ESP_PANIC_HANDLER_IRAM is not set 471 | CONFIG_ESP_MAC_ADDR_UNIVERSE_WIFI_STA=y 472 | CONFIG_ESP_MAC_ADDR_UNIVERSE_WIFI_AP=y 473 | CONFIG_ESP_MAC_ADDR_UNIVERSE_BT=y 474 | CONFIG_ESP_MAC_ADDR_UNIVERSE_BT_OFFSET=2 475 | CONFIG_ESP_MAC_ADDR_UNIVERSE_ETH=y 476 | # end of Common ESP-related 477 | 478 | # 479 | # Ethernet 480 | # 481 | CONFIG_ETH_ENABLED=y 482 | CONFIG_ETH_USE_ESP32_EMAC=y 483 | CONFIG_ETH_PHY_INTERFACE_RMII=y 484 | # CONFIG_ETH_PHY_INTERFACE_MII is not set 485 | CONFIG_ETH_RMII_CLK_INPUT=y 486 | # CONFIG_ETH_RMII_CLK_OUTPUT is not set 487 | CONFIG_ETH_RMII_CLK_IN_GPIO=0 488 | CONFIG_ETH_DMA_BUFFER_SIZE=512 489 | CONFIG_ETH_DMA_RX_BUFFER_NUM=5 490 | CONFIG_ETH_DMA_TX_BUFFER_NUM=5 491 | CONFIG_ETH_USE_SPI_ETHERNET=y 492 | # CONFIG_ETH_SPI_ETHERNET_DM9051 is not set 493 | # CONFIG_ETH_USE_OPENETH is not set 494 | # end of Ethernet 495 | 496 | # 497 | # Event Loop Library 498 | # 499 | # CONFIG_ESP_EVENT_LOOP_PROFILING is not set 500 | CONFIG_ESP_EVENT_POST_FROM_ISR=y 501 | CONFIG_ESP_EVENT_POST_FROM_IRAM_ISR=y 502 | # end of Event Loop Library 503 | 504 | # 505 | # GDB Stub 506 | # 507 | # end of GDB Stub 508 | 509 | # 510 | # ESP HTTP client 511 | # 512 | CONFIG_ESP_HTTP_CLIENT_ENABLE_HTTPS=y 513 | CONFIG_ESP_HTTP_CLIENT_ENABLE_BASIC_AUTH=y 514 | # end of ESP HTTP client 515 | 516 | # 517 | # HTTP Server 518 | # 519 | CONFIG_HTTPD_MAX_REQ_HDR_LEN=512 520 | CONFIG_HTTPD_MAX_URI_LEN=512 521 | CONFIG_HTTPD_ERR_RESP_NO_DELAY=y 522 | CONFIG_HTTPD_PURGE_BUF_LEN=32 523 | # CONFIG_HTTPD_LOG_PURGE_DATA is not set 524 | CONFIG_HTTPD_WS_SUPPORT=y 525 | # end of HTTP Server 526 | 527 | # 528 | # ESP HTTPS OTA 529 | # 530 | # CONFIG_OTA_ALLOW_HTTP is not set 531 | # end of ESP HTTPS OTA 532 | 533 | # 534 | # ESP HTTPS server 535 | # 536 | # CONFIG_ESP_HTTPS_SERVER_ENABLE is not set 537 | # end of ESP HTTPS server 538 | 539 | # 540 | # ESP NETIF Adapter 541 | # 542 | CONFIG_ESP_NETIF_IP_LOST_TIMER_INTERVAL=120 543 | CONFIG_ESP_NETIF_TCPIP_LWIP=y 544 | # CONFIG_ESP_NETIF_LOOPBACK is not set 545 | CONFIG_ESP_NETIF_TCPIP_ADAPTER_COMPATIBLE_LAYER=y 546 | # end of ESP NETIF Adapter 547 | 548 | # 549 | # ESP System Settings 550 | # 551 | # CONFIG_ESP_SYSTEM_PANIC_PRINT_HALT is not set 552 | CONFIG_ESP_SYSTEM_PANIC_PRINT_REBOOT=y 553 | # CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT is not set 554 | # CONFIG_ESP_SYSTEM_PANIC_GDBSTUB is not set 555 | # end of ESP System Settings 556 | 557 | # 558 | # High resolution timer (esp_timer) 559 | # 560 | # CONFIG_ESP_TIMER_PROFILING is not set 561 | CONFIG_ESP_TIMER_TASK_STACK_SIZE=2048 562 | # CONFIG_ESP_TIMER_IMPL_FRC2 is not set 563 | CONFIG_ESP_TIMER_IMPL_TG0_LAC=y 564 | # end of High resolution timer (esp_timer) 565 | 566 | # 567 | # Wi-Fi 568 | # 569 | CONFIG_ESP32_WIFI_SW_COEXIST_ENABLE=y 570 | CONFIG_ESP32_WIFI_STATIC_RX_BUFFER_NUM=10 571 | CONFIG_ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM=24 572 | # CONFIG_ESP32_WIFI_STATIC_TX_BUFFER is not set 573 | CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER=y 574 | CONFIG_ESP32_WIFI_TX_BUFFER_TYPE=1 575 | CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER_NUM=24 576 | # CONFIG_ESP32_WIFI_CSI_ENABLED is not set 577 | CONFIG_ESP32_WIFI_AMPDU_TX_ENABLED=y 578 | CONFIG_ESP32_WIFI_TX_BA_WIN=6 579 | CONFIG_ESP32_WIFI_AMPDU_RX_ENABLED=y 580 | CONFIG_ESP32_WIFI_RX_BA_WIN=6 581 | CONFIG_ESP32_WIFI_NVS_ENABLED=y 582 | CONFIG_ESP32_WIFI_TASK_PINNED_TO_CORE_0=y 583 | # CONFIG_ESP32_WIFI_TASK_PINNED_TO_CORE_1 is not set 584 | CONFIG_ESP32_WIFI_SOFTAP_BEACON_MAX_LEN=752 585 | CONFIG_ESP32_WIFI_MGMT_SBUF_NUM=32 586 | # CONFIG_ESP32_WIFI_DEBUG_LOG_ENABLE is not set 587 | CONFIG_ESP32_WIFI_IRAM_OPT=y 588 | CONFIG_ESP32_WIFI_RX_IRAM_OPT=y 589 | # CONFIG_ESP32_WIFI_ENABLE_WPA3_SAE is not set 590 | # end of Wi-Fi 591 | 592 | # 593 | # PHY 594 | # 595 | CONFIG_ESP32_PHY_CALIBRATION_AND_DATA_STORAGE=y 596 | # CONFIG_ESP32_PHY_INIT_DATA_IN_PARTITION is not set 597 | CONFIG_ESP32_PHY_MAX_WIFI_TX_POWER=20 598 | CONFIG_ESP32_PHY_MAX_TX_POWER=20 599 | # end of PHY 600 | 601 | # 602 | # Core dump 603 | # 604 | # CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH is not set 605 | # CONFIG_ESP32_ENABLE_COREDUMP_TO_UART is not set 606 | CONFIG_ESP32_ENABLE_COREDUMP_TO_NONE=y 607 | # end of Core dump 608 | 609 | # 610 | # FAT Filesystem support 611 | # 612 | # CONFIG_FATFS_CODEPAGE_DYNAMIC is not set 613 | CONFIG_FATFS_CODEPAGE_437=y 614 | # CONFIG_FATFS_CODEPAGE_720 is not set 615 | # CONFIG_FATFS_CODEPAGE_737 is not set 616 | # CONFIG_FATFS_CODEPAGE_771 is not set 617 | # CONFIG_FATFS_CODEPAGE_775 is not set 618 | # CONFIG_FATFS_CODEPAGE_850 is not set 619 | # CONFIG_FATFS_CODEPAGE_852 is not set 620 | # CONFIG_FATFS_CODEPAGE_855 is not set 621 | # CONFIG_FATFS_CODEPAGE_857 is not set 622 | # CONFIG_FATFS_CODEPAGE_860 is not set 623 | # CONFIG_FATFS_CODEPAGE_861 is not set 624 | # CONFIG_FATFS_CODEPAGE_862 is not set 625 | # CONFIG_FATFS_CODEPAGE_863 is not set 626 | # CONFIG_FATFS_CODEPAGE_864 is not set 627 | # CONFIG_FATFS_CODEPAGE_865 is not set 628 | # CONFIG_FATFS_CODEPAGE_866 is not set 629 | # CONFIG_FATFS_CODEPAGE_869 is not set 630 | # CONFIG_FATFS_CODEPAGE_932 is not set 631 | # CONFIG_FATFS_CODEPAGE_936 is not set 632 | # CONFIG_FATFS_CODEPAGE_949 is not set 633 | # CONFIG_FATFS_CODEPAGE_950 is not set 634 | CONFIG_FATFS_CODEPAGE=437 635 | CONFIG_FATFS_LFN_NONE=y 636 | # CONFIG_FATFS_LFN_HEAP is not set 637 | # CONFIG_FATFS_LFN_STACK is not set 638 | CONFIG_FATFS_FS_LOCK=0 639 | CONFIG_FATFS_TIMEOUT_MS=10000 640 | CONFIG_FATFS_PER_FILE_CACHE=y 641 | # end of FAT Filesystem support 642 | 643 | # 644 | # Modbus configuration 645 | # 646 | CONFIG_FMB_COMM_MODE_RTU_EN=y 647 | CONFIG_FMB_COMM_MODE_ASCII_EN=y 648 | CONFIG_FMB_MASTER_TIMEOUT_MS_RESPOND=150 649 | CONFIG_FMB_MASTER_DELAY_MS_CONVERT=200 650 | CONFIG_FMB_QUEUE_LENGTH=20 651 | CONFIG_FMB_SERIAL_TASK_STACK_SIZE=2048 652 | CONFIG_FMB_SERIAL_BUF_SIZE=256 653 | CONFIG_FMB_SERIAL_ASCII_BITS_PER_SYMB=8 654 | CONFIG_FMB_SERIAL_ASCII_TIMEOUT_RESPOND_MS=1000 655 | CONFIG_FMB_SERIAL_TASK_PRIO=10 656 | # CONFIG_FMB_CONTROLLER_SLAVE_ID_SUPPORT is not set 657 | CONFIG_FMB_CONTROLLER_NOTIFY_TIMEOUT=20 658 | CONFIG_FMB_CONTROLLER_NOTIFY_QUEUE_SIZE=20 659 | CONFIG_FMB_CONTROLLER_STACK_SIZE=4096 660 | CONFIG_FMB_EVENT_QUEUE_TIMEOUT=20 661 | CONFIG_FMB_TIMER_PORT_ENABLED=y 662 | CONFIG_FMB_TIMER_GROUP=0 663 | CONFIG_FMB_TIMER_INDEX=0 664 | # CONFIG_FMB_TIMER_ISR_IN_IRAM is not set 665 | # end of Modbus configuration 666 | 667 | # 668 | # FreeRTOS 669 | # 670 | # CONFIG_FREERTOS_UNICORE is not set 671 | CONFIG_FREERTOS_NO_AFFINITY=0x7FFFFFFF 672 | CONFIG_FREERTOS_CORETIMER_0=y 673 | # CONFIG_FREERTOS_CORETIMER_1 is not set 674 | CONFIG_FREERTOS_HZ=200 675 | CONFIG_FREERTOS_ASSERT_ON_UNTESTED_FUNCTION=y 676 | # CONFIG_FREERTOS_CHECK_STACKOVERFLOW_NONE is not set 677 | # CONFIG_FREERTOS_CHECK_STACKOVERFLOW_PTRVAL is not set 678 | CONFIG_FREERTOS_CHECK_STACKOVERFLOW_CANARY=y 679 | CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK=y 680 | CONFIG_FREERTOS_INTERRUPT_BACKTRACE=y 681 | CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS=5 682 | CONFIG_FREERTOS_ASSERT_FAIL_ABORT=y 683 | # CONFIG_FREERTOS_ASSERT_FAIL_PRINT_CONTINUE is not set 684 | # CONFIG_FREERTOS_ASSERT_DISABLE is not set 685 | CONFIG_FREERTOS_IDLE_TASK_STACKSIZE=768 686 | CONFIG_FREERTOS_ISR_STACKSIZE=1792 687 | # CONFIG_FREERTOS_LEGACY_HOOKS is not set 688 | CONFIG_FREERTOS_MAX_TASK_NAME_LEN=16 689 | # CONFIG_FREERTOS_SUPPORT_STATIC_ALLOCATION is not set 690 | CONFIG_FREERTOS_TIMER_TASK_PRIORITY=1 691 | CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=1536 692 | CONFIG_FREERTOS_TIMER_QUEUE_LENGTH=10 693 | CONFIG_FREERTOS_QUEUE_REGISTRY_SIZE=0 694 | CONFIG_FREERTOS_USE_TRACE_FACILITY=y 695 | CONFIG_FREERTOS_USE_STATS_FORMATTING_FUNCTIONS=y 696 | # CONFIG_FREERTOS_VTASKLIST_INCLUDE_COREID is not set 697 | CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS=y 698 | CONFIG_FREERTOS_RUN_TIME_STATS_USING_ESP_TIMER=y 699 | # CONFIG_FREERTOS_RUN_TIME_STATS_USING_CPU_CLK is not set 700 | CONFIG_FREERTOS_CHECK_MUTEX_GIVEN_BY_OWNER=y 701 | # CONFIG_FREERTOS_CHECK_PORT_CRITICAL_COMPLIANCE is not set 702 | CONFIG_FREERTOS_DEBUG_OCDAWARE=y 703 | # CONFIG_FREERTOS_FPU_IN_ISR is not set 704 | # end of FreeRTOS 705 | 706 | # 707 | # Heap memory debugging 708 | # 709 | # CONFIG_HEAP_POISONING_DISABLED is not set 710 | CONFIG_HEAP_POISONING_LIGHT=y 711 | # CONFIG_HEAP_POISONING_COMPREHENSIVE is not set 712 | CONFIG_HEAP_TRACING_OFF=y 713 | # CONFIG_HEAP_TRACING_STANDALONE is not set 714 | # CONFIG_HEAP_TRACING_TOHOST is not set 715 | CONFIG_HEAP_TASK_TRACKING=y 716 | # CONFIG_HEAP_ABORT_WHEN_ALLOCATION_FAILS is not set 717 | # end of Heap memory debugging 718 | 719 | # 720 | # jsmn 721 | # 722 | # CONFIG_JSMN_PARENT_LINKS is not set 723 | # CONFIG_JSMN_STRICT is not set 724 | # end of jsmn 725 | 726 | # 727 | # libsodium 728 | # 729 | # end of libsodium 730 | 731 | # 732 | # Log output 733 | # 734 | # CONFIG_LOG_DEFAULT_LEVEL_NONE is not set 735 | # CONFIG_LOG_DEFAULT_LEVEL_ERROR is not set 736 | # CONFIG_LOG_DEFAULT_LEVEL_WARN is not set 737 | CONFIG_LOG_DEFAULT_LEVEL_INFO=y 738 | # CONFIG_LOG_DEFAULT_LEVEL_DEBUG is not set 739 | # CONFIG_LOG_DEFAULT_LEVEL_VERBOSE is not set 740 | CONFIG_LOG_DEFAULT_LEVEL=3 741 | CONFIG_LOG_COLORS=y 742 | CONFIG_LOG_TIMESTAMP_SOURCE_RTOS=y 743 | # CONFIG_LOG_TIMESTAMP_SOURCE_SYSTEM is not set 744 | # end of Log output 745 | 746 | # 747 | # LWIP 748 | # 749 | CONFIG_LWIP_LOCAL_HOSTNAME="espressif" 750 | CONFIG_LWIP_DNS_SUPPORT_MDNS_QUERIES=y 751 | # CONFIG_LWIP_L2_TO_L3_COPY is not set 752 | # CONFIG_LWIP_IRAM_OPTIMIZATION is not set 753 | CONFIG_LWIP_TIMERS_ONDEMAND=y 754 | CONFIG_LWIP_MAX_SOCKETS=12 755 | # CONFIG_LWIP_USE_ONLY_LWIP_SELECT is not set 756 | # CONFIG_LWIP_SO_LINGER is not set 757 | CONFIG_LWIP_SO_REUSE=y 758 | CONFIG_LWIP_SO_REUSE_RXTOALL=y 759 | CONFIG_LWIP_SO_RCVBUF=y 760 | # CONFIG_LWIP_NETBUF_RECVINFO is not set 761 | CONFIG_LWIP_IP4_FRAG=y 762 | CONFIG_LWIP_IP6_FRAG=y 763 | # CONFIG_LWIP_IP4_REASSEMBLY is not set 764 | # CONFIG_LWIP_IP6_REASSEMBLY is not set 765 | # CONFIG_LWIP_IP_FORWARD is not set 766 | # CONFIG_LWIP_STATS is not set 767 | # CONFIG_LWIP_ETHARP_TRUST_IP_MAC is not set 768 | CONFIG_LWIP_ESP_GRATUITOUS_ARP=y 769 | CONFIG_LWIP_GARP_TMR_INTERVAL=60 770 | CONFIG_LWIP_TCPIP_RECVMBOX_SIZE=24 771 | CONFIG_LWIP_DHCP_DOES_ARP_CHECK=y 772 | # CONFIG_LWIP_DHCP_RESTORE_LAST_IP is not set 773 | 774 | # 775 | # DHCP server 776 | # 777 | CONFIG_LWIP_DHCPS_LEASE_UNIT=60 778 | CONFIG_LWIP_DHCPS_MAX_STATION_NUM=8 779 | # end of DHCP server 780 | 781 | # CONFIG_LWIP_AUTOIP is not set 782 | # CONFIG_LWIP_IPV6_AUTOCONFIG is not set 783 | CONFIG_LWIP_NETIF_LOOPBACK=y 784 | CONFIG_LWIP_LOOPBACK_MAX_PBUFS=8 785 | 786 | # 787 | # TCP 788 | # 789 | CONFIG_LWIP_MAX_ACTIVE_TCP=16 790 | CONFIG_LWIP_MAX_LISTENING_TCP=16 791 | CONFIG_LWIP_TCP_HIGH_SPEED_RETRANSMISSION=y 792 | CONFIG_LWIP_TCP_MAXRTX=12 793 | CONFIG_LWIP_TCP_SYNMAXRTX=6 794 | CONFIG_LWIP_TCP_MSS=1440 795 | CONFIG_LWIP_TCP_TMR_INTERVAL=250 796 | CONFIG_LWIP_TCP_MSL=60000 797 | CONFIG_LWIP_TCP_SND_BUF_DEFAULT=5744 798 | CONFIG_LWIP_TCP_WND_DEFAULT=5744 799 | CONFIG_LWIP_TCP_RECVMBOX_SIZE=6 800 | CONFIG_LWIP_TCP_QUEUE_OOSEQ=y 801 | # CONFIG_LWIP_TCP_SACK_OUT is not set 802 | # CONFIG_LWIP_TCP_KEEP_CONNECTION_WHEN_IP_CHANGES is not set 803 | # CONFIG_LWIP_TCP_OVERSIZE_MSS is not set 804 | CONFIG_LWIP_TCP_OVERSIZE_QUARTER_MSS=y 805 | # CONFIG_LWIP_TCP_OVERSIZE_DISABLE is not set 806 | CONFIG_LWIP_TCP_RTO_TIME=1500 807 | # end of TCP 808 | 809 | # 810 | # UDP 811 | # 812 | CONFIG_LWIP_MAX_UDP_PCBS=16 813 | CONFIG_LWIP_UDP_RECVMBOX_SIZE=6 814 | # end of UDP 815 | 816 | CONFIG_LWIP_TCPIP_TASK_STACK_SIZE=2048 817 | # CONFIG_LWIP_TCPIP_TASK_AFFINITY_NO_AFFINITY is not set 818 | CONFIG_LWIP_TCPIP_TASK_AFFINITY_CPU0=y 819 | # CONFIG_LWIP_TCPIP_TASK_AFFINITY_CPU1 is not set 820 | CONFIG_LWIP_TCPIP_TASK_AFFINITY=0x0 821 | # CONFIG_LWIP_PPP_SUPPORT is not set 822 | CONFIG_LWIP_IPV6_MEMP_NUM_ND6_QUEUE=3 823 | CONFIG_LWIP_IPV6_ND6_NUM_NEIGHBORS=5 824 | 825 | # 826 | # ICMP 827 | # 828 | # CONFIG_LWIP_MULTICAST_PING is not set 829 | # CONFIG_LWIP_BROADCAST_PING is not set 830 | # end of ICMP 831 | 832 | # 833 | # LWIP RAW API 834 | # 835 | CONFIG_LWIP_MAX_RAW_PCBS=16 836 | # end of LWIP RAW API 837 | 838 | # 839 | # SNTP 840 | # 841 | CONFIG_LWIP_DHCP_MAX_NTP_SERVERS=1 842 | CONFIG_LWIP_SNTP_UPDATE_DELAY=3600000 843 | # end of SNTP 844 | 845 | CONFIG_LWIP_ESP_LWIP_ASSERT=y 846 | # end of LWIP 847 | 848 | # 849 | # mbedTLS 850 | # 851 | CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC=y 852 | # CONFIG_MBEDTLS_DEFAULT_MEM_ALLOC is not set 853 | # CONFIG_MBEDTLS_CUSTOM_MEM_ALLOC is not set 854 | CONFIG_MBEDTLS_ASYMMETRIC_CONTENT_LEN=y 855 | CONFIG_MBEDTLS_SSL_IN_CONTENT_LEN=16384 856 | CONFIG_MBEDTLS_SSL_OUT_CONTENT_LEN=4096 857 | # CONFIG_MBEDTLS_DYNAMIC_BUFFER is not set 858 | # CONFIG_MBEDTLS_DEBUG is not set 859 | 860 | # 861 | # Certificate Bundle 862 | # 863 | CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=y 864 | CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_FULL=y 865 | # CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_CMN is not set 866 | # CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_NONE is not set 867 | # CONFIG_MBEDTLS_CUSTOM_CERTIFICATE_BUNDLE is not set 868 | # end of Certificate Bundle 869 | 870 | # CONFIG_MBEDTLS_ECP_RESTARTABLE is not set 871 | # CONFIG_MBEDTLS_CMAC_C is not set 872 | CONFIG_MBEDTLS_HARDWARE_AES=y 873 | # CONFIG_MBEDTLS_HARDWARE_MPI is not set 874 | CONFIG_MBEDTLS_HARDWARE_SHA=y 875 | # CONFIG_MBEDTLS_ATCA_HW_ECDSA_SIGN is not set 876 | # CONFIG_MBEDTLS_ATCA_HW_ECDSA_VERIFY is not set 877 | CONFIG_MBEDTLS_HAVE_TIME=y 878 | # CONFIG_MBEDTLS_HAVE_TIME_DATE is not set 879 | CONFIG_MBEDTLS_ECDSA_DETERMINISTIC=y 880 | CONFIG_MBEDTLS_SHA512_C=y 881 | CONFIG_MBEDTLS_TLS_SERVER_AND_CLIENT=y 882 | # CONFIG_MBEDTLS_TLS_SERVER_ONLY is not set 883 | # CONFIG_MBEDTLS_TLS_CLIENT_ONLY is not set 884 | # CONFIG_MBEDTLS_TLS_DISABLED is not set 885 | CONFIG_MBEDTLS_TLS_SERVER=y 886 | CONFIG_MBEDTLS_TLS_CLIENT=y 887 | CONFIG_MBEDTLS_TLS_ENABLED=y 888 | 889 | # 890 | # TLS Key Exchange Methods 891 | # 892 | CONFIG_MBEDTLS_PSK_MODES=y 893 | CONFIG_MBEDTLS_KEY_EXCHANGE_PSK=y 894 | CONFIG_MBEDTLS_KEY_EXCHANGE_DHE_PSK=y 895 | CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_PSK=y 896 | CONFIG_MBEDTLS_KEY_EXCHANGE_RSA_PSK=y 897 | CONFIG_MBEDTLS_KEY_EXCHANGE_RSA=y 898 | CONFIG_MBEDTLS_KEY_EXCHANGE_DHE_RSA=y 899 | CONFIG_MBEDTLS_KEY_EXCHANGE_ELLIPTIC_CURVE=y 900 | CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_RSA=y 901 | CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA=y 902 | CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA=y 903 | CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_RSA=y 904 | # end of TLS Key Exchange Methods 905 | 906 | CONFIG_MBEDTLS_SSL_RENEGOTIATION=y 907 | # CONFIG_MBEDTLS_SSL_PROTO_SSL3 is not set 908 | CONFIG_MBEDTLS_SSL_PROTO_TLS1=y 909 | CONFIG_MBEDTLS_SSL_PROTO_TLS1_1=y 910 | CONFIG_MBEDTLS_SSL_PROTO_TLS1_2=y 911 | # CONFIG_MBEDTLS_SSL_PROTO_DTLS is not set 912 | CONFIG_MBEDTLS_SSL_ALPN=y 913 | CONFIG_MBEDTLS_CLIENT_SSL_SESSION_TICKETS=y 914 | CONFIG_MBEDTLS_SERVER_SSL_SESSION_TICKETS=y 915 | 916 | # 917 | # Symmetric Ciphers 918 | # 919 | CONFIG_MBEDTLS_AES_C=y 920 | # CONFIG_MBEDTLS_CAMELLIA_C is not set 921 | # CONFIG_MBEDTLS_DES_C is not set 922 | CONFIG_MBEDTLS_RC4_DISABLED=y 923 | # CONFIG_MBEDTLS_RC4_ENABLED_NO_DEFAULT is not set 924 | # CONFIG_MBEDTLS_RC4_ENABLED is not set 925 | # CONFIG_MBEDTLS_BLOWFISH_C is not set 926 | # CONFIG_MBEDTLS_XTEA_C is not set 927 | CONFIG_MBEDTLS_CCM_C=y 928 | CONFIG_MBEDTLS_GCM_C=y 929 | # end of Symmetric Ciphers 930 | 931 | # CONFIG_MBEDTLS_RIPEMD160_C is not set 932 | 933 | # 934 | # Certificates 935 | # 936 | CONFIG_MBEDTLS_PEM_PARSE_C=y 937 | CONFIG_MBEDTLS_PEM_WRITE_C=y 938 | CONFIG_MBEDTLS_X509_CRL_PARSE_C=y 939 | CONFIG_MBEDTLS_X509_CSR_PARSE_C=y 940 | # end of Certificates 941 | 942 | CONFIG_MBEDTLS_ECP_C=y 943 | CONFIG_MBEDTLS_ECDH_C=y 944 | CONFIG_MBEDTLS_ECDSA_C=y 945 | # CONFIG_MBEDTLS_ECJPAKE_C is not set 946 | CONFIG_MBEDTLS_ECP_DP_SECP192R1_ENABLED=y 947 | CONFIG_MBEDTLS_ECP_DP_SECP224R1_ENABLED=y 948 | CONFIG_MBEDTLS_ECP_DP_SECP256R1_ENABLED=y 949 | CONFIG_MBEDTLS_ECP_DP_SECP384R1_ENABLED=y 950 | CONFIG_MBEDTLS_ECP_DP_SECP521R1_ENABLED=y 951 | CONFIG_MBEDTLS_ECP_DP_SECP192K1_ENABLED=y 952 | CONFIG_MBEDTLS_ECP_DP_SECP224K1_ENABLED=y 953 | CONFIG_MBEDTLS_ECP_DP_SECP256K1_ENABLED=y 954 | CONFIG_MBEDTLS_ECP_DP_BP256R1_ENABLED=y 955 | CONFIG_MBEDTLS_ECP_DP_BP384R1_ENABLED=y 956 | CONFIG_MBEDTLS_ECP_DP_BP512R1_ENABLED=y 957 | CONFIG_MBEDTLS_ECP_DP_CURVE25519_ENABLED=y 958 | CONFIG_MBEDTLS_ECP_NIST_OPTIM=y 959 | # CONFIG_MBEDTLS_POLY1305_C is not set 960 | # CONFIG_MBEDTLS_CHACHA20_C is not set 961 | # CONFIG_MBEDTLS_HKDF_C is not set 962 | # CONFIG_MBEDTLS_THREADING_C is not set 963 | # CONFIG_MBEDTLS_SECURITY_RISKS is not set 964 | # end of mbedTLS 965 | 966 | # 967 | # mDNS 968 | # 969 | CONFIG_MDNS_MAX_SERVICES=10 970 | CONFIG_MDNS_TASK_PRIORITY=1 971 | CONFIG_MDNS_TASK_STACK_SIZE=2560 972 | # CONFIG_MDNS_TASK_AFFINITY_NO_AFFINITY is not set 973 | CONFIG_MDNS_TASK_AFFINITY_CPU0=y 974 | # CONFIG_MDNS_TASK_AFFINITY_CPU1 is not set 975 | CONFIG_MDNS_TASK_AFFINITY=0x0 976 | CONFIG_MDNS_SERVICE_ADD_TIMEOUT_MS=2000 977 | CONFIG_MDNS_TIMER_PERIOD_MS=100 978 | # end of mDNS 979 | 980 | # 981 | # ESP-MQTT Configurations 982 | # 983 | CONFIG_MQTT_PROTOCOL_311=y 984 | # CONFIG_MQTT_TRANSPORT_SSL is not set 985 | CONFIG_MQTT_TRANSPORT_WEBSOCKET=y 986 | # CONFIG_MQTT_USE_CUSTOM_CONFIG is not set 987 | # CONFIG_MQTT_TASK_CORE_SELECTION_ENABLED is not set 988 | # CONFIG_MQTT_CUSTOM_OUTBOX is not set 989 | # end of ESP-MQTT Configurations 990 | 991 | # 992 | # Newlib 993 | # 994 | CONFIG_NEWLIB_STDOUT_LINE_ENDING_CRLF=y 995 | # CONFIG_NEWLIB_STDOUT_LINE_ENDING_LF is not set 996 | # CONFIG_NEWLIB_STDOUT_LINE_ENDING_CR is not set 997 | # CONFIG_NEWLIB_STDIN_LINE_ENDING_CRLF is not set 998 | # CONFIG_NEWLIB_STDIN_LINE_ENDING_LF is not set 999 | CONFIG_NEWLIB_STDIN_LINE_ENDING_CR=y 1000 | # CONFIG_NEWLIB_NANO_FORMAT is not set 1001 | # end of Newlib 1002 | 1003 | # 1004 | # NVS 1005 | # 1006 | # end of NVS 1007 | 1008 | # 1009 | # OpenSSL 1010 | # 1011 | # CONFIG_OPENSSL_DEBUG is not set 1012 | CONFIG_OPENSSL_ASSERT_DO_NOTHING=y 1013 | # CONFIG_OPENSSL_ASSERT_EXIT is not set 1014 | # end of OpenSSL 1015 | 1016 | # 1017 | # PThreads 1018 | # 1019 | CONFIG_PTHREAD_TASK_PRIO_DEFAULT=5 1020 | CONFIG_PTHREAD_TASK_STACK_SIZE_DEFAULT=2048 1021 | CONFIG_PTHREAD_STACK_MIN=768 1022 | CONFIG_PTHREAD_DEFAULT_CORE_NO_AFFINITY=y 1023 | # CONFIG_PTHREAD_DEFAULT_CORE_0 is not set 1024 | # CONFIG_PTHREAD_DEFAULT_CORE_1 is not set 1025 | CONFIG_PTHREAD_TASK_CORE_DEFAULT=-1 1026 | CONFIG_PTHREAD_TASK_NAME_DEFAULT="pthread" 1027 | # end of PThreads 1028 | 1029 | # 1030 | # SPI Flash driver 1031 | # 1032 | # CONFIG_SPI_FLASH_VERIFY_WRITE is not set 1033 | # CONFIG_SPI_FLASH_ENABLE_COUNTERS is not set 1034 | CONFIG_SPI_FLASH_ROM_DRIVER_PATCH=y 1035 | CONFIG_SPI_FLASH_DANGEROUS_WRITE_ABORTS=y 1036 | # CONFIG_SPI_FLASH_DANGEROUS_WRITE_FAILS is not set 1037 | # CONFIG_SPI_FLASH_DANGEROUS_WRITE_ALLOWED is not set 1038 | # CONFIG_SPI_FLASH_USE_LEGACY_IMPL is not set 1039 | # CONFIG_SPI_FLASH_SHARE_SPI1_BUS is not set 1040 | # CONFIG_SPI_FLASH_BYPASS_BLOCK_ERASE is not set 1041 | CONFIG_SPI_FLASH_YIELD_DURING_ERASE=y 1042 | CONFIG_SPI_FLASH_ERASE_YIELD_DURATION_MS=20 1043 | CONFIG_SPI_FLASH_ERASE_YIELD_TICKS=1 1044 | 1045 | # 1046 | # Auto-detect flash chips 1047 | # 1048 | CONFIG_SPI_FLASH_SUPPORT_ISSI_CHIP=y 1049 | CONFIG_SPI_FLASH_SUPPORT_MXIC_CHIP=y 1050 | CONFIG_SPI_FLASH_SUPPORT_GD_CHIP=y 1051 | # end of Auto-detect flash chips 1052 | # end of SPI Flash driver 1053 | 1054 | # 1055 | # SPIFFS Configuration 1056 | # 1057 | CONFIG_SPIFFS_MAX_PARTITIONS=3 1058 | 1059 | # 1060 | # SPIFFS Cache Configuration 1061 | # 1062 | CONFIG_SPIFFS_CACHE=y 1063 | CONFIG_SPIFFS_CACHE_WR=y 1064 | # CONFIG_SPIFFS_CACHE_STATS is not set 1065 | # end of SPIFFS Cache Configuration 1066 | 1067 | CONFIG_SPIFFS_PAGE_CHECK=y 1068 | CONFIG_SPIFFS_GC_MAX_RUNS=10 1069 | # CONFIG_SPIFFS_GC_STATS is not set 1070 | CONFIG_SPIFFS_PAGE_SIZE=256 1071 | CONFIG_SPIFFS_OBJ_NAME_LEN=32 1072 | # CONFIG_SPIFFS_FOLLOW_SYMLINKS is not set 1073 | CONFIG_SPIFFS_USE_MAGIC=y 1074 | CONFIG_SPIFFS_USE_MAGIC_LENGTH=y 1075 | CONFIG_SPIFFS_META_LENGTH=4 1076 | CONFIG_SPIFFS_USE_MTIME=y 1077 | 1078 | # 1079 | # Debug Configuration 1080 | # 1081 | # CONFIG_SPIFFS_DBG is not set 1082 | # CONFIG_SPIFFS_API_DBG is not set 1083 | # CONFIG_SPIFFS_GC_DBG is not set 1084 | # CONFIG_SPIFFS_CACHE_DBG is not set 1085 | # CONFIG_SPIFFS_CHECK_DBG is not set 1086 | # CONFIG_SPIFFS_TEST_VISUALISATION is not set 1087 | # end of Debug Configuration 1088 | # end of SPIFFS Configuration 1089 | 1090 | # 1091 | # TinyUSB 1092 | # 1093 | 1094 | # 1095 | # Descriptor configuration 1096 | # 1097 | CONFIG_USB_DESC_CUSTOM_VID=0x1234 1098 | CONFIG_USB_DESC_CUSTOM_PID=0x5678 1099 | # end of Descriptor configuration 1100 | # end of TinyUSB 1101 | 1102 | # 1103 | # Unity unit testing library 1104 | # 1105 | CONFIG_UNITY_ENABLE_FLOAT=y 1106 | CONFIG_UNITY_ENABLE_DOUBLE=y 1107 | # CONFIG_UNITY_ENABLE_COLOR is not set 1108 | CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=y 1109 | # CONFIG_UNITY_ENABLE_FIXTURE is not set 1110 | # CONFIG_UNITY_ENABLE_BACKTRACE_ON_FAIL is not set 1111 | # end of Unity unit testing library 1112 | 1113 | # 1114 | # Virtual file system 1115 | # 1116 | CONFIG_VFS_SUPPORT_IO=y 1117 | CONFIG_VFS_SUPPORT_DIR=y 1118 | CONFIG_VFS_SUPPORT_SELECT=y 1119 | CONFIG_VFS_SUPPRESS_SELECT_DEBUG_OUTPUT=y 1120 | CONFIG_VFS_SUPPORT_TERMIOS=y 1121 | 1122 | # 1123 | # Host File System I/O (Semihosting) 1124 | # 1125 | CONFIG_VFS_SEMIHOSTFS_MAX_MOUNT_POINTS=1 1126 | CONFIG_VFS_SEMIHOSTFS_HOST_PATH_MAX_LEN=128 1127 | # end of Host File System I/O (Semihosting) 1128 | # end of Virtual file system 1129 | 1130 | # 1131 | # Wear Levelling 1132 | # 1133 | # CONFIG_WL_SECTOR_SIZE_512 is not set 1134 | CONFIG_WL_SECTOR_SIZE_4096=y 1135 | CONFIG_WL_SECTOR_SIZE=4096 1136 | # end of Wear Levelling 1137 | 1138 | # 1139 | # Wi-Fi Provisioning Manager 1140 | # 1141 | CONFIG_WIFI_PROV_SCAN_MAX_ENTRIES=16 1142 | CONFIG_WIFI_PROV_AUTOSTOP_TIMEOUT=30 1143 | # end of Wi-Fi Provisioning Manager 1144 | 1145 | # 1146 | # Supplicant 1147 | # 1148 | CONFIG_WPA_MBEDTLS_CRYPTO=y 1149 | # CONFIG_WPA_DEBUG_PRINT is not set 1150 | # CONFIG_WPA_TESTING_OPTIONS is not set 1151 | # CONFIG_WPA_WPS_WARS is not set 1152 | # end of Supplicant 1153 | # end of Component config 1154 | 1155 | # 1156 | # Compatibility options 1157 | # 1158 | # CONFIG_LEGACY_INCLUDE_COMMON_HEADERS is not set 1159 | # end of Compatibility options 1160 | 1161 | # Deprecated options for backward compatibility 1162 | CONFIG_TOOLPREFIX="xtensa-esp32-elf-" 1163 | # CONFIG_LOG_BOOTLOADER_LEVEL_NONE is not set 1164 | # CONFIG_LOG_BOOTLOADER_LEVEL_ERROR is not set 1165 | # CONFIG_LOG_BOOTLOADER_LEVEL_WARN is not set 1166 | CONFIG_LOG_BOOTLOADER_LEVEL_INFO=y 1167 | # CONFIG_LOG_BOOTLOADER_LEVEL_DEBUG is not set 1168 | # CONFIG_LOG_BOOTLOADER_LEVEL_VERBOSE is not set 1169 | CONFIG_LOG_BOOTLOADER_LEVEL=3 1170 | # CONFIG_APP_ROLLBACK_ENABLE is not set 1171 | # CONFIG_FLASH_ENCRYPTION_ENABLED is not set 1172 | # CONFIG_FLASHMODE_QIO is not set 1173 | # CONFIG_FLASHMODE_QOUT is not set 1174 | CONFIG_FLASHMODE_DIO=y 1175 | # CONFIG_FLASHMODE_DOUT is not set 1176 | # CONFIG_MONITOR_BAUD_9600B is not set 1177 | # CONFIG_MONITOR_BAUD_57600B is not set 1178 | CONFIG_MONITOR_BAUD_115200B=y 1179 | # CONFIG_MONITOR_BAUD_230400B is not set 1180 | # CONFIG_MONITOR_BAUD_921600B is not set 1181 | # CONFIG_MONITOR_BAUD_2MB is not set 1182 | # CONFIG_MONITOR_BAUD_OTHER is not set 1183 | CONFIG_MONITOR_BAUD_OTHER_VAL=115200 1184 | CONFIG_MONITOR_BAUD=115200 1185 | # CONFIG_COMPILER_OPTIMIZATION_LEVEL_DEBUG is not set 1186 | CONFIG_COMPILER_OPTIMIZATION_LEVEL_RELEASE=y 1187 | # CONFIG_OPTIMIZATION_ASSERTIONS_ENABLED is not set 1188 | # CONFIG_OPTIMIZATION_ASSERTIONS_SILENT is not set 1189 | CONFIG_OPTIMIZATION_ASSERTIONS_DISABLED=y 1190 | # CONFIG_CXX_EXCEPTIONS is not set 1191 | # CONFIG_STACK_CHECK_NONE is not set 1192 | CONFIG_STACK_CHECK_NORM=y 1193 | # CONFIG_STACK_CHECK_STRONG is not set 1194 | # CONFIG_STACK_CHECK_ALL is not set 1195 | CONFIG_STACK_CHECK=y 1196 | # CONFIG_WARN_WRITE_STRINGS is not set 1197 | # CONFIG_DISABLE_GCC8_WARNINGS is not set 1198 | CONFIG_ESP32_APPTRACE_DEST_TRAX=y 1199 | # CONFIG_ESP32_APPTRACE_DEST_NONE is not set 1200 | CONFIG_ESP32_APPTRACE_ENABLE=y 1201 | CONFIG_ESP32_APPTRACE_LOCK_ENABLE=y 1202 | CONFIG_ESP32_APPTRACE_ONPANIC_HOST_FLUSH_TMO=-1 1203 | CONFIG_ESP32_APPTRACE_POSTMORTEM_FLUSH_TRAX_THRESH=0 1204 | CONFIG_ESP32_APPTRACE_PENDING_DATA_SIZE_MAX=0 1205 | # CONFIG_ESP32_GCOV_ENABLE is not set 1206 | # CONFIG_BTDM_CONTROLLER_MODE_BLE_ONLY is not set 1207 | # CONFIG_BTDM_CONTROLLER_MODE_BR_EDR_ONLY is not set 1208 | CONFIG_BTDM_CONTROLLER_MODE_BTDM=y 1209 | CONFIG_BTDM_CONTROLLER_BLE_MAX_CONN=2 1210 | CONFIG_BTDM_CONTROLLER_BR_EDR_MAX_ACL_CONN=2 1211 | CONFIG_BTDM_CONTROLLER_BR_EDR_MAX_SYNC_CONN=1 1212 | CONFIG_BTDM_CONTROLLER_BLE_MAX_CONN_EFF=2 1213 | CONFIG_BTDM_CONTROLLER_BR_EDR_MAX_ACL_CONN_EFF=2 1214 | CONFIG_BTDM_CONTROLLER_BR_EDR_MAX_SYNC_CONN_EFF=1 1215 | CONFIG_BTDM_CONTROLLER_PINNED_TO_CORE=0 1216 | CONFIG_BTDM_CONTROLLER_HCI_MODE_VHCI=y 1217 | # CONFIG_BTDM_CONTROLLER_HCI_MODE_UART_H4 is not set 1218 | CONFIG_BTDM_CONTROLLER_MODEM_SLEEP=y 1219 | # CONFIG_BLE_SCAN_DUPLICATE is not set 1220 | CONFIG_BTDM_CONTROLLER_FULL_SCAN_SUPPORTED=y 1221 | CONFIG_BLE_ADV_REPORT_FLOW_CONTROL_SUPPORTED=y 1222 | CONFIG_BLE_ADV_REPORT_FLOW_CONTROL_NUM=100 1223 | CONFIG_BLE_ADV_REPORT_DISCARD_THRSHOLD=20 1224 | CONFIG_BLUEDROID_ENABLED=y 1225 | # CONFIG_NIMBLE_ENABLED is not set 1226 | CONFIG_BTC_TASK_STACK_SIZE=2048 1227 | CONFIG_BLUEDROID_PINNED_TO_CORE_0=y 1228 | # CONFIG_BLUEDROID_PINNED_TO_CORE_1 is not set 1229 | CONFIG_BLUEDROID_PINNED_TO_CORE=0 1230 | CONFIG_BTU_TASK_STACK_SIZE=2048 1231 | # CONFIG_BLUEDROID_MEM_DEBUG is not set 1232 | CONFIG_CLASSIC_BT_ENABLED=y 1233 | # CONFIG_A2DP_ENABLE is not set 1234 | # CONFIG_HFP_ENABLE is not set 1235 | CONFIG_GATTS_ENABLE=y 1236 | # CONFIG_GATTS_SEND_SERVICE_CHANGE_MANUAL is not set 1237 | CONFIG_GATTS_SEND_SERVICE_CHANGE_AUTO=y 1238 | CONFIG_GATTS_SEND_SERVICE_CHANGE_MODE=0 1239 | CONFIG_GATTC_ENABLE=y 1240 | CONFIG_GATTC_CACHE_NVS_FLASH=y 1241 | CONFIG_BLE_SMP_ENABLE=y 1242 | # CONFIG_SMP_SLAVE_CON_PARAMS_UPD_ENABLE is not set 1243 | # CONFIG_BLE_HOST_QUEUE_CONGESTION_CHECK is not set 1244 | CONFIG_SMP_ENABLE=y 1245 | # CONFIG_BLE_ACTIVE_SCAN_REPORT_ADV_SCAN_RSP_INDIVIDUALLY is not set 1246 | CONFIG_BLE_ESTABLISH_LINK_CONNECTION_TIMEOUT=30 1247 | CONFIG_ADC2_DISABLE_DAC=y 1248 | # CONFIG_SPIRAM_SUPPORT is not set 1249 | CONFIG_MEMMAP_TRACEMEM=y 1250 | CONFIG_MEMMAP_TRACEMEM_TWOBANKS=y 1251 | CONFIG_TRACEMEM_RESERVE_DRAM=0x8000 1252 | # CONFIG_TWO_UNIVERSAL_MAC_ADDRESS is not set 1253 | CONFIG_FOUR_UNIVERSAL_MAC_ADDRESS=y 1254 | CONFIG_NUMBER_OF_UNIVERSAL_MAC_ADDRESS=4 1255 | # CONFIG_ULP_COPROC_ENABLED is not set 1256 | CONFIG_ULP_COPROC_RESERVE_MEM=0 1257 | CONFIG_BROWNOUT_DET=y 1258 | CONFIG_BROWNOUT_DET_LVL_SEL_0=y 1259 | # CONFIG_BROWNOUT_DET_LVL_SEL_1 is not set 1260 | # CONFIG_BROWNOUT_DET_LVL_SEL_2 is not set 1261 | # CONFIG_BROWNOUT_DET_LVL_SEL_3 is not set 1262 | # CONFIG_BROWNOUT_DET_LVL_SEL_4 is not set 1263 | # CONFIG_BROWNOUT_DET_LVL_SEL_5 is not set 1264 | # CONFIG_BROWNOUT_DET_LVL_SEL_6 is not set 1265 | # CONFIG_BROWNOUT_DET_LVL_SEL_7 is not set 1266 | CONFIG_BROWNOUT_DET_LVL=0 1267 | CONFIG_REDUCE_PHY_TX_POWER=y 1268 | CONFIG_ESP32_RTC_CLOCK_SOURCE_INTERNAL_RC=y 1269 | # CONFIG_ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL is not set 1270 | # CONFIG_ESP32_RTC_CLOCK_SOURCE_EXTERNAL_OSC is not set 1271 | # CONFIG_ESP32_RTC_CLOCK_SOURCE_INTERNAL_8MD256 is not set 1272 | # CONFIG_DISABLE_BASIC_ROM_CONSOLE is not set 1273 | # CONFIG_COMPATIBLE_PRE_V2_1_BOOTLOADERS is not set 1274 | CONFIG_SYSTEM_EVENT_QUEUE_SIZE=24 1275 | CONFIG_SYSTEM_EVENT_TASK_STACK_SIZE=1792 1276 | CONFIG_MAIN_TASK_STACK_SIZE=2048 1277 | CONFIG_IPC_TASK_STACK_SIZE=2048 1278 | CONFIG_CONSOLE_UART_DEFAULT=y 1279 | # CONFIG_CONSOLE_UART_CUSTOM is not set 1280 | # CONFIG_CONSOLE_UART_NONE is not set 1281 | CONFIG_CONSOLE_UART_NUM=0 1282 | CONFIG_CONSOLE_UART_TX_GPIO=1 1283 | CONFIG_CONSOLE_UART_RX_GPIO=3 1284 | CONFIG_CONSOLE_UART_BAUDRATE=115200 1285 | CONFIG_INT_WDT=y 1286 | CONFIG_INT_WDT_TIMEOUT_MS=500 1287 | CONFIG_INT_WDT_CHECK_CPU1=y 1288 | CONFIG_TASK_WDT=y 1289 | # CONFIG_TASK_WDT_PANIC is not set 1290 | CONFIG_TASK_WDT_TIMEOUT_S=8 1291 | CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU0=y 1292 | CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1=y 1293 | # CONFIG_EVENT_LOOP_PROFILING is not set 1294 | CONFIG_POST_EVENTS_FROM_ISR=y 1295 | CONFIG_POST_EVENTS_FROM_IRAM_ISR=y 1296 | # CONFIG_ESP32S2_PANIC_PRINT_HALT is not set 1297 | CONFIG_ESP32S2_PANIC_PRINT_REBOOT=y 1298 | # CONFIG_ESP32S2_PANIC_SILENT_REBOOT is not set 1299 | # CONFIG_ESP32S2_PANIC_GDBSTUB is not set 1300 | CONFIG_TIMER_TASK_STACK_SIZE=2048 1301 | CONFIG_SW_COEXIST_ENABLE=y 1302 | CONFIG_MB_MASTER_TIMEOUT_MS_RESPOND=150 1303 | CONFIG_MB_MASTER_DELAY_MS_CONVERT=200 1304 | CONFIG_MB_QUEUE_LENGTH=20 1305 | CONFIG_MB_SERIAL_TASK_STACK_SIZE=2048 1306 | CONFIG_MB_SERIAL_BUF_SIZE=256 1307 | CONFIG_MB_SERIAL_TASK_PRIO=10 1308 | # CONFIG_MB_CONTROLLER_SLAVE_ID_SUPPORT is not set 1309 | CONFIG_MB_CONTROLLER_NOTIFY_TIMEOUT=20 1310 | CONFIG_MB_CONTROLLER_NOTIFY_QUEUE_SIZE=20 1311 | CONFIG_MB_CONTROLLER_STACK_SIZE=4096 1312 | CONFIG_MB_EVENT_QUEUE_TIMEOUT=20 1313 | CONFIG_MB_TIMER_PORT_ENABLED=y 1314 | CONFIG_MB_TIMER_GROUP=0 1315 | CONFIG_MB_TIMER_INDEX=0 1316 | # CONFIG_SUPPORT_STATIC_ALLOCATION is not set 1317 | CONFIG_TIMER_TASK_PRIORITY=1 1318 | CONFIG_TIMER_TASK_STACK_DEPTH=1536 1319 | CONFIG_TIMER_QUEUE_LENGTH=10 1320 | # CONFIG_L2_TO_L3_COPY is not set 1321 | # CONFIG_USE_ONLY_LWIP_SELECT is not set 1322 | CONFIG_ESP_GRATUITOUS_ARP=y 1323 | CONFIG_GARP_TMR_INTERVAL=60 1324 | CONFIG_TCPIP_RECVMBOX_SIZE=24 1325 | CONFIG_TCP_MAXRTX=12 1326 | CONFIG_TCP_SYNMAXRTX=6 1327 | CONFIG_TCP_MSS=1440 1328 | CONFIG_TCP_MSL=60000 1329 | CONFIG_TCP_SND_BUF_DEFAULT=5744 1330 | CONFIG_TCP_WND_DEFAULT=5744 1331 | CONFIG_TCP_RECVMBOX_SIZE=6 1332 | CONFIG_TCP_QUEUE_OOSEQ=y 1333 | # CONFIG_ESP_TCP_KEEP_CONNECTION_WHEN_IP_CHANGES is not set 1334 | # CONFIG_TCP_OVERSIZE_MSS is not set 1335 | CONFIG_TCP_OVERSIZE_QUARTER_MSS=y 1336 | # CONFIG_TCP_OVERSIZE_DISABLE is not set 1337 | CONFIG_UDP_RECVMBOX_SIZE=6 1338 | CONFIG_TCPIP_TASK_STACK_SIZE=2048 1339 | # CONFIG_TCPIP_TASK_AFFINITY_NO_AFFINITY is not set 1340 | CONFIG_TCPIP_TASK_AFFINITY_CPU0=y 1341 | # CONFIG_TCPIP_TASK_AFFINITY_CPU1 is not set 1342 | CONFIG_TCPIP_TASK_AFFINITY=0x0 1343 | # CONFIG_PPP_SUPPORT is not set 1344 | CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT=5 1345 | CONFIG_ESP32_PTHREAD_TASK_STACK_SIZE_DEFAULT=2048 1346 | CONFIG_ESP32_PTHREAD_STACK_MIN=768 1347 | CONFIG_ESP32_DEFAULT_PTHREAD_CORE_NO_AFFINITY=y 1348 | # CONFIG_ESP32_DEFAULT_PTHREAD_CORE_0 is not set 1349 | # CONFIG_ESP32_DEFAULT_PTHREAD_CORE_1 is not set 1350 | CONFIG_ESP32_PTHREAD_TASK_CORE_DEFAULT=-1 1351 | CONFIG_ESP32_PTHREAD_TASK_NAME_DEFAULT="pthread" 1352 | CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ABORTS=y 1353 | # CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_FAILS is not set 1354 | # CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ALLOWED is not set 1355 | CONFIG_SUPPRESS_SELECT_DEBUG_OUTPUT=y 1356 | CONFIG_SUPPORT_TERMIOS=y 1357 | CONFIG_SEMIHOSTFS_MAX_MOUNT_POINTS=1 1358 | CONFIG_SEMIHOSTFS_HOST_PATH_MAX_LEN=128 1359 | # End of deprecated options 1360 | -------------------------------------------------------------------------------- /src/BluetoothScanner.cpp: -------------------------------------------------------------------------------- 1 | // Basic headers 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | // IDF headers 11 | #include 12 | #include 13 | #include 14 | #include "nvs.h" 15 | #include "nvs_flash.h" 16 | #include "esp_system.h" 17 | #include "esp_log.h" 18 | #include "esp32-hal-bt.h" 19 | #include "esp_bt.h" 20 | #include "esp_bt_main.h" 21 | #include "esp_bt_device.h" 22 | #include "esp_gap_bt_api.h" 23 | 24 | #include 25 | #include 26 | 27 | // Tweaked SDK configuration 28 | #include "sdkconfig.h" 29 | 30 | #include "BluetoothScanner.h" 31 | #include "parameter.h" 32 | #include "mqtt.h" 33 | #include "led.h" 34 | 35 | #include "stackDbgHelper.h" 36 | #include "CallbackHelper.h" 37 | 38 | 39 | #define GAP_TAG "GAP" 40 | #define SPP_TAG "SPP" 41 | #define GATTC_TAG "GATTC" 42 | 43 | #define BLE_SCAN_AFTER_READ_REMOTE_NAMES (0) 44 | 45 | 46 | #define BLE_SCAN_COMPLETE_CALLBACK_SIGNATURE void(BLEScanResults) 47 | #define GAP_CALLBACK_SIGNATURE void(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param) 48 | 49 | typedef void (*scanComplete_cb_t)(BLEScanResults); 50 | 51 | 52 | extern Timezone mTime; 53 | 54 | #define ENDIAN_CHANGE_U16(x) ((((x)&0xFF00) >> 8) + (((x)&0xFF) << 8)) 55 | 56 | // ================================================================================= 57 | // ================================================================================= 58 | // Free Functions: 59 | // ================================================================================= 60 | 61 | // ----------------------------------------------- 62 | #define BLUETOOTH_MON_VERSION "0.1.001" 63 | String createConfidenceMessage(const char* MAC, uint8_t confidence, const char* name, const char* timestamp, bool retain = false, const char* iBeaconStr = nullptr, const char* type = "KNOWN_MAC", const char* manufacturer = "Unknown") { 64 | char buf[420]; 65 | // Wed Jun 09 2021 20:03:45 GMT+0200 (CEST) 66 | snprintf(buf, 420, "{\"id\":\"%s\",%s%s\"confidence\":\"%d\",\"name\":\"%s\",\"manufacturer\":\"%s\",\"type\":\"%s\",\"retained\":\"%s\",\"timestamp\":\"%s\",\"version\":\"%s\"}", 67 | MAC, (iBeaconStr ? iBeaconStr : " "), (iBeaconStr ? "," : " "), confidence, name, manufacturer, type, (retain ? "true" : "false"), timestamp, BLUETOOTH_MON_VERSION); 68 | return buf; 69 | } 70 | 71 | // ----------------------------------------------- 72 | char *bda2str(const esp_bd_addr_t bda, char *str, size_t size) 73 | { 74 | if (bda == NULL || str == NULL || size < 18) { 75 | return NULL; 76 | } 77 | 78 | const uint8_t *p = bda; 79 | sprintf(str, "%02x:%02x:%02x:%02x:%02x:%02x", 80 | p[0], p[1], p[2], p[3], p[4], p[5]); 81 | return str; 82 | } 83 | 84 | // ----------------------------------------------- 85 | bool str2bda(const char* str, esp_bd_addr_t& mac) { 86 | if(strlen(str) < 12 || strlen(str) > 18) { 87 | return false; 88 | } 89 | 90 | uint8_t *p = mac; 91 | // Scan for MAC with semicolon separators 92 | int args_found = sscanf(str, "%" SCNx8 ":%" SCNx8 ":%" SCNx8 ":%" SCNx8 ":%" SCNx8 ":%" SCNx8, 93 | &p[0], &p[1], &p[2], &p[3], &p[4], &p[5]); 94 | if( args_found == 6 ) { 95 | return true; 96 | } 97 | 98 | // Scan for mac without semicolons 99 | args_found = sscanf(str, "%" SCNx8 "%" SCNx8 "%" SCNx8 "%" SCNx8 "%" SCNx8 "%" SCNx8, 100 | &p[0], &p[1], &p[2], &p[3], &p[4], &p[5]); 101 | 102 | if( args_found == 6 ) { 103 | return true; 104 | } 105 | 106 | // Invalid MAC 107 | return false; 108 | } 109 | 110 | // ----------------------------------------------- 111 | static char *uuid2str(esp_bt_uuid_t *uuid, char *str, size_t size) 112 | { 113 | if (uuid == NULL || str == NULL) { 114 | return NULL; 115 | } 116 | 117 | if (uuid->len == 2 && size >= 5) { 118 | sprintf(str, "%04x", uuid->uuid.uuid16); 119 | } else if (uuid->len == 4 && size >= 9) { 120 | sprintf(str, "%08x", uuid->uuid.uuid32); 121 | } else if (uuid->len == 16 && size >= 37) { 122 | uint8_t *p = uuid->uuid.uuid128; 123 | sprintf(str, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", 124 | p[15], p[14], p[13], p[12], p[11], p[10], p[9], p[8], 125 | p[7], p[6], p[5], p[4], p[3], p[2], p[1], p[0]); 126 | } else { 127 | return NULL; 128 | } 129 | 130 | return str; 131 | } 132 | 133 | // ----------------------------------------------- 134 | static bool get_name_from_eir(uint8_t *eir, uint8_t *bdname, uint8_t *bdname_len) 135 | { 136 | uint8_t *rmt_bdname = NULL; 137 | uint8_t rmt_bdname_len = 0; 138 | 139 | if (!eir) { 140 | return false; 141 | } 142 | 143 | rmt_bdname = esp_bt_gap_resolve_eir_data(eir, ESP_BT_EIR_TYPE_CMPL_LOCAL_NAME, &rmt_bdname_len); 144 | if (!rmt_bdname) { 145 | rmt_bdname = esp_bt_gap_resolve_eir_data(eir, ESP_BT_EIR_TYPE_SHORT_LOCAL_NAME, &rmt_bdname_len); 146 | } 147 | 148 | if (rmt_bdname) { 149 | if (rmt_bdname_len > ESP_BT_GAP_MAX_BDNAME_LEN) { 150 | rmt_bdname_len = ESP_BT_GAP_MAX_BDNAME_LEN; 151 | } 152 | 153 | if (bdname) { 154 | memcpy(bdname, rmt_bdname, rmt_bdname_len); 155 | bdname[rmt_bdname_len] = '\0'; 156 | } 157 | if (bdname_len) { 158 | *bdname_len = rmt_bdname_len; 159 | } 160 | return true; 161 | } 162 | 163 | return false; 164 | } 165 | 166 | // ----------------------------------------------- 167 | 168 | void BluetoothScanner::onResult(BLEAdvertisedDevice advertisedDevice) { 169 | bleAdvertisedDeviceResultQueue.push(advertisedDevice); 170 | } 171 | 172 | // ----------------------------------------------- 173 | void BluetoothScanner::HandleBleAdvertisementResult(BLEAdvertisedDevice& bleAdvertisedDeviceResult) { 174 | SCOPED_STACK_ENTRY; 175 | if (bleAdvertisedDeviceResult.haveName()) 176 | { 177 | mSerial.print("Device name: "); 178 | mSerial.println(bleAdvertisedDeviceResult.getName().c_str()); 179 | mSerial.println(""); 180 | } 181 | 182 | if (bleAdvertisedDeviceResult.haveServiceUUID()) 183 | { 184 | // BLEUUID devUUID = bleAdvertisedDeviceResult.getServiceUUID(); 185 | // mSerial.print("Found ServiceUUID: "); 186 | // mSerial.println(devUUID.toString().c_str()); 187 | // mSerial.println(""); 188 | } 189 | else 190 | { 191 | if (bleAdvertisedDeviceResult.haveManufacturerData() == true) 192 | { 193 | std::string strManufacturerData = bleAdvertisedDeviceResult.getManufacturerData(); 194 | 195 | uint8_t cManufacturerData[100]; 196 | strManufacturerData.copy((char *)cManufacturerData, strManufacturerData.length(), 0); 197 | 198 | if (strManufacturerData.length() == 25 && cManufacturerData[0] == 0x4C && cManufacturerData[1] == 0x00) 199 | { 200 | mSerial.println("Found an iBeacon!"); 201 | String rssi_s = "??"; 202 | if(bleAdvertisedDeviceResult.haveRSSI()) { 203 | rssi_s = bleAdvertisedDeviceResult.getRSSI(); 204 | } 205 | BLEBeacon oBeacon = BLEBeacon(); 206 | oBeacon.setData(strManufacturerData); 207 | mSerial.printf("iBeacon Frame\n"); 208 | 209 | BLEUUID uuid_swapped(oBeacon.getProximityUUID().getNative()->uuid.uuid128, 16, true); 210 | mSerial.printf("ID: %04X Major: %d Minor: %d UUID: %s Power: %d, RSSI: %s\n", oBeacon.getManufacturerId(), ENDIAN_CHANGE_U16(oBeacon.getMajor()), ENDIAN_CHANGE_U16(oBeacon.getMinor()), uuid_swapped.toString().c_str(), oBeacon.getSignalPower(), rssi_s.c_str()); 211 | 212 | std::lock_guard lock(iBeaconDevicesMutex); 213 | for (auto &iBeacon : iBeaconDevices){ 214 | if(iBeacon.uuid.equals(uuid_swapped)) { 215 | if(bleAdvertisedDeviceResult.haveRSSI()) { 216 | iBeacon.addRSSI(bleAdvertisedDeviceResult.getRSSI()); 217 | } 218 | iBeacon.power = oBeacon.getSignalPower(); 219 | iBeacon.confidence = 100; 220 | iBeacon.last_update_millis = millis(); 221 | 222 | int filteredRssi = iBeacon.getFilteredRSSI(); 223 | if(abs(filteredRssi - iBeacon.lastSentRssi) > 3) { 224 | char rssi_str[25]; 225 | snprintf(rssi_str, 24, "\"rssi\":\"%d\"", filteredRssi); 226 | iBeacon.lastSentRssi = filteredRssi; 227 | std::string topic = m_mqtt_topic + "/" + m_scanner_identity + "/" + iBeacon.name.c_str(); 228 | mqtt.send_message(topic.c_str(), 229 | createConfidenceMessage(iBeacon.uuid.toString().c_str(), iBeacon.confidence, iBeacon.name.c_str(), mTime.dateTime("D M d Y H:i:s ~G~M~TO (T)").c_str(), m_retain, rssi_str, "KNOWN_BEACON" ).c_str(), m_retain 230 | ); 231 | } 232 | else { 233 | mSerial.printf("%s rssi: %d, last sent rssi: %d\n", iBeacon.name.c_str(), filteredRssi, iBeacon.lastSentRssi); 234 | } 235 | } 236 | } 237 | } 238 | else 239 | { /* 240 | mSerial.println("Found another manufacturers beacon!"); 241 | mSerial.printf("strManufacturerData: %d ", strManufacturerData.length()); 242 | for (int i = 0; i < strManufacturerData.length(); i++) 243 | { 244 | mSerial.printf("[%X]", cManufacturerData[i]); 245 | } 246 | mSerial.printf("\n"); 247 | */ 248 | } 249 | } 250 | return; 251 | } 252 | 253 | uint8_t *payLoad = bleAdvertisedDeviceResult.getPayload(); 254 | 255 | BLEUUID checkUrlUUID = (uint16_t)0xfeaa; 256 | 257 | if (bleAdvertisedDeviceResult.getServiceUUID().equals(checkUrlUUID)) 258 | { 259 | if (payLoad[11] == 0x10) 260 | { 261 | mSerial.println("Found an EddystoneURL beacon!"); 262 | BLEEddystoneURL foundEddyURL = BLEEddystoneURL(); 263 | std::string eddyContent((char *)&payLoad[11]); // incomplete EddystoneURL struct! 264 | 265 | foundEddyURL.setData(eddyContent); 266 | std::string bareURL = foundEddyURL.getURL(); 267 | if (bareURL[0] == 0x00) 268 | { 269 | size_t payLoadLen = bleAdvertisedDeviceResult.getPayloadLength(); 270 | mSerial.println("DATA-->"); 271 | for (int idx = 0; idx < payLoadLen; idx++) 272 | { 273 | mSerial.printf("0x%08X ", payLoad[idx]); 274 | } 275 | mSerial.println("\nInvalid Data"); 276 | return; 277 | } 278 | 279 | mSerial.printf("Found URL: %s\n", foundEddyURL.getURL().c_str()); 280 | mSerial.printf("Decoded URL: %s\n", foundEddyURL.getDecodedURL().c_str()); 281 | mSerial.printf("TX power %d\n", foundEddyURL.getPower()); 282 | mSerial.println("\n"); 283 | } 284 | else if (payLoad[11] == 0x20) 285 | { 286 | mSerial.println("Found an EddystoneTLM beacon!"); 287 | BLEEddystoneTLM foundEddyURL = BLEEddystoneTLM(); 288 | std::string eddyContent((char *)&payLoad[11]); // incomplete EddystoneURL struct! 289 | 290 | eddyContent = "01234567890123"; 291 | 292 | for (int idx = 0; idx < 14; idx++) 293 | { 294 | eddyContent[idx] = payLoad[idx + 11]; 295 | } 296 | 297 | foundEddyURL.setData(eddyContent); 298 | mSerial.printf("Reported battery voltage: %dmV\n", foundEddyURL.getVolt()); 299 | mSerial.printf("Reported temperature from TLM class: %.2fC\n", (double)foundEddyURL.getTemp()); 300 | int temp = (int)payLoad[16] + (int)(payLoad[15] << 8); 301 | float calcTemp = temp / 256.0f; 302 | mSerial.printf("Reported temperature from data: %.2fC\n", calcTemp); 303 | mSerial.printf("Reported advertise count: %d\n", foundEddyURL.getCount()); 304 | mSerial.printf("Reported time since last reboot: %ds\n", foundEddyURL.getTime()); 305 | mSerial.println("\n"); 306 | mSerial.print(foundEddyURL.toString().c_str()); 307 | mSerial.println("\n"); 308 | } 309 | } 310 | } 311 | 312 | 313 | // ================================================================================= 314 | // ================================================================================= 315 | // Class Functions: 316 | // ================================================================================= 317 | 318 | // ----------------------------------------------- 319 | void BluetoothScanner::update_device_info(esp_bt_gap_cb_param_t *param) 320 | { 321 | char bda_str[18]; 322 | uint32_t cod = 0; 323 | int32_t rssi = -129; /* invalid value */ 324 | esp_bt_gap_dev_prop_t *p; 325 | 326 | ESP_LOGI(GAP_TAG, "Device found: %s", bda2str(param->disc_res.bda, bda_str, 18)); 327 | for (int i = 0; i < param->disc_res.num_prop; i++) { 328 | p = param->disc_res.prop + i; 329 | switch (p->type) { 330 | case ESP_BT_GAP_DEV_PROP_COD: 331 | cod = *(uint32_t *)(p->val); 332 | ESP_LOGI(GAP_TAG, "--Class of Device: 0x%x", cod); 333 | break; 334 | case ESP_BT_GAP_DEV_PROP_RSSI: 335 | rssi = *(int8_t *)(p->val); 336 | ESP_LOGI(GAP_TAG, "--RSSI: %d", rssi); 337 | break; 338 | case ESP_BT_GAP_DEV_PROP_BDNAME: 339 | default: 340 | break; 341 | } 342 | } 343 | 344 | /* search for device with MAJOR service class as "rendering" in COD */ 345 | app_gap_cb_t *p_dev = &m_dev_info; 346 | if (p_dev->dev_found && 0 != memcmp(param->disc_res.bda, p_dev->bda, ESP_BD_ADDR_LEN)) { 347 | return; 348 | } 349 | 350 | if (!esp_bt_gap_is_valid_cod(cod) || 351 | !(esp_bt_gap_get_cod_major_dev(cod) == ESP_BT_COD_MAJOR_DEV_PHONE)) { 352 | return; 353 | } 354 | 355 | memcpy(p_dev->bda, param->disc_res.bda, ESP_BD_ADDR_LEN); 356 | p_dev->dev_found = true; 357 | for (int i = 0; i < param->disc_res.num_prop; i++) { 358 | p = param->disc_res.prop + i; 359 | switch (p->type) { 360 | case ESP_BT_GAP_DEV_PROP_COD: 361 | p_dev->cod = *(uint32_t *)(p->val); 362 | break; 363 | case ESP_BT_GAP_DEV_PROP_RSSI: 364 | p_dev->rssi = *(int8_t *)(p->val); 365 | break; 366 | case ESP_BT_GAP_DEV_PROP_BDNAME: { 367 | uint8_t len = (p->len > ESP_BT_GAP_MAX_BDNAME_LEN) ? ESP_BT_GAP_MAX_BDNAME_LEN : 368 | (uint8_t)p->len; 369 | memcpy(p_dev->bdname, (uint8_t *)(p->val), len); 370 | p_dev->bdname[len] = '\0'; 371 | p_dev->bdname_len = len; 372 | break; 373 | } 374 | case ESP_BT_GAP_DEV_PROP_EIR: { 375 | memcpy(p_dev->eir, (uint8_t *)(p->val), p->len); 376 | p_dev->eir_len = p->len; 377 | break; 378 | } 379 | default: 380 | break; 381 | } 382 | } 383 | 384 | if (p_dev->eir && p_dev->bdname_len == 0) { 385 | get_name_from_eir(p_dev->eir, p_dev->bdname, &p_dev->bdname_len); 386 | ESP_LOGI(GAP_TAG, "Found a target device, address %s, name %s", bda_str, p_dev->bdname); 387 | p_dev->state = APP_GAP_STATE_DEVICE_DISCOVER_COMPLETE; 388 | ESP_LOGI(GAP_TAG, "Cancel device discovery ..."); 389 | esp_bt_gap_cancel_discovery(); 390 | } 391 | } 392 | 393 | // ----------------------------------------------- 394 | void BluetoothScanner::gap_callback(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param) 395 | { 396 | app_gap_cb_t *p_dev = &m_dev_info; 397 | char bda_str[18]; 398 | char uuid_str[37]; 399 | 400 | switch (event) { 401 | case ESP_BT_GAP_DISC_RES_EVT: { 402 | update_device_info(param); 403 | break; 404 | } 405 | case ESP_BT_GAP_DISC_STATE_CHANGED_EVT: { 406 | if (param->disc_st_chg.state == ESP_BT_GAP_DISCOVERY_STOPPED) { 407 | ESP_LOGI(GAP_TAG, "Device discovery stopped."); 408 | if ( (p_dev->state == APP_GAP_STATE_DEVICE_DISCOVER_COMPLETE || 409 | p_dev->state == APP_GAP_STATE_DEVICE_DISCOVERING) 410 | && p_dev->dev_found) { 411 | p_dev->state = APP_GAP_STATE_SERVICE_DISCOVERING; 412 | ESP_LOGI(GAP_TAG, "Discover services ..."); 413 | esp_bt_gap_get_remote_services(p_dev->bda); 414 | } 415 | } else if (param->disc_st_chg.state == ESP_BT_GAP_DISCOVERY_STARTED) { 416 | ESP_LOGI(GAP_TAG, "Discovery started."); 417 | } 418 | break; 419 | } 420 | case ESP_BT_GAP_RMT_SRVCS_EVT: { 421 | if (memcmp(param->rmt_srvcs.bda, p_dev->bda, ESP_BD_ADDR_LEN) == 0 && 422 | p_dev->state == APP_GAP_STATE_SERVICE_DISCOVERING) { 423 | p_dev->state = APP_GAP_STATE_SERVICE_DISCOVER_COMPLETE; 424 | if (param->rmt_srvcs.stat == ESP_BT_STATUS_SUCCESS) { 425 | ESP_LOGI(GAP_TAG, "Services for device %s found", bda2str(p_dev->bda, bda_str, 18)); 426 | for (int i = 0; i < param->rmt_srvcs.num_uuids; i++) { 427 | esp_bt_uuid_t *u = param->rmt_srvcs.uuid_list + i; 428 | ESP_LOGI(GAP_TAG, "--%s", uuid2str(u, uuid_str, 37)); 429 | // ESP_LOGI(GAP_TAG, "--%d", u->len); 430 | } 431 | } else { 432 | ESP_LOGI(GAP_TAG, "Services for device %s not found", bda2str(p_dev->bda, bda_str, 18)); 433 | } 434 | } 435 | break; 436 | } 437 | case ESP_BT_GAP_READ_RSSI_DELTA_EVT: 438 | ESP_LOGI(GAP_TAG, "Remote device status: %d, addr: %s, rssi: %d", param->read_rssi_delta.stat, bda2str(param->read_rssi_delta.bda, bda_str, 18), param->read_rssi_delta.rssi_delta); 439 | break; 440 | case ESP_BT_GAP_READ_REMOTE_NAME_EVT: 441 | SetReadRemoteNameResult(param->read_rmt_name); 442 | break; 443 | case ESP_BT_GAP_RMT_SRVC_REC_EVT: 444 | default: { 445 | ESP_LOGI(GAP_TAG, "event: %d", event); 446 | break; 447 | } 448 | } 449 | return; 450 | } 451 | 452 | // ----------------------------------------------- 453 | void BluetoothScanner::init() 454 | { 455 | esp_err_t ret; 456 | 457 | // No mem release allows BT and BLE to work side-by-side, but is there enough mem left for the rest...? 458 | //esp_bt_mem_release(ESP_BT_MODE_BTDM); 459 | // esp_bt_controller_mem_release(ESP_BT_MODE_BLE); // --> This fixes MEM Alloc failures in esp_bluedroid_enable!!! 460 | #if 1 461 | // Call bluetooth start from esp32 arduino framework 462 | if(!btStarted() && !btStart()){ 463 | log_e("btStart failed"); 464 | return; 465 | } 466 | 467 | esp_bluedroid_status_t bt_state = esp_bluedroid_get_status(); 468 | if(bt_state == ESP_BLUEDROID_STATUS_UNINITIALIZED){ 469 | if ((ret = esp_bluedroid_init()) != ESP_OK) { 470 | ESP_LOGE(GAP_TAG, "%s initialize bluedroid failed: %s\n", __func__, esp_err_to_name(ret)); 471 | return; 472 | } 473 | } 474 | 475 | if (bt_state != ESP_BLUEDROID_STATUS_ENABLED) { 476 | if ((ret = esp_bluedroid_enable()) != ESP_OK) { 477 | ESP_LOGE(GAP_TAG, "%s enable bluedroid failed: %s\n", __func__, esp_err_to_name(ret)); 478 | return; 479 | } 480 | } 481 | 482 | gap_init(); 483 | startupGap(); 484 | 485 | #endif 486 | 487 | // Setup static callback wrapper helper function for ble Scan Completed callback function: 488 | Callback::func = std::bind(&BluetoothScanner::bleScanCompleted, this, std::placeholders::_1); 489 | 490 | BLEDevice::init(""); 491 | pBLEScan = BLEDevice::getScan(); //create new scan 492 | pBLEScan->setAdvertisedDeviceCallbacks(this); 493 | pBLEScan->setActiveScan(false); //active scan uses more power, but get results faster 494 | pBLEScan->setInterval(200); 495 | pBLEScan->setWindow(160); // less or equal setInterval value 496 | // Scan for 100ms, then not for 25ms 497 | 498 | bleScan_shouldStart = true; 499 | } 500 | 501 | // ----------------------------------------------- 502 | void BluetoothScanner::setup() 503 | { 504 | // BT init malloc failure: 505 | 506 | // config.c L537 507 | // static void config_parse(nvs_handle_t fp, config_t *config) 508 | // ... 509 | // int buf_size = get_config_size_from_flash(fp); 510 | // char *buf = NULL; 511 | // if(buf_size == 0) { //First use nvs 512 | // goto error; 513 | // } 514 | // buf = osi_calloc(buf_size); 515 | 516 | } 517 | 518 | // ----------------------------------------------- 519 | void BluetoothScanner::bleScanCompleted(BLEScanResults) 520 | { 521 | mSerial.println(">> bleScanCompleted()"); 522 | 523 | bleScan_shouldStart = true; 524 | } 525 | 526 | // ----------------------------------------------- 527 | void BluetoothScanner::loop() 528 | { 529 | SCOPED_STACK_ENTRY; 530 | // Handle read remote name result outside of callback if made available: 531 | if(!readRemoteNameResultQueue.empty()) { 532 | HandleReadRemoteNameResult(readRemoteNameResultQueue.front()); 533 | readRemoteNameResultQueue.pop(); 534 | } 535 | 536 | if(!bleAdvertisedDeviceResultQueue.empty()) { 537 | // mSerial.printf("Found %d ble devices\n", bleAdvertisedDeviceResultQueue.size()); 538 | for(uint8_t i = 0; i < min(maxBleProcessPerIteration, bleAdvertisedDeviceResultQueue.size()); i++){ 539 | HandleBleAdvertisementResult(bleAdvertisedDeviceResultQueue.front()); 540 | bleAdvertisedDeviceResultQueue.pop(); 541 | vTaskDelay(1); 542 | } 543 | } 544 | 545 | unsigned long current_millis = millis(); 546 | 547 | // If scan is still active but timed out... 548 | if(scanMode != ScanType::None) { 549 | if(current_millis - getLastScanTime(scanMode) > scan_duration_timeout) { 550 | led.set(OFF); 551 | mSerial.println("Scan timed out. Reseting scanning state!\n"); 552 | scanIndex = -1; 553 | scanMode = ScanType::None; 554 | } 555 | } 556 | 557 | if(scanMode != ScanType::None && !scanInProgress && btDevices.size() != 0) { 558 | if(current_millis > last_scan_iter_millis + scan_iter_interval || scanIndex != 0) { // only block first device of the list! 559 | last_scan_iter_millis = current_millis; 560 | 561 | // ToDo: walk scanning state machine if scan active... 562 | 563 | // Loop over devices to be scanned: 564 | uint8_t devices_checked; 565 | for (devices_checked = 0; devices_checked <= btDevices.size(); devices_checked++) { 566 | scanIndex = (scanIndex+1)%btDevices.size(); 567 | if(scanIndex == 0) { 568 | // If we loop over the devices to scan, we start a new iteration! 569 | iterationScansLeft--; 570 | } 571 | 572 | if(scanForNextDevice()) { 573 | break; 574 | } 575 | } 576 | if (devices_checked >= btDevices.size()) { // All devices scanned. No more scanning 577 | mSerial.println("Stopping All searches...\n\n"); 578 | scanIndex = -1; 579 | scanMode = ScanType::None; 580 | 581 | bleScan_shouldStart = true; 582 | } 583 | } 584 | } 585 | 586 | // Check for periodic scan: 587 | if(periodic_scan_interval > 0) { 588 | if(current_millis > getLastScanTime(ScanType::Either) + periodic_scan_interval*1000) { 589 | startBluetoothScan(ScanType::Either); 590 | } 591 | } 592 | 593 | // Start new ble scan only if not performing regular scan 594 | if(bleScan_shouldStart && scanMode == ScanType::None){ 595 | pBLEScan->stop(); 596 | //pBLEScan->clearResults(); Taken care of when starting with continue! 597 | 598 | scanContinueCount = (scanContinueCount+1)%scanContinueWraparound; 599 | if(scanContinueCount == 0) { 600 | mSerial.println("Starting clean BLE scan"); 601 | } 602 | if(!pBLEScan->start(scanTime, static_cast(Callback::callback), scanContinueCount)) { 603 | mSerial.println("Problem starting BLE scan!!"); 604 | } 605 | else{ 606 | bleScan_shouldStart = false; 607 | } 608 | } 609 | 610 | // Check for outdated ble RSSI measurements and send update if needed: 611 | const auto _millis = millis(); 612 | const unsigned long iBeaconTimeout = 30000; // 30s 613 | for (auto &iBeacon : iBeaconDevices){ 614 | if(iBeacon.last_update_millis + iBeaconTimeout < _millis && !iBeacon.isVirgin()) { 615 | iBeacon.reset(); 616 | mSerial.printf("Lost beacon '%s'. Resetting.\n", iBeacon.name.c_str()); 617 | 618 | int filteredRssi = iBeacon.getFilteredRSSI(); 619 | if(abs(filteredRssi - iBeacon.lastSentRssi) > 3) { 620 | char rssi_str[25] = "\"rssi\":\"\""; 621 | // snprintf(rssi_str, 24, "\"rssi\":\"\"", filteredRssi); 622 | iBeacon.lastSentRssi = filteredRssi; 623 | std::string topic = m_mqtt_topic + "/" + m_scanner_identity + "/" + iBeacon.name.c_str(); 624 | mqtt.send_message(topic.c_str(), 625 | createConfidenceMessage(iBeacon.uuid.toString().c_str(), iBeacon.confidence, iBeacon.name.c_str(), mTime.dateTime("D M d Y H:i:s ~G~M~TO (T)").c_str(), m_retain, rssi_str, "KNOWN_BEACON").c_str(), m_retain 626 | ); 627 | } 628 | } 629 | } 630 | } 631 | 632 | // ----------------------------------------------- 633 | void BluetoothScanner::stop() { 634 | btStop(); 635 | } 636 | 637 | // ----------------------------------------------- 638 | uint8_t BluetoothScanner::getNumScans(ScanType scanType) { 639 | uint8_t numScans = 0; 640 | switch(scanType) { 641 | case ScanType::Either: 642 | numScans = max(num_arrival_scans, num_departure_scans); 643 | break; 644 | 645 | case ScanType::Arrival: 646 | numScans = num_arrival_scans; 647 | break; 648 | 649 | case ScanType::Departure: 650 | numScans = num_departure_scans; 651 | break; 652 | 653 | case ScanType::None: 654 | default: 655 | numScans = 0; 656 | } 657 | return numScans; 658 | } 659 | 660 | // ----------------------------------------------- 661 | unsigned long BluetoothScanner::getLastScanTime(ScanType scanType) { 662 | switch(scanType) { 663 | case ScanType::Arrival: 664 | return last_arrival_scan_time; 665 | break; 666 | case ScanType::Departure: 667 | return last_departure_scan_time; 668 | break; 669 | case ScanType::Either: 670 | return min(last_departure_scan_time, last_arrival_scan_time); 671 | break; 672 | default: 673 | return 0; 674 | break; 675 | } 676 | return 0; 677 | } 678 | 679 | // ----------------------------------------------- 680 | void BluetoothScanner::setLastScanTime(ScanType scanType, unsigned long time) { 681 | switch(scanType) { 682 | case ScanType::Arrival: 683 | last_arrival_scan_time = time; 684 | break; 685 | case ScanType::Departure: 686 | last_departure_scan_time = time; 687 | break; 688 | case ScanType::Either: 689 | last_departure_scan_time = time; 690 | last_arrival_scan_time = time; 691 | break; 692 | default: 693 | break; 694 | } 695 | } 696 | 697 | // ----------------------------------------------- 698 | void BluetoothScanner::setNumArrivalScans(uint8_t val) { 699 | num_arrival_scans = val; 700 | } 701 | 702 | // ----------------------------------------------- 703 | void BluetoothScanner::setNumDepartureScans(uint8_t val) { 704 | num_departure_scans = val; 705 | } 706 | 707 | // ----------------------------------------------- 708 | void BluetoothScanner::setSecondsBetweenScanIters(unsigned long val) { 709 | scan_iter_interval = val; 710 | } 711 | 712 | // ----------------------------------------------- 713 | void BluetoothScanner::setBeaconExpiration(uint32_t val) { 714 | beacon_expiration_seconds = val; 715 | 716 | } 717 | 718 | // ----------------------------------------------- 719 | void BluetoothScanner::setMinTimeBetweenScans(uint32_t val) { 720 | min_seconds_between_scans = val; 721 | } 722 | 723 | // ----------------------------------------------- 724 | void BluetoothScanner::setPeriodicScanInterval(uint32_t val) { 725 | periodic_scan_interval = val; 726 | } 727 | 728 | // ----------------------------------------------- 729 | void BluetoothScanner::setScanDurationTimeout(uint32_t val) { 730 | scan_duration_timeout = val*1000; 731 | } 732 | 733 | // ----------------------------------------------- 734 | void BluetoothScanner::setMqttTopic(const std::string& topic){ 735 | m_mqtt_topic = topic; 736 | } 737 | 738 | // ----------------------------------------------- 739 | void BluetoothScanner::setScannerIdentity(const char* identity){ 740 | m_scanner_identity = identity; 741 | } 742 | 743 | // ----------------------------------------------- 744 | void BluetoothScanner::setRetainFlag(bool flag) { 745 | m_retain = flag; 746 | } 747 | 748 | // ----------------------------------------------- 749 | void BluetoothScanner::startBluetoothScan(ScanType scanType) 750 | { 751 | SCOPED_STACK_ENTRY; 752 | uint8_t numScans = getNumScans(scanType); 753 | unsigned long reference_scan_time = getLastScanTime(scanType); 754 | unsigned long _millis = millis(); 755 | 756 | // Only scan if the last scan was long ago enough... 757 | if( _millis > reference_scan_time + min_seconds_between_scans*1000 && scanMode == ScanType::None) { 758 | mSerial.printf("Starting search for devices with scantype %d\n Set numScans to %d\n", (int)(scanType), numScans); 759 | 760 | scanMode = scanType; 761 | 762 | // Stop ble scan when starting regular scan: 763 | // pBLEScan->stop(); 764 | 765 | std::lock_guard lock(btDevicesMutex); 766 | // Set counter for all devices: 767 | iterationScansLeft = numScans; 768 | for( auto& dev : btDevices ) { 769 | if(scanMode == ScanType::Arrival && dev.state == 1) { // Device already present so only scan for departure 770 | dev.scansLeft = 0; 771 | } 772 | else if (scanMode == ScanType::Departure && dev.state == 0) { // Device not present, only scan for arrival 773 | dev.scansLeft = 0; 774 | } 775 | else { 776 | // ScanMode Either will always scan regardless of state. 777 | dev.scansLeft = numScans; 778 | } 779 | } 780 | 781 | // Start scanning at the beginning: 782 | scanIndex = 0; 783 | scanForNextDevice(); 784 | 785 | setLastScanTime(scanMode, _millis); 786 | last_scan_iter_millis = _millis; 787 | } 788 | else { 789 | mSerial.println("Skipping BT scan. Too soon dude..."); 790 | mSerial.printf("milllis %lu < %lu, scanIdx: %d\n", _millis, reference_scan_time + min_seconds_between_scans*1000, scanIndex); 791 | } 792 | } 793 | 794 | // ----------------------------------------------- 795 | bool BluetoothScanner::scanForNextDevice() { 796 | if(btDevices.size()) { 797 | auto& dev = btDevices.at(scanIndex); 798 | if(dev.scansLeft > 0) { 799 | led.set(128); 800 | scanInProgress = 1; 801 | esp_bt_gap_read_remote_name(dev.mac); 802 | mSerial.printf("Searching device: %s\n", dev.name.c_str()); 803 | return true; 804 | } 805 | } 806 | return false; 807 | } 808 | 809 | // ----------------------------------------------- 810 | void BluetoothScanner::addKnownDevice(const std::string& input) { 811 | std::string _in = input; 812 | _in.erase(_in.find_last_not_of(" \t\n\v") + 1); 813 | std::string identifier = _in.substr(0, _in.find(" ")); 814 | std::string alias = _in.substr(_in.find(" ")); 815 | 816 | size_t first = alias.find_first_not_of(" \t\n\v"); 817 | size_t last = alias.find_last_not_of(" \t\n\v"); 818 | if(first != std::string::npos) { 819 | alias = alias.substr(first, (last-first+1)); 820 | } 821 | 822 | BLEUUID ble_uuid = BLEUUID::fromString(identifier); 823 | 824 | esp_bd_addr_t addr; 825 | if(str2bda(identifier.c_str(), addr)) { 826 | addKnownDevice(addr, alias.c_str()); 827 | } 828 | else if (!ble_uuid.equals(BLEUUID())) { 829 | addKnownIBeacon(ble_uuid, alias.c_str()); 830 | } 831 | } 832 | 833 | // ----------------------------------------------- 834 | void BluetoothScanner::addKnownIBeacon(const BLEUUID uuid, const char* alias) { 835 | // Remove entry if it already existed in order to overwrite with updated one: 836 | deleteKnownIBeacon(uuid); 837 | 838 | { 839 | // Add to the set of currently known iBeacons: 840 | std::lock_guard lock(iBeaconDevicesMutex); 841 | 842 | iBeaconDevices.emplace_back(uuid, String(alias)); 843 | mSerial.printf("Added iBeacon '%s'\n", alias); 844 | } 845 | } 846 | 847 | // ----------------------------------------------- 848 | void BluetoothScanner::deleteKnownIBeacon(const BLEUUID uuid) { 849 | std::lock_guard lock(iBeaconDevicesMutex); 850 | for (auto it = iBeaconDevices.begin(); it != iBeaconDevices.end(); /* NOTHING */) { 851 | if ((*it).uuid.equals(uuid)) { 852 | it = iBeaconDevices.erase(it); 853 | } 854 | else{ 855 | ++it; 856 | } 857 | } 858 | } 859 | 860 | // ----------------------------------------------- 861 | void BluetoothScanner::addKnownDevice(const esp_bd_addr_t mac, const char* alias) { 862 | char bda_str[18]; 863 | if (bda2str(mac, bda_str, 18)) { 864 | // Add to the set of currently known devices: 865 | std::lock_guard lock(btDevicesMutex); 866 | // Remove entry if it already existed in order to overwrite with updated one: 867 | removeFromBtDevices(mac); 868 | btDevices.emplace_back(mac, String(alias)); 869 | } 870 | } 871 | 872 | // ----------------------------------------------- 873 | void BluetoothScanner::deleteKnownDevice(const std::string& mac) { 874 | esp_bd_addr_t addr; 875 | if(str2bda(mac.c_str(), addr)) { 876 | deleteKnownDevice(addr); 877 | } 878 | } 879 | 880 | // ----------------------------------------------- 881 | void BluetoothScanner::removeFromBtDevices(const esp_bd_addr_t mac) { 882 | for (auto it = btDevices.begin(); it != btDevices.end(); /* NOTHING */) { 883 | if (memcmp((*it).mac, mac, sizeof(esp_bd_addr_t)) == 0) { 884 | it = btDevices.erase(it); 885 | } 886 | else{ 887 | ++it; 888 | } 889 | } 890 | } 891 | 892 | // ----------------------------------------------- 893 | void BluetoothScanner::deleteKnownDevice(const esp_bd_addr_t mac) { 894 | std::lock_guard lock(btDevicesMutex); 895 | removeFromBtDevices(mac); 896 | } 897 | 898 | 899 | // ----------------------------------------------- 900 | void BluetoothScanner::clearKnownDevices() { 901 | std::lock_guard lock(btDevicesMutex); 902 | btDevices.clear(); 903 | } 904 | 905 | 906 | // ----------------------------------------------- 907 | const std::vector& BluetoothScanner::getBtDeviceStates() { 908 | std::lock_guard lock(btDevicesMutex); 909 | return btDevices; 910 | } 911 | 912 | // ----------------------------------------------- 913 | void BluetoothScanner::SetReadRemoteNameResult(const esp_bt_gap_cb_param_t::read_rmt_name_param& remoteNameParam) { 914 | readRemoteNameResultQueue.push(remoteNameParam); 915 | } 916 | 917 | // ----------------------------------------------- 918 | void BluetoothScanner::HandleReadRemoteNameResult(esp_bt_gap_cb_param_t::read_rmt_name_param& remoteNameParam) { 919 | SCOPED_STACK_ENTRY; 920 | char macbuf[19]; 921 | led.set(OFF); 922 | // If more scans are to be done, this causes the main loop to pick it up again! 923 | scanInProgress = 0; 924 | 925 | std::lock_guard lock(btDevicesMutex); 926 | btDeviceId_t& dev = btDevices.at(scanIndex); 927 | 928 | mSerial.printf("ScanResult Callback for dev(%d): %s, scansLeft %d \n", scanIndex, dev.name.c_str(), dev.scansLeft); 929 | 930 | // Decrement scansLeft for each finished scan: 931 | if (dev.scansLeft > 0) { 932 | dev.scansLeft--; 933 | mSerial.printf("Decrement scansleft for dev(%d): %s to %d \n", scanIndex, dev.name.c_str(), dev.scansLeft); 934 | } 935 | 936 | // Device found: 937 | if (ESP_BT_STATUS_SUCCESS == remoteNameParam.stat) { 938 | dev.confidence = 100; 939 | dev.state = 1; 940 | 941 | if(scanMode == ScanType::Arrival) { 942 | mSerial.printf("Found device, stopping Arrival Scan for %s\n", dev.name.c_str()); 943 | dev.scansLeft = 0; // Stop scanning, We've found him! 944 | } 945 | 946 | ESP_LOGI(GAP_TAG, "Remote device name: %s", remoteNameParam.rmt_name); 947 | std::string topic = m_mqtt_topic + "/" + m_scanner_identity + "/" + dev.name.c_str(); 948 | mqtt.send_message(topic.c_str(), 949 | createConfidenceMessage(bda2str(dev.mac, macbuf, 18), dev.confidence, dev.name.c_str(), mTime.dateTime("D M d Y H:i:s ~G~M~TO (T)").c_str(), m_retain).c_str(), m_retain 950 | ); 951 | } 952 | else { 953 | // Odd formula, but based on original in BluetoothPresence scripts... 954 | if(scanMode != ScanType::Arrival) { 955 | uint8_t confidence = min(100.0f, (90.0f * dev.scansLeft) / float(num_departure_scans)); 956 | dev.confidence = min(dev.confidence, confidence); // Dont increase confidence on departure scan. 957 | } 958 | else { 959 | dev.confidence = 0; 960 | } 961 | 962 | if(dev.scansLeft == 0) { 963 | dev.state = 0; 964 | dev.confidence = 0; 965 | } 966 | ESP_LOGI(GAP_TAG, "Remote device name read failed. Status: %d", remoteNameParam.stat); 967 | std::string topic = m_mqtt_topic + "/" + m_scanner_identity + "/" + dev.name.c_str(); 968 | mqtt.send_message(topic.c_str(), 969 | createConfidenceMessage(bda2str(dev.mac, macbuf, 18), dev.confidence, dev.name.c_str(), mTime.dateTime("D M d Y H:i:s ~G~M~TO (T)").c_str(), m_retain).c_str(), m_retain 970 | ); 971 | } 972 | // Next scan is triggered from main loop... 973 | } 974 | 975 | // ----------------------------------------------- 976 | void BluetoothScanner::gap_init() 977 | { 978 | app_gap_cb_t *p_dev = &m_dev_info; 979 | memset(p_dev, 0, sizeof(app_gap_cb_t)); 980 | } 981 | 982 | // ----------------------------------------------- 983 | void BluetoothScanner::startupGap(void) 984 | { 985 | const char *dev_name = "ESP32_scanner"; 986 | esp_bt_dev_set_device_name(dev_name); 987 | 988 | // set discoverable and connectable mode, wait to be connected 989 | // esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_GENERAL_DISCOVERABLE); 990 | esp_bt_gap_set_scan_mode(ESP_BT_NON_CONNECTABLE, ESP_BT_NON_DISCOVERABLE); 991 | 992 | // register GAP callback function 993 | Callback::func = std::bind(&BluetoothScanner::gap_callback, this, std::placeholders::_1, std::placeholders::_2); 994 | esp_bt_gap_cb_t callback_func = static_cast(Callback::callback); 995 | esp_bt_gap_register_callback(callback_func); 996 | 997 | // inititialize device information and status 998 | app_gap_cb_t *p_dev = &m_dev_info; 999 | memset(p_dev, 0, sizeof(app_gap_cb_t)); 1000 | 1001 | // start to discover nearby Bluetooth devices 1002 | // p_dev->state = APP_GAP_STATE_DEVICE_DISCOVERING; 1003 | // esp_bt_gap_start_discovery(ESP_BT_INQ_MODE_GENERAL_INQUIRY, 20, 0); 1004 | } 1005 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | FILE(GLOB_RECURSE app_sources ${CMAKE_SOURCE_DIR}/src/*.*) 3 | 4 | idf_component_register(SRCS ${app_sources} 5 | REQUIRES bt 6 | PRIV_REQUIRES bt) -------------------------------------------------------------------------------- /src/TelnetSerial.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | TelnetSerial.cpp - Arduino like serial library for printing over Telnet for ESP8266 & ESP32 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "Arduino.h" 10 | #include "TelnetSerial.h" 11 | #include "Esp.h" 12 | 13 | Telnet::Telnet(uint16_t port) : server(port) 14 | { 15 | 16 | } 17 | 18 | void Telnet::begin() 19 | { 20 | end(); 21 | 22 | // Start Ser2Net server 23 | server.setTimeout(15); // 15 seconds... 24 | server.begin(); 25 | // Keep delay enabled to combine smaller packages and thus reduce WiFi traffic! 26 | // server.setNoDelay(true); 27 | } 28 | 29 | void Telnet::end() 30 | { 31 | for(uint8_t i = 0; i < MAX_TELNET_CLIENTS; i++) { 32 | if(serverClients[i]) { 33 | serverClients[i].stop(); 34 | } 35 | } 36 | server.stop(); 37 | } 38 | 39 | void Telnet::handleConnections() 40 | { 41 | // Check if there are any new clients --------- 42 | if (server.hasClient()){ 43 | for(uint8_t i = 0; i < MAX_TELNET_CLIENTS; i++){ 44 | //find free/disconnected spot 45 | if (!serverClients[i] || !serverClients[i].connected()){ 46 | if(serverClients[i]){ 47 | serverClients[i].stop(); 48 | } 49 | serverClients[i] = server.available(); 50 | continue; 51 | } 52 | } 53 | // No free/disconnected spot so reject 54 | server.available().stop(); 55 | } 56 | } 57 | 58 | int Telnet::getConnectionCount() 59 | { 60 | int cnt = 0; 61 | for(uint8_t i = 0; i < MAX_TELNET_CLIENTS; i++){ 62 | if (serverClients[i] && serverClients[i].connected()){ 63 | cnt++; 64 | } 65 | } 66 | return cnt; 67 | } 68 | 69 | int Telnet::available(void) 70 | { 71 | int return_value = 0; 72 | for(uint8_t i = 0; i < MAX_TELNET_CLIENTS; i++){ 73 | if (serverClients[i] && serverClients[i].connected()){ 74 | return_value = serverClients[i].available(); 75 | if (return_value > 0) { 76 | break; 77 | } 78 | } 79 | } 80 | return return_value; 81 | } 82 | 83 | void Telnet::flush(void) 84 | { 85 | for(uint8_t i = 0; i < MAX_TELNET_CLIENTS; i++){ 86 | if (serverClients[i] && serverClients[i].connected()){ 87 | serverClients[i].flush(); 88 | } 89 | } 90 | } -------------------------------------------------------------------------------- /src/WiFiComponent.cpp: -------------------------------------------------------------------------------- 1 | 2 | // Basic headers 3 | #include 4 | #include 5 | #include 6 | 7 | // Tweaked SDK configuration 8 | #include "sdkconfig.h" 9 | 10 | // Arduino includes 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include // https://github.com/tzapu/WiFiManager 16 | #include 17 | #include "esp_sntp.h" 18 | 19 | #include "parameter.h" 20 | 21 | #include "stackDbgHelper.h" 22 | #include "WiFiComponent.h" 23 | #include "led.h" 24 | 25 | // Required available global entities: 26 | extern WiFiManager wm; 27 | 28 | const char* worldNtpServer = "pool.ntp.org"; 29 | const char* eurNtpServer = "europe.pool.ntp.org"; 30 | const char* nlNtpServer = "nl.pool.ntp.org"; 31 | const long gmtOffset_sec = 3600; 32 | const int daylightOffset_sec = 3600; 33 | 34 | // ----------------------------------------------- 35 | void WiFiComponent::wifiInfo(){ 36 | WiFi.printDiag(mSerial); 37 | mSerial.println("SAVED: " + (String)wm.getWiFiIsSaved() ? "YES" : "NO"); 38 | mSerial.println("SSID: " + (String)wm.getWiFiSSID()); 39 | mSerial.println("PASS: " + (String)wm.getWiFiPass()); 40 | } 41 | 42 | // ----------------------------------------------- 43 | void WiFiComponent::setup() { 44 | // Enable AP mode as well as STA, such that WM can create an access point if needed 45 | WiFi.mode(WIFI_MODE_APSTA); 46 | 47 | _hostname = String("ESP32_bt") + String(WIFI_getChipId(),HEX); 48 | 49 | // Load registered parameters from NVS 50 | loadParams(); 51 | 52 | // Blocking if not connected WiFi AP 53 | setupConfigPortal(); 54 | 55 | setupArduinoOTA(); 56 | } 57 | 58 | // ----------------------------------------------- 59 | void WiFiComponent::loop() { 60 | SCOPED_STACK_ENTRY; 61 | do { 62 | ArduinoOTA.handle(); 63 | feedLoopWDT(); 64 | delay(10); 65 | } while(otaBusy); 66 | wm.process(); 67 | } 68 | 69 | // ----------------------------------------------- 70 | void WiFiComponent::registerOtaStartCallback(std::function callback) { 71 | ota_callback_functions.push_back(callback); 72 | } 73 | 74 | // ----------------------------------------------- 75 | void WiFiComponent::registerParamSaveCallback(std::function callback) { 76 | paramSaved_callback_functions.push_back(callback); 77 | } 78 | 79 | // ----------------------------------------------- 80 | void WiFiComponent::setupConfigPortal() { 81 | 82 | wm.debugPlatformInfo(); 83 | 84 | //reset settings - for testing 85 | // wm.resetSettings(); 86 | // wm.erase(); 87 | 88 | // callbacks 89 | wm.setAPCallback(std::bind(&WiFiComponent::configModeCallback,this, std::placeholders::_1)); 90 | wm.setWebServerCallback(std::bind(&WiFiComponent::bindServerCallback,this)); 91 | wm.setSaveConfigCallback(std::bind(&WiFiComponent::saveWifiCallback,this)); 92 | wm.setSaveParamsCallback(std::bind(&WiFiComponent::saveParamCallback,this)); 93 | 94 | wm.setTitle("ESP32_bt"); 95 | wm.setHostname(_hostname.c_str()); 96 | 97 | // invert theme, dark 98 | wm.setDarkMode(true); 99 | 100 | // Show update button on info page 101 | wm.setShowInfoUpdate(true); 102 | 103 | // Separate ParamsPage from WiFi page 104 | wm.setParamsPage(true); 105 | 106 | // Clear the original menu list 107 | wm.setMenu(nullptr, 0); 108 | // Use custom menu list only: 109 | wm.setCustomMenuItems({{"/wifi" , "Configure WiFi", false}, 110 | {"/param" , "Settings" , false}, 111 | {"/info" , "Info" , false}, 112 | {"/bt" , "BT Monitor" , false}, 113 | {"--" , "" , false}, // Separator 114 | {"/erase" , "Erase" , true }, 115 | {"/update" , "Update" , false}, 116 | {"/restart", "Restart" , false}, 117 | {"--" , "" , false}}); // Ending Separator 118 | 119 | 120 | //sets timeout until configuration portal gets turned off 121 | //useful to make it all retry or go to sleep in seconds 122 | wm.setConfigPortalTimeout(600); // 10 minutes 123 | 124 | // set connection timeout 125 | wm.setConnectTimeout(3); 126 | 127 | // set wifi connect retries 128 | wm.setConnectRetries(3); 129 | 130 | // connect after portal save toggle 131 | wm.setSaveConnect(true); // do not connect, only save 132 | 133 | wm.setWiFiAutoReconnect(true); 134 | 135 | String apName = String(WIFI_getChipId(),HEX); 136 | apName.toUpperCase(); 137 | apName = "ESP32_bt_" + apName; 138 | 139 | // Setup httpd authentication for config portal once it's connected to users home WiFi network: 140 | wm.setHttpdAuthCredentials(HTTPD_USER, HTTPD_PASSWD); 141 | 142 | //fetches ssid and pass and tries to connect 143 | //if it does not connect it starts an access point with the specified name 144 | //and goes into a blocking loop awaiting configuration 145 | // --> last parameter ensures a retry to the blocking loop to connect to known WiFi AP 146 | if(!wm.autoConnect(apName.c_str(), AP_PASSWD, true)) { 147 | mSerial.println("failed to connect and hit timeout"); 148 | } 149 | else { 150 | wm.setHttpdAuthEnable(true); 151 | 152 | // Disable AP 153 | WiFi.softAPdisconnect(true); 154 | WiFi.mode(WIFI_STA); 155 | 156 | //if you get here you have connected to the WiFi 157 | mSerial.println("connected...yeey :)"); 158 | wm.startWebPortal(); 159 | } 160 | 161 | wifiInfo(); 162 | } 163 | 164 | // ----------------------------------------------- 165 | void WiFiComponent::setupArduinoOTA() { 166 | 167 | ArduinoOTA.setPort(3232); 168 | 169 | ArduinoOTA.setHostname("ESP32_BT"); 170 | 171 | ArduinoOTA.setPassword("admin"); 172 | 173 | ArduinoOTA 174 | .onStart([this]() { 175 | String type; 176 | if (ArduinoOTA.getCommand() == U_FLASH) 177 | type = "sketch"; 178 | else // U_SPIFFS 179 | type = "filesystem"; 180 | 181 | // NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end() 182 | mSerial.println("Start updating " + type); 183 | 184 | for (auto& cb : ota_callback_functions) { 185 | cb(); 186 | } 187 | 188 | // Start with 'blocking' ota loop: 189 | otaBusy = 1; 190 | led.set(OFF); 191 | }) 192 | .onEnd([this]() { 193 | Serial.println("\nEnd"); 194 | otaBusy = 0; 195 | led.set(ON); 196 | // Succesful end will reset ESP itself 197 | }) 198 | .onProgress([this](unsigned int progress, unsigned int total) { 199 | Serial.printf("Progress: %u%%\r", (progress / (total / 100))); 200 | // Have the led fade on 10 times 201 | led.set((progress / (total / 25))%255); 202 | 203 | feedLoopWDT(); 204 | }) 205 | .onError([this](ota_error_t error) { 206 | mSerial.printf("Error[%u]: ", error); 207 | if (error == OTA_AUTH_ERROR) mSerial.println("Auth Failed"); 208 | else if (error == OTA_BEGIN_ERROR) mSerial.println("Begin Failed"); 209 | else if (error == OTA_CONNECT_ERROR) mSerial.println("Connect Failed"); 210 | else if (error == OTA_RECEIVE_ERROR) mSerial.println("Receive Failed"); 211 | else if (error == OTA_END_ERROR) mSerial.println("End Failed"); 212 | otaBusy = 0; 213 | led.set(16); 214 | 215 | // Reset in order to have a fresh start on required functionality... 216 | delay(100); 217 | ESP.restart(); 218 | }); 219 | 220 | ArduinoOTA.begin(); 221 | } 222 | 223 | // ----------------------------------------------- 224 | void WiFiComponent::stopArduinoOTA() { 225 | ArduinoOTA.end(); 226 | } 227 | 228 | // ----------------------------------------------- 229 | void WiFiComponent::loadParams() { 230 | WiFiManagerParameter** params = wm.getParameters(); 231 | uint16_t paramCount = wm.getParametersCount(); 232 | for(uint16_t i = 0; i < paramCount; i++) { 233 | if(params[i]->getID()) { 234 | Parameter* p = static_cast(params[i]); 235 | if(p) { 236 | p->initialize(); 237 | } 238 | } 239 | } 240 | } 241 | 242 | // ----------------------------------------------- 243 | void WiFiComponent::storeParams() { 244 | WiFiManagerParameter** params = wm.getParameters(); 245 | uint16_t paramCount = wm.getParametersCount(); 246 | for(uint16_t i = 0; i < paramCount; i++) { 247 | if(params[i]->getID()) { 248 | Parameter* p = static_cast(params[i]); 249 | if(p) { 250 | p->storeValue(); 251 | } 252 | } 253 | } 254 | } 255 | 256 | // ----------------------------------------------- 257 | void WiFiComponent::addCustomHtmlPage(const char* path, customHtmlHandler_t handler) { 258 | custom_html_page_handlers.emplace_back(path, handler); 259 | } 260 | 261 | // ----------------------------------------------- 262 | void WiFiComponent::saveWifiCallback(){ 263 | 264 | } 265 | 266 | // ----------------------------------------------- 267 | //gets called when WiFiManager enters configuration mode 268 | void WiFiComponent::configModeCallback (WiFiManager *myWiFiManager) { 269 | // ToDo: stop Bluetooth processing!? 270 | } 271 | 272 | // ----------------------------------------------- 273 | void WiFiComponent::saveParamCallback(){ 274 | storeParams(); 275 | 276 | // Call registered callbacks to have them update the parameters 277 | for(auto& cb : paramSaved_callback_functions) { 278 | cb(); 279 | } 280 | // wm.stopConfigPortal(); 281 | } 282 | 283 | // ----------------------------------------------- 284 | void WiFiComponent::handleRoute(){ 285 | wm.server->send(200, "text/plain", "hello from user code"); 286 | } 287 | 288 | // ----------------------------------------------- 289 | void WiFiComponent::bindServerCallback(){ 290 | wm.server->on("/custom",std::bind(&WiFiComponent::handleRoute,*this)); 291 | // wm.server->on("/info",handleRoute); // you can override wm! 292 | 293 | for(auto& page : custom_html_page_handlers) { 294 | wm.server->on(page.path, page.handler); 295 | } 296 | } 297 | 298 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | // Basic headers 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | // IDF headers 8 | #include 9 | #include 10 | #include 11 | #include "nvs.h" 12 | #include "nvs_flash.h" 13 | #include "esp_system.h" 14 | #include "esp_log.h" 15 | #include "esp_bt.h" 16 | #include "esp_bt_main.h" 17 | #include "esp_bt_device.h" 18 | #include "esp_gap_bt_api.h" 19 | 20 | // Tweaked SDK configuration 21 | #include "sdkconfig.h" 22 | 23 | // Arduino includes 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include "time.h" 32 | 33 | #include 34 | 35 | #include "esp_task_wdt.h" 36 | 37 | // Custom components: 38 | #include "NestedStruct.h" 39 | #include "TelnetSerial.h" 40 | #include "WiFiComponent.h" 41 | #include "BluetoothScanner.h" 42 | #include "parameter.h" 43 | #include "BluetoothParameter.h" 44 | #include "led.h" 45 | #include "mqtt.h" 46 | 47 | #include "stackDbgHelper.h" 48 | 49 | #ifndef DDUMPTIMEMEMINTERVAL 50 | #define DDUMPTIMEMEMINTERVAL 10 51 | #endif 52 | 53 | #ifndef MAX_NUM_STORED_BLUETOOTH_DEVICES 54 | #define MAX_NUM_STORED_BLUETOOTH_DEVICES (8) 55 | #endif 56 | // Upper limit for max number of stored bluetooth devices. 57 | // Is rediculously large, but more would not fit in NVS!! 58 | static_assert(MAX_NUM_STORED_BLUETOOTH_DEVICES <= 40, "Cannot fit more than 40 bluetooth device identifiers in ESP32 storage!!"); 59 | 60 | #define WDT_TIMEOUT 8 61 | 62 | // ================================================ 63 | // >>>>>>>>>> Global objects >>>>>>>>>>>>>>>>>>>> 64 | 65 | #ifndef NO_USE_TELNET_SERIAL 66 | TelnetSerial telnetSerial; 67 | Stream& mSerial = telnetSerial; 68 | #else 69 | Stream& mSerial = Serial; 70 | #endif 71 | Preferences preferences; 72 | WiFiManager wm(mSerial); 73 | LED led; 74 | 75 | // >>> All parameters in order shown in WiFiManager: 76 | Parameter time_header ("

Timezone


Use e.g. Europe/Amsterdam. List of timezones
(empty for auto-geolocation)
"); 77 | Parameter time_zone ("time_zone", "", "", 40); 78 | // MQTT 79 | Parameter mqtt_header ("

MQTT parameters

"); 80 | Parameter mqtt_server ("mqtt_server", "mqtt server", "", 40); 81 | Parameter mqtt_port ("mqtt_port", "mqtt port", "1883", 6); 82 | Parameter mqtt_username ("mqtt_username", "username", "", 32); 83 | Parameter mqtt_password ("mqtt_password", "password", "", 32); 84 | Parameter mqtt_client_id("mqtt_clid", "Client ID", "", 32); 85 | 86 | Parameter mqtt_topic ("mqtt_topic", "topic root", "monitor", 32); 87 | Parameter mqtt_identity("mqtt_identity", "identity", "", 32); 88 | 89 | // Bluetooth Monitor settings 90 | Parameter bluetooth_monitor_header (PSTR("

Bluetooth Monitor settings

")); 91 | U16Parameter bluetooth_monitor_arrival_scans (PSTR("bm_arrival"), PSTR("# Arrival scans"), 1 ); 92 | U16Parameter bluetooth_monitor_departure_scans (PSTR("bm_depart"), PSTR("# Departure scans"), 3 ); 93 | U16Parameter bluetooth_monitor_seconds_between_scan_iters (PSTR("bm_iter_time"), PSTR("Seconds between scan tries"), 3 ); 94 | U16Parameter bluetooth_monitor_scan_timeout_seconds (PSTR("bm_timeout"), PSTR("Scan duration timeout (s)"), 60 ); 95 | U16Parameter bluetooth_monitor_beacon_expiration (PSTR("bm_beacon_exp"), PSTR("Beacon expiration time (s)"), 240); 96 | U16Parameter bluetooth_monitor_min_time_between_scans (PSTR("bm_min_time"), PSTR("Min. time between scans (s)"), 10 ); 97 | U16Parameter bluetooth_monitor_periodic_scan_interval (PSTR("bm_period"), PSTR("Periodic scan interval (s)
(Leave empty or '0' to disable periodic scanning)"), 0); 98 | BoolParameter bluetooth_monitor_retain_flag (PSTR("bm_retain"), PSTR("MQTT retain flag (true/false)"), false); 99 | 100 | // Known Static Bluetooth MAC 101 | // Nested struct helper to generate objects and reduce boilerplate... 102 | Parameter bluetooth_monitor_devices_header (PSTR("

Bluetooth Monitor Devices

")); 103 | NestWrapper bluetooth_monitor_parameter_sets; 104 | 105 | // <<< 106 | WiFiComponent wifi(mSerial); 107 | BluetoothScanner btScanner(mSerial); 108 | MQTT mqtt(mSerial); 109 | 110 | Timezone mTime; 111 | // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 112 | 113 | 114 | std::stack _stack; 115 | 116 | uint32_t startFreeHeap = 0; 117 | 118 | #define USE_SEPARATE_STATS_TASK 0 119 | 120 | #include "freertos/FreeRTOS.h" 121 | #include "freertos/task.h" 122 | #include "esp_heap_task_info.h" 123 | 124 | #define MAX_TASK_NUM 20 // Max number of per tasks info that it can store 125 | #define MAX_BLOCK_NUM 20 // Max number of per block info that it can store 126 | 127 | static size_t s_prepopulated_num = 0; 128 | static heap_task_totals_t s_totals_arr[MAX_TASK_NUM]; 129 | static heap_task_block_t s_block_arr[MAX_BLOCK_NUM]; 130 | 131 | // ----------------------------------------------- 132 | extern "C" void esp_task_wdt_isr_user_handler(void) 133 | { 134 | printf("\nSTACK:\n"); 135 | while(!_stack.empty()) { 136 | auto& e = _stack.top(); 137 | printf("PC: %d, Func: %s, Line: %d \n", e.pc, e.func, e.line); 138 | _stack.pop(); 139 | } 140 | } 141 | 142 | // ----------------------------------------------- 143 | static void esp_dump_per_task_heap_info(void) 144 | { 145 | SCOPED_STACK_ENTRY; 146 | heap_task_info_params_t heap_info = {0}; 147 | heap_info.caps[0] = MALLOC_CAP_8BIT; // Gets heap with CAP_8BIT capabilities 148 | heap_info.mask[0] = MALLOC_CAP_8BIT; 149 | heap_info.caps[1] = MALLOC_CAP_32BIT; // Gets heap info with CAP_32BIT capabilities 150 | heap_info.mask[1] = MALLOC_CAP_32BIT; 151 | heap_info.tasks = NULL; // Passing NULL captures heap info for all tasks 152 | heap_info.num_tasks = 0; 153 | heap_info.totals = s_totals_arr; // Gets task wise allocation details 154 | heap_info.num_totals = &s_prepopulated_num; 155 | heap_info.max_totals = MAX_TASK_NUM; // Maximum length of "s_totals_arr" 156 | heap_info.blocks = s_block_arr; // Gets block wise allocation details. For each block, gets owner task, address and size 157 | heap_info.max_blocks = MAX_BLOCK_NUM; // Maximum length of "s_block_arr" 158 | 159 | heap_caps_get_per_task_info(&heap_info); 160 | 161 | for (int i = 0 ; i < *heap_info.num_totals; i++) { 162 | const char* taskName = heap_info.totals[i].task ? pcTaskGetTaskName(heap_info.totals[i].task) : nullptr; 163 | bool tryHeap = true; 164 | if(taskName == nullptr) { 165 | tryHeap = false; 166 | } 167 | else { 168 | for (int i = 0; i < strlen(taskName); i++) { 169 | if(!isascii(taskName[i])) { 170 | tryHeap = false; 171 | break; 172 | } 173 | } 174 | } 175 | 176 | mSerial.printf("Task: %22s -> CAP_8BIT: %6d CAP_32BIT: %3d -> HighWaterMark: %5d\n", 177 | heap_info.totals[i].task ? pcTaskGetTaskName(heap_info.totals[i].task) : "Pre-Scheduler allocs" , 178 | heap_info.totals[i].size[0], // Heap size with CAP_8BIT capabilities 179 | heap_info.totals[i].size[1], // Heap size with CAP32_BIT capabilities 180 | tryHeap ? uxTaskGetStackHighWaterMark(heap_info.totals[i].task) : -1); 181 | 182 | feedLoopWDT(); // Must be done <= 360 mS 183 | } 184 | 185 | mSerial.printf("Free heap at start: %d, min free heap ever: %d\n", startFreeHeap, xPortGetMinimumEverFreeHeapSize()); 186 | mSerial.printf("Free heap size: %d, largest fee block: %d\n", heap_caps_get_free_size(MALLOC_CAP_8BIT), heap_caps_get_largest_free_block(MALLOC_CAP_8BIT)); 187 | 188 | mSerial.print("HeapCapsFreeSize - Internal: "); 189 | mSerial.print(heap_caps_get_free_size(MALLOC_CAP_INTERNAL)); 190 | mSerial.print(" SPIRAM: "); 191 | mSerial.print(heap_caps_get_free_size(MALLOC_CAP_SPIRAM)); 192 | mSerial.print(" IRAM8: "); 193 | mSerial.print(heap_caps_get_free_size(MALLOC_CAP_IRAM_8BIT)); 194 | mSerial.print(" Default: "); 195 | mSerial.println(heap_caps_get_free_size(MALLOC_CAP_DEFAULT)); 196 | 197 | mSerial.printf("\n\n"); 198 | } 199 | 200 | // ----------------------------------------------- 201 | void heap_caps_alloc_failed_hook(size_t requested_size, uint32_t caps, const char *function_name) 202 | { 203 | // No Printf to avoid MALLOCS!!! 204 | Serial.print(function_name); 205 | Serial.print(" in task "); 206 | Serial.print(pcTaskGetTaskName(NULL)); 207 | Serial.print(" was called but failed to allocate "); 208 | Serial.print(requested_size); 209 | Serial.print(" bytes, with capabilities: "); 210 | Serial.println(caps); 211 | Serial.print(" .High water mark: "); 212 | Serial.println(uxTaskGetStackHighWaterMark(NULL)); 213 | Serial.print("HeapCapsFreeSize - Internal: "); 214 | Serial.print(heap_caps_get_free_size(MALLOC_CAP_INTERNAL)); 215 | Serial.print(" SPIRAM: "); 216 | Serial.print(heap_caps_get_free_size(MALLOC_CAP_SPIRAM)); 217 | Serial.print(" IRAM8: "); 218 | Serial.print(heap_caps_get_free_size(MALLOC_CAP_IRAM_8BIT)); 219 | Serial.print(" Default: "); 220 | Serial.println(heap_caps_get_free_size(MALLOC_CAP_DEFAULT)); 221 | 222 | 223 | Serial.print("Free heap at start: "); 224 | Serial.print(startFreeHeap); 225 | Serial.print(", min free heap ever: "); 226 | Serial.println(xPortGetMinimumEverFreeHeapSize()); 227 | Serial.print("Free heap size: "); 228 | Serial.print(heap_caps_get_free_size(MALLOC_CAP_8BIT)); 229 | Serial.print(", largest fee block: "); 230 | Serial.println(heap_caps_get_largest_free_block(MALLOC_CAP_8BIT)); 231 | //Serial.printf("%s in task '%s' was called but failed to allocate %d bytes with 0x%X capabilities. HighWaterMark: %lu \n",function_name, pcTaskGetTaskName(NULL), requested_size, caps, (unsigned long)uxTaskGetStackHighWaterMark(NULL)); 232 | } 233 | 234 | // ----------------------------------------------- 235 | bool ensureAllCaps(std::string& str) { 236 | bool strUpdated = false; 237 | for (auto & c: str) { 238 | auto cu = toupper(c); 239 | if(c != cu) { 240 | strUpdated = true; 241 | } 242 | c = cu; 243 | } 244 | return strUpdated; 245 | } 246 | 247 | // ----------------------------------------------- 248 | #define PREFERENCES_TAG_VALUE (54) 249 | void setupPreferences() { 250 | preferences.begin("E32BT", false); 251 | 252 | // Perform a factory reset if we have not ran before... 253 | if(preferences.getUInt("ESP32_BT_TAG", 0) != PREFERENCES_TAG_VALUE) { 254 | Serial.print("Could not find Magic value in preferences. Clearing NVS for factory reset!"); 255 | 256 | preferences.end(); 257 | 258 | nvs_flash_erase(); // erase the NVS partition and... 259 | nvs_flash_init(); // initialize the NVS partition. 260 | Serial.print(" ... "); 261 | 262 | preferences.begin("E32BT", false); 263 | preferences.putUInt("ESP32_BT_TAG", PREFERENCES_TAG_VALUE); 264 | Serial.println("Done!!"); 265 | } 266 | 267 | // Set default MQTT Client ID if not set: 268 | if(strlen(mqtt_client_id.getValue()) < 1) { 269 | String client_id = String("ESP32_bt") + String(WIFI_getChipId(),HEX); 270 | mqtt_client_id.setValue(client_id.c_str()); 271 | } 272 | 273 | // Remove all preferences under the opened namespace 274 | //preferences.clear(); 275 | 276 | // Or remove the counter key only 277 | //preferences.remove("counter"); 278 | } 279 | 280 | // ----------------------------------------------- 281 | void setupMQTT() { 282 | mqtt.setup( mqtt_server.getValue(), 283 | mqtt_port.getValue(), 284 | mqtt_username.getValue(), 285 | mqtt_password.getValue(), 286 | mqtt_client_id.getValue() ); 287 | } 288 | 289 | // ----------------------------------------------- 290 | void setupMqttCallbacks() { 291 | // Clear existing topics and callbacks based on old parameters: 292 | mqtt.clear_subscription_topics(); 293 | mqtt.clear_callbacks(); 294 | 295 | std::string baseTopic = mqtt.trimWildcards(mqtt_topic.getValue()); 296 | mqtt.setStateTopic(baseTopic + "/" + mqtt_identity.getValue() + "/status"); 297 | mqtt.setIpTopic(baseTopic + "/" + mqtt_identity.getValue() + "/IP"); 298 | 299 | // ToDo: this becomes a problem when reloading parameters at runtime!! 300 | // FIX THIS! 301 | //mqtt.add_subscription_topic(mqtt_topic.getValue()); //TEST 302 | mqtt.add_subscription_topic((baseTopic + "/scan/#").c_str()); 303 | mqtt.add_subscription_topic((baseTopic + "/setup/#").c_str()); 304 | mqtt.add_subscription_topic((baseTopic + "/" + mqtt_identity.getValue() + "/restart").c_str()); 305 | mqtt.add_callback((baseTopic + "/" + mqtt_identity.getValue() + "/restart").c_str(), [](const byte* payload, unsigned int length) { 306 | mSerial.println("Restarting!!"); 307 | delay(1000); 308 | ESP.restart(); 309 | }); 310 | mqtt.add_callback((baseTopic + "/scan/ARRIVE").c_str(), [](const byte* payload, unsigned int length) { 311 | btScanner.startBluetoothScan(ScanType::Arrival); 312 | }); 313 | mqtt.add_callback((baseTopic + "/scan/DEPART").c_str(), [](const byte* payload, unsigned int length) { 314 | btScanner.startBluetoothScan(ScanType::Departure); 315 | }); 316 | mqtt.add_callback((baseTopic + "/scan/ANY").c_str(), [](const byte* payload, unsigned int length) { 317 | btScanner.startBluetoothScan(ScanType::Either); 318 | }); 319 | // MQTT setup interfaces: 320 | mqtt.add_callback((baseTopic + "/setup/ADD STATIC DEVICE").c_str(), [](const byte* payload, unsigned int length) { 321 | // Splitting the MAC from the alias: 322 | std::string _in = std::string((const char*)payload, length); 323 | _in.erase(_in.find_last_not_of(" \t\n\v") + 1); 324 | std::string mac = _in.substr(0, _in.find(" ")); 325 | std::string alias = _in.substr(_in.find(" ")); 326 | 327 | size_t first = alias.find_first_not_of(" \t\n\v"); 328 | size_t last = alias.find_last_not_of(" \t\n\v"); 329 | if(first != std::string::npos) { 330 | alias = alias.substr(first, (last-first+1)); 331 | } 332 | 333 | BLEUUID ble_uuid = BLEUUID::fromString(mac); 334 | 335 | // Add to storage and BluetoothScanner 336 | esp_bd_addr_t addr; 337 | if(str2bda(mac.c_str(), addr)) { 338 | char bda_str[18]; 339 | if (bda2str(addr, bda_str, 18)) { 340 | bool device_saved = false; 341 | for(int i = 0; i < bluetooth_monitor_parameter_sets.size; i++) { 342 | if(strlen(bluetooth_monitor_parameter_sets.data[i].getMacAddress()) == 0 || strcmp(bluetooth_monitor_parameter_sets.data[i].getMacAddress(), bda_str) == 0) { 343 | bluetooth_monitor_parameter_sets.data[i].setMacAddress(bda_str); 344 | bluetooth_monitor_parameter_sets.data[i].setAlias(alias.c_str()); 345 | device_saved = true; 346 | break; 347 | } 348 | } 349 | if (device_saved){ 350 | btScanner.addKnownDevice(addr, alias.c_str()); 351 | } 352 | } 353 | } 354 | else if (!ble_uuid.equals(BLEUUID())) { 355 | bool device_saved = false; 356 | for(int i = 0; i < bluetooth_monitor_parameter_sets.size; i++) { 357 | if(strlen(bluetooth_monitor_parameter_sets.data[i].getMacAddress()) == 0 || strcmp(bluetooth_monitor_parameter_sets.data[i].getMacAddress(), ble_uuid.toString().c_str()) == 0) { 358 | bluetooth_monitor_parameter_sets.data[i].setMacAddress(ble_uuid.toString().c_str()); 359 | bluetooth_monitor_parameter_sets.data[i].setAlias(alias.c_str()); 360 | device_saved = true; 361 | break; 362 | } 363 | } 364 | if (device_saved){ 365 | btScanner.addKnownIBeacon(ble_uuid, alias.c_str()); 366 | } 367 | } 368 | 369 | }); 370 | mqtt.add_callback((baseTopic + "/setup/DELETE STATIC DEVICE").c_str(), [](const byte* payload, unsigned int length) { 371 | esp_bd_addr_t addr; 372 | if(str2bda(std::string((const char*)payload, length).c_str(), addr)) { 373 | char bda_str[18]; 374 | if (bda2str(addr, bda_str, 18)) { 375 | btScanner.deleteKnownDevice(addr); 376 | for(int i = 0; i < bluetooth_monitor_parameter_sets.size; i++) { 377 | if(strcmp(bluetooth_monitor_parameter_sets.data[i].getMacAddress(), bda_str) == 0) { 378 | bluetooth_monitor_parameter_sets.data[i].setMacAddress(""); 379 | bluetooth_monitor_parameter_sets.data[i].setAlias(""); 380 | } 381 | } 382 | } 383 | } 384 | }); 385 | } 386 | 387 | // ----------------------------------------------- 388 | void setupBtScanner() { 389 | btScanner.setMqttTopic(mqtt.trimWildcards(mqtt_topic.getValue())); 390 | btScanner.setScannerIdentity(mqtt_identity.getValue()); 391 | btScanner.setRetainFlag(param2bool(bluetooth_monitor_retain_flag)); 392 | 393 | btScanner.setNumArrivalScans (bluetooth_monitor_arrival_scans.getValue()); 394 | btScanner.setNumDepartureScans (bluetooth_monitor_departure_scans.getValue()); 395 | btScanner.setSecondsBetweenScanIters(bluetooth_monitor_seconds_between_scan_iters.getValue()); 396 | btScanner.setBeaconExpiration (bluetooth_monitor_beacon_expiration.getValue()); 397 | btScanner.setMinTimeBetweenScans (bluetooth_monitor_min_time_between_scans.getValue()); 398 | btScanner.setPeriodicScanInterval (bluetooth_monitor_periodic_scan_interval.getValue()); 399 | btScanner.setScanDurationTimeout (bluetooth_monitor_scan_timeout_seconds.getValue()); 400 | // ToDo: Reload the rest of Bluetooth parameters 401 | 402 | // (Re)Load known BT devices: 403 | btScanner.clearKnownDevices(); 404 | for(int i = 0; i < bluetooth_monitor_parameter_sets.size; i++) { 405 | esp_bd_addr_t mac; 406 | std::string mac_str = bluetooth_monitor_parameter_sets.data[i].getMacAddress(); 407 | 408 | // Ensure Mac addresses are all CAPS: 409 | bool updateMac = ensureAllCaps(mac_str); 410 | if(updateMac) { 411 | bluetooth_monitor_parameter_sets.data[i].setMacAddress(mac_str.c_str()); 412 | } 413 | 414 | if(mac_str.find_first_not_of(" \t\n\v\f\r") != std::string::npos) { 415 | mSerial.printf("Validating MAC: '%s' \n", mac_str.c_str()); 416 | 417 | BLEUUID ble_uuid = BLEUUID::fromString(mac_str); 418 | 419 | // If valid mac address, add it! 420 | if(str2bda(mac_str.c_str(), mac)) { 421 | const char* alias = bluetooth_monitor_parameter_sets.data[i].getAlias(); 422 | mSerial.printf(" Adding device: %s, with MAC: %s \n", alias, mac_str.c_str()); 423 | btScanner.addKnownDevice(mac, alias); 424 | } 425 | else if (!ble_uuid.equals(BLEUUID())) { 426 | const char* alias = bluetooth_monitor_parameter_sets.data[i].getAlias(); 427 | mSerial.printf(" Adding device: %s, with UUID: '%s' \n", alias, mac_str.c_str()); 428 | btScanner.addKnownIBeacon(ble_uuid, alias); 429 | } 430 | } 431 | } 432 | 433 | } 434 | 435 | // ----------------------------------------------- 436 | void setupTimeZone() { 437 | // Or country codes for countries that do not span multiple timezones 438 | //mTime.setLocation(F("nl")); 439 | const char* timezone = time_zone.getValue(); 440 | mTime.setLocation(strlen(timezone) ? timezone : "GeoIP"); 441 | Serial.print(F("Local time: ")); 442 | Serial.println(mTime.dateTime()); 443 | } 444 | 445 | 446 | #if CONFIG_AUTOSTART_ARDUINO 447 | // ----------------------------------------------- 448 | void setup() { 449 | startFreeHeap = xPortGetFreeHeapSize(); 450 | esp_err_t error = heap_caps_register_failed_alloc_callback(heap_caps_alloc_failed_hook); 451 | 452 | // Turn on LED at start of setup 453 | led.setup(); 454 | led.set(ON); 455 | 456 | WiFi.setAutoConnect(true); 457 | 458 | // Begin Serial separate! 459 | Serial.begin(115200, SERIAL_8N1); 460 | 461 | setupPreferences(); 462 | 463 | mSerial.println("Booting"); 464 | ezt::setDebug(INFO, mSerial); 465 | 466 | if(error != ESP_OK) { 467 | Serial.printf("failed alloc callback error: %d\n", error); 468 | } 469 | 470 | // Note that this will not show up in the MENU!!! 471 | wifi.addCustomHtmlPage("/bt", [](){ 472 | String page = FPSTR(HTTP_HEAD_START); 473 | page.replace(FPSTR(T_v), "Bluetooth Status"); 474 | page += FPSTR(HTTP_SCRIPT); 475 | page += FPSTR(HTTP_STYLE); 476 | // Selection for DarkMode: _bodyClass = enable ? "invert" : ""; 477 | String p = FPSTR(HTTP_HEAD_END); 478 | p.replace(FPSTR(T_c), "invert"); // add class str 479 | page += p; 480 | 481 | auto& devList = btScanner.getBtDeviceStates(); 482 | char bda_str[18]; 483 | for (auto& dev : devList) { 484 | String str = FPSTR("
{n} is {s}
{m}
"); 485 | str.replace("{C}", (dev.state == 1 ? "S" : "D")); 486 | str.replace("{n}", dev.name); 487 | str.replace("{s}", (dev.state == 1 ? "Present" : "Away")); 488 | bda2str(dev.mac, bda_str, 18); 489 | str.replace("{m}", bda_str); 490 | page += str; 491 | } 492 | 493 | page += FPSTR("

" // MENU_SEP 494 | "
" 495 | "
" 496 | "
" 497 | "
"); 498 | 499 | if(wm.server->hasArg(F("arrive"))) { 500 | btScanner.startBluetoothScan(ScanType::Arrival); 501 | } 502 | if(wm.server->hasArg(F("depart"))) { 503 | btScanner.startBluetoothScan(ScanType::Departure); 504 | } 505 | if(wm.server->hasArg(F("scan"))) { 506 | btScanner.startBluetoothScan(ScanType::Either); 507 | } 508 | 509 | page += FPSTR(HTTP_END); 510 | 511 | wm.server->send(200, FPSTR(HTTP_HEAD_CT), page); 512 | }); 513 | 514 | // Blocking if not connected WiFi AP 515 | wifi.setup(); 516 | // Stop bluetooth on OTA to reduce chance of failures.. 517 | wifi.registerOtaStartCallback([](){ 518 | mSerial.println("Stopping BtScanner for OTA..."); 519 | btScanner.stop(); 520 | }); 521 | 522 | #ifndef NO_USE_TELNET_SERIAL 523 | telnetSerial.begin(); // Start the telnet bit... 524 | #endif 525 | setupMQTT(); 526 | setupMqttCallbacks(); 527 | 528 | // Setup bluetooth AFTER WiFi such that the persistent parameters are loaded! 529 | btScanner.init(); // ESP Bluetooth component, HAL, etc. 530 | setupBtScanner(); // Scanner configuration / settings 531 | 532 | // EZTime: sync time 533 | ezt::waitForSync(); 534 | setupTimeZone(); 535 | 536 | // Stop bluetooth on OTA to reduce chance of failures.. 537 | wifi.registerOtaStartCallback([](){ 538 | mSerial.println("Stopping BtScanner for OTA..."); 539 | btScanner.stop(); 540 | }); 541 | // Run MQTT setup again when new parameters are made available 542 | wifi.registerParamSaveCallback(setupMQTT); // This causes pubSub to destruct and panic!! 543 | wifi.registerParamSaveCallback(setupMqttCallbacks); 544 | wifi.registerParamSaveCallback(setupTimeZone); 545 | wifi.registerParamSaveCallback(setupBtScanner); 546 | 547 | // Turn off led at the end of setup 548 | led.set(OFF); 549 | 550 | btScanner.startBluetoothScan(ScanType::Either); 551 | 552 | esp_task_wdt_init(WDT_TIMEOUT, true); //enable panic so ESP32 restarts 553 | 554 | enableLoopWDT(); 555 | } 556 | 557 | uint16_t count = 0; 558 | 559 | unsigned long last_millis = 0; 560 | // unsigned long interval=10000; // 10sec 561 | unsigned long interval=DUMPTIMEMEMINTERVAL*1000; // 10sec 562 | 563 | void loop() { 564 | SCOPED_STACK_ENTRY; 565 | 566 | wifi.loop(); 567 | #ifndef NO_USE_TELNET_SERIAL 568 | telnetSerial.loop(); 569 | #endif 570 | 571 | mqtt.loop(); 572 | delay(10); 573 | btScanner.loop(); 574 | 575 | unsigned long current_millis = millis(); 576 | if(interval > 0 && current_millis > last_millis + interval) { 577 | last_millis = current_millis; 578 | mSerial.println(current_millis); 579 | 580 | mSerial.println(mTime.dateTime()); 581 | esp_dump_per_task_heap_info(); 582 | 583 | count++; 584 | } 585 | 586 | // WDT Feeding is done in Arduino scope outside of the loop... 587 | // Delay needed to give idle task some room!! 588 | delay(10); 589 | } 590 | #else 591 | #ERROR wrong SDK configuration! 592 | #endif -------------------------------------------------------------------------------- /src/mqtt.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #include 5 | #include "parameter.h" 6 | #include "mqtt.h" 7 | #include "stackDbgHelper.h" 8 | 9 | #ifndef MQTT_CLEAN_SESSION 10 | #define MQTT_CLEAN_SESSION 1 // 0 = No clean session, 1 = Clean session 11 | #endif 12 | 13 | // Monitor arguments to be used: 14 | // r: repeatedly scan for arrival / departure of known devices 15 | // x: retain mqtt status messages 16 | // b: report bluetooth beacon advertisements 17 | // Can only effectively run in '-tad' mode!! 18 | 19 | // Monitor MQTT preferences 20 | // mqtt address 21 | // mqtt broker username 22 | // mqtt broker password 23 | // mqtt publish topic root 24 | // mqtt publisher identity 25 | // mqtt port 26 | 27 | // Behavioral preferences 28 | // Arrival scan attempts # 29 | // Departure scan attempts # 30 | // Beacon expiration seconds 31 | // min time between scans seconds 32 | // .. others are irrelevant for now as we cannot passively scan... 33 | 34 | // A list of max 16 known mac addresses + aliasses 35 | 36 | // ------------------------------------------------ 37 | // Local functions: 38 | 39 | // ----------------------------------------------- 40 | void MQTT::setup( 41 | const char* mqtt_server, 42 | const char* mqtt_port, 43 | const char* mqtt_username, 44 | const char* mqtt_password, 45 | const char* mqtt_client_id) 46 | { 47 | // Store MQTT server parameters 48 | _mqtt_server = mqtt_server; 49 | _mqtt_port = mqtt_port; 50 | _mqtt_username = mqtt_username; 51 | _mqtt_password = mqtt_password; 52 | _mqtt_clientId = mqtt_client_id; 53 | 54 | // In case of a re-initialization with new parameters 55 | if(mqtt_client.connected()) { 56 | mqtt_client.disconnect(); 57 | } 58 | 59 | // Setup MQTT 60 | if(strlen(_mqtt_server) > 1) { 61 | mSerial.printf("MQTT connecting to: %s:%s\n", _mqtt_server, _mqtt_port); 62 | mqtt_client.setServer(_mqtt_server, atoi(_mqtt_port)); 63 | mqtt_client.setCallback(std::bind(&MQTT::mqtt_callback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); 64 | } 65 | else { 66 | mSerial.println("MQTT parameters incomplete. Not setting up MQTT."); 67 | } 68 | } 69 | 70 | // ----------------------------------------------- 71 | void MQTT::loop() { 72 | SCOPED_STACK_ENTRY; 73 | // Only loop if configured correctly 74 | if(!strlen(_mqtt_server)) { 75 | return; 76 | } 77 | 78 | if (!mqtt_client.connected()) { 79 | unsigned long now = millis(); 80 | 81 | if (now - last_reconnect_attempt > 5000) { 82 | last_reconnect_attempt = now; 83 | 84 | if (reconnect()) { 85 | last_reconnect_attempt = 0; 86 | } 87 | else { 88 | mSerial.println("Retrying MQTT connect in 5 seconds"); 89 | } 90 | } 91 | } 92 | else { 93 | mqtt_client.loop(); 94 | } 95 | } 96 | 97 | // ----------------------------------------------- 98 | // Used for setting service online / offline 99 | void MQTT::setStateTopic(std::string state_topic) { 100 | _mqtt_state_topic_str = state_topic; 101 | } 102 | 103 | // ----------------------------------------------- 104 | void MQTT::setIpTopic(std::string ip_topic) { 105 | _mqtt_ip_topic_str = ip_topic; 106 | } 107 | 108 | // ----------------------------------------------- 109 | std::string MQTT::trimWildcards(const char* topic) { 110 | const char* trimChars = " \t\n\r\f\v#/"; 111 | std::string trimmed_topic = topic; 112 | 113 | trimmed_topic.erase(trimmed_topic.find_last_not_of(trimChars) + 1); 114 | 115 | return trimmed_topic; 116 | } 117 | 118 | // ----------------------------------------------- 119 | // Send a message to a broker topic 120 | void MQTT::send_message(const char *topic, const char *payload, bool retain) 121 | { 122 | mSerial.printf("MQTT Outgoing on %s: ", topic); 123 | mSerial.println(payload); 124 | 125 | bool result = mqtt_client.publish(topic, payload, retain); 126 | 127 | if (!result) 128 | { 129 | mSerial.printf("MQTT publish to topic %s failed\n", topic); 130 | } 131 | } 132 | 133 | // ----------------------------------------------- 134 | void MQTT::add_callback(const char* topic, CallbackFunction_t callbackFunction) { 135 | callback_functions.push_back({topic, callbackFunction}); 136 | } 137 | 138 | // ----------------------------------------------- 139 | void MQTT::clear_callbacks() { 140 | callback_functions.clear(); 141 | } 142 | 143 | // ----------------------------------------------- 144 | void MQTT::add_subscription_topic(String topic) { 145 | subscription_topics.push_back(topic); 146 | if(mqtt_client.connected()) { 147 | mqtt_client.subscribe(topic.c_str()); 148 | } 149 | } 150 | 151 | 152 | // ----------------------------------------------- 153 | void MQTT::remove_subscription_topic(String topic) { 154 | subscription_topics.erase(std::remove(subscription_topics.begin(), subscription_topics.end(), topic), subscription_topics.end()); 155 | if(mqtt_client.connected()) { 156 | mqtt_client.unsubscribe(topic.c_str()); 157 | } 158 | } 159 | 160 | // ----------------------------------------------- 161 | void MQTT::clear_subscription_topics() { 162 | subscription_topics.clear(); 163 | } 164 | 165 | // ----------------------------------------------- 166 | void MQTT::mqtt_callback(char* topic, byte* payload, unsigned int length) { 167 | mSerial.print("Message arrived ["); 168 | mSerial.print(topic); 169 | mSerial.print("] "); 170 | for (unsigned int i = 0; i < length; i++) { 171 | mSerial.print((char)payload[i]); 172 | } 173 | mSerial.println(); 174 | 175 | for (auto& cb : callback_functions) { 176 | if(cb.first.equalsIgnoreCase(topic)) { 177 | mSerial.printf("Calling callback for topic '%s'\n", topic); 178 | cb.second(payload, length); 179 | } 180 | } 181 | } 182 | #if 0 183 | // ----------------------------------------------- 184 | // Reconnect to MQTT server and subscribe to in and out topics 185 | bool MQTT::reconnect() 186 | { 187 | // * Loop until we're reconnected 188 | int mqtt_reconnect_retries = 0; 189 | 190 | mSerial.println("reconnect();"); 191 | while (!mqtt_client.connected() && mqtt_reconnect_retries < mqtt_max_reconnect_tries) 192 | { 193 | mqtt_reconnect_retries++; 194 | mSerial.printf("MQTT connection attempt %d / %d ...\n", mqtt_reconnect_retries, mqtt_max_reconnect_tries); 195 | 196 | // * Attempt to connect 197 | 198 | // if (mqtt_client.connect(_mqtt_server, _mqtt_username, _mqtt_password)) 199 | if (mqtt_client.connect(_mqtt_server, _mqtt_username, _mqtt_password, _mqtt_state_topic_str.c_str(), 1, true, "offline", MQTT_CLEAN_SESSION)) 200 | { 201 | delay(100); 202 | mSerial.println(F("MQTT connected!")); 203 | send_message(_mqtt_state_topic_str.c_str(), "online", true); 204 | 205 | delay(25); 206 | /* 207 | // Subscribe to root topic 208 | for (auto topic : subscription_topics) { 209 | bool ret = mqtt_client.subscribe(topic.c_str()); // topic may include wildcards. Needs to be trimmed for publish stuff.. 210 | mSerial.printf("MQTT root topic: '%s', subscription: %d\n", topic.c_str(), ret); 211 | // Prevent broker disconnect: 212 | // mqtt_client.loop(); 213 | } 214 | */ 215 | } 216 | else 217 | { 218 | mSerial.print(F("MQTT Connection failed: rc=")); 219 | mSerial.println(mqtt_client.state()); 220 | // mSerial.println(F(" Retrying in 5 seconds")); 221 | // mSerial.println(""); 222 | 223 | return false; 224 | // * Wait 5 seconds before retrying 225 | feedLoopWDT(); 226 | delay(5000); 227 | } 228 | } 229 | 230 | if (mqtt_reconnect_retries >= mqtt_max_reconnect_tries) 231 | { 232 | mSerial.printf("*** MQTT connection failed, giving up after %d tries ...\n", mqtt_reconnect_retries); 233 | return false; 234 | } 235 | 236 | return true; 237 | } 238 | #else 239 | // ----------------------------------------------- 240 | // Reconnect to MQTT server and subscribe to in and out topics 241 | bool MQTT::reconnect() 242 | { 243 | SCOPED_STACK_ENTRY; 244 | _mqtt_reconnect_retries++; 245 | mSerial.printf("MQTT connection attempt %d ...\n", _mqtt_reconnect_retries); 246 | 247 | if (mqtt_client.connect(_mqtt_clientId, _mqtt_username, _mqtt_password, _mqtt_state_topic_str.c_str(), 1, true, "offline", MQTT_CLEAN_SESSION)) 248 | { 249 | delay(50); 250 | mSerial.println(F("MQTT connected!")); 251 | send_message(_mqtt_state_topic_str.c_str(), "online", true); 252 | 253 | if(!_mqtt_ip_topic_str.empty()) { 254 | delay(10); 255 | send_message(_mqtt_ip_topic_str.c_str(), WiFi.localIP().toString().c_str(), true); 256 | } 257 | 258 | delay(25); 259 | 260 | // Subscribe to root topic 261 | for (auto topic : subscription_topics) { 262 | bool ret = mqtt_client.subscribe(topic.c_str()); // topic may include wildcards. Needs to be trimmed for publish stuff.. 263 | mSerial.printf("MQTT root topic: '%s', subscription: %d\n", topic.c_str(), ret); 264 | // Prevent broker disconnect: 265 | mqtt_client.loop(); 266 | } 267 | 268 | return true; 269 | } 270 | 271 | mSerial.print(F("MQTT Connection failed: rc=")); 272 | mSerial.println(mqtt_client.state()); 273 | 274 | return false; 275 | } 276 | #endif 277 | -------------------------------------------------------------------------------- /src/parameter.cpp: -------------------------------------------------------------------------------- 1 | // Basic headers 2 | #include 3 | #include 4 | #include 5 | 6 | // Tweaked SDK configuration 7 | #include "parameter.h" 8 | 9 | // Arduino includes 10 | #include 11 | #include // https://github.com/tzapu/WiFiManager 12 | #include 13 | 14 | // Required available global entities: 15 | extern WiFiManager wm; 16 | extern Preferences preferences; 17 | 18 | // Free functions: 19 | // ----------------------------------------------- 20 | bool param2bool(const Parameter& param) { 21 | String val = param.getValue(); 22 | val.toLowerCase(); 23 | 24 | if(val == "true" || val == "1") { 25 | return true; 26 | } 27 | 28 | // fallback case: always false 29 | return false; 30 | } 31 | 32 | 33 | // Parameter class functions: 34 | 35 | // ----------------------------------------------- 36 | Parameter::Parameter(const char *custom) : WiFiManagerParameter(custom) 37 | { 38 | // Automatically add parameter to WiFiManager upon creation. 39 | wm.addParameter(this); 40 | } 41 | 42 | // ----------------------------------------------- 43 | Parameter::Parameter(const char *id, const char *label, const char* defaultValue, int length, const char *custom, int labelPlacement) 44 | : WiFiManagerParameter(id, label, defaultValue, length, custom, labelPlacement) 45 | { 46 | // Automatically add parameter to WiFiManager upon creation. 47 | wm.addParameter(this); 48 | } 49 | 50 | // ----------------------------------------------- 51 | void Parameter::initialize() { 52 | // CustomHTLM does not store to NVS. No need to read it... 53 | if(getID() != nullptr) { 54 | preferences.getString(WiFiManagerParameter::getID(), WiFiManagerParameter::_value, WiFiManagerParameter::_length); 55 | } 56 | _initialized = true; 57 | } 58 | 59 | // ----------------------------------------------- 60 | void Parameter::storeValue() { 61 | // CustomHTLM does not store to NVS. No need to store it... 62 | if(getID() != nullptr) { 63 | if(preferences.getString(WiFiManagerParameter::getID(),"") != WiFiManagerParameter::_value) { 64 | preferences.putString(WiFiManagerParameter::getID(), getValue()); 65 | } 66 | } 67 | } 68 | 69 | // ----------------------------------------------- 70 | void Parameter::setValue(const char *value) { 71 | if(_initialized) { 72 | preferences.putString(WiFiManagerParameter::getID(), value); 73 | } 74 | WiFiManagerParameter::setValue(value); 75 | } 76 | --------------------------------------------------------------------------------