Introduction
I believe that working on some simple and fun projects is a great way to get started.
During the practical process, you will encounter many requirements and issues, and solving these requirements and problems is part of the learning process.
Today, I will share a project that randomly displays classical poetry. If you are interested, feel free to join me in practicing.
Project Effect:
Task Breakdown
Now let’s break down the requirements.
The goal is to send a GET request to a web service to retrieve data and then display the data on the OLED screen, using segmented display for better effect.

First, we need to connect the OLED screen, which was covered in Tutorial (Part 2).
ESP32 + Arduino Getting Started Tutorial (Part 2): Connecting the OLED Screen
How do we output classical poetry or other content?
We need to create a web service and connect it to a large model to output classical poetry or other content and return it.
Even if you have never written a web service, it’s okay; this tutorial uses Python to create a very simple web service that even beginners can implement.
In Arduino, we will write code for the ESP32 to connect to WiFi, send a GET request to retrieve results, and display the results in segments on the OLED screen.
Setting up the development environment is covered in Tutorial (Part 1).
ESP32 + Arduino Getting Started (Part 1): Setting Up the Development Environment
Creating a Web Service
Create a Python virtual environment and install Flask and OpenAI.
Write an app.py as shown below:
from flask import Flask
from openai import OpenAI
app = Flask(__name__)
client = OpenAI(api_key="your 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': "Output a line of classical poetry."}
],
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)
After running, check your IP address.
Open cmd and type ipconfig:

Open your browser and enter<span>http://your ip address:5000/OutputClassicalPoetry</span>
, and you should see the following effect:

This indicates that the simple web service is up and running.
ESP32 Connects to WiFi and Sends GET Request
The code is as follows:
#include <WiFi.h>
#include <HTTPClient.h>
const char* ssid = "your WIFI name";
const char* password = "your WIFI password";
const char* serverUrl = "the above interface address";
void setup() {
Serial.begin(115200);
// Connect to WiFi
WiFi.begin(ssid, password);
Serial.print("Connecting to WiFi...");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nWiFi connected");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
}
void loop() {
// Ensure WiFi remains connected
if (WiFi.status() != WL_CONNECTED) {
Serial.println("WiFi disconnected, attempting to reconnect...");
WiFi.reconnect();
delay(2000); // Wait for reconnection
return;
}
HTTPClient http;
Serial.println("\nSending GET request...");
http.begin(serverUrl);
// Set timeout
http.setTimeout(10000);
// Send request and get status code
int httpCode = http.GET();
// Handle response
if (httpCode > 0) {
Serial.printf("HTTP status code: %d\n", httpCode);
if (httpCode == HTTP_CODE_OK) {
String payload = http.getString();
Serial.println("Response content:");
Serial.println(payload);
} else {
Serial.println("Non-200 response, full response header:");
Serial.println(http.getString()); // Output error message
}
} else {
Serial.printf("Request failed, error: %s\n", http.errorToString(httpCode).c_str());
}
http.end(); // Must release resources
delay(5000); // Wait 5 seconds
}
Your computer needs to be connected to the same WiFi.
Upload to ESP32 and run.
Now open the serial monitor to check the effect:

Segmented Display on OLED Screen
The code is as follows:
#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 configuration
const char* ssid = "your WIFI name";
const char* password = "your WIFI password";
const char* serverUrl = "the above interface address";
// Display related variables
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; // New cycle reset flag
// Text storage
String receivedText = "";
bool newTextReceived = false;
// UTF-8 character measurement
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;
}
// Segmented calculation (add cycle reset detection)
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; // Detect if advanced
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;
}
// When not advanced and not at the initial position, it indicates a need to reset
if (!hasAdvanced &&& startIndex != 0) return 0;
return (text[bytePos] == '\0') ? 0 : lastValidPos;
}
// Display handling
void drawSegment(const char* text, int startIndex) {
if (!text || strlen(text) == 0) {
u8g2.clearBuffer();
u8g2.setCursor(0, yPos);
u8g2.print("No content");
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();
}
// Data fetching
void fetchTextFromServer() {
if (WiFi.status() != WL_CONNECTED) return;
HTTPClient http;
http.begin(serverUrl);
Serial.print("[GET] Sending request to: ");
Serial.println(serverUrl);
int httpCode = http.GET();
if (httpCode == HTTP_CODE_OK) {
String newText = http.getString();
Serial.print("[GET] Received result: ");
Serial.println(newText);
if (newText != receivedText) {
receivedText = newText;
newTextReceived = true;
needFullCycleReset = true; // Mark for full cycle
}
}
http.end();
}
void setup() {
Serial.begin(115200);
WiFi.begin(ssid, password);
Serial.print("Connecting to WiFi: ");
Serial.println(ssid);
while (WiFi.status() != WL_CONNECTED) delay(500);
Serial.println("WiFi connected successfully");
u8g2.begin();
u8g2.enableUTF8Print();
u8g2.setFont(u8g2_font_wqy12_t_gb2312);
fontHeight = u8g2.getMaxCharHeight();
yPos = (32 + fontHeight) / 2;
drawSegment("Waiting for data...", 0);
fetchTextFromServer();
}
void loop() {
static unsigned long lastFetchTime = 0;
unsigned long currentTime = millis();
// Fetch data every 60 seconds
if (currentTime - lastFetchTime >= 60000) {
lastFetchTime = currentTime;
fetchTextFromServer();
}
// Handle new text
if (newTextReceived) {
newTextReceived = false;
currentSegmentStart = 0;
lastSegmentChange = currentTime;
drawSegment(receivedText.c_str(), currentSegmentStart);
needFullCycleReset = false; // Reset flag
}
// Segmented switching logic
if (currentTime - lastSegmentChange >= segmentDisplayTime) {
int prevStart = currentSegmentStart;
currentSegmentStart = getNextSegmentStart(receivedText.c_str(), currentSegmentStart, screenWidth);
// Reset after full cycle
if (currentSegmentStart == 0 &&& prevStart != 0) {
needFullCycleReset = true;
}
// Execute actual switch
if (needFullCycleReset) {
currentSegmentStart = 0;
needFullCycleReset = false;
lastSegmentChange = currentTime; // Reset timer
} else {
lastSegmentChange = currentTime;
}
drawSegment(receivedText.c_str(), currentSegmentStart);
}
delay(100);
}
Upload to ESP32 and run to achieve the above effect.
// Fetch data every 60 seconds
if (currentTime - lastFetchTime >= 60000) {
lastFetchTime = currentTime;
fetchTextFromServer();
}
You can change the data fetching time here.