Wednesday 30 November 2016

I2C with the ESP8266 and in General

The ESP8266 has no hardware support for I2C, however the Espressif SDK (version 2.0.0 non-os) exposes low level functions which implement the protocol robustly enough on a pair of GPIO pins through bit-banging. As a result of being done in software, the implementation does tie up a bit of processor time, and in addition, the functions block while they write or read, etc. Note also that this SDK version reportedly does not support clock-stretching nor being the slave device (neither of which I have tried).

As a demonstration, and to learn how to use this I2C functionality, I got the ESP8266 to talk to a HMC5883L compass module which I had successfully used with an Arduino previously.

Code example of talking to an I2C compass module here.


The SDK functions are as follows:


To initialise (this only needs to be done once, usually on boot):

  • i2c_master_gpio_init(), and then
  • i2c_master_init()


For the actual I2C communications you have these functions with which to indirectly manipulate the GPIOs and craft a datastream:

  • i2c_master_start()
  • i2c_master_writeByte(your_8_bits_of_data)
  • i2c_master_readByte(), simply clocks out 8 pulses on SCL and reads SDA on each rising edge then returns the read data as an 8 bit integer. Handling the state of the slave so that it is ready to have data clocked out of it is not handled by this function, however the datasheet of whatever slave device you are using will usually give details on its I2C  r/w addresses, how to configure internal registers and pointers - and thus how to ready the device to send data.
  • i2c_master_stop()


  • i2c_master_checkAck(), will return true if the slave ACKs, false otherwise. For almost all I2C devices, this will need to be called after a byte is written so that the ESP8266 produces a 9th clock pulse in which the slave has the opportunity to ACK or NACK. i2c_master_writeByte does not take care of this crucial part of writing a byte to a slave.


The following two functions are intended to be called only when reading data from the slave. Either an ACK or NACK is sent to the slave after receiving a packet; this is to signal whether more packets are going to be read by the master or that the master is done reading from the slave:

  • i2c_master_send_ack()
  • i2c_master_send_nack()

These are all quite straightforward functions that do as they say on the tin, however the functions relating to acknowledging packets required some investigation and probing on the oscilloscope to figure out.

Some problems that I ran into:


I initially had issues because the ESP8266 was not producing the 9th clock pulse which the HMC5883L expected when being written to, causing the two chips to go out of sync, hence the warning about needing to call i2c_master_checkAck.

Also, I had a nasty bug which was caused by ACKing the last byte that I intended to read from the HMC5883L instead of NACKing it. This would cause it to anticipate more clock pulses coming and believe that the ESP8266 was going to try to read from it, as such, it retained control of SDA, stopping the ESP8266 from controlling SDA, which is necessary for issuing a stop condition. As the ESP8266 tried to pull SDA high to signal a stop, the HMC5883L would sink all the current coming out of that GPIO pin, effectively being a short, however this didn't seem to stop or crash the ESP8266, and serial remained fine despite the serial indicator LED lighting up solid. The only noticeable effect other than the serial LED going solid was the numerical readings from the HMC5883L turning to gibberish. It took a long time to find the cause of this bug because it simply wouldn't appear unless the compass was rotated to certain orientations, otherwise all would work fine. Eventually putting the oscilloscope on revealed that the compass was hogging SDA; before that, I had suspected corruption in either the ESP8266 or the HMC5883L, or some code struggling with certain values being read, or even EMI, so I completely re-flashed the ESP8266, added pullup resistors on SDA and SCL and combed over the code - to no avail.

A third problem that I had, specific to the HMC5883L, was with the datasheet, which said that to read from the compass, the number of bytes to be read has to be sent in a second packet after the read address. This second packet specifying the number of bytes to be read was always NACKed by the compass and turned out to be unnecessary, and further, stopped data from being read.


Decoding an I2C transmission on an oscilloscope:



Above: three bytes being successfully written to the HMC5883L. SDA is in yellow on top, SCL on the bottom in blue. It can be seen that the slave is ACKing on the 9th clock pulses by pulling SDA low; notice that when it is the HMC5883L pulling SDA low, it goes slightly closer to 0V than when the ESP8266 is pulling it low, this is related related to the differing amount of current each can sink, possibly due to differing gate types/sizes.

Anyway, to read that I2C waveform above, keep foremost in mind that a high clock state indicates that SDA is stable and thus ready to be read, and that a low clock state indicates that SDA may now be changed, and that it is not stable because the signal may still be propagating or settling. At the start of the transmission, both SDA and SCL are high, then SDA is pulled low while SCL is high; this violates the cardinal rule of only changing SDA when SCL is low to transfer data, but it is done in I2C to indicate the beginning of a transmission. The stop condition is a mirror of this and is also notable for violating the rule - SDA is allowed to go high while SCL is already high.

So from beginning to end of the I2C transmission above:


  • Start condition

  • First byte: 00111100 (transmitted by the ESP8266 over eight clock pulses)
  • ACK from HMC5883L (one clock pulse)
  • SDA floats high as it is released before the next byte and after the ACK. Its state does not actually matter in this region (SCL is low)

  • Second byte: 00000001
  • ACK
  • SDA floats high

  • Third byte: 00100000
  • ACK
  • SDA floats high then is pulled low again to prepare for the stop condition



  • Stop condition


This particular captured transmission writes to the HMC5883L to configure it. The first byte is the compass' write address, which it acknowledges to indicate that it recognises that it is being talked to. The second byte is the address of the register inside the compass which we want to write to in order to configure it (the HM5883L moves its internal register pointer to point to this address). The third byte is the value we want to set this register to. This syntax for writing a register is specific to the HMC5883L, but I imagine that it is similar for other devices; once again, the datasheet is your friend - the purpose of this section was more to demonstrate how to decipher I2C.

An I2C read is similar:

  • Start condition
  • Master sends 8 bit read address
  • Slave acknowledges

  • Slave sends 8 bits (but it is the master that controls the clock)
  • Master acknowledges
  • Repeat previous two steps until last packet...

  • Slave sends 8 bits
  • Master does not acknowledge (NACK)
  • Stop condition


No comments:

Post a Comment