gardenergeek.com

Gardening, electronics and everything else that is fun.

Dht22 driver

I have finally done some coding using the RTOS SDK, and I think it works quite well. My first simple project will be a simple humidity/temperature logger running on batteries, to detect and prevent mold problems in our cellar. I have started with the Dht22 sensor driver. There are a number of drivers out there, but I decided to write my own as an excersie.

My requirements:

  • Simple API
  • Clear error handling
  • Clear code
  • Interrupt driven

Now the first version is ready, so feel free to use it. You find it in my github repository here

To use it clone the repository, and first build the “lib”, and then the “dht22” sample. Check the paths in the MakeFiles.

 

Setting up a dev. environment for ESP8266 using RTOS SDK

I had some troubles setting up a development environtment using the RTOS-SDK. There are a number of options I have tried but they all seem to complicated for me. For me it is important to understand what is happening all the way from compiling, linking and deploying the executable.

I tried the official RTOS SDK from expressif and the esp-open-rtos SDK. I had troubles getting them to work, I guess mainly because I prefer working in Windows. For me I also think that their build system are to complicated, but maybe I am just stupid.

For me a development environment should be quite simple:

  • Toolchain (compiler and linker)
  • Build automation tool (for instance make)
  • A tool to transfer the executable image to ESP8266
  • An SDK for the target plattform
  • An editor of some sort to edit code.

You develop some code, build it (compile and link), and then build an executable and transfer to development environment. Every step should be easy to understand, and of course as the project becomes more complicated, the build process will be more complicated, but I want a really simple project (f.i. “Hello World”) to have a really simple build system in order to understand every detail of it.

Let me explain how my system is setup.

Please note that you have to use an x64 version of windows for this setup to work, this is because the prebuilt compiler is x64.

Toolchain

First of all we must have a toolchain. When you try google it you will find numerous sites describing how  you build it from source. That is probably very easy using a Linux workstation of some sort, but I always have troubles doing that on Windows. I would rather just download prebuild binaries from somewhere.

Eventually I found prebuilt binaries (build for x64):  xtensa-lx106-elf-141114.7z

Download and extract to a folder, mine is: c:\xtensa-lx106-elf

Make sure to add bin folder to your PATH.

Build environment

Install MinGW which is a minimal build environment for gcc, make sure to a add bin folder to your path.

 

Tool to transfer firmware to ESP8266 (esptool.py)

There is an excelent tool created in python to communicate with the ESP8266, to be able to use it we have to install python.

Please NOTE, install Python 2 (and not Python 3)

Install Python

And also the tool (this is a single python file, save to some good place:, mine is:C:\projekt\esp8266\esptool.py) : esptool.py

 

RTOS-SDK

Obtain the RTOS-SDK from expressif. I have cloned it by using git, which is very convinient when updating, but you can also just download it from  github

First install git

.. and then clone the RTOS-SDK from expressif.

  1. Create a directory where you want the SDK, mine is c:\project\esp8266\SDK
  2. start cmd.exe
  3. And execute: git clone https://github.com/espressif/ESP8266_RTOS_SDK.git

Editor

You can use any texteditor that you want (even notepad), but I use ConText It is very lightweight, but still have code highligt for C/C++ (and many other languages)

Making it all work!

Ok, that was hopefully quite simple. Now we will make it all work.

The process of getting your code to work on the ESP8266 includes the following steps:

  1. Write the source
  2. Compile the source to object files
  3. Link your object file with object files from the SDK to produce a binary image
  4. Extract the parts from the binary image which should land in different areas of the ESP8266 memory space
  5. Transfer the parts to the ESP8266

All these steps is done by using a makefile. I have a makefile which I use for every project i write, and it is based on a Makefile I found somewhere. I am really sorry that I cannot remeber where I found it, I would really like to give the original author credit, since this was the part I had most trouble with trying the official SDK and open SDK.

Here it is:

# Com port to dev board
PORT = COM3

# Directories
SDKBASE = C:/esp8266/SDK/ESP8266_RTOS_SDK
PYTHON = C:/Python27/python.exe
ESPTOOLPY = C:/esp8266/tools/esptool/esptool.py
TOOLCHAINHOME = C:/esp8266/xtensa-lx106-elf

AR = xtensa-lx106-elf-ar
CC = xtensa-lx106-elf-gcc
CPP = xtensa-lx106-elf-gcc
LD = xtensa-lx106-elf-gcc
NM = xt-nm
OBJCOPY = xtensa-lx106-elf-objcopy
OD = xtensa-lx106-elf-objdump

ESPTOOL ?= $(PYTHON) $(ESPTOOLPY)

INCLUDES = -I $(SDKBASE)/include
INCLUDES += -I $(SDKBASE)/include/espressif
INCLUDES += -I $(SDKBASE)/extra_include
INCLUDES += -I $(SDKBASE)/include/lwip
INCLUDES += -I $(SDKBASE)/include/lwip/lwip
INCLUDES += -I $(SDKBASE)/include/lwip/ipv4
INCLUDES += -I $(SDKBASE)/include/lwip/ipv6
INCLUDES += -I $(TOOLCHAINHOME)/xtensa-lx106-elf/include

# don’t change -Os (or add other -O options) otherwise FLASHMEM and FSTR data will be duplicated in RAM
CFLAGS = -g -save-temps -Os -Wpointer-arith -Wundef -Werror -Wl,-EL -fno-inline-functions -nostdlib -mlongcalls -mtext-section-literals
CFLAGS += -D__ets__
CFLAGS += -DICACHE_FLASH
CFLAGS += -fno-exceptions

# -fno-rtti
#CFLAGS += -fno-threadsafe-statics
#CFLAGS += -fno-use-cxa-atexit

LDFLAGS = -nostdlib -Wl,–no-check-sections -u call_user_start -Wl,-static
LDFLAGS += -Wl,–gc-sections

LD_SCRIPT = eagle.app.v6.ld
#LD_SCRIPT = eagle.app.v6.new.2048.ld

SDK_LIBDIR = -L$(SDKBASE)/lib
SDK_LDDIR = $(SDKBASE)/ld
# linking libgccirom.a instead of libgcc.a causes reset when working with flash memory (ie spi_flash_erase_sector)
# linking libcirom.a causes conflicts with come std c routines (like strstr, strchr…)
LIBS = -lmain -lgcc -lfreertos -lnet80211 -lphy -lwpa -lcrypto -llwip -lpp -lminic -lhal
#-lminic -lm -lgcc -lhal -lphy -lpp -lnet80211 -lwpa -lmain -lfreertos -llwip -lcrypto

OBJ = user_main.o

SRCDIR = ./src/

TARGET_OUT = app.out
.PHONY: all flash clean flashweb flashdump flasherase
all: $(TARGET_OUT)
$(TARGET_OUT): libuser.a
$(LD) $(SDK_LIBDIR) -T$(SDK_LDDIR)/$(LD_SCRIPT) -Wl,-M >out.map $(LDFLAGS) -Wl,–start-group $(LIBS) libuser.a -Wl,–end-group -o $@
@$(OD) -h -j .data -j .rodata -j .bss -j .text -j .irom0.text $@
@$(OD) -t -j .text $@ >_text_content.map
@$(OD) -t -j .irom0.text $@ >_irom0_text_content.map
@$(ESPTOOL) elf2image $@
libuser.a: $(OBJ)
$(AR) cru $@ $^

%.o: $(SRCDIR)%.c $(wildcard $(SRCDIR)*.h)
$(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@
%.o: $(SRCDIR)%.cpp $(wildcard $(SRCDIR)*.h)
$(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@
flash: $(TARGET_OUT)-0x00000.bin $(TARGET_OUT)-0x20000.bin
@-$(ESPTOOL) –port $(PORT) write_flash 0x00000 $(TARGET_OUT)-0x00000.bin 0x20000 $(TARGET_OUT)-0x20000.bin
#flashweb:
# @python binarydir.py $(WEBDIR) webcontent.bin $(MAXWEBCONTENT)
# @-$(ESPTOOL) –port $(PORT) write_flash $(WEBCONTENTADDRS) webcontent.bin

flashdump:
@-$(ESPTOOL) –port $(PORT) read_flash 0x0000 0x80000 flash.dump
@xxd flash.dump > flash.hex

flasherase:
@-$(ESPTOOL) –port $(PORT) erase_flash

clean:
@rm -f *.a
@rm -f *.o
@rm -f *.out
@rm -f *.bin
@rm -f *.ii
@rm -f *.s
@rm -f *.expand
@rm -f *.map
@rm -f *.dump
@rm -f *.hex

Ok, lets test it all.

  1. Create a folder for your project, mine is c:\project\test1
  2. Create a new empty textfile and name it “Makefile” (no suffix), and copy the above content to it.
  3. Create a new folder named “src”, mine is c:\project\test1\src
  4. Create a new sourcefile within that folder, named user_main.cpp, mine is:c:\project\test1\src\user_main.cpp

Add the following to it:

extern "C"
{
   #include "esp_common.h"
   #include "freertos/FreeRTOS.h"
   #include "freertos/task.h"

   extern void uart_div_modify(int,int);
}

/*
* Simple task that prints "Hello world" every second
*/
void helloTask(void *pvParameters)
{
   while(1)
   {
      printf("Hello world\n");

      vTaskDelay(1000 / portTICK_RATE_MS);
   }
}

/*
* This is entry point for user code
*/
extern "C"
{
   void ICACHE_FLASH_ATTR user_init(void)
   {

      portBASE_TYPE ret;

      // Set UART speed to 115200
      uart_div_modify(0, UART_CLK_FREQ / 115200);
      wifi_set_opmode(NULL_MODE);

      xTaskHandle t;
      ret = xTaskCreate(helloTask, (const signed char *)"rx", 256, NULL, 2, &t);

   }
}

And now build it, start cmd.exe.

Go to the folder with your project, mine: c:\project\test1\

And execute command: “make”

make
xtensa-lx106-elf-gcc -g -save-temps -Os -Wpointer-arith -Wundef -Werror -Wl,-EL -fno-inline-functions -nostdlib -mlongcalls -mtext-section-literals -D__ets__ -DICACHE_FLASH -fno-exceptions -I C:/projekt/tester/esp_iot_rtos_sdk/include -I C:/projekt/tester/esp_iot_rtos_sdk/include/espressif -I C:/projekt/tester/esp_iot_rtos_sdk/extra_include -I C:/projekt/tester/esp_iot_rtos_sdk/include/lwip -I C:/projekt/tester/esp_iot_rtos_sdk/include/lwip/lwip -I C:/projekt/tester/esp_iot_rtos_sdk/include/lwip/ipv4 -I C:/projekt/tester/esp_iot_rtos_sdk/include/lwip/ipv6 -I C:/xtensa-lx106-elf/xtensa-lx106-elf/include -c src/user_main.cpp -o user_main.o
xtensa-lx106-elf-ar cru libuser.a user_main.o
xtensa-lx106-elf-gcc -LC:/projekt/tester/esp_iot_rtos_sdk/lib -TC:/projekt/tester/esp_iot_rtos_sdk//ld/eagle.app.v6.ld -Wl,-M >out.map -nostdlib -Wl,–no-check-sections -u call_user_start -Wl,-static -Wl,–gc-sections -Wl,–start-group -lmain -lgcc -lfreertos -lnet80211 -lphy -lwpa -lcrypto -llwip -lpp -lminic -lhal libuser.a -Wl,–end-group -o app.out

app.out: file format elf32-xtensa-le

Sections:
Idx Name Size VMA LMA File off Algn
0 .data 000003ac 3ffe8000 3ffe8000 000000e0 2**4
CONTENTS, ALLOC, LOAD, DATA
1 .rodata 00000294 3ffe83b0 3ffe83b0 00000490 2**4
CONTENTS, ALLOC, LOAD, READONLY, DATA
2 .bss 00006af0 3ffe8648 3ffe8648 00000728 2**4
ALLOC
3 .text 000064cc 40100000 40100000 00000724 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
4 .irom0.text 0003bb88 40220000 40220000 00006bf0 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE

And now you have the binary blobs ready to transfer to ESP8266. To transfer issue command:
“make flash”

make flash
Connecting…
reply
reply
reply
reply
reply
reply
reply
reply
Erasing flash…
reply
Writing at 0x00000000… (3 %) reply
Writing at 0x00000400… (7 %) reply
Writing at 0x00000800… (11 %) reply
Writing at 0x00000c00… (14 %) reply
Writing at 0x00001000… (18 %) reply
Writing at 0x00001400… (22 %) reply
Writing at 0x00001800… (25 %) reply
Writing at 0x00001c00… (29 %) reply

….

And then connect your favorite terminal (I use CoolTerm) to COM3 and you vill see “Hello World”!

 

The RTOS-SDK for ESP8266

In my previous post I described some basic options when starting developing for the ESP8266, and also described the NON-OS SDK briefly. I have choosen not to use the NON-OS SDK mainly based on three resons:

  • ESP8266 comes with built in software and API, your application don’t “own” the hardware by itself (this is the main reason)
  • Threading can be a good tool for creating simpler code.
  • I have never used FreeRTOS, it seems more challenging and fun to explore it rather than using f.i. the Arduino IDE and just hack away.

The first reason is the most important to me, let me explain the reasoning.

Lets consider an example, you want to build a remote sensor that monitor temperature and reports it back to a central server. Using a simpler plattform to build this, when you are finished your binary will consist of your application code, some library code to use sensor and RF and a “main” binary from the arduino SDK which bootstraps the application and calls your application entry points (setup() and loop()).

The flow in the application is very simple:

  1. The CPU starts into “main” from the SDK, it initializes itself and then call your setup() function.
  2. When your setup function returns, the “main” module runs again and probably sets up a timer function, which will call your loop() function.
  3. Thats all, everything else that happens in the application are handled by your application.

When building the same solution with the ESP8266 using the NON-OS SDK, the solution whould be very similar. The only difference would be that you had to setup the timer yourself.

Ok, but why would this be a problem when using the ESP8266 and not using the Arduino?

As I see it, the problem is that your application must “share” the hardware with firmware developed by ExpressIf. The ExpressIf code is closed source, so you will never know in detail what it does, the only thing that you can be sure off is that it does some very important and time critical stuff (communicating over Wifi, e.t.c.).

When reading the documentation from ExpressIf, clearly point this out (from ESP8266 SDK programming guide by ExpressIf):

Notice:

• Using non-OS SDK which is single-threaded, the CPU should not take long to execute tasks:

‣ If a task occupies the CPU too long, ESP8266 can’t feed the dog, it will cause a watchdog reset;

‣ If interrupt is disabled, CPU can only be occupied in us range and the time should not be more than 10 us; if interrupt is not disabled,  it is suggested that CPU should not be occupied more than 500 ms.

• We suggest using a timer to check periodically, if users need to call os_delay_us or function while, or function for in timer callback, please do not occupy CPU more than 10 ms.

• Using non-OS SDK, please do not call any function defined with  ICACHE_FLASH_ATTR in the interrupt handler.

• We suggest using RTOS SDK, RTOS to schedule different tasks.

In most cases it will not be a problem if guidelines from ExpressIf is taken into account, however in practical work they are not that easy to follow. For instance, you have some code that for some reason disables interrupts, how long in us does it take to execute ? Does it take less than 10us ?

I generally think it is hard to develop software according to some rules, it is much more easy to have the rules enforced by an API, and this is what you get by using the RTOS-SDK instead of the NON-OS SDK.

A really simple and stupid example (if you called someFunc from a timer it would work)

	/* using the NON-OS SDK */
	void someFunc()
	{
		/* Do some lengthy stuff */
		while(true);
	}

	void ICACHE_FLASH_ATTR user_init(void)
	{
		/* Will cause Watch dog reset */
		someFunc();
	}

Call someFunc as a RTOS task, scheduler will make sure that firmware can do what it needs.

	/* using the RTOS-SDK */
	void someFunc(void *pvParameters)
	{
		/* Do some lengthy stuff */
		while(true);
	}
	void ICACHE_FLASH_ATTR user_init(void)
	{
		/* will work just fine */
		xTaskCreate(someFunc, (const signed char *)"task", 256, NULL, 2, &t);	
	}

Using the ESP8266

Ok, now its time to start developing for the ESP8266. Basically you can use it in two ways, either as a “wifi modem” using a different MCU for application (f.i. an Arduino) or use it standalone.  My first project will be a simple humidity and temperature sensor for our cellar. The purpose is to monitor the climate in orde to make sure that no mold can grow.

The solution will look something like this:

  • DHT22 sensor (measures humidity and temperature)
  • Some central unit (probably a Raspberry PI) for data storage and monitoring
  • Network using Wifi
  • Power supplied by battery

The ESP8266 hardware would certainly meet these requirement by itself, the challenge is to have it run on battery. An Arduino based solution could poweroff the ESP8266 completely when not sending data, and this would consume much less power than using the ESP8266 by itself. My goal is to have the solution running for a year on some standard alkaline batteries.

Now when the hardware plattform is set, it is time to consider the software plattform. There are several options available:

Language: assembly, C/C++ and Lua

SDK: NON-OS SDK and RTOS SDK.

I have worked alot in C/C++ so for me deciding the language was easy. Lua is cerainly a good option as well, but it is a new language for me, and I also think that using a script language developing for embedded systems “feels” wrong. However, the CPU is quite powerful so it might work very well.

NON-OS SDK and RTOS SDK

For the difference according to Expressif, se this article

Initially I started to use the NON-OS sdk, again it felt right when developing for embedded systems to maximize control and minimize overhead. I have worked with some other MCU:s and I have always worked directly with the hardware without any OS. A big advantage when working with the ESP8266 is that if you decide to use the NON-OS SDK, you gain access to several very good and easy development environments:

The Arduino environment

As a hobbyist interested in embedded systems, it is impossible to miss the Arduino plattform. It has a large community, a great number of libraries and available open source software. It is certainly the plattform that will get you productive really fast. Nowdays, it is possible to use the arduino environment when developing for the ESP8266. I have not tried this myself, but if you have used Arduino before it seems like a good choice.

Sming

If I were to use the NON-OS SDK I would must certainly use Sming. Sming is an opensource framework which makes working with the ESP8266 really simple. If you come from an Arduino background, you will also be familiar with the API, it is very similar. The ESP8266 is a much more powerful plattform than Arduino, The Sming framework makes using the Wifi parts in ESP8266 simple, in wraps the ugly expressif API:s in clean and simple to understand API:s and makes using the “flash file system” Spiffy simple. In my opinion this is the best option.

Read more about Sming

Using the NON-OS SDK directly

It is certainly possible to use the NON-OS SDK directly. I preffer working on Windows and I am used to Visual Studio and also the Arduino environment, I had much difficulties to get it to work, I suppose if you are a Linux guy it would be much simpler. And, anyway I see no advantage of using it directly, use Sming instead!.

In my next post I will explain why I selected not to use the NON-OS SDK.

 

 

ESP8266

I guess you all have heard about the little wonder ESP8266. It is a soc that contains wifi, CPU, GPIO and much more. It is incredibly cheap, only about $2-3 on Ebay.

In my greenhouse I want to measure and control many different things, my previous plan was to use arduino and the cheap nrf24l01 radio for this. The disadvantage is that I have to have another network on our farm (we already have Wifi), and off course I also need to bridge this network onto the Internet in order to make the data accessible for central processing.

So why not use Wifi instead, and I will have all infrastructure already in place!

The ESP8266 soc is hard to use withou making your own PCB, fortunatly there are a number of modules mainly manufactured by AI-thinker. They are named ESP-XX and all have different configurations, I have bought some ESP-01 (the first module), ESP-07 (possibility to add external antenna), ESP-12E and ESP-12F.

List of all modules

The boards have onboard flash memory to store user applications, and some (or all) GPIO pins exposed.

I think a really good “starter kit” is:

A module with bread board adapter:

ESP8266 SMD Adapter Board R2

I bought one adapter with a ESP-12F already soldered on, which is really convinient and it works very well. I also bought two extra without any module on it, and it worked great to solder both ESP-07 and ESP-12E onto it.

A breadboard friendly power supply:

Breadboard-Friendly Power Supply Module 5V/3.3V

The modules are quite power hungry, first i tried to feed it by the FTDI adapter (via USB), but it didn’t work very stable. The bread board power supply is really good, with on/off swicthes and the possiblity to feed both 5v and 3.3v at the same time.

An FTDI adapter (USP to serial), make sure it supports 3.3v:

CP2102 USB-TTL UART Module V2 (Genuine IC)

 

 

© 2017 gardenergeek.com

Theme by Anders NorenUp ↑