First Steps with the ESP8266-03 Development Board
A fun afternoon project with two platforms...
As mentioned in my previous blog post I was very optimistic when designing my ESP8266-03 development board and didn't even prototype it before launching a batch in production. As I just received a few of these boards in the mail today and given that information about this module is scattered all over the internet, I'll detail here my first steps with it...
Configuring the FT230X
The onboard USB to UART adapter has 4 CBUS pins that can be configured as GPIOs or status pins. Three of them (CBUS0/1/3) are connected to green LEDs so if you want to change the default settings, you can download the FT_Prog utility here, program your new settings, disconnect/reconnect the dev board.
Playing with the default firmware
This part is inspired by this awesome web page.
Connect the development board to your computer using a micro-usb cable, launch your favorite terminal program (in my case Realterm), set a 9600 baudrate, head to the "Send" tab, tick the +CR & +LF boxes, enter these different commands and click "Send ASCII".
Testing the communication
Getting the firmware version:
Setting Wifi mode as AP+STA and listing nearby Access Points (APs):
As you can see you'll find the APs names together with their MAC addresses and RSSIs. The first digit is the encryption type (0:open, 1:wep, 2:wpa_psk, 3:wpa2_psk, 4:wpa_wpa2_psk).
Connecting to your access point:
Checking that you're actually connected:
Download and run this TCP server/terminal made by tomeko: Bcbserver, send data to your main computer:
>AT+CIPSTART="TCP","your computer IP",7778
Here the TCP connection is established, you need to specify how many bytes you want to send (here 4)
> (type your 4 bytes long text here)
You should see your text displayed in BCBcostam.
Finally, stop your TCP connection:
Building the Espressif toolchain
Until now we've used the ESP8266 as a peripheral, meaning that we need a computer/phone/microcontroller to operate it. However, since the ESP8266 manufacturer released the chip SDK we can reprogram it with our own code so it can work as a standalone device. To do so, we first need to build the Espressif toolchain.
This will unfortunately take quite a while... so only do it when you have some time to spend! I highly recommend following the great guide that can be found here. It is a tutorial for linux but you'll also find a Windows tutorial here.
Please note that in the Linux tutorial, at the time of writing I had to run "mv esp_iot_sdk_v0.9.3/* ESP8266_SDK" and not "mv esp_iot_sdk_v0.9.3 ESP8266_SDK".
Making our own program
Let's learn by example. In 4-5 hours I managed to make this fun setup:
The Doppler station
First, we need to setup one ESP8266 pin as input with its internal pull-up enabled so we can just connect our button between this IO and our platform ground:
PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U, FUNC_GPIO13); // Set GPIO13 function
gpio_output_set(0, 0, 0, BIT13); // Set GPIO13 as input
PIN_PULLDWN_DIS(PERIPHS_IO_MUX_MTCK_U); // Disable pulldown
PIN_PULLUP_EN(PERIPHS_IO_MUX_MTCK_U); // Enable pullup
Given that we don't have access to the ESP8266 english datasheet and want to use all its already included features, we are forced to use the SDK. Have a look for "ESP8266EX SDK Programming Guide" on your favorite search website.
As a given pin can be driven by several controllers inside the ESP8266, the first line specifies that we want to use the GPIO controller on GPIO13. The three others.... well read the comments . This is a simple and non energy-saving way to use a button as you'll need to poll its state rather than having an interrupt based readout:
ETS_GPIO_INTR_DISABLE(); // Disable gpio interrupts
ETS_GPIO_INTR_ATTACH(doppler_int_handler, 12); // GPIO12 interrupt handler
PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTDI_U, FUNC_GPIO12); // Set GPIO12 function
gpio_output_set(0, 0, 0, GPIO_ID_PIN(12)); // Set GPIO12 as input
GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, BIT(12)); // Clear GPIO12 status
gpio_pin_intr_state_set(GPIO_ID_PIN(12), 3); // Interrupt on any GPIO12 edge
ETS_GPIO_INTR_ENABLE(); // Enable gpio interrupts
The doppler sensor has a 5 volts frequency output (don't forget the voltage divider!) meaning that it'll toggle its pin several times per second at a frequency proportional to an object speed. In the simple firmware you can download below we're using a very simple strategy: everytime the HB-100 FOUT pin toggles, an interrupt is generated on the ESP8266 which will increment a counter. Every 0.5s we read this counter value and broadcast it to our computer and other ESP8266-03 module.
The interesting part above is the second line that registers doppler_int_handler as the GPIO12 interrupt function. Every time this function is called we increment our counter.
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_GPIO2); // Set GPIO2 function
gpio_output_set(0, BIT2, BIT2, 0); // Set GPIO2 low output
These two lines set GPIO2 as an output to drive the red LED. Given that the ESP8266-03 board already includes current limiting resistors you can directly connect your LED. As you can see in the video, this red LED blinks every 0.5sec:
os_timer_disarm(&led_timer); // Disarm led timer
os_timer_setfn(&led_timer, (os_timer_func_t *)led_timerfunc, NULL); // Setup led timer
os_timer_arm(&led_timer, 500, 1); // Arm led timer, 0.5sec, repeat
The ESP8266 'operating system' allows you to specify timers (functions) that will be fired at a pre-defined frequency. Here we specify that the led_timerfunc function should be called every 0.5sec indefinitely.
char ssid32 = "mywifissid"; // Wifi SSID
char password64 = "mywifipassord"; // Wifi Password
struct station_config stationConf; // Station conf struct
wifi_set_opmode(0x1); // Set station mode
os_memcpy(&stationConf.ssid, ssid, 32); // Set settings
os_memcpy(&stationConf.password, password, 64); // Set settings
wifi_station_set_config(&stationConf); // Set wifi conf
//wifi_status_led_install(13, PERIPHS_IO_MUX_MTCK_U, FUNC_GPIO13); // Wifi LED status
Having your board connect to your home Wifi is so simple! The last line allows you to specify an LED dedicated to Wifi status indication. It will blink when trying to connect/having an error, it will switch off when everything is ok.
void ICACHE_FLASH_ATTR network_check_ip(void)
struct ip_info ipconfig;
os_timer_disarm(&network_timer); // Disarm timer
wifi_get_ip_info(STATION_IF, &ipconfig); // Get Wifi info
if (wifi_station_get_connect_status() == STATION_GOT_IP && ipconfig.ip.addr != 0)
network_start(); // Everything in order
os_printf("Waiting for IP...\n\r");
os_timer_setfn(&network_timer, (os_timer_func_t *)network_check_ip, NULL);
os_timer_arm(&network_timer, 1000, 0);
This 1sec timer is called until the platform gets an IP from your home's DHCP server (usually your router). Once it has an IP address, you can start establishing connections to you computer and other devices.
global_tcp_connect.type=ESPCONN_TCP; // We want to make a TCP connection
global_tcp_connect.state=ESPCONN_NONE; // Set default state to none
global_tcp_connect.proto.tcp=&global_tcp; // Give a pointer to our TCP var
global_tcp_connect.proto.tcp->local_port=espconn_port(); // Ask a free local port to the API
global_tcp_connect.proto.tcp->remote_port=7778; // Set remote port (bcbcostam)
global_tcp_connect.proto.tcp->remote_ip0=ip1; // Your computer IP
global_tcp_connect.proto.tcp->remote_ip1=ip2; // Your computer IP
global_tcp_connect.proto.tcp->remote_ip2=ip3; // Your computer IP
global_tcp_connect.proto.tcp->remote_ip3=ip4; // Your computer IP
espconn_regist_connectcb(&global_tcp_connect, tcpNetworkConnectedCb); // Register connect callback
espconn_regist_disconcb(&global_tcp_connect, tcpNetworkDisconCb); // Register disconnect callback
espconn_regist_reconcb(&global_tcp_connect, tcpNetworkReconCb); // Register reconnection function
espconn_connect(&global_tcp_connect); // Start connection
Setting up a TCP connection is not so complex. You specify your destination IP/port together with functions to call if the connection is made, disconnected or reconnected.
global_udp_connect.type=ESPCONN_UDP; // We want to make a UDP connection
global_udp_connect.state=ESPCONN_NONE; // Set default state to none
global_udp_connect.proto.udp=&global_udp; // Give a pointer to our UDP var
global_udp_connect.proto.udp->local_port=2222; // Set local port
global_udp_connect.proto.udp->remote_port=2222; // Set remote port
global_udp_connect.proto.udp->remote_ip0=ip1; // The other ESP8266 IP
global_udp_connect.proto.udp->remote_ip1=ip2; // The other ESP8266 IP
global_udp_connect.proto.udp->remote_ip2=ip3; // The other ESP8266 IP
global_udp_connect.proto.udp->remote_ip3=ip4; // The other ESP8266 IP
if(espconn_create(&global_udp_connect) == 0) // Correctly setup
os_printf("UDP connection set\n\r");
udp_conn_ok = TRUE;
UDP connection are much simpler given that there's strictly no added logic on the data sending part like with TCP. The ESP8266 is just sending bytes to a given IP address.
I think I covered most of the low level stuff. I'll summarize what the firmware does in case you want to have a look at the source code:
- setup the input/outputs
- register a 0.5sec timer in charge of toggling the red LED, reading the doppler frequency, sending it to your computer and other ESP8266
- register a 1sec timer that will check the ESP8266 connection status. Once it has an IP, start making a TCP connection to the computer and a UDP one to the ESP8266
The indicating platform
The code here is much simpler:
- Connect to your Wifi
- Set 3 pins as outputs for the green/orange/red LEDs
- Create a UDP connection
- Parse the incoming data to know which LEDs to turn on/off.
Want the files? Here they are: doppler sensor code esp8266 client code
If you want more examples have a look at this github repository and don't hesitate to come to #esp8266 on freenode!
Thanks zarya for the help and code!