Adafruit Feather Huzzah ESP8266 Feather is a line of thin, beginner friendly development boards from Adafruit. The Feather Huzzah is Adafruits take on an ‘all-in-one’ development board for the popular ESP8266 Wi-Fi chip. It comes with USB, battery charging, and two LEDs built in. In this post, we’ll walk through the ‘hello world’ equivilant for AVR programming–blinky.

Before getting started, we need to select a framework for our appliation. EspressIf, the makers of the ESP8266 chip, provide two standard development frameworks: A Non-OS SDK, and the RTOS SDK. The Non-OS SDK is a proprietary general purpose framework for the ESP8266, while the RTOS SDK is based on the popular open source FreeRTOS kernel. In additional to these frameworks, the open source community has rallied to add support for other popular AVR environments, including the widely known Arduino platform. For our blinky project, we’ll choose to use the Non-OS SDK to gain an understanding for programming a chip using it’s default application framework.

Development environment

For your development environment, I strongly suggest using the wonderful PlatformIO toolkit. PlatformIO provides a uniform environment for programming the Feather Huzzah, and many other microcontrollers. I’ll follow up this tutorial with a guide on configuring PlatformIO.

Non-OS SDK

In order for our application to run, the Non-OS sdk requires three things: a user_config.h header file, a user_main.c file and a function void user_init(). user_init is the default method executed when the chip starts. Here, we can initialize our timers, network connections, and other board features. To get started, create an empty user_config.h file, and a user_main.c file with the following.

#include "user_config.h"

void user_init(void) {
}

Controlling GPIOs

Pins on a microcontroller can be configured as either input or output. Controlling these pins require use of special memory locations known as hardware registers.

The Non-OS sdk provides macros to assist with this process. Unfortunately, the amount of macros for GPIO is overwhelming, and the documentation unclear. Instead of relying on these macros, we’ll be manipulating the hardware registers directly to configure our LED pin for output, and to blink its light.

For our purpose, we’ll require four hardware registers. These are defined in the SDK documentation as:

GPIO_OUT will tell us which pins are currently ON and outputting. GPIO_ENABLE instructs the chip to enable a specific pin for output. Once in output mode, GPIO_ENABLE_W1TS turns on specific pins, while GPIO_ENABLE_W1TS turns them off.

The SDK already provides aliases for these registers; however, to be more explicit, let’s define them ourselves in our user_config.h.

#define GPIO_OUT *(uint32_t *)0x60000300
#define GPIO_OUT_W1TS *(uint32_t *)0x60000304
#define GPIO_OUT_W1TC *(uint32_t *)0x60000308
#define GPIO_ENABLE *(uint32_t *)0x6000030C

Blinky

With everything set up, it’s time to get coding. The first thing we’ll do is add another #define in our header file for our pin. We’ll be using pin 0, which is wired up to a red LED on the Feather Huzzah. Ad the following to user_config.h

#define PIN 0

Before we can start blinking our LED, we’ll need to instruct the microcontroller to enable the pin for output. This means writing a 1 in the appropriate bit for the GPIO_OUT registry.

GPIO_ENABLE |= (1 << PIN);

Above, we directly enable output on our PIN through bit shifting. (1 << PIN) creates us a number with a 1 in the PIN position. Given our PIN = 0, it is equivalent to 0b00000001. The second part of the assignment is the |= operator. Known as bitwise OR, we can pass a 1 in the bit we want to turn on, while leaving the rest of the register bitmask unchanged. Therefore, if we had enabled another GPIO pin prior to our LED, our assignment operation would leave its bit unchanged.

Next, we need to define our function which will do the blinking. Above user_init(), define blinker.

void blinker(void *arg)
{
  if (GPIO_OUT & (1 << PIN)) {
    // set gpio low
    GPIO_OUT_W1TC |= (1 << PIN);
  } else {
    // set gpio high
    GPIO_OUT_W1TS |= (1 << PIN);
  }
}

On the first line, we use the GPIO_OUT register to check if our PIN is currently on. The bitwise & uses a bitmask to check the state of individual bits stored in another bitmask. It returns 1 if the corresponding bits are on and 0 if they are off. E.g 00100001 & 00000001 //true. If the bit is on or high, we set it low in our GPIO_OUT_W1TC register. Remember, this register was defined as:

Output disable register

Inserting a 1 into a bit in this register actually disabled the PIN. An important implementation detail to keep in mind. In the case where our PIN bit is off, we set it high by enabling the bit in the GPIO_OUT_W1TS register.

The last thing we need to do is run our blinker function on a timer. The Non-OS SDK comes with software and hardware timer options. For our application, we’ll use the software timer. Start by importing the required headers at the top of the file, and defining our timer.

#include "os_type.h"
#include "osapi.h"

static volatile os_timer_t blink_timer;

Next, extend our user_init function.

void ICACHE_FLASH_ATTR user_init()
{
  GPIO_ENABLE |= (1 << PIN);

  os_timer_setfn(&blink_timer, (os_timer_func_t *)blinker, NULL);
  os_timer_arm(&blink_timer, 1000, 1);
}

In os_timer_arm, we pass our timer as the first argument, followed by the timer delay in milliseconds. The last parameter enables running the timer repeatedly.

That’s it! Our user_main.c should look like the following.

#include "user_config.h"
#include "os_type.h"
#include "osapi.h"

static volatile os_timer_t blink_timer;

void blinker(void *arg)
{
  //Do blinky stuff
  if (GPIO_OUT & (1 << PIN)) {
    // set gpio low
    GPIO_OUT_W1TC |= (1 << PIN);
  } else {
    // set gpio high
    GPIO_OUT_W1TS |= (1 << PIN);
  }
}

void ICACHE_FLASH_ATTR user_init()
{
  GPIO_ENABLE |= (1 << PIN);

  os_timer_setfn(&blink_timer, (os_timer_func_t *)blinker, NULL);
  os_timer_arm(&blink_timer, 1000, 1);
}

Flash your Feather and admire your blinking LED!