ESP32S31.14寸TFT Chatgpt
概述
ChatGPT是人工智能研究实验室OpenAI于2022年11月30日发布的一种新型聊天机器人模型,是一种人工智能技术驱动的自然语言处理工具。
它能够通过学习和理解人类语言来进行对话,还可以根据聊天上下文进行交互,真正像人一样聊天和交流,甚至可以执行编写电子邮件、视频脚本、文案、翻译等任务。编码。
![](/assets/img/esp32/esp32c3supermini/esp32c3chatgpt-1.png)
在嵌入式系统中,ChatGPT可以成为一个好帮手,帮助我们编写简单的程序,甚至检查和修复程序中出现的Bug。
令人兴奋的是,OpenAI官方提供了调用GPT-3.5模型的接口,这使得我们可以调用这些接口,并通过多种方法将这个伟大的模型部署到我们自己的嵌入式系统中。
因此,在本教程中,我们将引导用户学习和使用 ESP32S31.14寸TFT WiFiClient 和 HTTPClient 库、如何连接网络、如何发布网页以及 HTTP GET 和 POST 的基础知识。目标是调用 OpenAI ChatGPT 并创建您自己的问答网站。
入门
在本教程中,我们将使用 ESP32S31.14寸TFT 配置我们自己的 ChatGPT 问答页面。在此页面中,您可以输入您的问题,ESP32S31.14寸TFT 会记录您的问题,并使用 OpenAI 提供的 API 调用方法,使用 HTTP Client 发送请求命令,获取 ChatGPT 的答案并打印在串口中。
本教程中的任务可分为以下四个主要步骤。
配置 ESP32S31.14寸TFT 连接到网络:在这一步中,我们将学习使用ESP32S31.14寸TFT 的基本 WiFi 配置流程,并了解 ESP32S31.14寸TFT 的基本操作,例如网络配置、连接网络服务和获取 IP 地址。
构建嵌入式网页:在这一步中,我们主要接触 WiFi 客户端库。通过使用该库的 GET 和 POST 函数,我们可以使用 HTML 编写自己的问答网页并将其部署在 ESP32S31.14寸TFT 之上。
通过内置网页提交问题:这一步我们主要学习如何使用HTTP Client中的POST方法按照OpenAI API标准来POST我们提出的问题。我们将主要关注如何从网页中收集和存储问题的过程。
从 ChatGPT 获取答案:在这一步中,我们学习在 HTTP 客户端中使用 POST 方法,并从返回的消息中提取我们需要的问题的答案。最后一步就是梳理代码的结构并进行最后的集成。
材料
前期准备
您需要准备以下内容:
1 个 ESP32S31.14寸TFT
1 台电脑
1 根 USB Type-C数据线
1 个 ChatgptAPI (如果没有可以加群联系群主)
Tips
有些USB线只能供电,不能传输数据。如果您没有 USB 线或者不知道您的 USB 线是否可以传输数据,可以购买Type-c数据线
- 步骤 1.通过USB Type-C数据线将ESP32C3SuperMini连接到计算机
![](/assets/img/esp32/esp32s31.14tft/30.jpg)
软件设置
- 步骤1.根据您的操作系统下载并安装最新版本的Arduino IDE
![](/assets/img/arduino/other/ArduinoIDE.png)
如果下载缓慢可以在国内Arduino社区下载ArduinoIDE下载地址
步骤 2.启动 Arduino 应用程序
步骤 3.将 ESP32 板包添加到 Arduino IDE
![](/assets/img/esp32/esp32s31.14tft/5.jpg)
离线安装ESP32方法
导航到工具 > 开发板 > ESP32 Arduino并选择“ Adafruit Feather ESP32-S3 TFT ”。板的列表有点长,你需要滚动到底部才能到达它。
![](/assets/img/esp32/esp32s31.14tft/6.jpg)
导航到“工具”>“端口”,然后选择所连接的 ESP32S3 1.14 TFT 的串口名称。这可能是 COM3 或更高版本(COM1和COM2通常保留用于硬件串行端口)。
Warning
将代码从 Arduino IDE 上传到 ESP32-S3 后,请务必按下重置按钮!否则程序不运行
Warning
ESP32-S2/S3 引导加载程序不支持 Windows 7 或 8 的 USB 串行支持。(参考https://github.com/espressif/arduino-esp32/issues/5994)。 请更新到 espressif 支持的版本WIn 10!或者,您可以尝试这个社区制作的 Windows 7 驱动程序 ( https://github.com/kutukvpavel/Esp32-Win7-VCP-drivers )
配置ESP32S3连接网络
WiFi使用教程中有详细介绍。
当 ESP32 设置为 Wi-Fi 模式时,它可以连接到其他网络(例如路由器)。在这种情况下,路由器会为您的 ESP32开发板分配一个唯一的 IP 地址。
要使用 ESP32 Wi-Fi 功能,您需要做的第一件事就是在代码中包含 WiFi.h 库,如下所示:
#include <WiFi.h>
要将 ESP32 连接到特定的 Wi-Fi 网络,您必须知道其 SSID 和密码。此外,该网络必须位于 ESP32 Wi-Fi 范围内。
首先,设置Wi-Fi模式。如果 ESP32 将连接到另一个网络(接入点/热点),则它必须处于站模式。
WiFi.mode(WIFI_STA);
然后,用于WiFi.begin()连接到网络。您必须将网络 SSID 及其密码作为参数传递。
连接到 Wi-Fi 网络可能需要一段时间,因此我们通常添加一个 while 循环,不断检查连接是否已通过使用 建立WiFi.status()。当连接成功建立后,返回WL_CONNECTED。
当 ESP32 设置为 Wi-Fi 站时,它可以连接到其他网络(例如路由器)。在这种情况下,路由器会为您的 ESP32 板分配一个唯一的 IP 地址。要获取主板的 IP 地址,您需要WiFi.localIP()在与网络建立连接后进行调用。
void WiFiConnect(void){
WiFi.begin(ssid, password);
Serial.print("Connecting to ");
Serial.println(ssid);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected!");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
}
变量password和ssid保存您要连接的网络的 密码 和 WIFI。
// Replace with your network credentials
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";
这是一个非常简单的WiFi连接程序,将程序上传到ESP32C3SuperMini,然后打开串口助手,将波特率设置为115200。如果连接顺利,你会看到打印出的IP地址。
![](/assets/img/esp32/esp32c3supermini/esp32c3chatgpt-2.png)
如果您有兴趣阅读有关 ESP32C3 在 WiFi 中的应用和功能的更多信息,我们建议您阅读
Random Nerd Tutorials。
构建嵌入式网页
ESP32 在 WiFi 库中集成了许多非常有用的 WiFiClient 函数,这使得我们可以在不添加额外库的情况下设计和开发嵌入式网页。
创建一个新的 WiFiServer 对象,以便使用该对象来控制 XIAO ESP32C3 建立的 IoT 服务器。
WiFiServer server(80);
WiFiClient client1;
在上面的步骤中,我们让 ESP32C3SuperMini 连接到WiFi。WiFi连接成功后,您将可以从串口监视器中获取当前ESP32C3SuperMini的IP地址。此时,ESP32C3SuperMini已经成功搭建了Web服务器。您可以通过 ESP32C3SuperMini 的 IP 地址访问该 Web 服务器。
假设您的 ESP32C3SuperMini 的 IP 地址是192.168.7.152。然后你接下来可以通过浏览器输入这个IP地址。
输入这个IP地址后,我们可能只会看到一个空白页面。这是因为我们尚未发布该页面的页面内容。
![](/assets/img/esp32/esp32c3supermini/esp32c3chatgpt-3.png)
现在让我们用 C 语言创建一个数组来存储我们想要布局的页面内容,即 HTML 代码。
const char html_page[] PROGMEM = {
"HTTP/1.1 200 OK\r\n"
"Content-Type: text/html\r\n"
"Connection: close\r\n" // the connection will be closed after completion of the response
//"Refresh: 1\r\n" // refresh the page automatically every n sec
"\r\n"
"<!DOCTYPE HTML>\r\n"
"<html>\r\n"
"<head>\r\n"
"<meta charset=\"UTF-8\">\r\n"
"<title>Cloud Printer: ChatGPT</title>\r\n"
"<link rel=\"icon\" href=\"https://files.seeedstudio.com/wiki/xiaoesp32c3-chatgpt/chatgpt-logo.png\" type=\"image/x-icon\">\r\n"
"</head>\r\n"
"<body>\r\n"
"<p style=\"text-align:center;\">\r\n"
"<img alt=\"ChatGPT\" src=\"https://files.seeedstudio.com/wiki/xiaoesp32c3-chatgpt/chatgpt-logo.png\" height=\"200\" width=\"200\">\r\n"
"<h1 align=\"center\">Cloud Printer</h1>\r\n"
"<h1 align=\"center\">OpenAI ChatGPT</h1>\r\n"
"<div style=\"text-align:center;vertical-align:middle;\">"
"<form action=\"/\" method=\"post\">"
"<input type=\"text\" placeholder=\"Please enter your question\" size=\"35\" name=\"chatgpttext\" required=\"required\"/>\r\n"
"<input type=\"submit\" value=\"Submit\" style=\"height:30px; width:80px;\"/>"
"</form>"
"</div>"
"</p>\r\n"
"</body>\r\n"
"<html>\r\n"
};
这段代码给我们带来了下图所示的页面效果。
![](/assets/img/esp32/esp32c3supermini/esp32c3chatgpt-4.png)
Tips
网页的 HTML 语法超出了本教程的范围。您可以自己学习使用 HTML,或者,我们也可以使用现有的生成工具来完成代码生成工作。我们建议使用HTML 生成器。值得注意的是,在C程序中,“ \ ”和“””是特殊字符,如果想在程序中保留这些特殊字符的功能,需要在它们前面添加右斜杠。
Client1指的是Web服务器建立后的Socket客户端,下面的代码是Web服务器处理的流程。
client1 = server.available();
if (client1){
Serial.println("New Client."); // print a message out the serial port
// an http request ends with a blank line
boolean currentLineIsBlank = true;
while (client1.connected()){
if (client1.available()){ // Check if the client is connected
char c = client1.read();
json_String += c;
if (c == '\n' && currentLineIsBlank) {
dataStr = json_String.substring(0, 4);
Serial.println(dataStr);
if(dataStr == "GET "){
client1.print(html_page); //Send the response body to the client
}
else if(dataStr == "POST"){
json_String = "";
while(client1.available()){
json_String += (char)client1.read();
}
Serial.println(json_String);
dataStart = json_String.indexOf("chatgpttext=") + strlen("chatgpttext=");
chatgpt_Q = json_String.substring(dataStart, json_String.length());
client1.print(html_page);
// close the connection:
delay(10);
client1.stop();
}
json_String = "";
break;
}
if (c == '\n') {
// you're starting a new line
currentLineIsBlank = true;
}
else if (c != '\r') {
// you've gotten a character on the current line
currentLineIsBlank = false;
}
}
}
}
在上面的示例程序中,我们需要使用server.begin()来启动 IoT 服务器。该语句需要放在函数中setup。
void setup()
{
Serial.begin(115200);
// Set WiFi to station mode and disconnect from an AP if it was previously connected
WiFi.mode(WIFI_STA);
WiFi.disconnect();
while(!Serial);
Serial.println("WiFi Setup done!");
WiFiConnect();
// Start the TCP server server
server.begin();
}
一旦运行上述程序,并且在浏览器中输入 ESP32C3SuperMini 的 IP 地址(前提是您的主机也需要与 ESP32C3SuperMini 在同一 WIFI 中),则 WiFiClient 的 GET 步骤将开始执行。此时,我们借助客户端打印方法,提交页面的HTML代码。
if(dataStr == "GET "){
client1.print(html_page);
}
我们在页面中设计了用于问题输入的输入框,当用户输入内容并点击提交按钮后,网页会获取按钮的状态并将输入的问题存储到字符串变量中chatgpt_Q。
json_String = "";
while(client1.available()){
json_String += (char)client1.read();
}
Serial.println(json_String);
dataStart = json_String.indexOf("chatgpttext=") + strlen("chatgpttext=");
chatgpt_Q = json_String.substring(dataStart, json_String.length());
client1.print(html_page);
// close the connection:
delay(10);
client1.stop();
运行效果如下图
![](/assets/img/esp32/esp32c3supermini/esp32c3chatgpt-5.png)
提交问题发送给Chatgpt
在上一步的页面中,有一个输入框。输入框是我们需要用户输入他们想问的问题的地方。我们需要做的就是获取这个问题并通过OpenAI提供的API请求发送出去。
步骤1 注册 OpenAI 帐户。(国内无法正常注册,可以自行百度)
您可以点击这里 进入OpenAI的注册地址。如果您之前已经注册过该帐户,则可以跳过此步骤。
![](/assets/img/esp32/esp32c3supermini/esp32c3chatgpt-6.png)
步骤2。获取 OpenAI API。
登录OpenAI网站,点击右上角您的账户头像,然后选择查看API密钥。
![](/assets/img/esp32/esp32c3supermini/esp32c3chatgpt-7.png)
在新的弹出页面中选择+创建新密钥,然后复制您的密钥并保存。
![](/assets/img/esp32/esp32c3supermini/esp32c3chatgpt-8.png)
同时,我们可以在程序中创建字符串变量并将此键复制到此处。
char chatgpt_token[] = "sk**********Rj9DYWSDFNA";
步骤3。根据OpenAI的HTTP请求编写程序。
OpenAI提供了非常详细的API使用教程,以便用户可以使用自己的API密钥来调用ChatGPT。
根据ChatGPT的文档,我们需要发送请求的格式如下:
curl https://api.openai.com/v1/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{"model": "text-davinci-003", "prompt": "Say this is a test", "temperature": 0, "max_tokens": 7}'
超文本传输协议 (HTTP) 用作客户端和服务器之间的请求-响应协议。 GET用于从指定资源请求数据。它通常用于从 API 获取值。 POST用于将数据发送到服务器以创建/更新资源。ESP32 可以使用三种不同类型的正文请求发出 HTTP POST 请求:URL 编码、JSON 对象或纯文本。这些是最常见的方法,应该与大多数 API 或 Web 服务集成。
以上信息非常重要,为编写HTTP POST程序提供了理论基础。首先,我们首先导入 HTTPClient 库。
#include <HTTPClient.h>
您还需要输入 OpenAI 域名,以便 ESP32C3 将问题发布到 ChatGPT。并且不要忘记 OpenAI API 密钥。
HTTPClient https;
const char* chatgpt_token = "YOUR_API_KEY";
char chatgpt_server[] = "https://api.openai.com/v1/completions";
我们需要使用 JSON 对象发出 HTTP POST 请求。
if (https.begin(chatgpt_server)) { // HTTPS
https.addHeader("Content-Type", "application/json");
String token_key = String("Bearer ") + chatgpt_token;
https.addHeader("Authorization", token_key);
String payload = String("{\"model\": \"text-davinci-003\", \"prompt\": \"") + chatgpt_Q + String("\", \"temperature\": 0, \"max_tokens\": 100}"); //Instead of TEXT as Payload, can be JSON as Paylaod
httpCode = https.POST(payload); // start connection and send HTTP header
payload = "";
}
else {
Serial.println("[HTTPS] Unable to connect");
delay(1000);
}
在程序中,我们通过POST()方法将payload 发送到服务器。chatgpt_Q是我们要发送到 ChatGPT 的问题内容,该内容将在“获取问题”页面中提供。
如果您对 ESP32C3 HTTPClient 的更多功能感兴趣,我们建议您阅读ESP32 HTTP GET 和 HTTP POST with Arduino IDE。
从chatgpt获取答案
接下来是整个教程的最后一步,我们如何获取ChatGPT的答案并记录下来。
我们继续阅读OpenAI提供的API文档来了解ChatGPT返回的消息内容的结构是什么样的。这将使我们能够编写程序来解析我们需要的内容。
{
"id": "cmpl-GERzeJQ4lvqPk8SkZu4XMIuR",
"object": "text_completion",
"created": 1586839808,
"model": "text-davinci:003",
"choices": [
{
"text": "\n\nThis is indeed a test",
"index": 0,
"logprobs": null,
"finish_reason": "length"
}
],
"usage": {
"prompt_tokens": 5,
"completion_tokens": 7,
"total_tokens": 12
}
}
从OpenAI提供的参考文档中,我们知道接口返回的消息中问题答案的位置在{"choices": [{"text": "\n\nxxxxxxx",}]}.
所以现在我们可以确定我们需要的“答案”应该以\n\n开头并以 , 结尾。然后,在程序中,我们可以使用该方法检索文本开始和结束的位置indexOf(),并存储返回答案的内容。
dataStart = payload.indexOf("\\n\\n") + strlen("\\n\\n");
dataEnd = payload.indexOf("\",\"", dataStart);
chatgpt_A = payload.substring(dataStart, dataEnd);
综上所述,我们可以使用 switch 方法结合程序当前的状态来确定我们应该执行程序的哪一步。
typedef enum
{
do_webserver_index,
send_chatgpt_request,
get_chatgpt_list,
}STATE_;
STATE_ currentState;
switch(currentState){
case do_webserver_index:
...
case send_chatgpt_request:
...
case get_chatgpt_list:
...
}
至此,整个程序的逻辑就结构化了。点击下图即可获取完整的程序代码。请不要急于上传程序,您需要将程序的ssid、密码和chatgpt_token更改为您自己的。
示例代码
// Load Wi-Fi library
#include "WiFi.h"
#include <HTTPClient.h>
// Replace with your network credentials
const char* ssid = "WIFI账号";
const char* password = "WIFI密码";
//chatgpt api
const char* chatgpt_token = "有的openAPI-Key";
char chatgpt_server[] = "https://api.openai.com/v1/completions";
// Set web server port number to 80
WiFiServer server(80);
WiFiClient client1;
HTTPClient https;
String chatgpt_Q;
String chatgpt_A;
String json_String;
uint16_t dataStart = 0;
uint16_t dataEnd = 0;
String dataStr;
int httpCode = 0;
typedef enum
{
do_webserver_index,
send_chatgpt_request,
get_chatgpt_list,
}STATE_;
STATE_ currentState;
void WiFiConnect(void){
WiFi.begin(ssid, password);
Serial.print("Connecting to ");
Serial.println(ssid);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected!");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
currentState = do_webserver_index;
}
const char html_page[] PROGMEM = {
"HTTP/1.1 200 OK\r\n"
"Content-Type: text/html\r\n"
"Connection: close\r\n" // the connection will be closed after completion of the response
//"Refresh: 1\r\n" // refresh the page automatically every n sec
"\r\n"
"<!DOCTYPE HTML>\r\n"
"<html>\r\n"
"<head>\r\n"
"<meta charset=\"UTF-8\">\r\n"
"<title>Cloud Printer: ChatGPT</title>\r\n"
"<link rel=\"icon\" href=\"https://files.seeedstudio.com/wiki/xiaoesp32c3-chatgpt/chatgpt-logo.png\" type=\"image/x-icon\">\r\n"
"</head>\r\n"
"<body>\r\n"
"<p style=\"text-align:center;\">\r\n"
"<img alt=\"ChatGPT\" src=\"https://files.seeedstudio.com/wiki/xiaoesp32c3-chatgpt/chatgpt-logo.png\" height=\"200\" width=\"200\">\r\n"
"<h1 align=\"center\">Cloud Printer</h1>\r\n"
"<h1 align=\"center\">OpenAI ChatGPT</h1>\r\n"
"<div style=\"text-align:center;vertical-align:middle;\">"
"<form action=\"/\" method=\"post\">"
"<input type=\"text\" placeholder=\"Please enter your question\" size=\"35\" name=\"chatgpttext\" required=\"required\"/>\r\n"
"<input type=\"submit\" value=\"Submit\" style=\"height:30px; width:80px;\"/>"
"</form>"
"</div>"
"</p>\r\n"
"</body>\r\n"
"<html>\r\n"
};
void setup()
{
Serial.begin(115200);
// Set WiFi to station mode and disconnect from an AP if it was previously connected
WiFi.mode(WIFI_STA);
WiFi.disconnect();
while(!Serial);
Serial.println("WiFi Setup done!");
WiFiConnect();
// Start the TCP server server
server.begin();
}
void loop()
{
switch(currentState){
case do_webserver_index:
Serial.println("Web Production Task Launch");
client1 = server.available(); // Check if the client is connected
if (client1){
Serial.println("New Client."); // print a message out the serial port
// an http request ends with a blank line
boolean currentLineIsBlank = true;
while (client1.connected()){
if (client1.available()){
char c = client1.read(); // Read the rest of the content, used to clear the cache
json_String += c;
if (c == '\n' && currentLineIsBlank) {
dataStr = json_String.substring(0, 4);
Serial.println(dataStr);
if(dataStr == "GET "){
client1.print(html_page); //Send the response body to the client
}
else if(dataStr == "POST"){
json_String = "";
while(client1.available()){
json_String += (char)client1.read();
}
Serial.println(json_String);
dataStart = json_String.indexOf("chatgpttext=") + strlen("chatgpttext="); // parse the request for the following content
chatgpt_Q = json_String.substring(dataStart, json_String.length());
client1.print(html_page);
Serial.print("Your Question is: ");
Serial.println(chatgpt_Q);
// close the connection:
delay(10);
client1.stop();
currentState = send_chatgpt_request;
}
json_String = "";
break;
}
if (c == '\n') {
// you're starting a new line
currentLineIsBlank = true;
}
else if (c != '\r') {
// you've gotten a character on the current line
currentLineIsBlank = false;
}
}
}
}
delay(1000);
break;
case send_chatgpt_request:
Serial.println("Ask ChatGPT a Question Task Launch");
if (https.begin(chatgpt_server)) { // HTTPS
https.addHeader("Content-Type", "application/json");
String token_key = String("Bearer ") + chatgpt_token;
https.addHeader("Authorization", token_key);
String payload = String("{\"model\": \"text-davinci-003\", \"prompt\": \"") + chatgpt_Q + String("\", \"temperature\": 0, \"max_tokens\": 100}"); //Instead of TEXT as Payload, can be JSON as Paylaod
httpCode = https.POST(payload); // start connection and send HTTP header
payload = "";
currentState = get_chatgpt_list;
}
else {
Serial.println("[HTTPS] Unable to connect");
delay(1000);
}
break;
case get_chatgpt_list:
Serial.println("Get ChatGPT Answers Task Launch");
// httpCode will be negative on error
// file found at server
if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) {
String payload = https.getString();
// Serial.println(payload);
dataStart = payload.indexOf("\\n\\n") + strlen("\\n\\n");
dataEnd = payload.indexOf("\",\"", dataStart);
chatgpt_A = payload.substring(dataStart, dataEnd);
Serial.print("ChatGPT Answer is: ");
Serial.println(chatgpt_A);
Serial.println("Wait 10s before next round...");
currentState = do_webserver_index;
}
else {
Serial.printf("[HTTPS] GET... failed, error: %s\n", https.errorToString(httpCode).c_str());
while(1);
}
https.end();
delay(10000);
break;
}
}
本代码来源于代码地址