📜 ⬆️ ⬇️

Working with ESP8266: Compiling the compiler and writing the first firmware

In the last article, we looked at the initial setup and operation of the ESP-01 module with the base AT firmware. The capabilities of this firmware are quite limited and it is quite difficult to use it for some everyday tasks. As I wrote in the first article, for ESP8266 you can write your own firmware with the necessary functionality and thus make the ESP-01 board a self-sufficient device. Anyone who cares, I ask under habrakat.

As you know, the SoC ESP8266 is built on the basis of the Tensilica Xtensa LX106 processor, if anyone is interested, then there is an article in the network about configurable processors from this company . Espressif company provides full documentation, as well as a compiler and development environment for SoC ESP8266 only after signing a partnership agreement and not with everyone, they didn’t respond to my letter. With a little googling you can find the official compiler leaked to the network, a development environment based on Eclipse, a lot of documentation and licenses, but this is not our way. We will use an unofficial compiler based on Crosstool-NG

In this article I will explain how to build a compiler for Ubuntu Linux, as well as we will try to write the simplest firmware. I will focus on working with the Windows compiler, as well as setting up the Eclipse environment for writing firmware for ESP8266.

Part 1: Build a compiler for Ubuntu Linux, configure the SDK, build standard examples and firmware.
')
Install the build environment

For 32-bit Debian (Linux) we execute:
apt-get install git autoconf build-essential gperf bison flex texinfo libtool libncurses5-dev wget gawk libc6-dev-amd64 python-serial libexpat-dev 
For 64-bit Debian (Linux) we execute:
 apt-get install git autoconf build-essential gperf bison flex texinfo libtool libncurses5-dev wget gawk libc6-dev-i386 python-serial libexpat-dev 
Further:
USER change to the current user login.
 sudo mkdir /opt/Espressif sudo chown USER:root /opt/Espressif cd /opt/Espressif git clone -b lx106 git://github.com/jcmvbkbc/crosstool-NG.git cd crosstool-NG ./bootstrap && ./configure --prefix=`pwd` && make && make install ./ct-ng xtensa-lx106-elf ./ct-ng build PATH=$PWD/builds/xtensa-lx106-elf/bin:$PATH 

After that, you can sit back in the chair for 40-50 minutes and drink coffee. If everything is completed without errors, then we can move on.

Install SDK

 cd /opt/Espressif mkdir ESP8266_SDK cd ESP8266_SDK wget http://bbs.espressif.com/download/file.php?id=72 -O esp_iot_sdk_v0.9.3_14_11_21.zip wget http://bbs.espressif.com/download/file.php?id=73 -O esp_iot_sdk_v0.9.3_14_11_21_patch1.zip unzip esp_iot_sdk_v0.9.3_14_11_21.zip && rm esp_iot_sdk_v0.9.3_14_11_21.zip rm esp_iot_sdk_v0.9.3/lib/libpp.a unzip esp_iot_sdk_v0.9.3_14_11_21_patch1.zip && rm esp_iot_sdk_v0.9.3_14_11_21.zip mv esp_iot_sdk_v0.9.3/* . && rm -r esp_iot_sdk_v0.9.3/ 

Add libc libraries, libhal and header files to the SDK

 cd /opt/Espressif/ESP8266_SDK wget -O lib/libc.a https://github.com/esp8266/esp8266-wiki/raw/master/libs/libc.a wget -O lib/libhal.a https://github.com/esp8266/esp8266-wiki/raw/master/libs/libhal.a wget -O include.tgz https://github.com/esp8266/esp8266-wiki/raw/master/include.tgz tar -xvzf include.tgz && rm include.tgz 

Installing the ESP image tool

The ESP tool can be compiled from sources,
for Linux can be downloaded here
for debian / ubuntu here
Ready package for Ubuntu download from here
 cd /opt/Espressif/ wget https://github.com/esp8266/esp8266-wiki/raw/master/deb/esptool_0.0.2-1_i386.deb sudo dpkg -i esptool_0.0.2-1_i386.deb && rm esptool_0.0.2-1_i386.deb 

Install ESP upload tool

 cd /opt/Espressif git clone https://github.com/themadinventor/esptool esptool-py sudo ln -s $PWD/esptool-py/esptool.py crosstool-NG/builds/xtensa-lx106-elf/bin/ sudo ln -s $PWD/esptool-py/esptool.py /usr/sbin/ 

Build examples of firmware

First, open the file /opt/Espressif/ESP8266_SDK/include/osapi.h and comment out the line #include "user_config.h"

Download and compile the blinky and basic_example examples:
 cd /opt/Espressif/ESP8266_SDK/examples/ wget https://github.com/esp8266/source-code-examples/archive/master.zip && unzip master.zip && rm -r master.zip mv source-code-examples-master/* . && rm -r source-code-examples-master cd blinky make 
If all steps were done correctly, then the assembly will pass without errors and 2 firmware files 0x00000.bin and 0x40000.bin will appear in the firmware directory

Download and collect an example of the basic AT firmware:
 cd /opt/Espressif/ESP8266_SDK/examples/ wget -O at_v0.19_14_10_30.zip http://bbs.espressif.com/download/file.php?id=13 unzip at_v0.19_14_10_30.zip && rm at_v0.19_14_10_30.zip cd at_v0.19_on_SDKv0.9.2/ rm Makefile && cp ../example.Makefile . && mv example.Makefile Makefile 

To properly build the AT firmware, you need to edit the base Makefile in the line
LIBS = c gcc hal pp phy net80211 lwip wpa main
add the upgrade library link, the final line will look like this
LIBS = c gcc hal pp phy net80211 lwip wpa upgrade main
After that, the firmware can be assembled with the make command.

We are compiling an example of IoT firmware:
 cd /opt/Espressif/ESP8266_SDK/examples/IoT_Demo/ rm Makefile && cp ../example.Makefile . && mv example.Makefile Makefile 

As well as for AT firmware, for correct assembly of IoT firmware, it is necessary to edit the base Makefile into a string
MODULES = driver user
you need to add additional modules, the final line will look like this
MODULES = driver user json ssl upgrade lwip
and in line
LIBS = c gcc hal pp phy net80211 lwip wpa main
add json library link, the final line will look like this
LIBS = c gcc hal pp phy net80211 lwip wpa main json
After that, the firmware can be assembled with the make command.

To flash the ESP-01 board, use the make flash command.
Do not forget that to switch to the firmware update mode, you need to apply a low level to GPIO0 and a high level to CH_PD.
To understand what makes flash do, open any Makefile and find the flash line:
after the substitution of all arguments, we get the SoC firmware command:
 esptool.py --port /dev/ttyUSB0 write_flash 0x00000 firmware/0x00000.bin 0x40000 firmware/0x40000.bin 

The format of the firmware file, in fact, and how the exchange protocol can be read here in Russian or here in English .
That's where the compiler build and SDK for Linux are finished.

Part 2: Installing the compiler for Windows, setting up the SDK, building standard examples and firmware.

Since My main OS under which I work 90% of the time is Windows, then I was not interested in Linux development.
Below I will explain how to install and configure the compiler and SDK in Windows, as well as how to set up an Eclipse development environment for comfortable development of firmware in it.
I will not consider the process of building a compiler for Windows, because This is a rather complicated procedure, it is much more complicated than a build in Linux.
In order to save you from all the subtleties and nuances, I prepared the Espressif DevKit work package, which includes the compiler, the latest SDK version, standard firmware examples, as well as my own firmware samples.

So let's start:
1. Download (148Mb) and install my Espressif-ESP8266-DevKit-v2.2.1-x86.exe kit (as of November 15, 2016)
2. Download and install Java Runtime x86 or x64 (For example: jre-8u111-windows-x64.exe for Windows x64)
3. Download and install Eclipse Neon x86 or Eclipse Neon x64 for C ++ development. Unpack the archive in the root of drive C.
4. Download and install MinGW. Run mingw-get-setup.exe, during the installation process, select the mode without GUI, that is, remove the check mark "... also install support for the graphical user interface".
5. Download a set of my scripts to automate the installation of additional modules for MinGW. (current on 10/15/2016)
6. Run install-mingw-package.bat from my set. It will download the cached archives of packages for mingw from my site, approximately 90 Mb and install the basic modules for MinGW. Downloading a pre-made set of package files for MinGW ensures that all of them are installed, sometimes the servers where the MinGW packages are located become inaccessible and the necessary packages are not installed and therefore the firmware build can take place with all sorts of tricks.
7. Run Eclipse from the directory c: \ eclipse \ eclipse.exe
8. In Eclipse, select File -> Import -> General -> Existing Project into Workspace, in the Select root directory line, select the C: \ Espressif \ examples \ ESP8266 directory and import the working projects.
Next to the right in Make Target, select the desired project, for example hello-world, and run the all target on the assembly,
at the same time the assembly progress should be displayed in the Console window; if everything is done correctly, there will be something like this:

 17:00:00 **** Build of configuration Default for project hello-world **** mingw32-make.exe -f C:/Espressif/examples/ESP8266/hello-world/Makefile all CC driver/uart.c CC user/user_main.c AR build/app_app.a LD build/app.out FW firmware/0x00000.bin FW firmware/0x40000.bin 17:00:04 Build Finished (took 3s.740ms) 

This means that the firmware for ESP8266 is compiled and located in the directory C: \ Espressif \ examples \ hello-world \ firmware
To flash ESP8266, use the flash target, after having edited the Makefile project build file, line
ESPPORT? = COM2
where after COM the number 2 indicates the number of the COM port to which the board with the ESP8266 is connected.

Video with a demonstration of connecting projects in Eclipse, assembly and firmware ESP8266.

Video demonstrating the creation of a new project in Eclipse.

As mentioned above, to switch to the firmware update mode, you need to apply a low level to GPIO0 and a high level on CH_PD, after that either distort the board power or perform a Reset, feeding a low level to EXT_RSTB (also known as RESET). Perform these actions constantly very uncomfortable. A little thought, I changed the connection scheme of the ESP-01 board to the USB-to-RS232 converter, the meaning of the changes comes down to connecting the RTS output of the USB-to-RS232 converter to the EXT_RSTB output (also known as RESET) of the ESP-01 board and the DTR output of the USB-converter to-RS232 to output GPIO0 of the ESP-01 board.

ATTENTION! In my current build of the Unofficial Development Kit for Espressif ESP8266, the esptool utility already contains these improvements, so no additional patching is needed.

I also corrected the esptool.py utility in the connect procedure.
I also corrected the esptool.py utility in the connect procedure in order to reset the board by the RTS signal at startup and enter the bootloader mode by the DTR signal.
Changes to esptool.py
  def connect(self): print 'Entering bootloader...' self._port.setRTS(True) #RTS self._port.setDTR(True) #GPIO0 time.sleep(0.25) self._port.setRTS(False) self._port.setDTR(True) time.sleep(0.25) self._port.setRTS(False) self._port.setDTR(False) print 'Connecting...' 

With such light ears, I solved the problem of manually switching to the firmware update mode.


The final wiring diagram:


Now, fulfilling the goal of flash in Eclipse, we are making a new firmware with one click of the mouse. No need to poke on the breadboard and connect anything.

But in connection with this tricks my ears had to look for the normal Terminal program, since Putty no longer fit, it cannot control the RTS and DTR lines and moreover, immediately after connecting to the COM port via Putty, he set the DTR line to a low level, which led to a permanent entry into the bootloader mode. The Terminal program, convenient and at the same time rich in functionaries, was easily found; it is located in the C: \ Espressif \ utils \ directory.

Part 3: Writing the simplest firmware.

In order to start writing firmware, you need to study the documentation for the ESP8266 chip itself and the SDK, I did not include the documentation in my Espressif-ESP8266-DevKit assembly, so as not to violate all sorts of licensing agreements, all the same, all the documentation comes with the label confidential. However, there are plenty of sources on the Internet where you can get it, for example:
ESP8266 chip specification
Description The ESP8266 SDK is the main document we need.

In the last article, using the example of AT firmware, we connected to a Wi-Fi access point and ran a TCP server (TCP client) on the ESP8266, and also sent test data to a PC. As I wrote earlier, this method is not very convenient, because To execute AT commands to start a TCP server (TCP client), a separate controller is required. Below we consider an example of writing the simplest firmware for ESP8266, which implements all this without an external controller.
So the task:
1. The ESP-01 card should switch to the STA (Wi-Fi client) mode, establish a connection with our AP.
2. After establishing the connection with the AP, you must establish a TCP connection with the PC and send a test string.
3. We connect a button to GPIO0, when closing we send a text string.
3. The procedure for sending is repeated every 5 seconds.

Open the wifi-sta-tcp-client example from C: \ Espressif \ examples \ in Eclipse
A typical folder structure in any project for ESP8266 is something like this:
 wifi-sta-tcp-client |-Makefile |-driver |-uart.c |-include |-user |-user_main.c |-user_config.h 

where
- The Makefile is a set of instructions for the make program that helps build our project.
- driver directory - contains drivers for various devices, as long as we have only the uart.c file there, for working with the rs232 port of the ESP8266 chip, in principle there may be drivers for working with gpio, i2c, spi, pwm, etc., see the example IoT_Demo, there is more clearly what can be.
- directory include - contains auxiliary header files, while there are only files to work with port rs232
- user directory - the main directory, it contains the main firmware files, the file user_main.c and user_config.h

There may be other directories, it all depends on the model and principles of development, but the Makefile is configured exactly on such a structure, and if you start changing something, you will have to rewrite the instructions of the Makefile.
In the current form in order to add extra. the source directory in the assembly is enough in the Makefile in the string
MODULES = driver user
add our new directory, for example ssl and it will participate in the firmware build.
To add extra. libraries in the Makefile, you need to add a line
LIBS = c gcc hal phy pp net80211 lwip wpa main
we need libraries, for example, if we need a library for working with json in a project, then we write
LIBS = c gcc hal phy pp net80211 lwip wpa main json

Open the user \ user_main.c file

The main procedure that is performed when starting the firmware is
 //Init function void ICACHE_FLASH_ATTR user_init() { } 

We add header files to the beginning of the user \ user_main.c file:
 #include "ets_sys.h" #include "osapi.h" #include "os_type.h" #include "user_interface.h" #include "driver/uart.h" #include "espconn.h" #include "mem.h" #include "gpio.h" #include "user_config.h" 

These files, with the exception of user_config.h and driver / uart.h are located C: \ Espressif \ ESP8266_SDK \ include \
ets_sys.h - specific structures and definitions for working with events and timers
osapi.h - timers and some system functions, such as os_strcat, os_memcpy, os_delay_us, etc.
os_type.h - mapping structures from ets_sys.h
user_interface.h - a lot of supporting structures and API procedures, in particular for working with wi-fi, system_restart, system_deep_sleep procedures, etc.
espconn.h - main API file with structures and procedures for working with TCP and UDP connections
mem.h - work with memory, os_malloc, os_free, etc.
gpio.h - supporting structures and API procedures for working with GPIO

So we write in user_init () the following:
  //  uart0  uart1   115200 uart_init(BIT_RATE_115200, BIT_RATE_115200); //  100 . os_delay_us(100); #ifdef PLATFORM_DEBUG //    uart   , .  PLATFORM_DEBUG  user_config.h uart0_sendStr("ESP8266 platform starting...\r\n"); #endif //      STA (   AP) struct station_config stationConfig; char info[150]; //         AP,       //   SDK  0.9.2  wifi_set_opmode    system_restart if(wifi_get_opmode() != STATION_MODE) { #ifdef PLATFORM_DEBUG uart0_sendStr("ESP8266 not in STATION mode, restarting in STATION mode...\r\n"); #endif wifi_set_opmode(STATION_MODE); } //     STA,   ,  AP, , . user_config.h //   MAC      AP, . wifi_get_macaddr(SOFTAP_IF, macaddr); //   STA     MAC ,   ,                  if(wifi_get_opmode() == STATION_MODE) { wifi_station_get_config(&stationConfig); os_memset(stationConfig.ssid, 0, sizeof(stationConfig.ssid)); os_memset(stationConfig.password, 0, sizeof(stationConfig.password)); os_sprintf(stationConfig.ssid, "%s", WIFI_CLIENTSSID); os_sprintf(stationConfig.password, "%s", WIFI_CLIENTPASSWORD); wifi_station_set_config(&stationConfig); wifi_get_macaddr(SOFTAP_IF, macaddr); } //     uart     STA #ifdef PLATFORM_DEBUG if(wifi_get_opmode() == STATION_MODE) { wifi_station_get_config(&stationConfig); os_sprintf(info,"OPMODE: %u, SSID: %s, PASSWORD: %s\r\n", wifi_get_opmode(), stationConfig.ssid, stationConfig.password); uart0_sendStr(info); } #endif //      Wi-Fi,     1 .,   ,   TCP-    . os_timer_disarm(&WiFiLinker); os_timer_setfn(&WiFiLinker, (os_timer_func_t *)wifi_check_ip, NULL); os_timer_arm(&WiFiLinker, 1000, 0); //  GPIO, BtnInit(); //      #ifdef PLATFORM_DEBUG uart0_sendStr("ESP8266 platform started!\r\n"); #endif 

GPIO Initialization Procedure
 void BtnInit() { PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0); //     - (pull down) PIN_PULLDWN_DIS(PERIPHS_IO_MUX_GPIO0_U); //     + (pull up) PIN_PULLUP_EN(PERIPHS_IO_MUX_GPIO0_U); //  GPIO0   gpio_output_set(0, 0, 0, BIT0); //    GPIO os_timer_disarm(&BtnTimer); os_timer_setfn(&BtnTimer, BtnTimerCb, NULL); os_timer_arm(&BtnTimer, 500, 1); } 

The verification procedure is done in the forehead, without any elimination of contact bounce, according to the mind, you need to do it differently, it will also be seen below that in the senddata there is no check for raising the wi-fi interface, which should also be taken into account.
GPIO verification procedure
 static void ICACHE_FLASH_ATTR BtnTimerCb(void *arg) { if (!GPIO_INPUT_GET(BTNGPIO)) { GPIO_Time_Active++; } else { if (GPIO_Time_Active != 0) { #ifdef PLATFORM_DEBUG uart0_sendStr("Start sending data...\r\n"); #endif //   senddata(); } GPIO_Time_Active = 0; } } 

The procedure for checking Wi-Fi connection (called on a timer)
 static void ICACHE_FLASH_ATTR wifi_check_ip(void *arg) { //     , ip   STA,  , . struct ip_info ipConfig; //    wi-fi os_timer_disarm(&WiFiLinker); //      wifi_get_ip_info(STATION_IF, &ipConfig); //   wi-fi     ip  if (wifi_station_get_connect_status() == STATION_GOT_IP && ipConfig.ip.addr != 0) { //   wi-fi  connState = WIFI_CONNECTED; #ifdef PLATFORM_DEBUG uart0_sendStr("WiFi connected\r\n"); #endif #ifdef PLATFORM_DEBUG uart0_sendStr("Start TCP connecting...\r\n"); #endif connState = TCP_CONNECTING; //     senddata(); //           5 , . . os_timer_setfn(&WiFiLinker, (os_timer_func_t *)wifi_check_ip, NULL); os_timer_arm(&WiFiLinker, 5000, 0); } else { //   if(wifi_station_get_connect_status() == STATION_WRONG_PASSWORD) { connState = WIFI_CONNECTING_ERROR; #ifdef PLATFORM_DEBUG uart0_sendStr("WiFi connecting error, wrong password\r\n"); #endif os_timer_setfn(&WiFiLinker, (os_timer_func_t *)wifi_check_ip, NULL); os_timer_arm(&WiFiLinker, 1000, 0); } // AP   else if(wifi_station_get_connect_status() == STATION_NO_AP_FOUND) { connState = WIFI_CONNECTING_ERROR; #ifdef PLATFORM_DEBUG uart0_sendStr("WiFi connecting error, ap not found\r\n"); #endif os_timer_setfn(&WiFiLinker, (os_timer_func_t *)wifi_check_ip, NULL); os_timer_arm(&WiFiLinker, 1000, 0); } //    AP else if(wifi_station_get_connect_status() == STATION_CONNECT_FAIL) { connState = WIFI_CONNECTING_ERROR; #ifdef PLATFORM_DEBUG uart0_sendStr("WiFi connecting fail\r\n"); #endif os_timer_setfn(&WiFiLinker, (os_timer_func_t *)wifi_check_ip, NULL); os_timer_arm(&WiFiLinker, 1000, 0); } //   else { os_timer_setfn(&WiFiLinker, (os_timer_func_t *)wifi_check_ip, NULL); os_timer_arm(&WiFiLinker, 1000, 0); connState = WIFI_CONNECTING; #ifdef PLATFORM_DEBUG uart0_sendStr("WiFi connecting...\r\n"); #endif } } } 

The procedure for sending data to a PC
 static void ICACHE_FLASH_ATTR senddata() { char info[150]; char tcpserverip[15]; struct espconn *pCon = (struct espconn *)os_zalloc(sizeof(struct espconn)); if (pCon == NULL) { #ifdef PLATFORM_DEBUG uart0_sendStr("TCP connect failed\r\n"); #endif return; } pCon->type = ESPCONN_TCP; pCon->state = ESPCONN_NONE; //   TCP-,     os_sprintf(tcpserverip, "%s", TCPSERVERIP); uint32_t ip = ipaddr_addr(tcpserverip); pCon->proto.tcp = (esp_tcp *)os_zalloc(sizeof(esp_tcp)); pCon->proto.tcp->local_port = espconn_port(); //   TCP-,     pCon->proto.tcp->remote_port = TCPSERVERPORT; os_memcpy(pCon->proto.tcp->remote_ip, &ip, 4); //  callback ,     espconn_regist_connectcb(pCon, at_tcpclient_connect_cb); //   callback ,   ,       //espconn_regist_reconcb(pCon, at_tcpclient_recon_cb); //    #ifdef PLATFORM_DEBUG os_sprintf(info,"Start espconn_connect to " IPSTR ":%d\r\n", IP2STR(pCon->proto.tcp->remote_ip), pCon->proto.tcp->remote_port); uart0_sendStr(info); #endif //    TCP- espconn_connect(pCon); } 

callback function called after establishing a connection
 static void ICACHE_FLASH_ATTR at_tcpclient_connect_cb(void *arg) { struct espconn *pespconn = (struct espconn *)arg; #ifdef PLATFORM_DEBUG uart0_sendStr("TCP client connect\r\n"); #endif // callback ,     espconn_regist_sentcb(pespconn, at_tcpclient_sent_cb); // callback ,    espconn_regist_disconcb(pespconn, at_tcpclient_discon_cb); char payload[128]; //   ,   MAC  ESP8266   AP      ESP8266 os_sprintf(payload, MACSTR ",%s\r\n", MAC2STR(macaddr), "ESP8266"); #ifdef PLATFORM_DEBUG uart0_sendStr(payload); #endif //   espconn_sent(pespconn, payload, strlen(payload)); } 

callback functions called after sending data and after disconnecting
 static void ICACHE_FLASH_ATTR at_tcpclient_sent_cb(void *arg) { #ifdef PLATFORM_DEBUG uart0_sendStr("Send callback\r\n"); #endif //  ,   TCP- struct espconn *pespconn = (struct espconn *)arg; espconn_disconnect(pespconn); } static void ICACHE_FLASH_ATTR at_tcpclient_discon_cb(void *arg) { struct espconn *pespconn = (struct espconn *)arg; // ,   os_free(pespconn->proto.tcp); os_free(pespconn); #ifdef PLATFORM_DEBUG uart0_sendStr("Disconnect callback\r\n"); #endif } 

And in the conclusion of the video, with a demonstration of the operation of this firmware.

As you have already noticed, the motherboard on the Espressif ESP8266 chip has quite an impressive potential for writing your own firmware.
In the next article I will give an example of how the ESP8266 works in conjunction with the transmitter module of the nooLite MT1132 and we will try to control the lighting in a real apartment. Perhaps in the same article we will analyze the work of the simplest Web-server for ESP8266.

PS If you have any questions and suggestions, then I will be glad to hear them, but due to low karma, I will not be able to respond promptly in the comments, so write to the PM or email.

Source: https://habr.com/ru/post/363349/


All Articles