ThingJSのサーモスタット(ベータ版)



ほぼ1年前、私自分のペットプロジェクトであるThingJSIoTプラットフォーム発表しました。正直なところ、その記事を公開することで、自分で設定したすべての目標を達成することはできませんでした。しかし、その仕事は報われました。私はなんとか何か他のものを手に入れることができました-有用な批判。



私は過去の経験を考慮しました。理論は実践なしではうまくいきません。今回のプレゼンテーションは、適用されたソリューションに基づいています。誰もが「触って」日常生活で使うことができます。



しかし、最初に、いくつかの一般的な情報。



目次


前書き



ThingJSは、愛好家からプロレベルまでデバイスを作成できるアーキテクチャによって、他のプラットフォームとは一線を画しています。



わずか数分で、ニーズに合った家庭用デバイスを作成できます。これを行うために、アマチュアは既製のアプリケーションテンプレート、他の誰かのコードを再利用する機能、およびMQTTを介したほとんどのIoTサービスとの統合を持っています。



専門家はファームウェアを変更して、信頼できるデバイスを作成することができます。最新のWEBテクノロジーに基づいて構築された高品質のUIを開発します。完成したデバイスを作成します。



しかし、最も重要なのは、これらの非常に異なる「世界」の両方が調和して共存できることです。これは、プラットフォームの独自の機能であるアプリケーションを通じて可能になります。



, . (core) (Resource Interfaces). ( ), “” UI . , , .



. IDE. : Lion, CMake, webpack, npm .. ThingJS. TDD .



“” , . “ ” . “ ”. .



, . .





runtime ESP32. .



. backend frontend — . :



  • ();
  • Frontend ();
  • Backend ();
  • ();
  • ();
  • ();
  • ().


Backend . JavaScript — mJS.



Frontend SPA WEB-. . : UBUS Storage.





UBUS



. JSON-. . . .



, . . frontend, backend, , , .



. frontend WEBSocket.



, frontend , . — . backend.



.



, . , , .



Storage



. . frontend, backend.



. , . , . , .



. , , . .



. , . .



. , . . , .



API



API. , $res $bus.



API . , , (Resource interfaces).



Resource interfaces



, . .. , . . , launcher, vuetifyjs React.



— . . .



. . , FreeRTOS , . SmartLED.



, . , . . , . .





. .



:



  • ;
  • .


. . , GPIO . , .



. .



. , .



, , . , .



. , , GPIO, , UART .. . , TCP .. , , .



, , , ThingJS.



ThingJS



Thermostat .





. , . , , . .





ESP32 ESP8266. , , . 240, , 520, Bluetooth, Wifi. GPIO. : ES, SHA-2, RSA, ECC, RNG.



ESP32-DevKitC.







JavaScript VUE CLI. , frontend .



:



  • — .
  • (hot reload) — . . .
  • dev — dev- NodeJS. , . .


:



git clone --branch beta https://github.com/rpiontik/ThingJS-front
cd ThingJS-front
npm install


, dev :



npm run dev


http://0.0.0.0:8080/. dev-. .





100%, . , . . , /config/dev.env.js IP .



, dev- “” -, , . .. “”, , , thermostat.smt.



dev thermostat . .





, src/applications/thermostat/scripts/thermostat.js. , “debugger”. .



, dev- :



“Start debugger” .





. watch . . .



, :



npm run build


dist/apps/



.



Thermostat



. /src/applications/. “blink”. — thermostat.



. manifest.json. . .





"name": "Thermostat",
"vendor": "rpiontik",
"version": 1,
"subversion": 0,
"patch": 0,
"description": {
    "ru": "",
    "en": "Thermostat"
},


, .



components



frontend .



"components": {
 "thermostat-app": {
   "source": "thermostat.js",
   "intent_filter": [
     {
       "action": "thingjs.intent.action.MAIN",
       "category": "thingjs.intent.category.LAUNCH"
     }
   ]
 }
},


— “thermostat-app”. “thermostat.js”. “intent_filter”. .



“blink.js” -> “thermostat.js” :



import App from './Thermostat.vue';
import Langs from './langs';

$includeLang(Langs);
$exportComponent('thermostat-app', App);


VUE “Thermostat.vue”. . . :



$exportComponent('thermostat-app', App);


? . . , “thermostat.js” VUE “thermostat-app”. . “langs.js”.



frontend “Thermostat.vue”. . .



template
<template>
  <v-flex fill-height style="max-width: 600px">
    <h1>{{ 'TITLE'|lang }}</h1>
    <v-container>
      <v-layout>
        <v-flex xs12 md12>
          {{ 'DESCRIPTION'|lang }}
        </v-flex>
      </v-layout>
    </v-container>
    <v-tabs
        centered
        icons-and-text
    >
      <v-tab href="#tab-1">
        {{ 'CONTROL'|lang }}
        <v-icon>dashboard</v-icon>
      </v-tab>

      <v-tab href="#tab-2">
        {{ 'CLOUD'|lang }}
        <v-icon>cloud</v-icon>
      </v-tab>

      <v-tab-item value="tab-1">
        <v-container>
          <v-layout>
            <v-flex class="current-temp" xs12 md4>
                <span>
                  <template v-if="state.temp !== null">
                    {{ state.temp.toFixed(1) }}°
                  </template>
                  <template v-else>
                    --.--
                  </template>
                </span>
            </v-flex>
            <v-flex xs12 md4 style="text-align: center; padding: 12px; ">
              <template v-if="state.state === 1">
                <v-icon
                    title="Power on"
                    class="indicator"
                >power
                </v-icon>
              </template>
              <template v-else-if="state.state === 0">
                <v-icon
                    title="Power off"
                    class="indicator"
                >power_off
                </v-icon>
              </template>
            </v-flex>
            <v-flex xs12 md4 style="text-align: center; padding: 12px;">
              <template v-if="!!state.connected">
                <v-icon
                    title="Connected"
                    class="indicator"
                >cloud
                </v-icon>
              </template>
              <template v-else>
                <v-icon
                    title="Disconnected"
                    class="indicator"
                >cloud_off
                </v-icon>
              </template>
            </v-flex>
          </v-layout>
        </v-container>
        <v-container grid-list-xl>
          <v-layout>
            <v-flex xs12 md3>
              <v-select
                  label="Mode"
                  :items="modes"
                  v-model="state.mode"
                  @change="onChangeMode"
              ></v-select>
            </v-flex>
            <v-flex xs12 md9>
              <v-slider v-if="state.mode <= 1"
                        thumb-label="always"
                        v-model="state.target"
                        :disabled="!state.target"
                        @change="onChangeTarget"
              ></v-slider>
            </v-flex>
          </v-layout>
        </v-container>
      </v-tab-item>
      <v-tab-item value="tab-2">
        <v-container>
          <p>
            Android applications:
            <ul>
              <li><a href="https://play.google.com/store/apps/details?id=net.routix.mqttdash" target="_blank">MQTT Dash (RUS)</a></li>
              <li><a href="https://play.google.com/store/apps/details?id=snr.lab.iotmqttpanel.prod" target="_blank">IoT MQTT Panel (EN)</a></li>
            </ul>
          </p>
          <p>
            Server params:
            <ul>
              <li>Address: mqtt.eclipse.org</li>
              <li>port: 1883</li>
            </ul>
          </p>
          <table class="topic-table">
            <tr>
              <th>{{ 'TOPIC'|lang }}</th>
              <th>{{ 'TOPIC_DESCRIPTION'|lang }}</th>
            </tr>
            <tr>
              <td>/thingjs/{{ state.chip_id }}/temp</td>
              <td>{{ 'TOPIC_TEMP_DESC'|lang }}</td>
            </tr>
            <tr>
              <td>/thingjs/{{ state.chip_id }}/state</td>
              <td>{{ 'TOPIC_STATE_DESC'|lang }}</td>
            </tr>
            <tr>
              <td>/thingjs/{{ state.chip_id }}/target/out</td>
              <td>{{ 'TOPIC_TARGET_OUT'|lang }}</td>
            </tr>
            <tr>
              <td>/thingjs/{{ state.chip_id }}/target/in</td>
              <td>{{ 'TOPIC_TARGET_IN'|lang }}</td>
            </tr>
            <tr>
              <td>/thingjs/{{ state.chip_id }}/mode/out</td>
              <td>{{ 'TOPIC_MODE_OUT'|lang }}</td>
            </tr>
            <tr>
              <td>/thingjs/{{ state.chip_id }}/mode/in</td>
              <td>{{ 'TOPIC_MODE_IN'|lang }}</td>
            </tr>
          </table>
        </v-container>
      </v-tab-item>
    </v-tabs>
  </v-flex>
</template>


data () {
   return {
       modes: [ //    .
           { text: 'Less then', value: 0 },
           { text: 'More then', value: 1 },
           { text: 'On', value: 2 },
           { text: 'Off', value: 3 }
       ],
       isHold: false, //  .   ,      .
       state: { //   
           connected: null, //     MQTT 
           mode: null, //   
           target: null, //  
           temp: null, //  
           state: null, //   (/)
           chip_id: null //    MQTT .
       }
   };
}


:



isHold — . , . . , . , . “” .



chip_id — . MQTT .



mounted () {
   this.$bus.$on($consts.EVENTS.UBUS_MESSAGE, (type, data) => {
       if (this.isHold) return;

       switch (type) {
       case 'thermostat-state':
           this.state = JSON.parse(data);
           break;
       }
   });
   this.refreshState();
},


. “thermostat-state”. . “isHold” .



refreshState () {
   this.$bus.$emit($consts.EVENTS.UBUS_MESSAGE, 'tmst-refresh-state');
},


. .



flushData () {
   if (this.isHold) { clearTimeout(this.isHold); }
   this.isHold = setTimeout(() => {
       this.isHold = null;
       this.refreshState();
   }, 1000);
},


. , .



onChangeTarget (val) {
   this.$bus.$emit($consts.EVENTS.UBUS_MESSAGE, 'tmst-set-target', val);
   this.flushData();
},
onChangeMode (val) {
   this.$bus.$emit($consts.EVENTS.UBUS_MESSAGE, 'tmst-set-mode', val);
   this.flushData();
}


.



- frontend. “” .



requires



, . “requires”.



"requires": {
 "interfaces": {
   "mqtt": {
     "type": "mqttc",
     "required": true
   },
   "timers": {
     "type": "timers",
     "required": true,
     "description": {
       "ru": " ",
       "en": "System timers"
     }
   },
   "ds18x20": {
     "type": "DS18X20",
     "required": true
   },
   "relay": {
     "type": "bit_port",
     "required": true,
     "default": 2,
     "description": {
       "ru": "",
       "en": "Relay"
     }
   },
   "sys_info": {
     "type": "sys_info",
     "required": true,
     "description": {
       "ru": "  ",
       "en": "System information"
     }
   }
 }
}


:



  • mqttc — MQTT . .
  • timers — . .
  • DS18X20 — OneWire .
  • bit_port — . .
  • sys_info — . .


, . . , . "required" . .



. , “DS18X20”. , OneWire.



. . . , , “relay”, “type”. “relay” “bit_port”.



scripts



.



"scripts": {
 "entry": "thermostat",
 "subscriptions": ["tmst-refresh-state", "tmst-set-target", "tmst-set-mode"],
 "modules": {
   "thermostat": {
     "hot_reload": true,
     "source": "scripts/thermostat.js",
     "optimize": false
   }
 }
},


  • entry — . . . .
  • subscriptions — . - . , “”. .
  • modules — .

    • thermostat — .
    • hot_reload — “” . true, . . dev-.
    • source — .
    • optimize — true, webpack.


“scripts/blink.js” “scripts/thermostat.js”. .



let MQTT_SERVER = 'wss://mqtt.eclipse.org:443/mqtt';


MQTT . . . . MQTT , .



let CHIP_ID = $res.sys_info.chip_id;


. $res. . sys_info . MQTT .



let TOPIC_TEMP = '/thingjs/' + CHIP_ID + '/temp';
let TOPIC_TARGET_OUT = '/thingjs/' + CHIP_ID + '/target/out';
let TOPIC_TARGET_IN = '/thingjs/' + CHIP_ID + '/target/in';
let TOPIC_MODE_OUT = '/thingjs/' + CHIP_ID + '/mode/out';
let TOPIC_MODE_IN = '/thingjs/' + CHIP_ID + '/mode/in';
let TOPIC_MODE_STATE = '/thingjs/' + CHIP_ID + '/state';


MQTT . “out” “in”. . out — , in — .



:



//        .   .
let MODE_LESS = 0;
//        .   .
let MODE_MORE = 1;
//   .
let MODE_ON = 2;
//   .
let MODE_OFF = 3;


:



//     MQTT 
let isConnected = false;
//   
let mode = MODE_LESS;
//  
let target = 32;
//  
let state = 0;
//   
let sensor = null;
//  
let temp = null;
//    ,        .   .  “”     .
let fakeVector = 0.5;


. OneWire. sensor .



$res.ds18x20.search(function (addr) {
   if (sensor === null) {
       sensor = addr;
   }
});

function publishState () {
   $bus.emit('thermostat-state', JSON.stringify({
       connected: isConnected,
       mode: mode,
       target: target,
       temp: temp,
       state: state,
       chip_id: CHIP_ID
   }));

   if (isConnected) {
       $res.mqtt.publish(TOPIC_MODE_OUT, JSON.stringify(mode));
       $res.mqtt.publish(TOPIC_TARGET_OUT, JSON.stringify(target));
       $res.mqtt.publish(TOPIC_MODE_STATE, JSON.stringify(state));
       $res.mqtt.publish(TOPIC_TEMP, JSON.stringify(temp));
   }
}


publishState :



  • UBUS. . . , . , frontend. frontend.
  • MQTT . . .


MQTT :



//       
$res.mqtt.onconnected = function () {
   print('MQTT client is connected');
   isConnected = true;
   $res.mqtt.subscribe(TOPIC_TARGET_IN);
   $res.mqtt.subscribe(TOPIC_MODE_IN);
   publishState();
};

//      online 
$res.mqtt.disconnected = function () {
   print('MQTT client is disconnected');
   isConnected = false;
   publishState();
};

//      MQTT 
$res.mqtt.ondata = function (topic, data) {
   print('MQTT client received from topic [', topic, '] with data [', data, ']');
   if (topic === TOPIC_TARGET_IN) {
       target = JSON.parse(data);
   } else if (topic === TOPIC_MODE_IN) {
       mode = JSON.parse(data);
   }
};


UBUS. , .



$bus.on(function (event, data) {
   if (event === 'tmst-set-target') {
       target = JSON.parse(data);
   } else if (event === 'tmst-set-mode') {
       mode = JSON.parse(data);
   }
   publishState();
}, null);


.



$res.timers.setInterval(function () {
   if (sensor !== null) {
       $res.ds18x20.convert_all();
       temp = $res.ds18x20.get_temp_c(sensor);
   } else { // Fake temperature
       if (temp > 99) {
           fakeVector = -0.5;
       } else if (temp < 1) {
           fakeVector = 0.5;
       }

       temp += fakeVector;
   }
   // Refresh sensor data
   if (mode === MODE_ON) {
       state = 1;
   } else if (mode === MODE_OFF) {
       state = 0;
   } else if (mode === MODE_LESS) {
       if (temp < target) {
           state = 1;
       } else {
           state = 0;
       }
   } else if (mode === MODE_MORE) {
       if (temp > target) {
           state = 1;
       } else {
           state = 0;
       }
   }

   publishState();
   //        
   $res.relay.set(!state);
}, 1000);


:



//      . 
temp = 34.5;
//   GPIO
$res.relay.direction($res.relay.DIR_MODE_OUTPUT);
//   
publishState();
//      MQTT .
$res.mqtt.connect(MQTT_SERVER);




. VUE . :



<h1>{{ 'TITLE'|lang }}</h1>


langs.js frontend .



favicon



. favicon.svg .





. .. /src/applications/. npm



npm run build



smt . thermostat.smt Thermostat. /dist/apps/







. WEB. “” “” “ ”. thermostat.smt.





, . . , , MQTT . . , OneWire UART GPIO . , GPIO .



default . , “”. .



:



  1. . .
  2. . .
  3. . , .

    . , .


. .





: MQTT. , MQTT.





. . .



. .







Android. MQTT Dash.



, . IP . .



. MQTT .





. . . .





. — /thingjs/TJS-030BE4/temp .



. , — .



().



. . out in .



, , .



!





- IoT. , , .



, .



. - , . , . , .



.



?



  • ;
  • ;
  • , ;
  • モバイルデバイスへのアプリケーションのインストール。
  • アマチュア向けのThingJS開発キットの開発とリリース。
  • 人気のあるIoTエコシステムとの統合。
  • 音声制御の実装。


リンク



ThingJSプロジェクトリソース:





ThingJSプロジェクトリポジトリ:





どこプラットフォームがされ、すでに使用します:





使用される場所:





使用済みプロジェクト:






All Articles