RP2040で動作するAjaxWebserver

最終更新日:2023/2/3

RP2040でI/O制御するWebserverの構築ソースを移植してみました。普通に動くのですが、クライアントブラズザでラジオボタンによる8者択一という仕組みを組み込もうとしたら、ブラウザ側は択一なのにサーバ側は自分で戻さないといけないことが判りました。これはこれで実際に構築したことで気づいたことであり意味がありました。

/*
 * 2023年2月3日T.Wanibe
 *      過去に作成したMEGA+WIZ5100で動作するAJAXWebserverをPicp+W5500で動作する様に移植作業を実施する
 *      浅井さん案件では8chの切替をブラウザで行うことを期待しています。このデモンストレーション版です
 *      基本的に動作します。
 * 20180914 T.Wanibe
 * オリジナルのコードは反応が良い事を確認した。このコードを元に幾つかレタッチしてゆく事にする
 * まずはHTML部分をページ毎に関数化したい。インデントが深くなりすぎるのを解決したい。
 *- 
 * Ajax Web  Server
 * 
 *  A simple web server that shows the value of the analog input pins.
 *  using an Arduino Wiznet Ethernet shield. 
 *  
 *  Circuit:
 *      Ethernet shield attached to pins 10, 11, 12, 13
 *      Analog inputs attached to pins A0 through A5 (optional)
 *  
 *  created 18 Dec 2009
 *  by David A. Mellis
 *  modified 4 Sep 2010
 *  by Tom Igoe
 * 
 *  - - - - - - - - - - - - - - - - - - - - - - - - 
 *  modified 16 Sep 2011 // Ajax version.
 *  by TETRASTYLE 
 *  for Arduino IDE 0022
 * 
 *  get Digital & Analog port status ( JSON(P) formatted data)
 *   <-- http://192.168.1.177/?=
 * 
 *  return value
 *   --> cb({"D":[D0,D1,D2,D3,D4,D5,D6,D7],"A":[A0,A1,A2,A3,A4,A5]});
 * 
 *  toggle Digital Port (D0 - D7) 
 *   <-- http://192.168.1.177/?btn=0
 *  <-- http://192.168.1.177/?btn=1
 *      ...
 *   <-- http://192.168.1.177/?btn=7
 *-  
 *
 * オリジナルコードのコンパイルを実行したところRAM使用量が2Kを超えた。UNOでは使えない。
 * 最大253952バイトのフラッシュメモリのうち、スケッチが14006バイト(5%)を使っています。
 * 最大8192バイトのRAMのうち、グローバル変数が2265バイト(27%)を使っていて、ローカル変数で5927バイト使うことができます
 * F()でFlashメモリ(プログラム領域)にデータを格納し、読み込む手続きを実施した
 * 最大253952バイトのフラッシュメモリのうち、スケッチが13912バイト(5%)を使っています。
 * 最大8192バイトのRAMのうち、グローバル変数が495バイト(6%)を使っていて、ローカル変数で7697バイト使うことができます。
 * RAMの使用量が500Byteまで落ちており意味がある。実行速度の違いは体感は出来ないと思う。
 * IDE1.8.7に更新
 * 最大253952バイトのフラッシュメモリのうち、スケッチが15020バイト(5%)を使っています。
 * 最大8192バイトのRAMのうち、グローバル変数が500バイト(6%)を使っていて、ローカル変数で7692バイト使うことができます。
 * Pico環境での状況
 * 最大1044480バイトのフラッシュメモリのうち、スケッチが68140バイト(6%)を使っています。
 * 最大262144バイトのRAMのうち、グローバル変数が8064バイト(3%)を使っていて、ローカル変数で254080バイト使うことができます。
 */
#define          TIMER_INTERRUPT_DEBUG          1
#define         _TIMERINTERRUPT_LOGLEVEL_       4
#include        <TimeLib.h>
#include        "RPi_Pico_TimerInterrupt.h"
#include        <SPI.h>
#include        <Ethernet3.h>
#define         HttpPort 80
#define         BAUDRATE                115200
#define         NICReset                18
#define         NICInit                 19
#define         SPI_SCK                 2
#define         SPI_TX                  3
#define         SPI_RX                  4
#define         SPI_CS                  5
#define         TIMER0_INTERVAL_MS      1000
#define         vers                    "Pico_AjaxWebserver_1_01"
uint32_t        delay_ms        =       8000;                                           //8秒 最大8.3秒
boolean         gflag                   =false;
String          parm;
// 1 で消灯 0で点灯
int             dout[8]         =       {1,1,1,1,1,1,1,1};
//物理接続要素
int             dOsPins[]       =       {16,17,20,21,22,26,27,28};
//
byte            mac[]           =       {0x00,0x08,0xDC,0x54,0x4D,0xDD};                //WIZNET
byte            ip[]            =       {192, 168, 0, 208};
byte            subnet[]        =       {255,255,255,0};
byte            gateway[]       =       {192,168,0,1};
byte            fTogle1         =       LOW;
unsigned long   OperatingTime   =       0;                                              //稼働時間 Count2で1秒
long            gPreTime,gNowTime;
//
EthernetServer  webServer(HttpPort);
RPI_PICO_Timer  ITimer0(0);
//--------------------------------------------------------------------------------------------------------
//normal HTTP access
void normalHttpAccess(EthernetClient client){
        sendHttpResponseOk;
//
        client.print(("<html><head><title>"));
        client.print(vers);
        client.print(("</title>\n<META HTTP-EQUIV='Content-Type' CONTENT='text/html;CHARSET=x-sjis'>\n"));
//
        client.print(("<meta name='viewport' content='width=240px' />\n<script type='text/javascript'>\n"));
        client.print(("function createXMLHttpRequest(cbFunc){\n\tvar XObj = new XMLHttpRequest();\n\tif(XObj) XObj.onreadystatechange = cbFunc;return XObj;}\n"));
        client.print(("function setData(val){htObj = createXMLHttpRequest(displayData);\n\tif(htObj){htObj.open('GET','/?btn='+val,true);htObj.send(null);}}\n"));
        client.print(("function getData(){htObj = createXMLHttpRequest(displayData);\n\tif(htObj){htObj.open('GET','/?=',true);htObj.send(null);}}\n"));
        client.print(("function displayData(){ if((htObj.readyState == 4) && (htObj.status == 200)){\n\tdocument.getElementById('result').innerHTML =  htObj.responseText;}}\n"));
        client.print(("function strT(){ getData(); timerID=setTimeout('strT()',  document.getElementById('tf1').value);}\n"));
        client.print(("function clrT(){clearTimeout(timerID);}\n</script>\n"));
        client.print(("<style>\ninput[type=radio] {\ndisplay: none;\n}\n"));
        client.print(("\tinput[type=radio]:checked + label {\n\tbackground: #31A9EE;\n\tcolor: #ffffff;\n}\n"));
        client.print((".label{\n\tdisplay: block;\n"));
        client.print(("\tfloat: left;\n"));
        client.print(("\tmargin: 5px;\n"));
        client.print(("\twidth: 100px;\n"));
        client.print(("\theight: 45px;\n"));
        client.print(("\tpadding-left: 5px;\n"));
        client.print(("\tpadding-right: 5px;\n"));
        client.print(("\tcolor: #b20000;\n"));
        client.print(("\ttext-align: center;\n"));
        client.print(("\tline-height: 45px;\n"));
        client.print(("\tcursor: pointer;\n"));
        client.print(("\tborder: 2px solid #006DD9;\n"));
        client.print(("\tborder-radius: 5px;\n}\n"));
        client.print(("</style>\n</head>\n<body onLoad='getData()'><form action='/' method='GET'><br /><P>\n"));
        client.println(("\t<input id='btn0' type='radio' name='button' value=' A ' onClick='setData(0)'><label for='btn0' class='label'>A</label>"));
        client.println(("\t<input id='btn1' type='radio' name='button' value=' B ' onClick='setData(1)'><label for='btn1' class='label'>B</label>"));
        client.println(("\t<input id='btn2' type='radio' name='button' value=' C ' onClick='setData(2)'><label for='btn2' class='label'>C</label>"));
        client.println(("\t<input id='btn3' type='radio' name='button' value=' D ' onClick='setData(3)'><label for='btn3' class='label'>D</label><br><br></P><P>"));
        client.println(("\t<input id='btn4' type='radio' name='button' value=' E ' onClick='setData(4)'><label for='btn4' class='label'>E</label>"));
        client.println(("\t<input id='btn5' type='radio' name='button' value=' F ' onClick='setData(5)'><label for='btn5' class='label'>F</label>"));
        client.println(("\t<input id='btn6' type='radio' name='button' value=' G ' onClick='setData(6)'><label for='btn6' class='label'>G</label>"));
        client.println(("\t<input id='btn7' type='radio' name='button' value=' H ' onClick='setData(7)'><label for='btn7' class='label'>H</label><br><br></P>"));
        client.println(("</form><br /><div id='result'></div>"));
        client.println(("<br /></body></html>"));
}
//--------------------------------------------------------------------------------------------------------
//using XMLhttpObject access
void XMLhttpObjectAccess(EthernetClient client,String parm){
        
}
//--------------------------------------------------------------------------------------------------------
// 200 OK means the resource was located on the server and the browser (or service consumer) should expect a happy response
void sendHttpResponseOk(EthernetClient client){
        Serial.println(("200 OK"));
        Serial.println();
        // send a standard http response header
        client.println(("HTTP/1.1 200 OK"));
        client.println(("Content-Type: text/html"));
        client.println(("Connnection: close")); // do not reuse connection
        client.println();
}
//--------------------------------------------------------------------------------------------------------
// 404 means it ain't here. quit asking.
void sendHttp404(EthernetClient client){
        Serial.println(("404 Not Found"));
        Serial.println();
        client.println(("HTTP/1.1 404 Not Found"));
        client.println(("Content-Type: text/html"));
        client.println(("Connnection: close")); // do not reuse connection
        client.println();
}
//-------------タイマ割込で呼ばれるルーチン クロック表示 500msec毎
bool    TimerHandler0(struct repeating_timer *t){
        OperatingTime++;
        /*
        currentTime     =       now();
        if(Serial)      Serial.println(currentTime);
        currentTime     %=  86400;
        currentHour     = currentTime / 3600;
        currentMin      = (currentTime % 3600)/60;
        TimeDisp[0]     = currentHour/ 10;
        TimeDisp[1]     = currentHour % 10;
        TimeDisp[2]     = currentMin / 10;
        TimeDisp[3]     = currentMin % 10;
        if(fTogle1)     tm1637.point(POINT_OFF);
        else            tm1637.point(POINT_ON);
        tm1637.display(TimeDisp);
        */
        digitalWrite(LED_BUILTIN,fTogle1);
        fTogle1         = !fTogle1;
        return true;
}
//--------------------------------------------------------------
void setup(){
        Serial.begin(BAUDRATE);
        Serial.println(("Start"));
        for (int i=0;i<8;i++){
                //Picoの GP16..23を使用する
                pinMode(dOsPins[i] ,OUTPUT);
                digitalWrite(dOsPins[i],dout[i]);
        }
        pinMode(LED_BUILTIN, OUTPUT);
        SPI.setSCK(SPI_SCK);
        SPI.setRX(SPI_RX);
        SPI.setTX(SPI_TX);
        SPI.setCS(SPI_CS);
        SPI.begin(); 
        //Ethernet3で使用可能なAPI
        pinMode(SPI_CS, OUTPUT);                                                //NIC_CS出力設定
        Ethernet.setCsPin(SPI_CS);                                              //NIC_CSアサイン
        Ethernet.setRstPin(NICReset);                                           //NIC_RSTアサイン
        //NICリセット処理
        pinMode(NICReset, OUTPUT);
        digitalWrite(NICReset, LOW);
        delay(10);                                                              //10msecパルス幅で初期化
        digitalWrite(NICReset, HIGH);
        // start the Ethernet connection and the server:
        Ethernet.begin(mac, ip);
        Serial.print(("Server is at ")); Serial.println(Ethernet.localIP()); 
        webServer.begin();
        //マイクロ秒単位の間隔
        if (ITimer0.attachInterruptInterval(TIMER0_INTERVAL_MS * 500, TimerHandler0)){
                if(Serial){
                        Serial.print(("Starting ITimer0 OK, millis() = ")); Serial.println(millis());
                }
        }else{
                if(Serial){
                        Serial.println(("Can't set ITimer0. Select another freq. or timer"));
                }
        }
        watchdog_reboot(0,0,delay_ms);
        gPreTime        =       millis();
        if(Serial)      Serial.println(("EndSetup"));
}
//--------------------------------------------------------------
void loop(){
        //1秒のループ WDT8秒
        if (watchdog_caused_reboot()){
                if(Serial)    Serial.print("Rebooted by Watchdog!\n");
        }else{
                if(Serial)    Serial.print("Clean boot\n");
        }
        watchdog_update();
        // listen for incoming clients
        EthernetClient client = webServer.available();
        if (client) {
                // an http request ends with a blank line
                boolean currentLineIsBlank = true;
    
                while (client.connected()) {
                        if (client.available()) {
                                char c = client.read();
                                // serch parameter from "HTTP GET"
                                if(gflag){
                                        if(c != ' '){
                                                parm += c;
                                        }else{
                                                gflag = false;
                                        }
                                }
                                if(c == '?' && parm == ""){
                                        gflag = true;
                                }
                                //Serial.print(c);
                                // 改行コードか空行を受けたら返信
                                if (c == '\n' && currentLineIsBlank) {
                                        // 標準のhttp応答ヘッダーを送信する
                                        if(parm == ""){                                 // normal HTTP access
                                                normalHttpAccess(client);
                                        }else{                                          // using XMLhttpObject access
  
                                                int check = parm.indexOf('=');
                                                if(check != -1){
                                                        //Set Digital Port
                                                        int check2 = parm.indexOf('btn');
                                                        if(check2 != -1){
                                                                int port = (parm.substring(check+1)).toInt();
                                                                dout[port] = !dout[port];
                                                                digitalWrite(dOsPins[port] , dout[port]);
                                                        }
                                                        //Write JSONP Data (Digital & Analog Ports Status, Callback function name is 'cb')
                                                        client.print(("cb({\"D\":["));
                                                        for(int i=0; i<8; i++ ){
                                                                client.print(dout[i]);
                                                                if(i<7){
                                                                        client.print(",");
                                                                }
                                                        }
                                                        client.print("]});");
      
                                                }
                                                parm = "";
                                        }
                                        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;
                                }
                        }
                }
                // give the web browser time to receive the data
                delay(1);
                // close the connection:
                client.stop();
        }
}


戯言(nonsense)に戻る


問い合わせ頁の表示


免責事項

本ソフトウエアは、あなたに対して何も保証しません。本ソフトウエアの関係者(他の利用者も含む)は、あなたに対して一切責任を負いません。
あなたが、本ソフトウエアを利用(コンパイル後の再利用など全てを含む)する場合は、自己責任で行う必要があります。

本ソフトウエアの著作権はToolsBoxに帰属します。
本ソフトウエアをご利用の結果生じた損害について、ToolsBoxは一切責任を負いません。
ToolsBoxはコンテンツとして提供する全ての文章、画像等について、内容の合法性・正確性・安全性等、において最善の注意をし、作成していますが、保証するものではありません。
ToolsBoxはリンクをしている外部サイトについては、何ら保証しません。
ToolsBoxは事前の予告無く、本ソフトウエアの開発・提供を中止する可能性があります。

商標・登録商標

Microsoft、Windows、WindowsNTは米国Microsoft Corporationの米国およびその他の国における登録商標です。
Windows Vista、Windows XPは、米国Microsoft Corporation.の商品名称です。
LabVIEW、National Instruments、NI、ni.comはNational Instrumentsの登録商標です。
I2Cは、NXP Semiconductors社の登録商標です。
その他の企業名ならびに製品名は、それぞれの会社の商標もしくは登録商標です。
すべての商標および登録商標は、それぞれの所有者に帰属します。