This documents an attempt to build a test application on a laptop with Ubuntu 22.04 installed. The target is an open hardware NodeMCU (Lua) ESP8266 board with an ESP-12E Wi-Fi module and CH340G USB-to-serial driver chip.
Setup the Edit/Build Environment
- Install Microsoft’s VS Code. PlatformIO sits on top of this code editor.
- Open VS Code
- Install “PlatformIO IDE” extension.
- Select the PlatformIO Home icon to kick off any required initialization.
- Close VS Code.
- Restart VS Code
- Open Terminal Window and type “
git --version
“ - If
git
is not installed, do so now.
- Open Terminal Window and type “
- At this time, the library reference for the Arduino code is Welcome to ESP8266 Arduino Core’s documentation!
Test Project #1 – LED Blinker
- Open VS Code, select PlatformIO Home icon and create a new project.
- Name: Test1
- Board: Espressif ESP8266 ESP-12E
- Framework: Arduino
- Select “Finish” and let the environment will be initialized.
- (Once complete, libraries will be located at
c:\Users\name\.platformio\~
)
- Open file
src/main.cpp
and edit code to match below.
#include "Arduino.h" // Set LED_BUILTIN if it is not defined by Arduino framework // #define LED_BUILTIN 13 void setup() { // initialize LED digital pin as an output. pinMode(LED_BUILTIN, OUTPUT); } void loop() { // turn the LED on (HIGH is the voltage level) digitalWrite(LED_BUILTIN, HIGH); // wait for a second delay(1000); // turn the LED off by making the voltage LOW digitalWrite(LED_BUILTIN, LOW); // wait for a second delay(1000); }
Plug in a NodeMCU, ESP-12E, or other ESP8266 device, build then upload the code. If there are any problems with the USB /dev/ttyUSB0 driver, see this topic to help clear it up.
If all goes well, the onboard LED should be blinking at a rate of one second off, one second on.
Test Project #2 – USB Serial Communication
- Open VS Code, select PlatformIO Home icon and create a new project (same as above) or edit the an existing project.
- Open file
src/main.cpp
and edit code to match below.
#include "Arduino.h" void setup() { Serial.begin(115200); } void loop() { Serial.println("Hello"); delay(1000); }
You will also need to change the platformio.ini
file to match the application UART speed.
[env:esp12e] ... monitor_speed = 115200
Build and upload the code.
Open the Serial Monitor by clicking the icon at the bottom of VS Code. If all goes well, you should see the word “Hello” being printed in the window. You can right click in the teminal window and clear the screen to see the window redraw itself.
Test Project #3 – Connect to Local Wi-Fi as Client
This time go the Arduino 8266 Core documents and load the source file for Wi-Fi client (here). It connects to the local network and prints the IP address returned by DHCP. Make sure you edit the WiFi.begin()
command to enter the SID and password for your network.
#include <ESP8266WiFi.h> void setup() { Serial.begin(115200); Serial.println(); WiFi.begin("ssid", "password"); Serial.print("Connecting"); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(); Serial.print("Connected, IP address: "); Serial.println(WiFi.localIP()); } void loop() {}
Build and upload the code then open the PlatformIO Serial Monitor and should respond quickly.
Connecting.............................. ........................................ Connected, IP address: 192.168.1.123
Test Project #4 – Get UTC from Time Server over Wi-Fi
This example is in the Arduino libraries downloaded to the local system by PlatformIO at C:\Users\user\.platformio\packages\framework-arduinoespressif8266\libraries\ESP8266WiFi\examples\NTPClient\NTPClient.ino
. It gets the UTC time from “time.nist.gov” every ten seconds.
/* Udp NTP Client Get the time from a Network Time Protocol (NTP) time server Demonstrates use of UDP sendPacket and ReceivePacket For more on NTP time servers and the messages needed to communicate with them, see http://en.wikipedia.org/wiki/Network_Time_Protocol created 4 Sep 2010 by Michael Margolis modified 9 Apr 2012 by Tom Igoe updated for the ESP8266 12 Apr 2015 by Ivan Grokhotkov This code is in the public domain. */ #include <ESP8266WiFi.h> #include <WiFiUdp.h> #ifndef STASSID #define STASSID "your-ssid" #define STAPSK "your-password" #endif const char* ssid = STASSID; // your network SSID (name) const char* pass = STAPSK; // your network password unsigned int localPort = 2390; // local port to listen for UDP packets /* Don't hardwire the IP address or we won't get the benefits of the pool. Lookup the IP address for the host name instead */ // IPAddress timeServer(129, 6, 15, 28); // time.nist.gov NTP server IPAddress timeServerIP; // time.nist.gov NTP server address const char* ntpServerName = "time.nist.gov"; const int NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 48 bytes of the message byte packetBuffer[NTP_PACKET_SIZE]; // buffer to hold incoming and outgoing packets // A UDP instance to let us send and receive packets over UDP WiFiUDP udp; // Forward reference to locally defined methods. void sendNTPpacket(IPAddress& address); void setup() { Serial.begin(115200); Serial.println(); Serial.println(); // We start by connecting to a WiFi network Serial.print("Connecting to "); Serial.println(ssid); WiFi.mode(WIFI_STA); WiFi.begin(ssid, pass); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(""); Serial.println("WiFi connected"); Serial.println("IP address: "); Serial.println(WiFi.localIP()); Serial.println("Starting UDP"); udp.begin(localPort); Serial.print("Local port: "); Serial.println(udp.localPort()); } void loop() { // get a random server from the pool WiFi.hostByName(ntpServerName, timeServerIP); sendNTPpacket(timeServerIP); // send an NTP packet to a time server // wait to see if a reply is available delay(1000); int cb = udp.parsePacket(); if (!cb) { Serial.println("no packet yet"); } else { Serial.print("packet received, length="); Serial.println(cb); // We've received a packet, read the data from it udp.read(packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer // the timestamp starts at byte 40 of the received packet and is four bytes, // or two words, long. First, esxtract the two words: unsigned long highWord = word(packetBuffer[40], packetBuffer[41]); unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]); // combine the four bytes (two words) into a long integer // this is NTP time (seconds since Jan 1 1900): unsigned long secsSince1900 = highWord << 16 | lowWord; Serial.print("Seconds since Jan 1 1900 = "); Serial.println(secsSince1900); // now convert NTP time into everyday time: Serial.print("Unix time = "); // Unix time starts on Jan 1 1970. In seconds, that's 2208988800: const unsigned long seventyYears = 2208988800UL; // subtract seventy years: unsigned long epoch = secsSince1900 - seventyYears; // print Unix time: Serial.println(epoch); // print the hour, minute and second: Serial.print("The UTC time is "); // UTC is the time at Greenwich Meridian (GMT) Serial.print((epoch % 86400L) / 3600); // print the hour (86400 equals secs per day) Serial.print(':'); if (((epoch % 3600) / 60) < 10) { // In the first 10 minutes of each hour, we'll want a leading '0' Serial.print('0'); } Serial.print((epoch % 3600) / 60); // print the minute (3600 equals secs per minute) Serial.print(':'); if ((epoch % 60) < 10) { // In the first 10 seconds of each minute, we'll want a leading '0' Serial.print('0'); } Serial.println(epoch % 60); // print the second } // wait ten seconds before asking for the time again delay(10000); } // send an NTP request to the time server at the given address void sendNTPpacket(IPAddress& address) { Serial.println("sending NTP packet..."); // set all bytes in the buffer to 0 memset(packetBuffer, 0, NTP_PACKET_SIZE); // Initialize values needed to form NTP request // (see URL above for details on the packets) packetBuffer[0] = 0b11100011; // LI, Version, Mode packetBuffer[1] = 0; // Stratum, or type of clock packetBuffer[2] = 6; // Polling Interval packetBuffer[3] = 0xEC; // Peer Clock Precision // 8 bytes of zero for Root Delay & Root Dispersion packetBuffer[12] = 49; packetBuffer[13] = 0x4E; packetBuffer[14] = 49; packetBuffer[15] = 52; // all NTP fields have been given values, now // you can send a packet requesting a timestamp: udp.beginPacket(address, 123); // NTP requests are to port 123 udp.write(packetBuffer, NTP_PACKET_SIZE); udp.endPacket(); }
The output is displayed below.
Connecting to Snowflake ......... WiFi connected IP address: 192.168.1.198 Starting UDP Local port: 2390 sending NTP packet... packet received, length=48 Seconds since Jan 1 1900 = 3929195828 Unix time = 1720207028 The UTC time is 19:17:08
Test Project #5 – I2C Scanner
Walk through the I2C addresses and look for devices. This code from the Random Nerd Tutorials site.
#include <Wire.h> // Needed to add to Random Nerd Tutorials code to resolve Serial. #include <ESP8266WiFi.h> void setup() { Wire.begin(); Serial.begin(115200); Serial.println("\nI2C Scanner"); } void loop() { byte error, address; int nDevices; Serial.println("Scanning..."); nDevices = 0; for(address = 1; address < 127; address++ ) { Wire.beginTransmission(address); error = Wire.endTransmission(); if (error == 0) { Serial.print("I2C device found at address 0x"); if (address<16) { Serial.print("0"); } Serial.println(address,HEX); nDevices++; } else if (error==4) { Serial.print("Unknow error at address 0x"); if (address<16) { Serial.print("0"); } Serial.println(address,HEX); } } if (nDevices == 0) { Serial.println("No I2C devices found\n"); } else { Serial.println("done\n"); } delay(5000); }
At this point, with no I2C peripherals, no devices will be found.
Add a RTC to the Circuit – DS3231 Module
Connect the ESP3266 to the RTC module as follows. This will use the default ESP3266 I2C pins.
- Connect ESP8266 SDA (D2) to DS3231 SDA
- Connect ESP8266 SCL (D1) to DS3231 SCL
- Connect ESP8266 3.3V to DS3231 VCC
- Connect ESP8266 GND to DS3231 GND
Rerun the code. The new output will resemble below.
Scanning... I2C device found at address 0x57 I2C device found at address 0x68 done
Notice that the scanner found two addresses from the RTC:
- The 7-bit I2C device address for the DS3231 RTC is 1101000 (0x68).
- Read address: 11010001 (0xD1)
- Write address: 11010000 (0xD0).
- The 7-bit I2C device address for the AT24C32 EEPROM is 1010111 (0x57).
- Read address: 10101111 (0xAF), and the
- Write address: 10101110 (0xAE).
Test Project #6 – Read Light Dependent Resistor (LDR) Value
In this example, we will add an LDR to the ESP8266. An example usage is if you create a device that has a display, you can use the LDR to sense the current room light level and dim the backlight if necessary. Be aware that the Arduino library does have a warning with the usage of the analog input hardware. Reading between the lines, the ADC has multiple input channels and it is shared with the Wi-Fi functionality. Performing an analogRead(A0)
instruction usings low-level software semaphore logic to acquire the ADC for the external pin read.
ADC sampling rate: can reach 100,000 times per second with Wi-Fi turned off, and 1,000 times per second with Wi-Fi turned on. Exceeding this capability can cause Wi-Fi instability.
The code below applies an exponential average to the instantaneous LDR analog reading to smooth the value then outputs the LDR reading and the smoothed value normalized by the maximum value (0 to 0.99).
#include <Arduino.h> // Averaged value of LDR. double _ldrAvg = 0; // Shared string buffer. char t[100]; void setup() { Serial.begin(9600); } void loop() { // Update LDR value. double v = analogRead(A0); _ldrAvg = _ldrAvg * 0.5 + v * 0.5; double s = _ldrAvg / 1024.0; sprintf(t, "\nAnalog: %d, (%.2f)", (int)v, s); Serial.println(t); delay(1000); // Delay for 1 second before the next iteration }
Example outputs value are displayed below.
Analog: 172, (0.17) - LDR covered by hand Analog: 541, (0.55) - Office lighting Analog: 910, (0.89) - Flashlight on LDR