Cython A Guide For Python Programmers
Cython A Guide For Python Programmers
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
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
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
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
8.4 Summary . . . . . . . . . . . . . . . . . . . . . . . . . 60
8.5 Coding Challenge . . . . . . . . . . . . . . . . . . . . 60
Bibliography 97
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
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
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
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.
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.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
Blink an LED
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.
(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
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);
Figure 2.3. NodeMCU pinout. Retrieved August 26, 2018, from teachmemi-
cro.com
Local\Arduino15\packages\esp8266\hardware\esp8266\
2.4.1\cores\esp8266.
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.
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
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?
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
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.
(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
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
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.
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?
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.
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:
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)
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
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?
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Ω.
(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
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);
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
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 }
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
• 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.
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
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 }
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
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?
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
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.
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.
(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?
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
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.
9600 bps. In line 10, a ”Hello, world!” message is sent to the serial
communication.
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
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
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
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.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
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?
72
CHAPTER 11. RECEIVE DATA FROM PC USING SERIAL 73
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
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
method as shown in line 21. To test the program, you can send a
number between 0–1023 from the Serial Monitor.
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.
str_s.toCharArray(str_c, 64);
By using strtok function, split the RGB LED and OLED display
commands, and then procces the commands!
Chapter 12
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
For more information about the millis and micros functions, you
can see in core_esp8266_wiring.c.
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.
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.
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.
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?
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).
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
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?
91
CHAPTER 14. DHT11 TEMPERATURE AND HUMIDITY SENSOR 92
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
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.
Figure 14.1. Time, date, temperature, and humidity are displayed on OLED display.
Bibliography
97
BIBLIOGRAPHY 98
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
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
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
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 }