// #include // #include "freertos/FreeRTOS.h" // #include "freertos/task.h" #include #include //WiFi Soft-Access Point #include "esp_wifi.h" #include "esp_event_loop.h" #include "nvs_flash.h" #define DEVICE_SSID "auxbridge" #define DEVICE_PASS "" //HTTP Servers #include "tcpip_adapter.h" #include "esp_log.h" #include "esp_err.h" #include "esp_system.h" //SD Card #include #include #include "esp_vfs_fat.h" #include "driver/sdmmc_host.h" #include "sdmmc_cmd.h" //i2s #include #include #include #include #include #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/event_groups.h" #include "driver/i2s.h" #include "driver/adc.h" // fix for i2s error with SPH54065 MEMS microphone #include "soc/i2s_reg.h" //file server #include "esp_http_server.h" #include "esp_vfs.h" //file info #include #include #include //GPIO #include "driver/gpio.h" /* Max length a file path can have on storage */ #define FILE_PATH_MAX (ESP_VFS_PATH_MAX + CONFIG_SPIFFS_OBJ_NAME_LEN) /* Max size of an individual file. Make sure this * value is same as that set in upload_script.html */ #define MAX_FILE_SIZE (200*1024) // 200 KB #define MAX_FILE_SIZE_STR "200KB" /* Scratch buffer size */ #define SCRATCH_BUFSIZE 8192 struct file_server_data { /* Base path of file storage */ char base_path[ESP_VFS_PATH_MAX + 1]; /* Scratch buffer for temporary storage during file transfer */ char scratch[SCRATCH_BUFSIZE]; }; static const char *TAG = "auxbridge"; /////////////////////////////////////////////// //// SD CARD ////////////////////////////////// /////////////////////////////////////////////// void sd_init(void) { ESP_LOGI(TAG, "Initializing SD card"); ESP_LOGI(TAG, "Using SDMMC peripheral"); sdmmc_host_t host = SDMMC_HOST_DEFAULT(); sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT(); gpio_set_pull_mode(15, GPIO_PULLUP_ONLY); // CMD, needed in 4- and 1- line modes gpio_set_pull_mode(2, GPIO_PULLUP_ONLY); // D0, needed in 4- and 1-line modes gpio_set_pull_mode(4, GPIO_PULLUP_ONLY); // D1, needed in 4-line mode only gpio_set_pull_mode(12, GPIO_PULLUP_ONLY); // D2, needed in 4-line mode only gpio_set_pull_mode(13, GPIO_PULLUP_ONLY); // D3, needed in 4- and 1-line modes esp_vfs_fat_sdmmc_mount_config_t mount_config = { .format_if_mount_failed = false, .max_files = 5, .allocation_unit_size = 16 * 1024 }; sdmmc_card_t* card; esp_err_t ret = esp_vfs_fat_sdmmc_mount("/sd", &host, &slot_config, &mount_config, &card); if (ret != ESP_OK) { if (ret == ESP_FAIL) { ESP_LOGE(TAG, "Failed to mount filesystem. " "If you want the card to be formatted, set format_if_mount_failed = true."); } else { ESP_LOGE(TAG, "Failed to initialize the card (%s). " "Make sure SD card lines have pull-up resistors in place.", esp_err_to_name(ret)); } return; } sdmmc_card_print_info(stdout, card); } void sd_test() { ESP_LOGI(TAG, "Opening file"); FILE* f = fopen("/sd/hello.txt", "w"); if (f == NULL) { ESP_LOGE(TAG, "Failed to open file for writing"); return; } fprintf(f, "Hello there!\n"); fclose(f); } void gpio_init() { gpio_pad_select_gpio(GPIO_NUM_16); gpio_set_direction(GPIO_NUM_16, GPIO_MODE_OUTPUT); gpio_set_level(GPIO_NUM_16, 1); } #define I2S_SAMPLE_RATE (44100) #define I2S_NUM (I2S_NUM_0) #define I2S_READ_LEN (16 * 1024) // bits per sample * dma_buf_len //http://www.topherlee.com/software/pcm-tut-wavformat.html void wav_header(char* header, int waveFileSize){ header[0] = 'R'; header[1] = 'I'; header[2] = 'F'; header[3] = 'F'; unsigned int fileSizeMinus8 = waveFileSize - 8; header[4] = (char)(fileSizeMinus8 & 0xFF); header[5] = (char)((fileSizeMinus8 >> 8) & 0xFF); header[6] = (char)((fileSizeMinus8 >> 16) & 0xFF); header[7] = (char)((fileSizeMinus8 >> 24) & 0xFF); header[8] = 'W'; header[9] = 'A'; header[10] = 'V'; header[11] = 'E'; header[12] = 'f'; header[13] = 'm'; header[14] = 't'; header[15] = ' '; header[16] = 0x10; // linear PCM header[17] = 0x00; header[18] = 0x00; header[19] = 0x00; header[20] = 0x01; // linear PCM header[21] = 0x00; header[22] = 0x01; // monoral header[23] = 0x00; header[24] = 0x44; // sampling rate 44100 header[25] = 0xAC; header[26] = 0x00; header[27] = 0x00; header[28] = 0x88; // Byte/sec = 44100x (16 / 8) x1 = 88200 header[29] = 0x58; header[30] = 0x01; header[31] = 0x00; header[32] = 0x02; // 16bit monoral header[33] = 0x00; header[34] = 0x10; // 16bit header[35] = 0x00; header[36] = 'd'; header[37] = 'a'; header[38] = 't'; header[39] = 'a'; unsigned int fileSizeMinusHeader = waveFileSize - 8; header[40] = (char)(fileSizeMinusHeader & 0xFF); header[41] = (char)((fileSizeMinusHeader >> 8) & 0xFF); header[42] = (char)((fileSizeMinusHeader >> 16) & 0xFF); header[43] = (char)((fileSizeMinusHeader >> 24) & 0xFF); } void i2s_init(void) { i2s_config_t i2s_config = { .mode = I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN | I2S_MODE_ADC_BUILT_IN, .sample_rate = I2S_SAMPLE_RATE, .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, .communication_format = I2S_COMM_FORMAT_I2S_MSB, // Big Endian .channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT, .intr_alloc_flags = 0, .dma_buf_count = 5, .dma_buf_len = 1024, .use_apll = 1, }; //install and start i2s driver i2s_driver_install(I2S_NUM, &i2s_config, 0, NULL); //init ADC pad i2s_set_adc_mode(ADC_UNIT_1, ADC1_CHANNEL_7); } void disp_buf(uint8_t* buf, int length) { printf("======\n"); for (int i = 0; i < length; i++) { printf("%02x ", buf[i]); if ((i + 1) % 16 == 0) { printf("\n"); } } printf("======\n"); } void i2s_sample() { int i2s_read_len = I2S_READ_LEN; size_t bytes_read, bytes_written; char* i2s_read_buff = (char*) calloc(i2s_read_len, sizeof(char)); uint8_t* flash_write_buff = (uint8_t*) calloc(i2s_read_len, sizeof(char)); i2s_adc_enable(I2S_NUM); int limit = 700; printf("Create file: sample.txt\n"); FILE* f = fopen("/sd/sample.txt", "w"); // char* header = calloc(44, sizeof(char)); // wav_header(header, 0); // fwrite(header, 1, 44, f); // if (f == NULL) { // ESP_LOGE(TAG, "Failed to open file for writing"); // return; // } while (1) { i2s_read(I2S_NUM, (void*) i2s_read_buff, i2s_read_len, &bytes_read, portMAX_DELAY); disp_buf((uint8_t*) i2s_read_buff, 64); if (limit > 0) { printf("%i Write to file: sample.wav\n", limit); fwrite(i2s_read_buff, 16, 1024, f); limit--; } else if (limit == 0) { fclose(f); struct stat file_stat; stat("/sd/sample.txt", &file_stat); // update WAV header with file size // FILE* f = fopen("/sd/sample.wav", "r+b"); // wav_header(header, file_stat.st_size); // fseek(f, 0, SEEK_SET); // fwrite(header, 1, 44, f); // fclose(f); printf("Done writing sample file (%ld bytes).\n\n", file_stat.st_size); limit--; } } } // const int headerSize = 44; // const int waveDataSize = record_time * 88000; // const int numCommunicationData = 8000; // const int numPartWavData = numCommunicationData/4; // byte header[headerSize]; // char communicationData[numCommunicationData]; // char partWavData[numPartWavData]; // #define SAMPLE_RATE (48000) // #define I2S_NUM (0) // #define BUFFER_SAMPLES (2048) // #define BITS_PER_SAMPLE (32) // #define WS (GPIO_NUM_27) // #define BLK (GPIO_NUM_25) // #define DATA (GPIO_NUM_26) // static void init_i2s() // { // const int sample_rate = 22050;//44100; // gpio_set_direction(WS, GPIO_MODE_OUTPUT); // gpio_set_direction(BLK, GPIO_MODE_OUTPUT); // gpio_set_direction(DATA, GPIO_MODE_INPUT); // /* RX: I2S_NUM_0 */ // i2s_config_t i2s_config_rx = { // .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX), // .sample_rate = sample_rate, // .bits_per_sample = I2S_BITS_PER_SAMPLE_32BIT, // .channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT, // .communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB), // .intr_alloc_flags = 0, // .dma_buf_count = 32, // number of buffers, 128 max. // .dma_buf_len = 512 // size of each buffer // }; // i2s_pin_config_t pin_config_rx = { // .bck_io_num = BLK, // .ws_io_num = WS, // .data_out_num = I2S_PIN_NO_CHANGE, // .data_in_num = DATA // }; // i2s_driver_install(I2S_NUM_0, &i2s_config_rx, 0, NULL); // // REG_SET_BIT( I2S_TIMING_REG(I2S_NUM_0),BIT(9)); // // REG_SET_BIT( I2S_CONF_REG(I2S_NUM_0), I2S_RX_MSB_SHIFT); // i2s_set_pin(I2S_NUM_0, &pin_config_rx); // // i2s_mclk_gpio_select(I2S_NUM_0, 27); // // i2s_set_clk(I2S_NUM_0, sample_rate, I2S_BITS_PER_SAMPLE_32BIT, I2S_CHANNEL_FMT_ONLY_RIGHT); // //i2s_set_sample_rates(I2S_NUM_0, 16000); //set sample rates // } // int i2s_Read(char* data, int numData) { // return i2s_read_bytes(I2S_NUM_0, (char *)data, numData, portMAX_DELAY); // } // void task_megaphone(void *pvParams) // { // int buf_len = 1024; // char* buf = (char*) calloc(buf_len, sizeof(char)); // // struct timeval tv = {0}; // // struct timezone *tz = {0}; // // gettimeofday(&tv, &tz); // // uint64_t micros = tv.tv_usec + tv.tv_sec * 1000000; // // uint64_t micros_prev = micros; // // uint64_t delta = 0; // init_i2s(); // while(1) // { // size_t num_bytes_read = 0; // //char *buf_ptr_write = buf; // // read whole block of samples // esp_err_t ret = i2s_read(I2S_NUM_0, (void*) buf, buf_len, &num_bytes_read, portMAX_DELAY); // if (ret != ESP_OK) { // ESP_LOGE(TAG, "Parameter Error with i2s_read"); // } // printf("num bytes: %d\n", num_bytes_read); // example_disp_buf((uint8_t*)(uint8_t*) buf, 64); // // uint32_t samples_read = num_bytes_read / 2 / (I2S_BITS_PER_SAMPLE_32BIT / 8); // // printf("%s samples\n", num_bytes_read); // // convert 2x 32 bit stereo -> 1 x 16 bit mono // for(int i = 0; i < 32; i++) { // printf("%d ", buf[i]); // } // printf("...\nok."); // // local echo // // bytes_written = samples_read * 2 * (I2S_BITS_PER_SAMPLE_16BIT / 8); // // i2s_write_bytes(I2S_NUM_0, buf, bytes_written, portMAX_DELAY); // // cnt += samples_read; // // if(cnt >= 22500) { // // gettimeofday(&tv, &tz); // // micros = tv.tv_usec + tv.tv_sec * 1000000; // // delta = micros - micros_prev; // // micros_prev = micros; // // printf("%d samples in %" PRIu64 " usecs\n", cnt, delta); // // cnt = 0; // // } // } // } /** * entry point */ #define IS_FILE_EXT(filename, ext) \ (strcasecmp(&filename[strlen(filename) - sizeof(ext) + 1], ext) == 0) /* Set HTTP response content type according to file extension */ static esp_err_t set_content_type_from_file(httpd_req_t *req, const char *filename) { if (IS_FILE_EXT(filename, ".pdf")) { return httpd_resp_set_type(req, "application/pdf"); } else if (IS_FILE_EXT(filename, ".html")) { return httpd_resp_set_type(req, "text/html"); } else if (IS_FILE_EXT(filename, ".jpeg")) { return httpd_resp_set_type(req, "image/jpeg"); } else if (IS_FILE_EXT(filename, ".ico")) { return httpd_resp_set_type(req, "image/x-icon"); } /* This is a limited set only */ /* For any other type always set as plain text */ return httpd_resp_set_type(req, "text/plain"); } static const char* get_path_from_uri(char *dest, const char *base_path, char *uri, size_t destsize) { // const size_t base_pathlen = strlen(base_path); // size_t pathlen = strlen(uri); // if (base_pathlen + pathlen + 1 > destsize) { // /* Full path string won't fit into destination buffer */ // return NULL; // } // char *out = basename(uri); // int length = strlen(out); // printf("%s\n", out); // /* Construct full path (base + path) */ // strcpy(dest, base_path); // strlcpy(dest + base_pathlen, basename(uri), length + 1); // // char *idx = strchr(uri, '/'); // // while (idx != NULL) { // // if (uri[idx - uri] == 'f' && // // uri[idx - uri + 1] == 'i' && // // uri[idx - uri + 2] == 'l' && // // uri[idx - uri + 3] == 'e' && // // uri[idx - uri + 4] == '/') { // // sprintf(dest, "%s%.*s", base_path, pathlen - (idx - uri + 5), uri + idx); // // return dest; // // } // // idx = strchr(uri, '/'); // // } // return dest; // // //https://stackoverflow.com/questions/4214314/get-a-substring-of-a-char // // /* Construct full path (base + path) */ // // sprintf(dest, "%s%.*s", base_path, ) // // strcpy(dest, base_path); // // strlcpy(dest + base_pathlen, uri, pathlen + 1); // // /* Return pointer to path, skipping the base */ return dest; } /* Handler to download a file kept on the server */ static esp_err_t download_get_handler(httpd_req_t *req) { char filepath[FILE_PATH_MAX]; FILE *fd = NULL; struct stat file_stat; const char *filename = get_path_from_uri(filepath, ((struct file_server_data *)req->user_ctx)->base_path, req->uri, sizeof(filepath)); if (!filename) { ESP_LOGE(TAG, "Filename is too long"); /* Respond with 500 Internal Server Error */ httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Filename too long"); return ESP_FAIL; } if (stat(filepath, &file_stat) == -1) { ESP_LOGE(TAG, "Failed to stat file : %s", filepath); /* Respond with 404 Not Found */ httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, "File does not exist"); return ESP_FAIL; } fd = fopen(filepath, "r"); if (!fd) { ESP_LOGE(TAG, "Failed to read existing file : %s", filepath); /* Respond with 500 Internal Server Error */ httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to read existing file"); return ESP_FAIL; } ESP_LOGI(TAG, "Sending file : %s (%ld bytes)...", filename, file_stat.st_size); set_content_type_from_file(req, filename); /* Retrieve the pointer to scratch buffer for temporary storage */ char *chunk = ((struct file_server_data *)req->user_ctx)->scratch; size_t chunksize; do { /* Read file in chunks into the scratch buffer */ chunksize = fread(chunk, 1, SCRATCH_BUFSIZE, fd); /* Send the buffer contents as HTTP response chunk */ if (httpd_resp_send_chunk(req, chunk, chunksize) != ESP_OK) { fclose(fd); ESP_LOGE(TAG, "File sending failed!"); /* Abort sending file */ httpd_resp_sendstr_chunk(req, NULL); /* Respond with 500 Internal Server Error */ httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to send file"); return ESP_FAIL; } /* Keep looping till the whole file is sent */ } while (chunksize != 0); /* Close file after sending complete */ fclose(fd); ESP_LOGI(TAG, "File sending complete"); /* Respond with an empty chunk to signal HTTP response completion */ httpd_resp_send_chunk(req, NULL, 0); return ESP_OK; } static esp_err_t sample_get_handler(httpd_req_t *req) { ESP_LOGI(TAG, "Reading sample file"); char filepath[FILE_PATH_MAX]; FILE *fd = NULL; struct stat file_stat; fd = fopen("/sd/sample.wav", "r"); if (!fd) { ESP_LOGE(TAG, "Failed to read existing file : %s", filepath); /* Respond with 500 Internal Server Error */ httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to read existing file"); return ESP_FAIL; } ESP_LOGI(TAG, "Sending file : sample.txt..."); set_content_type_from_file(req, "sample.txt"); /* Retrieve the pointer to scratch buffer for temporary storage */ char *chunk = ((struct file_server_data *)req->user_ctx)->scratch; size_t chunksize; do { /* Read file in chunks into the scratch buffer */ chunksize = fread(chunk, 1, SCRATCH_BUFSIZE, fd); /* Send the buffer contents as HTTP response chunk */ if (httpd_resp_send_chunk(req, chunk, chunksize) != ESP_OK) { fclose(fd); ESP_LOGE(TAG, "File sending failed!"); /* Abort sending file */ httpd_resp_sendstr_chunk(req, NULL); /* Respond with 500 Internal Server Error */ httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to send file"); return ESP_FAIL; } /* Keep looping till the whole file is sent */ } while (chunksize != 0); /* Close file after sending complete */ fclose(fd); ESP_LOGI(TAG, "File sending complete"); /* Respond with an empty chunk to signal HTTP response completion */ httpd_resp_send_chunk(req, NULL, 0); return ESP_OK; } #define JSON_NO_DELIMITER 0 #define JSON_WITH_DELIMITER 1 #define JSON_STRING_FORMAT "\"%s\":\"%s\"%s" #define JSON_NUM_FORMAT "\"%s\":%s%s" #define JSON_FILESIZE_FORMAT "\"%s\":%ld%s" #define JSON_STRING_FORMAT_WITH_DELIMITER JSON_STRING_FORMAT, JSON_WITH_DELIMITER #define JSON_STRING_FORMAT_NO_DELIMITER JSON_STRING_FORMAT, JSON_NO_DELIMITER #define JSON_NUM_FORMAT_WITH_DELIMITER JSON_NUM_FORMAT, JSON_WITH_DELIMITER #define JSON_NUM_FORMAT_NO_DELIMITER JSON_NUM_FORMAT, JSON_NO_DELIMITER char* json(char *ret, char *key, char *val, const char *fmt, int deliminator) { sprintf(ret, fmt, key, val, (deliminator == JSON_NO_DELIMITER) ? "\n": ",\n"); return ret; } static esp_err_t device_status_handler(httpd_req_t *req) { char buf[80]; httpd_resp_set_type(req, "application/json"); httpd_resp_sendstr_chunk(req, "{\n"); httpd_resp_sendstr_chunk(req, json(buf, "name", "auxbridge", JSON_STRING_FORMAT_WITH_DELIMITER)); httpd_resp_sendstr_chunk(req, json(buf, "author", "mschrage", JSON_STRING_FORMAT_WITH_DELIMITER)); httpd_resp_sendstr_chunk(req, json(buf, "version", "1.0", JSON_NUM_FORMAT_NO_DELIMITER)); httpd_resp_sendstr_chunk(req, "}"); httpd_resp_send_chunk(req, NULL, 0); return ESP_OK; } static esp_err_t manifest_handler(httpd_req_t *req) { char buf[80]; char entrypath[20]; struct dirent *entry; struct stat entry_stat; char entrysize[16]; char *dirpath = "/sd/"; DIR *dir = opendir(dirpath); const size_t dirpath_len = strlen(dirpath); strlcpy(entrypath, dirpath, sizeof(entrypath)); httpd_resp_set_type(req, "application/json"); httpd_resp_sendstr_chunk(req, "{\n"); httpd_resp_sendstr_chunk(req, "\"files\": [\n"); int count = 0; while ((entry = readdir(dir)) != NULL) { strlcpy(entrypath + dirpath_len, entry->d_name, sizeof(entrypath) - dirpath_len); if (stat(entrypath, &entry_stat) == -1) { ESP_LOGE(TAG, "Failed to stat %s, %s", entrypath, entry->d_name); continue; } sprintf(entrysize, "%ld", entry_stat.st_size); ESP_LOGI(TAG, "Found %s (%s bytes)", entry->d_name, entrysize); if (count > 0) { httpd_resp_sendstr_chunk(req, ","); } httpd_resp_sendstr_chunk(req, "{\n"); httpd_resp_sendstr_chunk(req, json(buf, "id", entry->d_name, JSON_STRING_FORMAT_WITH_DELIMITER)); //httpd_resp_sendstr_chunk(req, json(buf, "dt", ctime(&entry_stat.st_mtime), JSON_STRING_FORMAT_WITH_DELIMITER)); httpd_resp_sendstr_chunk(req, json(buf, "size", entrysize, JSON_NUM_FORMAT_WITH_DELIMITER)); httpd_resp_sendstr_chunk(req, "}"); count++; } closedir(dir); httpd_resp_sendstr_chunk(req, "]\n"); httpd_resp_sendstr_chunk(req, "}"); httpd_resp_send_chunk(req, NULL, 0); return ESP_OK; } esp_err_t server_init(const char *base_path) { static struct file_server_data *server_data = NULL; /* Validate file storage base path */ if (!base_path || strcmp(base_path, "/sd") != 0) { ESP_LOGE(TAG, "File server presently supports only '/sd' as base path"); return ESP_ERR_INVALID_ARG; } if (server_data) { ESP_LOGE(TAG, "File server already started"); return ESP_ERR_INVALID_STATE; } /* Allocate memory for server data */ server_data = calloc(1, sizeof(struct file_server_data)); if (!server_data) { ESP_LOGE(TAG, "Failed to allocate memory for server data"); return ESP_ERR_NO_MEM; } strlcpy(server_data->base_path, base_path, sizeof(server_data->base_path)); httpd_handle_t server = NULL; httpd_config_t config = HTTPD_DEFAULT_CONFIG(); /* Use the URI wildcard matching function in order to * allow the same handler to respond to multiple different * target URIs which match the wildcard scheme */ config.uri_match_fn = httpd_uri_match_wildcard; ESP_LOGI(TAG, "Starting server on port: '%d'", config.server_port); if (httpd_start(&server, &config) != ESP_OK) { ESP_LOGE(TAG, "Error starting file server!"); return ESP_FAIL; } ESP_LOGI(TAG, "Registering URI handlers"); httpd_uri_t sample_download = { .uri = "/sample", .method = HTTP_GET, .handler = sample_get_handler, .user_ctx = server_data // Pass server data as context }; httpd_register_uri_handler(server, &sample_download); /* URI handler for getting uploaded files */ httpd_uri_t file_download = { .uri = "/file/*", // Match all URIs of type /path/to/file .method = HTTP_GET, .handler = download_get_handler, .user_ctx = server_data // Pass server data as context }; httpd_register_uri_handler(server, &file_download); httpd_uri_t device_status = { .uri = "/device", // Match all URIs of type /upload/path/to/file .method = HTTP_GET, .handler = device_status_handler, .user_ctx = server_data // Pass server data as context }; httpd_register_uri_handler(server, &device_status); /* URI handler for deleting files from server */ httpd_uri_t manifest = { .uri = "/manifest", // Match all URIs of type /delete/path/to/file .method = HTTP_GET, .handler = manifest_handler, .user_ctx = server_data // Pass server data as context }; httpd_register_uri_handler(server, &manifest); return ESP_OK; } /* FreeRTOS event group to signal when we are connected*/ static EventGroupHandle_t s_wifi_event_group; static esp_err_t event_handler(void *ctx, system_event_t *event) { switch(event->event_id) { case SYSTEM_EVENT_AP_STACONNECTED: ESP_LOGI(TAG, "station:"MACSTR" join, AID=%d", MAC2STR(event->event_info.sta_connected.mac), event->event_info.sta_connected.aid); break; case SYSTEM_EVENT_AP_STADISCONNECTED: ESP_LOGI(TAG, "station:"MACSTR"leave, AID=%d", MAC2STR(event->event_info.sta_disconnected.mac), event->event_info.sta_disconnected.aid); break; default: break; } return ESP_OK; } void wifi_init(void) { esp_err_t ret = nvs_flash_init(); if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { ESP_ERROR_CHECK(nvs_flash_erase()); ret = nvs_flash_init(); } ESP_ERROR_CHECK(ret); s_wifi_event_group = xEventGroupCreate(); tcpip_adapter_init(); ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL)); wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); ESP_ERROR_CHECK(esp_wifi_init(&cfg)); wifi_config_t wifi_config = { .ap = { .ssid = DEVICE_SSID, .ssid_len = strlen(DEVICE_SSID), .password = DEVICE_PASS, .max_connection = 1, .authmode = WIFI_AUTH_OPEN }, }; ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP)); ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_AP, &wifi_config)); ESP_ERROR_CHECK(esp_wifi_start()); tcpip_adapter_ip_info_t ip_info; IP4_ADDR(&ip_info.ip, 192, 168, 1, 1); IP4_ADDR(&ip_info.gw, 192, 168, 1, 1); IP4_ADDR(&ip_info.netmask, 255, 255, 255, 0); //192.168.4.1 // tcpip_adapter_ap_start((uint8_t *)"9C:B6:D0:E7:30:0F", &ip_info); ESP_LOGI(TAG, "wifi_init_softap finished.SSID:%s password:%s", DEVICE_SSID, DEVICE_PASS); // start_webserver(); } void app_main() { printf("starting app_main()\n"); gpio_init(); sd_init(); sd_test(); wifi_init(); server_init("/sd"); i2s_init(); i2s_sample(); } // s