ESP8266 Output GPIO and Optimization

Like many microcontrollers, the ESP8266 has a slew of GPIO pins that can be used to interact with the outside world. Espressif provides a number of ways to interact with these pins, including the popular function gpio_output_set. When we were first getting oriented with the ESP, we utilized demonstration code by Tom Trebisky to learn how the GPIO worked. Tom’s code is probably the most basic scenario, useful for changing the GPIO on a pin-by-pin basis. Tom’s code begins with some arrays that store the Espressif SDK’s internal codes for the GPIO pins and their functions, as well as masking bits for use in his GPIO logic.

//Tom Trebisky's GPIO code
static const int bits[] = { 1, 2, 4, 8, 0x10, 0x20, 0x40, 0x80,
0x100, 0x200, 0x400, 0x800, 0x1000, 0x2000, 0x4000, 0x8000 };

static const int mux[] = {
PERIPHS_IO_MUX_GPIO0_U, /* 0 - D3 */
PERIPHS_IO_MUX_U0TXD_U, /* 1 - uart */
PERIPHS_IO_MUX_GPIO2_U, /* 2 - D4 */
PERIPHS_IO_MUX_U0RXD_U, /* 3 - uart */
PERIPHS_IO_MUX_GPIO4_U, /* 4 - D2 */
PERIPHS_IO_MUX_GPIO5_U, /* 5 - D1 */
Z, /* 6 */
Z, /* 7 */
Z, /* 8 */
PERIPHS_IO_MUX_SD_DATA2_U, /* 9 - D11 (SD2) */
PERIPHS_IO_MUX_SD_DATA3_U, /* 10 - D12 (SD3) */
Z, /* 11 */
PERIPHS_IO_MUX_MTDI_U, /* 12 - D6 */
PERIPHS_IO_MUX_MTCK_U, /* 13 - D7 */
PERIPHS_IO_MUX_MTMS_U, /* 14 - D5 */
PERIPHS_IO_MUX_MTDO_U /* 15 - D8 */
};

static const int func[] = { 0, 3, 0, 3, 0, 0, Z, Z, Z, 3, 3, Z, 3, 3, 3, 3 };

Looking at eagle_soc.h, the SDK’s header file pertaining to the CPU’s hardware registers and associated manipulation macros, we can see that the macros (created with #define) referred to in Tom’s mux[] are references to specific memory locations, and the constants in func[] refer to the register settings required to configure these registers as general-purpose I/O instead of more specific functionality like the ESP8266’s SPI or SD card I/O.

//Espressif's eagle_soc.h

#define PERIPHS_DPORT_BASEADDR 0x3ff00000
#define PERIPHS_GPIO_BASEADDR 0x60000300
#define PERIPHS_TIMER_BASEDDR 0x60000600
#define PERIPHS_RTC_BASEADDR 0x60000700
#define PERIPHS_IO_MUX 0x60000800

...

#define PERIPHS_IO_MUX_FUNC             0x13
#define PERIPHS_IO_MUX_FUNC_S           4

...

#define PERIPHS_IO_MUX_CONF_U (PERIPHS_IO_MUX + 0x00)
#define SPI0_CLK_EQU_SYS_CLK BIT8
#define SPI1_CLK_EQU_SYS_CLK BIT9
#define PERIPHS_IO_MUX_MTDI_U (PERIPHS_IO_MUX + 0x04)
#define FUNC_GPIO12 3
#define PERIPHS_IO_MUX_MTCK_U (PERIPHS_IO_MUX + 0x08)
#define FUNC_GPIO13 3
#define PERIPHS_IO_MUX_MTMS_U (PERIPHS_IO_MUX + 0x0C)
#define FUNC_GPIO14 3
#define PERIPHS_IO_MUX_MTDO_U (PERIPHS_IO_MUX + 0x10)
#define FUNC_GPIO15 3
#define FUNC_U0RTS 4
#define PERIPHS_IO_MUX_U0RXD_U (PERIPHS_IO_MUX + 0x14)
#define FUNC_GPIO3 3
#define PERIPHS_IO_MUX_U0TXD_U (PERIPHS_IO_MUX + 0x18)
#define FUNC_U0TXD 0
#define FUNC_GPIO1 3
#define PERIPHS_IO_MUX_SD_CLK_U (PERIPHS_IO_MUX + 0x1c)
#define FUNC_SDCLK 0
#define FUNC_SPICLK 1
#define PERIPHS_IO_MUX_SD_DATA0_U (PERIPHS_IO_MUX + 0x20)
#define FUNC_SDDATA0 0
#define FUNC_SPIQ 1
#define FUNC_U1TXD 4
#define PERIPHS_IO_MUX_SD_DATA1_U (PERIPHS_IO_MUX + 0x24)
#define FUNC_SDDATA1 0
#define FUNC_SPID 1
#define FUNC_U1RXD 4
#define FUNC_SDDATA1_U1RXD 7
#define PERIPHS_IO_MUX_SD_DATA2_U (PERIPHS_IO_MUX + 0x28)
#define FUNC_SDDATA2 0
#define FUNC_SPIHD 1
#define FUNC_GPIO9 3
#define PERIPHS_IO_MUX_SD_DATA3_U (PERIPHS_IO_MUX + 0x2c)
#define FUNC_SDDATA3 0
#define FUNC_SPIWP 1
#define FUNC_GPIO10 3
#define PERIPHS_IO_MUX_SD_CMD_U (PERIPHS_IO_MUX + 0x30)
#define FUNC_SDCMD 0
#define FUNC_SPICS0 1
#define PERIPHS_IO_MUX_GPIO0_U (PERIPHS_IO_MUX + 0x34)
#define FUNC_GPIO0 0
#define PERIPHS_IO_MUX_GPIO2_U (PERIPHS_IO_MUX + 0x38)
#define FUNC_GPIO2 0
#define FUNC_U1TXD_BK 2
#define FUNC_U0TXD_BK 4
#define PERIPHS_IO_MUX_GPIO4_U (PERIPHS_IO_MUX + 0x3C)
#define FUNC_GPIO4 0
#define PERIPHS_IO_MUX_GPIO5_U (PERIPHS_IO_MUX + 0x40)
#define FUNC_GPIO5 0

The first step to using a pin for any purpose is to use these registers and function values in order to configure the ESP to do what we want. Espressif provides a macro, PIN_FUNC_SELECT, that takes in the appropriate memory location and function and configures the pin for us.

PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U, FUNC_GPIO13);

Essentially, at compile time, this code that instructs GPIO13 to be configured as a GPIO pin is substituted with a far more complex block of code.

do {
	WRITE_PERI_REG(PERIPHS_IO_MUX_MTCK_U,
		READ_PERI_REG(PERIPHS_IO_MUX_MTCK_U)
		&  (~(PERIPHS_IO_MUX_FUNC<<PERIPHS_IO_MUX_FUNC_S))
		|( (((FUNC_GPIO13&BIT2)<<2)|(FUNC_GPIO13&0x3))<<PERIPHS_IO_MUX_FUNC_S) );
    } while (0);

Of course, the whole purpose of providing these macros in the first place is so that you don’t need to do the heavy lifting when configuring a pin to blink a light, as Tom does in his code. Memory manipulation is hard, close to the metal stuff. This example demonstrates a lot of the bitwise manipulation you have to do when you want to flip specific bits without touching other ones. It’s a lot of fun for me since I enjoy the feeling of manipulating computer hardware by manually flipping the bits, but it’s a bit of an acquired taste. When you’re trying to solve problems efficiently, even like configuring a piece of hardware with its memory register as in the above example, almost-unreadable code sometimes makes a lot of sense.

Breaking down the PIN_FUNC_SELECT macro, we can see exactly how the pin function configuration works:

  1. Read the register associated with GPIO13
  2. Take a value representing the bits that can be set to configure the pin and shift it to line up with the register, PERIPHS_IO_MUX_FUNC<<PERIPHS_IO_MUX_FUNC_S (which evaluates to 0b000100110000), and invert it into the mask 0b111011001111
  3. Bitwise AND the mask with the register’s value to clear the function configuration bits, giving us a blank slate without touching bits that don’t have to do with the function configuration
  4. Interpret the provided function, whose max value can be 7 (or 0b111) by isolating the most significant bit (with a bitwise AND) and moving it to the left two bits, then combining it with the two least significant bits (with a bitwise OR) before moving the whole thing over 4 bits to line it up with the empty spaces in the mask
  5. Bitwise OR the configuration mask with the actual configuration data, and then writes it to memory.

In case you’re wondering, WRITE_PERI_REG(addr, val) is just a nice-looking way to set a pointer referring to the location “addr” to the value “val.” It just substitutes in a line of code that turns the call into pointer magic.

#define WRITE_PERI_REG(addr, val) (*((volatile uint32_t *)ETS_UNCACHED_ADDR(addr))) = (uint32_t)(val)

Further exploration tells me that the ETS_UNCACHED_ADDR macro doesn’t actually do anything, so the above line of code is literally just casting a memory location as a pointer.

With the first step out of the way, we can see how easy it is to use gpio_output_set to set a pin high or low. Looking back at Tom’s code, we can see that he’s accepting a single pin as input, grabbing the corresponding mask, and then feeding it into the arguments of the function.

void
eio_high ( int gpio )
{
	register int mask = bits[gpio];

	gpio_output_set(0, mask, mask, 0);
}

void
eio_low ( int gpio )
{
	register int mask = bits[gpio];

	gpio_output_set ( mask, 0, mask, 0);
}

So, what do each of these arguments do? According to the SDK API Guide:

void gpio_output_set(
	uint32 set_mask,
	uint32 clear_mask, 
	uint32 enable_mask,
	uint32 disable_mask

	)

Set_mask sets high output, clear_mask sets low output, enable_mask enables the corresponding pin for use as an output, and disable_mask enables the corresponding pin for use as an input. This is where things get a little hairy, as the actual SDK code is not open source, so we have to do a bit of detective work to figure out what’s happening. These arguments actually correspond to four memory registers associated with the ESP8266’s GPIO. To figure out what’s going on here, let’s take a look at Espressif’s Interface GPIO Manual. It turns out, there are at least six 16-bit registers associated with the general-purpose output:

  1. GPIO_ENABLE, a status register indicating which pins are enabled as outputs. If a pin’s bit isn’t high here, the ESP considers it an input
  2. GPIO_ENABLE_W1TS, a register where writing 1s sets the corresponding bits of the GPIO_ENABLE register to high
  3. GPIO_ENABLE_W1TC, a register where writing 1s sets the corresponding bits of the GPIO_ENABLE register to low
  4. GPIO_OUT, a status register indicating the values driven to outputs
  5. GPIO_OUT_W1TS, a register where writing 1s sets the corresponding sets bits of the GPIO_OUT register to high
  6. GPIO_OUT_W1TS, a register where writing 1s sets the corresponding clears bits of the GPIO_OUT register to low

Now we can come to understand what is actually happening when gpio_output_set is called. The function at the very least performs four writes to memory, and those writes will need to respect the current contents of memory. In eagle_soc.h, Espressif provides a function macro, GPIO_REG_WRITE, and additional macros designed to simplify calls to WRITE_PERI_REG when GPIO is concerned.

#define GPIO_REG_READ(reg)            READ_PERI_REG(PERIPHS_GPIO_BASEADDR + reg)
#define GPIO_REG_WRITE(reg, val)      WRITE_PERI_REG(PERIPHS_GPIO_BASEADDR + reg, val)
#define GPIO_OUT_ADDRESS              0x00
#define GPIO_OUT_W1TS_ADDRESS         0x04
#define GPIO_OUT_W1TC_ADDRESS         0x08

#define GPIO_ENABLE_ADDRESS           0x0c
#define GPIO_ENABLE_W1TS_ADDRESS      0x10
#define GPIO_ENABLE_W1TC_ADDRESS      0x14

So if we were to reconstruct the code that comprises gpio_output_set, it would look something like this:

void gpio_output_set(uint32 set_mask, uint32 clear_mask, uint32 enable_mask, uint32 disable_mask
)
{
	GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, GPIO_REG_READ(GPIO_OUT_ADDRESS) | set_mask);
	GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, ~GPIO_REG_READ(GPIO_OUT_ADDRESS) | clear_mask);
	GPIO_REG_WRITE(GPIO_ENABLE_W1TS_ADDRESS, GPIO_REG_READ(GPIO_ENABLE_ADDRESS) | enable_mask);
	GPIO_REG_WRITE(GPIO_ENABLE_W1TC_ADDRESS, ~GPIO_REG_READ(GPIO_ENABLE_ADDRESS) | clear_mask);
}

It should be very clear at this point that gpio_output_set is a generalized function designed to do any major GPIO work a user trying to avoid memory manipulation may need. It may look clean but doesn’t lend itself to practical applications where speed is a factor. Using it necessitates entering into the function itself, which comes with overhead. Additionally, unless Espressif is detecting whether a mask is 0 (itself necessitating overhead via branching code), every gpio_output_set call performs several useless memory writes. You may be asking, useless how?

If you’re designing a substantial project or product that uses the ESP8266, you likely already know which pins you are using for I/O. In our stepper motor project, we know that we have specific pins for enabling the motor, motor direction, microstepping and stepping. If we manually enable only those pins during our program’s initialization, suddenly those calls to write to the enable addresses (and related bitwise math) are totally useless. We intended to drive the stepper motor by toggling the stepping pin with a resolution of 5 microseconds using Tom’s eio_high and eio_low functions, and the overhead of hopping into his function, and then hopping into Espressif’s slow GPIO function caused a plethora of watchdog timer resets. Drivers are all about efficiency, especially in embedded systems where a greedy driver can cause a critical failure. Don’t take my word for it, however. Other people far more experienced than I have proved beyond a measure of a doubt that controlling GPIO through a special function is too slow. Nerd Ralph, working on the Arduino-based implementation of the ESP8266 SDK, replaced the digitalWrite function with inline memory manipulation and managed to squeeze 4Mbps communication out of it.

If that weren’t enough, Espressif provides a macro-based way to manipulate singular GPIO pins, confusingly named GPIO_OUTPUT_SET. It takes a pin number and the binary value to drive the pin. According to the Interface GPIO manual,

GPIO_OUTPUT_SET(GPIO_ID_PIN(12), 1);

is functionally equivalent to

GPIO_REG_WRITE(GPIO_ENABLE_W1TS_ADDRESS, GPIO_REG_READ(GPIO_ENABLE_ADDRESS) | 0x1000);
GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, GPIO_REG_READ(GPIO_OUT_ADDRESS) | 0x1000);

Of course, a nice person from the internet with an oscilloscope proved that this macro was still too slow compared to just manually writing to the registers. Besides, it only works on a pin-by-pin basis. What happens when, such as the case of our hobby servo controller, we will potentially be flipping up to 4 pins simultaneously? Using GPIO_REG_WRITE with the correct mask is the only option.

Our GPIO code is still in progress. For v0.3.0 Pulled Pork, we wanted to be completely weaned off of Tom’s demo GPIO code in a way that was efficient enough that the motor driver interrupts could run at the intended resolution of 5 microseconds. For the most part, we succeeded. Having moved on to some internal product application demonstrations, we’ve discovered that the hobby servo controller still has the occasional watchdog timer reset, and I’m pretty sure I know how to make the code more efficient.

Pulled Pork’s GPIO code relies on this macro:

#define GPIO_MASK_WRITE(mask)	{			\
	GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, (mask));	\
	GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, ~(mask));	\
	}

Doing both of these writes in one macro is actually really unnecessary. All of the stepper motor driver’s writes to the GPIO registers outside of the initialization are either setting or clearing a bit. Doing both a set and a clear is just a waste of a few cycles. This problem is exacerbated in the hobby servo array driver, which features an embarrassingly inefficient for loop.

//OpenMYR's quad_servo_driver.c
void step_driver ( void )
{
	ticks++;
	if(ticks <= SERVO_TICKS_CEILING)
	{
		int n = 0;
		unsigned int gpio_output = 0;
		for(n; n < 4; n++)
		{
			gpio_output += (ticks <= high_ticks[n]) * gpio_output_mask[n];
		}
		GPIO_MASK_WRITE(gpio_output);
	}
	else if(ticks == PULSE_LENGTH_TICKS)
	{
		ticks = 0;
		int current_motor = 0;
		GPIO_MASK_WRITE(GPIO_ALL_MASK);
...
	}
}

The irst branching block only sets the motor signal pins low to indicate the end of a duty cycle, and the else if block only sets sets all the signal pins high at the beginning of the pulse. Doing an unnecessary extra memory write is really just icing on the cake; the for loop is way too much overhead. All we’re doing is taking the boolean value of whether a motor is still within its duty cycle and then multiplying it by the mask associated with that pin and adding it to the mask to be sent to GPIO_MASK_WRITE. However, assembling the entire mask to be sent to a clear-only line of code can be done in one line with just as many additions and multiplications, by detecting whether the duty cycle is done instead of ongoing. What a waste!

I hope you’ve gained some knowledge of how the ESP SDK handles GPIO, and how some resourcefulness, Google-fu and digging in the include files can yield much better results. I still have a lot of experimentation to do. The stepper motor board has tri-state LEDs on it, and I need to learn how the pullup resistor registers work in order to use them. Additionally, one feature we want to support is external peripheral I/O, so I’m going to need to learn all about input and the input interrupts as well. I expect that I’ll be writing a post about these topics in the future!

Posted in Development, ESP8266, Reference | Tagged , , , , , , , | Leave a comment

v0.3.0 Pulled Pork Released

Download it here!

This release contains the improvements for milestones 0.2.0 and 0.3.0.

New Features

  • Built-in HTTP configuration and control page
  • Support for up to 4 servos on one servo driver
  • Stepper driver supports 32-bit microstepping via JSON (via TCP) and theoretically UDP packet
  • The bounds of the servo duty cycle are now changeable via UDP packet

Improvements

  • GPIO code foregoes using Espressif’s gpio_output_set in favor of much faster direct register manipulation, allowing us to phase out code based on internet demo code

Bugfixes

  • Stepper motor driver sanitizes negative wait_time input
  • Fixed a race condition that caused stops to crash the stepper motor driver
  • Fixed a condition where some devices couldn’t connect to the driver’s softAP broadcast mode
  • The 5us motor driver timer resolution no longer causes a watchdog timer reset

Other notes

  • Brushed motor driver has been removed from the builds since it is low priority and will not be fully implemented for a long, long time
  • The singular servo driver (servo_driver.c) has been depreciated and removed from the builds since it has been replaced in the product line by the quad servo driver
  • Improvements to the stepper driver hardware design have eliminated a crash or two involving the WiFi controller
  • The built-in website control page only supports sending commands to the first servo motor in the 4-servo array, and uses outdated values for the calculation of the command values
Posted in Development, OpenMYR, Software Releases | Tagged , , , , | Leave a comment

Solder or no Solder

It came time to attach an ESP to the newly-arrived PCB for our stepper motor. It was only a prototype run, so we had very few boards and couldn’t afford waiting for more to come. From there the issue arose that if the ESP or the board were flawed, both would be lost since castellations don’t like being unsoldered. It would also be hard (if not impossible) to test and measure specific leads with the ESP in circuit.

That is when I got the idea to solder 16 leads onto our board to hold the ESP in place with only the spring tension in the wires.

DSC_0074

Cutting the wires to size and stripping the insulation ended up being much harder than the actual soldering. Once everything was in place, I simply bent the wires in a little and wedged the ESP in from the bottom at an angle. After coercing a couple of misaligned wires into place with the help of a jeweler’s screwdriver, everything was set. In the end it worked perfectly, no flaws. It reminded me a bit of a sculpture made by Arthur Ganson.

I’m not sure I would suggest someone to do this to a project board since the wires are soldered onto surface mounted pads. This makes it much easier to rip off traces and destroy your board, but in a pinch, in works!

Posted in Development, ESP8266, Hack | Tagged , , , , | 2 Comments

Configuring the Raspberry Pi as an ESP8266 Development Environment

The Raspberry Pi is an advantageous platform for developing ESP8266 software, as it can power the ESP with its 3.3V pins, communicate directly with the ESP via its serial GPIO pins, and natively runs the Xtensa cross-compiler on its Linux-based Raspbian OS. We first began using it out of convenience, as it was the only thing that we had on-hand that could power and communicate with the ESP-12. However, we have found that we really like the workflow of coding, compiling and directly pushing firmware onto the ESP.

The first step to developing on a Raspberry Pi is to enable the Pi to communicate with the ESP8266 via its two serial pins. GPIO 14 and 15 are the Pi’s serial port, 14 being transmit and 15 being receive. A fresh installation of Raspbian needs a tiny bit of tweaking to use the port, however. The Wheezy-based version of Raspbian sends boot information to the serial port during the Pi’s startup, and this functionality must be disabled. To do so, open “cmdline.txt” in /boot using superuser privileges (ie “sudo nano /boot/cmdline.txt” in the terminal) and remove this segment:

console=ttyAMA0,115200 kgdboc=ttyAMA0,115200

Raspbian’s default configuration automatically provides a serial login service using getty over the serial port, which must be disabled in both Wheezy and Jessie. To disable this, use the command “sudo raspi-config” in the terminal. It will open a configuration program.

advanced

Select “Advanced” (option 9 on the main menu) and then “Serial” (option 8 on the Advanced menu). It will ask if the Raspberry Pi should provide serial login, to which you should select “no.”

raspi-config-2

raspi-config-3

You may need to use the “expand filesystem” option while you are in raspi-config (option 1 on the main menu). A freshly-written Raspbian SD card installation is likely not taking full advantage of the space provided to it, making it difficult to install new packages. If you have space issues in this tutorial, run this option before buying a new SD card!

The main menu of raspi-config

Select “Finish” on the main menu and allow the Pi to reboot.

Now you’ll need software to test the serial connection with. Open a terminal and install minicom (“sudo apt-get install minicom”). Connect the transmit pin of your ESP to the Pi’s receive pin and vice versa. Now, in the terminal, open a serial terminal:

minicom -D /dev/ttyAMA0

You should be able to send and receive communications with the ESP8266.

The next portion of the getting started guide covers the toolchain.

It is a good idea to start with updated repositories and upgrading the currently installed software:

sudo apt-get update
sudo apt-get upgrade

Once the update is finished, reboot the Pi. When the system is back on, open a terminal and install the required dependencies for the toolchain:

sudo apt-get install make autoconf automake libtool gcc g++ gperf flex bison texinfo gawk ncurses-dev libexpat-dev python python-serial sed git unzip bash libtool-bin

We are now ready to install the toolchain. You can put install this anywhere, but a good place for it would be the /opt directory:

cd /opt
sudo git clone –recursive https://github.com/pfalcon/esp-open-sdk.git
sudo chmod 777 -R esp-open-sdk
sudo chown -R YourRPiUsername esp-open-sdk/
cd esp-open-sdk
make

This will take approximately forever. Once it finishes, the final piece of the puzzle is adding the compiler to your path variable. We’ll put it at the very end of your ~/.profile:

PATH=”/opt/esp-open-sdk/xtensa-lx106-elf/bin:$PATH”

Placing the Xtensa Tools in the PATH Variable

Reboot the Pi and open a terminal. Check to make sure the PATH variable contains the xtensa toolchain by using the following command:

$PATH

The path to the compiler should be listed in the output of the PATH variable. You can now compile ESP8266 firmware and flash it to the device. You can test this by pulling the OpenMYR code and using the makefile while your ESP is connected to the serial and booted into write mode:

git clone https://github.com/OpenMYR/IoT_Motors.git
cd IoT_Motors
make flash-stepper

Finally, unless you’re all about terminal-based editors, you will want a text editor that is condusive for programming. The Pi’s built-in GUI text editors aren’t that useful, but we found that SciTE was perfect for our needs. It feels a lot like Notepad++ and made coding on the Pi a lot saner. It can be installed with a simple apt-get command:

sudo apt-get install scite

Posted in ESP8266, Raspberry Pi, Raspbian, Reference | Tagged , , , , | 2 Comments

Sacrificing Our Raspberry Pi to Our Stepper Motor Prototype

On Thursday we excitedly hurried into our workspace with a newly-arrived package. We had just received a box from Chris, our hardware engineer, containing two prototype stepper motor controller boards with a pair of motors. While we have been driving a servo with a bread-boarded controller, we had not yet been able to test our stepper motor code with actual hardware. After attaching an ESP-12 to the board and verifying that the chip was functioning correctly, we connected our Raspberry Pi’s UART pins to the TX\RX of the ESP and loaded our firmware onto the device.

It didn’t work. Our stepper motor wouldn’t move. We realized that the pin assignments we had used in our configuration header file was out of date and we were stepping to the wrong pin! We corrected our settings, recompiled and re-flashed the ESP.

It didn’t work. Our motor sputtered and swerved wildly while executing commands, sometimes making massive 45 degree jumps before alternating direction with every step, preventing any meaningful movement. The number of steps made didn’t correspond with the commands we had sent. At this point, Kyle had decided that there must have been an issue with the power supplied to the board, theorizing that the ground noise of the 12V power supply was interfering with the motor. After using a different power supply and getting the same results, we decided to make some diagnostic changes to the code to see if we were sending the correct signals to the correct pins. We recompiled and attempted to re-flash.

It didn’t work. In fact, it wouldn’t even flash the ESP. At first we thought the ESP was broken, but it would still boot and respond to our commands. We noticed that we weren’t seeing any serial communications with the device using minicom. We hooked up our servo prototype. No serial activity, no flashing ability. Horrified, we realized that we had fried the UART of our Raspberry Pi. We have no idea how, but we destroyed our development environment’s ability to deploy code!

After wallowing in crushing defeat, we finally managed to contact our hardware engineer. He told us we wired the motor up wrong. The board has 4 ports: A1, A2, B1, B2. The stepper motor has two wire pairs, and we assumed one pair was to get A and the other was to use B. It turns out that each pair needs an A and a B. Oops. We fixed the wiring and our stepper motor code and controller worked like a charm!

We now have a working prototype for our flagship WiFi motor, with servo motor controller prototypes on the way. Too bad we need to figure out another way to deploy firmware. Perhaps we can use other GPIO pins as a fake UART? Or maybe we’ll just bite the bullet and get FTDI USB-to-3.3V-serial converters.

Posted in Development, OpenMYR | Tagged , , , , | Leave a comment