ArduinoをAJAXで制御してみました UNO篇

最終更新2018年9月20日

スケッチは以下の通りです

/*
 * 20180918 T.Wanibe    7
 * Ajax Web  Server のコードをKeyStudio0304
 * Ajexの勉強をしている。なかなかうまくゆかない
 * 専用シールドを設置してデバッグ。
 * 最大32256バイトのフラッシュメモリのうち、スケッチが16756バイト(51%)を使っています。
 * 最大2048バイトのRAMのうち、グローバル変数が735バイト(35%)を使っていて、ローカル変数で1313バイト使うことができます。
 * 最大32256バイトのフラッシュメモリのうち、スケッチが16874バイト(52%)を使っています。
 * 最大2048バイトのRAMのうち、グローバル変数が735バイト(35%)を使っていて、ローカル変数で1313バイト使うことができます。
 * 最大32256バイトのフラッシュメモリのうち、スケッチが17342バイト(53%)を使っています。
 * 最大2048バイトのRAMのうち、グローバル変数が749バイト(36%)を使っていて、ローカル変数で1299バイト使うことができます。
 * 
 */
 
#include <SPI.h>
#include <Ethernet3.h>
#include <Wire.h>
#include <RTClib.h>
#define HttpPort 80
#define LED1    5
#define LED2    6
#define LED3    7
#define BTN1    2
#define BTN2    3

boolean gflag=false;
String parm;
// 0 で消灯 1で点灯
int dout[3]     ={0,0,0};
long int outDateTime;
// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network:
const byte mac[]        =       {0x00,0x50,0xC2,0x97,0x2C,0x3D};
const byte ip[]         =       {192,168,0,198};
const byte subnet[]     =       {255,255,255,0};
const byte gateway[]    =       {192,168,0,1};
// Initialize the Ethernet server library
// with the IP address and port you want to use 
// (port 80 is default for HTTP):
EthernetServer server(HttpPort);
RTC_DS3231              RTC;
DateTime now;

//--------------------------------------------------------------
void setup()
{
        Serial.begin(115200);
        Serial.println(F("Start"));
        pinMode(LED1,OUTPUT);digitalWrite(LED1,dout[0]);
        pinMode(LED2,OUTPUT);digitalWrite(LED2,dout[0]);
        pinMode(LED3,OUTPUT);digitalWrite(LED3,dout[0]);
        pinMode(BTN1,INPUT_PULLUP);
        pinMode(BTN2,INPUT_PULLUP);
  
        // start the Ethernet connection and the server:
        Ethernet.begin(mac, ip);
        Serial.print(F("Server is at ")); Serial.println(Ethernet.localIP()); 
        server.begin();
        Wire.begin();
        
        RTC.begin();
        now = RTC.now();
        outDateTime = (long int)now.year() * 10000 + now.month() * 100 + now.day();
        Serial.print(F("Date: "));Serial.println(outDateTime);
        outDateTime = (long int)now.hour() * 10000 + now.minute() * 100 + now.second();
        Serial.print(F("Time: "));Serial.println(outDateTime);
}
//--------------------------------------------------------------
void loop()
{
        // listen for incoming clients
        EthernetClient client = server.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応答ヘッダーを送信する
                                        Serial.print(F("PreCheck: "));Serial.println(parm);
                                        if(parm == ""){                                 // normal HTTP access
                                                normalHttpAccess(client);
                                        }else{                                          // using XMLhttpObject access
                                                XMLhttpObjectAccess(client,parm);
                                                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();
        }
}
//--------------------------------------------------------------------------------------------------------
// 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(F("200 OK"));
        Serial.println();
  
        // send a standard http response header
        client.println(F("HTTP/1.1 200 OK"));
        client.println(F("Content-Type: text/html"));
        client.println(F("Connnection: close"));                        // do not reuse connection
        client.println();
}
//--------------------------------------------------------------------------------------------------------
// 404 means it ain't here. quit asking.
void sendHttp404(EthernetClient client)
{
        Serial.println(F("404 Not Found"));
        Serial.println();
  
        client.println(F("HTTP/1.1 404 Not Found"));
        client.println(F("Content-Type: text/html"));
        client.println(F("Connnection: close"));                        // do not reuse connection
        client.println();
}
//--------------------------------------------------------------------------------------------------------
//normal HTTP access
void normalHttpAccess(EthernetClient client)
{
        sendHttpResponseOk;
        
        client.println(F("<html lang='ja'>\n\t<head>\n\t\t<title>Arduino Ajax TEST</title>\n"));
        client.println(F("\t\t<meta charset='UTF-8' name='viewport' content='width=240px' />\n"));
        client.println(F("\t\t<script type='text/javascript'>\n"));
        
        client.println(F("\t\tfunction createXMLHttpRequest(cbFunc){\n"));
        client.println(F("\t\t\tvar XObj = new XMLHttpRequest();\n"));
        client.println(F("\t\t\tif(XObj) XObj.onreadystatechange = cbFunc;return XObj;\n\t\t}\n"));
        client.println(F("\t\tfunction setData(val){\n\t\t\thtObj = createXMLHttpRequest(displayData);\n"));
        client.println(F("\t\t\tif(htObj){\n\t\t\t\thtObj.open('GET','/?btn='+val,true);htObj.send(null);\n\t\t\t}\n\t\t}\n"));
        client.println(F("\t\tfunction getData(){\n\t\t\thtObj = createXMLHttpRequest(displayData);\n"));
        client.println(F("\t\t\tif(htObj){\n\t\t\t\thtObj.open('GET','/?=',true);htObj.send(null);\n\t\t\t}\n\t\t}\n"));
        client.println(F("\t\tfunction displayData(){\n\t\t\tif((htObj.readyState == 4) && (htObj.status == 200)){\n"));
        client.println(F("\t\t\tdocument.getElementById('result').innerHTML =  htObj.responseText;\n\t\t\t}\n\t\t}\n"));
        client.println(F("\t\tfunction strT(){\n\t\t\tgetData(); timerID=setTimeout('strT()',document.getElementById('tf1').value);\n")); 
        client.println(F("\t\t\tdocument.getElementById('btn100').value = 'STARTING';\n\t\t}\n"));
        client.println(F("\t\tfunction clrT(){\n\t\t\tclearTimeout(timerID);\n"));
        client.println(F("\t\t\tdocument.getElementById('btn100').value = 'START';\n\t\t}\n"));
        
        client.println(F("\t\t</script>\n\t</head>\n\t<body onLoad='getData()'>\n\t\t<form action='/' method='GET'><br />\n"));
        client.println(F("\t\t<input id='btn5' type='button' value=' D5 ' onClick='setData(0)'>\n"));
        client.println(F("\t\t<input id='btn6' type='button' value=' D6 ' onClick='setData(1)'>\n"));
        client.println(F("\t\t<input id='btn7' type='button' value=' D7 ' onClick='setData(2)'><br /><br />\n"));
        client.println(F("\t\t</form><form>\t\t<input id='btn100' type='button' value=' START ' onClick='strT()'>\n"));
        client.println(F("\t\t<input id='btn200' type='button' value=' STOP ' onClick='clrT()'>\n"));
        client.println(F("\t\t<input id='tf1' type='text' size='6' value='1000'>\n"));
        client.println(F("\t\t</form><br />\n\t\t<div id='result'></div><br />\n"));
        client.println(F("\t</body>\n</html>"));
}
//--------------------------------------------------------------------------------------------------------
//using XMLhttpObject access
void XMLhttpObjectAccess(EthernetClient client,String parm)
{
        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();
                        Serial.print(F("Port="));Serial.println(port);
                        dout[port] = !dout[port];
                        digitalWrite(port+5, dout[port]);
                }
                //Write JSONP Data (Digital & Analog Ports Status, Callback function name is 'cb')
                client.print(F("cb({DO:["));
                for(int i=0; i<3; i++ ){
                        client.print(dout[i]);
                        if(i<2){
                                client.print(",");
                        }
                }
                client.print("],DI:[");
                client.print(digitalRead(2));
                client.print(",");
                client.print(digitalRead(3));
                client.print("],AI:[");
                for(int i=0; i<4; i++ ){
                        client.print(analogRead(i));
                        if(i<3){
                                client.print(",");
                        }
                }
                client.print("],TIME:[");
                now = RTC.now();
                outDateTime = (long int)now.hour() * 10000 + now.minute() * 100 + now.second();
                Serial.print(F("Time: "));Serial.println(outDateTime);
                client.print(outDateTime);
                client.print("]});");
        }
}

上記スケッチにはブラウザに送るHTMLのコードが含まれています。実際にブラウザで読み込んでコードを確認すればいいのですが一応示します。

<html lang='ja'>
	<head>
		<title>Arduino Ajax TEST</title>
		<meta charset='UTF-8' name='viewport' content='width=240px' />
		<script type='text/javascript'>
		function createXMLHttpRequest(cbFunc){
			var XObj = new XMLHttpRequest();
			if(XObj) XObj.onreadystatechange = cbFunc;return XObj;
		}
		function setData(val){
			htObj = createXMLHttpRequest(displayData);
			if(htObj){
				htObj.open('GET','/?btn='+val,true);htObj.send(null);
			}
		}
		function getData(){
			htObj = createXMLHttpRequest(displayData);
			if(htObj){
				htObj.open('GET','/?=',true);htObj.send(null);
			}
		}
		function displayData(){
			if((htObj.readyState == 4) && (htObj.status == 200)){
			document.getElementById('result').innerHTML =  htObj.responseText;
			}
		}
		function strT(){
			getData(); timerID=setTimeout('strT()',document.getElementById('tf1').value);
			document.getElementById('btn100').value = 'STARTING';
		}
		function clrT(){
			clearTimeout(timerID);
			document.getElementById('btn100').value = 'START';
		}
		</script>
	</head>
	<body onLoad='getData()'>
		<form action='/' method='GET'><br />
		<input id='btn5' type='button' value=' D5 ' onClick='setData(0)'>
		<input id='btn6' type='button' value=' D6 ' onClick='setData(1)'>
		<input id='btn7' type='button' value=' D7 ' onClick='setData(2)'><br /><br />
		</form><form>
		<input id='btn100' type='button' value=' START ' onClick='strT()'>
		<input id='btn200' type='button' value=' STOP ' onClick='clrT()'>
		<input id='tf1' type='text' size='6' value='1000'>
		</form><br />
		<div id='result'></div><br />
	</body>
</html>

このスケッチだとSTARTボタンを押す事で1秒毎にEventが起きます。また、D5/6/7のボタンを押すとRequestをサーバに送りLEDの点消灯が出来ます。ネットワーク間のデータ量は殆ど無い事も判ります。

SDカードにアクセスしない事もあり、UNOの実装にはまだ余裕がありそうです。

オリジナルのスケッチの問題点を幾つか手直ししています。


Ajaxを使うとPage更新をせずに表示内容を更新出来るのはとても意味があります。

ただ、実現するには結構敷居が高いです。Ajaxと云う方法論は、必ずしも1本化されているモノではないため、いろいろな実現方法が提供されています。そのため、ちょっとレタッチしようとするにも参考情報が混ざってしまい、ドツボに嵌まります。

少なくともJavaScriptの構文は理解しておく必要がありそうです。ExampleをレタッチしてCH数の変更や表現方法の変更(ボタン色の変更等々)したくともなかなか難しいです。

戯言(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社の登録商標です。
その他の企業名ならびに製品名は、それぞれの会社の商標もしくは登録商標です。
すべての商標および登録商標は、それぞれの所有者に帰属します。