Close
0%
0%

ESP8266 Arduino Tutorial, Part 2

Power up your ESP8266 with Arduino!

Public Chat
Similar projects worth following
Now that we've covered the basics, let's dive into using the Arduino environment to control peripherals!

Take a look at Part 1 of this tutorial series:

ESP8266 SDK TutorialLearn how to use the ESP8266 SDK. This is what the pros use!
ESP8266 Lua/NodeMCU TutorialA look at the NodeMCU Lua interpreter for the ESP8266.  Learn how to get to Blinky!
ESP8266 Arduino TutorialUse the Arduino IDE to simplify development and get up to speed very quickly!

And here's the links to the other tutorials in Part 2:

ESP8266 SDK TutorialLooking at using the linker to get PWM, and the included I2C libraries
ESP8266 Lua/NodeMCU TutorialUsing PWM and I2C with Lua!
ESP8266 Arduino Tutorial (You are here)Using the Wire library for I2C, and AnalogWrite for fading!


Links to Part 3

ESP8266 SDK TutorialUsing MQTT to develop an IoT device
ESP8266 Lua/NodeMCU TutorialUsing the NodeMCU MQTT module to communicate with a cloud data service
ESP8266 Arduino TutorialWe use the simpler, more widely available HTTP protocol to log data to the cloud

Getting Help

If you run into trouble while following these tutorials, you have a few different options:

  • Ask in the discussion area below the article
  • Join the ##esp8266 channel on Freenode IRC and ping me (MrAureliusR) or ask anyone who is in there
  • Post on the ESP8266 Community Forums (note it can take a while to get a response!)
  • Send me a private message here on Hackaday

Arduino Part 2: PWM and I2C

What is PWM?

Pulse-width modulation (PWM) is a method to control the average voltage of a signal without using analog means, such as a digital-to-analog converter (DAC) or digital potentiometer. By producing a square wave and changing the duty cycle, we control the amount of power delivered. The duty cycle is the ratio between the time the signal is in the high state and the low state. Think of it like an integral -- we are changing the area under the curve, which is equivalent to power. Take a look at this diagram:

Square wave at 50% duty cycle

This is a plot of a square wave at 50% duty cycle. This means it spends half of its period (τ) in its high state, and the other half in its low state. In the case of this output, that means 1 V and 0 V respectively. The power transmitted is the integral of this plot, which is represented by the blue shading under the curve. Notice how that area is equal to the area during the low state; this is how we know it's at 50%. This has the effect of producing an average voltage of 500 mV. If we were to produce an analog output at 500 mV, and take the integral, it would be equal to the integral of this PWM output. This is extremely useful, as creating square waves is simple with a microcontroller, whereas creating analog voltages requires many extra components. 

What happens if we reduce that output to 30%?

Square wave with 30% duty cycle

Now the on state is only on for 30% of the time. Our average voltage has now dropped to 300 mV. We have reduced the power sent out of our microcontroller pin just by changing the duty cycle of a square wave! If you are into math, you can prove that the analog voltage and the square wave are equivalent:

T is the period in seconds, and sgn() is the sign function. It's a simple way to get a perfect square wave on a plot. The integral of 0.5 from 0 to 20 is exactly 10, and the integral of the square wave will be almost exactly 10 as well. They will get closer with a faster period, and with more cycles. If you were to integrate to infinity, they would be exactly the same.

This is a common technique to control things like LED brightness, servo position, and buzzers. We are going to use it to fade an LED in the tutorial section below.

A quick side note about brightness of LEDs and PWM. The way human eyes respond to light is non-linear. Especially when LEDs are near the limit of their brightness, adding more current...

Read more »

  • 1
    Using PWM with Arduino

    Alright, we have two goals in this tutorial, just like in the parallel SDK and Lua tutorials: we're going to learn how to use PWM to fade an LED, and we're going to use I2C to communicate with a chip. Make sure to read the details section first if these terms don't make sense to you; I go into a detailed description of how PWM and I2C work.

    PWM is very easy in the Arduino environment. Arduino has a built-in function called analogWrite() which creates a PWM output on pins which support it. Again, for all these examples I will be using an Adafruit HUZZAH Feather, but they will work just fine with any ESP board. I recommend avoiding the ESP-01 for development. Use something like the ESP-12F instead, it's much easier. On the Feather, we will be using GPIO 0 and 2, which are the two on-board LEDs. On other boards, just check the documentation to see what pins have LEDs on them. You can, of course, attach an external resistor and LED if you want.

    The analogWrite() function takes two parameters: the pin we want to use for PWM output, and the duty cycle value. The duty cycle is a 10-bit value, from 0-1023. To get a specific percentage, just multiply the percentage you want by 1024 (the number of values). If we wanted 30% duty cycle, for example, we would use:

    Note, however, that 30% duty cycle is not the same as 30% brightness. The relationship between current and brightness is non-linear in LEDs. This means most of the dynamic range of an LED's brightness is near the low end of the 10-bit scale. There are many ways to scale the output to make it look smoother to the human eye. We will take a quick look at this later on.

    We're going to use a very simple state machine to fade our LEDs up and down. The state machine can have two states: increasing or decreasing. The duty cycle increases from 0 to 1023. When it hits 1023 the state changes to decreasing, and it heads back down to 0. This loops indefinitely. It's one of the simplest ways to fade an LED! Let's take a look at the code.

  • 2
    The "Simple Fade"

    Our first test program will be using a very simple state machine algorithm, which, as described, simply keeps track of the fade direction and modifies the duty cycle as required.

    /*
     * This sketch is written by MrAureliusR (Alexander Rowsell)
     * It it released under the terms of the Mozilla Public License v2 (https://www.mozilla.org/en-US/MPL/2.0/)
     * It is part of the ESP8266 Tutorial Series on Hackaday! Make sure to read the accompanying article. 
     * 
     * We will be using the built-in analogWrite function to do our PWM for us!
     * 
     */
    
    enum {DECREASING = 0, INCREASING = 1};
    
    int dir = DECREASING, stepSize = 1, curStep = 0, led = 2, delayTime = 10;
    
    void setup() {
      Serial.begin(115200);
      pinMode(led, OUTPUT);
    }
    
    void loop() {
      if(curStep == 0) dir = INCREASING;
      else if(curStep >= 1024) dir = DECREASING;
    
      if(dir == INCREASING) {
        analogWrite(led, curStep);
        curStep += stepSize;
        if(curStep >= 1024) curStep = 1024;
      } else if (dir == DECREASING) {
        analogWrite(led, curStep);
        curStep -= stepSize;
        if(curStep <= 0) curStep = 0;
      }
      Serial.print("Current step: ");
      Serial.println(curStep);
      delay(delayTime);
    
    }

    To make things simple, we declare a few variables for things like the LED pin, the current duty cycle, the amount to change the duty cycle in each loop, and how long to delay at the end of the loop. I've used an enum instead of #defines for increasing and decreasing. This is a good habit to get into, but it doesn't really matter which you use for such a small program.

    We also start the serial output up, so we can check the progress of the fade, though this can be commented out and is mostly just for debugging. Even beginners should be able to grasp what is happening in this code. If you have questions or are confused, feel free to ask in the discussion below, or join us on Freenode IRC in ##esp8266.

    Next, we'll take a look at a little trick to make the fade look a bit smoother.

  • 3
    The "Smooth Fade"

    The human eye does not perceive light output linearly in relation to LED current. What this means is to get twice the perceived brightness, you often need to use about four times the current. LEDs function most efficiently below their max current, so making the most of the dynamic range we have is essential. Mike Harrison of mikeselectricstuff explains this really well in this video. The simplest way to do this is to take the square of the output we want. The square root of 1024 is 32, but I take the input up to 50 which gives us a bit more time at max brightness. Compare this with the previous sketch and see which one looks better to you!

    /*
     * This sketch is written by MrAureliusR (Alexander Rowsell)
     * It it released under the terms of the Mozilla Public License v2 (https://www.mozilla.org/en-US/MPL/2.0/)
     * It is part of the ESP8266 Tutorial Series on Hackaday! Make sure to read the accompanying article. 
     * 
     * We will be using the built-in analogWrite function to do our PWM for us!
     * 
     */
    
    enum {DECREASING = 0, INCREASING = 1};
    
    int dir = DECREASING, stepSize = 1, curStep = 0, outStep = 0, led = 2, delayTime = 50;
    
    void setup() {
      Serial.begin(115200);
      pinMode(led, OUTPUT);
    }
    
    void loop() {
      if(curStep == 0) dir = INCREASING;
      else if(curStep >= 50) dir = DECREASING;
    
      if(dir == INCREASING) {
        outStep = pow(curStep, 2);
        if(outStep >= 1024) outStep = 1024;
        analogWrite(led, outStep);
        curStep += stepSize;
        if(curStep >= 50) curStep = 50;
      } else if (dir == DECREASING) {
        outStep = pow(curStep, 2);
        if(outStep >= 1024) outStep = 1024;
        analogWrite(led, outStep);
        curStep -= stepSize;
        if(curStep <= 0) curStep = 0;
      }
      Serial.print("Current step: ");
      Serial.print(curStep);
      Serial.print("\tOut step: ");
      Serial.println(outStep);
      delay(delayTime);
    
    }

    Again we have serial debugging just so we can easily see where in the cycle we are. Now that we've mastered PWM, let's move on to the slightly more complex I2C serial bus. Make sure you've fully read the details section so you have a basic understanding of how the I2C bus works.

View all 4 instructions

Enjoy this project?

Share

Discussions

Slasscom Afka wrote 09/17/2022 at 02:24 point

Bad

  Are you sure? yes | no

Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates