ええ、自分でESP8266開発ボードのファームウェアを書くと言っていた私が戻ってきました。20年前に約束した通り、今日、その願いを叶えに戻ってきました。ESP8266串口WiFi模块 - WiFi杀手
今日はOLEDディスプレイも接続します。今回購入したのは4ピンのOLED(128*64)で、カラー表示には対応していません。
NodeMCU開発ボード
NodeMCUはオープンソースのIoTハードウェア開発ボードです。WIFI機能をサポートし、Arduino開発ボードと非常に似た使用方法であるため、近年、世界中のメイカーたちからますます人気を集めています。NodeMCUのサイズはArduino Nanoに似ています。Arduinoチームによって開発されたものではありませんが、Arduino IDEを使用して開発することも可能です。
あらゆるものがインターネットに接続されるIoTの基盤として、まずIoT制御ボードのコストが高すぎてはいけません。高価なIoT制御コンポーネントは、プロジェクトのコスト管理に不利であり、多くのメイカー愛好家が学習し使用する上でも不利です。この点において、NodeMCUはRaspberry PiやArduinoファミリーのIoTプラットフォームなどよりも優位性があります。
注意深い方はすでにお気づきかもしれませんが、私の開発ボードはESP8266-NodeMCUと呼ばれています。しかし、他のウェブサイトや資料では、ESP8266と表記されたり、NodeMCUと表記されたりすることがあります。では、ESP8266とNodeMCUの間にはどのような関係があるのでしょうか?
ESP8266はチップ(鉄製のケースに収められた四角いもの)であり、NodeMCUはESP8266チップを核とした開発ボードです。下の図に示す通りです。

NodeMCU開発ボード上の2列のピンヘッダーは、ESP8266チップのピンに接続されています。開発ボード上の2列のピンヘッダーがあれば、デュポン線を使ってチップのピンを実験回路に簡単に接続できます。NodeMCU開発ボードにはUSBポートと電圧変換回路も搭載されています。これらは私たちに大きな利便性を提供します。USBデータケーブル1本で、ESP8266への給電とプログラムのアップロードを簡単に実現できます。もちろん、NodeMCU開発ボードの回路機能はこれだけではありませんが、これ以上は深掘りしません。
ドライバーのインストール
以前にも開発ボードのドライバーインストールについては説明しましたが、十分詳細ではありませんでした。
現在市販されているESP8266ドライバーには様々な種類があり、同じNodeMCU開発ボードであってもドライバーが異なる場合があります。現在主流なのはCH340とCP210Xのドライバーです。
ドライバーのダウンロードは、チップメーカーの公式サイトから直接行ってください。 CP210X:https://cn.silabs.com/developers/usb-to-uart-bridge-vcp-drivers CH340C:https://www.wch.cn/downloads/CH341SER_EXE.html
ご自身のプラットフォームに適したドライバーインストーラーをダウンロードしてください。ご自身のシリアルチップのモデルがドライバーのサポート範囲内にあるか、よく確認する必要があります。
自分の開発ボードに必要なドライバーを確認する方法
- 直接見る。下の図の縦長の黒い長方形がUSB-シリアル変換チップです。その上にチップのモデル名が記載されています。

- 購入した販売店に尋ねる。
ディスプレイのはんだ付け
はんだ付けを間違えないでください。間違えるとチップが焼損します。OLEDの配線:
- GND - GND
- VCC - VCC
- SCL - GPIO5(D1)
- SDA - GPIO4(D2)
はんだごて加熱中

以下がはんだ付け完了したものです。配線に注意してください。この順番であるとは限りません。名称を確認してください。

Arduino IDEのインストール
-
Arduino IDEをダウンロード https://www.arduino.cc/en/software
-
インストール。インストールまで私が教える必要がありますか?
-
設定。「ツール」-「ボード」-「ボードマネージャー」をクリックし、設定でボードアドレスを記述します:http://arduino.esp8266.com/stable/package_esp8266com_index.json その後、対応するサポートライブラリファイルが自動的にダウンロードされます。この間、ネットワークの安定性を確保する必要があります。
-
開発ボードを選択: NodeMCU1.0(ESP-12EModule)
-
ポートを選択: コンピューターのデバイスマネージャーで開発ボードのCOMポートを見つけます。
Arduinoコード
時々、私が長々と説明するよりも、直接コードを見せる方が手っ取り早いと感じます。あなたもきっと、私の無駄話を聞くよりも直接コードを見たいと思っているでしょう。私も可能な限りコードにコメントを付けました(Arduinoコードの記述はC/C++言語の規範に従ってください)。
以下の例では、WiFi接続にはライブラリを使用しています。初回接続時には、ESP8266が発信するWiFiに携帯電話で接続し、ネットワーク設定を行う必要があります。画面への書き出しにはライブラリを使用しており、中国語の直接書き出しをサポートしています。
- これは、WiFi接続と画面への画像表示の簡単な例です。
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <ESP8266WiFi.h>
#include <NTPClient.h>
#include <WiFiManager.h>
#include <U8g2lib.h>
#define SDA 4 // SDA引脚,默认gpio4(D2)
#define SCL 5 // SCL引脚,默认gpio5(D1)
Adafruit_SSD1306 oled(128, 64, &Wire,-1); //OLED 屏幕实例化
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP,"pool.ntp.org", 8*3600, 60000);
U8G2_SSD1306_128X64_NONAME_F_SW_I2C u8g2(U8G2_R0, /*clock=*/SCL, /*data=*/SDA, /*reset=*/U8X8_PIN_NONE); // 选择显示屏幕
// 只执行一次
void setup() {
u8g2.begin(); // 初始化
u8g2.enableUTF8Print(); // UTF8允许
u8g2.setFont(u8g2_font_wqy12_t_gb2312b); //字体大小 u8g2_font_wqy15_t_gb2312b
// 初始化屏幕
OLED_Init();
OLED_Showchin(1,13,"正在连接WiFi....",0);
WiFiManager wifiManager;
wifiManager.autoConnect("ESP8266");
OLED_Showchin(1,27,"WiFi连接成功!",0);
OLED_Showchin(1,41,"名称: " + WiFi.SSID(),0);
OLED_Showchin(1,55,"IP: " + WiFi.localIP().toString(),1);
// 获取时间
timeClient.begin();
u8g2.setFont(u8g2_font_wqy15_t_gb2312b); //字体大小 u8g2_font_wqy15_t_gb2312b
OLED_Showchin(1,13,"宁小建",0);
OLED_Showchin(1,27,"hhhhhhhhhhhh",0);
OLED_Showchin(1,55,"大傻逼哈哈哈",1);
timeClient.update();
OLED_Showchin(1,20,"当前北京时间: ",0);
OLED_Showchin(1,41,timeClient.getFormattedTime(),1);
delay(1000);
OLED_img();
}
// 重复执行程序
void loop() {
// 更新时间
// timeClient.update();
// OLED_Showchin(1,27,"当前北京时间: ",0);
// OLED_Showchin(1,41,timeClient.getFormattedTime(),0);
}
// 打印输出到屏幕(支持中文)
void OLED_Showchin(uint8_t x, uint8_t y, String string, uint8_t boot) {
u8g2.setCursor(x, y); //设置显示坐标
u8g2.print(string); // 指定缓存区需要打印的字符串
u8g2.sendBuffer(); // 将定位信息发送到缓冲区
if(boot == 1){
delay(1000);
u8g2.clearBuffer(); // 清除缓存,其实初始化里有清除,循环时一定要加上
}
}
// 屏幕初始化
void OLED_Init() {
oled.begin(SSD1306_SWITCHCAPVCC, 0x3C); //"SSD1306_SWITCHCAPVCC"表示显示器为OLED ,"0x3C"为OLED屏幕默认通信地址
oled.setTextColor(WHITE);//开像素点发光
oled.clearDisplay();//清屏
}
// 输出屏幕
void OLED_ShowString(uint8_t x, uint8_t y, uint8_t font_size, String string) {
oled.setTextSize(font_size); //设置字体尺寸 (>=1)
oled.setCursor(x, y); //设置显示坐标
oled.println(string); //显示内容
oled.display(); //开启显示
}
// 绘画
void OLED_img() {
// 图片数据
const unsigned char gImage_1[518] = { 0X00,0X01,0X40,0X00,0X40,0X00,
0X00,0X40,0X00,0X00,0X00,0X3E,0XF0,0X00,0X00,0X80,0X00,0X00,0X00,0X1F,0XE0,0X00,
0X00,0X00,0X00,0X00,0X00,0X06,0XB0,0X00,0X01,0X00,0X00,0X00,0X00,0X03,0XF8,0X00,
0X00,0X00,0X00,0X00,0X00,0X03,0XFC,0X00,0X00,0X00,0X00,0X00,0X00,0X03,0XDC,0X00,
0X04,0X00,0X00,0X00,0X00,0X01,0XC0,0X00,0X0E,0X00,0X00,0X00,0X00,0X00,0X80,0X00,
0X06,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X01,0X00,0X00,0X00,0X10,0X00,0X00,0X00,
0X08,0X00,0X00,0X00,0X0E,0X00,0X00,0X00,0X08,0X00,0X00,0X00,0X8F,0X00,0X00,0X00,
0X00,0X00,0X02,0X02,0X47,0X00,0X00,0X00,0X00,0X00,0X01,0X10,0X63,0X80,0X00,0X00,
0X00,0X00,0X01,0XC9,0X73,0XC0,0X00,0X00,0X00,0X00,0X00,0XE4,0X79,0XE0,0X00,0X00,
0X00,0X02,0X00,0XFA,0XF9,0XE0,0X00,0X00,0X10,0X02,0X00,0X7D,0X8C,0XF0,0X00,0X00,
0X10,0X03,0X01,0X7E,0X90,0XF1,0X80,0X00,0X10,0X01,0X20,0XFD,0X1C,0X7B,0XE0,0X00,
0X00,0X01,0XA0,0X7D,0X03,0X7B,0X20,0X00,0X10,0X01,0XD8,0XFF,0X8B,0X7E,0X30,0X00,
0X00,0X01,0XFC,0X7F,0X83,0XB6,0X10,0X00,0X00,0X00,0XF7,0XFF,0XC7,0XBE,0X10,0X00,
0X00,0X01,0X8F,0XFF,0XE3,0XF6,0X10,0X00,0X01,0X01,0X4F,0XFF,0XFF,0XFF,0X10,0X00,
0X00,0X01,0XCF,0XFF,0XFF,0XDF,0X10,0X00,0X00,0X01,0XC3,0XFF,0XFF,0XDF,0X70,0X00,
0X01,0X03,0XE3,0XFF,0XFF,0XDF,0XE0,0X00,0X04,0X01,0XE7,0XFF,0XFF,0XDF,0XE4,0X00,
0X07,0X00,0XFB,0XFF,0XFF,0XDF,0XC0,0X00,0X07,0X00,0XFB,0XFF,0XFF,0XFE,0X00,0X00,
0X13,0X00,0X7F,0XFF,0XFF,0XFC,0X00,0X00,0X01,0X20,0X3F,0XFF,0XFF,0XFC,0X00,0X00,
0X00,0X00,0X3F,0XFF,0XFF,0XF8,0X00,0X0B,0X00,0X00,0X1F,0XFE,0X0F,0XF8,0X00,0X1F,
0X00,0X00,0X1F,0XF8,0X6F,0XF0,0X00,0XFF,0X00,0X00,0X1F,0XE1,0XFF,0XF0,0X05,0XFF,
0X00,0X00,0X0F,0XF3,0XFF,0XE0,0X03,0XFF,0X00,0X40,0X0F,0XF7,0XFF,0XC0,0X07,0XFF,
0X00,0X00,0X07,0XFF,0XFF,0X80,0X07,0XFF,0X08,0X00,0X07,0XFF,0XFF,0X00,0X0F,0XFF,
0X00,0X00,0X23,0XFF,0XFE,0X00,0X0F,0XFF,0X00,0X08,0X60,0XFF,0XFE,0X00,0X0F,0XFF,
0X00,0X40,0X20,0X3F,0XFE,0X00,0X1F,0XFF,0X00,0X00,0X10,0X0B,0XFF,0X80,0X1F,0XFF,
0X00,0X00,0X40,0X00,0X3F,0X00,0X1F,0XFF,0X00,0X00,0X10,0X00,0X3F,0X00,0X1F,0XFF,
0X20,0X00,0X02,0X00,0X3F,0X00,0X3F,0XFF,0X00,0X00,0X00,0X00,0X7F,0X80,0X3F,0XFF,
0X00,0X00,0X00,0X10,0X7F,0X80,0X7F,0XFF,0X00,0X00,0X00,0X80,0X7F,0X80,0X7F,0XFF,
0X10,0X00,0X00,0X00,0X7F,0X80,0XFF,0XFF,0X30,0X00,0X00,0X00,0X7F,0X80,0XFF,0XFF,
0X10,0X10,0X00,0X00,0X7F,0XC1,0XFF,0XFF,0X00,0X00,0X00,0X00,0X7F,0XF3,0XFF,0XFF,
0X00,0X00,0X01,0X00,0X3F,0XFF,0XFF,0XFF,0X00,0X00,0X00,0X04,0X3F,0XFF,0XFF,0XFF,
0X00,0X00,0X00,0X00,0X3F,0XFF,0XFF,0XFF,0X00,0X00,0X10,0X80,0X1F,0XFF,0XFF,0XFF,
0X01,0X00,0X00,0X05,0X1F,0XFF,0XFF,0XFB,0X00,0X00,0X00,0X00,0X1F,0XF7,0XFF,0XFC,
0X00,0X00,0X00,0X00,0X1F,0XFB,0XFF,0XFF,0X00,0X00,0X40,0X00,0X19,0XFF,0XFF,0XFF,
};
oled.clearDisplay();
oled.drawBitmap(3, 1, gImage_1, 64, 64, WHITE);
oled.display();
}

- これは、HTTPリクエストを送信し、画面に過去4日間の天気情報を表示する例です。
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <WiFiManager.h> // 引入 WiFiManager 库
#include <ArduinoJson.h> // 引入 ArduinoJson 库
#include <U8g2lib.h> // 引入 U8g2 库
#define SDA 4 // SDA引脚,默认gpio4(D2)
#define SCL 5 // SCL引脚,默认gpio5(D1)
// 选择显示屏幕
U8G2_SSD1306_128X64_NONAME_F_SW_I2C u8g2(U8G2_R0, /*clock=*/SCL, /*data=*/SDA, /*reset=*/U8X8_PIN_NONE);
// 要请求的 URL
const char* url = "http://t.weather.itboy.net/api/weather/city/101230308";
// Wi-Fi 自动连接
void connectToWiFi() {
WiFiManager wifiManager;
wifiManager.autoConnect("ESP8266"); // 自动连接到已保存的 Wi-Fi 网络,或设置一个指定名称的接入点
if (WiFi.status() == WL_CONNECTED) {
Serial.println("已连接到 Wi-Fi");
} else {
Serial.println("未连接到 Wi-Fi");
}
}
// 发起 HTTP GET 请求并返回响应
String httpRequest(const char* url) {
WiFiClient client;
HTTPClient http;
http.begin(client, url); // 使用 WiFiClient 和 URL
int httpCode = http.GET(); // 发起请求
String payload;
if (httpCode > 0) {
Serial.printf("HTTP GET 请求已发送,响应码: %d\n", httpCode);
if (httpCode == HTTP_CODE_OK) {
payload = http.getString(); // 获取响应内容
Serial.println("响应内容:");
Serial.println(payload); // 打印响应内容
}
} else {
Serial.printf("HTTP GET 请求失败,错误: %s\n", http.errorToString(httpCode).c_str());
}
http.end(); // 关闭连接
return payload;
}
// 全局变量存储天气数据
StaticJsonDocument<2048> doc;
int dayIndex = 0;
unsigned long previousMillis = 0;
const long interval = 5000; // 每隔 5 秒切换一次显示
// 解析 JSON 并保存天气信息
bool parseWeather(const String& payload) {
DeserializationError error = deserializeJson(doc, payload); // 反序列化 JSON
if (!error) {
Serial.println("JSON 解析成功!");
return true;
} else {
Serial.print("JSON 解析失败: ");
Serial.println(error.c_str());
return false;
}
}
// 显示天气信息
void displayWeather(int index) {
if (!doc["data"]["forecast"][index]) return; // 检查 JSON 数据是否存在
char buffer[128];
const char* high = doc["data"]["forecast"][index]["high"].as<const char*>();
const char* low = doc["data"]["forecast"][index]["low"].as<const char*>();
const char* ymd = doc["data"]["forecast"][index]["ymd"].as<const char*>();
const char* week = doc["data"]["forecast"][index]["week"].as<const char*>();
const char* type = doc["data"]["forecast"][index]["type"].as<const char*>();
const char* notice = doc["data"]["forecast"][index]["notice"].as<const char*>();
u8g2.clearBuffer(); // 先清空缓冲区
// 将日期和星期显示在一行中
snprintf(buffer, sizeof(buffer), "%s %s", ymd, week);
OLED_Showchin(0, 15, buffer, 2); // 调整文本大小为 1
// 显示高温和低温在一行中
snprintf(buffer, sizeof(buffer), "%s %s", high, low);
OLED_Showchin(0, 35, buffer, 2); // 调整文本大小为 1
// 显示天气类型
snprintf(buffer, sizeof(buffer), "%s", type);
OLED_Showchin(0, 55, buffer, 3); // 调整文本大小为 1
// 显示提示信息
// snprintf(buffer, sizeof(buffer), "%s", notice);
// OLED_Showchin(0, 55, buffer, 1); // 调整文本大小为 1
u8g2.sendBuffer(); // 发送缓冲区内容到显示屏
}
// 打印输出到屏幕(支持中文和设置字体大小)
void OLED_Showchin(uint8_t x, uint8_t y, const char* string, uint8_t textSize) {
u8g2.setCursor(x, y); // 设置显示坐标
switch (textSize) {
case 1:
u8g2.setFont(u8g2_font_wqy12_t_gb2312b); // 设置最小字体
break;
case 2:
u8g2.setFont(u8g2_font_wqy13_t_gb2312b); // 设置大字体
break;
case 3:
u8g2.setFont(u8g2_font_wqy15_t_gb2312b); // 设置最大字体
break;
}
u8g2.print(string); // 指定缓存区需要打印的字符串
}
void setup() {
Serial.begin(115200);
delay(1000);
u8g2.begin(); // 初始化 U8g2
u8g2.enableUTF8Print(); // 允许 UTF8
connectToWiFi(); // 连接 Wi-Fi
if (WiFi.status() == WL_CONNECTED) {
String payload = httpRequest(url); // 发起 HTTP 请求
if (!payload.isEmpty()) {
if (parseWeather(payload)) { // 解析天气数据
displayWeather(dayIndex); // 显示初始天气数据
}
}
} else {
Serial.println("无法连接到 Wi-Fi");
}
}
void loop() {
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
dayIndex = (dayIndex + 1) % 4; // 切换到下一天的数据
displayWeather(dayIndex); // 显示天气信息
}
// 检查WiFi连接状态,如果断开则重新连接
if (WiFi.status() != WL_CONNECTED) {
connectToWiFi();
}
}