goo blog サービス終了のお知らせ 

南無ちゃんのブログ https://namva.net/blog2

上記のアドレスに引っ越しました。

ラズパイpicoWで作るシンプルなWebサーバー例

2025-02-06 13:03:18 | プログラミング
 ラズパイpicoWを使ってリニアアンプ用操作パネルを製作し、WiFi経由でUDP通信により遠隔操作できるようしました。ここまでできると、もう一つ欲が出てきて、Webサーバー機能を付加することを検討しています。
 Web上にある色んな記事を参考にして試行錯誤していましたが、これで行けそうだというシンプルな例を作ることができました。開発環境はArduino IDE 1.8です。巻末にソースコードを示しますが、既にコア0でUDPサーバーが動作しているので、SSIDやIPアドレスの設定、Web.begin処理などは重複するためCore1では省略しています。

 ブラウザでボタンを操作して、ラズパイpicoWのデジタル出力をON/OFFできて、かつ1秒周期でラズパイpicoWの状態を更新します。ラズパイpicoW内蔵のLEDを使用しており、ブラウザからLEDの状態が色で確認でき、ボタンを押せばLEDの状態が反転するというものです。
 最初は、この記事を参考にして実験していました。最後に、<meta http-equiv="refresh" content="1">というオマジナイを加えて1秒毎に更新されるようにしたころ、ボタンを押さないのにトグル動作するようになってしまいました。原因は、ボタンはtype="button"で、aタグが付けてあり、ボタンが押されると/Temperatureにジャンプするという構造にあると考えられます。
 そこで、ボタンのタイプがsubmitの例を探していると、この記事を見つけました。
 この例ではボタンがON用とOFF用に分かれていて、かつLEDの状態が分からないので、格好悪いのでトグル動作するように改良しました。併せて、1秒毎に更新するようにしました。
 最終的なサンプルプログラムのソースコードを以下に示します。
 bool core1_separate_stack = true;というのは、コア1用のスタックを別途8kB設けるというオマジナイです。String page;は、グローバル変数として宣言し、何度もヒープの割り当てを受けないようにし、setup1()の中で、page.reserve(1000);と宣言しています。これらの工夫をする前は、30分位動作させるとスタックしていましたので、効果があったようです。しかし、やはり3時間位動作させるとハングアップしてしまいました。pingにも応答しなくなりました。原因は今のところ不明です。

#include <WiFi.h>
#include <WebServer.h>
WebServer server(80);
bool core1_separate_stack = true;
String page;

String getPage(){
char msgBuf[16];
int iSS = millis() / 1000;
int iMM = iSS / 60;
int iHH = iMM / 60;

page = "<!DOCTYPE HTML>";
page += "<html>";
page += "<head>";
page += "<meta charset=\"utf-8\">";
page += "<meta http-equiv=\"refresh\" content=\"1\">";
page += "<title>button example</title>";
page += "<style>";
page += ".green{background-color: green; color: white}";
page += ".white{background-color: white; color: black}";
page += "</style>";
page += "</head>";
page += "<body style='background-color:powderblue;'>";
page += "<h2 style='color:red'>button toggle example</h2>";
page += "<br>";
page += "<br>";
page += "<FORM action=\"/\" method=\"post\">";
if(digitalRead(LED_BUILTIN) == HIGH){
page += "<button type=\"submit\" name=\"btnLED\" id=\"btnLED\" value=\"LED_OFF\" class=\"green\">LED</button>";
}else{
page += "<button type=\"submit\" name=\"btnLED\" id=\"btnLED\" value=\"LED_ON\" class=\"white\">LED</button>";
}
page += "</form>";
page += "<br>";
page +="<div><p>Uptime: ";
sprintf(msgBuf, "%02d:%02d:%02d", iHH, iMM%60, iSS%60);
String strMsg = String(msgBuf);
page += strMsg;
page +="</p></div>";
page += "<br>";
page += "</body></html>";
return page;
}

void handleNotFound(){
server.send(200, "text/html", getPage());
}

void handleSubmit(){
if (server.hasArg("btnLED")){
String btnValue = server.arg("btnLED");
Serial.print("received value is: ");
Serial.println(btnValue);
if(btnValue == "LED_ON"){
digitalWrite(LED_BUILTIN, HIGH);
}else{
digitalWrite(LED_BUILTIN, LOW);
}
}
server.send(200, "text/html", getPage());
}

void handleRoot(){
if (server.args()){
handleSubmit();
}else{
server.send(200, "text/html", getPage());
}
}

void setup1(){
delay(1000);
Serial.begin(115200);
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN,LOW);
page.reserve(1000);

Serial.println("WiFi connected.");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
server.begin();
server.on("/", handleRoot);
server.onNotFound(handleNotFound);
server.begin();
delay(500);
digitalWrite(LED_BUILTIN,LOW);
}

void loop1(){
server.handleClient();
delay(100);
}

 WiFiWebServerというライブラリも存在するようなので、それも試してみました。先頭の3行を次のように入れ替えるとコンパイルできて、正常に起動しました。しかし、残念ながら、WebServerの場合と同様に3時間程でハングアップしました。1秒毎にアップデートしているのが原因だと思われます。長時間連続運転するような場合には困りものですが、EMEのように2時間程度の運用なら使い方によっては問題ないかもしれません。所詮趣味なので、これ以上は深入りしないことにします。

#define ARDUINO_RASPBERRY_PI_PICO_W
#include "defines.h"
WiFiWebServer server(80);









リニアアンプ用操作パネルをブラウザで表示する

2025-02-01 09:58:09 | プログラミング
 リニアアンプ用操作パネルをUDPで通信してWiFi経由で遠隔操作できるようになりましたが、もう少し機能を追加することを試みています。ラズパイpicoはデュアルコアですが、現在のアプリはシングルコアでのみ動作していて、処理能力には未だ余裕があります。そこで、ラズパイpicoの空いているコアでWebサーバーを動作させて、ブラウザを使って遠隔操作できるようにするという企てです。このようにすれば、パソコン側の特別なアプリが不要になるので、アプリのインストールが不要になり、LinuxやMacOSなどの他、スマホやタブレットからでもリニアアンプを操作できるようになります。
 現在、調査、研究および実験段階ですが、出来そうだなぁという目途はついています。GUIは次のようなものです。Microsoft Edgeで表示しています。


 ブラウザで表示できるのは、基本的にhtmlで記述されたテキストファイルであり、ソースコードは次のような内容です。Vddの値を変化させるには、ラズパイpicoから送るhtmlファイルの54Vの部分を相当する値の文字列にリプレースしてやれば良いのです。同様に、PWRやOPRボタンおよびアラームボタンの色は、参照しているaタグ内のclassを相当するクラス名に置き換えることで変化させることができます。

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="refresh" content="1">
<title>PA WiFi operation panel</title>
<style>
.container{
max-width: 500px;
margin: auto;
text-align: center;
font-size: 1.2rem;
}


.buttonOFF {
background-color: #fff;
border: solid 2px #2f4f4f;
color: #2f4f4f;
padding: 5px 30px;
text-decoration: none;
font-size: 1em;
}
.buttonOFF:hover {
color: #2f4f4f;
background-color: #b0e0e6;
}


.buttonON_PWR {
background-color: blue;
border: solid 2px #2f4f4f;
color: white;
padding: 5px 30px;
text-decoration: none;
font-size: 1em;
}
.buttonON_PWR:hover {
color: #2f4f4f;
background-color: #b0e0e6;
}


.buttonON_OPR {
background-color: green;
border: solid 2px #2f4f4f;
color: white;
padding: 5px 30px;
text-decoration: none;
font-size: 1em;
}
.buttonON_OPR:hover {
color: #2f4f4f;
background-color: #b0e0e6;
}


.alarm_off {
background-color: #2f4f4f;
color: #fff;
padding: 5px 20px;
text-decoration: none;
font-size: 0.5em;
}
.alarm_on {
background-color: #ff0000;
color: #fff;
padding: 5px 20px;
text-decoration: none;
font-size: 0.5em;
}


</style>
</head>


<body>
<div class="container">
<h3>PA operation panel</h3>
<a href="./pwr" class="buttonON_PWR">PWR</a>
<a href="./opr" class="buttonOFF">OPR</a>
<br><br>
<table border="3" align="center">
<tr>
<th>Vdd</th>
<th>Id</th>
<th>FWD</th>
<th>REF</th>
<th>SWR</th>
</tr>
<tr>
<td>54V</td>
<td>17A</td>
<td>600W</td>
<td>2W</td>
<td>1.15</td>
</tr>
</table>
<br>
<a href="#" class="alarm_off">Temp</a>
<a href="#" class="alarm_on">Over</a>
<a href="#" class="alarm_off">FWD</a>
<a href="#" class="alarm_off">REF</a>


<hr>
<code>uptime 00:12:34</code>
</div>
</body>
</html>



 


WiFi経由でリニアアンプを遠隔操作

2025-01-28 15:38:51 | プログラミング
 ラズパイpicoを使ってリニアアンプの操作パネルを製作していました。そのCPUをラズパイpicoWに交換して、プログラムを追加して、WiFi経由でパソコンから遠隔操作できるようにしました。
 ラズパイpico/picoWを入れるケース等のハードウェアは未完成ですが、ラズパイ側のファームウェアとパソコン側のアプリは、ほぼ完成です。
 リニアアンプが遠隔操作できるようになったので、近い将来には、無線設備一式をアンテナ直下に設けた物置に設置したいと考えています。こうすることで、同軸ケーブルによるロスを小さくすることができるため、EMEのようにVUHFを使用する場合にはメリットは大きいと思うのです。




 パソコンとラズパイpicoWとはUDPを使って通信しており、パソコンから定周期にポーリングしてデータを吸い上げています。UDPはTCPと違ってコネクションレスなので、シリアルポートのように好きな時にメッセージを送ることができて待ち合わせをする必要がなく、遅延が小さいためリアルタイム性に秀でています。パソコン側のアプリはVB.NET で開発しました。
 ここまでが当初目指していた目標でしたので、区切りをつける意味でドキュメントをまとめておきたいと思います。


リニアアンプ操作パネル via WiFi

2025-01-23 17:41:37 | プログラミング
 リニアアンプ側に取り付けるラズパイpicoW搭載の操作パネルのファームウェアがほぼ完成したので、パソコン側で遠隔操作するための操作パネルの設計に取り掛かりました。パソコンで動作するGUIアプリなので、ヒューマンインタフェースから設計します。リニアアンプ側の操作パネルと違って、液晶パネルの表示能力に制約されず大きな画面にすることができます。とは言え、表示する内容はある程度決まっているので、今の処、次のような画面にしました。



 開発言語はVB.NET(Visual Studio 2019 Community Edition)です。VB.NETのツールボックスには、単純なLEDランプのようなものが無いので、小さなPictureBoxを定義して、それを塗りつぶすということで実現しています。当初は、チェックボックスやラジオボタンなどのツールを使っていましたが、使用目的が異なるので、あまりしっくり来ませんでした。PictureBoxを使う方法はまぁまぁな感じです。

Arduino言語でWiFiのプログラミング

2025-01-08 12:37:10 | プログラミング
 Arduino言語はCでもなくC++でもないハイブリッドなので、一般的な教科書の説明が通用しないことがままあります。ネットワークのプログラミングに関しても同様のようです。
 1月6日のブログで、Udpというスケッチ例をモジって動作させた例を紹介していますが、これをベースにリニアアンプ用表示装置に移植しようとしたところ、幾つかの問題があり少々手間取りました。

<<<問題其の一 WiFi.beginのパラメータはchar[]である>>>
 SDカードから読み込んだSSIDとKEYはString型の変数なので、そのままWiFi.beginのパラメータとして使うとエラーになります。これは、次に示すようにString型からchar[]型に変換することで解決しました。
String sSSID = "abcdefg";
String sKEY = "xyz12345";
char charSSID[30];
char charKEY[30];

strcpy(charSSID, sSSID.c_str());
strcpy(charKEY, sKEY.c_str());
WiFi.begin(charSSID, charKEY);

<<<問題其の二 IPアドレスの設定に関して>>>
 先日の例では、IPAddressクラスを使って、固定IPアドレスを次のように定義しました。
 IPAddress myIP(192,168,0,123);
 この宣言では、4つの8ビット変数(uint8_t型)を羅列してIPAddressクラスのインスタンスであるmyIPを初期化しています。IPAddress (uint32_t address)という形で初期化することもできますが、そもそもIPAddressクラスのインスタンスを使う必要があるのかどうかさえ疑問です。要は、WiFi.configが正常に実行できれば良いのです。Arudinoのリファレンスには、WiFi.configのパラメータは (array of 4 bytes)と書いてありIPAddressクラスのインスタンスでなくても良さそうです。
 SDカードから読み込んだIPアドレスは"192.168.0.123"というような文字列(String型)で表現されているので、これを文字列からバイナリに変換する必要があります。このような変換を行うライブラリがあるかどうか調べましたが見つからないので、自作しました。ベタなやり方ですが、次のような関数を作成しました。

uint32_t xchgDnIPA(String sIPA){//exchange dotted notaion IP address to uint32
int pos;
String sDigit1, sDigit2, sDigit3, sDigit4;
String sRest;
long iDigit1, iDigit2, iDigit3, iDigit4;
uint32_t uIP = 0;
pos = sIPA.indexOf(".");
if(pos < 0) return 0;
sDigit1 = sIPA.substring(0, pos);
sRest = sIPA.substring(pos+1);
pos = sRest.indexOf(".");
if(pos < 0) return 0;
sDigit2 = sRest.substring(0, pos);
sRest = sRest.substring(pos+1);
pos = sRest.indexOf(".");
if(pos < 0) return 0;
sDigit3 = sRest.substring(0, pos);
sRest = sRest.substring(pos+1);
sDigit4 = sRest;
iDigit1 = sDigit1.toInt();
iDigit2 = sDigit2.toInt();
iDigit3 = sDigit3.toInt();
iDigit4 = sDigit4.toInt();
//network byte order
uIP = (iDigit4 << 24) + (iDigit3 << 16) + (iDigit2 << 8) + iDigit1;
return uIP;
}

 プログラムのsetup関数の中で次のように記述すると、期待通りに動作しました。
uint32_t ipadr;
String sIPA = "192.168.0.123";
ipadr = xchgDnIPA(sIPA);
WiFi.config(ipadr);