W5100S-POE-EVB-PICO上でSSL通信の暗号化を実現します。例として、W5100Sを使ってChatGPTに直接アクセスする方法を紹介します。
W5100S-POE-EVB-PICO / W5100S-EVB-PICOでSSLを使用して通信を暗号化したいと思います。情報を検索した結果、「SSLclient」というSSLをサポートするオープンソースライブラリを見つけました。W5100S-POE-EVB-PICO/W5100S-EVB-PICOでSSLclientを使用してHTTPS通信を行うプロセスを共有します。
ChatGPTのOpenAI APIとの通信にはSSL暗号化通信が必要なため、以前に開発されたChatGPTを使用したプロジェクトはすべてWizFi360モジュールを使用していました。このモジュールにはSSL通信があり、今回はW5100S-EVB-PICOを使用します。これはW5100SでSSL暗号化通信を使用する初めての試みです。
Step 1. Install SSLclient library on Arduino.
Arduinoに以下のパスに従ってSSLclientライブラリをインストールします:
Tools >> Manage libraries >> Search“SSLclient” >> Install
Github:https://github.com/OPEnSLab-OSU/SSLClient
"SSLClientは、Arduino EthernetClientおよびWiFiClientクラスを含む、Arduino Clientインタフェースを実装する任意のネットワークライブラリにTLS 1.2機能を追加します。SSLClientは、ArduinoインフラストラクチャとシームレスにTLSを統合するためにBearSSLを基礎とするTLSエンジンを使用して作成されました。ArduinoBearSSLとは異なり、SSLClientは完全に自己完結しており、ネットワーク接続以外に追加のハードウェアは必要ありません。
SSLClientは公式にSAMD21、SAM3X、ESP32、TIVA C、STM32F7、およびTeensy >= 3.0をサポートしていますが、少なくとも110kBのフラッシュメモリと7kBのRAMを持つ任意のボードで動作するはずです。SSClientは現在、ESP8266(この問題を参照)やAVRをサポートしていません。これは両プラットフォームのメモリ制約によるものです。
このライブラリの説明にはRaspberry Pi RP2040のサポートが記載されていませんが、少なくとも110kBのフラッシュメモリと7kBのRAMを持つ任意のボードで動作するはずです。
テストの結果、このライブラリはRaspberry Pi RP2040をサポートしていることがわかりましたが、「Ethernet.h」ライブラリにいくつかの修正を加える必要があります。
そして、ライブラリの説明には、シンプルなWEBクライアント「http://www.arduino.cc」 の例があります。
// connect to ardiuino.cc over ssl (port 443 for websites)
client.connect("www.arduino.cc", 443);
// Make a HTTP request
client.println("GET /asciilogo.txt HTTP/1.1");
client.println("User-Agent: AdafruitFeatherM0WiFi");
client.print("Host: ");
client.println(server);
client.println("Connection: close");
client.println();
client.flush();
// read and print the data
...
Step 2. Expand the w5100s RX_BUFFER size.
「Ethernet.h」を修正して、RX_BUFFERのサイズを増やしてください。
"**\Arduino\libraries\Ethernet\src\Ethernet.h"
MAX_SOCK_NUMパラメータを2に変更し、ソケット番号を2に設定すると、対応するソケット受信バッファが増加します。
Step 3. Get trusted anchor from domain.
SSLClient library use BearSSL's minimal x509 verification engine to verify the certificate of an SSL connection. This engine requires the developer create a trust anchor array using values stored in trusted root certificates. In short, these trust anchor arrays allow BearSSL to verify that the server being connected to is who they say they are, and not someone malicious.
Take “api.openai.com” as an example:
コードを生成します。「api.openai.comの信頼できるアンカーを取得します。」
生成された内容を「trusted_anchor.h」ファイルに保存します。
Step 4. Initialize the network and act as an HTTP server to start a ChatGPT session.
// Initialize the Ethernet server library
// with the IP address and port you want to use
// (port 80 is default for HTTP):
EthernetServer server(80);
EthernetClient http_client;
void setup() {
Serial.begin(115200);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}
delay(500);
Serial.println("W5100S_EVB_PICO ChatGPT API SSL Communication Test");
// You can use Ethernet.init(pin) to configure the CS pin
Ethernet.init(17); // WIZnet W5100S-EVB-Pico
if (Ethernet.begin(mac)) {
Serial.print("W5100S_EVB_PICO IP : ");
Serial.println(Ethernet.localIP());
}
delay(5000);
// start the web server on port 80
server.begin();
}
Cloud pixelとCloud printerでほぼ同じHTMLページデザインを使用しましたが、今回は前回のHTMLページにChatGPTセッション履歴を表示するコードを追加しました。
const char html_page_start[] 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: 5\r\n" // refresh the page automatically every n sec
"\r\n"
"<!DOCTYPE HTML>"
"<html>"
"<head>"
"<meta charset='UTF-8'>"
"<title>Cloud Printer: ChatGPT</title>"
"<link rel='icon' href='https://seeklogo.com/images/C/chatgpt-logo-02AFA704B5-seeklogo.com.png' type='image/x-icon'>"
"</head>"
"<body>"
"<p style='text-align:center;'>"
"<img alt='ChatGPT' src='https://seeklogo.com/images/C/chatgpt-logo-02AFA704B5-seeklogo.com.png' height='200' width='200'>"
"<h1 align='center'>W5100s-EVB-PICO SSL TEST</h1>"
"<h1 align='center'>ChatGPT</h1>"
"<div style='width:900px;margin: 0 auto;text-align: center'>"
"<form action='/' method='post'>"
"<input type='text' placeholder='Please enter your question' size='35' name='chatgpttext' required='required'/><br><br>"
"<input type='submit' value='Submit' style='height:30px; width:80px;'/>"
"</form>"
"<div style='text-align: left;'>"
"<h5>"
};
const char html_page_end[] PROGMEM = {
"</h5>\r\n"
"</div>"
"</div>"
"</body>\r\n"
"<html>\r\n"
};
元のHTMLウェブページファイルをhtml_page_startとhtml_page_endの2つの部分に分割し、その中間にChatGPTの会話履歴History_Stringを挿入しました。
History_String += String("<span style=\"color: green;\">You:");
History_String += chatgpt_Q;
History_String += String("</span><br>");
History_String += String("<span style=\"color: blue;\">OpenAI:");
History_String += chatgpt_A;
History_String += String("</span><br>");
ウェブページを5秒ごとに自動更新するように設定し、その後HTMLページ上にChatGPTセッション履歴を表示できます。
case do_webserver_index:
{
http_client = server.available();
if (http_client)
{
// an http request ends with a blank line
boolean currentLineIsBlank = true;
while (http_client.connected())
{
if (http_client.available()) {
char c = http_client.read();
json_String += c;
if (c == '\n' && currentLineIsBlank) {
dataStr = json_String.substring(0, 4);
if(dataStr == "GET ")
{
// Serial.println(html_page_start + History_String + html_page_end);
http_client.print(html_page_start + History_String + html_page_end);
}
else if(dataStr == "POST")
{
json_String = "";
while(http_client.available())
{
json_String += (char)http_client.read();
}
dataStart = json_String.indexOf("chatgpttext=") + strlen("chatgpttext=");
chatgpt_Q = json_String.substring(dataStart, json_String.length());
http_client.print(html_page_start + History_String + html_page_end);
// close the connection:
delay(10);
http_client.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;
}
}
}
}
}
break;
ブラウザがHTMLページをリクエストするとGETリクエストを送信し、HTMLページ上でテキストボックスにChatGPTの質問を入力して「送信」をクリックすると、W5100SはPOSTリクエストを受信します。POSTデータを処理することで、ページ入力内容を取得し「chatgpt_Q」に保存します。
Take ChatGPT as an example to do SSL communication of W5100S-EVB-PICO
ChatGPTのOpenAI APIとの通信にはSSL暗号化通信が必要であるため、以前に開発されたChatGPTを使用したプロジェクトはすべてWizFi360モジュールを使用していました。このモジュールにはSSL通信があり、今回はW5100S-EVB-PICOを使用します。これはW5100SでSSL暗号化通信を使用する初めての試みです。
Chatgpt recorder monitor raspberry pi-pico wizfi360
https://maker.wiznet.io/gavinchang/projects/chatgpt-recorder-monitor-raspberry-pi-pico-wizfi360/
Chatgpt Drecorder Dprin
https://maker.wiznet.io/gavinchang/projects/chatgpt%2Drecorder%2Dprin
まず、ライブラリと「trusted_anchor.h」のインクルージョンとサポートを追加します。
#include <SPI.h>
#include <Ethernet.h>
#include <SSLClient.h>
#include "trust_anchors.h"
SSL用にランダムな電圧源からランダムデータを取得するために、アナログピンを選択します。
// Choose the analog pin to get random data from a randomish voltage source for SSL
const int rand_pin = 28;
"SSLclient"を初期化し、Ethernet_Client、trust_anchors、およびランダムを入力します。
// Init "SSLclient" and Input Ethernet_Client, trust_anchors and the random
EthernetClient Ethernet_client;
SSLClient client(Ethernet_client, TAs, (size_t)TAs_NUM, rand_pin);
client.connect(chatgpt_server,443)を通じてchatgpt_serverとのSSL通信ソケットを確立します。
case send_chatgpt_request:
{
// if you get a connection, report back via serial
if (client.connect(chatgpt_server,443)){
// Make a HTTPS request
client.println(String("POST /v1/chat/completions HTTP/1.1"));
client.println(String("Host: ")+ chatgpt_server);
client.println(String("Content-Type: application/json"));
client.println(String("Content-Length: ")+(95+chatgpt_Q.length()));
client.println(String("Authorization: Bearer ")+ chatgpt_token);
client.println("Connection: close");
client.println();
client.println(String("{\"model\": \"gpt-3.5-turbo-0125\",\"messages\": [{\"role\": \"user\", \"content\": \"")+ chatgpt_Q + String("\"}],\"temperature\": 0}"));
chatgpt_Q.replace("+", " ");
History_String += String("<span style=\"color: green;\">You:");
History_String += chatgpt_Q;
History_String += String("</span><br>");
json_String= "";
delay(300);
currentState = get_chatgpt_answer;
}
else
{
client.stop();
delay(1000);
}
}
break;
OpenAIがAPIの新バージョンを使用しているため、元のAPIプロセスはもはや有効ではありません。そのため、「gpt-3.5-turbo-0125」モデルに基づいてこのコードの部分を書き直しました。
OpenAIのAPIは、私たちのリクエストを受け取った後、私たちの質問に応答します。これは、応答部分の処理コードです。
case get_chatgpt_answer:
{
while (client.available()) {
json_String += (char)client.read();
data_now =1;
}
if(data_now)
{
dataStart = json_String.indexOf("\"content\": \"") + strlen("\"content\": \"");
dataEnd = json_String.indexOf("\"", dataStart);
chatgpt_A = json_String.substring(dataStart, dataEnd);
chatgpt_A.replace("\\n\\n", "<br>");
History_String += String("<span style=\"color: blue;\">OpenAI:");
History_String += chatgpt_A;
History_String += String("</span><br>");
Serial.println(History_String);
json_String = "";
data_now =0;
client.stop();
currentState = do_webserver_index;
}
}
break;
文字列を処理することで、OpenAIのAPIの応答から必要な内容を見つけ出します。これは通常、「content:」の後に含まれ、chatgpt_Aに保存されるとともに、ChatGPTセッション履歴History_Stringにも保存されます。
完了しました!