Programming networking services with ESP requires a connection on the networking layer between parties, mostly TCP.
ESP SoC can act as Access Point (AP): a device you connect to, like you connect a notebook to the Internet router, and as a client: ESP then behaves like any wifi enabled device, i.e. tablet or mobile phone, connecting to the Internet infrastructure. Interestingly, ESP 8366 SoC can act simultaneously in both modes at once, even, if it has only one WiFi interface!
Below there is sample code, how to implement both modes, using ESP libraries that came during installation of the development environment for Arduino IDE.
The third example shows how to send and receive a UDP packet while in client mode. It is the full solution to connect ESP to the NTP (Network Time Protocol) server to obtain current date and time from the Internet.
Last examples show, how to make a handy WiFi scanner showing available networks nearby.
This sketch based on standard example demonstrates how to program ESP8266 in AP mode:
#include <ESP8266WiFi.h> #include <WiFiClient.h> #include <ESP8266WebServer.h> /* Set these variables to your desired credentials. */ const char *ssid = "APmode"; const char *password = "password"; ESP8266WebServer server(80); void hRoot() { server.send(200, "text/html", "<h1>You are connected</h1>"); } /* Initialization */ void setup() { delay(1500); /* You can remove the password parameter if you want the AP to be open. */ WiFi.softAP(ssid, password); IPAddress myIP = WiFi.softAPIP(); server.on("/", hRoot); server.begin(); } void loop() { server.handleClient(); }
This sketch (standard example) demonstrates how to program ESP8266 in client mode:
#include <ESP8266WiFi.h> #include <ESP8266WiFiMulti.h> ESP8266WiFiMulti WiFiMulti; void setup() { delay(1000); // We start by connecting to a WiFi network WiFi.mode(WIFI_STA); WiFiMulti.addAP("SSID", "password"); while(WiFiMulti.run() != WL_CONNECTED) { delay(500); } delay(500); } void loop() { const uint16_t port = 80; const char * host = "192.168.1.1"; // ip or dns // Use WiFiClient class to create TCP connections WiFiClient client; if (!client.connect(host, port)) { delay(5000); return; } // This will send the request to the server client.println("Send this data to server"); //read back one line from server String line = client.readStringUntil('\r'); Serial.println(line); Serial.println("closing connection"); client.stop(); Serial.println("wait 5 sec..."); delay(5000); }
This sketch (based on standard example) demonstrates how to program ESP8266 as NTP client using UDP packets (send and receive):
#include <ESP8266WiFi.h> #include <WiFiUdp.h> char ssid[] = "**************"; // your network SSID (name) char pass[] = "**************"; // your network password unsigned int localPort = 2390; // local port to listen for UDP packets // NTP servers IPAddress ntpServerIP; // 0.pl.pool.ntp.org NTP server address const char* ntpServerName[] = {"0.pl.pool.ntp.org","1.pl.pool.ntp.org","2.pl.pool.ntp.org","3.pl.pool.ntp.org"}; const int timeZone = 1; //Central European Time int servernbr=0; // NTP time stamp is in the first 48 bytes of the message const int NTP_PACKET_SIZE = 48; //buffer to hold incoming and outgoing packets byte packetBuffer[ NTP_PACKET_SIZE]; // A UDP instance to let us send and receive packets over UDP WiFiUDP udp; void setup() { Serial.begin(115200); Serial.println(); Serial.print("Connecting to "); Serial.println(ssid); // WiFi.persistent(false); WiFi.mode(WIFI_OFF); delay(2000); // We start by connecting to a WiFi network WiFi.mode(WIFI_STA); delay(3000); WiFi.begin(ssid, pass); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(""); Serial.println("WiFi connected"); Serial.println("DHCP assigned IP address: "); Serial.println(WiFi.localIP()); Serial.println("Starting UDP"); udp.begin(localPort); Serial.print("Local port: "); Serial.println(udp.localPort()); // first ntp server servernbr = 0; } void loop() { //get a random server from the pool WiFi.hostByName(ntpServerName[servernbr], ntpServerIP); Serial.print(ntpServerName[servernbr]); Serial.print(":"); Serial.println(ntpServerIP); sendNTPpacket(ntpServerIP); // send an NTP packet to a time server // wait to see if a reply is available delay(1000); int cb = udp.parsePacket(); if (!cb) { Serial.println("no packet yet"); if ( servernbr = 5 ) { servernbr =0; } else { servernbr++; } } else { Serial.print("packet received, length="); Serial.println(cb); // We've received a packet, read the data from it udp.read(packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer //the timestamp starts at byte 40 of the received packet and is four bytes, // or two words, long. First, esxtract the two words: unsigned long highWord = word(packetBuffer[40], packetBuffer[41]); unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]); // combine the four bytes (two words) into a long integer // this is NTP time (seconds since Jan 1 1900): unsigned long secsSince1900 = highWord << 16 | lowWord; Serial.print("Seconds since Jan 1 1900 = " ); Serial.println(secsSince1900); // now convert NTP time into everyday time: Serial.print("Unix time = "); // Unix time starts on Jan 1 1970. In seconds, that's 2208988800: const unsigned long seventyYears = 2208988800UL; // subtract seventy years: unsigned long epoch = secsSince1900 - seventyYears; // print Unix time: Serial.println(epoch); // print the hour, minute and second: // UTC is the time at Greenwich Meridian (GMT) Serial.print("The UTC time is "); // print the hour (86400 equals secs per day) Serial.print((epoch % 86400L) / 3600); Serial.print(':'); if ( ((epoch % 3600) / 60) < 10 ) { // In the first 10 minutes of each hour, we'll want a leading '0' Serial.print('0'); } // print the minute (3600 equals secs per minute) Serial.print((epoch % 3600) / 60); Serial.print(':'); if ( (epoch % 60) < 10 ) { // In the first 10 seconds of each minute, we'll want a leading '0' Serial.print('0'); } Serial.println(epoch % 60); // print the second } // wait ten seconds before asking for the time again delay(10000); } // send an NTP request to the time server at the given address void sendNTPpacket(IPAddress& address) { Serial.print("sending NTP packet to: "); Serial.println( address ); // set all bytes in the buffer to 0 memset(packetBuffer, 0, NTP_PACKET_SIZE); // Initialize values needed to form NTP request // (see URL above for details on the packets) packetBuffer[0] = 0b11100011; // LI, Version, Mode packetBuffer[1] = 0; // Stratum, or type of clock packetBuffer[2] = 6; // Polling Interval packetBuffer[3] = 0xEC; // Peer Clock Precision // 8 bytes of zero for Root Delay & Root Dispersion packetBuffer[12] = 49; packetBuffer[13] = 0x4E; packetBuffer[14] = 49; packetBuffer[15] = 52; // all NTP fields have been given values, now // you can send a packet requesting a timestamp: udp.beginPacket(address, 123); //NTP requests are to port 123 udp.write(packetBuffer, NTP_PACKET_SIZE); udp.endPacket(); }
This sketch demonstrates how to scan WiFi networks. ESP8266 is programmed in access point mode. All found WiFi networks will be printed in TTY serial window.
#include "ESP8266WiFi.h" void setup() { Serial.begin(115200); // Set WiFi to station mode and disconnect // from an AP if it was previously connected WiFi.mode(WIFI_STA); WiFi.disconnect(); delay(100); Serial.println("Setup done"); } void loop() { Serial.println("scan start"); // WiFi.scanNetworks will return the number of networks found int n = WiFi.scanNetworks(); Serial.println("scan done"); if (n == 0) Serial.println("no networks found"); else { Serial.print(n); Serial.println(" networks found"); for (int i = 0; i < n; ++i) { // Print SSID and RSSI for each network found Serial.print(i + 1); Serial.print(": "); Serial.print(WiFi.SSID(i)); Serial.print(" ("); Serial.print(WiFi.RSSI(i)); Serial.print(")"); Serial.println((WiFi.encryptionType(i) == ENC_TYPE_NONE)?" ":"*"); delay(10); } } Serial.println(""); // Wait a bit before scanning again delay(5000); }
There are many different development software and tools which can be used for ESP32 programming [1]:
Of course, for programming ESP32 We can use all the previously described Arduino examples for sensors and actuators. But in our example, we will focus on programming in ESP-IDF, as this is the native Development Platform for ESP32. A detailed description of the installation of the development environment can be found here.
This example shows how to use the All Channel Scan or Fast Scan to connect to a Wi-Fi network. In the Fast Scan mode, the scan will stop as soon as the first network matching the SSID is found. In this mode, an application can set the threshold for the authentication mode and the Signal strength. Networks that do not meet the threshold requirements will be ignored. In the All Channel Scan mode, the scan will end after all the channels are scanned, and the connection will start with the best network. The networks can be sorted based on Authentication Mode or Signal Strength. The priority for the Authentication mode is: WPA2 > WPA > WEP > Open.
#include "freertos/FreeRTOS.h" #include "freertos/event_groups.h" #include "esp_wifi.h" #include "esp_log.h" #include "esp_event_loop.h" #include "nvs_flash.h" /*Set the SSID and Password via "make menuconfig"*/ #define DEFAULT_SSID CONFIG_WIFI_SSID #define DEFAULT_PWD CONFIG_WIFI_PASSWORD #if CONFIG_WIFI_ALL_CHANNEL_SCAN #define DEFAULT_SCAN_METHOD WIFI_ALL_CHANNEL_SCAN #elif CONFIG_WIFI_FAST_SCAN #define DEFAULT_SCAN_METHOD WIFI_FAST_SCAN #else #define DEFAULT_SCAN_METHOD WIFI_FAST_SCAN #endif /*CONFIG_SCAN_METHOD*/ #if CONFIG_WIFI_CONNECT_AP_BY_SIGNAL #define DEFAULT_SORT_METHOD WIFI_CONNECT_AP_BY_SIGNAL #elif CONFIG_WIFI_CONNECT_AP_BY_SECURITY #define DEFAULT_SORT_METHOD WIFI_CONNECT_AP_BY_SECURITY #else #define DEFAULT_SORT_METHOD WIFI_CONNECT_AP_BY_SIGNAL #endif /*CONFIG_SORT_METHOD*/ #if CONFIG_FAST_SCAN_THRESHOLD #define DEFAULT_RSSI CONFIG_FAST_SCAN_MINIMUM_SIGNAL #if CONFIG_EXAMPLE_OPEN #define DEFAULT_AUTHMODE WIFI_AUTH_OPEN #elif CONFIG_EXAMPLE_WEP #define DEFAULT_AUTHMODE WIFI_AUTH_WEP #elif CONFIG_EXAMPLE_WPA #define DEFAULT_AUTHMODE WIFI_AUTH_WPA_PSK #elif CONFIG_EXAMPLE_WPA2 #define DEFAULT_AUTHMODE WIFI_AUTH_WPA2_PSK #else #define DEFAULT_AUTHMODE WIFI_AUTH_OPEN #endif #else #define DEFAULT_RSSI -127 #define DEFAULT_AUTHMODE WIFI_AUTH_OPEN #endif /*CONFIG_FAST_SCAN_THRESHOLD*/ static const char *TAG = "scan"; static esp_err_t event_handler(void *ctx, system_event_t *event) { switch (event->event_id) { case SYSTEM_EVENT_STA_START: ESP_LOGI(TAG, "SYSTEM_EVENT_STA_START"); ESP_ERROR_CHECK(esp_wifi_connect()); break; case SYSTEM_EVENT_STA_GOT_IP: ESP_LOGI(TAG, "SYSTEM_EVENT_STA_GOT_IP"); ESP_LOGI(TAG, "Got IP: %s\n", ip4addr_ntoa(&event->event_info.got_ip.ip_info.ip)); break; case SYSTEM_EVENT_STA_DISCONNECTED: ESP_LOGI(TAG, "SYSTEM_EVENT_STA_DISCONNECTED"); ESP_ERROR_CHECK(esp_wifi_connect()); break; default: break; } return ESP_OK; } /* Initialize Wi-Fi as sta and set scan method */ static void wifi_scan(void) { 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)); ESP_LOGI(TAG, DEFAULT_SSID); ESP_LOGI(TAG, DEFAULT_PWD); wifi_config_t wifi_config = { .sta = { .ssid = DEFAULT_SSID, .password = DEFAULT_PWD, .scan_method = DEFAULT_SCAN_METHOD, .sort_method = DEFAULT_SORT_METHOD, .threshold.rssi = DEFAULT_RSSI, .threshold.authmode = DEFAULT_AUTHMODE, }, }; ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config)); ESP_ERROR_CHECK(esp_wifi_start()); } void app_main() { // Initialize NVS esp_err_t ret = nvs_flash_init(); if (ret == ESP_ERR_NVS_NO_FREE_PAGES) { ESP_ERROR_CHECK(nvs_flash_erase()); ret = nvs_flash_init(); } ESP_ERROR_CHECK( ret ); wifi_scan(); }
To properly set up Station mode, it is necessary to enter SSID and password. To enter these values, before compiling the program, run the command:
make menuconfig
and then
make all
or
make flash