前言

我觉得去做一些简单又好玩的案例是入门很好的选择。

在实践的过程中会碰到很多需求很多问题在解决这些需求这些问题的过程就是在学习的过程。

今天我来分享一个随机显示古诗的案例,如果对此感兴趣可以跟我一起动手实践起来。

案例效果:

拆解任务

现在我们先来拆解一下需求。

就是向一个web服务发送一个Get请求获取数据然后将数据显示在OLED屏上,为了效果更好采用分段显示。

首先我们的OLED屏需要连接到一个OLED屏,这个在教程(二)中写了。

ESP32+Arduino入门教程(二):连接OLED屏

如何输出古诗或者其他内容呢?

需要创建一个Web服务,然后随便接入一个大模型,让其输出古诗或其他内容再返回。

就算你从来没有写过Web服务也没关系,这个教程使用Python来创建一个非常简单的Web服务,小白也能实现。

在Arduino中编写代码,ESP32连接WIFI然后发送一个Get请求获取结果,将结果分段显示在OLED屏上。

搭建开发环境在教程(一)中写了。

ESP32+Arduino入门(一):开发环境搭建

创建Web服务

创建一个Python虚拟环境,安装一下flask与openai。

写一个app.py如下所示:

from flask import Flask
from openai import OpenAI app = Flask(__name__) client = OpenAI(api_key="你的api key",
base_url="https://api.siliconflow.cn/v1") @app.route('/OutputClassicalPoetry', methods=['GET'])
def output_classical_poetry():
response = client.chat.completions.create(
model="internlm/internlm2_5-20b-chat",
messages=[
{'role': 'user',
'content': "输出一句古诗。"}
],
stream=False,
temperature=0.7
)
response_text = response.choices[0].message.content
return response_text if __name__ == '__main__':
app.run(host='0.0.0.0', debug=True)

运行之后,查看一下自己的ip地址。

打开cmd输入ipconfig:

打开浏览器,输入http://你的ip地址:5000/OutputClassicalPoetry,即可看到如下的效果:

就说明这个简单的Web服务已经搞定了。

ESP32连接WIFI并发送Get请求

代码如下:

#include <WiFi.h>
#include <HTTPClient.h>
const char* ssid = "你的WIFI名称";
const char* password = "你的WIFI密码";
const char* serverUrl = "上面的接口地址";
void setup() {
Serial.begin(115200); // 连接WiFi
WiFi.begin(ssid, password);
Serial.print("正在连接到WiFi..."); while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
} Serial.println("\nWiFi已连接");
Serial.print("IP地址: ");
Serial.println(WiFi.localIP());
}
void loop() {
// 确保WiFi保持连接
if (WiFi.status() != WL_CONNECTED) {
Serial.println("WiFi连接断开,尝试重连...");
WiFi.reconnect();
delay(2000); // 等待重连
return;
}
HTTPClient http; Serial.println("\n正在发送GET请求...");
http.begin(serverUrl);
// 设置超时时间
http.setTimeout(10000); // 发送请求并获取状态码
int httpCode = http.GET(); // 处理响应
if (httpCode > 0) {
Serial.printf("HTTP状态码: %d\n", httpCode); if (httpCode == HTTP_CODE_OK) {
String payload = http.getString();
Serial.println("响应内容:");
Serial.println(payload);
} else {
Serial.println("非200响应,完整响应头:");
Serial.println(http.getString()); // 输出错误信息
}
} else {
Serial.printf("请求失败,错误: %s\n", http.errorToString(httpCode).c_str());
} http.end(); // 必须释放资源 delay(5000); // 等待5秒
}

电脑需要连接同一个WIFI。

烧录到ESP32运行。

现在打开串口监视器查看效果:

分段显示在OLED屏

代码如下:

#include <WiFi.h>
#include <HTTPClient.h>
#include <U8g2lib.h>
U8G2_SSD1306_128X32_UNIVISION_F_SW_I2C u8g2(U8G2_R0, 0, 1, U8X8_PIN_NONE);
// WiFi配置
const char* ssid = "你的WIFI名称";
const char* password = "你的WIFI密码";
const char* serverUrl = "上面的接口地址";
// 显示相关变量
uint8_t fontHeight;
uint8_t yPos;
const uint16_t screenWidth = 128;
const unsigned long segmentDisplayTime = 2000;
unsigned long lastSegmentChange = 0;
int currentSegmentStart = 0;
bool needFullCycleReset = false; // 新增循环重置标志
// 文本存储
String receivedText = "";
bool newTextReceived = false;
// UTF-8字符测量
int utf8CharBytes(char c) {
if ((c & 0x80) == 0) return 1;
else if ((c & 0xE0) == 0xC0) return 2;
else if ((c & 0xF0) == 0xE0) return 3;
else if ((c & 0xF8) == 0xF0) return 4;
return 1;
}
// 分段计算(增加循环重置检测)
int getNextSegmentStart(const char* text, int startIndex, int maxWidth) {
if (!text || strlen(text) == 0 || startIndex >= strlen(text)) return 0; int bytePos = startIndex;
int segmentWidth = 0;
int lastValidPos = bytePos;
bool hasAdvanced = false; // 检测是否前进过 while (text[bytePos] != '\0') {
hasAdvanced = true;
int charBytes = utf8CharBytes(text[bytePos]);
char buffer[10] = {0};
for (int i = 0; i < charBytes; i++) buffer[i] = text[bytePos + i]; int charWidth = u8g2.getUTF8Width(buffer);
if (segmentWidth + charWidth > maxWidth) break; segmentWidth += charWidth;
lastValidPos = bytePos + charBytes;
bytePos += charBytes;
} // 当没有前进且不是初始位置时,说明需要重置
if (!hasAdvanced && startIndex != 0) return 0;
return (text[bytePos] == '\0') ? 0 : lastValidPos;
}
// 显示处理
void drawSegment(const char* text, int startIndex) {
if (!text || strlen(text) == 0) {
u8g2.clearBuffer();
u8g2.setCursor(0, yPos);
u8g2.print("无内容");
u8g2.sendBuffer();
return;
} u8g2.clearBuffer();
char segment[256] = {0};
int bytePos = startIndex;
int segmentWidth = 0;
int segIndex = 0; while (text[bytePos] != '\0') {
int charBytes = utf8CharBytes(text[bytePos]);
char temp[10] = {0};
for (int i = 0; i < charBytes; i++) temp[i] = text[bytePos + i];
int charWidth = u8g2.getUTF8Width(temp); if (segmentWidth + charWidth > screenWidth) break; for (int i = 0; i < charBytes; i++) segment[segIndex++] = text[bytePos + i];
segmentWidth += charWidth;
bytePos += charBytes;
}
segment[segIndex] = '\0'; u8g2.setCursor(0, yPos);
u8g2.print(segment);
u8g2.sendBuffer();
}
// 数据获取
void fetchTextFromServer() {
if (WiFi.status() != WL_CONNECTED) return; HTTPClient http;
http.begin(serverUrl);
Serial.print("[GET] 发送请求至: ");
Serial.println(serverUrl); int httpCode = http.GET();
if (httpCode == HTTP_CODE_OK) {
String newText = http.getString();
Serial.print("[GET] 接收结果: ");
Serial.println(newText); if (newText != receivedText) {
receivedText = newText;
newTextReceived = true;
needFullCycleReset = true; // 标记需要完整循环
}
}
http.end();
}
void setup() {
Serial.begin(115200);
WiFi.begin(ssid, password);
Serial.print("连接WiFi: ");
Serial.println(ssid); while (WiFi.status() != WL_CONNECTED) delay(500);
Serial.println("WiFi连接成功"); u8g2.begin();
u8g2.enableUTF8Print();
u8g2.setFont(u8g2_font_wqy12_t_gb2312); fontHeight = u8g2.getMaxCharHeight();
yPos = (32 + fontHeight) / 2;
drawSegment("等待数据...", 0);
fetchTextFromServer();
}
void loop() {
static unsigned long lastFetchTime = 0;
unsigned long currentTime = millis();
// 每60秒获取数据
if (currentTime - lastFetchTime >= 60000) {
lastFetchTime = currentTime;
fetchTextFromServer();
}
// 处理新文本
if (newTextReceived) {
newTextReceived = false;
currentSegmentStart = 0;
lastSegmentChange = currentTime;
drawSegment(receivedText.c_str(), currentSegmentStart);
needFullCycleReset = false; // 重置标志
} // 分段切换逻辑
if (currentTime - lastSegmentChange >= segmentDisplayTime) {
int prevStart = currentSegmentStart;
currentSegmentStart = getNextSegmentStart(receivedText.c_str(), currentSegmentStart, screenWidth); // 完整循环后重置
if (currentSegmentStart == 0 && prevStart != 0) {
needFullCycleReset = true;
} // 执行实际切换
if (needFullCycleReset) {
currentSegmentStart = 0;
needFullCycleReset = false;
lastSegmentChange = currentTime; // 重置计时器
} else {
lastSegmentChange = currentTime;
} drawSegment(receivedText.c_str(), currentSegmentStart);
} delay(100);
}

烧录到ESP32运行即可得到上面的效果。

 // 每60秒获取数据
if (currentTime - lastFetchTime >= 60000) {
lastFetchTime = currentTime;
fetchTextFromServer();
}

这里可以更改获取数据的时间。

ESP32+Arduino入门(四):OLED屏随机显示古诗的更多相关文章

  1. 原生js实现头像大屏随机显示

    效果如下图所示: 一.html部分 <div class="myContainer"> <ul> <li class="first" ...

  2. 树莓派使用 OLED 屏显示图片及文字

    树莓派默认是不带显示屏的,如果想要查看系统的一些信息,需要使用电脑登录到树莓派,或者通过 HDMI 连接外接显示器查看.这样做总是有点麻烦,我们可以通过外接一个 OLED 屏来显示一些关键参数或者图片 ...

  3. STM32学习笔记——OLED屏

    STM32学习笔记--OLED屏 OLED屏的特点: 1.  模块有单色和双色可选,单色为纯蓝色,双色为黄蓝双色(本人选用双色): 2.  显示尺寸为0.96寸 3.  分辨率为128*64 4.   ...

  4. 图像分辨率 像素 大小 LCD显示 为OLED屏增加GUI支持

    1. 根据一张标准图片的分辨率,结合每个像素的大小,可以计算得到这张图片的大小(字节数) 补充点:bmp格式的图片有24色或者32色.(其一个像素点可能占用24bits或者32bits)  至于图片怎 ...

  5. 【小梅哥FPGA进阶教程】串口发送图片数据到SRAM在TFT屏上显示

    十五.串口发送图片数据到SRAM在TFT屏上显示 之前分享过rom存储图片数据在TFT屏上显示,该方法只能显示小点的图片,如果想显示TFT屏幕大小的图片上述方法rom内存大小不够.小梅哥给了个方案,利 ...

  6. DevExpress XtraReports 入门四 创建 Web 报表

    原文:DevExpress XtraReports 入门四 创建 Web 报表 本文只是为了帮助初次接触或是需要DevExpress XtraReports报表的人群使用的,为了帮助更多的人不会像我这 ...

  7. 脑残式网络编程入门(四):快速理解HTTP/2的服务器推送(Server Push)

    本文原作者阮一峰,作者博客:ruanyifeng.com. 1.前言 新一代HTTP/2 协议的主要目的是为了提高网页性能(有关HTTP/2的介绍,请见<从HTTP/0.9到HTTP/2:一文读 ...

  8. 为OLED屏添加GUI支持6:进度条控件

    为OLED屏添加GUI支持6:进度条控件 本文博客链接:http://blog.csdn.net/jdh99,作者:jdh,转载请注明. 环境: 主机:WIN10 开发环境:MDK5.13 MCU:S ...

  9. Python爬虫入门四之Urllib库的高级用法

    1.设置Headers 有些网站不会同意程序直接用上面的方式进行访问,如果识别有问题,那么站点根本不会响应,所以为了完全模拟浏览器的工作,我们需要设置一些Headers 的属性. 首先,打开我们的浏览 ...

  10. GEF入门实例_总结_05_显示一个空白编辑器

    一.前言 本文承接上一节:GEF入门实例_总结_04_Eclipse插件启动流程分析 在第三节( GEF入门实例_总结_03_显示菜单和工具栏  ),我们创建了菜单和工具栏. 这一节,我们来实现:点击 ...

随机推荐

  1. 动图图解 | UDP就一定比TCP快吗?

    学习&转载文章:"动图图解 | UDP就一定比TCP快吗?" UDP比TCP快吗? 相信就算不是八股文老手,也会下意识的脱口而出:"是". 这要追问为什 ...

  2. 分圆多项式(cyclotomic polynomial)

    最近论文中经常遇到分圆多项式,现在系统的学习一下! 本原单位根 之前介绍n次单位根,现在详细学习一下n次本原单位根(n-th primitive unit root) 一个复数是n次单位根,当且仅当具 ...

  3. 『Python底层原理』--CPython 虚拟机

    在 Python 编程的世界里,我们每天都在使用 python 命令运行程序,但你是否曾好奇这背后究竟发生了什么? 本文将初步探究 CPython(Python 中最流行的实现)的一些内部机制,为了更 ...

  4. 使用 Dify + LLM 构建精确任务处理应用

    在构建基于大语言模型(LLM)的应用时,如何确保返回结果的准确性和可重复性是一个常见的挑战.本文将结合 Dify + LLM 的使用经验,介绍如何设计一个精确的 LLM 任务处理流程,避免传统 LLM ...

  5. TCP协议的三次握手-4次挥手

    TCP的连接建立是一个三次握手过程,目的是为了通信双方确认开始序号,以便后续通信的有序进行.主要步骤如下: 连接开始时,连接建立方(Client)发送SYN包,并包含了自己的初始序号a: 连接接受方( ...

  6. 引燃算力新基建,天翼云亮相DCIC2024第13届数据中心产业发展大会!

    近日,由中国通信企业协会主办的"第13届数据中心产业发展大会暨AIDC智能算力生态合作展览会"在北京顺利举行.现场展示了天翼云"AIDC""紫金&qu ...

  7. spark-sql查询Iceberg时处理流程

    1.查询表结构 show create table data_lake_ods.test CREATE TABLE spark_catalog.data_lake_ods.test ( `user_n ...

  8. hibernate基础查询语句使用

    hibernate最新文档 项目仓库地址项目201102-hibernateSelect 数据准备 -- 创建数据表 drop table if exists t_book; create table ...

  9. 动手学深度学习-python基础知识介绍(数据处理基础流程)part2

    数据预处理 import os os.makedirs(os.path.join('..','data'),exist_ok=True) data_file=os.path.join('..','da ...

  10. [SDOI2009] 晨跑 题解

    每个点拆成入点和出点. 发现每个点.每条边都只能经过一次,所以所有边的容量都是 \(1\). #include<bits/stdc++.h> #define ll long long us ...