ここからは、Flashに保存してデータを編集する為にHTTP Serverを立ち上げて行きたいと思います。
Baseは、サンプルプログラム ”simple”
ESP32とAP接続し必要なプログラムを開発して行くのが筋ですが、プログラムの開発を考えると先ずSTA接続しサーバーを立ち上げ、その環境で全てプログラムを書き終えてからAP接続に変更した方が効率が良さそうです。先ずはESP32とはSTA接続する事にします。
今回HTTPサーバを立ち上げたいのですが、条件に有ったサンプルプログラムが、”~/esp/esp-idf/examples/protocols/http_server/simple”に有りました。これを元にプログラムを書いて行けそうなのですが、このサンプルで使用している関数、”example_connect()”が理解出来ません。この関数はESP32の接続を行う関数で、それが理解出来ないと先に進みません。そこでこの部分を代行する他のサンプルプログラムを探すと、”~/esp/esp-idf/examples/wifi/getting_started/station”にSTA接続するサンプルプログラムが有りました。”example_connect()”をこのサンプルのコードを元に書き換えてプログラムを書き始める事にしました。ちなみに、”~/esp/esp-idf/examples/wifi/getting_started/softAP”にAP接続のサンプルプログラムが有ります。
先ずはサンプル通りに
プロジェクトの構造は以下の様になっています。今回はmainフォルダーの下のmain.cの変更と、そこにKconfig.projbuildを追加しています。
- http_sample/
- CMakeLists.txt
- partitions_example.csv
- sdkconfig.defaults
- main/
- CMakeLists.txt
- main.c
- Kconfig.projbuild
- components/
- spiffs_image/
- station.txt
先ずはmain.cの説明から。サンプルプログラムを元に下記の様にコードを書いて見ました。
main.c
#include <esp_wifi.h>
#include <esp_event.h>
#include <esp_log.h>
#include "nvs_flash.h"
#include <esp_http_server.h>
#include "mdns.h"
#include "esp_spiffs.h"
#include "freertos/event_groups.h"
static const char *TAG = "example";
struct st_info_data { //Station info
char _deco[5];
char _st_ID[20];
char _st_URL[200];
};
struct st_info_data info_data[24];
void read_station(void)
{
char buf[330];
FILE *fp = NULL;
int a,b;
fp = fopen("/spiffs/station.txt", "r");
for(a = 0; a < 24; a ++)
{
fgets(buf, 300, fp);
*(strchr(buf, ',')) = 0;
strcpy(info_data[a]._deco, buf);
b = strlen(buf) + 1;
*(strchr(&buf[b], ',')) = 0;
strcpy(info_data[a]._st_ID, &buf[b]);
b += (strlen(&buf[b]) + 1);
strcpy(info_data[a]._st_URL, &buf[b]);
}
fclose(fp);
}
void init_spiffs(void)
{
esp_vfs_spiffs_conf_t conf = {
.base_path = "/spiffs",
.partition_label = NULL,
.max_files = 5,
.format_if_mount_failed = false
};
// Use settings defined above to initialize and mount SPIFFS filesystem.
// Note: esp_vfs_spiffs_register is an all-in-one convenience function.
esp_vfs_spiffs_register(&conf);
}
esp_err_t get_handler(httpd_req_t *req)
{
httpd_resp_set_type(req, "text/html");
httpd_resp_send(req, "<h1>Hello Secure World!</h1>", HTTPD_RESP_USE_STRLEN);
return ESP_OK;
}
httpd_handle_t start_webserver(void)
{
httpd_handle_t server = NULL;
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
config.lru_purge_enable = true;
config.uri_match_fn = httpd_uri_match_wildcard;
// Start the httpd server
ESP_LOGI(TAG, "Starting server on port: '%d'", config.server_port);
if (httpd_start(&server, &config) != ESP_OK) return NULL;
// Set URI handlers
httpd_uri_t hello = {
.uri = "/*", // Match all URIs of type /path/to/file
.method = HTTP_GET,
.handler = get_handler,
.user_ctx = "Hello World!"
};
ESP_LOGI(TAG, "Registering URI handlers");
httpd_register_uri_handler(server, &hello);
return server;
}
void stop_webserver(httpd_handle_t server)
{
// Stop the httpd server
httpd_stop(server);
}
void disconnect_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data)
{
httpd_handle_t* server = (httpd_handle_t*) arg;
if (*server) {
ESP_LOGI(TAG, "Stopping webserver");
stop_webserver(*server);
*server = NULL;
}
}
// FreeRTOS event group to signal when we are connected
static EventGroupHandle_t s_wifi_event_group;
// The event group allows multiple bits for each event, but we only care about two events:
// - we are connected to the AP with an IP
// - we failed to connect after the maximum amount of retries
#define WIFI_CONNECTED_BIT BIT0
#define WIFI_FAIL_BIT BIT1
#define EXAMPLE_ESP_MAXIMUM_RETRY 10
static int s_retry_num = 0;
static void event_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data)
{
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START)
{
esp_wifi_connect();
}
else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED)
{
if (s_retry_num < EXAMPLE_ESP_MAXIMUM_RETRY)
{
esp_wifi_connect();
s_retry_num++;
ESP_LOGI(TAG, "retry to connect to the AP");
}
else
{
xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
}
ESP_LOGI(TAG,"connect to the AP fail");
}
else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP)
{
ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip));
s_retry_num = 0;
xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
}
}
esp_err_t wifi_init_sta(void)
{
esp_event_handler_instance_t instance_any_id;
esp_event_handler_instance_t instance_got_ip;
s_wifi_event_group = xEventGroupCreate();
esp_netif_create_default_wifi_sta();
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
ESP_EVENT_ANY_ID,
&event_handler,
NULL,
&instance_any_id));
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
IP_EVENT_STA_GOT_IP,
&event_handler,
NULL,
&instance_got_ip));
wifi_config_t wifi_config = {
.sta = {
.ssid = CONFIG_ESP_WIFI_SSID,
.password = CONFIG_ESP_WIFI_PASSWORD,
.threshold.authmode = WIFI_AUTH_WPA2_PSK,
},
};
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config) );
ESP_ERROR_CHECK(esp_wifi_start() );
ESP_LOGI(TAG, "wifi_init_sta finished.");
// Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum
// number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above)
EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
pdFALSE,
pdFALSE,
portMAX_DELAY);
// xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually happened.
ESP_ERROR_CHECK(esp_event_handler_instance_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, instance_got_ip));
ESP_ERROR_CHECK(esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, instance_any_id));
vEventGroupDelete(s_wifi_event_group);
if (bits & WIFI_CONNECTED_BIT)
{
ESP_LOGI(TAG, "connected to ap SSID:%s password:%s",CONFIG_ESP_WIFI_SSID, CONFIG_ESP_WIFI_PASSWORD);
return ESP_OK;
}
else if (bits & WIFI_FAIL_BIT)
{
ESP_LOGI(TAG, "Failed to connect to SSID:%s, password:%s",CONFIG_ESP_WIFI_SSID, CONFIG_ESP_WIFI_PASSWORD);
}
else
{
ESP_LOGE(TAG, "UNEXPECTED EVENT");
}
return ESP_FAIL;
}
//========================================== Main ============================================================
void app_main(void)
{
esp_err_t err;
ESP_ERROR_CHECK(nvs_flash_init());
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
err = wifi_init_sta();
if(err)
{
ESP_LOGE(TAG, "WiFi Init failed: %d\n", err);
return;
}
start_webserver();
//initialize mDNS service
err = mdns_init();
if (err)
{
ESP_LOGE(TAG, "MDNS Init failed: %d\n", err);
return;
}
//set hostname
mdns_hostname_set("my-esp32");
//set default instance
mdns_instance_name_set("Jhon's ESP32 Thing");
init_spiffs();
read_station();
}
- 216行からプログラムが開始します。
- 223行:err = wifi_init_sta(); STA接続する関数です。
- 153行:esp_err_t wifi_init_sta(void) 関数本体
- 158から161行でWiFi接続する為の初期設定をします。
- 163行:ここでWiFiイベントの登録
- 168行:IPイベントの登録
- 174行:ここでWiFi関係のパラメータを設定しています。
- .ssid: WiFiのSSID
- .password: WiFIのpassword
- .threshold.authmode:接続方法
- これらのパラメータを持って、182,183,184でWiFiと接続を開始します。
- 190行:WiFi接続処理が終了するまでここでウエートなります。
- ウエートしている間に接続イベントが起こると、123行の関数が起動します。
- 126行:ルータ側がOKを出した場合、実際の接続動作128行に入ります。
- 130行:ルータがNGを出した場合、リトライしそれでもダメならFailとします。
- 144行:IPアドレスが送られて来たらそれを表示します。
- 197から199行で登録を解除
- 200行以下で接続の結果を表示
- メインプログラムに戻って、230行でHTTPサーバーの開始
- 69行:httpd_handle_t start_webserver(void) 関数の本体
- 75行:このパラメータを指定するとワイルドカードが使用出来ます。詳細は後ほど
- 79行:ここでサーバが起動します。
- 82行:URIの処理の指定を行います。URIの処理にはGETとPOSTが有りますが今回はGETのみ使用します。
- 83行:ここでワイルドカードを使用しています。”/*”の意味はサーバーへのアクセス全てと言う意味です。
- 84行:アクセス方法は、”GET”
- 85行:処理する関数の指定
- 89行で上記の設定を行う。これで、サーバーにGETでアクセスしたら全て、処理用の関数が実行される事になります。
- 再び、メインプログラムに戻って、240行からHostNameの設定を行っています。
- 241から243行で、HostNameを、”my-esp32″と設定しています。
- 残りは前回実装したSPIFFS用のコードです。
- プログラムを実行してサーバーにGETでアクセスすると、60行のesp_err_t get_handler(httpd_req_t *req)が実行されます。今回は、”Hello Secure World!”と画面に表示するだけです。
次は、Kconfig.projbuild。このファイルはmain.c で使用するWiFISSID,Passwordの情報を持っています。
Kconfig.projbuild
menu "Example Configuration"
config ESP_WIFI_SSID
string "WiFi SSID"
default "myssid"
help
SSID (network name) for the example to connect to.
config ESP_WIFI_PASSWORD
string "WiFi Password"
default "mypassword"
help
WiFi password (WPA or WPA2) for the example to use.
config ESP_MAXIMUM_RETRY
int "Maximum retry"
default 5
help
Set the Maximum retry to avoid station reconnecting to the AP unlimited when the AP is really inexistent.
endmenu
menuconfigで設定出来ますが、ここでSSIDとPasswordを設定して置けばmenuconfigを立ち上げる必要がなくなるのでここで設定しています。今回のプロジェクトをここに保存します。
コンパイルと実行
ESP32を使用するので、モニタから、”idf.py -p [usb port] flash monitor” でコンパイル実行されます。モニター表示の最後に
I (2114) example: Starting server on port: ’80’
I (2124) example: Registering URI handlers
と表示されればサーバーは立ち上がっています。今回はHostName”my-esp32”を設定しているので、ブラウザに”my-esp32.local”と入力すればサーバーにアクセス出来ます。
次回は
今回はHTTPサーバーを立ち上げる事が出来ました。次回はデータ編集用のサーバー側のプログラムを書いて行きます。