Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
100% found this document useful (4 votes)
677 views

Cython A Guide For Python Programmers

This document is a book about mastering basic peripherals of the ESP8266 microcontroller. It covers topics such as blinking an LED, dimming an LED with PWM, controlling an RGB LED, displaying messages on an OLED display, reading buttons, using the analog to digital converter to read a trimpot, using timers and the real-time clock, and reading temperature and humidity with a DHT11 sensor.

Uploaded by

Cristh Juarez
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
100% found this document useful (4 votes)
677 views

Cython A Guide For Python Programmers

This document is a book about mastering basic peripherals of the ESP8266 microcontroller. It covers topics such as blinking an LED, dimming an LED with PWM, controlling an RGB LED, displaying messages on an OLED display, reading buttons, using the analog to digital converter to read a trimpot, using timers and the real-time clock, and reading temperature and humidity with a DHT11 sensor.

Uploaded by

Cristh Juarez
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 120

Erwin Ouyang

Hands-On ESP8266:
Mastering Basic
Peripherals
Developing with Arduino and C/C++
by Examples
Hands-On ESP8266: Mastering
Basic Peripherals
Developing with Arduino and C/C++ by Examples

Erwin Ouyang
Build your own dreams, or someone else will hire you to
build theirs.

— FARRAH GRAY
Preface

Rapid advances in IoT technology demand a lot of devices to be con-


nected to the internet. To design such devices, we usually need mi-
crocontrollers and network modules (Ethernet or WiFi). ESP8266 is
a low-cost microcontroller that already has an on-chip WiFi module.
This WiFi module makes the ESP8266 as a popular microcontroller
for IoT device development. With ESP8266, we do not need external
the WiFi module, so we can significantly reduce the Bill of Material
(BOM) cost of the IoT product. This book is primarily written as a
hands-on material rather than theory. This book covers the basic of
ESP8266 peripherals and the popular sensors and actuators. In this
book, the Arduino library is used for programming the ESP8266, and
the NodeMCU development board is used.

Erwin Ouyang
October 2018

i
Contents

Preface i

Contents ii

Listings v

1 Introduction 1
1.1 ESP8266 . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.2 ESP-12E . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.3 NodeMCU . . . . . . . . . . . . . . . . . . . . . . . . 4
1.4 Driver Installation . . . . . . . . . . . . . . . . . . . . 5
1.5 ESP8266 Library Installation . . . . . . . . . . . . . . 7
1.6 Create the First Program . . . . . . . . . . . . . . . . 7
1.7 Development Breadboard . . . . . . . . . . . . . . . . 10
1.8 Summary . . . . . . . . . . . . . . . . . . . . . . . . . 11

2 Blink an LED 15
2.1 LED Circuit . . . . . . . . . . . . . . . . . . . . . . . 15
2.2 Digital Output . . . . . . . . . . . . . . . . . . . . . . 16
2.3 Example Program . . . . . . . . . . . . . . . . . . . . 18
2.4 Summary . . . . . . . . . . . . . . . . . . . . . . . . . 20

3 Dim an LED using PWM 21

ii
CONTENTS iii

3.1 PWM . . . . . . . . . . . . . . . . . . . . . . . . . . 21
3.2 Analog Output . . . . . . . . . . . . . . . . . . . . . . 23
3.3 Example Program . . . . . . . . . . . . . . . . . . . . 23
3.4 Summary . . . . . . . . . . . . . . . . . . . . . . . . . 25
3.5 Coding Challenge . . . . . . . . . . . . . . . . . . . . 25

4 Control an RGB LED 27


4.1 RGB LED . . . . . . . . . . . . . . . . . . . . . . . . 27
4.2 Example Program . . . . . . . . . . . . . . . . . . . . 28
4.3 Summary . . . . . . . . . . . . . . . . . . . . . . . . . 30
4.4 Coding Challenge . . . . . . . . . . . . . . . . . . . . 30

5 Display a Message on OLED Display 31


5.1 OLED Display . . . . . . . . . . . . . . . . . . . . . . 31
5.2 OLED Library . . . . . . . . . . . . . . . . . . . . . . 32
5.3 Example Program . . . . . . . . . . . . . . . . . . . . 33
5.4 Summary . . . . . . . . . . . . . . . . . . . . . . . . . 40

6 Read a Button 41
6.1 Button Circuit . . . . . . . . . . . . . . . . . . . . . . 41
6.2 Digital Input . . . . . . . . . . . . . . . . . . . . . . . 42
6.3 Example Program . . . . . . . . . . . . . . . . . . . . 44
6.4 Button Debouncing . . . . . . . . . . . . . . . . . . . 45
6.5 Summary . . . . . . . . . . . . . . . . . . . . . . . . . 49

7 Read a Button using External Interrupt 50


7.1 Interrupt . . . . . . . . . . . . . . . . . . . . . . . . . 50
7.2 Example Program . . . . . . . . . . . . . . . . . . . . 52
7.3 Summary . . . . . . . . . . . . . . . . . . . . . . . . . 55

8 Read a Trimpot using ADC 56


8.1 ADC . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
8.2 Analog Input . . . . . . . . . . . . . . . . . . . . . . . 58
8.3 Example Program . . . . . . . . . . . . . . . . . . . . 58
CONTENTS iv

8.4 Summary . . . . . . . . . . . . . . . . . . . . . . . . . 60
8.5 Coding Challenge . . . . . . . . . . . . . . . . . . . . 60

9 Serial Communication between ESP8266 and PC 62


9.1 Serial Communication . . . . . . . . . . . . . . . . . . 62
9.2 Serial Print . . . . . . . . . . . . . . . . . . . . . . . . 64
9.3 Example Program . . . . . . . . . . . . . . . . . . . . 64
9.4 Summary . . . . . . . . . . . . . . . . . . . . . . . . . 66

10 Data Logging to PC using Serial 67


10.1 Print Format . . . . . . . . . . . . . . . . . . . . . . . 67
10.2 Example Program . . . . . . . . . . . . . . . . . . . . 69
10.3 Serial Plotter . . . . . . . . . . . . . . . . . . . . . . . 70
10.4 Summary . . . . . . . . . . . . . . . . . . . . . . . . . 71

11 Receive Data from PC using Serial 72


11.1 Serial Read . . . . . . . . . . . . . . . . . . . . . . . . 72
11.2 Example Program . . . . . . . . . . . . . . . . . . . . 73
11.3 Summary . . . . . . . . . . . . . . . . . . . . . . . . . 78
11.4 Coding Challenge . . . . . . . . . . . . . . . . . . . . 78

12 Measure Time using a Timer 80


12.1 Timer . . . . . . . . . . . . . . . . . . . . . . . . . . 80
12.2 Example Program . . . . . . . . . . . . . . . . . . . . 81
12.3 Ticker . . . . . . . . . . . . . . . . . . . . . . . . . . 84
12.4 Summary . . . . . . . . . . . . . . . . . . . . . . . . . 85
12.5 Coding Challenge . . . . . . . . . . . . . . . . . . . . 85

13 DS1307 Real-Time Clock 86


13.1 Real-Time Clock . . . . . . . . . . . . . . . . . . . . . 86
13.2 Example Program . . . . . . . . . . . . . . . . . . . . 87
13.3 Summary . . . . . . . . . . . . . . . . . . . . . . . . . 90

14 DHT11 Temperature and Humidity Sensor 91


CONTENTS v

14.1 Temperature and Humidity Sensor . . . . . . . . . . . 91


14.2 Example Program . . . . . . . . . . . . . . . . . . . . 92
14.3 Summary . . . . . . . . . . . . . . . . . . . . . . . . . 95
14.4 Coding Challenge . . . . . . . . . . . . . . . . . . . . 95

Bibliography 97

Solution to Coding Challenge 99


Listings

1.1 Turn on the on-board LED . . . . . . . . . . . . . . . 9


2.1 Blink an LED . . . . . . . . . . . . . . . . . . . . . . 19
3.1 Dim an LED using PWM . . . . . . . . . . . . . . . . 24
4.1 Control an RGB LED . . . . . . . . . . . . . . . . . . 29
5.1 Display a message on OLED display . . . . . . . . . . 34
5.2 Display bitmap images on OLED display . . . . . . . . 36
6.1 Read a button . . . . . . . . . . . . . . . . . . . . . . 44
6.2 Debounce a button . . . . . . . . . . . . . . . . . . . 46
7.1 Read a button using external interrupt . . . . . . . . . 53
8.1 Read a trimpot using ADC . . . . . . . . . . . . . . . 58
9.1 Send a message to PC . . . . . . . . . . . . . . . . . . 65
10.1 Log the trimpot value to PC . . . . . . . . . . . . . . 69
11.1 Receive a message from PC . . . . . . . . . . . . . . . 74
11.2 Turn on or off the LED using serial . . . . . . . . . . . 75
11.3 Change the LED brightness using serial . . . . . . . . . 77
12.1 Measure time using millis function . . . . . . . . . 81
12.2 Blink an LED without using delay function . . . . . . 82
12.3 Blink an LED using ticker . . . . . . . . . . . . . . . . 84
13.1 Log the DS1307 time and date to PC . . . . . . . . . 87
13.2 Display the DS1307 time and date on OLED display . . 88
14.1 Log the DHT11 temperature and humidity to PC . . . 92
14.2 Display the DHT11 temperature and humidity on OLED
display . . . . . . . . . . . . . . . . . . . . . . . . . . 93

vi
Chapter 1

Introduction

1.1 ESP8266
ESP8266 is a low-cost WiFi chip. ESP8266 chip is shown in Figure
1.1. This chip consists of a microcontroller and a full TCP/IP stack.
ESP8266 is made by Espressif Systems, a Shanghai-based Chinese
fabless semiconductor company. ESP8266 is a very popular chip for
developing IoT devices. The ESP8266’s specifications are listed as the
following [1]:

Figure 1.1. ESP8266 chip. Retrieved August 26, 2018, from gridconnect.com

1
CHAPTER 1. INTRODUCTION 2

• L106 32-bit RISC microprocessor core based on the Tensilica


Xtensa Diamond Standard 106Micro running at 80 MHz
• Memory:
– 32 KiB instruction RAM
– 32 KiB instruction cache RAM
– 80 KiB user-data RAM
– 16 KiB ETS system-data RAM
• External QSPI flash: up to 16 MiB is supported (512 KiB to 4
MiB typically included)
• IEEE 802.11 b/g/n Wi-Fi
– Integrated transmit/receive (TR) switch, balun, LNA, power
amplifier, and matching network
– WEP or WPA/WPA2 authentication, or open networks
• 16 GPIO pins (GPIO6–11 are used for communication with on-
board flash memory).
• SPI
• I2 C (software implementation)
• I2 S interfaces with DMA (sharing pins with GPIO)
• UART on dedicated pins, plus a transmit-only UART can be
enabled on GPIO2
• 10-bit ADC (successive approximation ADC)

With ESP8266, you can make IoT devices that are connected to the
internet through WiFi network. You can also make embedded web
CHAPTER 1. INTRODUCTION 3

server in this chip, so your PC or smartphone can connect to the


ESP8266.
What is the difference between ESP8266 and ESP8266EX?
ESP8266 is the initial version. ESP8266EX is the updated version. Now
ESP8266EX is the most commonly available.

1.2 ESP-12E
ESP-12E is a WiFi module that uses the ESP8266. ESP-12E is shown
in Figure 1.2. ESP-12E is made by Ai-Thinker, a third-party manufac-
turer. There are other ESP modules made by this manufacturer. They
are referred to as “ESP-xx modules”. The disadvantage of ESP-12E
is that it is not breadboard friendly.

Figure 1.2. ESP-12E module. Retrieved August 26, 2018, from hackster.io

ESP-12E has an RF shield (metal enclosure) that covers the ESP8266


chip as shown in Figure 1.3. This shield is used for compliance with
Federal Communications Commission (FCC) emissions rules. This
shiled can minimize interference with other devices.
CHAPTER 1. INTRODUCTION 4

Figure 1.3. RF shield on ESP-12E. Retrieved August 26, 2018, from amicus.com.sg

1.3 NodeMCU
NodeMCU is a development board that uses the ESP-12E. NodeMCU
is shown in Figure 1.4. This is NodeMCU V3 which is used in this book.
At the time of writing, it is the latest NodeMCU generation. Compared
to the ESP-12E, NodeMCU is breadboard friendly and includes USB
to serial interface. NodeMCU can be programmed using Lua scipting,
Arduino, or ESP8266 SDK. In this book, we will program NodeMCU
using Arduino, which is the easiest and most popular method.

Figure 1.4. NodeMCU development board. Retrieved August 26, 2018, from
root.cz
CHAPTER 1. INTRODUCTION 5

What are the differences between NodeMCU V1, V2, and V3?
NodeMCU V1 is the first generation, but now outdated. NodeMCU V2
and V3 are the second generation. V2 is made by Amica, while V3 is
made by LoLin. V2 and V3 use a different USB to serial chip. V3’s board
size is significantly larger than the V2.

1.4 Driver Installation


Before we can program NodeMCU, we need to install the USB driver.
The step-by-step how to install NodeMCU’s USB driver is described
as the following:

• Download NodeMCU V3 driver from this link: https://github.


com/nodemcu/nodemcu-devkit/tree/master/Drivers
• Connect NodeMCU development board to the USB port.
• Run the driver installer file: CH341SER.EXE, and install the
driver as shown in Figure 1.5.
• Open Device Manager in order to find NodeMCU’s COM port
as shown in Figure 1.6.

Figure 1.5. NodeMCU V3 USB driver installation


CHAPTER 1. INTRODUCTION 6

Figure 1.6. NodeMCU’s COM port in Device Manager

If you use NodeMCU V2, then the USB driver is different. NodeMCU
V2 use CP210x USB to serial chip, while NodeMCU V3 use CH341
USB to serial chip. The installation process NodeMCU V2 USB
driver is similar to NodeMCU V3 USB driver. You can download
the NodeMCU V2 USB driver from https://www.silabs.com/
products/development-tools/software/usb-to-uart-
bridge-vcp-drivers.
You can also find NodeMCU V2 USB driver in Arduino software in-
stallation folder, under the driver folder, there is CP210x driver.
You can also use this driver for NodeMCU V2, so you do not have
to download the driver. You have to manually install the driver from
Device Manager by right clicking on the NodeMCU’s USB port,
select Update Driver Software menu, and search for the driver in
arduino-x.x.x/driver folder.
CHAPTER 1. INTRODUCTION 7

1.5 ESP8266 Library Installation


Arduino IDE will be used for programming ESP8266 in this book. At
the time of writing, the latest Arduino IDE version is 1.8.5. You can
download either the installed or the portable version. ESP8266 library
is not included in Arduino IDE, so you must install it manually. The
step-by-step how to install the ESP8266 library is described as the
following:

• Start Arduino IDE, go to File menu, and open Preferences


window.
• Enter http://arduino.esp8266.com/stable/package_
esp8266com_index.json into Additional Board Manager
URLs field as shown in Figure 1.7.
• Go to Tools → Board menu, open Boards Manager, and
install esp8266 platform as shown in Figure 1.8.
• After installation, select NodeMCU 1.0 (ESP-12E Module)
board from Tools → Board menu.

The ESP8266 library files can be found in C:\Users\<USER>\AppData\


Local\Arduino15\packages\esp8266\hardware\esp8266\
2.4.1\cores\esp8266.

1.6 Create the First Program


For the very first program, we will create a simple program that turns
on the ESP-12E’s on-board LED. The code for turning on the on-
board LED is shown in Listing 1.1. The on-board LED is connected to
pin D4, and the circuit is active-low, so a logic LOW is needed to turn
on the LED. The details about LED circuits will be explained later in
chapter 2. The digitalWrite function is used for writing a digital
value to a digital output pin.
CHAPTER 1. INTRODUCTION 8

Figure 1.7. Enter ESP8266 package in Arduino preferences

Figure 1.8. Install ESP8266 library


CHAPTER 1. INTRODUCTION 9

Listing 1.1. Turn on the on-board LED


1 // *** File : /esp8266-arduino/on-board-led/
2 // on-board-led.ino
3 // *** Author : Erwin Ouyang
4 // *** Date : 17 Agt 2018
5
6 void setup()
7 {
8 // Set on-board LED pin as output
9 pinMode(D4, OUTPUT);
10 }
11
12 void loop()
13 {
14 // Turn on the active-low LED
15 digitalWrite(D4, LOW);
16 }

To compile the code, go to Sketch → Verify/Compile menu or click


the checklist button in toolbar menu as shown in Figure 1.9. After
the code is compiled without any error, you can connect NodeMCU
to USB port, then go to Tools → Port menu, and select the correct
COM port as you found in Device Manager. To upload the code to
NodeMCU, you can go to Sketch → Upload menu or click the right
arrow button in toolbar menu. The result of this program is shown in
Figure 1.10.

Figure 1.9. Verify and Upload buttons


CHAPTER 1. INTRODUCTION 10

Figure 1.10. The on-board LED is on

1.7 Development Breadboard


All of the projects in this book will be done in a breadboard. The
breadboard consists of NodeMCU, sensors, and actuators as shown in
Figure 1.11. The list of components for this development breadboard
is listed in Table 1.1. The circuit of this development breadboard is
shown in Figure 1.12 and in Figure 1.13 for the schematic version.

Table 1.1. List of components for development breadboard

No. Component Name Quantity


1 NodeMCU board 1
2 Red LED 1
3 RGB LED common anode 1
4 0.96 Inch OLED I2C display 128x64 1
5 Push button 1
6 Trimpot 10kΩ 1
7 DS1307 RTC 1
8 32.768 kHz quartz crystal 1
9 DHT11 temperature and humidity sensor 1
10 100Ω resistor 3
11 150Ω resistor 1
12 4.7kΩ resistor 2
CHAPTER 1. INTRODUCTION 11

1.8 Summary
The development board used in this book is NodeMCU V3. Several
sensors and actuators are required for example code in this book. The
software tools required for programming NodeMCU are USB driver,
Arduino IDE, and ESP8266 library.
CHAPTER 1. INTRODUCTION

Figure 1.11. Development breadboard photograph


12
CHAPTER 1. INTRODUCTION
13

Figure 1.12. Development breadboard circuit


CHAPTER 1. INTRODUCTION

Figure 1.13. Development breadboard schematic


14
Chapter 2

Blink an LED

In chapter 1, the requirements for programming ESP8266 are ex-


plained. The very first program is created to make sure that everyting
is properly installed. In this chapter, you will learn about General
Purpose Input Output (GPIO) for blinking an LED.
What will you learn in this chapter?

• Build an LED circuit.


• Set a GPIO pin as output.
• Blink the LED using digitalWrite and delay function.

2.1 LED Circuit


In on-board-led program, when a logic LOW is written to the
GPIO pin, then the LED will turn on. This is because the LED circuit
is active-low. It is called active-low because a logic LOW is needed to
activate (to turn on) the LED. The active-low LED circuit is shown in
Figure 2.1. When a logic LOW is written to the GPIO pin, the current

15
CHAPTER 2. BLINK AN LED 16

(a) (b)

Figure 2.1. Active-low LED circuit: (a) A logic LOW turns on the LED; (b) A logic
HIGH turns off the LED

can flow from the 3.3V through the resistor and LED, and then flows
to the GND. Therefore the LED is on. On the other hand, when a logic
HIGH is written to the GPIO pin, the current can not flow through
the resistor and LED. Therefore the LED is off.
In this chapter, you will build the active-high LED circuit. It is called
active-high because a logic HIGH is needed to activate (to turn on)
the LED. The active-high circuit is shown in Figure 2.2. In active-high
circuit, the LED will turn on, when a logic HIGH is written to the
GPIO pin, and the LED will turn off, when a logic LOW is written to
the GPIO pin. When a logic HIGH is written to the GPIO pin, the
current can flow through the resistor and LED, and then flows to the
GND. Therefore the LED is on. When a logic LOW is written, the
current can not flow.

2.2 Digital Output


NodeMCU has 10 GPIO pins. The GPIO pins can be identified by
ESP8266 pin numbering or NodeMCU pin numbering. NodeMCU pin
is shown in Figure 2.3. The NodeMCU pin numbering starts from
CHAPTER 2. BLINK AN LED 17

(a) (b)

Figure 2.2. Active-high LED circuit: (a) A logic HIGH turns on the LED; (b) A
logic LOW turns off the LED

D0–10, while the ESP8266 pin numbering starts from GPIO0–16.


To set a GPIO pin as output, you can use pinMode function. You
can use either NodeMCU or ESP8266 pin numbering as its input pa-
rameter. The pinMode function is defined as
// pin : D0, D1, ... (NodeMCU pins) or
// 0, 1, ... (ESP8266 pins).
// mode: OUTPUT, INPUT, or INPUT_PULLUP.
void pinMode(pin, mode);

After the GPIO pin is initialized as output, you can write a value to the
GPIO pin by using digitalWrite function. The digitalWrite
function is defined as
// pin : D0, D1, ... (NodeMCU pins) or
// 0, 1, ... (ESP8266 pins).
// value: LOW or HIGH.
void digitalWrite(pin, value);

The pinMode and digitalWrite function are defined in core_


esp8266_wiring_digital.c. This is the ESP8266 library file.
The ESP8266 library files can be found in C:\Users\<USER>\AppData\
CHAPTER 2. BLINK AN LED 18

Figure 2.3. NodeMCU pinout. Retrieved August 26, 2018, from teachmemi-
cro.com

Local\Arduino15\packages\esp8266\hardware\esp8266\
2.4.1\cores\esp8266.

2.3 Example Program


In this example program, you will use a GPIO pin to blink the red
LED. The code for the program is shown in Listing 2.1. The red LED
is connected to pin D3. Before you can use a GPIO pin as output,
you must configure it with pinMode function, as shown in line 8. In
this line, the GPIO0 is configured as output by using NodeMCU pin
numbering (D3).
LED blinking is created by turning on and off the LED every 1 second
CHAPTER 2. BLINK AN LED 19

with digitalWrite and delay functions. The digitalWrite


function in line 16 and 19 are used for writing a value to output pin.
The delay function in line 17 and 20 are used for pausing the code
execution for 1 second. The delay function is defined as
// value: Delay in milliseconds.
void delay(value);

The delay function is defined in core_esp8266_wiring.c.

What is the difference between setup and loop function?

The setup function runs once at the very beginning of the program, i.e.
when you turn on the NodeMCU, reset the NodeMCU, or upload new
code. The loop function runs over-and-over until the ESP8266 is reset.
The loop function is just like the while (1) loop that runs forever.

Listing 2.1. Blink an LED


1 // *** File : /esp8266-arduino/led/led.ino
2 // *** Author : Erwin Ouyang
3 // *** Date : 17 Agt 2018
4
5 void setup()
6 {
7 // Set GPIO pin as output using NodeMCU pins,
8 pinMode(D3, OUTPUT);
9 // or use ESP8266 pins
10 //pinMode(0, OUTPUT);
11 }
12
13 void loop()
14 {
15 // Turn on LED
16 digitalWrite(D3, HIGH);
17 delay(1000);
18 // Turn off LED
CHAPTER 2. BLINK AN LED 20

19 digitalWrite(D3, LOW);
20 delay(1000);
21 }

2.4 Summary
There are two types of LED circuit, namely active-low and active-
high. The pinMode function is used for configuring the GPIO pin.
The digitalWrite function is used for writing a value to an output
pin. The delay function is used for pausing the code execution.
Chapter 3

Dim an LED using PWM

In chapter 2, you have learned how to turn on and off the LED and
blinking the LED. In this chapter, you will learn how to dim an LED.
To dim an LED, you can use Pulse Width Modulation (PWM).
What will you learn in this chapter?

• Use analogWrite function to create PWM.


• Increase and decrease the LED brightness.

3.1 PWM
Pulse Width Modulation (PWM) is a square wave, a signal switched
between HIGH and LOW, which the HIGH duration can be varied [2].
The HIGH duration is called pulse width, and can be varied from zero
(fully LOW) up to the period of square wave (fully HIGH). The ratio of
pulse width to the square wave period is called duty cycle as shown in
Figure 3.1. The average voltage of PWM signal can simulate analog
value between 0V to VDD (3.3V on ESP8266). The analog value is

21
CHAPTER 3. DIM AN LED USING PWM 22

Figure 3.1. PWM duty cycle. Retrieved September 11, 2018, from caferacer-
sjpg.com

Figure 3.2. The average voltage of PWM signals. Retrieved September 11, 2018,
from web.stanford.edu

actually the average voltage of the square wave that corresponds to


duty cycle as shown in Figure 3.2. The larger the duty cycle, the larger
the average voltage.
CHAPTER 3. DIM AN LED USING PWM 23

3.2 Analog Output


In Arduino, the PWM output is also called analog output. The PWM
is implemented by software, and can be used on GPIO0–16 [3]. To
write an analog value to a GPIO pin, you can use analogWrite
function. The analogWrite function is defined as
// pin : D0, D1, ... (NodeMCU pins) or
// 0, 1, ... (ESP8266 pins)
// value: 0 to PWMRANGE=1023 (default)
void analgWrite(pin, value);

The analog value can be in range from 0–PWMRANGE, which is defined


as 1023 in Arduino.h. The range of analog value can be changed by
using analogWriteRange function. The analogWriteRange
function is defined as
// range: PWM maximum range
void analogWriteRange(range);

The PWM frequency is 1 kHz by default. The PWM frequency can


be changed by using analogWriteFreq function. The analog
WriteFreq function is defined as
// freq: PWM frequency.
void analogWriteFreq(freq);

The analogWrite, analogWriteRange, and analogWriteFreq


functions are defined in core_esp8266_wiring_pwm.c.

3.3 Example Program


In this example program, you will use a GPIO pin to dim the red LED.
The code for the program is shown in Listing 3.1. The red LED is
connected to pin D3. When you use analog output, you do not have
CHAPTER 3. DIM AN LED USING PWM 24

to initialize it using pinMode function, because it is already initialized


by the Arduino library.
The code in the line 12–16 are used for increasing the LED brightness
from 0–1023. The value is incremented by 20 every 25 milliseconds.
The codes in the line 18–22 are used for decreasing the LED brightness
from 1023–0. The value is decremented by 20 every 25 milliseconds.

Listing 3.1. Dim an LED using PWM


1 // *** File : /esp8266-arduino/led-pwm/led-pwm.ino
2 // *** Author : Erwin Ouyang
3 // *** Date : 17 Agt 2018
4
5 void setup()
6 {
7 }
8
9 void loop()
10 {
11 // *** Increase brightness ***
12 for (int i = 0; i <= 1023; i += 20)
13 {
14 analogWrite(D3, i);
15 delay(25);
16 }
17 // *** Decrease brightness ***
18 for (int i = 1023; i >= 0; i -= 20)
19 {
20 analogWrite(D3, i);
21 delay(25);
22 }
23 }
CHAPTER 3. DIM AN LED USING PWM 25

3.4 Summary
The analog output can be use to simulate an analog voltage, which
can be varied from 0V–3.3V. The analogWrite function is used for
writing an analog value to a GPIO pin.

3.5 Coding Challenge


In the previous example, you may notice the red LED do not dim
nicely. It appears to dim very quickly at first, and then spend a long
time with almost full brightness. This happens because human eyes
do not respond to light linearly. The led’s actual brightness and eye’s
preceived brightness curve are shown in Figure 3.3(a).
To make the LED brightness to be appeared linearly to human eye, you
can counteract the non-linear preceived brightness by using a curve as
shown in Figure 3.3(b) (blue curve). You can create this curve by
using equation 3.1 [4]. Where x is the linear curve (PWM value
between 0–1023), and y is the counteract non-linear curve. In this
coding challenge, you have to create a dimming program that use this
counteract non-linear curve!
x
y = 2 102.3 − 1 (3.1)
CHAPTER 3. DIM AN LED USING PWM 26

(a)

(b)

Figure 3.3. LED dimming curve: (a) A linear LED dimming curve (blue) produces
a non-linear preceived curve (orange); (b) A counteract non-linear LED dimming
curve (blue) produces linear preceived curve (orange)
Chapter 4

Control an RGB LED

In chapter 3, you have learned how to use analog output to dim an


LED. In this chapter, you will learn how to use analog output to control
the color of RGB LED.
What will you learn in this chapter?

• Two different kinds of RGB LED.


• Control the color of the RGB LED using PWM.

4.1 RGB LED


RGB LED is just like the normal LED, but it has three LED with
three different colors (red, green, and blue) within the package. With
these primary colors of light, you can create other colors by varying
the brightness of primary colors. To varying the brightness of RGB
LED, you can use PWM.
There are two different kinds of RGB LED, namely common anode and
common cathode. In common anode, the positive terminals of three

27
CHAPTER 4. CONTROL AN RGB LED 28

Figure 4.1. Common anode and common cathode RGB LED. Retrieved September
15, 2018, from www.hackster.io

LEDs are connected together, while in common cathode, the negative


terminals of three LEDs are connected together as shown in Figure 4.1.
In commom anode, the anode is connected to supply voltage, and the
cathodes are connected to PWM output pins through resistors, so the
LEDs are active-low. In common cathode, the cathode is connected to
ground, and the anodes are connected to PWM output pins through
resistors, so the LEDs are active-high.

4.2 Example Program


In this example program, you will use analog output to control the
color of RGB LED. The code for the program is shown in Listing 4.1.
In this example, a common anode RGB LED is used. The cathodes
of red, green, and blue LEDs are connected to pin D5, D7, and D8,
respectively.
In line 8, the analogWriteRange function is used for changing the
PWM range from 0–255, so the range is equal to the 24-bit color. The
24-bit color uses 8-bit for each R, G, and B value. In line 39, there
is a function called rgbLedWrite. This function is used for setting
the color of RGB LED. The input parameters are the RGB value. In
CHAPTER 4. CONTROL AN RGB LED 29

this function, the analogWrite function is called to write the RGB


value to pin D5, D7, and D8. The values are inversed by subtracting
the RGB value from 255 because the RGB LED is active-low. In main
program, the color of RGB LED is changed from red to white with a
delay of 1 second for every color.

Listing 4.1. Control an RGB LED


1 // *** File : /esp8266-arduino/rgb-led/rgb-led.ino
2 // *** Author : Erwin Ouyang
3 // *** Date : 17 Agt 2018
4
5 void setup()
6 {
7 // Set PWM range from 0 to 255
8 analogWriteRange(255);
9 }
10
11 void loop()
12 {
13 // Red
14 rgbLedWrite(255, 0, 0);
15 delay(1000);
16 // Orange
17 rgbLedWrite(255, 50, 0);
18 delay(1000);
19 // Yellow
20 rgbLedWrite(255, 150, 0);
21 delay(1000);
22 // Green
23 rgbLedWrite(0, 255, 0);
24 delay(1000);
25 // Light Blue
26 rgbLedWrite(0, 150, 255);
27 delay(1000);
28 // Blue
29 rgbLedWrite(0, 0, 255);
CHAPTER 4. CONTROL AN RGB LED 30

30 delay(1000);
31 // Purple
32 rgbLedWrite(150, 0, 255);
33 delay(1000);
34 // White
35 rgbLedWrite(255, 255, 255);
36 delay(1000);
37 }
38
39 void rgbLedWrite(byte red, byte green, byte blue)
40 {
41 analogWrite(D5, 255-red);
42 analogWrite(D7, 255-green);
43 analogWrite(D8, 255-blue);
44 }

4.3 Summary
The analogWriteRange function is used for changing the PWM
range to 8-bit. The analogWrite function is used for writing an
analog value to the RGB pins of RGB LED.

4.4 Coding Challenge


In the previous example, you have created a program that changes
the RGB LED color every 1 second. In this coding challenge, you
have to create a program that gradually cycle the color spectrum, i.e
start from red color, then the red color is gradually decremented, while
at the same time the green color is also gradually incremented. Do
this process until the RGB LED color is fully green. Through this
process, the RGB LED color will gradually change from red, orange,
yellow, and finally green. Do the same method for gradually cycling
the entire color spectrum (except the white color)!
Chapter 5

Display a Message on OLED


Display

In chapter 4, you have learned how to use RGB LED. In this chapter,
you will learn how to use OLED display to display a message and a
bitmap image.
What will you learn in this chapter?

• Install an external library on Arduino IDE.


• Use OLED SSD1306 display library.
• Display a message and a bitmap image on OLED display.

5.1 OLED Display


Organic LED (OLED) display is just like LCD display, but it uses
organic component and no backlight. In this book, the popular 0.96
inch OLED with 128x64 pixels is used as shown in Figure 5.1. This
OLED display uses SSD1306 chip as its controller. This OLED display

31
CHAPTER 5. DISPLAY A MESSAGE ON OLED DISPLAY 32

Figure 5.1. 0.96 inch 128x64 OLED display module. Retrieved September 15,
2018, from www.megaeshop.pk

has two types of interfaces, either I2C or SPI bus. In this book, the I2C
bus is used. In general, the I2C bus is used for communication between
ICs on a PCB. This bus only needs two wires which are for data and
clock. Because of that, I2C bus is also called Two Wire Interface
(TWI). In ESP8266, the I2C bus is handled by using Wire.h library.

5.2 OLED Library


To use OLED display with ESP8266, you need external libraries. There
are two external libraries needed for the OLED display, namely Adafruit
SSD1306 and Adafruit GFX Library. The step-by-step how to install
these libraries is explained as the following:

• Download Adafruit SSD1306 from this link: https://github.


com/adafruit/Adafruit_SSD1306
• Download Adafruit GFX Library from this link: https://
github.com/adafruit/Adafruit-GFX-Library
• Go to Sketch → Include Library → Add .ZIP Library... menu,
open the library file Adafruit_SSD1306-master.zip, and
CHAPTER 5. DISPLAY A MESSAGE ON OLED DISPLAY 33

then the Adafruit-GFX-Library-master.zip

After the libraries are installed, you have to setup the OLED display
size in Adafruit SSD1306. The step-by-step how to setup the OLED
display size is explained as the following:

• Go to Adafruit SSD1306 installation folder C:\Users\<USER>


\Documents\Arduino\libraries\Adafruit_SSD1306.
• Open the Adafruit_SSD1306.h file.
• Uncomment line 73 in order to set the OLED display size to
128x64, and then comment the other lines.

In Adafruit SSD1306 library, there is a memory buffer that holds the


value of every OLED display pixel. The buffer is stored in ESP8266’s
RAM, so you should send the buffer to the OLED display periodically
in order to update/refresh the display. This can be done by using
display method1 . You can use a method called clearDisplay
to clear the buffer. After the buffer is cleared, you should call the
display method.

5.3 Example Program


In the first example program, you will use the OLED display to display
a message. The code for the program is shown in Listing 5.1. In
line 16, the OLED’s I2C address is defined. In line 19, the Adafruit
OLED object is created, and it is initialized in line 24 by using be
gin method. The code in line 29–32, are used for setup the text size,
color, location, and display the ”Hello, world!” text. Finally, in line 35,
you should send the buffer to the OLED display by using display
method.
1 A function that is created inside a class is called method. The term method is
used almost exclusively in object-oriented programming.
CHAPTER 5. DISPLAY A MESSAGE ON OLED DISPLAY 34

Listing 5.1. Display a message on OLED display


1 // *** File : /esp8266-arduino/oled-display/
2 // oled-display.ino
3 // *** Author : Erwin Ouyang
4 // *** Date : 17 Agt 2018
5
6 #include <Wire.h>
7 #include <Adafruit_SSD1306.h>
8 #include <Adafruit_GFX.h>
9
10 // *** Check library setting ***
11 #if (SSD1306_LCDHEIGHT != 64) // 128 x 64 pixel display
12 #error("Height incorrect, please fix Adafruit_SSD1306.h!");
13 #endif
14
15 // OLED I2C address
16 #define OLED_ADDR 0x3C
17
18 // OLED object declaration
19 Adafruit_SSD1306 oled;
20
21 void setup()
22 {
23 // *** Initialize and clear display ***
24 oled.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR);
25 // Clear OLED buffer
26 oled.clearDisplay();
27
28 // *** Display text ***
29 oled.setTextSize(1);
30 oled.setTextColor(WHITE);
31 oled.setCursor(27, 30);
32 oled.print("Hello, world!");
33
34 // Show OLED buffer on the display
35 oled.display();
CHAPTER 5. DISPLAY A MESSAGE ON OLED DISPLAY 35

36 }
37
38 void loop()
39 {
40 }

The setTextSize method is used for setting text size. The set
TextSize method is defined as
// s: Text size, 1 is 6x8, 2 is 12x16, 3 is 18x24,
// and so on
void Adafruit_GFX::setTextSize(uint8_t s)

The size 1 is the default size that has 6×8 pixels. The size 2 has
12×16 pixels. The size 3 has 24×32 pixels.
The setTextColor method has two definitions2 which are defined
as
// c: Text color. For monochrome OLED, the color
// is only WHITE.
void Adafruit_GFX::setTextColor(uint16_t c)

// c: Text color. For monochrome OLED, the color


// should be BLACK.
// b: Background color. For monochrome OLED,
// the color should be WHITE.
void Adafruit_GFX::setTextColor(uint16_t c,
uint16_t b)

The first setTextColor method is used for setting the text color
with transparent background, while in the second setTextColor
method, you can also set the text’s background color. For example,
2 Function/method overloading is a feature that allows you to define more than
one function/method having the same name, but the input arguments or the
return value are different.
CHAPTER 5. DISPLAY A MESSAGE ON OLED DISPLAY 36

you can call setTextColor(BLACK, WHITE) in order to set the


text color to BLACK and the background color to WHITE.
The setCursor method is used for setting the X and Y coordinates
in pixels. The setCursor method is defined as
// x: X coordinate in pixels
// y: Y coordinate in pixels
void Adafruit_GFX::setCursor(int16_t x,
int16_t y)

The coordinate (0,0) is at the top left corner of the display.


In the second example program, you will use the OLED display to
display bitmap images. The code for the program is shown in Listing
5.2. In line 19 and 39, there are two bitmap arrays for temperature
and humidity symbols, respectively.
What do static and PROGMEM mean?
The static keyword has two cases. A static global variable or function
is seen only in the file it is declared in. A static variable inside a function
keeps the value when you call the function again, i.e. it behaves like a
global variable. The PROGMEM keyword is used for storing data in program
memory instead of RAM. This method is used for saving RAM space.

Listing 5.2. Display bitmap images on OLED display


1 // *** File : /esp8266-arduino/oled-display-bitmap/
2 // oled-display-bitmap.ino
3 // *** Author : Erwin Ouyang
4 // *** Date : 17 Agt 2018
5
6 #include <Wire.h>
7 #include <Adafruit_SSD1306.h>
8 #include <Adafruit_GFX.h>
9
10 // *** Check library setting ***
CHAPTER 5. DISPLAY A MESSAGE ON OLED DISPLAY 37

11 #if (SSD1306_LCDHEIGHT != 64) // 128 x 64 pixel display


12 #error("Height incorrect, please fix Adafruit_SSD1306.h!");
13 #endif
14
15 // OLED I2C address
16 #define OLED_ADDR 0x3C
17
18 // Temperature logo
19 static const unsigned char PROGMEM temperature_bmp[] =
20 {
21 B00000011, B11001111,
22 B00000110, B01100000,
23 B00000100, B00101111,
24 B00000100, B00100000,
25 B00000100, B00101111,
26 B00000101, B10100000,
27 B00000101, B10101111,
28 B00000101, B10100000,
29 B00001101, B10110000,
30 B00011011, B11011000,
31 B00110111, B11101100,
32 B00101111, B11110100,
33 B00101111, B11110100,
34 B00110111, B11101100,
35 B00011000, B00011000,
36 B00001111, B11110000
37 };
38 // Humidity logo
39 static const unsigned char PROGMEM humidity_bmp[] =
40 {
41 B00000001, B10000000,
42 B00000011, B11000000,
43 B00000110, B01100000,
44 B00000100, B00100000,
45 B00001100, B00110000,
46 B00001000, B00010000,
47 B00011000, B00011000,
CHAPTER 5. DISPLAY A MESSAGE ON OLED DISPLAY 38

48 B00010000, B00001000,
49 B00110100, B00001100,
50 B00101100, B00000100,
51 B00101100, B00000100,
52 B00101100, B00000100,
53 B00110110, B00001100,
54 B00011011, B00011000,
55 B00001100, B00110000,
56 B00000111, B11100000
57 };
58
59 // OLED object declaration
60 Adafruit_SSD1306 oled;
61
62 void setup()
63 {
64 // *** Initialize and clear display ***
65 oled.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR);
66 // Clear OLED buffer
67 oled.clearDisplay();
68
69 // *** Display bitmap ***
70 oled.drawBitmap(32, 16, temperature_bmp, 16, 16, 1);
71 oled.drawBitmap(32, 36, humidity_bmp, 16, 16, 1);
72
73 // *** Display text ***
74 oled.setTextSize(2);
75 oled.setTextColor(WHITE);
76 oled.setCursor(55, 16);
77 oled.printf("25%cC", (char)247);
78 oled.setCursor(55, 36);
79 oled.print("60%");
80
81 // Show OLED buffer on the display
82 oled.display();
83 }
84
CHAPTER 5. DISPLAY A MESSAGE ON OLED DISPLAY 39

(a) (b)

Figure 5.2. Bitmap symbols: (a) Temperature symbol; (b) Humidity symbol

85 void loop()
86 {
87 }

Every data bit in the bitmap array corresponds to every pixel of the
symbol as shown in Figure 5.2. In line 70 and 71, the bitmap is
drawn to the OLED buffer by using drawBitmap method. The draw
Bitmap method is defined as
// x : X coordinate in pixels
// y : Y coordinate in pixels
// bitmap: Byte array with monochrome bitmap
// w : Width of bitmap in pixels
// h : Height of bitmap in pixels
void Adafruit_GFX::drawBitmap(int16_t x,
int16_t y, uint8_t *bitmap, int16_t w,
int16_t h, uint16_t color)

In line 74–79, the dummy value for temperature and humidity is printed
by using print and printf methods.
CHAPTER 5. DISPLAY A MESSAGE ON OLED DISPLAY 40

5.4 Summary
In this chapter, you have learned how to install external libraries for
OLED display. You have learned how to display a message and a
bitmap image on OLED display. You can change the size, color, and
location of the text by using setTextSize, setTextColor, and
setCursor methods, respectively. The drawBitmap method is
used for drawing a bitmap image.
Chapter 6

Read a Button

In chapter 1–5, you have learned how to use simple actuators, which
are LED, RGB LED, and OLED display. In this chapter, you will learn
how to read a button, which is a simplest sensor that produces digital
output.
What will you learn in this chapter?

• Build a button circuit


• Set a GPIO pin as input.
• Read the button using digitalRead function.

6.1 Button Circuit


There are two types of button circuits, namely active-low and active-
high. The active-low button circuit is shown in Figure 6.1. In this
circuit, the VCC is connected to a GPIO pin through a resistor, so the
current can flow to the GPIO pin. Therefore it receives a logic HIGH.
When the button is pressed, the current flows through the button,

41
CHAPTER 6. READ A BUTTON 42

(a) (b)

Figure 6.1. Active-low button circuit: (a) Button released, input pin receives a
logic HIGH; (b) Button pressed, input pin receives a logic LOW

because the button resistance is almost zero, and the GPIO pin (when
it is configured as input) has high impedance. Therefore the GPIO
pin receives a logic LOW. The resistor in active-low button circuit is
called pull-up resistor. The typical pull-up resistor value is 1–10kΩ.
On the other hand, the active-high button circuit is shown in Figure
6.2. In this circuit, the GND is connected to a GPIO pin through a
resistor, so there is no current flows to the GPIO pin. Therefore it
receives a logic LOW. When the button is pressed, the current flows
from VCC to the GPIO pin and also to the resistor. Therefore the
GPIO pin receives a logic HIGH. The resistor in active-high button
circuit is called pull-down resistor. The typical pull-down resistor value
is 1–10kΩ.

6.2 Digital Input


In order to read a button, you should configure a GPIO pin as input.
You can configure the GPIO pin by using pinMode function. The
CHAPTER 6. READ A BUTTON 43

(a) (b)

Figure 6.2. Active-high button circuit: (a) Button released, input pin receives a
logic LOW; (b) Button pressed, input pin receives a logic HIGH

pinMode function is already defined in chapter 2 as


// pin : D0, D1, ... (NodeMCU pins) or
// 0, 1, ... (ESP8266 pins)
// mode: OUTPUT, INPUT, or INPUT_PULLUP
void pinMode(pin, mode);

After the GPIO pin is initialized as input, you can read the value of
the GPIO pin by using digitalRead function. The digitalRead
function is defined as
// pin : D0, D1, ... (NodeMCU pins) or
// 0, 1, ... (ESP8266 pins)
// return: 0 or 1
int digitalRead(pin);

The pinMode and digitalRead functions are defined in core_


esp8266_wiring_digital.c.
CHAPTER 6. READ A BUTTON 44

6.3 Example Program


In this example program, you will use the GPIO input to read the
button state. The code for the program is shown in Listing 6.1. In
line 8, pin D3 is initialized as output for LED. In line 10, pin D6 is
initialized as input for button with internal pull-up resistor. The button
circuit is active-low.
In main program, you can read the button state by using digital
Read function as shown in line 16. If the button is pressed, then the
LED will be on, otherwise the LED will be off.

Listing 6.1. Read a button


1 // *** File : /esp8266-arduino/button/button.ino
2 // *** Author : Erwin Ouyang
3 // *** Date : 17 Agt 2018
4
5 void setup()
6 {
7 // Set pin as output
8 pinMode(D3, OUTPUT);
9 // Set pin as input pull-up
10 pinMode(D6, INPUT_PULLUP);
11 }
12
13 void loop()
14 {
15 // *** Read button state ***
16 if (digitalRead(D6) == LOW)
17 {
18 // If button is pressed, then turn on
19 // the LED
20 digitalWrite(D3, HIGH);
21 }
22 else
23 {
CHAPTER 6. READ A BUTTON 45

24 // If button is not pressed, then turn off


25 // the LED
26 digitalWrite(D3, LOW);
27 }
28 }

6.4 Button Debouncing


Ideally, when you press the button, it should produce a clean transition
from a logic HIGH to LOW (in case of an active-low button circuit),
but in reality there are spurious transitions as shown in Figure 6.3.
This happens because of the mechanical and physical issues. If the
button is used as input to a counter, the counter will get multiple
counts rather than the expected single count. [5]

Figure 6.3. The output of an active-low button circuit when it is pressed. Retrieved
October 14, 2018, from www.labbookpages.co.uk

To solve this bouncing problem, you can use either hardware or soft-
ware solution. In this book, only the software solution is explained.
CHAPTER 6. READ A BUTTON 46

There are many software solutions to solve the bouncing problem. In


this book, I use a debouncing counter. The button is read every 10 mil-
liseconds. The debouncing counter counts from 0–4. The debouncing
counter will reset the counts to 0 when there is a spurious transition.
If the counts reach 4, then button state has reached the stable state.
The code for button debouncing is shown in Listing 6.2. This program
use a button and an OLED display. The button is used as input to
a counter count which is declared in line 24. The counter value is
displayed on OLED display. You can compare the counter behaviour
between the button that use debouncing and not use debouncing by
commenting/uncommenting the line 16.

Listing 6.2. Debounce a button


1 // *** File : /esp8266-arduino/button-debouncing/
2 // button-debouncing.ino
3 // *** Author : Erwin Ouyang
4 // *** Date : 17 Agt 2018
5
6 #include <Wire.h>
7 #include <Adafruit_SSD1306.h>
8 #include <Adafruit_GFX.h>
9
10 // *** Check library setting ***
11 #if (SSD1306_LCDHEIGHT != 64) // 128 x 64 pixel display
12 #error("Height incorrect, please fix Adafruit_SSD1306.h!");
13 #endif
14
15 // Comment this line to disable button debouncing
16 #define DEBOUNCE
17
18 // OLED I2C address
19 #define OLED_ADDR 0x3C
20
21 // OLED object declaration
22 Adafruit_SSD1306 oled;
CHAPTER 6. READ A BUTTON 47

23 // Counter
24 uint8_t count = 0;
25 // Debounced press
26 uint8_t deb_press = 0;
27
28 void setup()
29 {
30 // Set pin as input pull-up
31 pinMode(D6, INPUT_PULLUP);
32
33 // *** Initialize and clear display ***
34 oled.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR);
35 // Clear OLED buffer
36 oled.clearDisplay();
37
38 // *** Display text ***
39 oled.setTextSize(3);
40 oled.setTextColor(WHITE);
41 oled.setCursor(48, 24);
42 oled.printf("%02d", count);
43 oled.display();
44 }
45
46 void loop()
47 {
48 #ifndef DEBOUNCE
49 if (digitalRead(D6) == LOW)
50 {
51 oled.clearDisplay();
52 oled.setCursor(48, 24);
53 oled.printf("%02d", ++count);
54 oled.display();
55 }
56 #else
57 // *** Debouncing the button ***
58 debounce();
59 delay(10);
CHAPTER 6. READ A BUTTON 48

60
61 if (deb_press == HIGH)
62 {
63 deb_press = LOW;
64 oled.clearDisplay();
65 oled.setCursor(48, 24);
66 oled.printf("%02d", ++count);
67 oled.display();
68 }
69 #endif
70 }
71
72 void debounce()
73 {
74 static uint8_t deb_count = 0;
75 static uint8_t deb_state = 0;
76
77 // Read and invert the active-low button state
78 uint8_t read_state = !digitalRead(D6);
79
80 // *** Debouncing the button state ***
81 if (read_state != deb_state)
82 {
83 // If there is a transition then increment
84 // the debouncing count
85 deb_count++;
86 if (deb_count >= 4)
87 {
88 // If the debouncing count reach 4
89 // then update the debounced state
90 deb_state = read_state;
91 // If the debounced state is HIGH,
92 // it means the button is pressed
93 if (deb_state == HIGH)
94 deb_press = HIGH;
95 deb_count = 0;
96 }
CHAPTER 6. READ A BUTTON 49

97 }
98 else
99 {
100 // Reset debouncing count
101 deb_count = 0;
102 }
103 }

In line 58 and 59, debounce function is called every 10 milliseconds.


The debounce function is defined in line 72. In line 74 and 75,
there are debouncing counter deb count and debouncing button
state deb state. These variables are declared with static keyword,
so the variables keep the value every time you call this function. In
line 81–102, the debouncing counter is implemented. The debounced
button press is stored in a global variable deb press. In line 53 and
66, the counter value is printed to the OLED buffer. The counter
value always has two characters, i.e if the counter value is only one
digit (0–9), then a leading zero will be added. This is done by using
"%02d" format.

What is the difference between ++count and count++?


++count will increment the value, and then return the incremented value.
count++ will increment the value, but return the original value before
being incremented.

6.5 Summary
There are two types of button circuits, namely active-low and active-
high. On every GPIO pin, there is an internal pull-up resistor that can
be configured for GPIO input. The digitalRead function is used
for reading the state of a GPIO pin. Button debouncing is a method
for avoiding multiple button press when you press the button.
Chapter 7

Read a Button using External


Interrupt

In chapter 6, you have learned how to read a button. In this chapter,


you will learn how to read a button using external interrupt.
What will you learn in this chapter?

• Interrupt concept.
• Use attachInterrupt function.
• Read a button inside an interrupt function.

7.1 Interrupt
Interrupt is a signal to the CPU emitted by an internal peripheral or
an external device indicating that the internal peripheral or external
device needs immediate attention [6]. There is an Interrupt Service
Routine (ISR) function that is executed every Interrupt Request (IRQ).
When an interrupt occurs, the CPU suspends the execution of main

50
CHAPTER 7. READ A BUTTON USING EXTERNAL INTERRUPT 51

Figure 7.1. An Interrupt Service Routine (ISR) is called when interrupt occurs.

program (loop function), and executes the ISR function as shown in


Figure 7.1. The concept of interupt can also be explained by a simple
code as the following:
void loop()
{
digitalWrite(D3, HIGH);
delay(2000);
digitalWrite(D3, LOW);
delay(2000);

if (digitalRead(D6) == LOW)
// Do something
else
// Do something else
}

In this program, the LED blinks every 2 seconds, and then reads the
button state. If you run this program, the button works, but the
response time is slow. This happens because the CPU executes the
digitalRead function every 4 seconds (because of the delay func-
tion).
To solve this problem, you can use an interrupt. You can place the
digitalRead function in the ISR function, so when the interrupt
is occured, the execution of main program is suspended in order to
executes the ISR. After the ISR is executed, then the execution of
CHAPTER 7. READ A BUTTON USING EXTERNAL INTERRUPT 52

main program is resumed.


There are two types of interrupts, namely internal interrupt and exter-
nal interrupt. Internal interrupt comes from internal peripherals such
as timer, UART, SPI, I2C, etc. External interrupt come from external
devices such as button.

7.2 Example Program


In this example program, you will read the button by using external
interrupt. The code for the program is shown in Listing 7.1. To use
external interrupt, you should initialize a GPIO input with attach
Interrupt function. The attachInterrupt function is defined
as
// pin : Any GPIO pin, except D0 (GPIO16)
// ISR : The ISR to call when the interrupt occurs
// mode: RISING, FALLING, or CHANGE
void attachInterrupt(digitalPinToInterrupt(pin),
ISR, mode);

There are three modes of external interrupts, namely rising, falling,


and change, as shown in Figure 7.2. In rising mode, an interrupt
occurs when the input value changes from a logic LOW to HIGH. In
falling mode, an interrupt occurs when the input value changes from
a logic HIGH to LOW. The change mode is the combination of rising
and falling mode.

Figure 7.2. Three modes of external interrupts.


CHAPTER 7. READ A BUTTON USING EXTERNAL INTERRUPT 53

The external interrupt can be attached on any GPIO pin, except D0


(GPIO16). In line 11, pin D6 is initialized as input with internal pull-
up resistor for the button. In line 14, an ISR function called isr is
attached to pin D6 with change mode. The ISR function is created in
line 20. The interrupt occurs when the input value goes from a logic
HIGH to LOW or from a logic LOW to HIGH. The input value changes
from a logic HIGH to LOW when the button is pressed, and it changes
from a logic LOW to HIGH when the button is released.

Listing 7.1. Read a button using external interrupt


1 // *** File : /esp8266-arduino/button-interrupt/
2 // button-interrupt.ino
3 // *** Author : Erwin Ouyang
4 // *** Date : 17 Agt 2018
5
6 void setup()
7 {
8 // Set pin as output
9 pinMode(D3, OUTPUT);
10 // Set pin as input pull-up
11 pinMode(D6, INPUT_PULLUP);
12 // Set external interrupt from pin D6 that is
13 // connected to button
14 attachInterrupt(digitalPinToInterrupt(D6),
15 isr, CHANGE);
16 // Set PWM range from 0 to 255
17 analogWriteRange(255);
18 }
19
20 void isr()
21 {
22 // *** Read button state ***
23 if (digitalRead(D6) == LOW)
24 {
25 // If button is pressed, then turn on the LED
26 digitalWrite(D3, HIGH);
CHAPTER 7. READ A BUTTON USING EXTERNAL INTERRUPT 54

27 }
28 else
29 {
30 // If button is not pressed, then turn off the LED
31 digitalWrite(D3, LOW);
32 }
33 }
34
35 void loop()
36 {
37 // *** Blink LED white ***
38 rgbLedWrite(255, 255, 255);
39 delay(2000);
40 rgbLedWrite(0, 0, 0);
41 delay(2000);
42 }
43
44 void rgbLedWrite(byte red, byte green, byte blue)
45 {
46 analogWrite(D5, 255-red);
47 analogWrite(D7, 255-green);
48 analogWrite(D8, 255-blue);
49 }

In the isr function, the button state is read by using digital


Read function. When the button is pressed, the red LED will be on,
otherwise the red LED will be off. In main program, you can blink
the RGB LED every 2 seconds. The timing diagram of this program
is shown in Figure 7.3. The execution of main program is preempted
when the button interrupt occurs, and resumed after the CPU executes
button ISR.
CHAPTER 7. READ A BUTTON USING EXTERNAL INTERRUPT 55

Figure 7.3. Timing diagram of the button interrupt program.

7.3 Summary
By using external interrupt, the button response time is faster than
without interrupt. This happens because interrupt can preempt the
execution of main program in order to serve the request from button.
Chapter 8

Read a Trimpot using ADC

In chapter 6 and 7, you have learned how to read a digital value from
a button. In this chapter, you will learn how to read an analog value
from a trimpot using Analog-to-Digital Converter (ADC).
What will you learn in this chapter?

• Read a trimpot using analogRead function.


• Display the trimpot value on OLED display.

8.1 ADC
Analog-to-Digital Converter (ADC) is a component that converts an
analog voltage to a digital value. In mathematics, it is said that the
analog voltage consists of an infinite number of points between point
A and point B, for example between 0–5V. In Figure 8.1, you can see
that after 2.7V there are 2.71V, 2.718V, 2.7182V, and so on, but a
microcontroller can only represent a finite set of numbers (discrete
numbers). For example, 8-bit number can only represent numbers

56
CHAPTER 8. READ A TRIMPOT USING ADC 57

Figure 8.1. Conversion between an analog voltage to a digital value.

from 0–255, so between 139 and 140 there is no other number.


The function of an ADC is to map the infinite number of points (e.g.
between 0–5V) to the finite number of points (e.g. between 0–255).
This process is called quantization. When the analog voltage is 2.71V
then it will be represented as 139. The analog voltage is rounded
to the nearest value of the digital number. The ADC converts the
analog voltage to the digital value periodically. This process is called
sampling. The ADC period or sampling rate determines the bandwidth
of the signal that can be captured.
The ESP8266’s ADC has 10-bit resulotion, and the input voltage range
is between 0–1V. The NodeMCU V3 board has a voltage divider cir-
cuit, so the input voltage range on pin A0 is between 0–3.3V. In
older version of NodeMCU, the input voltage range on pin A0 may
be between 0–1V. If you are not sure, you can check the input volt-
age range by inputting 0.5V to the pin A0, then read the value using
analogRead function. If the result is around 512, then the input
CHAPTER 8. READ A TRIMPOT USING ADC 58

range is between 0–1V, otherwise the input range is between 0–3.3V.

8.2 Analog Input


When you use the ESP8266 Arduino library, the ADC is already initial-
ized, so you just need to use the analogRead function for reading
the analog voltage. The analogRead function is defined as
// pin : A0
// return: ADC value
int analogRead(pin);

The parameter input is the analog pin A0, because the ESP8266
only has one ADC input. The function returns analog value between
0–1023. The analogRead function are defined in core_esp8266_
wiring_analog.c.

8.3 Example Program


In this example program, you will use the ADC to read a trimpot.
The code for the program is shown in Listing 8.1. The output of the
trimpot is an analog voltage between 0–3.3V. The analog voltage is
converted to a digital representation between 0–1023 by using analo
gRead function as shown in line 33, and then the value is stored in
variable trimpotValue.
The code in line 36–42 is used for displaying the trimpotValue on
OLED display. In line 44, there is a delay of 1 second, so the trim
potValue is updated every 1 second. The print method from the
OLED library can be used for printing a string or an integer, as shown
in line 40 and 41.

Listing 8.1. Read a trimpot using ADC


1 // *** File : /esp8266-arduino/trimpot/trimpot.ino
2 // *** Author : Erwin Ouyang
CHAPTER 8. READ A TRIMPOT USING ADC 59

3 // *** Date : 17 Agt 2018


4
5 #include <Wire.h>
6 #include <Adafruit_SSD1306.h>
7 #include <Adafruit_GFX.h>
8
9 // *** Check library setting ***
10 #if (SSD1306_LCDHEIGHT != 64) // 128 x 64 pixel display
11 #error("Height incorrect, please fix Adafruit_SSD1306.h!");
12 #endif
13
14 // OLED I2C address
15 #define OLED_ADDR 0x3C
16
17 // OLED object declaration
18 Adafruit_SSD1306 oled;
19
20 int trimpotValue;
21
22 void setup()
23 {
24 // *** Initialize and clear display ***
25 oled.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR);
26 // Clear OLED buffer
27 oled.clearDisplay();
28 }
29
30 void loop()
31 {
32 // Read analog value
33 trimpotValue = analogRead(A0);
34
35 // *** Display timpot value ***
36 oled.clearDisplay();
37 oled.setTextSize(1);
38 oled.setTextColor(WHITE);
39 oled.setCursor(27,30);
CHAPTER 8. READ A TRIMPOT USING ADC 60

40 oled.print("Trimpot: ");
41 oled.print(trimpotValue);
42 oled.display();
43
44 delay(1000);
45 }

8.4 Summary
The ADC is used for reading an analog voltage. The ADC quantizes
and samples the analog voltage to produce the digital value. To use
ESP8266’s ADC, you can just call the analogRead function.

8.5 Coding Challenge


In this coding challenge, you should read a trimpot, and then use
the trimpot value as input for cycling an RGB LED through the color
spectrum. This is like the coding challenge in chapter 4, but in-
stead of three input (RGB value), you should use one input (trimpot
value).This can be done by using HSV to RGB algorithm [7]. Given
an HSV color with hue 0◦ ≤ H < 360◦ , saturation 0 ≤ S ≤ 1, and
value 0 ≤ V ≤ 1. The R, G, and B values can be calculated by using
the following equation:
C =V ×S (8.1)
H
H0 = (8.2)
60◦
X = C × (1 − |H 0 mod 2 − 1|) (8.3)
CHAPTER 8. READ A TRIMPOT USING ADC 61



 (C, X, 0) if 0◦ ≤ H < 60◦
60◦ ≤ H < 120◦

(X, C, 0) if



120◦ ≤ H < 180◦

(0, C, X)
0 0 0 if
(R , G , B ) = (8.4)


 (0, X, C) if 180◦ ≤ H < 240◦
(X, 0, C) if 240◦ ≤ H < 300◦





(C, 0, X) if 300◦ ≤ H < 360◦

m=V −C (8.5)
0 0 0
(R, G, B) = ((R + m) × 255, (G + m) × 255, (B + m) × 255) (8.6)
For this coding challenge, the trimpot value is used as input for H
value, while the S and V values are set to 1, so you have only one
input which is the trimpot value. By using equation 8.1–8.6, you can
calculate the R, G, and B values. Create a function for implemeting
this algorithm, and send the RGB value to the RGB LED!
Chapter 9

Serial Communication
between ESP8266 and PC

In chapter 6–8, you have learned how to use simple sensors, which
are button and trimpot. In this chapter, you will learn how to use
serial communication. Serial communication can be used for sending
or receiving data to or from PC. For example, you can send the trimpot
value to PC instead of displays it on the OLED display.
What will you learn in this chapter?

• Setup the serial communication.


• Send a message to PC using print or println method.

9.1 Serial Communication


Serial communication is the process of sending data one bit at a time,
serially[8]. There are many protocols that send data serially such as
SPI, I2C, USB, etc. The term serial in serial communication refers to

62
CHAPTER 9. SERIAL COMMUNICATION BETWEEN ESP8266 AND PC 63

Figure 9.1. DB9 connector for serial port. Retrieved September 26, 2018, from
www.wikipedia.com

Figure 9.2. UART data frame.

Universal Asynchronous Receiver Transmitter (UART) protocol. This


protocol is widely used for communication between device and PC.
In PC, the serial communication is called RS232 or serial port, which
uses DB9 connector as shown in Figure 9.1. In modern PC, the DB9
connector usually not avaliable, but the serial communication can still
be used through the USB port. The software in PC can access the
serial port through the COM interface. In Arduino IDE, there is a
tool for accessing the serial port, namely Serial Monitor. There is
also a tool called Serial Plotter. This tool is useful when the data are
numbers, and you want to plot the data in real-time.
The UART protocol uses two wires for sending and receiving data,
namely TX and RX. The UART data frame is shown in Figure 9.2.
Each of the data frame consists of 5–9 data bits with LSB first config-
uration. The commonly used data bits is 8 bits or 1 byte. There are
start bit and stop bit which are used for indicating where the first and
CHAPTER 9. SERIAL COMMUNICATION BETWEEN ESP8266 AND PC 64

the last data bits. The start bit is 0, and the stop bit is 1. The length
of the stop bit can be configured either 1, 1.5, or 2 bits. The parity
bit is an optional bit that can be used for detecting error. There are
two types of parity bits, namely even and odd parity. There are sev-
eral standards for baud rate or data speed such as 1200, 2400, 4800,
19200, 38400, 57600, and 115200 bps.

9.2 Serial Print


To use serial communication, you should initilize the transmision speed
(baud rate) by using begin method. This method is defined in a class
named HardwareSerial. The HardwareSerial class is defined
in HardwareSerial.h. This class is instantiated as an object called
Serial. Hence, you should call Serial.begin. The most commonly
used begin method is defined as
// baud: Use one of these rates: 300, 600, 1200,
// 2400, 4800, 9600, 14400, 19200, 28800,
// 38400, 57600, or 115200
void begin(unsigned long baud);

After the serial communication is initialized, you can send a mes-


sage through the serial communication by using print or println
methods. The println method adds a carriage return and a new-
line character (\r\n) at the end of the message. The print and
println methods can be used for all data types which are String,
char, int, long, and double.

9.3 Example Program


In this example program, you will use serial communication for sending
a message to PC. The code for the program is shown in Listing 9.1.
In line 8, the serial communication is initialized with the baud rate of
CHAPTER 9. SERIAL COMMUNICATION BETWEEN ESP8266 AND PC 65

9600 bps. In line 10, a ”Hello, world!” message is sent to the serial
communication.

Listing 9.1. Send a message to PC


1 // *** File : /esp8266-arduino/serial/serial.ino
2 // *** Author : Erwin Ouyang
3 // *** Date : 17 Agt 2018
4
5 void setup()
6 {
7 // Setup serial communication
8 Serial.begin(9600);
9 // Print text
10 Serial.println("Hello, world!");
11 }
12
13 void loop()
14 {
15 }

To open Serial Monitor, you can go to Tools → Serial Monitor menu.


If you do not see the ”Hello, world!” message in the Serial Monitor,
then you should open the Serial Monitor first before you upload the
program to the ESP8266 or after the program is uploaded, you can
press the NodeMCU’s reset button. This happens because the ”Hello,
world!” message is sent only once after you upload the program to the
ESP8266, i.e. because the ”Hello, world!” message is placed inside
the setup function. You can place the ”Hello, world!” message inside
the loop function, so the message is sent repeatedly.
CHAPTER 9. SERIAL COMMUNICATION BETWEEN ESP8266 AND PC 66

What are garbage characters?

When you reset the NodeMCU board, you probably see garbage characters
in Serial Monitor. These characters are appeared as garbage because the
baud rate between transmitter (ESP8266) and receiver (Serial Monitor)
is mismatch. Actually these characters are messages from ESP8266’s
bootloader. The bootloader uses baud rate of 74880 bps. You can see
the bootloader messages by changing the baud rate to 74880.

9.4 Summary
Serial communication is the simplest protocol for communication be-
tween ESP8266 and PC. The most commonly used methods in serial
communication are print and println.
Chapter 10

Data Logging to PC using


Serial

In chapter 9, you have learned how to use serial communication for


sending a message to PC. In this chapter, you will learn how to use
serial communication for logging ADC data to PC.
What will you learn in this chapter?

• Send the trimpot value to PC using printf method.


• Plot the trimpot value using Serial Plotter tool.

10.1 Print Format


In C programming language, there is a function called printf, which
stands for ”print formatted”. The printf function uses format spec-
ifiers to print formatted output to console. Format specifiers start with
a % character, and indicate the location to translate a piece of data
(char, int, float, etc.) to characters. The example of printf func-

67
CHAPTER 10. DATA LOGGING TO PC USING SERIAL 68

Figure 10.1. The example of printf function. Retrieved October 15, 2018, from
www.wikipedia.com

Table 10.1. The commonly used format specifiers [9]

Specifier Output Example


%d or %i Signed decimal integer 392
%x Unsigned hexadecimal integer 7fa
%X Unsigned hexadecimal integer (uppercase) 7FA
%o Unsigned octal 610
%f Decimal floating point 392.65
%c Character a
%s String of characters sample

Table 10.2. The example of commonly used sub-specifiers [9]

Sub-specifier Output Example


%08d Preceding with zeros 00001977
%#x Preceding with 0x 0x7fa
%.2f Number of digit after decimal point 3.14

tion is shown in Figure 10.1. The commonly used format specifiers are
shown in Table 10.1. The specifiers can also contain sub-specifiers,
which are optional. The example of commonly used sub-specifiers are
shown in Table 10.2. In Arduino library, the printf function is added
to the Serial class, so you can use the printf method for sending
data through serial communication.
CHAPTER 10. DATA LOGGING TO PC USING SERIAL 69

How to print a number in binary?

The printf method is only able to print a number in base 8 (octal), 10


(decimal), and 16 (hexadecimal), so there is no specifier for binary inte-
ger. Alternatively, you can use the print or println method, which
has an optional second parameter specifies the base to use. The op-
tional second parameters are BIN, OCT, DEC, and HEX. For example,
Serial.print(78, BIN) gives ”1001110”.

10.2 Example Program


In this example program, you will read a trimpot using analogRead
function, then send the value to the PC using printf method. The
code for the program is shown in Listing 10.1. In line 17, the trimpot is
read, and then in line 19, the value is sent to the PC. The trimpot value
is formatted with an escape sequence \n, which is a newline character.
There are several commonly used escape sequences as shown in Table
10.3.
Listing 10.1. Log the trimpot value to PC
1 // *** File : /esp8266-arduino/serial-data-logger/
2 // serial-data-logger.ino
3 // *** Author : Erwin Ouyang
4 // *** Date : 17 Agt 2018
5
6 int trimpotValue;
7
8 void setup()
9 {
10 // Setup serial communication
11 Serial.begin(74880);
12 }
13
14 void loop()
CHAPTER 10. DATA LOGGING TO PC USING SERIAL 70

Table 10.3. The commonly used escape sequences

Escape Sequence Character Represented


\b Backspace
\n Newline
\r Carriage return
\t Horizontal tab
\\ Backslash
\’ Single quotation mark
\” Double quotation mark

15 {
16 // Read analog value
17 trimpotValue = analogRead(A0);
18 // Send trimpot value to serial monitor
19 Serial.printf("%d\n", trimpotValue);
20 delay(1000);
21 }

10.3 Serial Plotter


To display the trimpot value, you can use the Serial Monitor, but there
is another tool useful for displaying variables, namely Serial Plotter.
With Serial Plotter, you can plot the trimpot value as shown in Figure
10.2. You can visualize the trimpot value as a waveform in real-time.
To open Serial Plotter, you can go to Tools → Serial Plotter menu.
To plot one variable in Serial Plotter, you just need to use printf
method with a newline character at the end of the value as in line 19.
You can plot multiple variables in Serial Plotter by separating each of
the variable with a space or a comma, and end with newline. The
example code how to plot three variables is shown in the following
code:
CHAPTER 10. DATA LOGGING TO PC USING SERIAL 71

Figure 10.2. Serial Plotter tool.

// Using spaces as the separator, or


Serial.printf("%d %d %d\n", var1, var2, var3);
// using commas as the separator
Serial.printf("%d,%d,%d\n", var1, var2, var3);

10.4 Summary
Data logging using serial communication is the simplest way to display
value from trimpot or other sensors. You can also visualize a variable
as a waveform in real-time using Serial Plotter tool.
Chapter 11

Receive Data from PC using


Serial

In chapter 9 and 10, you have learned how to send data to PC using
serial communication. In this chapter, you will learn how to use serial
communication for receiving data from PC.
What will you learn in this chapter?

• Receive a message from PC using readString and read


StringUntil methods.
• Control the LED from PC using Serial Monitor tool.

11.1 Serial Read


There are several methods that will be used for receiving data from PC
using serial communication, namely available, readString, and
readStringUntil. The available method returns the number
of data bytes available to read from the receive buffer (which holds 64

72
CHAPTER 11. RECEIVE DATA FROM PC USING SERIAL 73

bytes). The available method is defined in HardwareSerial.


h. The available method is defined as
// return: The number of bytes available to read
int available(void);

The readString method returns a string read from the receive


buffer. The readString method is defined in Stream.h. The
readString method is defined as
// return: A string read from the receive buffer
String readString();

The readStringUntil method returns the entire string read from


the receive buffer, until the terminator character is detected. The
readStringUntil method is defined in Stream.h. The read
StringUntil method is defined as
// terminator: The character to search for (char)
// return : The entire string read from
// the receive buffer, until the
// terminator character is detected
String readStringUntil(char terminator);

How to read a number using serial communication?

A number can be received from PC as a string, then it is converted to an


int or a float by using toInt or toFloat method, repectively.

11.2 Example Program


In the first example program, you will read a message from PC using
readString method. The code for the program is shown in Listing
11.1. In line 17, the available data bytes in the receive buffer are
checked by using available method. If the data bytes are available,
CHAPTER 11. RECEIVE DATA FROM PC USING SERIAL 74

then the data bytes are read from the receive buffer by using read
String method. The data bytes are stored in a variable called se
rialInput. The data bytes in the variable serialInput are sent
back to the PC by using println method. To test the program,
you can open the Serial Monitor, and then you can send a message to
ESP8266. The message will be echoed back to the Serial Monitor.
Listing 11.1. Receive a message from PC
1 // *** File : /esp8266-arduino/serial-receive-data/
2 // serial-receive-data.ino
3 // *** Author : Erwin Ouyang
4 // *** Date : 17 Agt 2018
5
6 String serialInput;
7
8 void setup()
9 {
10 // Setup serial communication
11 Serial.begin(74880);
12 }
13
14 void loop()
15 {
16 // If there is data in receive buffer
17 if (Serial.available() > 0)
18 {
19 // Read string from receive buffer
20 serialInput = Serial.readString();
21 // Print to serial monitor
22 Serial.println(serialInput);
23 }
24 }

In the second example program, you will create a program for turning
on or off the LED from PC using serial communication. The code for
the program is shown in Listing 11.2. In main program, the data bytes
CHAPTER 11. RECEIVE DATA FROM PC USING SERIAL 75

are read by using readStringUntil method as shown in line 21.


This method will read a string, which is a command for turning on
or off the LED from PC. Every command from Serial Monitor will be
sent with a newline ending character.

Listing 11.2. Turn on or off the LED using serial


1 // *** File : /esp8266-arduino/serial-led/
2 // serial-led.ino
3 // *** Author : Erwin Ouyang
4 // *** Date : 17 Agt 2018
5
6 String cmd;
7
8 void setup()
9 {
10 // Set pin as output
11 pinMode(D3, OUTPUT);
12 // Setup serial communication
13 Serial.begin(74880);
14 }
15
16 void loop()
17 {
18 if (Serial.available() > 0)
19 {
20 // Read command from receive buffer
21 cmd = Serial.readStringUntil(’\n’);
22
23 // *** Process the command ***
24 if (cmd == "LED+ON") // Turn on LED
25 {
26 digitalWrite(D3, HIGH);
27 }
28 else if (cmd == "LED+OFF") // Turn off LED
29 {
30 digitalWrite(D3, LOW);
CHAPTER 11. RECEIVE DATA FROM PC USING SERIAL 76

31 }
32 else if (cmd == "LED") // Ask the LED value
33 {
34 if (digitalRead(D3))
35 Serial.println("LED is on");
36 else
37 Serial.println("LED is off");
38 }
39 else
40 {
41 Serial.println("Unknown command");
42 }
43 }
44 }

There are three commands defined for controlling the LED, namely
LED+ON, LED+OFF, and LED. The LED+ON command is used for
turning on the LED. The LED+OFF command is used for turning off
the LED. The LED command is used for asking the current LED state.
The commands are processed by using the if statement as shown in
line 24–42. To turn on or off the LED, you can use the digital
Write function as shown in line 26 and 30. To read the state of the
LED, you can use the digitalRead function as shown in line 34. To
test the program, you can send the command (LED+ON, LED+OFF,
or LED) from the Serial Monitor. The ending character of the Serial
Monitor must be set to Newline as shown in Figure 11.1.
In the third example program, you will create a program for changing
the brightness of the LED from PC using serial communication. The
code for the program is shown in Listing 11.3. In main program, the
data bytes are read by using readStringUntil method. The data
bytes are the PWM value, and it is stored in a variable called pwm
Value. The variable pwmValue is a string, so it must be converted
to an integer, in order to be used as input for analogWrite function.
The variable pwmValue is converted to an integer by using toInt
CHAPTER 11. RECEIVE DATA FROM PC USING SERIAL 77

Figure 11.1. Newline ending in Serial Monitor tool.

method as shown in line 21. To test the program, you can send a
number between 0–1023 from the Serial Monitor.

Listing 11.3. Change the LED brightness using serial


1 // *** File : /esp8266-arduino/serial-led-pwm/
2 // serial-led-pwm.ino
3 // *** Author : Erwin Ouyang
4 // *** Date : 17 Agt 2018
5
6 String pwmValue;
7
8 void setup()
9 {
10 // Setup serial communication
11 Serial.begin(74880);
12 }
13
14 void loop()
15 {
16 if (Serial.available() > 0)
CHAPTER 11. RECEIVE DATA FROM PC USING SERIAL 78

17 {
18 // Read PWM value from receive buffer
19 pwmValue = Serial.readStringUntil(’\n’);
20 // Process the command
21 analogWrite(D3, pwmValue.toInt());
22 }
23 }

11.3 Summary
To receive data from PC, you can use the readString or read
StringUntil methods. You can use the serial communication for
controlling the LED or other actuators from PC by defining your own
commands.

11.4 Coding Challenge


In this coding challenge, you should modify the program in Listing 11.2
by adding two new commands for setting the color of the RGB LED and
displaying a message on OLED display. The command for RGB LED
should follow this format: RGB+<red>,<green>,<blue>. The
command for OLED display should follow this format: OLED+<len>
+<message>, where len is the number of characters of the message.
In C library, there is a function called strtok. This function is used
for splitting a string into tokens. You can use this function for splitting
the RGB LED and OLED command. The example how to use this
function is shown in the following code:
String str_s = "Split+string,into,tokens.";
char str_c[64];
char *chr_p;

// Convert string to array of char


CHAPTER 11. RECEIVE DATA FROM PC USING SERIAL 79

str_s.toCharArray(str_c, 64);

// *** Split string into tokens ***


chr_p = strtok(str_c, "+,.");
while (chr_p != NULL)
{
Serial.printf("%s\n", chr_p);
chr_p = strtok(NULL, "+,.");
}

By using strtok function, split the RGB LED and OLED display
commands, and then procces the commands!
Chapter 12

Measure Time using a Timer

In chapter 9–11, you have learned how to use serial communication


for sending and receiving data to and from PC. In this chapter, you
will learn how to measure time using a timer.
What will you learn in this chapter?

• Measure time using millis or micros function.


• Blink an LED without using delay function.

12.1 Timer
A timer is one of the basic peripherals that almost all microcontrollers
have it. The easiest way how to use the ESP8266’s timer is by using
millis or micros function. The millis and micros functions
return the number of milliseconds and microseconds since the program
started, respectively. The millis function is defined as
// return: Number of milliseconds since
// the program started

80
CHAPTER 12. MEASURE TIME USING A TIMER 81

unsigned long millis()

The micros function is defined as


// return: Number of microseconds since
// the program started
unsigned long micros()

For more information about the millis and micros functions, you
can see in core_esp8266_wiring.c.

12.2 Example Program


In the first example program, you will use the millis function to read
the current milliseconds value. The code for the program is shown in
Listing 12.1. In line 16, the current milliseconds value is read, and then
it is stored in a variable called ms. The milliseconds value is printed to
the Serial Monitor by using println function every 1 second. The
milliseconds value printed on the Serial Monitor is incremented every
1000 because of the 1 second delay.

Listing 12.1. Measure time using millis function


1 // *** File : /esp8266-arduino/timer/timer.ino
2 // *** Author : Erwin Ouyang
3 // *** Date : 17 Agt 2018
4
5 unsigned long ms;
6
7 void setup()
8 {
9 // Setup serial communication
10 Serial.begin(74880);
11 }
12
13 void loop()
CHAPTER 12. MEASURE TIME USING A TIMER 82

14 {
15 // Get milliseconds value
16 ms = millis();
17 // Send milliseconds value to serial monitor
18 Serial.println(ms);
19 delay(1000);
20 }

In the second example program, you will blink an LED without using
delay fucntion. This method is useful when you need to do more
than one thing at once. For example, you need to blink an LED and
also read a button. In this case, if you use the delay function, then
the button response is slow. This happens because the CPU needs to
wait until the delay is passed. To overcome this problem, you can
use the external interrupt as in chapter 7, but in this example, you
will use another method, which is the timer millis. The code for the
program is shown in Listing 12.2.
In main program, there are codes for blinking the red LED and for
reading the button state. For blinking the LED, you need two vari-
ables called now and last. These variables are used for storing the
milliseconds values. In line 23, the current milliseconds value is read
and stored in variable now. On every loop, the variable now is checked
by subtracting it with the variable last, which is start from 0. If the
result is larger than 1000, then 1000 milliseconds have passed, so you
should toggle the LED. The variable last should be updated to be
used on the next loop. For the button code, the button state is read,
and then turn on the RGB LED if the button is pressed, otherwise
turn off the RGB LED.

Listing 12.2. Blink an LED without using delay function


1 // *** File : /esp8266-arduino/led-without-delay/
2 // led-without-delay.ino
3 // *** Author : Erwin Ouyang
4 // *** Date : 17 Agt 2018
CHAPTER 12. MEASURE TIME USING A TIMER 83

5
6 unsigned long now = 0;
7 unsigned long last = 0;
8
9 void setup()
10 {
11 // Set pin as output
12 pinMode(D3, OUTPUT);
13 // Set pin as input pull-up
14 pinMode(D6, INPUT_PULLUP);
15 // Set PWM range from 0 to 255
16 analogWriteRange(255);
17 }
18
19 void loop()
20 {
21 // *** Blink the red LED ***
22 // Read current millis value
23 now = millis();
24 // If 1 second has been elapsed
25 if ((now - last) > 1000)
26 {
27 // Toggle the LED
28 digitalWrite(D3, !digitalRead(D3));
29 // Save current millis value as a
30 // reference for next loop
31 last = now;
32 }
33
34 // *** Read button state ***
35 if (digitalRead(D6) == LOW)
36 rgbLedWrite(255, 255, 255);
37 else
38 rgbLedWrite(0, 0, 0);
39 }
40
41 void rgbLedWrite(byte red, byte green, byte blue)
CHAPTER 12. MEASURE TIME USING A TIMER 84

42 {
43 analogWrite(D5, 255-red);
44 analogWrite(D7, 255-green);
45 analogWrite(D8, 255-blue);
46 }

12.3 Ticker
Ticker is a library for calling functions periodically. Ticker works like
a timer interrupt occurrs every a certain period. The ticker example
is shown in Listing 12.3. To use the ticker, you should include the
Ticker.h library as shown in line 5. In line 7, the ticker object is
created. In line 14, a function called tickerCallback is attached
to the ticker by using attach method. The tickerCallback
function will be executed every 1 second. In the tickerCallback
function, there is code for blinking the LED.

Listing 12.3. Blink an LED using ticker


1 // *** File : /esp8266-arduino/ticker/ticker.ino
2 // *** Author : Erwin Ouyang
3 // *** Date : 17 Agt 2018
4
5 #include <Ticker.h>
6
7 Ticker ticker;
8
9 void setup()
10 {
11 // Set pin as output
12 pinMode(D3, OUTPUT);
13 // Set ticker to generate interrupt every 1 second
14 ticker.attach(1, tickerCallback);
15 }
16
CHAPTER 12. MEASURE TIME USING A TIMER 85

17 void tickerCallback()
18 {
19 // Toggle LED
20 digitalWrite(D3, !digitalRead(D3));
21 }
22
23 void loop()
24 {
25 }

12.4 Summary
The millis and micros functions are used for getting the current
milliseconds and microseconds value, respectively since the program
started. You can call these functions periodically in order to blink the
LED without the delay function.

12.5 Coding Challenge


In this coding challenge, you should create a dual function button
program, i.e. a program that can detect a short button press and a
long button press (press and hold) with only one button. You can use
the timer for measuring the button press. Use the external interrupt
on the GPIO input pin!
Chapter 13

DS1307 Real-Time Clock

In chapter 12, you have learned how to measure time using timer. In
this chapter, you will learn how to use a Real-Time Clock (RTC). You
will read the time and date from DS1307 RTC, and send them to PC
using serial communication. You will also use the OLED display for
displaying the time and date.
What will you learn in this chapter?

• Read the time and date from DS1307 RTC.


• Send the time and date to PC using serial communication.
• Display the time and date on OLED display.

13.1 Real-Time Clock


A Real-Time Clock (RTC) is used for keeping time. The RTC works
like a watch, and can run on a battery, so it works even there is a
power outage. The DS1307 is a popular and low cost RTC. The
DS1307 can be connected to ESP8266 by using I2C bus. To create a

86
CHAPTER 13. DS1307 REAL-TIME CLOCK 87

program for DS1307, you need an external library. The external library
used in this chapter is RTC by Makuna (https://github.com/
Makuna/Rtc).

13.2 Example Program


In the first example program, you will read the time and date from
DS1307, and send them to PC using serial communication. The code
for the program is shown in Listing 13.1. In line 5 and 6, you should
include the Wire.h and RtcDS1307.h libraries. In line 9, the rtc
object is created, and you should initialize it by using Begin method
as in line 16. In line 20–23, the DS1307’s time and date is set to
the compiled time of this code, and the DS1307 is enabled by using
SetIsRunning method.
In main program, the time and date are read by using GetDateTime
method. The GetDateTime method returns the time and date, and
then they are stored in a structure called RtcTimeDate as shown in
line 29. In line 32, you should send the time and date to PC using
printf method. The time and date are sent to PC every 1 second.

Listing 13.1. Log the DS1307 time and date to PC


1 // *** File : /esp8266-arduino/ds1307/ds1307.ino
2 // *** Author : Erwin Ouyang
3 // *** Date : 17 Agt 2018
4
5 #include <Wire.h>
6 #include <RtcDS1307.h>
7
8 // RTC object declaration
9 RtcDS1307<TwoWire> rtc(Wire);
10
11 void setup()
12 {
13 // Setup serial communication
CHAPTER 13. DS1307 REAL-TIME CLOCK 88

14 Serial.begin(74880);
15 // Initialize RTC
16 rtc.Begin();
17
18 // *** Set RTC date and time to code
19 // compiled time ***
20 RtcDateTime compiled = RtcDateTime(__DATE__,
21 __TIME__);
22 rtc.SetDateTime(compiled);
23 rtc.SetIsRunning(true);
24 }
25
26 void loop()
27 {
28 // Read RTC date and time
29 RtcDateTime now = rtc.GetDateTime();
30 // *** Send RTC date and time to serial
31 // monitor ***
32 Serial.printf("%04d/%02d/%02d %02d:%02d:%02d\n",
33 now.Year(), now.Month(), now.Day(),
34 now.Hour(), now.Minute(), now.Second());
35 delay(1000);
36 }

In the second example, you should modify the code in the previous
example. Instead of sending the time and date to PC, you can display
them on OLED display. The code for the program is shown in Listing
13.2. The code is similar to the previous example, but you should add
the OLED display. In line 48–55, the time and date are displayed on
the OLED display.

Listing 13.2. Display the DS1307 time and date on OLED display
1 // *** File : /esp8266-arduino/ds1307-oled-display/
2 // ds1307-oled-display.ino
3 // *** Author : Erwin Ouyang
4 // *** Date : 17 Agt 2018
CHAPTER 13. DS1307 REAL-TIME CLOCK 89

5
6 #include <Wire.h>
7 #include <Adafruit_SSD1306.h>
8 #include <Adafruit_GFX.h>
9 #include <RtcDS1307.h>
10
11 // *** Check library setting ***
12 #if (SSD1306_LCDHEIGHT != 64) // 128 x 64 pixel display
13 #error("Height incorrect, please fix Adafruit_SSD1306.h!");
14 #endif
15
16 // OLED I2C address
17 #define OLED_ADDR 0x3C
18
19 // OLED object declaration
20 Adafruit_SSD1306 oled;
21 // RTC object declaration
22 RtcDS1307<TwoWire> rtc(Wire);
23
24 void setup()
25 {
26 // *** Initialize and clear display ***
27 oled.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR);
28 // Clear OLED buffer
29 oled.clearDisplay();
30
31 // Initialize RTC
32 rtc.Begin();
33
34 // *** Set RTC date and time to code
35 // compiled time ***
36 RtcDateTime compiled = RtcDateTime(__DATE__,
37 __TIME__);
38 rtc.SetDateTime(compiled);
39 rtc.SetIsRunning(true);
40 }
41
CHAPTER 13. DS1307 REAL-TIME CLOCK 90

42 void loop()
43 {
44 // Read RTC date and time
45 RtcDateTime now = rtc.GetDateTime();
46
47 // *** Display RTC date and time on OLED ***
48 oled.clearDisplay();
49 oled.setTextSize(1);
50 oled.setTextColor(WHITE);
51 oled.setCursor(8, 30);
52 oled.printf("%04d/%02d/%02d %02d:%02d:%02d",
53 now.Year(), now.Month(), now.Day(),
54 now.Hour(), now.Minute(), now.Second());
55 oled.display();
56
57 delay(1000);
58 }

13.3 Summary
To set the DS1307’s time and date, you can use the SetDateTime
method. To start the DS1307, you can use the SetIsRunning
method. To get the current time and date, you can use the GetDate
Time method.
Chapter 14

DHT11 Temperature and


Humidity Sensor

In chapter 13, you have learned how to use the DS1307 RTC. In this
chapter, you will learn how to measure temperature and humidity using
DHT11 sensor. The temperature and humidity are sent to PC by using
serial communication, and also displayed on the OLED display.
What will you learn in this chapter?

• Read the temperature and humidity from DHT11 sensor.


• Send the temperature and humidity to PC using serial communi-
cation.
• Display the temperature and humidity on OLED display.

14.1 Temperature and Humidity Sensor


The DHT11 sensor is a popular and low cost temperature and humid-
ity sensor. This sensor uses a digital interface, which is the 1-wire

91
CHAPTER 14. DHT11 TEMPERATURE AND HUMIDITY SENSOR 92

bus, so you do not need an ADC. To create a program for DHT11,


you need two external libraries. The first library is Adafruit sensor
(https://github.com/adafruit/Adafruit_Sensor), and
the second library is DHT sensor library (https://github.com/
adafruit/DHT-sensor-library).

14.2 Example Program


In the first example program, you will read the temperature and humid-
ity from DHT11, and send them to PC using serial communication.
The code for the program is shown in Listing 14.1. In line 5, you
should include the DHT.h library. In line 8, the dht object is created.
It has two input parameters, which are the DHT GPIO pin and the
DHT type, which is DHT11 (because this library can also be used for
DHT22 sensor). In line 15, the DHT11 is initialized by using begin
method.
In main program, you can use the readTemperature and readHu
midity methods for reading the temperature and humidity. In line
22, the value true is passed as input parameter for readTemper
ature method. This paratemer is used to return the temperature
in Fahrenheit. The temperature and humidity are sent to PC using
printf function as shown in line 27. You should add 2 seconds delay,
because the minimum reading period of the DHT11 is 2 seconds, i.e.
the DHT11 is a very slow sensor.
Listing 14.1. Log the DHT11 temperature and humidity to PC
1 // *** File : /esp8266-arduino/dht11/dht11.ino
2 // *** Author : Erwin Ouyang
3 // *** Date : 17 Agt 2018
4
5 #include <DHT.h>
6
7 // DHT object declaration
8 DHT dht(D4, DHT11);
CHAPTER 14. DHT11 TEMPERATURE AND HUMIDITY SENSOR 93

9
10 void setup()
11 {
12 // Setup serial communication
13 Serial.begin(74880);
14 // Initialize DHT11
15 dht.begin();
16 }
17
18 void loop()
19 {
20 // *** Read temperature and humidity ***
21 float celcius = dht.readTemperature();
22 float fahrenheit = dht.readTemperature(true);
23 float humidity = dht.readHumidity();
24
25 // *** Send temperature and humidity to
26 // serial monitor ***
27 Serial.printf("Temperature: %.0fC, %.0fF " \
28 "Humidity: %.0f%%\n", celcius,
29 fahrenheit, humidity);
30
31 delay(2000);
32 }

In the second example program, you should modify the code in the
previous example. Instead of sending the temperature and humidity to
PC, you can display them on OLED display. The code for the program
is shown in Listing 14.2. The code is similar to the previous example,
but you should use OLED display instead of serial communication. In
main program, the temperature and humidity are read and sent to the
OLED display every 3 seconds.
Listing 14.2. Display the DHT11 temperature and humidity on OLED display
1 // *** File : /esp8266-arduino/dht11-oled-display/
2 // dht11-oled-display.ino
CHAPTER 14. DHT11 TEMPERATURE AND HUMIDITY SENSOR 94

3 // *** Author : Erwin Ouyang


4 // *** Date : 17 Agt 2018
5
6 #include <Wire.h>
7 #include <Adafruit_SSD1306.h>
8 #include <Adafruit_GFX.h>
9 #include "DHT.h"
10
11 // *** Check library setting ***
12 #if (SSD1306_LCDHEIGHT != 64) // 128 x 64 pixel display
13 #error("Height incorrect, please fix Adafruit_SSD1306.h!");
14 #endif
15
16 // OLED I2C address
17 #define OLED_ADDR 0x3C
18
19 // OLED object declaration
20 Adafruit_SSD1306 oled;
21 // DHT object declaration
22 DHT dht(D4, DHT11);
23
24 void setup()
25 {
26 // *** Initialize and clear display ***
27 oled.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR);
28 oled.clearDisplay();
29
30 // Initialize DHT11
31 dht.begin();
32 }
33
34 void loop()
35 {
36 // *** Read temperature and humidity ***
37 float celcius = dht.readTemperature();
38 float humidity = dht.readHumidity();
39
CHAPTER 14. DHT11 TEMPERATURE AND HUMIDITY SENSOR 95

40 // *** Display temperature and humidity on OLED ***


41 oled.clearDisplay();
42 oled.setTextSize(2);
43 oled.setTextColor(WHITE);
44 oled.setCursor(45, 12);
45 oled.printf("%.0f%cC", celcius, (char)247);
46 oled.setCursor(50, 38);
47 oled.printf("%.0f%%", humidity);
48 oled.display();
49
50 delay(3000);
51 }

14.3 Summary
To get the temperature and humidity values, you can use the read
Temperature and readHumidity methods. The DHT11 is a slow
sensor, which the minimum reading period is 2 seconds.

14.4 Coding Challenge


In this coding challenge, you should display the temperature and hu-
midity from the DHT11 along with the time and date from the DS1307
on the OLED display. You should also display the temperature and
humidity symbols as shown in Figure 14.1. The DS1307 is read every
1 second, while the DHT11 is read every 3 seconds.
CHAPTER 14. DHT11 TEMPERATURE AND HUMIDITY SENSOR 96

Figure 14.1. Time, date, temperature, and humidity are displayed on OLED display.
Bibliography

[1] Wikipedia, “Esp8266.” https://en.wikipedia.org/


wiki/ESP8266. Accessed on 2018-08-25.
[2] Arduino, “Pwm.” https://www.arduino.cc/en/
Tutorial/PWM. Accessed on 2018-09-11.
[3] ESP8266, “Esp8266 arduino core reference.” http:
//esp8266.github.io/Arduino/versions/2.0.0/
doc/reference.html. Accessed on 2018-09-11.
[4] Diarmuid, “Pwm exponential led fading on arduino (or
other platforms).” https://diarmuid.ie/blog/pwm-
exponential-led-fading-on-arduino-or-other-
platforms. Accessed on 2018-10-7.
[5] A. Greenste, “Switch debouncing.” http://www.
labbookpages.co.uk/electronics/debounce.html.
Accessed on 2018-10-14.
[6] Wikipedia, “Interrupt.” https://en.wikipedia.org/
wiki/Interrupt. Accessed on 2018-09-20.
[7] RapidTables, “Hsv to rgb color conversion.” https:
//www.rapidtables.com/convert/color/hsv-to-
rgb.html. Accessed on 2018-10-14.

97
BIBLIOGRAPHY 98

[8] Wikipedia, “Serial communication.” https://en.


wikipedia.org/wiki/Serial_communication. Ac-
cessed on 2018-10-14.
[9] cplusplus, “printf.” www.cplusplus.com/reference/
cstdio/printf/. Accessed on 2018-10-16.
Solution to Coding Challenge

Chapter 3
Non-linear LED dimming
1 // *** File : /esp8266-arduino/led-pwm-non-linear/
2 // led-pwm-non-linear.ino
3 // *** Author : Erwin Ouyang
4 // *** Date : 17 Agt 2018
5
6 void setup()
7 {
8 }
9
10 void loop()
11 {
12 // *** Increase brightness ***
13 for (int i = 0; i <= 1023; i += 20)
14 {
15 analogWrite(D3, pow(2, (i/102.3))-1);
16 delay(25);
17 }
18 // *** Decrease brightness ***
19 for (int i = 1023; i >= 0; i -= 20)
20 {
21 analogWrite(D3, pow(2, (i/102.3))-1);

99
SOLUTION TO CODING CHALLENGE 100

22 delay(25);
23 }
24 }

Chapter 4
RGB LED color spectrum
1 // *** File : /esp8266-arduino/rgb-led-spectrum/
2 // rgb-led-spectrum.ino
3 // *** Author : Erwin Ouyang
4 // *** Date : 17 Agt 2018
5
6 // RGB color, starts with red
7 byte rgb[3] = {255, 0, 0};
8
9 void setup()
10 {
11 // Set PWM range from 0 to 255
12 analogWriteRange(255);
13 }
14
15 void loop()
16 {
17 for (int i = 0; i <= 2; i++)
18 {
19 // *** Select which colors to decrement and
20 // increment ***
21 int dec = i;
22 int inc = (dec + 1) % 3;
23
24 // *** Decrement and increment the colors ***
25 for (int j = 0; j < 255; j++)
26 {
27 rgb[dec] -= 1;
28 rgb[inc] += 1;
SOLUTION TO CODING CHALLENGE 101

29 rgbLedWrite(rgb[0], rgb[1], rgb[2]);


30 delay(5);
31 }
32 }
33 }
34
35 void rgbLedWrite(byte red, byte green, byte blue)
36 {
37 analogWrite(D5, 255-red);
38 analogWrite(D7, 255-green);
39 analogWrite(D8, 255-blue);
40 }

Chapter 8
RGB LED color spectrum using trimpot
1 // *** File : /esp8266-arduino/trimpot-rgb-led/
2 // rgb-led.ino
3 // *** Author : Erwin Ouyang
4 // *** Date : 17 Agt 2018
5
6 #include<math.h>
7
8 int trimpotValue;
9
10 uint8_t r, g, b;
11
12 void setup()
13 {
14 // Set PWM range from 0 to 255
15 analogWriteRange(255);
16 }
17
18 void loop()
19 {
SOLUTION TO CODING CHALLENGE 102

20 // Read trimpot value


21 trimpotValue = analogRead(A0);
22 // Map trimpot value from 0-1023 to 0-359
23 trimpotValue = map(trimpotValue, 0, 1023, 0, 359);
24 // Convert trimpot value to RGB value
25 hsvToRgb(trimpotValue, 1.0, 1.0, &r, &g, &b);
26 // Send RGB value to RGB LED
27 rgbLedWrite(r, g, b);
28
29 delay(10);
30 }
31
32 void hsvToRgb(uint16_t H, float S, float V,
33 uint8_t *R, uint8_t *G, uint8_t *B)
34 {
35 float R_a, G_a, B_a;
36
37 // Calculate C
38 float C = V * S;
39 // Calculte X
40 float X = C * (1.0 - fabs(fmod(H/60.0, 2.0) - 1.0));
41 // *** Calculate R’, G’, and B’ ***
42 if ((H >= 0) && (H < 60))
43 {
44 R_a = C; G_a = X; B_a = 0;
45 }
46 else if ((H >= 60) && (H < 120))
47 {
48 R_a = X; G_a = C; B_a = 0;
49 }
50 else if ((H >= 120) && (H < 180))
51 {
52 R_a = 0; G_a = C; B_a = X;
53 }
54 else if ((H >= 180) && (H < 240))
55 {
56 R_a = 0; G_a = X; B_a = C;
SOLUTION TO CODING CHALLENGE 103

57 }
58 else if ((H >= 240) && (H < 300))
59 {
60 R_a = X; G_a = 0; B_a = C;
61 }
62 else if ((H >= 300) && (H < 360))
63 {
64 R_a = C; G_a = 0; B_a = X;
65 }
66 // Calculate m
67 float m = V - C;
68 // *** Calculate R, G, and B ***
69 *R = (uint8_t)((R_a + m) * 255);
70 *G = (uint8_t)((G_a + m) * 255);
71 *B = (uint8_t)((B_a + m) * 255);
72 }
73
74 void rgbLedWrite(byte red, byte green, byte blue)
75 {
76 analogWrite(D5, 255-red);
77 analogWrite(D7, 255-green);
78 analogWrite(D8, 255-blue);
79 }

Chapter 11
RGB LED and OLED commands
1 // *** File : /esp8266-arduino/serial-strtok/
2 // serial-strtok.ino
3 // *** Author : Erwin Ouyang
4 // *** Date : 17 Agt 2018
5
6 #include <Wire.h>
7 #include <Adafruit_SSD1306.h>
8 #include <Adafruit_GFX.h>
SOLUTION TO CODING CHALLENGE 104

9
10 // *** Check library setting ***
11 #if (SSD1306_LCDHEIGHT != 64) // 128 x 64 pixel display
12 #error("Height incorrect, please fix Adafruit_SSD1306.h!");
13 #endif
14
15 // OLED I2C address
16 #define OLED_ADDR 0x3C
17
18 // OLED object declaration
19 Adafruit_SSD1306 oled;
20
21 String cmd_s;
22 char cmd_c[64];
23 char *chr_p;
24
25 void setup()
26 {
27 // Set pin as output
28 pinMode(D3, OUTPUT);
29 // Set PWM range from 0 to 255
30 analogWriteRange(255);
31 // Setup serial communication
32 Serial.begin(74880);
33
34 // *** Initialize OLED display ***
35 oled.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR);
36 oled.clearDisplay();
37 oled.setTextSize(1);
38 oled.setTextColor(WHITE);
39 oled.display();
40 }
41
42 void loop()
43 {
44 if (Serial.available() > 0)
45 {
SOLUTION TO CODING CHALLENGE 105

46 // Read command from receive buffer


47 cmd_s = Serial.readStringUntil(’\n’);
48
49 // *** Split the command ***
50 cmd_s.toCharArray(cmd_c, 64);
51 chr_p = strtok(cmd_c, "+,");
52 String tokens[4];
53 uint8_t idx = 0;
54 while (chr_p != NULL)
55 {
56 tokens[idx++] = chr_p;
57 chr_p = strtok(NULL, "+,");
58 }
59
60 // *** Process the command ***
61 if (tokens[0] == "LED")
62 {
63 if (tokens[1] == "ON")
64 {
65 digitalWrite(D3, HIGH);
66 }
67 else if (tokens[1] == "OFF")
68 {
69 digitalWrite(D3, LOW);
70 }
71 else
72 {
73 if (digitalRead(D3))
74 Serial.println("LED is on");
75 else
76 Serial.println("LED is off");
77 }
78 }
79 else if (tokens[0] == "RGB")
80 {
81 rgbLedWrite(tokens[1].toInt(),
82 tokens[2].toInt(), tokens[3].toInt());
SOLUTION TO CODING CHALLENGE 106

83 }
84 else if (tokens[0] == "OLED")
85 {
86 oled.clearDisplay();
87 oled.setCursor(0, 0);
88 oled.printf("%s", tokens[2].c_str());
89 oled.display();
90 }
91 else
92 {
93 Serial.println("Unknown command");
94 }
95 }
96 }
97
98 void rgbLedWrite(byte red, byte green, byte blue)
99 {
100 analogWrite(D5, 255-red);
101 analogWrite(D7, 255-green);
102 analogWrite(D8, 255-blue);
103 }

Chapter 12
Dual function button
1 // *** File : /esp8266-arduino/button-dual-function/
2 // button-dual-function.ino
3 // *** Author : Erwin Ouyang
4 // *** Date : 17 Agt 2018
5
6 unsigned long pressed = 0;
7 unsigned long released = 0;
8 uint8_t r = 0, g = 0, b = 0;
9
10 void setup()
SOLUTION TO CODING CHALLENGE 107

11 {
12 // Set pin as output
13 pinMode(D3, OUTPUT);
14 // Set pin as input pull-up
15 pinMode(D6, INPUT_PULLUP);
16 // Set external interrupt from pin D6 that is
17 // connected to button
18 attachInterrupt(digitalPinToInterrupt(D6),
19 isr, CHANGE);
20 // Set PWM range from 0 to 255
21 analogWriteRange(255);
22 }
23
24 void isr()
25 {
26 // *** Read button state ***
27 if (digitalRead(D6) == LOW)
28 {
29 pressed = millis();
30 }
31 else
32 {
33 released = millis();
34 if ((released - pressed) < 1000)
35 {
36 // Toggle the LED
37 digitalWrite(D3, !digitalRead(D3));
38 }
39 else
40 {
41 // *** Toggle the RGB LED ***
42 r ˆ= 0xFF;
43 g ˆ= 0xFF;
44 b ˆ= 0xFF;
45 rgbLedWrite(r, g, b);
46 }
47 }
SOLUTION TO CODING CHALLENGE 108

48 }
49
50 void loop()
51 {
52 }
53
54 void rgbLedWrite(byte red, byte green, byte blue)
55 {
56 analogWrite(D5, 255-red);
57 analogWrite(D7, 255-green);
58 analogWrite(D8, 255-blue);
59 }

Chapter 14
DHT11 and DS1307 on OLED display
1 // *** File : /esp8266-arduino/dht11-ds1307/
2 // dht11-ds1307.ino
3 // *** Author : Erwin Ouyang
4 // *** Date : 17 Agt 2018
5
6 #include <Wire.h>
7 #include <Adafruit_SSD1306.h>
8 #include <Adafruit_GFX.h>
9 #include <RtcDS1307.h>
10 #include <DHT.h>
11
12 // *** Check library setting ***
13 #if (SSD1306_LCDHEIGHT != 64) // 128 x 64 pixel display
14 #error("Height incorrect, please fix Adafruit_SSD1306.h!");
15 #endif
16
17 // OLED I2C address
18 #define OLED_ADDR 0x3C
19
SOLUTION TO CODING CHALLENGE 109

20 // Temperature logo
21 static const unsigned char PROGMEM temperature_bmp[] =
22 {
23 B00000011, B11001111,
24 B00000110, B01100000,
25 B00000100, B00101111,
26 B00000100, B00100000,
27 B00000100, B00101111,
28 B00000101, B10100000,
29 B00000101, B10101111,
30 B00000101, B10100000,
31 B00001101, B10110000,
32 B00011011, B11011000,
33 B00110111, B11101100,
34 B00101111, B11110100,
35 B00101111, B11110100,
36 B00110111, B11101100,
37 B00011000, B00011000,
38 B00001111, B11110000
39 };
40 // Humidity logo
41 static const unsigned char PROGMEM humidity_bmp[] =
42 {
43 B00000001, B10000000,
44 B00000011, B11000000,
45 B00000110, B01100000,
46 B00000100, B00100000,
47 B00001100, B00110000,
48 B00001000, B00010000,
49 B00011000, B00011000,
50 B00010000, B00001000,
51 B00110100, B00001100,
52 B00101100, B00000100,
53 B00101100, B00000100,
54 B00101100, B00000100,
55 B00110110, B00001100,
56 B00011011, B00011000,
SOLUTION TO CODING CHALLENGE 110

57 B00001100, B00110000,
58 B00000111, B11100000
59 };
60
61 // OLED object declaration
62 Adafruit_SSD1306 oled;
63 // RTC object declaration
64 RtcDS1307<TwoWire> rtc(Wire);
65 // DHT object declaration
66 DHT dht(D4, DHT11);
67
68 RtcDateTime now;
69 float celcius, humidity;
70 uint8_t second = 0;
71
72 void setup()
73 {
74 // *** Initialize and clear display ***
75 oled.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR);
76 // Clear OLED buffer
77 oled.clearDisplay();
78
79 // Initialize RTC
80 rtc.Begin();
81
82 // *** Set RTC date and time to code
83 // compiled time ***
84 RtcDateTime compiled = RtcDateTime(__DATE__,
85 __TIME__);
86 rtc.SetDateTime(compiled);
87 rtc.SetIsRunning(true);
88
89 // Initialize DHT11
90 dht.begin();
91 }
92
93 void loop()
SOLUTION TO CODING CHALLENGE 111

94 {
95 // Read RTC date and time
96 now = rtc.GetDateTime();
97
98 // *** Read temperature and humidity every 3
99 // seconds ***
100 if (second == 3)
101 {
102 celcius = dht.readTemperature();
103 humidity = dht.readHumidity();
104 second = 0;
105 }
106
107 // *** Display date, time, temperature,
108 // and humidity on OLED ***
109 oled.clearDisplay();
110 oled.setTextSize(1);
111 oled.setTextColor(WHITE);
112 oled.setCursor(0, 0);
113 oled.printf("%04d/%02d/%02d",
114 now.Year(), now.Month(), now.Day());
115 oled.setCursor(80, 0);
116 oled.printf("%02d:%02d:%02d",
117 now.Hour(), now.Minute(), now.Second());
118 oled.drawBitmap(32, 20, temperature_bmp, 16, 16, 1);
119 oled.drawBitmap(32, 40, humidity_bmp, 16, 16, 1);
120 oled.setTextSize(2);
121 oled.setCursor(55, 20);
122 oled.printf("%.0f%cC", celcius, (char)247);
123 oled.setCursor(55, 40);
124 oled.printf("%.0f%%", humidity);
125 oled.display();
126
127 delay(1000);
128 second++;
129 }

You might also like