This page describes basic simple source code organization for programming the esp8266 module with esp-nonos-sdk using toolchain installed on previous page. Again, this is simplified version yet sufficient to conduct quite complicated programs.
There is two versions, simple standalone skeleton and more advanced version with host-compiling projects being self contained subtrees utilizing shared common upper lib for all projects common functions.
Simple source code skeleton is downloadable as esp-skeleton-simple.tar.gz. Get and extract it to convient place. Package extracts with contents :
standalone/ standalone/esp-main.c standalone/user_config.h standalone/Makefile
The file is empty.
Yes, it's stupid but it exists here because that for some reason ExpressIf's SDK's headers deliberately include it. In SDK's headers there includes in osapi.h and user_interface.h that include that nonexistent user_config.h in a way like :
esp:~$ head /usr/local/ESP8266_NONOS_SDK/include/osapi.h /* * Copyright (c) 2010 Espressif System */ #ifndef _OSAPI_H_ #define _OSAPI_H_ #include <string.h> #include "user_config.h"
If that annoys you, either clean up headers (two files, probably breaks compilation of SDK's examples as side effect), touch /usr/local/ESP8266_NONOS_SDK/include/user_config.h or go on with that stupid extra file on your sources maybe even utilizing it to something.
Template for bootstrapping the program. Ie. "The main()".
The esp8266 program requires only user_init to exists, but since in no way you do all your stuff branching from there, but you must do everything event based returning control to system after very short execution there's also some extra glue in that file explained later in Anatomy of Hello World
Simple readable Makefile most simple projects.
Breaking that Makefile to parts, first define location of SDK and potential othed defined constant your program requires. Since toolchain was extracted with /usr/local/ESP8266_NONOS_SDK symlink pointing to actual SDK-directory this section requires no changes between different projects.
# common DEFS = CFLAGS := LDFLAGS := SDKPATH = /usr/local/ESP8266_NONOS_SDK SDKINCLUDES = -I$(SDKPATH)/include SDKLIBS = -L$(SDKPATH)/ld -L$(SDKPATH)/lib
Compiler arguments for producing .o from .c. There's two noteworthy parameters. -Os means optimize for size - which is necessary where available space is precious. In practice -Os is -O2 excluding options that would increase code size.
Option -Wall is always nice since it gives you lots of nice warnings. Given the prefection of SDK's headers, you need lots of own fixes for missing function definitons. Ultimately passing -Wall cleanly is allways a good target. They are there for reason. Remove it if you are not so pedantic.
One important define is -DICACHE_FLASH. Function attribute ICACHE_FLASH_ATTR does not do anything without that define. Also if you are going to use os_timer_arm_us and system_timer_reinit(), define USE_US_TIMER before including header files.
# esp8266 ESP_DEFS = -DICACHE_FLASH -DUSE_US_TIMER -D__TARGET_ESP__ $(DEFS) ESP_CC = xtensa-lx106-elf-gcc ESP_AR = xtensa-lx106-elf-ar ESP_CFLAGS = -I. $(SDKINCLUDES) \ -mlongcalls -Os -Wall $(ESP_DEFS) $(CFLAGS)
Required libraries for linking the binary. The libnames gives you advise what they contain, but for example whether you use wifi functionality or not you need to link -lwpa and -lnet8211. The libs are quite cross referenced, you cannot omit any of them. The furher linker option --gc-sections salvages a bit, stripping unneeded functions from being included into binary.
The newer versions of SDK require even more libs to be linked. For example the big -lssl needs always to be linked in. That's one reason the newer versions of SDK is not used in these examples.
ESP_LIBS = \ -lmain \ -lnet80211 \ -lwpa \ -llwip \ -lpp \ -lphy \ -ldriver
Flags for linking process to produce the final binary. There is some changes to SDK default linking attributes to reduce resulting total code size thought options itself might add bit overhead.
For background, the lib is constructed from .o's, ie. the .c's compiled into machine code. When you link a lib into program with gcc tools, these .o's in .a goes into resulting binaries in complete. If the original .c contains lots of exported function, because the .o goes linked in complete, all function in it goes to resulting exe despite you would need only one of them.
If you link libbiglib.a containing lotsoffunctions.o into your program, everything on that .o gets into resulting exe despite you would use only the little_small_function() in lotsoffunctions.o. That is the reason, for example in Linux's libc every function is single file and so a single .o
The options -fdata-sections, -ffunction-sections and --Wl,--gc-sections are here to break .o's down when possible and garbage collect unnecessary functions off. These are explained on more detail on separate page.
Option -Teagle.app.v6.ld refers to file /usr/local/ESP8266_NONOS_SDK/ld/eagle.app.v6.ld containing instructions of positioning programs internals into memory locations. Again, this is explained in same separate page.
ESP_LDFLAGS = -L. $(SDKLIBS) -Teagle.app.v6.ld $(LDFLAGS) ESP_LINKFLAGS = $(ESP_LDFLAGS) \ -nostdlib -fdata-sections -ffunction-sections \ -Wl,-static -Wl,--gc-sections \ -Wl,--start-group $(ESP_LIBS) -Wl,--end-group -lgcc
Magic for including what gets compiled. Since these are wildcarded, every .c gets compiled and into resulting binary and all .h's are dependended so forcing recompilation if changed.
Simplified, write a new .c or .h if you need. You need not to change the Makefile.
SOURCES = $(wildcard *.c) HEADERS = $(wildcard *.h) OBJECTS_ESP = $(SOURCES:.c=.o)
Genering targets for compiling the sources and producing required final binary. These define targets esp, flash and clean.
esp: esp-image-0x00000.bin esp-image: $(OBJECTS_ESP) $(HEADERS) $(ESP_CC) $(ESP_CFLAGS) -o $@ \ $(OBJECTS_ESP) $(ESP_LINKFLAGS) esp-image-0x00000.bin: esp-image esptool.py elf2image $^ flash: esp-image-0x00000.bin esptool.py write_flash \ --flash_freq 80m --flash_mode qio --flash_size 32m \ 0x00000 esp-image-0x00000.bin 0x40000 esp-image-0x40000.bin .c.o: $(ESP_CC) $(ESP_CFLAGS) $(ESP_INCLUDES) -c $< -o $@ clean: rm -f *.o rm -f *.a rm -f *.bin rm -f esp-image rm -f *~
This makefile defines targets esp, flash and clean. If you have everything working, enable the cross compiler if you haven't already done it and invoke the make targets.
esp:~/Src/esp/examples/standalone$ . use-esp8266-build-system esp:~/Src/esp/examples/standalone$ make clean esp
If you have cables ready and esp8266 wired to receive flash updates, you can also directly flash it :
esp:~/Src/esp/examples/standalone$ make clean esp flash
Personally I write short parts, compile often and flash often. ESP code is impossible to debug live, so it's necessary to proceed on small safe steps. Especially if you are not so studious, planning engineer type person.
I keep practically all the time esp wired to accept (GPIO0 down) flashes, since after flashing it seems to boot once to normal mode even GPIO0 is wired down.
My own development cycle is :
Steps 2 to 7 takes less than minute. I case I missed the results at stage 7, I don't even bother to reboot esp to see again what happened. I reflash it with same unmodified image to get reboot that way. Reflashing the same image saves me for wasting time on disconnecting and reconnecting the GPIO0
Most of time I forget to disconnect the cutecom resulting flashing not starting - althought often flashing starts just fine and cutecom disconnects itself as not capable to handle all flashing flood it sees on line. Often I don't even bother to disconnect the cutecom but just keep reseting and clean-compile-flashing in hope that after couple resets image goes in fine.
Once flashing has started I have never seen it failed. Also, current chip in my breadboard has been flashed hundreds of times without any sign of wearout.
Everyone is of course free to choose own development model. Personnally I have little tolerance to waiting and hate things like deployment state. This Makefile model is made for fast compiles and fast tests.
To be written..