banner
阿珏酱

阿珏酱

いつもとは逆の電車に乗り、見たこともない風景を見に行く
twitter
github
facebook
bilibili
zhihu
steam_profiles
youtube

用ESP8266-NodeMCU開發板顯示一下我的QQ頭像

嘿,說好的自己寫 esp8266 的開發板固件的我回來了。
20 年說好的,今天回來還願了 ESP8266 串口 WiFi 模組 - WiFi 殺手

今天我們把 OLED 顯示屏也接上,我此次買的是 4 腳的 OLED (128*64),不支持彩色顯示的。

NodeMCU 開發板#

NodeMCU 是一個開源的 IoT 物聯網硬體開發板,由於它支持 WIFI 功能且使用方法十分類似 Arduino 開發板,所以近些年獲得了越來越多來自全世界的創客朋友們的青睞。NodeMCU 尺寸與 Arduino Nano 類似。它並不是 Arduino 團隊開發的,但是我們也可以使用 Arduino IDE 對它進行開發。

作為萬物互聯的 IoT 基礎,首先物聯網控制板的成本不能過高。高昂的物聯網控制元件不利於項目成本控制也不利於廣大創客愛好者學習和使用它。在這一點上,NodeMCU 比樹莓派以及 Arduino 家族的 IoT 平台等更具優勢。

細心的朋友可能已經發現了,這會我開發板叫做了 ESP8266-NodeMCU。然而在其它的網站或資料中,有時是用 ESP8266 有時是用 NodeMCU。那么 ESP8266 和 NodeMCU 之間是什麼關係呢?

ESP8266 是一塊芯片(被鐵殼子保住的方型的東西),而 NodeMCU 則是以 ESP8266 芯片為核心的開發板,如下圖所示。
img
要想對 ESP8266 芯片來進行實驗操作是很困難的,因為我們很難將小小一枚芯片上的引腳與我們的電腦連接起來然後再進行上傳程序等操作。於是便誕生了圍繞 ESP8266 芯片的各種開發板。NodeMCU 就是這些開發板中的一員。

NodeMCU 開發板上的兩排插針與 ESP8266 芯片的引腳相連。有了開發板上的兩排插針,我們就可以很輕鬆的使用杜邦線將芯片的引腳接到實驗電路中。NodeMCU 開發板上還配有 USB 接口以及電壓轉換電路。這些為我們提供了很大的便利。我們只要用一根 USB 數據線就可以輕鬆的實現為 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

下載適合自己平台的驅動安裝程序
需要看清楚自己的串口芯片型號是否在驅動程度的支持範圍內

如何查看自己的開發板需要什麼驅動#

  1. 直接看,下圖中豎著的長方形黑條,那個就是 USB 轉串口芯片。上面會寫明芯片的型號名稱
    img
  2. 問你買的商家

顯示屏的焊接#

不要焊接錯了,不然會燒壞芯片的
OLED 接線:

  • GND - GND
  • VCC - VCC
  • SCL - GPIO5(D1)
  • SDA - GPIO4(D2)

洛鐵加熱中
img

下面就是焊接好的,注意接線,不一定是按我這個順序的,要看名稱
img

Arduino IDE 的安裝#

  1. 下載 arduino IDE
    https://www.arduino.cc/en/software

  2. 安裝
    安裝還要我教?

  3. 配置
    點擊工具 - 開發板 - 開發板管理器,在設置中寫上開發板地址:http://arduino.esp8266.com/stable/package_esp8266com_index.json
    之後就會自動下載相應的支持庫文件了,期間需要保證網絡的科學性

  4. 選擇開發板
    NodeMCU1.0(ESP-12EModule)

  5. 選擇端口
    在計算機的設備管理器中找到你的開發板的 COM 口

Arduino 代碼#

有時候我覺得我逼逼叨叨說一大堆還不如直接上代碼來的直接,我覺得你肯定也是想直接看代碼的而不是聽我說一堆廢話
我也盡可能在代碼中註釋了 (編寫 arduino 代碼請遵循 C/C++ 語言規範)

下列的例子中,連接 WiFi 使用的是 <WiFiManager.h> 庫,首次連接需要使用手機連接 ESP8266 發送出來的 WiFi 先進行配網操作。寫出屏幕使用的是 < U8g2lib.h > 庫,支持中文直接寫出

  1. 這是一個簡單的連接 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();
}

img
2. 這是一個發起 HTTP 請求,然後在屏幕顯示近四天的天氣信息

#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();
  }
}

img

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。