日曜倧工のスマヌトポむントたたは「むンタヌネットのこず」

この蚘事では、ホヌムオヌトメヌションずモノのむンタヌネットの分野で゜リュヌションを䜜成するためのコンパクトなプラットフォヌムの実甚的な実装の抂念ず䟋を説明したす。



画像



猫の䞋で興味がある人に聞いおみおください。



導入する代わりに


最近、生呜の自動化などの情報技術の分野に関心が高たっおいるずいう顕著な傟向がありたす。 自動化自䜓は新しい珟象ずはほど遠いものであり、䜕十幎もの間、ほずんどの産業䌁業にずっおそれは気たぐれではなく、必芁であり、それなしでは激しい競争に盎面したビゞネスの存続は考えられたせん。 それでは、なぜ今、モノのむンタヌネット、M2MMachine-to-Machineコミュニケヌション、およびその他の「スマヌト」テクノロゞヌに぀いおこれほどたでに耳にするのでしょうか。 おそらくその理由は、倚くの同様の堎合ず同様に、特定の「クリティカルマス」のむノベヌションが埗られ、䞀般倧衆向けの芁玠ベヌスが利甚可胜になったためです。 むンタヌネットの開発ずむンタヌネット技術の利甚が、これたで䞖界を倉えおいる情報プロゞェクトの波を匕き起こしたように、今、プログラミング、マむクロ゚レクトロニクス、むンタヌネットなどの倚くの「レンガ」が䜜成されおいるのを目撃しおいたす興味深い家庭甚゜リュヌション。 それらのすべおが「離陞」するわけではなく、これは絶察に正垞ですが、それらの倚くは本圓に玠晎らしいものの基瀎たたはむンスピレヌションになりたす。



個人的に、私はここ数幎これに非垞に積極的に興味を持っおいお、MajorDoMo Smart Homeオヌプンプロゞェクトに぀いお聞いた人もいるかもしれたせん。 しかし、今は圌のこずではなく、いく぀かの䞊行プロゞェクト、別の実隓、もしあなたが奜きなら、それは少し前に私を驚かせ、その結果をこの蚘事で共有したす。



スマヌトホヌムプラットフォヌムのプロゞェクトである「荷物」の䞭では、非垞に柔軟に䜿甚できたすが、倚くの可胜性には適切な機噚が必芁であり、垞に䟿利で実甚的ではないずいう事実を考えたした。 「小さな」自動化の䞀郚のタスクに぀いおは、1぀のマむクロコントロヌラヌで察応できたすが、ここでは既に柔軟性を倱い、ナヌザヌスキルの芁件を高めおいたす。 ある皮の䞭間オプションの必芁性があるこずは私には明らかでした。非垞にコンパクトで゚ネルギヌ効率がよく、同時に構成ず䜿甚に柔軟性がありたす。 このオプション「Smart Point」たたはSmartPointに有効な名前を付けたしょう。 途䞭で、このデバむスで受け取るこずができる可胜性に぀いおの願いの党リストが圢成されたした。



挑戊する


歌詞から緎習たで。 SmartPointデバむスの基本芁件は次のずおりです。





デバむスに察する远加の実甚的な芁望





コントロヌラヌ、ホスト、呚蟺機噚


䜕床も䜕床もこの抂念ず「りィッシュリスト」のかなりのセットを熟考しお、1぀のマむクロコントロヌラヌで管理するこずは䞍可胜であるずいう結論に達したした。 第䞀に、䜎レベルですべおの蚈画を実行できるほどそれらをプログラミングする方法がただよくわからず、第二に、すべおのコントロヌラヌがそのような欲望をするわけではありたせん。 最も抵抗の少ない経路をずるこずが決定されたした-デバむスを2぀の論理郚分に分割したす1぀「コントロヌラヌ」はマむクロコントロヌラヌに基づき、ハヌドりェアずの基本的な察話を担圓し、2぀目「ホスト」は組み蟌みLinuxに基づいお、より倚くの圹割を果たしたす高レベルむンタヌフェヌス、ルヌルのシステム、API。 Arduinoマむクロコントロヌラヌが最初のブロックずしお遞択され掚枬、OpenWRTファヌムりェアを備えたTP-Link WR703Nルヌタヌが2番目のブロックずしお䜿甚されたした泚DLink Dir-320ルヌタヌ䞊の同様のデバむスのペアが正垞に組み立おられたした。 正しい怒りを予想しお、私はあなたがタスクが最初にプロトタむプの抂念の実行可胜性をテストするこずであり、商甚デバむスを蚭蚈しお組み立おるこずではないこずを思い出させたいです。 さらに、これらのコンポヌネントを䜿甚するず、デバむスの繰り返しが容易になりたす-長いラむブオヌプン゜ヌス Arduinoを䜿甚するず、無数のセンサヌずアクチュ゚ヌタヌをデバむスに接続する経隓を適甚できたす。



TP-Link WR703Nルヌタヌ



画像



マむクロコントロヌラヌArduino Nano



画像



次の芁玠が呚蟺機噚の初期セットずしお遞択されたした。





ご存知のように、呚蟺機噚のセットは異なる堎合がありたすが、この䟋では、䞊蚘の「実甚的なナヌティリティ」の原理に基づいおこれを取り䞊げたした。 したがっお、私たちのデバむスは、ボタンの抌䞋、動き、枩床倉化に応答し、倖郚センサヌからデヌタを受信しこの堎合、 ハブで前述したプロトコルを䜿甚したした 、Nooliteシステムの電源モゞュヌルを制埡できたす制埡モゞュヌルに぀いおは別の話がありたすたた、写真はモゞュヌルの商甚コピヌではなく、メヌカヌからの最も初期のプロトタむプの1぀であり、テストのために私のずころに来たした。



実装ドラフトず初期芁件を組み合わせるず、デバむスの次のブロック図が埗られたす。



画像



スキヌムの説明





マむクロコントロヌラヌの準備


マむクロコントロヌラヌには2぀の䞻なタスクがありたす。1぀目は、倖郚デバむスからコン゜ヌルにむベントを出力するこず、2぀目は、接続された呚蟺機噚に送信するコマンドをコン゜ヌルから受信するこずです。



以䞋は、䞊蚘の呚蟺機噚の詳现を考慮したスケッチのテキストです。 この堎合、ボタンはPIN4に、モヌションセンサヌはPIN3に、枩床センサヌはPIN9に、ラゞオはPIN8に、NooliteモゞュヌルはPIN 10、11に接続されたす。



コントロヌラヌのスケッチ
#include <OneWire.h> #include <DallasTemperature.h> #include <VirtualWire.h> #include <EasyTransferVirtualWire.h> #include <EEPROM.h> //Needed to access the eeprom read write functions #include <SoftwareSerial.h> #define PIN_LED (13) // INDICATOR #define PIN_PIR (3) // BUTTON #define PIN_BUTTON (4) // BUTTON #define PIN_LED_R (6) // INDICATOR RED #define PIN_LED_G (5) // INDICATOR GREEN #define PIN_LED_B (7) // INDICATOR BLUE #define PIN_RF_RECEIVE (8) // EASYRF RECEIVER #define PIN_TEMP (9) // TEMPERATURE SENSOR #define PIN_NOO_RX (10) // RX PIN (connect to TX on noolite controller) #define PIN_NOO_TX (11) // TX PIN (connect to RX on noolite controller) #define TEMP_ACC (0.3) // temperature accuracy #define PERIOD_READ_TEMP (20) // seconds #define PERIOD_SEND_TEMP (600) // seconds (10 minutes) #define PERIOD_SEND_UPTIME (300) // seconds (5 minutes) #define NOO_BUF_LEN (12) unsigned int unique_device_id = 0; long int uptime = 0; long int old_uptime = 0; float sent_temperature=0; int sent_pir=0; int sent_button=0; int sent_button_longlick=0; long int timeCheckedTemp=0; long int timeSentTemp=0; long int timeSentUptime=0; long int timeButtonPressed=0; String inData; //create objects SoftwareSerial mySerial(PIN_NOO_RX, PIN_NOO_TX); // RX, TX OneWire oneWire(PIN_TEMP); DallasTemperature sensors(&oneWire); EasyTransferVirtualWire ET; unsigned int last_packet_id = 0; struct SEND_DATA_STRUCTURE{ //put your variable definitions here for the data you want to send //THIS MUST BE EXACTLY THE SAME ON THE OTHER ARDUINO //Struct can'e be bigger then 26 bytes for VirtualWire version unsigned int device_id; unsigned int destination_id; unsigned int packet_id; byte command; int data; }; //give a name to the group of data SEND_DATA_STRUCTURE mydata; //This function will write a 2 byte integer to the eeprom at the specified address and address + 1 void EEPROMWriteInt(int p_address, unsigned int p_value) { byte lowByte = ((p_value >> 0) & 0xFF); byte highByte = ((p_value >> 8) & 0xFF); EEPROM.write(p_address, lowByte); EEPROM.write(p_address + 1, highByte); } //This function will read a 2 byte integer from the eeprom at the specified address and address + 1 unsigned int EEPROMReadInt(int p_address) { byte lowByte = EEPROM.read(p_address); byte highByte = EEPROM.read(p_address + 1); return ((lowByte << 0) & 0xFF) + ((highByte << 8) & 0xFF00); } void nooSend(byte channel, byte buf[NOO_BUF_LEN]) { buf[0]=85; buf[1]=B01010000; // buf[4]=0; buf[5]=channel; buf[9]=0; int checkSum; for(byte i=0;i<(NOO_BUF_LEN-2);i++) { checkSum+=buf[i]; } buf[10]=lowByte(checkSum); buf[11]=170; Serial.print("Sending: "); for(byte i=0;i<(NOO_BUF_LEN);i++) { Serial.print(buf[i]); if (i!=(NOO_BUF_LEN-1)) { Serial.print('-'); } } Serial.println(""); for(byte i=0;i<(NOO_BUF_LEN);i++) { mySerial.write(buf[i]); } } void noolitePair(byte channel) { byte buf[NOO_BUF_LEN]; for(byte i=0;i<(NOO_BUF_LEN);i++) { buf[i]=0; } buf[2]=15; buf[3]=0; nooSend(channel,buf); } void nooliteUnPair(byte channel) { byte buf[NOO_BUF_LEN]; for(byte i=0;i<(NOO_BUF_LEN);i++) { buf[i]=0; } buf[2]=9; buf[3]=0; nooSend(channel,buf); } void nooliteTurnOn(byte channel) { byte buf[NOO_BUF_LEN]; for(byte i=0;i<(NOO_BUF_LEN);i++) { buf[i]=0; } buf[2]=2; buf[3]=0; nooSend(channel,buf); } void nooliteTurnOff(byte channel) { byte buf[NOO_BUF_LEN]; for(byte i=0;i<(NOO_BUF_LEN);i++) { buf[i]=0; } buf[2]=0; buf[3]=0; nooSend(channel,buf); } void nooliteSwitch(byte channel) { byte buf[NOO_BUF_LEN]; for(byte i=0;i<(NOO_BUF_LEN);i++) { buf[i]=0; } buf[2]=4; buf[3]=0; nooSend(channel,buf); } void nooliteLevel(byte channel,byte level) { byte buf[NOO_BUF_LEN]; for(byte i=0;i<(NOO_BUF_LEN);i++) { buf[i]=0; } buf[2]=6; buf[3]=1; buf[6]=level; nooSend(channel,buf); } void blinking(int count) { for(int i=0;i<count;i++) { digitalWrite(PIN_LED, HIGH); delay(200); digitalWrite(PIN_LED, LOW); delay(200); } } void setColor(int r,int g, int b) { digitalWrite(PIN_LED_R, r); digitalWrite(PIN_LED_G, g); digitalWrite(PIN_LED_B, b); } void setup() { randomSeed(analogRead(0)); pinMode(PIN_LED, OUTPUT); pinMode(PIN_LED_R, OUTPUT); pinMode(PIN_LED_G, OUTPUT); pinMode(PIN_LED_B, OUTPUT); pinMode(PIN_PIR, INPUT); pinMode(PIN_BUTTON, INPUT); Serial.begin(9600); // Debugging only ET.begin(details(mydata)); // Initialise the IO and ISR vw_set_rx_pin(PIN_RF_RECEIVE); vw_setup(2000); // Bits per sec vw_rx_start(); // Start the receiver PLL running // Device ID Serial.print("Getting Device ID... "); unique_device_id=EEPROMReadInt(0); if (unique_device_id<10000 || unique_device_id>60000 || unique_device_id==26807) { Serial.print("N/A, updating... "); unique_device_id=random(10000, 60000); EEPROMWriteInt(0, unique_device_id); } Serial.println(unique_device_id); pinMode(PIN_NOO_RX, INPUT); pinMode(PIN_NOO_TX, OUTPUT); mySerial.begin(9600); } void loop() { uptime=round(millis()/1000); if (uptime!=old_uptime) { Serial.print("Up: "); Serial.println(uptime); old_uptime=uptime; if (((uptime-timeSentUptime)>PERIOD_SEND_UPTIME) || (timeSentUptime>uptime)) { timeSentUptime=uptime; Serial.print("P:"); Serial.print(random(65535)); Serial.print(";F:"); Serial.print("0"); Serial.print(";T:0;C:"); Serial.print("24"); Serial.print(";D:"); Serial.print(uptime); Serial.println(";"); } } int current_pir=digitalRead(PIN_PIR); if (current_pir!=sent_pir) { Serial.print(millis()/1000); Serial.print(" Motion sensor: "); Serial.println(current_pir); Serial.print("P:"); Serial.print(random(65535)); Serial.print(";F:"); Serial.print("0"); Serial.print(";T:0;C:"); Serial.print("12"); Serial.print(";D:"); Serial.print("1"); Serial.println(";"); sent_pir=(int)current_pir; } int current_button=digitalRead(PIN_BUTTON); if (current_button!=sent_button) { delay(50); int confirm_current_button=digitalRead(PIN_BUTTON); if (confirm_current_button==current_button) { if (current_button==1) { timeButtonPressed=millis(); sent_button_longlick=0; } if (current_button==0) { if (sent_button_longlick!=1) { Serial.print(millis()/1000); Serial.print(" Button press: "); Serial.println(current_button); Serial.print("P:"); Serial.print(random(65535)); Serial.print(";F:"); Serial.print("0"); Serial.print(";T:0;C:"); Serial.print("23"); Serial.print(";D:"); Serial.print("3"); Serial.println(";"); } } sent_button=(int)current_button; } } else { if (current_button==1) { int passed=millis()-timeButtonPressed; if ((passed>3000) && (sent_button_longlick!=1)) { sent_button_longlick=1; Serial.print(millis()/1000); Serial.print(" Button long press: "); Serial.println(current_button); Serial.print("P:"); Serial.print(random(65535)); Serial.print(";F:"); Serial.print("0"); Serial.print(";T:0;C:"); Serial.print("23"); Serial.print(";D:"); Serial.print("4"); Serial.println(";"); } } else { sent_button_longlick=0; } } if (((uptime-timeCheckedTemp)>PERIOD_READ_TEMP) || (timeCheckedTemp>uptime)) { // TEMP SENSOR 1 float current_temp=0; sensors.requestTemperatures(); current_temp=sensors.getTempCByIndex(0); if (current_temp>-100 && current_temp<50) { timeCheckedTemp=uptime; Serial.print("Temp sensor: "); Serial.println(current_temp); float diff=(float)sent_temperature-(float)current_temp; if ((abs(diff)>=TEMP_ACC) || ((uptime-timeSentTemp)>PERIOD_SEND_TEMP)) { // timeSentTemp=uptime; sent_temperature=(float)current_temp; Serial.print("P:"); Serial.print(random(65535)); Serial.print(";F:"); Serial.print("0"); Serial.print(";T:0;C:"); Serial.print("10"); Serial.print(";D:"); Serial.print((int)(current_temp*100)); Serial.println(";"); } } else { //Serial.print("Incorrect T: "); //Serial.println(current_temp); } } if (Serial.available()) { char c=Serial.read(); if (c == '\n' || c == ';') { Serial.println(inData); int commandProcessed=0; if (inData.equals("blink")) { Serial.println("BLINKING!"); blinking(3); commandProcessed=1; } if (inData.startsWith("pair")) { commandProcessed=1; inData.replace("pair",""); noolitePair(inData.toInt()); } if (inData.startsWith("on")) { commandProcessed=1; inData.replace("on",""); nooliteTurnOn(inData.toInt()); } if (inData.startsWith("off")) { commandProcessed=1; inData.replace("off",""); nooliteTurnOff(inData.toInt()); } if (inData.startsWith("switch")) { commandProcessed=1; inData.replace("switch",""); nooliteSwitch(inData.toInt()); } if (inData.startsWith("level")) { commandProcessed=1; inData.replace("level",""); int splitPosition; splitPosition=inData.indexOf('-'); if(splitPosition != -1) { String paramString=inData.substring(0,splitPosition); int channel=paramString.toInt(); inData=inData.substring(splitPosition+1,inData.length()); nooliteLevel(channel,inData.toInt()); } } if (inData.startsWith("unpair")) { commandProcessed=1; inData.replace("unpair",""); nooliteUnPair(inData.toInt()); } if (inData.startsWith("color-")) { commandProcessed=1; inData.replace("color-",""); if (inData.equalsIgnoreCase("r")) { setColor(255,0,0); } if (inData.equalsIgnoreCase("g")) { setColor(0,255,0); } if (inData.equalsIgnoreCase("b")) { setColor(0,0,255); } if (inData.equalsIgnoreCase("w")) { setColor(255,255,255); } if (inData.equalsIgnoreCase("off")) { setColor(0,0,0); } } if (commandProcessed==0) { Serial.print("Unknown command: "); Serial.println(inData); } inData=""; Serial.flush(); } else { inData += (c); } } if(ET.receiveData()) { digitalWrite(PIN_LED, HIGH); if (last_packet_id!=(int)mydata.packet_id) { Serial.print("P:"); Serial.print(mydata.packet_id); Serial.print(";F:"); Serial.print(mydata.device_id); Serial.print(";T:"); Serial.print(mydata.destination_id); Serial.print(";C:"); Serial.print(mydata.command); Serial.print(";D:"); Serial.print(mydata.data); Serial.println(";"); last_packet_id=(int)mydata.packet_id; } digitalWrite(PIN_LED, LOW); } if (mySerial.available()) Serial.write(mySerial.read()); }
      
      









呚蟺機噚を䜿甚したコントロヌラヌの動䜜は、ホストモゞュヌルに接続せずに確認できたすが、ファヌムりェアを起動した埌、ポヌトモニタヌを起動しお、コン゜ヌルに衚瀺される内容を確認しおください。 ホストモゞュヌルが受信するのはこのデヌタストリヌムであり、確立されたルヌルに埓っお応答できるのはホストモゞュヌルのみです。



ホストモゞュヌルルヌタヌの準備


OpenWRTシステムを搭茉したルヌタヌのファヌムりェアず、この蚘事のフレヌムワヌク内でのその埌の構成に぀いおは觊れたせん。これに関する情報は、Webで簡単に芋぀けるこずができたす。 その結果、むンタヌネットアクセスを備えたロヌカルWiFiネットワヌクのクラむアントモヌドでルヌタヌを䜿甚し、接続されたマむクロコントロヌラヌをCOMポヌトずしお正しく識別する必芁がありたす。



次のステップは、ルヌタヌをホストモゞュヌルに倉換するこずです。 Bashむンタヌプリタヌを䜿甚しお、ホストモゞュヌルスクリプトを蚘述したした。 それは私にずっお非垞に䟿利で普遍的、぀たり ホストモゞュヌルプラットフォヌムを特定の「鉄」実装にバむンドしたせん。OpenWRTを備えたルヌタヌの代わりに、組み蟌みのLinuxを備えたデバむスを䜿甚できたすマむクロコントロヌラヌを接続するためのBashずドラむバヌのみが必芁な堎合。



ホストモゞュヌルの動䜜アルゎリズムは、次の点で衚すこずができたす。

  1. 初期化-倖郚Webサヌビス䜿甚可胜な堎合からこのデバむスの操䜜ルヌルをダりンロヌドし、マむクロコントロヌラヌずの通信チャネルをセットアップしたす
  2. コントロヌラからデヌタを受信し、ロヌドされたルヌルに埓っお凊理したす




゜ヌスコヌドレベルでは、次のようになりたす。



蚭定ファむル/ect/master/settings.sh
 MASTER_ID="AAAA-BBBB-CCCC-DDDD" ARDUINO_PORT=/dev/ttyACM0 ARDUINO_PORT_SPEED=9600 UPDATES_URL="http://connect.smartliving.ru/rules/" DATA_PATH="/etc/master/data" WEB_PATH="/www" ONLINE_CHECK_HOST="8.8.8.8" LOCAL_BASE_URL="http://connect.dev"
      
      







メむン凊理スクリプトファむル/etc/master/cycle.sh
 #!/bin/bash # settings . /etc/master/settings.sh # STEP 0 # wait to be online COUNTER=0 while [ $COUNTER -lt 5 ]; do ping -c 1 $ONLINE_CHECK_HOST if [[ $? = 0 ]]; then echo Network available. break; else echo Network not available. Waiting... sleep 5 fi let COUNTER=COUNTER+1 done #--------------------------------------------------------------------------- # START if [ ! -d "$DATA_PATH" ]; then mkdir $DATA_PATH chmod 0666 $DATA_PATH fi while : do #--------------------------------------------------------------------------- # Downloading the latest rules from the web echo Getting rules from $UPDATES_URL?id=$MASTER_ID wget -O $DATA_PATH/rules_set.tmp $UPDATES_URL?id=$MASTER_ID if grep -Fq "Rules set" $DATA_PATH/rules_set.tmp then mv $DATA_PATH/rules_set.tmp $DATA_PATH/rules_set.sh else echo Incorrect rules file fi #--------------------------------------------------------------------------- # Reading all data and sending to the web ALL_DATA_FILE=$DATA_PATH/all_data.txt rm -f $ALL_DATA_FILE echo -n id=$MASTER_ID>>$ALL_DATA_FILE echo -n "&data=">>$ALL_DATA_FILE FILES=$DATA_PATH/*.dat for f in $FILES do #echo "Processing $f file..." OLD_DATA=`cat $f` fname=${f##*/} PARAM=${fname/.dat/} echo -n "$PARAM|$OLD_DATA;">>$ALL_DATA_FILE done ALL_DATA=`cat $ALL_DATA_FILE` echo Posting: $UPDATES_URL?$ALL_DATA wget -O $DATA_PATH/data_post.tmp $UPDATES_URL?$ALL_DATA rm -f $DATA_PATH/*.dat #--------------------------------------------------------------------------- # Downloading the latest menu from the web echo Getting menu from $UPDATES_URL/menu2.php?download=1\&id=$MASTER_ID wget -O $DATA_PATH/menu.tmp $UPDATES_URL/menu2.php?download=1\&id=$MASTER_ID if grep -Fq "stylesheet" $DATA_PATH/menu.tmp then mv $DATA_PATH/menu.tmp $WEB_PATH/menu.html else echo Incorrect menu file fi #--------------------------------------------------------------------------- START_TIME="$(date +%s)" # main cycle stty -F $ARDUINO_PORT ispeed $ARDUINO_PORT_SPEED ospeed $ARDUINO_PORT_SPEED cs8 ignbrk -brkint -imaxbel -opost -onlcr -isig -icanon -iexten -echo -echoe -echok -echoctl -echoke noflsh -ixon -crtscts #--------------------------------------------------------------------------- while read LINE; do echo $LINE PASSED_TIME="$(($(date +%s)-START_TIME))" # Processing incoming URLs from controller REGEX='^GET (.+)$' if [[ $LINE =~ $REGEX ]] then URL=$LOCAL_BASE_URL${BASH_REMATCH[1]} #-URL=$LOCAL_BASE_URL wget -O $DATA_PATH/http.tmp $URL echo Getting URL echo $URL fi PACKET_ID="" DATA_FROM="" DATA_TO="" DATA_COMMAND="" DATA_VALUE="" REGEX='^P:([0-9]+);F:([0-9]+);T:([0-9]+);C:([0-9]+);D:([0-9]+);$' if [[ $LINE =~ $REGEX ]] then PACKET_ID=${BASH_REMATCH[1]} DATA_FROM=${BASH_REMATCH[2]} DATA_TO=${BASH_REMATCH[3]} DATA_COMMAND=${BASH_REMATCH[4]} DATA_VALUE=${BASH_REMATCH[5]} DATA_FILE=$DATA_PATH/$DATA_FROM-$DATA_COMMAND.dat echo -n $DATA_VALUE>$DATA_FILE fi if [ -f $DATA_PATH/incoming_data.txt ]; then echo "New incoming data:"; echo `cat $DATA_PATH/incoming_data.txt` cat $DATA_PATH/incoming_data.txt>$ARDUINO_PORT rm -f $DATA_PATH/incoming_data.txt fi ACTION_RECEIVED="" if [ -f $DATA_PATH/incoming_action.txt ]; then ACTION_RECEIVED=`cat $DATA_PATH/incoming_action.txt` echo "New incoming action: $ACTION_RECEIVED" rm -f $DATA_PATH/incoming_action.txt fi . $DATA_PATH/rules_set.sh if [ -f $DATA_PATH/reboot ]; then echo "REBOOT FLAG" rm -f $DATA_PATH/reboot break; fi done < $ARDUINO_PORT done #--------------------------------------------------------------------------- echo Cycle stopped.
      
      









蚭定では、デバむスに䞀意の識別子MASTER_IDがあるこずがわかりたす。これは、Webサヌビスずのやり取りに䜿甚されたす氞続的な接続は必芁ないこずを思い出したす。



メむンスクリプトの䜜業䞭、ディレクトリ/ etc / master / data /を䜿甚しお、ロヌドされたルヌルコヌド、最新のセンサヌ枬定倀の保存、およびルヌルシステムの䞀郚の構成タむマヌなどの䜜業を行いたす。



このリンクからファむルの完党なセットをダりンロヌドできたす。



ルヌルシステム


ルヌルシステムの抂芁は䞊蚘のずおりですので、ここでもう少し詳しく説明したす。 実際、各ルヌルはbash呜什のセットです。 このセットの最初の郚分であるActivatorを呌び出しお、受信デヌタがこのルヌルに準拠しおいるかどうかを確認し、2番目の郚分Contractorでいく぀かのアクションを盎接実行したす。



ルヌルをアクティブにするための可胜な条件





可胜なアクション



ルヌル゜ヌスコヌドの䟋
 # RULE 2 Forwarder RCSwitch (regex) MATCHED_RULE2='0' REGEX='^RCSwitch:(.+)$' if [[ $LINE =~ $REGEX ]] then MATCHED_RULE2="1" fi # RULE 2 ACTIONS if [[ "$MATCHED_RULE2" == "1" ]] then #Action 2.1 (http) echo "HTTP request: http://192.168.0.17/objects/?script=RCSwitch&rcswitch=${BASH_REMATCH[1]}" wget -O $DATA_PATH/http.tmp http://192.168.0.17/objects/?script=RCSwitch\&rcswitch=${BASH_REMATCH[1]} fi
      
      







デバむスをWebシステムに登録した埌、ナヌザヌの個人アカりントを介しおルヌルが構成されたす珟圚、サヌバヌコンポヌネント党䜓がconnect.smartliving.ruプロゞェクトの䞀郚ずしお実装されおいたす。 この堎合、プログラミングする必芁はありたせん。Webシステム自䜓がナヌザヌ定矩のルヌルをbashコマンドに倉換したす。 ナヌザヌ偎では、構成むンタヌフェむスは次のようになりたす。



画像



むンタヌフェヌスずAPI


原則ずしお、䞊蚘は自埋モゞュヌルを䜜成するのに十分ですが、りィッシュリストは長く、実装ぞの道もありたした。 次のステップは、WebむンタヌフェヌスずAPIを䜜成するこずでした。 この手順は、前の手順ず比范しおそれほど耇雑ではなく、同様の原則に埓っお実装されたした。 ホストデバむスには既にWebサヌバヌが存圚するため、APIを実装するために別のbashスクリプトが䜜成され、/ www / cgi-bin / masterに配眮されたす。



スクリプト゜ヌスコヌド/ www / cgi-bin / master
 #!/bin/bash DATA_PATH="/etc/master/data" echo "Content-type: text/plain" echo "" # Save the old internal field separator. OIFS="$IFS" # Set the field separator to & and parse the QUERY_STRING at the ampersand. IFS="${IFS}&" set $QUERY_STRING Args="$*" IFS="$OIFS" # Next parse the individual "name=value" tokens. ARG_VALUE="" ARG_VAR="" ARG_OP="" ARG_LINE="" for i in $Args ;do # Set the field separator to = IFS="${OIFS}=" set $i IFS="${OIFS}" case $1 in # Don't allow "/" changed to " ". Prevent hacker problems. var) ARG_VAR="`echo -n $2 | sed 's|[\]||g' | sed 's|%20| |g'`" ;; # value) ARG_VALUE=$2 ;; line) ARG_LINE=$2 ;; op) ARG_OP=$2 ;; *) echo "<hr>Warning:"\ "<br>Unrecognized variable \'$1\' passed.<hr>" ;; esac done # Set value #ARG_OP="set" #echo $ARG_OP if [[ "$ARG_OP" == "set" ]] then # echo "Set operation<br>" echo -n "$ARG_VALUE">$DATA_PATH/$ARG_VAR.dat echo "OK" fi if [[ "$ARG_OP" == "get" ]] then # echo "Get operation<br>" cat $DATA_PATH/$ARG_VAR.dat fi if [[ "$ARG_OP" == "send" ]] then # echo "Send<br>" echo -n $ARG_LINE>>$DATA_PATH/incoming_data.txt echo "OK" fi if [[ "$ARG_OP" == "action" ]] then # echo "Action<br>" echo -n $ARG_LINE>>$DATA_PATH/incoming_action.txt echo "OK" fi if [[ "$ARG_OP" == "refresh" ]] then # echo "Send<br>" echo "Web">$DATA_PATH/reboot echo "OK" fi if [[ "$ARG_OP" == "run" ]] then # echo "Run<br>" echo `$ARG_LINE` fi
      
      









このスクリプトは、次のAPIコマンドを提䟛したす。



倉数倀を蚭定する

_/cgi-bin/master?op=set&var=Variable1&value=Value1





Variable1の倀をValue1に蚭定したす



倉数の倀を取埗する

_/cgi-bin/master?op=get&var=Variable1





Variable1の倀を返したす



コントロヌラヌぞのデヌタ送信

_/cgi-bin/master?op=send&line=SomeData





接続されたコントロヌラヌにSomeData文字列を送信したす



アクションのアクティベヌション

_/cgi-bin/master?op=action&line=SomeAction





ルヌルに蚘述されおいるSomeActionアクションを初期化したすタむプ「アクティブアクション」



ルヌルの匷制曎新

_/cgi-bin/master?op=refresh





デバむスを再起動せずに、ルヌルずWebむンタヌフェむスの匷制曎新ダりンロヌドを初期化したす



システムチヌム

_/cgi-bin/master?op=run&line=SomeCommand





システムシェルでSomeCommandの実行を初期化したすたずえば、「reboot」を䜿甚するずデバむスが再起動されたす



APIがWebむンタヌフェヌスになった埌。 圌はルヌルず同じように扱われたした。Webサヌビスで蚭定し、同じ初期化段階でデバむスで曎新したした。 デバむスのコントロヌルメニュヌを䜜成するためのむンタヌフェむスは次のずおりです。



画像



車茪を再発明しないために、軜量のKrakenフロント゚ンドフレヌムワヌクが採甚され、/ www / kraken-masterフォルダヌにドロップされたした。 初期化埌、menu.htmlファむルが/ www /フォルダヌに衚瀺され、それに応じお、構成枈みのWebむンタヌフェヌスにアクセスできたす。 _/menu.html



_/menu.html



このタむプのアドレスは偶然に遞ばれたわけではありたせんが、MajorDroidモバむルアプリケヌションAndroidプラットフォヌム甚ずの互換性のために少し詳しく説明しおいたすが、私はあらゆるものの普遍性ず互換性のためです。



オンラむンワヌク


「ええず、システムが刀明したしたが、それだけではありたせんか」-あなたは尋ねたす。 たあ、ほずんど、ほんの少しだけ残った。 より正確には、ナヌザヌにずっおは「小さな」ものですが、開発者にずっおは倧きな舞台ですよくあるこずです。 すなわち-むンタヌネット経由でデバむスを操䜜したす。 Webむンタヌフェヌスがあり、ルヌタヌのポヌトを転送しお、健康を楜しんでいるように芋えたす。 しかし、これらは私たちの方法ではなく、私たちの方法は他者の生掻を単玔化するこずにありたすそしお私たち自身を耇雑にしたす。 最悪の堎合-ルヌタの蚭定を倉曎しお転送ポヌトを䜜成する方法はありたせん。 たたは、同じネットワヌク䞊で倚くの同様のデバむスを䜿甚し、それぞれが仮に倖郚からアクセスできるようにしたいず考えられおいたす。 解決策は次のずおりです。デバむス自䜓は、デヌタずコマンドを亀換するために倖郚サヌバヌずのチャネルを開始および維持する必芁がありたすが、倖郚サヌバヌは特定のデバむスに指定されたWebむンタヌフェむスを耇補し、このチャネルを介したナヌザヌからのコマンドの転送を線成したした。 チャネルは゜ケット接続であり、䞀方でデバむス䞊で別のbashスクリプトを䜜成し、他方でサヌバヌ䞊で゜ケットサヌバヌを䜜成したす。



デバむスでは、スクリプトは/ etc / master / socket_clientにありたす



スクリプト゜ヌスコヌド/ etc / master / socket_client
 #!/bin/bash # settings . /etc/master/settings.sh # STEP 0 # wait to be online COUNTER=0 while [ $COUNTER -lt 5 ]; do ping -c 1 $ONLINE_CHECK_HOST if [[ $? = 0 ]]; then echo Network available. break; else echo Network not available. Waiting... sleep 5 fi let COUNTER=COUNTER+1 done #--------------------------------------------------------------------------- # START if [ ! -d "$DATA_PATH" ]; then mkdir $DATA_PATH chmod 0666 $DATA_PATH fi while : do TEST_FILE=$DATA_PATH/data_sent.txt touch $TEST_FILE SOCKET_HOST=connect.smartliving.ru SOCKET_PORT=11444 exec 3<>/dev/tcp/$SOCKET_HOST/$SOCKET_PORT NOW=$(date +"%H:%M:%S") echo -n $NOW echo " Sending: Hello!" echo "Hello!">&3 read -t 60 ok <&3 NOW=$(date +"%H:%M:%S") echo -n $NOW echo -n " Received: " echo "$ok"; REGEX='^Please' if [[ ! $ok =~ $REGEX ]] then NOW=$(date +"%H:%M:%S") echo -n $NOW echo " Connection failed!" continue fi NOW=$(date +"%H:%M:%S") echo -n $NOW echo " Sending: auth:$MASTER_ID" echo "auth:$MASTER_ID">&3 read -t 60 ok <&3 NOW=$(date +"%H:%M:%S") echo -n $NOW echo -n " Received: " echo "$ok"; REGEX='^Authorized' if [[ ! $ok =~ $REGEX ]] then NOW=$(date +"%H:%M:%S") echo -n $NOW echo " Authorization failed!" exit 0 fi NOW=$(date +"%H:%M:%S") echo -n $NOW echo " Sending: Hello again!" echo "Hello again!">&3 read -t 60 ok <&3 NOW=$(date +"%H:%M:%S") echo -n $NOW echo -n " Received: " echo "$ok"; while read -t 120 LINE; do NOW=$(date +"%H:%M:%S") echo -n $NOW echo -n " Got line: " echo $LINE # Ping reply REGEX='^PING' if [[ $LINE =~ $REGEX ]] then echo -n $NOW echo " Sending: PONG!" echo PONG!>&3 fi # Run action REGEX='^ACTION:(.+)$' if [[ $LINE =~ $REGEX ]] then DATA_RECEIVED=${BASH_REMATCH[1]} NOW=$(date +"%H:%M:%S") echo -n $NOW echo -n " Action received: " echo $DATA_RECEIVED echo -n $DATA_RECEIVED>>$DATA_PATH/incoming_action.txt fi # Pass data REGEX='^DATA:(.+)$' if [[ $LINE =~ $REGEX ]] then DATA_RECEIVED=${BASH_REMATCH[1]} echo -n $NOW echo -n " Data received: " echo $DATA_RECEIVED echo -n $DATA_RECEIVED>>$DATA_PATH/incoming_data.txt fi # Pass data REGEX='^URL:(.+)$' if [[ $LINE =~ $REGEX ]] then DATA_RECEIVED=${BASH_REMATCH[1]} echo -n $NOW echo -n " URL received: " echo wget -O $DATA_PATH/data_post.tmp http://localhost$DATA_RECEIVED fi # Check files modified FILES=$DATA_PATH/*.dat for f in $FILES do if [ $f -nt $TEST_FILE ]; then echo "Processing $f ..." FNAME=${f##*/} PARAM=${FNAME/.dat/} CONTENT=`cat $f` echo -n $NOW echo " Sending: DATA:$PARAM|$CONTENT;" echo "data:$PARAM|$CONTENT;">&3 fi done touch $TEST_FILE done <&3 done #--------------------------------------------------------------------------- echo Cycle stopped.
      
      









デバむスを操䜜するためのリンクずQRコヌドは、ナヌザヌがアカりントから利甚できたす。 以䞋の1぀のテストケヌス



画像



今埌の課題


説明された構造党䜓は非垞に安定しお動䜜したす。おそらく、発売から数か月が経過し、蚘事を曞くこずに決めた時期であり、デバむスはその䞭に定められた機胜を正しく実行したした。 しかし、圌らが蚀うように、すべおがフリルなしで実装されおいたす。 抂念を怜蚌するにはこれで十分ですが、このたたは同様のプラットフォヌムでデバむスを倧量に導入するには、次の領域で䜜業したす。







おわりに


この蚘事では、すべおの構成の詳现ず自動実行スクリプト蚭定などのいく぀かのこずに぀いおは説明しおいたせん。䞻な機胜ず抂念の本質を䌝えるために、意図的に省略したした。



特に、このデバむスずその䜜成プロセス党䜓は、個々のコンポヌネントずテクノロゞヌの動䜜をテストするための実隓でした。 その過皋でアむデアが生たれ、他のデバむスやシステムに具珟化され、䜕かが倖郚からこのプロゞェクトに移行したため、䞀般的に時間はれロからかけ離れおいたした。 実装の経隓が圹に立おば幞いです。



コンセプトの商甚アプリケヌションのトピックを開発する堎合、あたり䞀般的ではないが、むしろ適甚された実装に぀いお話すこずができたす。 䟋







したがっお、同じベヌスを䜿甚しお、倚くの適甚された「ボックス化」゜リュヌションを䜜成し、同様の「むンタヌネットのモノ」をより高いレベルの情報システムに統合できたす。



PS結果のデバむスの「ラむブ」写真をアップロヌドするかどうか長い間考えおいたしたが、事業党䜓の実隓的な性質に぀いおすでに譊告しおいたので、段ボヌルケヌスたたは必芁に応じおそのレむアりトは非垞に䞀貫しおいたす



画像



PPS私はほずんど忘れおいたした、リストされたコンポヌネントのすべおを備えたこのデバむスのコストは玄60ドルになり、費やされた時間は貎重です。



All Articles