免责声明
←点击查看上一篇文章内容
上一篇文章中写了思路以及实现代码,但是我忘记了一个重要的问题,那就是如果答案太长了,就这么一小块160*80的屏幕怎么回显全部答案呢?
当然,拍摄也不是手动的,就很无厘头
所以此次增加三个按钮,拍摄 上翻页 下翻页
【硬件连接】
1、ESP32
和 ST7735S 0.96英寸TFT彩屏
的连接方式如下:
TFT_MISO -> 无需连接(SPI不需要)
TFT_MOSI -> GPIO23
TFT_SCLK -> GPIO18
TFT_CS -> GPIO5
TFT_DC -> GPIO16
TFT_RST -> GPIO17
3.3V -> 3.3V
GND -> GND
2、按钮连接:
上翻页按钮:一端连接到GPIO34,另一端连接到GND。
下翻页按钮:一端连接到GPIO35,另一端连接到GND。
拍摄按钮:一端连接到GPIO32,另一端连接到GND。
【安装必要库】
● Adafruit_ST7735:打开Arduino IDE,进入工具 -> 库管理,搜索Adafruit_ST7735并安装。
● Adafruit_GFX:打开Arduino IDE,进入工具 -> 库管理,搜索Adafruit_GFX并安装。
【代码】
#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ST7735.h>
// WiFi
const char* ssid = "SSID";
const char* password = "PASSWORD";
// 通义千问API设置
const char* api_url = "https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions";
const char* api_key = "写自己的";
// 摄像头配置
#define PWDN_GPIO_NUM 32
#define RESET_GPIO_NUM -1
#define XCLK_GPIO_NUM 0
#define SIOD_GPIO_NUM 26
#define SIOC_GPIO_NUM 27
#define Y9_GPIO_NUM 35
#define Y8_GPIO_NUM 34
#define Y7_GPIO_NUM 39
#define Y6_GPIO_NUM 36
#define Y5_GPIO_NUM 21
#define Y4_GPIO_NUM 19
#define Y3_GPIO_NUM 18
#define Y2_GPIO_NUM 5
#define VSYNC_GPIO_NUM 25
#define HREF_GPIO_NUM 23
#define PCLK_GPIO_NUM 22
// TFT LCD配置
#define TFT_CS 5
#define TFT_DC 16
#define TFT_RST 17
#define TFT_MOSI 23
#define TFT_SCLK 18
// 按钮配置
#define UP_BUTTON_PIN 34
#define DOWN_BUTTON_PIN 35
#define SHOOT_BUTTON_PIN 32
Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_MOSI, TFT_SCLK, TFT_RST);
int currentLine = 0;
int maxLines = 10; // 每页显示的最大行数
String answer = "";
void setup() {
// 初始化串口通信
Serial.begin(115200);
// 初始化TFT LCD
tft.initR(INITR_BLACKTAB); // 初始化屏幕
tft.setRotation(1); // 设置屏幕方向
tft.fillScreen(ST7735_BLACK);
// 初始化摄像头
camera_config_t config;
config.ledc_channel = LEDC_CHANNEL_0;
config.ledc_timer = LEDC_TIMER_0;
config.pin_d0 = Y2_GPIO_NUM;
config.pin_d1 = Y3_GPIO_NUM;
config.pin_d2 = Y4_GPIO_NUM;
config.pin_d3 = Y5_GPIO_NUM;
config.pin_d4 = Y6_GPIO_NUM;
config.pin_d5 = Y7_GPIO_NUM;
config.pin_d6 = Y8_GPIO_NUM;
config.pin_d7 = Y9_GPIO_NUM;
config.pin_xclk = XCLK_GPIO_NUM;
config.pin_pclk = PCLK_GPIO_NUM;
config.pin_vsync = VSYNC_GPIO_NUM;
config.pin_href = HREF_GPIO_NUM;
config.pin_sscb_sda = SIOD_GPIO_NUM;
config.pin_sscb_scl = SIOC_GPIO_NUM;
config.pin_pwdn = PWDN_GPIO_NUM;
config.pin_reset = RESET_GPIO_NUM;
config.xclk_freq_hz = 20000000;
config.pixel_format = PIXFORMAT_JPEG;
config.frame_size = FRAMESIZE_UXGA; // 合适的分辨率
config.jpeg_quality = 12; // JPEG质量
config.fb_count = 1;
// 初始化摄像头
esp_err_t err = esp_camera_init(&config);
if (err != ESP_OK) {
Serial.printf("Camera init failed with error 0x%x", err);
return;
}
// 连接到WiFi
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Connecting to WiFi...");
}
Serial.println("Connected to WiFi");
// 设置HTTP客户端
HTTPClient http;
http.begin(api_url);
http.addHeader("Content-Type", "application/json");
http.addHeader("Authorization", "Bearer " + String(api_key));
// 初始化按钮
pinMode(UP_BUTTON_PIN, INPUT_PULLUP);
pinMode(DOWN_BUTTON_PIN, INPUT_PULLUP);
pinMode(SHOOT_BUTTON_PIN, INPUT_PULLUP);
}
void loop() {
// 处理按钮输入
handleButtons();
}
void handleButtons() {
static unsigned long lastDebounceTimeUp = 0;
static unsigned long lastDebounceTimeDown = 0;
static unsigned long lastDebounceTimeShoot = 0;
static int lastButtonStateUp = HIGH;
static int lastButtonStateDown = HIGH;
static int lastButtonStateShoot = HIGH;
static int debounceDelay = 50;
int readingUp = digitalRead(UP_BUTTON_PIN);
int readingDown = digitalRead(DOWN_BUTTON_PIN);
int readingShoot = digitalRead(SHOOT_BUTTON_PIN);
if (readingUp != lastButtonStateUp) {
lastDebounceTimeUp = millis();
}
if (readingDown != lastButtonStateDown) {
lastDebounceTimeDown = millis();
}
if (readingShoot != lastButtonStateShoot) {
lastDebounceTimeShoot = millis();
}
if ((millis() - lastDebounceTimeUp) > debounceDelay) {
if (readingUp != lastButtonStateUp) {
if (readingUp == LOW) {
currentLine--;
if (currentLine < 0) {
currentLine = 0;
}
displayAnswer();
}
lastButtonStateUp = readingUp;
}
}
if ((millis() - lastDebounceTimeDown) > debounceDelay) {
if (readingDown != lastButtonStateDown) {
if (readingDown == LOW) {
currentLine++;
if (currentLine * maxLines >= answer.length()) {
currentLine = answer.length() / maxLines;
}
displayAnswer();
}
lastButtonStateDown = readingDown;
}
}
if ((millis() - lastDebounceTimeShoot) > debounceDelay) {
if (readingShoot != lastButtonStateShoot) {
if (readingShoot == LOW) {
takePhotoAndProcess();
}
lastButtonStateShoot = readingShoot;
}
}
}
void takePhotoAndProcess() {
// 拍摄照片
camera_fb_t * fb = esp_camera_fb_get();
if (!fb) {
Serial.println("Camera capture failed");
return;
}
// 将照片转换为Base64编码
size_t encodedLen = base64_encoded_length(fb->len);
char *encodedData = (char *)malloc(encodedLen + 1);
base64_encode(encodedData, (const uint8_t *)fb->buf, fb->len);
// 构造JSON请求体
String payload = "{"messages":[{"role":"user","content":"![](data:image/jpeg;base64," + String(encodedDat) + ")"}]}";
// 发送POST请求
HTTPClient http;
http.begin(api_url);
http.addHeader("Content-Type", "application/json");
http.addHeader("Authorization", "Bearer " + String(api_key));
int httpResponseCode = http.POST(payload);
if (httpResponseCode > 0) {
// 读取响应
String response = http.getString();
Serial.println(response);
// 解析JSON响应
DynamicJsonDocument doc(1024);
DeserializationError error = deserializeJson(doc, response);
if (!error) {
answer = doc["choices"][0]["message"]["content"].as<String>(); // 根据实际API返回的字段名称进行调整
Serial.println("Answer: " + answer);
// 初始化显示
currentLine = 0;
displayAnswer();
} else {
Serial.println("Error parsing JSON");
}
} else {
Serial.println("Error on sending POST: " + String(httpResponseCode));
}
// 释放资源
esp_camera_fb_return(fb);
free(encodedData);
}
nvoid displayAnswer() {
tft.fillScreen(ST7735_BLACK);
tft.setTextColor(ST7735_WHITE);
tft.setTextSize(1); // 设置字体大小
int y = 0;
int lines = 0;
int startLine = currentLine * maxLines;
int endLine = min(startLine + maxLines, answer.length());
for (int i = startLine; i < endLine; i++) {
if (answer[i] == 'n') {
lines++;
y += 8; // 每行的高度
} else {
tft.drawChar(i - startLine, 0, y, answer[i], ST7735_WHITE, ST7735_BLACK, 1);
}
}
}
详细解释
【变量声明】lastDebounceTimeUp, lastDebounceTimeDown, lastDebounceTimeShoot:记录上次检测到按钮状态变化的时间。
lastButtonStateUp, lastButtonStateDown, lastButtonStateShoot:记录上次的按钮状态。
debounceDelay:去抖延迟时间,单位为ms。
【读取按钮状态】digitalRead(UP_BUTTON_PIN):读取上翻页按钮的状态。
digitalRead(DOWN_BUTTON_PIN):读取下翻页按钮的状态。
digitalRead(SHOOT_BUTTON_PIN):读取拍摄按钮的状态。
【检测状态变化】
如果当前读取的按钮状态与上次不同,则更新lastDebounceTime。
如果从上次状态变化到现在的时间超过了debounceDelay,则检查按钮状态是否真的发生了变化。
如果按钮状态确实发生了变化且按钮被按下(即读取到LOW),则执行相应的动作。
【上翻页按钮】
减少currentLine的值,确保不会小于0。
调用displayAnswer()函数更新屏幕显示。
【下翻页按钮】
增加currentLine的值,确保不会超过答案的总行数。
调用displayAnswer()函数更新屏幕显示。
【拍摄按钮】
调用takePhotoAndProcess()函数进行拍摄和处理。
【显示答案】
displayAnswer()函数负责在TFT LCD上显示当前页的答案。
等我做出来完整的我再发布视频教程!!!
题外话
我们搭建了社区论坛,可以在社区中分享相关资源以及思路,社区已经开放用户发布文章的权限,论坛部分资源已经在"百度网盘资源共享"群内分享过,可进群免费获取,详情见下文,社区论坛URL:
https://www.cn-fnst.top/ 欢迎发文
↑↑↑↑↑
点击上方"资源共享与交流社区"查看相关信息
往期推荐
推荐站内搜索:最好用的开发软件、免费开源系统、渗透测试工具云盘下载、最新渗透测试资料、最新黑客工具下载……
还没有评论,来说两句吧...