I2C With the ESP8266-01 !? || Exploring ESP8266:Part 1

Hello all ! With this instructable I will be writing and documenting a series of projects mentioning and exploring the various hidden ( rather unexplored ) features of the tiny ESP8266-01 WiFi Module..

Did you know that the ESP8266-01 supports I2C communication? Yes it is true, this little module lying unattended in one corner of your electronics bench has the capability to support this amazing communication protocol. This module is mainly used to connect and provide WiFi connectivity to a project using the serial communication involving Tx and Rx pins of the module. But in reality this cheap module is in itself a very capable microcontroller that can be used as standalone projects as well.

In this instructable we will be exploring the I2C communication protocol and interfacing an OLED display to show some animations and custom data as well.

So without any further delay let's jump into some theory and finally a practical application showing the hidden talents of this little buddy!

Step 1: First: Checking the Datasheet

The main microcontroller of the ESP8266-01 module is Espressif's ESP8266EX which is pretty powerful as compared to the more popular microcontrollers on the arduino.

This little thing has the following specifications:

  1. CPU : Tensilica L106 32-bit processor
  2. 1 MB of programmable memory!
  3. A good range of communication protocols like: I2S,I2C, UART and SPI
  4. It also has a PWM feature and interestingly also an ADC!

Okay so enough of good talks.Does this acually support I2C? Yes,and a quick look through the data sheet confirms this feature as well.

In this step I have attached the snapshots of the sections of datasheet that clearly mentions the communication capability and the pins required for I2C connections.

I2C is a very powerful communication protocol and used just 2 pins to communicate:

  1. The SDA (this is the data pin)
  2. The SCL (or SCK in some cases, this is the clock pin)

Systems connected via I2C have a master slave kind of communication topology. Here we can have multiple masters and multiple slave devices as well. But how does a master device specifically communicate with a particular slave? Well here each device has a unique address of itself that can be used to access(or communicate) with that particular device. The device can be a output device, like the very popular OLED screens or it can also be an input device like the MPU6050 or a BMP180 sensor.

So coming back to the ESP8266, I have attached snapshots of the part of datasheet that mention I2C details. Here the main thing is that the SDA or the data pin is actually the GPIO2 pin which is available in the pinout of the WiFi module. What about the the SCL pin? Well, we only have the GPIO 0 pin left as an available pin so we can assign it as a SCL pin-more on this during the coding process.

I have also attached the datasheet for your reference

Step 2: Connecting Up the Modules

We now have a basic understanding of how I2C works and it's possible to implement the same in our ESP8266 module. In this step you can see a basic connection overview for the ESP8266 and the OLED display. As you can see that both module have a common power supply which ideally should be 3.3volts to 2.7volts at max as the ESP is a 3.3V compatible device and any higher voltage may potentially damage the board. But I have tested this board and does work at 5 volts logic though it gets more heated up, so let's not take any risk and stick to 3.3volts supply. here the GPIO2 is tied to SDA and the GOIP0 to SCK. The CH_PD is tied to Vcc to enable the module's functionality.

Step 3: Breadboard Setup

Now to test this setup, I have previously made a few mods and adapters so that I can easily program and fix the ESP8266 on to the breadboard. You know that ESP8266 is not a breadboard friendly module so I made a tiny little adapter that lets me plug the most important pins on to a bread board and access it conveniently.

I do have a instructable for that:

Breadboard Friendly Breakout Board for ESP8266-01: https://www.instructables.com/Breadboard-Friendly-...

You can go through this instructable to make your own little adapter in very less time.

Next up is programming: For this I have used the CH340G USB to serial converter which can communicate with the ESP via serial interface and upload codes accordingly.

I have also set this up on a bradboard,you can also buy those readymade CH340 programmer boards on which you can directly plug the ESP for programming. I personally wanted all of them to be on a breadboard so I made my choices.

In the first Image you can see the setup of the ESP along with the programmer setup.

In the next image, you can see the that I have soldered 2 wires underneath my ESP adapter to the GPIO pins which to the SDA and SCL of the OLED display. I have already loaded an animation code and it successfully displays those flawlessly.

What about the code you ask? Well, it's the next step

Step 4: The Coding..

To upload some test code is the next obvious thing to do and I thought of uploading the sample animations sketch that the Adafruit OLED library provides.

The link to the Library is: https://github.com/adafruit/Adafruit..

The code definitely needs to be modified in order for the ESP to work and communicate with the OLED-Here in the code we also specify GPIO as the SCK pin.

Here is a brief summary of the code, which for your reference is also attached in this step.


  #include <SPI.h>
  #include <Wire.h>
  #include <Adafruit_GFX.h>
  #include <Adafruit_SSD1306.h>
  #define SCREEN_WIDTH 128 // OLED display width, in pixels
  #define SCREEN_HEIGHT 64 // OLED display height, in pixels
  #define OLED_RESET     1 
  Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

  #define NUMFLAKES     10 // Number of snowflakes in the animation example

  #define LOGO_HEIGHT   16
  #define LOGO_WIDTH    16
  static const unsigned char PROGMEM logo_bmp[] =
  { B00000000, B11000000,
  B00000001, B11000000,
  B00000001, B11000000,
  B00000011, B11100000,
  B11110011, B11100000,
  B11111110, B11111000,
  B01111110, B11111111,
  B00110011, B10011111,
  B00011111, B11111100,
  B00001101, B01110000,
  B00011011, B10100000,
  B00111111, B11100000,
  B00111111, B11110000,
  B01111100, B11110000,
  B01110000, B01110000,
  B00000000, B00110000 };

  void setup() 
  {
  Wire.begin(2,0);
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  display.display();
  delay(2000); // Pause for 2 seconds  
  display.clearDisplay();
  display.setTextSize(1);
  display.setCursor(0, 0);
  display.setTextColor(WHITE);
  display.display();
  testdrawline();      // Draw many lines

  testdrawrect();      // Draw rectangles (outlines)

  testfillrect();      // Draw rectangles (filled)

  testdrawcircle();    // Draw circles (outlines)

  testfillcircle();    // Draw circles (filled)

  testdrawroundrect(); // Draw rounded rectangles (outlines)

  testfillroundrect(); // Draw rounded rectangles (filled)

  testdrawtriangle();  // Draw triangles (outlines)

  testfilltriangle();  // Draw triangles (filled)

  testdrawchar();      // Draw characters of the default font

  testdrawstyles();    // Draw 'stylized' characters

  testscrolltext();    // Draw scrolling text

  testdrawbitmap();    // Draw a small bitmap image

  // Invert and restore display, pausing in-between
  display.invertDisplay(true);
  delay(1000);
  display.invertDisplay(false);
  delay(1000);

  testanimate(logo_bmp, LOGO_WIDTH, LOGO_HEIGHT); // Animate bitmaps

  }
  void loop()
  {}
  void testdrawline() {
  int16_t i;

  display.clearDisplay(); // Clear display buffer

  for(i=0; i<display.width(); i+=4) {
  display.drawLine(0, 0, i, display.height()-1, SSD1306_WHITE);
  display.display(); // Update screen with each newly-drawn line
  delay(1);
  }
  for(i=0; i<display.height(); i+=4) {
  display.drawLine(0, 0, display.width()-1, i, SSD1306_WHITE);
  display.display();
  delay(1);
  }
  delay(250);

  display.clearDisplay();

  for(i=0; i<display.width(); i+=4) {
  display.drawLine(0, display.height()-1, i, 0, SSD1306_WHITE);
  display.display();
  delay(1);
  }
  for(i=display.height()-1; i>=0; i-=4) {
  display.drawLine(0, display.height()-1, display.width()-1, i, SSD1306_WHITE);
  display.display();
  delay(1);
  }
  delay(250);

  display.clearDisplay();

  for(i=display.width()-1; i>=0; i-=4) {
  display.drawLine(display.width()-1, display.height()-1, i, 0, SSD1306_WHITE);
  display.display();
  delay(1);
  }
  for(i=display.height()-1; i>=0; i-=4) {
  display.drawLine(display.width()-1, display.height()-1, 0, i, SSD1306_WHITE);
  display.display();
  delay(1);
  }
  delay(250);

  display.clearDisplay();

  for(i=0; i<display.height(); i+=4) {
  display.drawLine(display.width()-1, 0, 0, i, SSD1306_WHITE);
  display.display();
  delay(1);
  }
  for(i=0; i<display.width(); i+=4) {
  display.drawLine(display.width()-1, 0, i, display.height()-1, SSD1306_WHITE);
  display.display();
  delay(1);
  }

  delay(2000); // Pause for 2 seconds
  }

  void testdrawrect(void) {
  display.clearDisplay();

  for(int16_t i=0; i<display.height()/2; i+=2) {
  display.drawRect(i, i, display.width()-2*i, display.height()-2*i, SSD1306_WHITE);
  display.display(); // Update screen with each newly-drawn rectangle
  delay(1);
  }

  delay(2000);
  }

  void testfillrect(void) {
  display.clearDisplay();

  for(int16_t i=0; i<display.height()/2; i+=3) {
  // The INVERSE color is used so rectangles alternate white/black
  display.fillRect(i, i, display.width()-i*2, display.height()-i*2, SSD1306_INVERSE);
  display.display(); // Update screen with each newly-drawn rectangle
  delay(1);
  }

  delay(2000);
  }

  void testdrawcircle(void) {
  display.clearDisplay();

  for(int16_t i=0; i<max(display.width(),display.height())/2; i+=2) {
  display.drawCircle(display.width()/2, display.height()/2, i, SSD1306_WHITE);
  display.display();
  delay(1);
  }

  delay(2000);
  }

  void testfillcircle(void) {
  display.clearDisplay();

  for(int16_t i=max(display.width(),display.height())/2; i>0; i-=3) {
  // The INVERSE color is used so circles alternate white/black
  display.fillCircle(display.width() / 2, display.height() / 2, i, SSD1306_INVERSE);
  display.display(); // Update screen with each newly-drawn circle
  delay(1);
  }

  delay(2000);
  }

  void testdrawroundrect(void) {
  display.clearDisplay();

  for(int16_t i=0; i<display.height()/2-2; i+=2) {
  display.drawRoundRect(i, i, display.width()-2*i, display.height()-2*i,
  display.height()/4, SSD1306_WHITE);
  display.display();
  delay(1);
  }

  delay(2000);
  }

  void testfillroundrect(void) {
  display.clearDisplay();

  for(int16_t i=0; i<display.height()/2-2; i+=2) {
  // The INVERSE color is used so round-rects alternate white/black
  display.fillRoundRect(i, i, display.width()-2*i, display.height()-2*i,
  display.height()/4, SSD1306_INVERSE);
  display.display();
  delay(1);
  }

  delay(2000);
  }

  void testdrawtriangle(void) {
  display.clearDisplay();

  for(int16_t i=0; i<max(display.width(),display.height())/2; i+=5) {
  display.drawTriangle(
  display.width()/2  , display.height()/2-i,
  display.width()/2-i, display.height()/2+i,
  display.width()/2+i, display.height()/2+i, SSD1306_WHITE);
  display.display();
  delay(1);
  }

  delay(2000);
  }

  void testfilltriangle(void) {
  display.clearDisplay();

  for(int16_t i=max(display.width(),display.height())/2; i>0; i-=5) {
  // The INVERSE color is used so triangles alternate white/black
  display.fillTriangle(
  display.width()/2  , display.height()/2-i,
  display.width()/2-i, display.height()/2+i,
  display.width()/2+i, display.height()/2+i, SSD1306_INVERSE);
  display.display();
  delay(1);
  }

  delay(2000);
  }

  void testdrawchar(void) {
  display.clearDisplay();

  display.setTextSize(1);      // Normal 1:1 pixel scale
  display.setTextColor(SSD1306_WHITE); // Draw white text
  display.setCursor(0, 0);     // Start at top-left corner
  display.cp437(true);         // Use full 256 char 'Code Page 437' font

  // Not all the characters will fit on the display. This is normal.
  // Library will draw what it can and the rest will be clipped.
  for(int16_t i=0; i<256; i++) {
  if(i == '\n') display.write(' ');
  else          display.write(i);
  }

  display.display();
  delay(2000);
  }

  void testdrawstyles(void) {
  display.clearDisplay();

  display.setTextSize(1);             // Normal 1:1 pixel scale
  display.setTextColor(SSD1306_WHITE);        // Draw white text
  display.setCursor(0,0);             // Start at top-left corner
  display.println(F("Hello, world!"));

  display.setTextColor(SSD1306_BLACK, SSD1306_WHITE); // Draw 'inverse' text
  display.println(3.141592);

  display.setTextSize(2);             // Draw 2X-scale text
  display.setTextColor(SSD1306_WHITE);
  display.print(F("0x")); display.println(0xDEADBEEF, HEX);

  display.display();
  delay(2000);
  }

  void testscrolltext(void) {
  display.clearDisplay();

  display.setTextSize(2); // Draw 2X-scale text
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(10, 0);
  display.println(F("scroll"));
  display.display();      // Show initial text
  delay(100);

  // Scroll in various directions, pausing in-between:
  display.startscrollright(0x00, 0x0F);
  delay(2000);
  display.stopscroll();
  delay(1000);
  display.startscrollleft(0x00, 0x0F);
  delay(2000);
  display.stopscroll();
  delay(1000);
  display.startscrolldiagright(0x00, 0x07);
  delay(2000);
  display.startscrolldiagleft(0x00, 0x07);
  delay(2000);
  display.stopscroll();
  delay(1000);
  }

  void testdrawbitmap(void) {
  display.clearDisplay();

  display.drawBitmap(
  (display.width()  - LOGO_WIDTH ) / 2,
  (display.height() - LOGO_HEIGHT) / 2,
  logo_bmp, LOGO_WIDTH, LOGO_HEIGHT, 1);
  display.display();
  delay(1000);
  }

  #define XPOS   0 // Indexes into the 'icons' array in function below
  #define YPOS   1
  #define DELTAY 2

  void testanimate(const uint8_t *bitmap, uint8_t w, uint8_t h) {
  int8_t f, icons[NUMFLAKES][3];

  // Initialize 'snowflake' positions
  for(f=0; f< NUMFLAKES; f++) {
  icons[f][XPOS]   = random(1 - LOGO_WIDTH, display.width());
  icons[f][YPOS]   = -LOGO_HEIGHT;
  icons[f][DELTAY] = random(1, 6);
  Serial.print(F("x: "));
  Serial.print(icons[f][XPOS], DEC);
  Serial.print(F(" y: "));
  Serial.print(icons[f][YPOS], DEC);
  Serial.print(F(" dy: "));
  Serial.println(icons[f][DELTAY], DEC);
  }

  for(;;) { // Loop forever...
  display.clearDisplay(); // Clear the display buffer

  // Draw each snowflake:
  for(f=0; f< NUMFLAKES; f++) {
  display.drawBitmap(icons[f][XPOS], icons[f][YPOS], bitmap, w, h, SSD1306_WHITE);
  }

  display.display(); // Show the display buffer on the screen
  delay(200);        // Pause for 1/10 second

  // Then update coordinates of each flake...
  for(f=0; f< NUMFLAKES; f++) {
  icons[f][YPOS] += icons[f][DELTAY];
  // If snowflake is off the bottom of the screen...
  if (icons[f][YPOS] >= display.height()) {
  // Reinitialize to a random position, just off the top
  icons[f][XPOS]   = random(1 - LOGO_WIDTH, display.width());
  icons[f][YPOS]   = -LOGO_HEIGHT;
  icons[f][DELTAY] = random(1, 6);
  }
  }
  }
  }
                      

  1. Importing the Libraries that are used for initialising and setting up the OLED display
  2. Importing the Wire Library for the I2C communication.
  3. Setting up the width and height of the OLED
  4. Initialising GPIO 0 as the SDA pin
  5. Specifying the OLED address and setting up the text size and colour of the display
  6. The remaining part of the code can be found in the Adafruit library example. I have just added hose codes from there.

With everything in place, now we have to select the proper COM port and select generic ESP8266 as our target board.You can check the code snippets in this step.

Step 5: Making Things a Little Easier..

To make working with ESP8266-01 a little easier, I decided to make a sort of development board for the same. This little board includes the following:

  1. USB to serial programmer
  2. A microUSB port for programming
  3. A voltage regulator
  4. GPIO and power pins female header
  5. A switch to toggle between programming mode and running mode
  6. LED's to indicate data communication

With this is is way more easier to upload and test programs and I have made the pinout in such a way that I can plug the OLED directly into the female headers!

I am sure this will be very helpful for you and if you wish to make this well then you guessed it right, I have a instructable for this as well :)

ESP-O-One : Making Your Own ESP Development Board :https://www.instructables.com/ESP-O-One-Making-You...

Step 6: Final Words

Phew! I feel that knowing this cool feature opens up a lot of possibilities with this powerful and minimalistic board because we know that a lot of sensors and devices use I2C communication.

You can see that I have attached the OLED with my development board running the animations sketch and I hope that this information would help you in many ways to experiment with this board.

Feel free to share your feedbacks and suggestions in the comment section below and do watch the video at the beginning of the tutorial so as to get detailed information about this project and while you are there, don't forget to give that video a like and even consider subscribing! that would be awesome.

I will be documenting the later stages of this project and other capabilities of the ESP boards as mentioned earlier. Thanks again for going through my instructable and have a good day!