├── ESP32WebcamBluetoothWifi.ino ├── README.md ├── camera_index.h └── camera_pins.h /ESP32WebcamBluetoothWifi.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include "esp_http_server.h" 3 | #include 4 | #include "esp_camera.h" 5 | #include "camera_index.h" 6 | #include 7 | #include "BluetoothSerial.h" 8 | 9 | 10 | #define CAMERA_MODEL_ESP_EYE 11 | //#define CAMERA_MODEL_AI_THINKER 12 | #include "camera_pins.h" 13 | 14 | String ssids_array[50]; 15 | String network_string; 16 | String connected_string; 17 | 18 | const char* pref_ssid = ""; 19 | const char* pref_pass = ""; 20 | String client_wifi_ssid; 21 | String client_wifi_password; 22 | 23 | const char* bluetooth_name = "robot01"; 24 | 25 | long start_wifi_millis; 26 | long wifi_timeout = 10000; 27 | bool bluetooth_disconnect = false; 28 | 29 | enum wifi_setup_stages { NONE, SCAN_START, SCAN_COMPLETE, SSID_ENTERED, WAIT_PASS, PASS_ENTERED, WAIT_CONNECT, LOGIN_FAILED }; 30 | enum wifi_setup_stages wifi_stage = NONE; 31 | 32 | camera_fb_t * fb = NULL; 33 | 34 | using namespace websockets; 35 | WebsocketsServer socket_server; 36 | BluetoothSerial SerialBT; 37 | Preferences preferences; 38 | 39 | httpd_handle_t camera_httpd = NULL; 40 | 41 | void setup() 42 | { 43 | Serial.begin(115200); 44 | Serial.println("Booting..."); 45 | 46 | preferences.begin("wifi_access", false); 47 | 48 | camera_config_t config; 49 | config.ledc_channel = LEDC_CHANNEL_0; 50 | config.ledc_timer = LEDC_TIMER_0; 51 | config.pin_d0 = Y2_GPIO_NUM; 52 | config.pin_d1 = Y3_GPIO_NUM; 53 | config.pin_d2 = Y4_GPIO_NUM; 54 | config.pin_d3 = Y5_GPIO_NUM; 55 | config.pin_d4 = Y6_GPIO_NUM; 56 | config.pin_d5 = Y7_GPIO_NUM; 57 | config.pin_d6 = Y8_GPIO_NUM; 58 | config.pin_d7 = Y9_GPIO_NUM; 59 | config.pin_xclk = XCLK_GPIO_NUM; 60 | config.pin_pclk = PCLK_GPIO_NUM; 61 | config.pin_vsync = VSYNC_GPIO_NUM; 62 | config.pin_href = HREF_GPIO_NUM; 63 | config.pin_sscb_sda = SIOD_GPIO_NUM; 64 | config.pin_sscb_scl = SIOC_GPIO_NUM; 65 | config.pin_pwdn = PWDN_GPIO_NUM; 66 | config.pin_reset = RESET_GPIO_NUM; 67 | config.xclk_freq_hz = 20000000; 68 | config.pixel_format = PIXFORMAT_JPEG; 69 | //init with high specs to pre-allocate larger buffers 70 | if (psramFound()) { 71 | config.frame_size = FRAMESIZE_UXGA; 72 | config.jpeg_quality = 10; 73 | config.fb_count = 2; 74 | } else { 75 | config.frame_size = FRAMESIZE_SVGA; 76 | config.jpeg_quality = 12; 77 | config.fb_count = 1; 78 | } 79 | 80 | #if defined(CAMERA_MODEL_ESP_EYE) 81 | pinMode(13, INPUT_PULLUP); 82 | pinMode(14, INPUT_PULLUP); 83 | #endif 84 | 85 | // camera init 86 | esp_err_t err = esp_camera_init(&config); 87 | if (err != ESP_OK) { 88 | Serial.printf("Camera init failed with error 0x%x", err); 89 | return; 90 | } 91 | 92 | sensor_t * s = esp_camera_sensor_get(); 93 | s->set_framesize(s, FRAMESIZE_QVGA); 94 | 95 | if (!init_wifi()) { // Connect to Wi-Fi fails 96 | SerialBT.register_callback(callback); 97 | } else { 98 | SerialBT.register_callback(callback_show_ip); 99 | } 100 | 101 | SerialBT.begin(bluetooth_name); 102 | 103 | app_httpserver_init(); 104 | socket_server.listen(82); 105 | } 106 | 107 | bool init_wifi() 108 | { 109 | String temp_pref_ssid = preferences.getString("pref_ssid"); 110 | String temp_pref_pass = preferences.getString("pref_pass"); 111 | pref_ssid = temp_pref_ssid.c_str(); 112 | pref_pass = temp_pref_pass.c_str(); 113 | 114 | Serial.println(pref_ssid); 115 | Serial.println(pref_pass); 116 | 117 | WiFi.config(INADDR_NONE, INADDR_NONE, INADDR_NONE); 118 | 119 | start_wifi_millis = millis(); 120 | WiFi.begin(pref_ssid, pref_pass); 121 | while (WiFi.status() != WL_CONNECTED) { 122 | delay(500); 123 | Serial.print("."); 124 | if (millis() - start_wifi_millis > wifi_timeout) { 125 | WiFi.disconnect(true, true); 126 | return false; 127 | } 128 | } 129 | return true; 130 | } 131 | 132 | void scan_wifi_networks() 133 | { 134 | WiFi.mode(WIFI_STA); 135 | // WiFi.scanNetworks will return the number of networks found 136 | int n = WiFi.scanNetworks(); 137 | if (n == 0) { 138 | SerialBT.println("no networks found"); 139 | } else { 140 | SerialBT.println(); 141 | SerialBT.print(n); 142 | SerialBT.println(" networks found"); 143 | delay(1000); 144 | for (int i = 0; i < n; ++i) { 145 | ssids_array[i + 1] = WiFi.SSID(i); 146 | Serial.print(i + 1); 147 | Serial.print(": "); 148 | Serial.println(ssids_array[i + 1]); 149 | network_string = i + 1; 150 | network_string = network_string + ": " + WiFi.SSID(i) + " (Strength:" + WiFi.RSSI(i) + ")"; 151 | SerialBT.println(network_string); 152 | } 153 | wifi_stage = SCAN_COMPLETE; 154 | } 155 | } 156 | 157 | void callback(esp_spp_cb_event_t event, esp_spp_cb_param_t *param) 158 | { 159 | 160 | if (event == ESP_SPP_SRV_OPEN_EVT) { 161 | wifi_stage = SCAN_START; 162 | } 163 | 164 | if (event == ESP_SPP_DATA_IND_EVT && wifi_stage == SCAN_COMPLETE) { // data from phone is SSID 165 | int client_wifi_ssid_id = SerialBT.readString().toInt(); 166 | client_wifi_ssid = ssids_array[client_wifi_ssid_id]; 167 | wifi_stage = SSID_ENTERED; 168 | } 169 | 170 | if (event == ESP_SPP_DATA_IND_EVT && wifi_stage == WAIT_PASS) { // data from phone is password 171 | client_wifi_password = SerialBT.readString(); 172 | client_wifi_password.trim(); 173 | wifi_stage = PASS_ENTERED; 174 | } 175 | 176 | } 177 | 178 | void callback_show_ip(esp_spp_cb_event_t event, esp_spp_cb_param_t *param) 179 | { 180 | if (event == ESP_SPP_SRV_OPEN_EVT) { 181 | SerialBT.print("ESP32 IP: "); 182 | SerialBT.println(WiFi.localIP()); 183 | bluetooth_disconnect = true; 184 | } 185 | } 186 | 187 | static esp_err_t main_handler(httpd_req_t *req) { 188 | httpd_resp_set_type(req, "text/html"); 189 | httpd_resp_set_hdr(req, "Content-Encoding", "gzip"); 190 | return httpd_resp_send(req, (const char *)main_html, main_html_len); 191 | } 192 | 193 | httpd_uri_t main_uri = { 194 | .uri = "/", 195 | .method = HTTP_GET, 196 | .handler = main_handler, 197 | .user_ctx = NULL 198 | }; 199 | 200 | void app_httpserver_init () 201 | { 202 | httpd_config_t config = HTTPD_DEFAULT_CONFIG(); 203 | if (httpd_start(&camera_httpd, &config) == ESP_OK) 204 | { 205 | Serial.println("httpd_start"); 206 | httpd_register_uri_handler(camera_httpd, &main_uri); 207 | } 208 | } 209 | 210 | void disconnect_bluetooth() 211 | { 212 | delay(1000); 213 | Serial.println("BT stopping"); 214 | SerialBT.println("Bluetooth disconnecting..."); 215 | delay(1000); 216 | SerialBT.flush(); 217 | SerialBT.disconnect(); 218 | SerialBT.end(); 219 | Serial.println("BT stopped"); 220 | delay(1000); 221 | bluetooth_disconnect = false; 222 | } 223 | 224 | void loop() 225 | { 226 | 227 | if (bluetooth_disconnect) 228 | { 229 | disconnect_bluetooth(); 230 | } 231 | 232 | switch (wifi_stage) 233 | { 234 | case SCAN_START: 235 | SerialBT.println("Scanning Wi-Fi networks"); 236 | Serial.println("Scanning Wi-Fi networks"); 237 | scan_wifi_networks(); 238 | SerialBT.println("Please enter the number for your Wi-Fi"); 239 | wifi_stage = SCAN_COMPLETE; 240 | break; 241 | 242 | case SSID_ENTERED: 243 | SerialBT.println("Please enter your Wi-Fi password"); 244 | Serial.println("Please enter your Wi-Fi password"); 245 | wifi_stage = WAIT_PASS; 246 | break; 247 | 248 | case PASS_ENTERED: 249 | SerialBT.println("Please wait for Wi-Fi connection..."); 250 | Serial.println("Please wait for Wi_Fi connection..."); 251 | wifi_stage = WAIT_CONNECT; 252 | preferences.putString("pref_ssid", client_wifi_ssid); 253 | preferences.putString("pref_pass", client_wifi_password); 254 | if (init_wifi()) { // Connected to WiFi 255 | connected_string = "ESP32 IP: "; 256 | connected_string = connected_string + WiFi.localIP().toString(); 257 | SerialBT.println(connected_string); 258 | Serial.println(connected_string); 259 | bluetooth_disconnect = true; 260 | } else { // try again 261 | wifi_stage = LOGIN_FAILED; 262 | } 263 | break; 264 | 265 | case LOGIN_FAILED: 266 | SerialBT.println("Wi-Fi connection failed"); 267 | Serial.println("Wi-Fi connection failed"); 268 | delay(2000); 269 | wifi_stage = SCAN_START; 270 | break; 271 | } 272 | 273 | if (socket_server.poll()) { 274 | disconnect_bluetooth(); 275 | auto client = socket_server.accept(); 276 | while (client.available()) { 277 | fb = esp_camera_fb_get(); 278 | client.sendBinary((const char *)fb->buf, fb->len); 279 | esp_camera_fb_return(fb); 280 | fb = NULL; 281 | } 282 | } 283 | 284 | } 285 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # esp32-bluewifi 2 | Wi-Fi setup using Bluetooth serial - Full details on the blog: https://robotzero.one/esp32-wi-fi-connection-bluetooth/ 3 | -------------------------------------------------------------------------------- /camera_index.h: -------------------------------------------------------------------------------- 1 | 2 | //File: index_ov2640.html.gz, Size: 4316 3 | #define main_html_len 4316 4 | 5 | const uint8_t main_html[] = { 6 | 0x1f,0x8b,0x08,0x08,0x49,0xa1,0x00,0x5e,0x00,0xff,0x69,0x6e,0x64,0x65,0x78,0x2e,0x68,0x74,0x6d,0x6c,0x2e,0x67,0x7a,0x00,0x55,0x52,0x5f,0x6b,0xdb,0x30,0x10,0x7f,0x8e,0x3f,0xc5,0x21,0x18,0x4e,0xe9,0x22,0xa7,0xdd,0xcb,0x48,0x6c,0xc3,0x5a,0xfa,0x30,0xd8,0xd8,0x58,0x18,0x7d,0x1c,0xb2,0x74,0x8d,0x6f,0xb3,0xa5,0x22,0x9d,0x63,0xc2,0xc8,0x77,0xef,0x39,0xff,0xc6,0xfc,0x72,0xd2,0xdd,0xef,0xcf,0xdd,0xc9,0x65,0xcb,0x7d,0x57,0x67,0x65,0x8b,0xc6,0x49,0xe8,0x91,0x0d,0x78,0xd3,0x63,0x95,0xef,0x08,0xc7,0xd7,0x10,0x39,0x87,0xcb,0x67,0x83,0x67,0xf4,0x5c,0xe5,0x23,0x39,0x6e,0x2b,0x87,0x3b,0xb2,0xb8,0x38,0x5e,0xde,0x5f,0x41,0xe4,0x89,0xc9,0x74,0x8b,0x64,0x4d,0x87,0xd5,0x9d,0x5e,0xfe,0x2b,0x0d,0x09,0xe3,0x31,0x6f,0x1a,0x29,0xf9,0x90,0x43,0x21,0x96,0x4c,0xdc,0x61,0xfd,0xb4,0xf9,0xfe,0xe1,0x7e,0xf1,0xf8,0xe9,0x6b,0x59,0x9c,0x12,0x59,0x59,0x9c,0x7b,0x6a,0x82,0xdb,0x4b,0x70,0xb4,0x03,0x72,0x55,0x1e,0x06,0xc6,0xf8,0x28,0xad,0x18,0xf2,0x18,0xf3,0x3a,0x83,0x92,0xfa,0xed,0x54,0x52,0x89,0x23,0x9a,0x5e,0x41,0x8a,0xb6,0x52,0x12,0x78,0x2f,0x3e,0xea,0xd8,0xe0,0xca,0x0c,0x1c,0xd6,0xd0,0x22,0x6d,0x5b,0x5e,0xdd,0x2d,0x97,0xef,0xd4,0x64,0x21,0xa2,0x12,0x92,0x8d,0xf4,0xca,0x75,0x26,0x03,0x26,0x86,0x69,0x70,0xa8,0xc0,0x05,0x3b,0xf4,0x32,0xae,0xde,0x22,0x3f,0x75,0x38,0x1d,0x1f,0xf6,0x9f,0xdd,0x3c,0x3f,0xb9,0xe4,0x37,0xeb,0x33,0xfe,0x79,0xf3,0xeb,0xe7,0x8f,0x2f,0xc2,0x50,0x63,0x5a,0x15,0x85,0x82,0x5b,0x18,0xc9,0xbb,0x30,0xea,0x2e,0x58,0xc3,0x14,0xbc,0x6e,0x83,0xe0,0x6e,0x41,0xad,0x3e,0xde,0xab,0x0b,0x6d,0x4c,0x42,0xf1,0x62,0xf5,0x8c,0xcd,0x26,0xd8,0x3f,0xc8,0xf3,0x93,0x92,0x08,0xcf,0x66,0xd9,0x98,0x74,0xf0,0x3d,0xa6,0x64,0xb6,0x28,0xc0,0xeb,0xa9,0x86,0xbf,0xd9,0x8c,0x5e,0x60,0x7e,0xce,0x68,0x67,0xe4,0xc9,0x48,0x14,0x8d,0xb7,0x18,0x5e,0xe0,0xa1,0x0b,0xcd,0xcd,0x04,0x02,0xd8,0x99,0x08,0x43,0xec,0xbe,0x35,0xbf,0xd1,0xb2,0x88,0x88,0xb8,0xb6,0xd2,0x3c,0xe3,0x29,0x25,0xf7,0xff,0x64,0x26,0x67,0x61,0xc9,0xfc,0x5a,0x56,0x28,0x84,0x2b,0x59,0x0a,0x87,0xec,0xb0,0x96,0x8d,0x5d,0x76,0x55,0x16,0xe7,0x77,0x29,0x8e,0x7f,0xd0,0x1b,0x43,0x52,0xe5,0x80,0x48,0x02,0x00,0x00 7 | }; 8 | -------------------------------------------------------------------------------- /camera_pins.h: -------------------------------------------------------------------------------- 1 | 2 | #if defined(CAMERA_MODEL_WROVER_KIT) 3 | #define PWDN_GPIO_NUM -1 4 | #define RESET_GPIO_NUM -1 5 | #define XCLK_GPIO_NUM 21 6 | #define SIOD_GPIO_NUM 26 7 | #define SIOC_GPIO_NUM 27 8 | 9 | #define Y9_GPIO_NUM 35 10 | #define Y8_GPIO_NUM 34 11 | #define Y7_GPIO_NUM 39 12 | #define Y6_GPIO_NUM 36 13 | #define Y5_GPIO_NUM 19 14 | #define Y4_GPIO_NUM 18 15 | #define Y3_GPIO_NUM 5 16 | #define Y2_GPIO_NUM 4 17 | #define VSYNC_GPIO_NUM 25 18 | #define HREF_GPIO_NUM 23 19 | #define PCLK_GPIO_NUM 22 20 | 21 | #elif defined(CAMERA_MODEL_ESP_EYE) 22 | #define PWDN_GPIO_NUM -1 23 | #define RESET_GPIO_NUM -1 24 | #define XCLK_GPIO_NUM 4 25 | #define SIOD_GPIO_NUM 18 26 | #define SIOC_GPIO_NUM 23 27 | 28 | #define Y9_GPIO_NUM 36 29 | #define Y8_GPIO_NUM 37 30 | #define Y7_GPIO_NUM 38 31 | #define Y6_GPIO_NUM 39 32 | #define Y5_GPIO_NUM 35 33 | #define Y4_GPIO_NUM 14 34 | #define Y3_GPIO_NUM 13 35 | #define Y2_GPIO_NUM 34 36 | #define VSYNC_GPIO_NUM 5 37 | #define HREF_GPIO_NUM 27 38 | #define PCLK_GPIO_NUM 25 39 | 40 | #elif defined(CAMERA_MODEL_M5STACK_PSRAM) 41 | #define PWDN_GPIO_NUM -1 42 | #define RESET_GPIO_NUM 15 43 | #define XCLK_GPIO_NUM 27 44 | #define SIOD_GPIO_NUM 25 45 | #define SIOC_GPIO_NUM 23 46 | 47 | #define Y9_GPIO_NUM 19 48 | #define Y8_GPIO_NUM 36 49 | #define Y7_GPIO_NUM 18 50 | #define Y6_GPIO_NUM 39 51 | #define Y5_GPIO_NUM 5 52 | #define Y4_GPIO_NUM 34 53 | #define Y3_GPIO_NUM 35 54 | #define Y2_GPIO_NUM 32 55 | #define VSYNC_GPIO_NUM 22 56 | #define HREF_GPIO_NUM 26 57 | #define PCLK_GPIO_NUM 21 58 | 59 | #elif defined(CAMERA_MODEL_M5STACK_WIDE) 60 | #define PWDN_GPIO_NUM -1 61 | #define RESET_GPIO_NUM 15 62 | #define XCLK_GPIO_NUM 27 63 | #define SIOD_GPIO_NUM 22 64 | #define SIOC_GPIO_NUM 23 65 | 66 | #define Y9_GPIO_NUM 19 67 | #define Y8_GPIO_NUM 36 68 | #define Y7_GPIO_NUM 18 69 | #define Y6_GPIO_NUM 39 70 | #define Y5_GPIO_NUM 5 71 | #define Y4_GPIO_NUM 34 72 | #define Y3_GPIO_NUM 35 73 | #define Y2_GPIO_NUM 32 74 | #define VSYNC_GPIO_NUM 25 75 | #define HREF_GPIO_NUM 26 76 | #define PCLK_GPIO_NUM 21 77 | 78 | #elif defined(CAMERA_MODEL_AI_THINKER) 79 | #define PWDN_GPIO_NUM 32 80 | #define RESET_GPIO_NUM -1 81 | #define XCLK_GPIO_NUM 0 82 | #define SIOD_GPIO_NUM 26 83 | #define SIOC_GPIO_NUM 27 84 | 85 | #define Y9_GPIO_NUM 35 86 | #define Y8_GPIO_NUM 34 87 | #define Y7_GPIO_NUM 39 88 | #define Y6_GPIO_NUM 36 89 | #define Y5_GPIO_NUM 21 90 | #define Y4_GPIO_NUM 19 91 | #define Y3_GPIO_NUM 18 92 | #define Y2_GPIO_NUM 5 93 | #define VSYNC_GPIO_NUM 25 94 | #define HREF_GPIO_NUM 23 95 | #define PCLK_GPIO_NUM 22 96 | 97 | #else 98 | #error "Camera model not selected" 99 | #endif 100 | --------------------------------------------------------------------------------