HOME 教育用公開ウェブサービス ROBINS

ITO Manager 4.0 for Kinoma Create の技術情報

3-3301
KinomaSource
情報処理概論 センサー

新バージョンへの移行のお願い

ITOマネジャー4.0では,複数のアナログ入力チャネルを使うと正しい電圧が測定できないバグがありました. この修正には,根本的な設計変更が必要であったため,ITOマネージャー4.1を新しくリリースしました. 詳細は,こちらITO Manager 4.1 for Kinoma Create の技術情報をご覧ください.

はじめに

ITOマネジャーは,キノマクリエイトを使って電圧入力3チャンネルとデジタル出力2チャンネルによって,インターネット経由でスマホやデータベースから測定結果の閲覧と制御を可能したアプリである. 電圧入力3チャンネルは,電圧および電流,温度の入力インターフェースとして利用できるように設計した.最大入力電圧は,3.3Vであり,温度は,高精度IC温度センサLM61BIZに対応している. 電流測定については,まだ,電流-電圧変換器(I-V Convertor)仕様が決定していない.

開発環境

開発用パソコンにKinoma Studioをダウンロードしてインストールする. 開発用パソコンと同じLANに無線LANのアクセスポイントをブリッジモードで接続する. 無線LANのアクセスポイントとキノマクリエイトを接続する.

仕様

入出力ピンアサイン

入出力ピンのアサインは表1に示すとおりである. 物理的なピン配置については,Marvell社のProgramming with Hardware Pinsのドキュメントを参照してください.

表1 ITO Manager 4.0のピンアサイン
ピン番号 機能 BLL名称 コード内PIN名称 備考
52 アナログ入力 AD1 AD1 電圧測定用
51 アナログ入力 AD2 AD2 電流測定用;要I-V変換器
54 アナログ入力 LM61BIZ temperature1 温度測定用;要LM61BIZ
53 アナログ入力 AD1,AD2,LM61BIZ dummy グラウンドへショート
4 デジタル出力 DIO DIO1
6 デジタル出力 DIO DIO2
35,36,41,42 グラウンド - - -

サーバとのコマンドセット

サーバからキノマクリエイトを制御するとき通信のコマンドセットを表2に示す.デジタル出力のコマンドセットはデラマネジャー互換である.

表2 ITO Manager 4.0のサーバからのコマンドセット
機能 コマンド文字列
on1 DIO1出力ON
off1 DIO1出力OFF
on2 DIO2出力ON
off2 DIO2出力OFF

センターノードの一覧

https://a.yamagata-u.ac.jp/amenity/network/SensorNodeList.aspをブラウザーで閲覧するとセンサーノードのリストが表示される.

ソースコードの解説

以下にコメント付きのソースコード示す. 特にpinのアサインについては,キノマクリエイトのFirmwareを7.1.54のバージョンにアップしたら動作しなくなった. Maravel社が提供している7.1.54から採用されたJS6に対応したサンプルコードを元に開発すると,最初にアプリを起動するときに,Front pins appが立ち上がり,その都度,LCDパネルの[Apply]ボタンをタップする必要があり,無人運転ができない問題があった. この問題を解決するためは,ソースコードの(1)のおまじないをコードに追加した.

本ソースコードは,20160505ITOManager4.0b2-kosodate.zipというファイル名でダウンロードできる.

//ピンアサインを設定するときのオブジェクトPinsの定義↓
let Pins = require("pins");
//bdebug変数はKinoma StudioでデバッグするときのデバッグモードのOn=True, Off=Falseで設定↓
const bdebug = false;

//グロバール変数の定義
var temp=0; //温度
var AD1=0; //アナログ入力ch1の電圧
var AD2=0; //アナログ入力ch2の電圧
var tAD1=0; //アナログ入力ch1の一次保存電圧(積算処理用)
var tAD2=0; //アナログ入力ch2の一次保存電圧(積算処理用)
var ttemp = 0; //温度の一次保存電圧(積算処理用)
var cADC1=0; //アナログ入力ch1の積算回数カウンター
var cADC2=0; //アナログ入力ch2の積算回数カウンター
var ctemp=0; //温度の積算回数カウンター
var bCompletedAD1 = 0; //アナログ入力ch1のAD変換のスタートの完了フラグ
var bCompletedAD2 = 0;//アナログ入力ch2のAD変換のスタートの完了フラグ
var bCompletedTemp = 0; //温度測定のAD変換のスタートの完了フラグ
var nEventID =0; //イベント検出時のIDフラグ(未使用)
var DIO1=0; //DIO1の状態(ON:1 , OFF:0)
var DIO2=0; //DIO2の状態(ON:1 , OFF:0)
var szCommand = ""; //制御コマンド受信変数
//以下,定数の定義↓
const deviceID = K4.deviceID; //キノマクリエイトのデバイスID(例:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)
const protomode = "MieSAN"; //制御モード変数: 子育て見守り MieSAN; 稲の水耕栽培 RiceField; ニッケル水素 NiMH
const nDataUploadInterval=30000; //ms データアップロード間隔 単位: ms
const nGetCommandInterval=1000; //ms コマンド取得間隔 単位: ms
const nDisplayInterval=1000; //ms 画面表示更新間隔 単位:ms
const nAccumAD1=4; //AD 1chの積算回数の定義
const nAccumAD2=4; //AD 2chの積算回数の定義
const nAccumTemp=40; //温度の積算回数の定義
const xAD1Gain = 1.9918; //- 外部回路に増幅器または分圧
const szVer = "4.0b2"; //バージョン定義
const xErrorVoltageMax = 3.1; //V AD変換器のノイズ処理のための上限閾値
const xErrorVoltageMin = 0.2; //V AD変換器のノイズ処理のための下限閾値
const xErrorTempMax = 100;//C 温度測定のノイズ処理のための上限値
const xErrorTempMin = -35;//C 温度測定のノイズ処理のための下限値

log("DeiveID: " +deviceID +"\n"); //deviceIDの動作デバッグ用

//画面の見てくれの作成↓
let whiteSkin = new Skin({ fill:"white" });
let normalText = new Style( { font: "30px", color:"black" } );
let smallText = new Style( { font: "20px", color:"black" } ); 
let label0 = new Label({ left:0, right:0, top:0, bottom:0, string:"ITO Manager Ver. " + szVer, style: normalText });
let labelpin = new Label({ left:0, right:0, top:40, bottom:0, string:"AD1:52, AD2:51, Vout(LM61BIZ):54 dummy: 54 to GND", style: smallText }),
let label1 = new Label({ left:0, right:0, top:64, bottom:0, string:"---", style: smallText });

let mainCon = new Container({ 
    left:0, right:0, top:0, bottom:0, skin: whiteSkin
});
mainCon.add(label0);
mainCon.add(labelpin);
mainCon.add(label1);
application.add(mainCon);
//ここまで画面の見てくれ

//ここからPinの設定
// analogpins 37 -> 52, 38->51, 39->54
//(1)ピンの設定をするときに,Front pins appがを動作すると毎回[Apply]をタッチする必要がるので,下記のおまじない
Pins.invoke("setPinMux", {
    leftVoltage: 3.3, rightVoltage: 3.3,
    leftPins: [3, 3, 3, 3, 0, 0, 0, 0],
    rightPins: [0, 0, 0, 0, 0, 0, 0, 0]
});
//ここまでおまじない.動作的には,pinのモードが変更がなければ,Front pins appが起動しないことがわかった
//ピンのアサイン設定↓
				Pins.configure({
						analogSensor1: {
						require: "AD1",
						pins: {
						dummy:{ pin: 53 },
							AD1: { pin: 52 }
                  			}
					},
					analogSensor2: {
						require: "AD2",
						pins: {
						dummy:{ pin: 53 },
							AD2: { pin: 51 }
                  			}
					},
					analogTemp: {
						require: "LM61BIZ",
						pins: {
							dummy:{ pin: 53 },
							temperature1: { pin: 54 }
                 			}
               		},
              		DIO: {
               			require: "DIO",
                			pins: {
                 				DIO1: { pin: 4 },
                  				DIO2: { pin: 6 }
               			}
            		},
               		ground: {
               			pin: 35, type: "Ground"
               			}
					}
					, function(success) {
                        //pinの設定が終了したら,下記のルーチンを開始
   						 if (success){
                        //成功したら
   						 log("Successed to Pin configure\n");
   						Pins.repeat("/analogTemp/read", 50, value => TemperatureValueChanged(value)); //温度計測スタート,50ms間隔で測定
						Pins.repeat("/analogSensor1/read", 3, value => Analog1ValueChanged(value)); //AD1計測スタート,3ms間隔で測定
						Pins.repeat("/analogSensor2/read", 3, value => Analog2ValueChanged(value));//AD2計測スタート,3ms間隔で測定
						Pins.share("ws", {zeroconf: true, name: "analog-temperature"});
   						 }else{
   						 //pin congiureに失敗したときの処理
   						  log("Failed to configure\n");
   						  }
					}
				);


        application.invoke(new Message("/UploadTimer")); //アップロードのタイマースタート
        application.invoke(new Message("/GetCommandTimer")); //コマンド取得タイマーのスタート
        application.invoke(new Message("/DisplayTimer")); //画面更新タイマーのスタート


//--sub routine --				
function log(messsage) {
if (bdebug==true){
   trace(messsage);
	}
};

function sleep(time) {
		 let d1 = new Date().getTime();
		 let d2 = new Date().getTime();
			 while (d2 < d1 + time) {
  				  d2 = new Date().getTime();
  				 }
 				  return;
};
	
function TemperatureValueChanged(value){
		let xtemp1 = value.temperature1;
		if (xtemp1 < xErrorTempMax && xtemp1 > xErrorTempMin) {
			ttemp = (ttemp * ctemp + xtemp1)/(ctemp+1);
			ctemp=ctemp+1;
		}
		if (ctemp==nAccumTemp){
			temp= ttemp;
			ctemp =0;
			log("temperature:"+temp.toFixed(3) + ' C AD1:'+AD1.toFixed(3)+'V AD2:'+AD2.toFixed(3)+'V\n');
			if ( bCompletedTemp==0){
	 			bCompletedTemp = 1;
	 			application.invoke(new Message("/KinomaDataUpload"));
			 }
		}					
}

function Analog1ValueChanged(value){
	let voltage = value.AD1;
	if (voltage < xErrorVoltageMax && voltage > xErrorVoltageMin) {
		tAD1 = (tAD1 * cADC1 +voltage/xAD1Gain)/(cADC1+1);
		cADC1 = cADC1+1;
	}
	if (cADC1==nAccumAD1){
		AD1= tAD1;
		cADC1 = 0;
		if ( bCompletedAD1==0){
	 		bCompletedAD1 = 1;
	 		application.invoke(new Message("/KinomaDataUpload"));
	 }
	}
//	log("AD1:"+AD1.toFixed(3) + 'V AD2:'+AD2.toFixed(3) +'\n');
}
function Analog2ValueChanged(value){
	let voltage = value.AD2;
	if (voltage < xErrorVoltageMax && voltage > xErrorVoltageMin) {
		tAD2 = (tAD2 * cADC2 +voltage)/(cADC2+1);
		cADC2 = cADC2+1;
	}
	if (cADC2==nAccumAD2){
		AD2= tAD2;
		cADC2 = 0;
		if ( bCompletedAD2==0){
	 		bCompletedAD2 = 1;
	 		application.invoke(new Message("/KinomaDataUpload"));
	 	}
	}
//	log('AD2:'+AD2.toFixed(3) +'\n');
}

//--handler routine--
//アップロードタイマー
Handler.bind("/UploadTimer", {
    onInvoke: function(handler, message){
    handler.invoke(new Message("/KinomaDataUpload"));
     log("Time \n");
   handler.invoke( new Message("/delay?duration=" +nDataUploadInterval ));
    },
    onComplete: function(handler, message){
     log("Time2 \n");   
     //    handler.invoke( new Message("/delay?duration=" +nDataUploadInterval ));
    }
});

Handler.bind("/delay", {
    onInvoke: function(handler, message){
    	let query = parseQuery( message.query );
		let duration = query.duration;
        handler.wait(duration); //will call onComplete after 10 seconds
    },
    onComplete: function(handler, message){
        handler.invoke(new Message("/UploadTimer"));
    }
});
//ここまでアップロードタイマー

//サーバからコマンドを取得するコマンド取得タイマー
Handler.bind("/GetCommandTimer", {
    onInvoke: function(handler, message){
    handler.invoke(new Message("/KinomaGetCommand"));
     log("GetCommandTimer\n");
   handler.invoke( new Message("/delayCommand?duration=" +nGetCommandInterval ));
    },
    onComplete: function(handler, message){
     log("GetCommandTimer2 \n");   
     //    handler.invoke( new Message("/delay?duration=" +nDataUploadInterval ));
    }
});

Handler.bind("/delayCommand", {
    onInvoke: function(handler, message){
    	let query = parseQuery( message.query );
		let duration = query.duration;
        handler.wait(duration); 
    },
    onComplete: function(handler, message){
        handler.invoke(new Message("/GetCommandTimer"));
    }
});
//ここまでコマンド取得タイマー

//画面を更新する更新タイマー
Handler.bind("/DisplayTimer", {
    onInvoke: function(handler, message){
    label1.string = "Temperature:"+temp.toFixed(1) + ' C AD1:'+AD1.toFixed(3)+' V AD2:'+AD2.toFixed(3)+' V';
    handler.invoke( new Message("/delayDisplay?duration=" +nDisplayInterval ));
    log("TImeD\n");
    },
    onComplete: function(handler, message){
     log("TimeD2 \n");   
     //     handler.invoke( new Message("/delayCommand?duration=" +nDisplayInterval ));
    }
});

Handler.bind("/delayDisplay", {
    onInvoke: function(handler, message){
    	let query = parseQuery( message.query );
		let duration = query.duration;
        handler.wait(duration); //will call onComplete after 10 seconds
    },
    onComplete: function(handler, message){
        handler.invoke(new Message("/DisplayTimer"));
    }
});
//ここまで更新タイマー

//アップロードモジュールの処理ルーチン
Handler.bind("/KinomaDataUpload", {
			onInvoke: function(handler, message){
            //以下,測定データのアップロード処理
			if (bCompletedAD1 == 1 && bCompletedAD2 == 1 && bCompletedTemp == 1){
				var uri = "http://a.yamagata-u.ac.jp/amenity/network/M2M/KinomaDataReciever.aspx?DeviceID="+deviceID;
				uri = uri + "&AI1=" + AD1;
				uri = uri + "&AI2=" + AD2;
				uri = uri + "&AI3=" + temp;
				uri = uri + "&pm=" + protomode;
				uri = uri + "&EventID=" + nEventID;
				handler.invoke( new Message( uri ), Message.TEXT );
				log("uri:" + uri +"\n");
				}
			},	
			onComplete: function(handler, message){
			//szCommand = data
				//application.distribute( "onLabelDisplayCom", "Command: " + message.status + "," + szCommand);
			log("Http:" +  message.status +"\n");
			},	
			onError: function(handler, message){
				let result = { success: false, items: [] };
				log("Http error:" +  result +"\n");
				//application.distribute( "onLabelDisplayCom", "Command: " + result);
			}	
	}
);

//コマンド取得のルーチン	
Handler.bind("/KinomaGetCommand", {
			onInvoke: function(handler, message){
				var uri = "http://a.yamagata-u.ac.jp/amenity/network/M2M/KinomaControlRequest.aspx?DeviceID="+deviceID;
				handler.invoke( new Message( uri ), Message.TEXT );
			},	
			onComplete: function(handler, message,data){
			szCommand = data
                //以下得られたコマンドの処理ルーチン
				//command analysis
				if (szCommand.match(/on1/)){
                    //on1のコマンド処理
					application.invoke(new Message("/KinomaDataUpload"));
					DIO1=1;
					application.invoke(new MessageWithObject("pins:/DIO/turnOn1"));
					sleep( 50 );
					application.invoke(new Message("/KinomaDataUpload"));
				   }
				   if(szCommand.match(/off1/)) {
                     //off1のコマンド処理
					application.invoke(new Message("/KinomaDataUpload"));
					DIO1=0;
					application.invoke(new MessageWithObject("pins:/DIO/turnOff1"));
					sleep( 50 );
					application.invoke(new Message("/KinomaDataUpload"));
					}
				   if(szCommand.match(/on2/)){
                    //on2のコマンド処理
					DIO2=1;
					application.invoke(new MessageWithObject("pins:/DIO/turnOn2"));
				   }
				   if(szCommand.match(/off2/)) {
                    //off2のコマンド処理
					DIO2=0;
					application.invoke(new MessageWithObject("pins:/DIO/turnOff2"));
					}	 
				 log("DO1(4): "+DIO1+"\n");
            	 log("DO2(6): "+DIO2+"\n");
			    log("Http-GetCommand: " + message.status + "," + szCommand +"\n");
			},	
			onError: function(handler, message){
				let result = { success: false, items: [] };
				log("Http error:" +  result +"\n");
				//application.distribute( "onLabelDisplayCom", "Command: " + result);
			}	
	}
);

でら キノマクリエイト
デラさんのM2MとIoT
菅野の電卓
菅野の電卓2
菅野のクラウドポテンショスタット
その他のキノマクリエイトのソースコード 旧米沢高等工業学校の設立
リチウム電池とLEDによるイネの室内水耕栽培
伊藤の冬休みの自由研究 紙おむつの吸水性高分子を培地で稲の苗は育つか?

学会発表

エコ研究のすすめ


山形大学 大学院 理工学研究科 C1ラボラトリー
〒992-8510 山形県米沢市城南4丁目3-16 3号館(物質化学工学科棟) 3-3301
准教授 伊藤智博
0238-26-3753
http://c1.yz.yamagata-u.ac.jp/

QRコード
https://edu.yz.yamagata-u.ac.jp/Public/54299/c1/IoT/KinomaCreate/ITOmanager4/Default.asp