{"repo": "aaaayl0n/iris-chatbot-v0.1", "files": {"2.Firmware/ChatBot-OLED/src/main.cpp": {"f6c3b4f16d8f83d48aa96cb66eda30ed81e7f2b6": {"t": 1785768230.6560867, "n": 24496, "h": "01f2c59b8621d6d0", "c": "#include \n#include \"base64.h\"\n#include \"WiFi.h\"\n#include \n#include \"HTTPClient.h\"\n#include \"Audio1.h\"\n#include \"Audio2.h\"\n#include \n#include \n#include \nusing namespace websockets;\n\n#define key 38\n#define ADC 32\nconst char *wifiData[][2] = {\n {\"eworks\", \"zdkjy221\"}, \n {\"550A\", \"1122334455\"}};//替换为你自己的wifi\n\n//替换为你自己的讯飞API\nString APPID = \"XXXXXX\"; \nString APIKey = \"XXXXXXXXXXXXXXXXXXXXXX\";\nString APISecret = \"XXXXXXXXXXXXXXXXXXXXXXXX\";\n\n\nbool ledstatus = true;\nbool startPlay = false;\nbool lastsetence = false;\nbool isReady = false;\nunsigned long urlTime = 0;\nunsigned long pushTime = 0;\nint mainStatus = 0;\nint receiveFrame = 0;\nint noise = 50;\nHTTPClient https;\nU8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE, /* clock=*/ 12, /* data=*/ 11); // OLED\n\nhw_timer_t *timer = NULL;\n\nuint8_t adc_start_flag = 0;\nuint8_t adc_complete_flag = 0;\n\nAudio1 audio1;\nAudio2 audio2(false, 3, I2S_NUM_1);\n\n#define I2S_DOUT 16 // DIN\n#define I2S_BCLK 17 // BCLK\n#define I2S_LRC 18 // LRC\n\nvoid gain_token(void);\nvoid getText(String role, String content);\nvoid checkLen(JsonArray textArray);\nint getLength(JsonArray textArray);\nfloat calculateRMS(uint8_t *buffer, int bufferSize);\nvoid ConnServer();\nvoid ConnServer1();\n\nDynamicJsonDocument doc(4000);\nJsonArray text = doc.to();\n\nString url = \"\";\nString url1 = \"\";\nString Date = \"\";\nDynamicJsonDocument gen_params(const char *appid, const char *domain);\n\nString askquestion = \"\";\nString Answer = \"\";\n\nconst char *appId1 = \"508482c0\"; \nconst char *domain1 = \"generalv3.5\";\nconst char *websockets_server = \"ws://spark-api.xf-yun.com/v3.5/chat\";\nconst char *websockets_server1 = \"ws://ws-api.xfyun.cn/v2/iat\";\nusing namespace websockets;\n\nWebsocketsClient webSocketClient;\nWebsocketsClient webSocketClient1;\n\nint loopcount = 0;\nvoid onMessageCallback(WebsocketsMessage message)\n{\n StaticJsonDocument<4096> jsonDocument;\n DeserializationError error = deserializeJson(jsonDocument, message.data());\n\n if (!error)\n {\n int code = jsonDocument[\"header\"][\"code\"];\n if (code != 0)\n {\n Serial.print(\"sth is wrong: \");\n Serial.println(code);\n Serial.println(message.data());\n webSocketClient.close();\n }\n else\n {\n receiveFrame++;\n Serial.print(\"receiveFrame:\");\n Serial.println(receiveFrame);\n JsonObject choices = jsonDocument[\"payload\"][\"choices\"];\n int status = choices[\"status\"];\n const char *content = choices[\"text\"][0][\"content\"];\n Serial.println(content);\n Answer += content;\n String answer = \"\";\n if (Answer.length() >= 120 && (audio2.isplaying == 0))\n {\n String subAnswer = Answer.substring(0, 120);\n Serial.print(\"subAnswer:\");\n Serial.println(subAnswer);\n int lastPeriodIndex = subAnswer.lastIndexOf(\"。\");\n\n if (lastPeriodIndex != -1)\n {\n answer = Answer.substring(0, lastPeriodIndex + 1);\n Serial.print(\"answer: \");\n Serial.println(answer);\n Answer = Answer.substring(lastPeriodIndex + 2);\n Serial.print(\"Answer: \");\n Serial.println(Answer);\n audio2.connecttospeech(answer.c_str(), \"zh\");\n }\n else\n {\n const char *chinesePunctuation = \"?,:;,.\";\n\n int lastChineseSentenceIndex = -1;\n\n for (int i = 0; i < Answer.length(); ++i)\n {\n char currentChar = Answer.charAt(i);\n\n if (strchr(chinesePunctuation, currentChar) != NULL)\n {\n lastChineseSentenceIndex = i;\n }\n }\n if (lastChineseSentenceIndex != -1)\n {\n answer = Answer.substring(0, lastChineseSentenceIndex + 1);\n audio2.connecttospeech(answer.c_str(), \"zh\");\n Answer = Answer.substring(lastChineseSentenceIndex + 2);\n }\n else\n {\n answer = Answer.substring(0, 120);\n audio2.connecttospeech(answer.c_str(), \"zh\");\n Answer = Answer.substring(120 + 1);\n }\n }\n startPlay = true;\n }\n\n if (status == 2)\n {\n getText(\"assistant\", Answer);\n if (Answer.length() <= 80 && (audio2.isplaying == 0))\n {\n // getText(\"assistant\", Answer);\n audio2.connecttospeech(Answer.c_str(), \"zh\");\n }\n }\n }\n }\n}\n\nvoid onEventsCallback(WebsocketsEvent event, String data)\n{\n if (event == WebsocketsEvent::ConnectionOpened)\n {\n Serial.println(\"Send message to server0!\");\n DynamicJsonDocument jsonData = gen_params(appId1, domain1);\n String jsonString;\n serializeJson(jsonData, jsonString);\n Serial.println(jsonString);\n webSocketClient.send(jsonString);\n }\n else if (event == WebsocketsEvent::ConnectionClosed)\n {\n Serial.println(\"Connnection0 Closed\");\n }\n else if (event == WebsocketsEvent::GotPing)\n {\n Serial.println(\"Got a Ping!\");\n }\n else if (event == WebsocketsEvent::GotPong)\n {\n Serial.println(\"Got a Pong!\");\n }\n}\n\nvoid onMessageCallback1(WebsocketsMessage message) //语音流转文本 消息回调函数\n{\n StaticJsonDocument<4096> jsonDocument;\n DeserializationError error = deserializeJson(jsonDocument, message.data());\n\n if (!error)\n {\n int code = jsonDocument[\"code\"];\n if (code != 0)\n {\n Serial.println(code);\n Serial.println(message.data());\n webSocketClient1.close();\n }\n else\n {\n Serial.println(\"xunfeiyun return message:\");\n Serial.println(message.data());\n JsonArray ws = jsonDocument[\"data\"][\"result\"][\"ws\"].as();\n\n for (JsonVariant i : ws)\n {\n for (JsonVariant w : i[\"cw\"].as())\n {\n askquestion += w[\"w\"].as();\n }\n }\n Serial.println(askquestion);\n int status = jsonDocument[\"data\"][\"status\"];\n if (status == 2)\n {\n Serial.println(\"status == 2\");\n webSocketClient1.close();\n if (askquestion == \"\")\n {\n askquestion = \"sorry, i can't hear you\";\n audio2.connecttospeech(askquestion.c_str(), \"zh\");\n }\n else if (askquestion.substring(0, 9) == \"唱歌了\" or askquestion.substring(0, 9) == \"唱歌啦\")\n {\n\n if (askquestion.substring(0, 12) == \"唱歌了,\" or askquestion.substring(0, 12) == \"唱歌啦,\")\n { // 自建音乐服务器,按照文件名查找对应歌曲\n String audioStreamURL = \"http://192.168.0.1/mp3/\" + askquestion.substring(12, askquestion.length() - 3) + \".mp3\";\n Serial.println(audioStreamURL.c_str());\n audio2.connecttohost(audioStreamURL.c_str());\n }\n else if (askquestion.substring(9) == \"。\")\n {\n askquestion = \"好啊, 你想听什么歌?\";\n mainStatus = 1;\n audio2.connecttospeech(askquestion.c_str(), \"zh\");\n }\n else\n {\n String audioStreamURL = \"http://192.168.0.1/mp3/\" + askquestion.substring(9, askquestion.length() - 3) + \".mp3\";\n Serial.println(audioStreamURL.c_str());\n audio2.connecttohost(audioStreamURL.c_str());\n }\n }\n else if (mainStatus == 1)\n {\n askquestion.trim();\n if (askquestion.endsWith(\"。\"))\n {\n askquestion = askquestion.substring(0, askquestion.length() - 3);\n }\n else if (askquestion.endsWith(\".\") or askquestion.endsWith(\"?\"))\n {\n askquestion = askquestion.substring(0, askquestion.length() - 1);\n }\n String audioStreamURL = \"http://192.168.0.1/mp3/\" + askquestion + \".mp3\";\n Serial.println(audioStreamURL.c_str());\n audio2.connecttohost(audioStreamURL.c_str());\n mainStatus = 0;\n }\n else\n {\n getText(\"user\", askquestion);\n Serial.print(\"text:\");\n Serial.println(text);\n Answer = \"\";\n lastsetence = false;\n isReady = true;\n ConnServer();\n }\n }\n }\n }\n else\n {\n Serial.println(\"error:\");\n Serial.println(error.c_str());\n Serial.println(message.data());\n }\n}\n\nvoid onEventsCallback1(WebsocketsEvent event, String data)\n{\n if (event == WebsocketsEvent::ConnectionOpened)\n {\n Serial.println(\"Send message to xunfeiyun\");\n //digitalWrite(led2, HIGH);\n int silence = 0;\n int firstframe = 1;\n int j = 0;\n int voicebegin = 0;\n int voice = 0;\n DynamicJsonDocument doc(2500);\n while (1)\n {\n doc.clear();\n JsonObject data = doc.createNestedObject(\"data\");\n audio1.Record();\n float rms = calculateRMS((uint8_t *)audio1.wavData[0], 1280);//i2s数据存入wavedata了\n printf(\"%d %f\\n\", 0, rms);\n if (rms < noise)\n {\n if (voicebegin == 1)\n {\n silence++;\n // Serial.print(\"noise:\");\n // Serial.println(noise);\n }\n }\n else\n {\n voice++;\n if (voice >= 5)\n {\n voicebegin = 1;\n }\n else\n {\n voicebegin = 0;\n }\n silence = 0;\n }\n if (silence == 6)\n {\n data[\"status\"] = 2;\n data[\"format\"] = \"audio/L16;rate=8000\";\n data[\"audio\"] = base64::encode((byte *)audio1.wavData[0], 1280);\n data[\"encoding\"] = \"raw\";\n j++;\n\n String jsonString;\n serializeJson(doc, jsonString);\n\n webSocketClient1.send(jsonString);\n u8g2.clearBuffer(); // 清空缓存\n u8g2.drawStr(0,10,\"Thinking...\"); // 写文字\n u8g2.drawStr(0,20,\"OvO\");\n u8g2.sendBuffer();\n //digitalWrite(led2, LOW);\n delay(40);\n break;\n }\n if (firstframe == 1)\n {\n data[\"status\"] = 0;\n data[\"format\"] = \"audio/L16;rate=8000\";\n data[\"audio\"] = base64::encode((byte *)audio1.wavData[0], 1280);\n data[\"encoding\"] = \"raw\";\n j++;\n\n JsonObject common = doc.createNestedObject(\"common\");\n common[\"app_id\"] = appId1;\n\n JsonObject business = doc.createNestedObject(\"business\");\n business[\"domain\"] = \"iat\";\n business[\"language\"] = \"zh_cn\";\n business[\"accent\"] = \"mandarin\";\n business[\"vinfo\"] = 1;\n business[\"vad_eos\"] = 1000;\n\n String jsonString;\n serializeJson(doc, jsonString);\n\n webSocketClient1.send(jsonString);\n firstframe = 0;\n delay(40);\n }\n else\n {\n data[\"status\"] = 1;\n data[\"format\"] = \"audio/L16;rate=8000\";\n data[\"audio\"] = base64::encode((byte *)audio1.wavData[0], 1280);\n data[\"encoding\"] = \"raw\";\n\n String jsonString;\n serializeJson(doc, jsonString);\n\n webSocketClient1.send(jsonString);\n delay(40);\n }\n }\n }\n else if (event == WebsocketsEvent::ConnectionClosed)\n {\n Serial.println(\"Connnection1 Closed\");\n }\n else if (event == WebsocketsEvent::GotPing)\n {\n Serial.println(\"Got a Ping!\");\n }\n else if (event == WebsocketsEvent::GotPong)\n {\n Serial.println(\"Got a Pong!\");\n }\n}\nvoid ConnServer() //3.5对话模型\n{\n Serial.println(\"url:\" + url);\n\n webSocketClient.onMessage(onMessageCallback);\n webSocketClient.onEvent(onEventsCallback);\n // Connect to WebSocket\n Serial.println(\"Begin connect to server0......\");\n if (webSocketClient.connect(url.c_str()))\n {\n Serial.println(\"Connected to server0!\");\n }\n else\n {\n Serial.println(\"Failed to connect to server0!\");\n }\n}\n\nvoid ConnServer1() //语音流转文本\n{\n // Serial.println(\"url1:\" + url1);\n webSocketClient1.onMessage(onMessageCallback1);\n webSocketClient1.onEvent(onEventsCallback1);\n // Connect to WebSocket\n Serial.println(\"Begin connect to server1......\");\n if (webSocketClient1.connect(url1.c_str()))\n {\n Serial.println(\"Connected to server1!\");\n }\n else\n {\n Serial.println(\"Failed to connect to server1!\");\n }\n}\n\nvoid voicePlay()\n{\n if ((audio2.isplaying == 0) && Answer != \"\")\n {\n // String subAnswer = \"\";\n // String answer = \"\";\n // if (Answer.length() >= 100)\n // subAnswer = Answer.substring(0, 100);\n // else\n // {\n // subAnswer = Answer.substring(0);\n // lastsetence = true;\n // // startPlay = false;\n // }\n\n // Serial.print(\"subAnswer:\");\n // Serial.println(subAnswer);\n int firstPeriodIndex = Answer.indexOf(\"。\");\n int secondPeriodIndex = 0;\n\n if (firstPeriodIndex != -1)\n {\n secondPeriodIndex = Answer.indexOf(\"。\", firstPeriodIndex + 1);\n if (secondPeriodIndex == -1)\n secondPeriodIndex = firstPeriodIndex;\n }\n else\n {\n secondPeriodIndex = firstPeriodIndex;\n }\n\n if (secondPeriodIndex != -1)\n {\n String answer = Answer.substring(0, secondPeriodIndex + 1);\n Serial.print(\"answer: \");\n Serial.println(answer);\n Answer = Answer.substring(secondPeriodIndex + 2);\n audio2.connecttospeech(answer.c_str(), \"zh\");\n }\n else\n {\n const char *chinesePunctuation = \"?,:;,.\";\n\n int lastChineseSentenceIndex = -1;\n\n for (int i = 0; i < Answer.length(); ++i)\n {\n char currentChar = Answer.charAt(i);\n\n if (strchr(chinesePunctuation, currentChar) != NULL)\n {\n lastChineseSentenceIndex = i;\n }\n }\n\n if (lastChineseSentenceIndex != -1)\n {\n String answer = Answer.substring(0, lastChineseSentenceIndex + 1);\n audio2.connecttospeech(answer.c_str(), \"zh\");\n Answer = Answer.substring(lastChineseSentenceIndex + 2);\n }\n }\n startPlay = true;\n }\n else\n {\n // digitalWrite(led3, LOW);\n }\n}\n\nvoid wifiConnect(const char *wifiData[][2], int numNetworks)\n{\n WiFi.disconnect(true);\n for (int i = 0; i < numNetworks; ++i)\n {\n const char *ssid = wifiData[i][0];\n const char *password = wifiData[i][1];\n\n Serial.print(\"Connecting to \");\n Serial.println(ssid);\n\n WiFi.begin(ssid, password);\n uint8_t count = 0;\n while (WiFi.status() != WL_CONNECTED)\n {\n //digitalWrite(led1, ledstatus);\n ledstatus = !ledstatus;\n Serial.print(\".\");\n u8g2.clearBuffer();\n u8g2.drawStr(0,20,\"Connecting to wifi\");\n u8g2.sendBuffer(); \n count++;\n if (count >= 30)\n {\n Serial.printf(\"\\r\\n-- wifi connect fail! --\");\n u8g2.clearBuffer(); // 清空缓存\n u8g2.drawStr(0,10,\"wifi connect fail!\"); // 写文字\n u8g2.sendBuffer();\n break;\n }\n vTaskDelay(100);\n }\n\n if (WiFi.status() == WL_CONNECTED)\n {\n Serial.printf(\"\\r\\n-- wifi connect success! --\\r\\n\");\n Serial.print(\"IP address: \");\n Serial.println(WiFi.localIP());\n Serial.println(\"Free Heap: \" + String(ESP.getFreeHeap()));\n return; // 如果连接成功,退出函数\n u8g2.clearBuffer(); // 清空缓存\n u8g2.drawStr(0,10,\"Wifi Connected\"); // 写文字\n u8g2.sendBuffer();\n }\n }\n}\n\nString getUrl(String Spark_url, String host, String path, String Date)\n{\n\n // 拼接字符串\n String signature_origin = \"host: \" + host + \"\\n\";\n signature_origin += \"date: \" + Date + \"\\n\";\n signature_origin += \"GET \" + path + \" HTTP/1.1\";\n // signature_origin=\"host: spark-api.xf-yun.com\\ndate: Mon, 04 Mar 2024 19:23:20 GMT\\nGET /v3.5/chat HTTP/1.1\";\n\n // hmac-sha256 加密\n unsigned char hmac[32];\n mbedtls_md_context_t ctx;\n mbedtls_md_type_t md_type = MBEDTLS_MD_SHA256;\n const size_t messageLength = signature_origin.length();\n const size_t keyLength = APISecret.length();\n mbedtls_md_init(&ctx);\n mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(md_type), 1);\n mbedtls_md_hmac_starts(&ctx, (const unsigned char *)APISecret.c_str(), keyLength);\n mbedtls_md_hmac_update(&ctx, (const unsigned char *)signature_origin.c_str(), messageLength);\n mbedtls_md_hmac_finish(&ctx, hmac);\n mbedtls_md_free(&ctx);\n\n // base64 编码\n String signature_sha_base64 = base64::encode(hmac, sizeof(hmac) / sizeof(hmac[0]));\n\n // 替换Date\n Date.replace(\",\", \"%2C\");\n Date.replace(\" \", \"+\");\n Date.replace(\":\", \"%3A\");\n String authorization_origin = \"api_key=\\\"\" + APIKey + \"\\\", algorithm=\\\"hmac-sha256\\\", headers=\\\"host date request-line\\\", signature=\\\"\" + signature_sha_base64 + \"\\\"\";\n String authorization = base64::encode(authorization_origin);\n String url = Spark_url + '?' + \"authorization=\" + authorization + \"&date=\" + Date + \"&host=\" + host;\n Serial.println(url);\n return url;\n}\n\nvoid getTimeFromServer()\n{\n String timeurl = \"https://www.baidu.com\";\n HTTPClient http;\n http.begin(timeurl);\n const char *headerKeys[] = {\"Date\"};\n http.collectHeaders(headerKeys, sizeof(headerKeys) / sizeof(headerKeys[0]));\n int httpCode = http.GET();\n Date = http.header(\"Date\");\n Serial.println(Date);\n http.end();\n // delay(50); // 可以根据实际情况调整延时时间\n}\n\nvoid setup()\n{\n // String Date = \"Fri, 22 Mar 2024 03:35:56 GMT\";\n Serial.begin(115200);\n u8g2.begin();\n u8g2.clearBuffer(); // 清空缓存\n u8g2.setFont(u8g2_font_HelvetiPixel_tr); // 选择字体\n u8g2.drawStr(0,10,\"ChatBot Init\"); // 写文字\n u8g2.sendBuffer();\n // pinMode(ADC,ANALOG);\n pinMode(key, INPUT_PULLUP);\n pinMode(34, INPUT_PULLUP);\n pinMode(35, INPUT_PULLUP);\n //pinMode(led1, OUTPUT);\n //pinMode(led2, OUTPUT);\n //pinMode(led3, OUTPUT);\n audio1.init();\n\n int numNetworks = sizeof(wifiData) / sizeof(wifiData[0]);\n wifiConnect(wifiData, numNetworks);\n\n getTimeFromServer();\n\n audio2.setPinout(I2S_BCLK, I2S_LRC, I2S_DOUT);\n audio2.setVolume(50);\n audio2.connecttospeech(\"Hello! I'm Iris,your personal AI assistant\", \"en\");\n\n // String Date = \"Fri, 22 Mar 2024 03:35:56 GMT\";\n url = getUrl(\"ws://spark-api.xf-yun.com/v3.5/chat\", \"spark-api.xf-yun.com\", \"/v3.5/chat\", Date);\n url1 = getUrl(\"ws://ws-api.xfyun.cn/v2/iat\", \"ws-api.xfyun.cn\", \"/v2/iat\", Date);\n urlTime = millis();\n u8g2.clearBuffer(); // 清空缓存\n u8g2.drawStr(0,10,\"^^\"); // 写文字\n u8g2.drawStr(0,30,\"Wifi connection failed.\"); // 写文字\n u8g2.drawStr(0,40,\"Please restart\"); // 写文字\n u8g2.drawStr(0,50,\"and try again.\");\n u8g2.sendBuffer();\n\n ///////////////////////////////////\n}\nint flagfirsttalking = 0;//全局变量,记录是否在说话\nvoid loop()\n{\n \n webSocketClient.poll();\n webSocketClient1.poll();\n // delay(10);\n if (startPlay)\n {\n voicePlay(); //播放声音\n }\n\n audio2.loop();\n \n if (audio2.isplaying == 1)\n {\n if(flagfirsttalking == 0){\n u8g2.clearBuffer(); // 清空缓存\n u8g2.drawStr(0,10,\"Talking...\"); // 写文字\n u8g2.sendBuffer();\n flagfirsttalking = 1;\n }\n }\n else\n {\n if(flagfirsttalking == 1){\n u8g2.clearBuffer(); // 清空缓存\n u8g2.setFont(u8g2_font_helvB24_tr);\n u8g2.drawStr(0,30,\"^^\"); // 写文字\n u8g2.sendBuffer();\n u8g2.setFont(u8g2_font_HelvetiPixel_tr);\n flagfirsttalking = 0;\n }\n \n //digitalWrite(led3, LOW);\n if ((urlTime + 240000 < millis()) && (audio2.isplaying == 0)) //鉴权\n {\n urlTime = millis();\n getTimeFromServer();\n url = getUrl(\"ws://spark-api.xf-yun.com/v3.5/chat\", \"spark-api.xf-yun.com\", \"/v3.5/chat\", Date);\n url1 = getUrl(\"ws://ws-api.xfyun.cn/v2/iat\", \"ws-api.xfyun.cn\", \"/v2/iat\", Date);\n }\n }\n\n if (digitalRead(key) == 0)\n {\n audio2.isplaying = 0;\n startPlay = false;\n isReady = false;\n Answer = \"\";\n Serial.printf(\"Start recognition\\r\\n\\r\\n\");\n u8g2.clearBuffer(); // 清空缓存\n u8g2.drawStr(0,10,\"Listening...\"); // 写文字\n u8g2.sendBuffer();\n\n adc_start_flag = 1;\n // Serial.println(esp_get_free_heap_size());\n\n if (urlTime + 240000 < millis()) // 超过4分钟,重新做一次鉴权\n {\n urlTime = millis();\n getTimeFromServer();\n url = getUrl(\"ws://spark-api.xf-yun.com/v3.5/chat\", \"spark-api.xf-yun.com\", \"/v3.5/chat\", Date);\n url1 = getUrl(\"ws://ws-api.xfyun.cn/v2/iat\", \"ws-api.xfyun.cn\", \"/v2/iat\", Date);\n }\n askquestion = \"\";\n ConnServer1();\n adc_complete_flag = 0;\n }\n}\n\nvoid getText(String role, String content)\n{\n checkLen(text);\n DynamicJsonDocument jsoncon(1024);\n jsoncon[\"role\"] = role;\n jsoncon[\"content\"] = content;\n text.add(jsoncon);\n jsoncon.clear();\n String serialized;\n serializeJson(text, serialized);\n Serial.print(\"text: \");\n Serial.println(serialized);\n // serializeJsonPretty(text, Serial);\n}\n\nint getLength(JsonArray textArray)\n{\n int length = 0;\n for (JsonObject content : textArray)\n {\n const char *temp = content[\"content\"];\n int leng = strlen(temp);\n length += leng;\n }\n return length;\n}\n\nvoid checkLen(JsonArray textArray)\n{\n while (getLength(textArray) > 3000)\n {\n textArray.remove(0);\n }\n // return textArray;\n}\n\nDynamicJsonDocument gen_params(const char *appid, const char *domain)\n{\n DynamicJsonDocument data(2048);\n\n JsonObject header = data.createNestedObject(\"header\");\n header[\"app_id\"] = appid;\n header[\"uid\"] = \"1234\";\n\n JsonObject parameter = data.createNestedObject(\"parameter\");\n JsonObject chat = parameter.createNestedObject(\"chat\");\n chat[\"domain\"] = domain;\n chat[\"temperature\"] = 0.5;\n chat[\"max_tokens\"] = 1024;\n\n JsonObject payload = data.createNestedObject(\"payload\");\n JsonObject message = payload.createNestedObject(\"message\");\n\n JsonArray textArray = message.createNestedArray(\"text\");\n for (const auto &item : text)\n {\n textArray.add(item);\n }\n return data;\n}\n\nfloat calculateRMS(uint8_t *buffer, int bufferSize)\n{\n float sum = 0;\n int16_t sample;\n\n for (int i = 0; i < bufferSize; i += 2)\n {\n\n sample = (buffer[i + 1] << 8) | buffer[i];\n sum += sample * sample;\n }\n\n sum /= (bufferSize / 2);\n\n return sqrt(sum);\n}\n"}}, "4.Docs/report.md": {"f6c3b4f16d8f83d48aa96cb66eda30ed81e7f2b6": {"t": 1785768230.8279724, "n": 24826, "h": "88fb42c6158d65ca", "c": "# 1\n\n\n\n```\n\t系统初始化 ---> WiFi连接 ---> 时间获取\n ↓ ↓ ↓\n 主循环处理 <------ 更新时间和URL <--- 定时触发\n ↓\n 检测按键 ---> 语音录制 ---> WebSocket通讯(ASR)\n ↓ ↓\n 语音处理 ---> 语音播放 <--- WebSocket通讯(NLP)\n ↓\n OLED状态显示\n```\n\n获取时间主要是为了加时间戳来鉴权\n\n\n\n目前还是一个注册加回调函数的方式,屏幕显示不能显示一些动态的表情之类的。现在的代码就挺屎山的,所以后面打算重构一下并引入FreeRTOS。\n\n\n\n```c\n#include \n#include \"base64.h\"\n#include \"WiFi.h\"\n#include \n#include \"HTTPClient.h\"\n#include \"Audio1.h\"\n#include \"Audio2.h\"\n#include \n#include \n#include \nusing namespace websockets;\n\n#define key 38\n#define ADC 32\nconst char *wifiData[][2] = {\n {\"550A\", \"1122334455\"},\n {\"eworks\", \"zdkjy221\"} \n };\n\n\nString APPID = \"508482c0\"; \nString APIKey = \"533a93f2e37fcdecc92e46421aee339a\";\nString APISecret = \"ZTIwOTMwM2JlOWIxZTMxNDM5NGEzN2Uy\";\n\n\nbool ledstatus = true;\nbool startPlay = false;\nbool lastsetence = false;\nbool isReady = false;\nunsigned long urlTime = 0;\nunsigned long pushTime = 0;\nint mainStatus = 0;\nint receiveFrame = 0;\nint noise = 50;\nHTTPClient https;\nU8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE, /* clock=*/ 12, /* data=*/ 11); // OLED\n\nhw_timer_t *timer = NULL;\n\nuint8_t adc_start_flag = 0;\nuint8_t adc_complete_flag = 0;\n\nAudio1 audio1;\nAudio2 audio2(false, 3, I2S_NUM_1);\n\n#define I2S_DOUT 16 // DIN\n#define I2S_BCLK 17 // BCLK\n#define I2S_LRC 18 // LRC\n\n//void gain_token(void);\nvoid getText(String role, String content);\nvoid checkLen(JsonArray textArray);\nint getLength(JsonArray textArray);\nfloat calculateRMS(uint8_t *buffer, int bufferSize);\nvoid ConnServer();\nvoid ConnServer1();\n\nDynamicJsonDocument doc(4000);\nJsonArray text = doc.to();\n\nString url = \"\";\nString url1 = \"\";\nString Date = \"\";\nDynamicJsonDocument gen_params(const char *appid, const char *domain);\n\nString askquestion = \"\";\nString Answer = \"\";\n\nconst char *appId1 = \"508482c0\"; \nconst char *domain1 = \"generalv3.5\";\nconst char *websockets_server = \"ws://spark-api.xf-yun.com/v3.5/chat\";\nconst char *websockets_server1 = \"ws://ws-api.xfyun.cn/v2/iat\";\nusing namespace websockets;\n\nWebsocketsClient webSocketClient;\nWebsocketsClient webSocketClient1;\n\nint loopcount = 0;\nvoid onMessageCallback(WebsocketsMessage message) //NLP 消息回调函数\n{\n StaticJsonDocument<4096> jsonDocument;\n DeserializationError error = deserializeJson(jsonDocument, message.data());\n\n if (!error)\n {\n int code = jsonDocument[\"header\"][\"code\"];\n if (code != 0)\n {\n Serial.print(\"error code: \");\n Serial.println(code);\n Serial.println(message.data());\n webSocketClient.close();\n }\n else\n {\n receiveFrame++;\n Serial.print(\"receiveFrame:\");\n Serial.println(receiveFrame);\n JsonObject choices = jsonDocument[\"payload\"][\"choices\"];\n int status = choices[\"status\"];\n const char *content = choices[\"text\"][0][\"content\"];\n Serial.println(content);\n Answer += content;\n String answer = \"\";\n if (Answer.length() >= 120 && (audio2.isplaying == 0))\n {\n String subAnswer = Answer.substring(0, 120);\n Serial.print(\"subAnswer:\");\n Serial.println(subAnswer);\n int lastPeriodIndex = subAnswer.lastIndexOf(\"。\");\n\n if (lastPeriodIndex != -1)\n {\n answer = Answer.substring(0, lastPeriodIndex + 1);\n Serial.print(\"answer: \");\n Serial.println(answer);\n Answer = Answer.substring(lastPeriodIndex + 2);\n Serial.print(\"Answer: \");\n Serial.println(Answer);\n audio2.connecttospeech(answer.c_str(), \"zh\");\n }\n else\n {\n const char *chinesePunctuation = \"?,:;。,.\";\n\n int lastChineseSentenceIndex = -1;\n\n for (int i = 0; i < Answer.length(); ++i)\n {\n char currentChar = Answer.charAt(i);\n\n if (strchr(chinesePunctuation, currentChar) != NULL)\n {\n lastChineseSentenceIndex = i;\n }\n }\n if (lastChineseSentenceIndex != -1)\n {\n answer = Answer.substring(0, lastChineseSentenceIndex + 1);\n audio2.connecttospeech(answer.c_str(), \"zh\");\n Answer = Answer.substring(lastChineseSentenceIndex + 2);\n }\n else\n {\n answer = Answer.substring(0, 120);\n audio2.connecttospeech(answer.c_str(), \"zh\");\n Answer = Answer.substring(120 + 1);\n }\n }\n startPlay = true;\n }\n\n if (status == 2)\n {\n getText(\"assistant\", Answer);\n if (Answer.length() <= 80 && (audio2.isplaying == 0))\n {\n // getText(\"assistant\", Answer);\n audio2.connecttospeech(Answer.c_str(), \"zh\");\n }\n }\n }\n }\n}\n\nvoid onEventsCallback(WebsocketsEvent event, String data) //NLP 事件回调函数\n{\n if (event == WebsocketsEvent::ConnectionOpened)\n {\n Serial.println(\"Send message to server0!\");\n DynamicJsonDocument jsonData = gen_params(appId1, domain1);\n String jsonString;\n serializeJson(jsonData, jsonString);\n Serial.println(jsonString);\n webSocketClient.send(jsonString);\n }\n else if (event == WebsocketsEvent::ConnectionClosed)\n {\n Serial.println(\"Connnection0 Closed\");\n }\n else if (event == WebsocketsEvent::GotPing)\n {\n Serial.println(\"Got a Ping!\");\n }\n else if (event == WebsocketsEvent::GotPong)\n {\n Serial.println(\"Got a Pong!\");\n }\n}\n\nvoid onMessageCallback1(WebsocketsMessage message) //语音流转文本 消息回调函数\n{\n StaticJsonDocument<4096> jsonDocument;\n DeserializationError error = deserializeJson(jsonDocument, message.data());\n\n if (!error)\n {\n int code = jsonDocument[\"code\"];\n if (code != 0)\n {\n Serial.println(code);\n Serial.println(message.data());\n webSocketClient1.close();\n }\n else\n {\n Serial.println(\"xunfeiyun return message:\");\n Serial.println(message.data());\n JsonArray ws = jsonDocument[\"data\"][\"result\"][\"ws\"].as();\n\n for (JsonVariant i : ws)\n {\n for (JsonVariant w : i[\"cw\"].as())\n {\n askquestion += w[\"w\"].as();\n }\n }\n Serial.println(askquestion);\n int status = jsonDocument[\"data\"][\"status\"];\n if (status == 2)\n {\n Serial.println(\"status == 2\");\n webSocketClient1.close();\n if (askquestion == \"\")\n {\n askquestion = \"sorry, i can't hear you\";\n audio2.connecttospeech(askquestion.c_str(), \"zh\");\n }\n /*else if (askquestion.substring(0, 9) == \"唱歌了\" or askquestion.substring(0, 9) == \"唱歌啦\")\n {\n\n if (askquestion.substring(0, 12) == \"唱歌了,\" or askquestion.substring(0, 12) == \"唱歌啦,\")\n { // 自建音乐服务器,按照文件名查找对应歌曲\n String audioStreamURL = \"http://192.168.0.1/mp3/\" + askquestion.substring(12, askquestion.length() - 3) + \".mp3\";\n Serial.println(audioStreamURL.c_str());\n audio2.connecttohost(audioStreamURL.c_str());\n }\n else if (askquestion.substring(9) == \"。\")\n {\n askquestion = \"好啊, 你想听什么歌?\";\n mainStatus = 1;\n audio2.connecttospeech(askquestion.c_str(), \"zh\");\n }\n else\n {\n String audioStreamURL = \"http://192.168.0.1/mp3/\" + askquestion.substring(9, askquestion.length() - 3) + \".mp3\";\n Serial.println(audioStreamURL.c_str());\n audio2.connecttohost(audioStreamURL.c_str());\n }\n }\n else if (mainStatus == 1)\n {\n askquestion.trim();\n if (askquestion.endsWith(\"。\"))\n {\n askquestion = askquestion.substring(0, askquestion.length() - 3);\n }\n else if (askquestion.endsWith(\".\") or askquestion.endsWith(\"?\"))\n {\n askquestion = askquestion.substring(0, askquestion.length() - 1);\n }\n String audioStreamURL = \"http://192.168.0.1/mp3/\" + askquestion + \".mp3\";\n Serial.println(audioStreamURL.c_str());\n audio2.connecttohost(audioStreamURL.c_str());\n mainStatus = 0;\n }*/\n else\n {\n getText(\"user\", askquestion);\n Serial.print(\"text:\");\n Serial.println(text);\n Answer = \"\";\n lastsetence = false;\n isReady = true;\n ConnServer();\n }\n }\n }\n }\n else\n {\n Serial.println(\"error:\");\n Serial.println(error.c_str());\n Serial.println(message.data());\n }\n}\n\nvoid onEventsCallback1(WebsocketsEvent event, String data)\n{\n if (event == WebsocketsEvent::ConnectionOpened)\n {\n Serial.println(\"Send message to xunfeiyun\");\n //digitalWrite(led2, HIGH);\n int silence = 0;\n int firstframe = 1;\n int j = 0;\n int voicebegin = 0;\n int voice = 0;\n DynamicJsonDocument doc(2500);\n while (1)\n {\n doc.clear();\n JsonObject data = doc.createNestedObject(\"data\");\n audio1.Record();\n float rms = calculateRMS((uint8_t *)audio1.wavData[0], 1280);//i2s数据存入wavedata了\n printf(\"%d %f\\n\", 0, rms);\n if (rms < noise)\n {\n if (voicebegin == 1)\n {\n silence++;\n // Serial.print(\"noise:\");\n // Serial.println(noise);\n }\n }\n else\n {\n voice++;\n if (voice >= 5)\n {\n voicebegin = 1;\n }\n else\n {\n voicebegin = 0;\n }\n silence = 0;\n }\n if (silence == 6)\n {\n data[\"status\"] = 2;\n data[\"format\"] = \"audio/L16;rate=8000\";\n data[\"audio\"] = base64::encode((byte *)audio1.wavData[0], 1280);\n data[\"encoding\"] = \"raw\";\n j++;\n\n String jsonString;\n serializeJson(doc, jsonString);\n\n webSocketClient1.send(jsonString);\n u8g2.clearBuffer(); // 清空缓存\n u8g2.drawStr(0,10,\"Thinking...\"); // 写文字\n u8g2.drawStr(0,20,\"OvO\");\n u8g2.sendBuffer();\n //digitalWrite(led2, LOW);\n delay(40);\n break;\n }\n if (firstframe == 1)\n {\n data[\"status\"] = 0;\n data[\"format\"] = \"audio/L16;rate=8000\";\n data[\"audio\"] = base64::encode((byte *)audio1.wavData[0], 1280);\n data[\"encoding\"] = \"raw\";\n j++;\n\n JsonObject common = doc.createNestedObject(\"common\");\n common[\"app_id\"] = appId1;\n\n JsonObject business = doc.createNestedObject(\"business\");\n business[\"domain\"] = \"iat\";\n business[\"language\"] = \"zh_cn\";\n business[\"accent\"] = \"mandarin\";\n business[\"vinfo\"] = 1;\n business[\"vad_eos\"] = 1000;\n\n String jsonString;\n serializeJson(doc, jsonString);\n\n webSocketClient1.send(jsonString);\n firstframe = 0;\n delay(40);\n }\n else\n {\n data[\"status\"] = 1;\n data[\"format\"] = \"audio/L16;rate=8000\";\n data[\"audio\"] = base64::encode((byte *)audio1.wavData[0], 1280);\n data[\"encoding\"] = \"raw\";\n\n String jsonString;\n serializeJson(doc, jsonString);\n\n webSocketClient1.send(jsonString);\n delay(40);\n }\n }\n }\n else if (event == WebsocketsEvent::ConnectionClosed)\n {\n Serial.println(\"Connnection1 Closed\");\n }\n else if (event == WebsocketsEvent::GotPing)\n {\n Serial.println(\"Got a Ping!\");\n }\n else if (event == WebsocketsEvent::GotPong)\n {\n Serial.println(\"Got a Pong!\");\n }\n}\nvoid ConnServer() //3.5对话模型\n{\n Serial.println(\"url:\" + url);\n\n webSocketClient.onMessage(onMessageCallback);\n webSocketClient.onEvent(onEventsCallback);\n // Connect to WebSocket\n Serial.println(\"Begin connect to server0......\");\n if (webSocketClient.connect(url.c_str()))\n {\n Serial.println(\"Connected to server0!\");\n }\n else\n {\n Serial.println(\"Failed to connect to server0!\");\n }\n}\n\nvoid ConnServer1() //语音流转文本\n{\n // Serial.println(\"url1:\" + url1);\n webSocketClient1.onMessage(onMessageCallback1);\n webSocketClient1.onEvent(onEventsCallback1);\n // Connect to WebSocket\n Serial.println(\"Begin connect to server1......\");\n if (webSocketClient1.connect(url1.c_str()))\n {\n Serial.println(\"Connected to server1!\");\n }\n else\n {\n Serial.println(\"Failed to connect to server1!\");\n }\n}\n\nvoid voicePlay()\n{\n if ((audio2.isplaying == 0) && Answer != \"\")\n {\n // String subAnswer = \"\";\n // String answer = \"\";\n // if (Answer.length() >= 100)\n // subAnswer = Answer.substring(0, 100);\n // else\n // {\n // subAnswer = Answer.substring(0);\n // lastsetence = true;\n // // startPlay = false;\n // }\n\n // Serial.print(\"subAnswer:\");\n // Serial.println(subAnswer);\n int firstPeriodIndex = Answer.indexOf(\"。\");\n int secondPeriodIndex = 0;\n\n if (firstPeriodIndex != -1)\n {\n secondPeriodIndex = Answer.indexOf(\"。\", firstPeriodIndex + 1);\n if (secondPeriodIndex == -1)\n secondPeriodIndex = firstPeriodIndex;\n }\n else\n {\n secondPeriodIndex = firstPeriodIndex;\n }\n\n if (secondPeriodIndex != -1)\n {\n String answer = Answer.substring(0, secondPeriodIndex + 1);\n Serial.print(\"answer: \");\n Serial.println(answer);\n Answer = Answer.substring(secondPeriodIndex + 2);\n audio2.connecttospeech(answer.c_str(), \"zh\");\n }\n else\n {\n const char *chinesePunctuation = \"?,:;,.\";\n\n int lastChineseSentenceIndex = -1;\n\n for (int i = 0; i < Answer.length(); ++i)\n {\n char currentChar = Answer.charAt(i);\n\n if (strchr(chinesePunctuation, currentChar) != NULL)\n {\n lastChineseSentenceIndex = i;\n }\n }\n\n if (lastChineseSentenceIndex != -1)\n {\n String answer = Answer.substring(0, lastChineseSentenceIndex + 1);\n audio2.connecttospeech(answer.c_str(), \"zh\");\n Answer = Answer.substring(lastChineseSentenceIndex + 2);\n }\n }\n startPlay = true;\n }\n else\n {\n // digitalWrite(led3, LOW);\n }\n}\n\nvoid wifiConnect(const char *wifiData[][2], int numNetworks)\n{\n WiFi.disconnect(true);\n for (int i = 0; i < numNetworks; ++i)\n {\n const char *ssid = wifiData[i][0];\n const char *password = wifiData[i][1];\n\n Serial.print(\"Connecting to \");\n Serial.println(ssid);\n\n WiFi.begin(ssid, password);\n uint8_t count = 0;\n while (WiFi.status() != WL_CONNECTED)\n {\n //digitalWrite(led1, ledstatus);\n ledstatus = !ledstatus;\n Serial.print(\".\");\n u8g2.clearBuffer();\n u8g2.drawStr(0,20,\"Connecting to wifi\");\n u8g2.sendBuffer(); \n count++;\n if (count >= 30)\n {\n Serial.printf(\"\\r\\n-- wifi connect fail! --\");\n u8g2.clearBuffer(); // 清空缓存\n u8g2.drawStr(0,10,\"^^\"); // 写文字\n u8g2.drawStr(0,30,\"Wifi connection failed.\"); // 写文字\n u8g2.drawStr(0,40,\"Please restart\"); // 写文字\n u8g2.drawStr(0,50,\"and try again.\");\n u8g2.sendBuffer();\n break;\n }\n vTaskDelay(100);\n }\n\n if (WiFi.status() == WL_CONNECTED)\n {\n Serial.printf(\"\\r\\n-- wifi connect success! --\\r\\n\");\n Serial.print(\"IP address: \");\n Serial.println(WiFi.localIP());\n Serial.println(\"Free Heap: \" + String(ESP.getFreeHeap()));\n return; // 如果连接成功,退出函数\n u8g2.clearBuffer(); // 清空缓存\n u8g2.drawStr(0,10,\"Wifi Connected\"); // 写文字\n u8g2.sendBuffer();\n }\n }\n}\n\nString getUrl(String Spark_url, String host, String path, String Date)\n{\n\n // 拼接字符串\n String signature_origin = \"host: \" + host + \"\\n\";\n signature_origin += \"date: \" + Date + \"\\n\";\n signature_origin += \"GET \" + path + \" HTTP/1.1\";\n // signature_origin=\"host: spark-api.xf-yun.com\\ndate: Mon, 04 Mar 2024 19:23:20 GMT\\nGET /v3.5/chat HTTP/1.1\";\n\n // hmac-sha256 加密\n unsigned char hmac[32];\n mbedtls_md_context_t ctx;\n mbedtls_md_type_t md_type = MBEDTLS_MD_SHA256;\n const size_t messageLength = signature_origin.length();\n const size_t keyLength = APISecret.length();\n mbedtls_md_init(&ctx);\n mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(md_type), 1);\n mbedtls_md_hmac_starts(&ctx, (const unsigned char *)APISecret.c_str(), keyLength);\n mbedtls_md_hmac_update(&ctx, (const unsigned char *)signature_origin.c_str(), messageLength);\n mbedtls_md_hmac_finish(&ctx, hmac);\n mbedtls_md_free(&ctx);\n\n // base64 编码\n String signature_sha_base64 = base64::encode(hmac, sizeof(hmac) / sizeof(hmac[0]));\n\n // 替换Date\n Date.replace(\",\", \"%2C\");\n Date.replace(\" \", \"+\");\n Date.replace(\":\", \"%3A\");\n String authorization_origin = \"api_key=\\\"\" + APIKey + \"\\\", algorithm=\\\"hmac-sha256\\\", headers=\\\"host date request-line\\\", signature=\\\"\" + signature_sha_base64 + \"\\\"\";\n String authorization = base64::encode(authorization_origin);\n String url = Spark_url + '?' + \"authorization=\" + authorization + \"&date=\" + Date + \"&host=\" + host;\n Serial.println(url);\n return url;\n}\n\nvoid getTimeFromServer()\n{\n String timeurl = \"https://www.baidu.com\";\n HTTPClient http;\n http.begin(timeurl);\n const char *headerKeys[] = {\"Date\"};\n http.collectHeaders(headerKeys, sizeof(headerKeys) / sizeof(headerKeys[0]));\n int httpCode = http.GET();\n Date = http.header(\"Date\");\n Serial.println(Date);\n http.end();\n // delay(50); // 可以根据实际情况调整延时时间\n}\n\nvoid setup()\n{\n // String Date = \"Fri, 22 Mar 2024 03:35:56 GMT\";\n Serial.begin(115200);\n u8g2.begin();\n u8g2.clearBuffer(); // 清空缓存\n u8g2.setFont(u8g2_font_HelvetiPixel_tr); // 选择字体\n u8g2.drawStr(0,10,\"ChatBot Init\"); // 写文字\n u8g2.sendBuffer();\n // pinMode(ADC,ANALOG);\n pinMode(key, INPUT_PULLUP);\n pinMode(34, INPUT_PULLUP);\n pinMode(35, INPUT_PULLUP);\n //pinMode(led1, OUTPUT);\n //pinMode(led2, OUTPUT);\n //pinMode(led3, OUTPUT);\n audio1.init();\n\n int numNetworks = sizeof(wifiData) / sizeof(wifiData[0]);\n wifiConnect(wifiData, numNetworks);\n\n getTimeFromServer();\n\n audio2.setPinout(I2S_BCLK, I2S_LRC, I2S_DOUT);\n audio2.setVolume(50);\n audio2.connecttospeech(\"Hello! I'm Iris,your personal AI assistant\", \"en\");\n\n // String Date = \"Fri, 22 Mar 2024 03:35:56 GMT\";\n url = getUrl(\"ws://spark-api.xf-yun.com/v3.5/chat\", \"spark-api.xf-yun.com\", \"/v3.5/chat\", Date);\n url1 = getUrl(\"ws://ws-api.xfyun.cn/v2/iat\", \"ws-api.xfyun.cn\", \"/v2/iat\", Date);\n urlTime = millis();\n \n\n ///////////////////////////////////\n}\nint flagfirsttalking = 0;//全局变量,记录是否在说话\nvoid loop()\n{\n \n webSocketClient.poll();\n webSocketClient1.poll();\n // delay(10);\n if (startPlay)\n {\n voicePlay(); //播放声音\n }\n\n audio2.loop();\n \n if (audio2.isplaying == 1)\n {\n if(flagfirsttalking == 0){\n u8g2.clearBuffer(); // 清空缓存\n u8g2.drawStr(0,10,\"Talking...\"); // 写文字\n u8g2.sendBuffer();\n flagfirsttalking = 1;\n }\n }\n else\n {\n if(flagfirsttalking == 1){\n u8g2.clearBuffer(); // 清空缓存\n u8g2.setFont(u8g2_font_helvB24_tr);\n u8g2.drawStr(0,30,\"^^\"); // 写文字\n u8g2.sendBuffer();\n u8g2.setFont(u8g2_font_HelvetiPixel_tr);\n flagfirsttalking = 0;\n }\n \n //digitalWrite(led3, LOW);\n if ((urlTime + 240000 < millis()) && (audio2.isplaying == 0)) //鉴权\n {\n urlTime = millis();\n getTimeFromServer();\n url = getUrl(\"ws://spark-api.xf-yun.com/v3.5/chat\", \"spark-api.xf-yun.com\", \"/v3.5/chat\", Date);\n url1 = getUrl(\"ws://ws-api.xfyun.cn/v2/iat\", \"ws-api.xfyun.cn\", \"/v2/iat\", Date);\n }\n }\n\n if (digitalRead(key) == 0)\n {\n audio2.isplaying = 0;\n startPlay = false;\n isReady = false;\n Answer = \"\";\n Serial.printf(\"Start recognition\\r\\n\\r\\n\");\n u8g2.clearBuffer(); // 清空缓存\n u8g2.drawStr(0,10,\"Listening...\"); // 写文字\n u8g2.sendBuffer();\n\n adc_start_flag = 1;\n // Serial.println(esp_get_free_heap_size());\n\n if (urlTime + 240000 < millis()) // 超过4分钟,重新做一次鉴权\n {\n urlTime = millis();\n getTimeFromServer();\n url = getUrl(\"ws://spark-api.xf-yun.com/v3.5/chat\", \"spark-api.xf-yun.com\", \"/v3.5/chat\", Date);\n url1 = getUrl(\"ws://ws-api.xfyun.cn/v2/iat\", \"ws-api.xfyun.cn\", \"/v2/iat\", Date);\n }\n askquestion = \"\";\n ConnServer1();\n adc_complete_flag = 0;\n }\n}\n\nvoid getText(String role, String content)\n{\n checkLen(text);\n DynamicJsonDocument jsoncon(1024);\n jsoncon[\"role\"] = role;\n jsoncon[\"content\"] = content;\n text.add(jsoncon);\n jsoncon.clear();\n String serialized;\n serializeJson(text, serialized);\n Serial.print(\"text: \");\n Serial.println(serialized);\n // serializeJsonPretty(text, Serial);\n}\n\nint getLength(JsonArray textArray)\n{\n int length = 0;\n for (JsonObject content : textArray)\n {\n const char *temp = content[\"content\"];\n int leng = strlen(temp);\n length += leng;\n }\n return length;\n}\n\nvoid checkLen(JsonArray textArray)\n{\n while (getLength(textArray) > 3000)\n {\n textArray.remove(0);\n }\n // return textArray;\n}\n\nDynamicJsonDocument gen_params(const char *appid, const char *domain)\n{\n DynamicJsonDocument data(2048);\n\n JsonObject header = data.createNestedObject(\"header\");\n header[\"app_id\"] = appid;\n header[\"uid\"] = \"1234\";\n\n JsonObject parameter = data.createNestedObject(\"parameter\");\n JsonObject chat = parameter.createNestedObject(\"chat\");\n chat[\"domain\"] = domain;\n chat[\"temperature\"] = 0.5;\n chat[\"max_tokens\"] = 1024;\n\n JsonObject payload = data.createNestedObject(\"payload\");\n JsonObject message = payload.createNestedObject(\"message\");\n\n JsonArray textArray = message.createNestedArray(\"text\");\n for (const auto &item : text)\n {\n textArray.add(item);\n }\n return data;\n}\n\nfloat calculateRMS(uint8_t *buffer, int bufferSize)\n{\n float sum = 0;\n int16_t sample;\n\n for (int i = 0; i < bufferSize; i += 2)\n {\n\n sample = (buffer[i + 1] << 8) | buffer[i];\n sum += sample * sample;\n }\n\n sum /= (bufferSize / 2);\n\n return sqrt(sum);\n}\n\n```\n\n"}}}}