Project

General

Profile

Using an ESP8266 WiFi Module

Introduction

Produced by Espressif Systems, the ESP8266 is a WiFi SoC (System on Chip) very commonly found on simple modules sold for few US dollars.
These modules generally communicate over a serial port, and are controlled using a special AT instruction set - a collection of text-based commands separated by newlines.

The Dooba SDK includes a library providing a simple API for making use of such modules:
  • List available Access Points
  • Join Access Point (Station Mode)
  • Create Access Point (AP Mode)
  • Create TCP Server/Client
  • Open UDP Channel
  • Send/Receive Data

The modules allow up to four simultaneous IP channels (TCP or UDP).

Note: the examples below use the eloop library to automatically provide a main loop.

Baudrate Warning

Note: you don't need to worry about this if you're using a Fiddle.

As with any serial line, the esp8266 module needs to be talked to at the right speed, which may vary from one distributor to another.
Baudrates commonly found are 9600 and 115200 baud, with a few rare modules with other baudrates.

After some testing, it appears that the best speed for reliable communication with an ioNode is 9600 baud.
Until now it no attempt to communicate at 115200 baud has proven successful.
The baudrate can be changed, but for that, reliable communication at the "default" baudrate of the module is required.

For these reasons, the wifi module included in every Fiddle is configured for 9600 baud before assembly.

Before soldering your esp8266 module to anything, be sure that it is configured to talk 9600 baud (or more generally, a speed that is known to be supported by the host system).

The most common way of doing that is with the following command (sent to the esp8266 over its serial line):

AT+UART_DEF=9600,8,1,0,0

Using the library

The esp8266 library is configured by default for use with the Fiddle.
If you are using a board with a different wiring, you will need to override its configuration to reflect your specific hardware setup.

To use the library, simply add esp8266 as a dependency to your definition file (dfe.conf):

# Firmware Element Configuration
# ...
deps:
  - esp8266

Initialization

Before we can do anything, we need to initialize the library (and the module). The library will require a buffer to store packets, which needs to be provided at initialization.
The library also requires its "update" method to be called regularly to process packets (the I/O is interrupt driven and buffered).

Finally, esp8266 relies on the AVR's interrupts, which means that at the end of the init method (but before we actually use the module) we should call sei (from avr/interrupt.h) to enable interrupts.

#include <avr/interrupt.h>
#include <esp8266/esp8266.h>

// WiFi will need quite some space to store packets
#define wifi_buf_size            2048
uint8_t wifi_buf[wifi_buf_size];

void init()
{
    // Initialize WiFi
    esp8266_init(wifi_buf, wifi_buf_size);

    // Enable Interrupts
    sei();
}

void update()
{
    // Update WiFi
    esp8266_update();
}

Switching modes

Once interrupts have been enabled (with sei as shown above), we can start using the module.
The first thing we want to do is configure the module. We should set the mode to either access point or station, and also enable multiple connections.

This is done using the esp8266_set_mode and esp8266_set_mux methods:

uint8_t esp8266_set_mode(uint8_t mode))
Arguments:

  • mode -> Can be ESP8266_MODE_AP or ESP8266_MODE_STA.

Return Value:
0 on success, anything else on error.

uint8_t esp8266_set_mux(uint8_t mux))
Arguments:

  • mux -> 1 = enable

Return Value:
0 on success, anything else on error.

#include <avr/interrupt.h>
#include <esp8266/esp8266.h>

// WiFi will need quite some space to store packets
#define wifi_buf_size            2048
uint8_t wifi_buf[wifi_buf_size];

void init()
{
    // Initialize WiFi
    esp8266_init(wifi_buf, wifi_buf_size);

    // Enable Interrupts
    sei();

    // Configure WiFi
    esp8266_set_mux(1);
    esp8266_set_mode(ESP8266_MODE_STA);
}

void update()
{
    // Update WiFi
    esp8266_update();
}

List available Access Points (STA Mode)

To list access points, we need to call esp8266_list_ap. This function takes another function (callback) as argument. The callback function will be run and passed access point information as arguments for each access point found.

uint8_t esp8266_list_ap(void (*ap_handler)(uint8_t wchan, uint8_t sec_type, uint8_t q, uint8_t *name, uint8_t name_l))
Arguments:

  • ap_handler -> Callback function - will be called for each access point detected

Return Value:
0 on success, anything else on error.

ap_handler arguments:

  • wchan -> WiFi channel number
  • sec_type -> Security type (ESP8266_SEC_OPEN / ESP8266_SEC_WEP / ESP8266_SEC_WPA_PSK / ESP8266_SEC_WPA2_PSK / ESP8266_SEC_WPA_WPA2_PSK)
  • q -> Signal quality
  • name -> Access point name
  • name_l -> Access point name length

#include <avr/interrupt.h>
#include <esp8266/esp8266.h>

// WiFi will need quite some space to store packets
#define wifi_buf_size            2048
uint8_t wifi_buf[wifi_buf_size];

void example_ap_handler(uint8_t wchan, uint8_t sec_type, uint8_t q, uint8_t *name, uint8_t name_l)
{
    // ToDo: Do something with the AP information...
}

void init()
{
    // Initialize WiFi
    esp8266_init(wifi_buf, wifi_buf_size);

    // Enable Interrupts
    sei();

    // Configure WiFi
    esp8266_set_mux(1);
    esp8266_set_mode(ESP8266_MODE_STA);

    // List APs
    esp8266_list_ap(example_ap_handler);
}

void update()
{
    // Update WiFi
    esp8266_update();
}

Join an Access Point (STA Mode)

To join an access point we can call the esp8266_join_ap method:

uint8_t esp8266_join_ap(void *name, uint8_t name_l, void *password, uint8_t password_l))
Arguments:

  • name -> Access point name
  • name_l -> Access point name length
  • password -> WiFi password
  • password_l -> WiFi password length

Return Value:
0 on success, anything else on error.

The example below shows how to join a WiFi network named "YourNetwork" protected with the password "helloworld1234":

#include <string.h>
#include <avr/interrupt.h>
#include <esp8266/esp8266.h>

// WiFi will need quite some space to store packets
#define wifi_buf_size            2048
uint8_t wifi_buf[wifi_buf_size];

void init()
{
    // Initialize WiFi
    esp8266_init(wifi_buf, wifi_buf_size);

    // Enable Interrupts
    sei();

    // Configure WiFi
    esp8266_set_mux(1);
    esp8266_set_mode(ESP8266_MODE_STA);

    // Join an Access Point
    if(esp8266_join_ap("YourNetwork", strlen("YourNetwork"), "helloworld1234", strlen("helloworld1234")))
    {
        // Failed - Something went wrong
    }
    else
    {
        // Connection successful!
    }
}

void update()
{
    // Update WiFi
    esp8266_update();
}

Getting local IP & MAC (STA Mode)

Once we've successfully joined an access point, we can obtain our local IP address (as well as our MAC address if needed) using the esp8266_get_local_info method:

uint8_t esp8266_get_local_info(uint8_t *mac, uint8_t *ip))
Arguments:

  • ip -> Buffer to receive the MAC address - must be at least NET_ETH_ADDR_LEN (from the net/eth.h file in the net library) which is 6 bytes (the address will not be in text-form, just the actual bytes making up the address)
  • ip -> Buffer to receive the IP address - must be at least NET_IP_ADDR_LEN_TXT (from the net/ip.h file in the net library) which is 16 bytes (the address will be a zero-terminated string in the form aaa.bbb.ccc.ddd)

Return Value:
0 on success, anything else on error.

The following example shows how to use these (note how we've added two #include directives for the constants mentioned earlier - this requires us to add the net library as a dependency in our dfe.conf definition file):

#include <string.h>
#include <net/eth.h>
#include <net/ip.h>
#include <avr/interrupt.h>
#include <esp8266/esp8266.h>

// WiFi will need quite some space to store packets
#define wifi_buf_size            2048
uint8_t wifi_buf[wifi_buf_size];

void init()
{
    uint8_t local_mac[NET_ETH_ADDR_LEN];
    uint8_t local_ip[NET_IP_ADDR_LEN_TXT];

    // Initialize WiFi
    esp8266_init(wifi_buf, wifi_buf_size);

    // Enable Interrupts
    sei();

    // Configure WiFi
    esp8266_set_mux(1);
    esp8266_set_mode(ESP8266_MODE_STA);

    // Join an Access Point
    if(esp8266_join_ap("YourNetwork", strlen("YourNetwork"), "helloworld1234", strlen("helloworld1234")))
    {
        // Failed - Something went wrong
    }
    else
    {
        // Connection successful - Let's fetch our local information!
        esp8266_get_local_info(local_mac, local_ip);
    }
}

void update()
{
    // Update WiFi
    esp8266_update();
}

Creating an Access Point (AP Mode)

When in AP mode, we can create an access point using the esp8266_create_ap method:

uint8_t esp8266_create_ap(void *name, uint8_t name_l, void *password, uint8_t password_l, uint8_t sec_type, uint8_t wchan))
Arguments:

  • name -> Access point name
  • name_l -> Access point name length
  • password -> WiFi password
  • password_l -> WiFi password length
  • sec_type -> Security type (ESP8266_SEC_OPEN / ESP8266_SEC_WEP / ESP8266_SEC_WPA_PSK / ESP8266_SEC_WPA2_PSK / ESP8266_SEC_WPA_WPA2_PSK)
  • wchan -> WiFi channel number

Return Value:
0 on success, anything else on error.

The example below shows how to create a WiFi network named "ExampleWiFi" protected with the WPA2 password "helloworld1234" on channel 4:

#include <string.h>
#include <avr/interrupt.h>
#include <esp8266/esp8266.h>

// WiFi will need quite some space to store packets
#define wifi_buf_size            2048
uint8_t wifi_buf[wifi_buf_size];

void init()
{
    // Initialize WiFi
    esp8266_init(wifi_buf, wifi_buf_size);

    // Enable Interrupts
    sei();

    // Configure WiFi
    esp8266_set_mux(1);
    esp8266_set_mode(ESP8266_MODE_AP);

    // Create an Access Point
    if(esp8266_create_ap("ExampleWiFi", strlen("ExampleWiFi"), "helloworld1234", strlen("helloworld1234"), ESP8266_SEC_WPA2_PSK, 4))
    {
        // Failed - Something went wrong
    }
    else
    {
        // Access point created!
    }
}

void update()
{
    // Update WiFi
    esp8266_update();
}

Access Point control (AP Mode)

Once an access point has been created, there might be a few things that we want to do with it.

For starters, we can set its IP address (which will in turn determine the IP addresses assigned to client stations). For this, let's use the esp8266_set_ap_addr method:

uint8_t esp8266_set_ap_addr(void *ip, uint8_t ip_l))
Arguments:

  • ip -> IP address in text form (aaa.bbb.ccc.ddd)
  • ip_l -> IP address length

Return Value:
0 on success, anything else on error.

Or, we can just leave the default, in which case the following method might be useful - it reads the IP address of the current access point:

uint8_t esp8266_get_ap_addr(uint8_t *ip))
Arguments:

  • ip -> Buffer to receive the IP address - must be at least NET_IP_ADDR_LEN_TXT (from the net/ip.h file in the net library) which is 16 bytes (the address will be zero-terminated)

Return Value:
0 on success, anything else on error.

Then, we can obtain the list of client stations currently connected to our access point with the esp8266_list_ap_clients method:

uint8_t esp8266_list_ap_clients(void (*ap_client_handler)(uint8_t *ip, uint8_t ip_l))
Arguments:

  • ap_client_handler -> Callback function - will be called for each client station

Return Value:
0 on success, anything else on error.

ap_client_handler arguments:

  • ip -> IP address in text form (aaa.bbb.ccc.ddd)
  • ip_l -> IP address length

The following example shows how to set and AP address, then retrieve it, and finally list connected stations:

#include <string.h>
#include <avr/interrupt.h>
#include <esp8266/esp8266.h>

// WiFi will need quite some space to store packets
#define wifi_buf_size            2048
uint8_t wifi_buf[wifi_buf_size];

void example_ap_client_handler(uint8_t *ip, uint8_t ip_l)
{
    // ToDo: Do something with the client station information...
}

void init()
{
    uint8_t ap_addr[NET_IP_ADDR_LEN_TXT];

    // Initialize WiFi
    esp8266_init(wifi_buf, wifi_buf_size);

    // Enable Interrupts
    sei();

    // Configure WiFi
    esp8266_set_mux(1);
    esp8266_set_mode(ESP8266_MODE_AP);

    // Create an Access Point
    if(esp8266_create_ap("ExampleWiFi", strlen("ExampleWiFi"), "helloworld1234", strlen("helloworld1234"), ESP8266_SEC_WPA2_PSK, 4))
    {
        // Failed - Something went wrong
    }
    else
    {
        // Access point created!

        // Set IP
        esp8266_set_ap_addr("10.0.232.1", strlen("10.0.232.1"));

        // Retrieve IP
        esp8266_get_ap_addr(ap_addr);

        // List client stations
        esp8266_list_ap_clients(example_ap_client_handler);
    }
}

void update()
{
    // Update WiFi
    esp8266_update();
}

Create TCP client

To connect to a remote system over TCP we can use the esp8266_open_channel method:

uint8_t esp8266_open_channel(uint8_t channel, uint8_t type, void *addr, uint16_t port, uint16_t local_port, uint8_t udp_peer_mode, void (*data_handler)(uint8_t chan, uint8_t *x, uint16_t s))
Arguments:

  • channel -> Channel number (0-3, up to 4 simultaneous sockets allowed)
  • type -> Either ESP8266_CHAN_TCP or ESP8266_CHAN_UDP
  • addr -> IP address or hostname of target (zero-terminated)
  • port -> TCP / UDP port number of target
  • local_port -> Local port (from which to send packets) - UDP ONLY
  • udp_peer_mode -> Determines how the peer IP address is allowed to change (ESP8266_UDP_PEERNOCHANGE / ESP8266_UDP_PEERCHANGEONCE / ESP8266_UDP_PEERCHANGEMULTI) - UDP ONLY
  • data_handler -> Callback function - will be executed when data is received from this channel

Return Value:
0 on success, anything else on error.

data_handler arguments:

  • chan -> Channel number associated with this data packet
  • x -> Pointer to data
  • s -> Size of data in bytes

The esp8266_open_channel_n method does the same, but instead of requiring a zero-terminated target address (addr), here we can pass a buffer and a length (addr_l):

uint8_t esp8266_open_channel_n(uint8_t channel, uint8_t type, void *addr, uint8_t addr_l, uint16_t port, uint16_t local_port, uint8_t udp_peer_mode, void (*data_handler)(uint8_t chan, uint8_t *x, uint16_t s))

The example below shows how to connect to a fictitious SMTP (email) server "smtp.example.com" (port 25 for SMTP):

#include <string.h>
#include <avr/interrupt.h>
#include <esp8266/esp8266.h>

// WiFi will need quite some space to store packets
#define wifi_buf_size            2048
uint8_t wifi_buf[wifi_buf_size];

void example_data_handler(uint8_t chan, uint8_t *x, uint16_t s)
{
    // The server is talking to us! Do something with it...
}

void init()
{
    // Initialize WiFi
    esp8266_init(wifi_buf, wifi_buf_size);

    // Enable Interrupts
    sei();

    // Configure WiFi
    esp8266_set_mux(1);
    esp8266_set_mode(ESP8266_MODE_STA);

    // Join an Access Point
    if(esp8266_join_ap("YourNetwork", strlen("YourNetwork"), "helloworld1234", strlen("helloworld1234")))
    {
        // Failed - Something went wrong
    }
    else
    {
        // Connection successful - Let's connect to a mail server
        if(esp8266_open_channel(0, ESP8266_CHAN_TCP, "smtp.example.com", 25, 0, 0, example_data_handler))
        {
            // Failed - Something went wrong
        }
        else
        {
            // Connected to our mail server!
        }
    }
}

void update()
{
    // Update WiFi
    esp8266_update();
}

Create TCP server

The modules allow having TCP servers, but only one at any given moment.
Also, closing the server will automatically trigger a reset of the module, meaning that any configuration (mode, mux, joining/creating an AP) will need to be re-applied.

If we need to create a TCP server, we can achieve this with the esp8266_create_server method:

uint8_t esp8266_create_server(uint16_t port, void (*cnct_handler)(uint8_t channel), void (*dcnt_handler)(uint8_t channel), void (*data_handler)(uint8_t chan, uint8_t *x, uint16_t s))
Arguments:

  • port -> TCP port number
  • cnct_handler -> Callback function - will be executed when a new client connects to our server
  • dcnt_handler -> Callback function - will be executed when a previously connected client disconnects from our server
  • data_handler -> Callback function - will be executed when data is received from a client (the channel associated with this client will be passed as argument)

Return Value:
0 on success, anything else on error.

cnct_handler arguments:

  • chan -> Channel number associated with this new client

dcnt_handler arguments:

  • chan -> Channel number associated with the disconnected client

data_handler arguments:

  • chan -> Channel number associated with this data packet
  • x -> Pointer to data
  • s -> Size of data in bytes

Once we're done with our server, we can close it using esp8266_close_server (this will automatically trigger a reset, meaning any previous configuration such as mode, mux, joining/creating an AP will need to be re-applied):

uint8_t esp8266_close_server()

Return Value:
0 on success, anything else on error.

Create UDP channel

If all we want to do is just simple UDP communication, we can again use the esp8266_open_channel and esp8266_open_channel_n methods.

To communicate with one specific host, pass its IP address or hostname as the addr argument.
In order to accept communication from anyone (sometimes called an "UDP server"), simply pass "0.0.0.0" as the addr argument.

The example below show how to open an UDP channel for communication with anyone on port 10232:

#include <string.h>
#include <avr/interrupt.h>
#include <esp8266/esp8266.h>

// WiFi will need quite some space to store packets
#define wifi_buf_size            2048
uint8_t wifi_buf[wifi_buf_size];

void example_data_handler(uint8_t chan, uint8_t *x, uint16_t s)
{
    // Someone is talking to us! Do something with the data...
}

void init()
{
    // Initialize WiFi
    esp8266_init(wifi_buf, wifi_buf_size);

    // Enable Interrupts
    sei();

    // Configure WiFi
    esp8266_set_mux(1);
    esp8266_set_mode(ESP8266_MODE_STA);

    // Join an Access Point
    if(esp8266_join_ap("YourNetwork", strlen("YourNetwork"), "helloworld1234", strlen("helloworld1234")))
    {
        // Failed - Something went wrong
    }
    else
    {
        // Connection successful - Let's open an UDP Channel
        if(esp8266_open_channel(0, ESP8266_CHAN_UDP, "0.0.0.0", 10232, 10232, ESP8266_UDP_PEERCHANGEMULTI, example_data_handler))
        {
            // Failed - Something went wrong
        }
        else
        {
            // Ready to communicate over UDP!
        }
    }
}

void update()
{
    // Update WiFi
    esp8266_update();
}

Send and receive data

As soon as we successfully create a channel (TCP or UDP), we can start sending and receiving data.

When data is received, our data_handler (passed during the creation of the channel) will be called directly with the data so we can process it.

To send some data over a channel, we can use the esp8266_send_channel method:

uint8_t esp8266_send_channel(uint8_t channel, void *x, uint16_t s)
Arguments:

  • channel -> Channel over which to send data
  • x -> Data to be sent
  • s -> Size of data to be sent in bytes

Return Value:
0 on success, anything else on error.

The example belows shows how to send a hello message over a UDP channel:

#include <string.h>
#include <avr/interrupt.h>
#include <esp8266/esp8266.h>

// WiFi will need quite some space to store packets
#define wifi_buf_size            2048
uint8_t wifi_buf[wifi_buf_size];

void example_data_handler(uint8_t chan, uint8_t *x, uint16_t s)
{
    // Someone is talking to us! Do something with the data...
}

void init()
{
    // Initialize WiFi
    esp8266_init(wifi_buf, wifi_buf_size);

    // Enable Interrupts
    sei();

    // Configure WiFi
    esp8266_set_mux(1);
    esp8266_set_mode(ESP8266_MODE_STA);

    // Join an Access Point
    if(esp8266_join_ap("YourNetwork", strlen("YourNetwork"), "helloworld1234", strlen("helloworld1234")))
    {
        // Failed - Something went wrong
    }
    else
    {
        // Connection successful - Let's open an UDP Channel with 10.0.232.101
        if(esp8266_open_channel(0, ESP8266_CHAN_UDP, "10.0.232.101", 10232, 10232, ESP8266_UDP_PEERNOCHANGE, example_data_handler))
        {
            // Failed - Something went wrong
        }
        else
        {
            // Ready to communicate over UDP! Let's send a hello message...
            esp8266_send_channel(0, "Hello World!", strlen("Hello World!"));
        }
    }
}

void update()
{
    // Update WiFi
    esp8266_update();
}

Close channel

When we're done with a channel (TCP or UDP), we can use esp8266_close_channel:

uint8_t esp8266_close_channel(uint8_t channel)
Arguments:

  • channel -> Channel we want to close

Return Value:
0 on success, anything else on error.