This is an amendment to the previous ESP8266v7 memo. More specifically, the I/O pins don’t behave exactly as described by internet sources.
A little bit of background: I was presented with a challenge to read from an analog sensor and send the reading through wifi network to the cloud. This can be accomplished easily using SIM800A GPRS module and data connection but not so using the ESP8266.
ESP8266’s I/O problems
There are two main problems with the ESP8266 in dealing with analog inputs. The chip operates on 3.3V logic instead of the common 5V. It appears to have sufficient over-voltage protection and can withstand 5V inputs without visible problems. A straightforward voltage divider circuit to linearly scale 5V down 3.3V range would have been the answer if not for the second problem…
All GPIO pins of the ESP8266 chip are digital.
ESP8266v7 and v12 have a new ADC pin which can read analog inputs from 0-1V range. That’s a far too small operation range to be useful and only one analog pin is nowhere near enough in a real application.
Off the top of my head, there are three possible approaches.
The first approach is to use a voltage divider for scaling and an analog multiplexer to increase the number of analog inputs. This is done in the article here at the cost of digital pins.
The ESP8266 has TX/RX pins for serial, SCL/SDA pins for I2C and MISO/MOSI pins for SPI connection. These interfaces allow ESP8266 to communicate with an external device that has better analog I/O pinout; a device such as an Arduino or an RF transceiver. This second approach does not use ADC pin or any complicated circuitry.
The final, “least sane” method that I actually tried was software PWM. I let the Arduino read analog inputs and output the reading to one of its PWM digital pins using analogWrite() function. This “encoded” signal can then be connected to any GPIO pins of the ESP8266 and the ESP8266 can reconstruct the analog signal from the encoded PWM input using pulseIn() function. This technique uses up only one of the ESO8266’s GPIO pins and it also requires no extra circuitry.
The downside of this third approach is the fact that pulseIn() is a synchronous operation that stalls the program for 20ms in order to wait for a pulse (polling, learn more about it here). Too many of those will take up all processing time and stall the program indefinitely. Using interrupts is naturally a possible alternative but asynchronous programming is never for the faint of heart.
Pin test data
Below is the measurement data for all ESP8266 pins in the following cases:
- float aka not connected to pull-up/pull-down resistor by pinMode().
- set to HIGH by digitalWrite().
- set to LOW by digitalWrite().
- set to 300 by analogWrite().
- GPIO0: 4.57 (float), 3.4 (digital HIGH), 0.88 (digital LOW), 1.63 (analog 300)
- GPIO1/TXD: 5 (float), 3.3 (digital HIGH), 0.00 (digital LOW), 0.98 (analog 300)
- GPIO2: 0.97 (float), 3.3 (digital HIGH), 0.04 (digital LOW), 1 (analog 300), 1.95 (analog 600)
- GPIO3/RXD: 5 (float), 3.3 (digital HIGH), 0.00 (digital LOW), 1.09 (analog 300)
- GPIO4: 3.3 (float), 3.3 (digital HIGH), 0.00 (digital LOW), 0.98 (analog 300)
- GPIO5: 0.00 (float), 3.3 (digital HIGH), 0.00 (digital LOW), 0.98 (analog 300)
- GPIO12: 0.05 (float), 3.3 (digital HIGH), 0.00 (digital LOW), 0.98 (analog 300)
- GPIO13: 0.05 (float), 3.3 (digital HIGH), 0.00 (digital LOW), 0.98 (analog 300)
- GPIO14: 0.05 (float), 3.3 (digital HIGH), 0.00 (digital LOW), 0.98 (analog 300)
- GPIO15: 0.00 (float), 3.25 (digital HIGH), 0.00 (digital LOW), 0.98 (analog 300)
- GPIO16/XPD: 0.05 (float), 3.3 (digital HIGH), 0.00 (digital LOW), 0.98 (analog 300)
- GPIOA0/ADC: 0.05 (float), 0.05 (digital HIGH), 0.05 (digital LOW), 0.05 (analog 300)
Additional tests for ADC pin’s analog input capability using analogRead(0) operation:
- 9 (0.00V GND)
- 60 (0.05V GPIO12 float)
- 80 (0.05V ADC float)
- 222 (3.3V GPIO4 float)
- 1024 (3.3V GPIO5 digital HIGH)
- 1024 (5V VCC)
Things to know
analogRead(0) and analogRead(A0) are interchangeable.
digitalRead(0) and digitalRead(A0) are not interchangeable. The first one will read from GPIO0 while the second will read from ADC pin.
All GPIOxx pins can output analog in the form of PWM signal.
GPIO4 has a float voltage of 3.3V when read by a multimeter. This is not the same as the 3.3V output by GPIOxx pins when they are set to digital HIGH. When GPIO4 (float) is read by ADC pin, it outputs a value of circa 220 instead of 1024. This suggests the floating value is not clearly defined and can vary from one observer to another.
As such, do not connect a floating pin to other devices. GPIO0, for example, has a floating value of 5V (likely connected to the power adapter) and this over-voltage can easily damage 3.3V devices.