/*
ChatGPT進階實習,以序列埠提出問題,硬體控制LED。
*/
#include <WiFi.h> //wifi連接
#include <WiFiClientSecure.h> //提供安全的加密之 WiFi 連接功能。
#include <ArduinoJson.h> //解析和生成 JSON 格式的數據
#include <ChatGPT.hpp> //與OpenAI 的 ChatGPT API 進行互動的
#include <Adafruit_ILI9341.h>
#include <Adafruit_GFX.h> //Adafruit提供的圖形庫,用於顯示繪製圖形和文字。
#include <U8g2_for_Adafruit_GFX.h>
const char* ssid = "Wokwi-GUEST"; //模擬WIFI
const char* password = "";
const byte led1=15;
const byte led2=2;
//SPI介面 //顯示器名稱:tft
#define TFT_DC 21
#define TFT_CS 22
#define TFT_RST 5
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC,TFT_RST);
U8G2_FOR_ADAFRUIT_GFX u8g2GFX; //U8G2_FOR_ADAFRUIT_GFX物件名稱u8g2
#define FONT u8g2_font_wqy16_t_gb2312b
const char* apiKey = "向openai註冊申請的api_key";
const char* server = "https://api.openai.com/v1/completions";
WiFiClientSecure client; //建立物件
ChatGPT<WiFiClientSecure> chatGPT_Client(&client, "v1", apiKey, 60000);
void setup() {
pinMode(led1,OUTPUT);
pinMode(led2,OUTPUT);
Serial.begin(115200); //啟動序列埠
tft.begin(); //啟動
tft.setRotation(1);
ClearScreen();
tft.setCursor(0, 0); //游標定位
tft.setTextSize(2); //字體大小
u8g2GFX.begin(tft); // 連結U8G2到Adafruit GFX
u8g2GFX.setFont(FONT); //u8g2_font_wqy16_t_gb2312b
connectToWiFi(); //呼叫連結WIFI
client.setInsecure(); //實驗階段暫時取消安全認證
Serial.println("[ChatGPT]-實例測試- ");
}
void loop() {
delay(100);
u8g2GFX.setFontMode(0); //設置字體模式為透明
u8g2GFX.setFontDirection(0); //內定由左至右
u8g2GFX.setForegroundColor(ILI9341_WHITE); //前景色白色
//取得使用者輸入文字
String userInput = getUserInput();
//將使用者輸入文字向ChatGPT請求回應,答案為content
String content = getChatGPTResponse(userInput);
Serial.println(content); //由序列埠印出答案
Serial.println();
//解析回應,做ESP32硬體控制---------------------------------
if (content.indexOf("LED1=ON")!=-1) digitalWrite(led1,1);
if (content.indexOf("LED2=ON")!=-1) digitalWrite(led2,1);
if (content.indexOf("LED1=OFF")!=-1) digitalWrite(led1,0);
if (content.indexOf("LED2=OFF")!=-1) digitalWrite(led2,0);
delay(2000);
}
//-----------------------------------------
void ClearScreen(){
for(uint8_t rotation=0; rotation<4; rotation++) {
tft.setRotation(rotation);
tft.fillScreen(ILI9341_BLACK);//清除畫面
delay(10);
}
tft.setRotation(0);
}
//連線WIFI--------------------------------------------------
void connectToWiFi() {
tft.setTextColor(ILI9341_YELLOW);
tft.println("Connecting to WiFi");
WiFi.begin(ssid, password); //啟動連線
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
tft.print(".");
}
tft.println();
tft.println("Connected!");
tft.println(WiFi.localIP()); //顯示連線之IP
Serial.println("已連線:" + WiFi.localIP().toString());
tft.setTextColor(ILI9341_YELLOW);
tft.println("\nKeyin your Question:");
}
//----------------------------------------------------
String getUserInput() {
Serial.println("請輸入你的問題:");
while (!Serial.available()) {
delay(100); //等待使用者輸入…
}
String userInput = Serial.readStringUntil('n');
userInput.trim(); //去除前後的空白
Serial.flush(); //清除序列埠Buffer
Serial.print("[User Say]:");
Serial.println(userInput); //顯示輸入的問題
return userInput; //字串回傳
}
//------------------------------------------------------
String getChatGPTResponse(String pp) { //接收字串
String result; //設定回應的格式為字串
tft.setRotation(0);
//tft.fillScreen(ILI9341_BLACK);//清除畫面
ClearScreen();
u8g2GFX.setForegroundColor(ILI9341_GREEN); //前景色
tft.setTextSize(2); //字體大小
u8g2GFX.setCursor(0, 2*16); //游標定位
u8g2GFX.println("[Question] You say:");
u8g2GFX.println(pp); //顯示你的問題
u8g2GFX.println("Waiting ... ");
const char* pps = pp.c_str(); //String轉const char
//問題的格式為const char*,應將pp轉換後為pps
if (chatGPT_Client.chat_message("gpt-4", "user",\
pps, 100, true, result, false)) //content_only=true
{
Serial.print("[ChatGPT] response :");
u8g2GFX.println("[ChatGPT] response:\n");
//--------------------------------------------------------
const char* text = result.c_str(); //將結果result字串轉字元陣列
String wrappedText = wrapText(text, 30); //每列最多240/8
u8g2GFX.print(wrappedText); // 可自動換行之處理
//u8g2GFX.println(result); //超過螢幕寬不會自動換行
return getContent(result,true);//content_only=true
} else
return "ChatGPT Response Err!";
}
//------------------------------------------------------------
String getContent(String r,bool only) {
//如果content_only=true,result就是答案,不需解析,直接回傳。
if(only) return r; //回應值只有答案,不需再解析
//解析------------
StaticJsonDocument<512> doc;//創建一個JSON文檔存入解析數據
DeserializationError error = deserializeJson(doc, r);//
if (error) { // 檢查解析是否成功
Serial.print(F("deserializeJson() failed: "));
Serial.println(error.f_str()); //以Flash 記憶體顯示訊息。
return "Deserialization Error!";
}
// 提取答案content內容
const char* content = doc["choices"][0]["message"]["content"];
return content; //回傳答案
}
String wrapText(const char* text, int lineLength) {
String wrappedText = "";
int len = strlen(text);
for (int i = 0; i < len; i++) {
if(text[i]!='\n'){
wrappedText += text[i];
if ((i + 1) % lineLength == 0) {
wrappedText += '\n';
}
}
}
return wrappedText;
}