2020/08/24

[第6回]温度・湿度センサーの情報をThingSpeakにアップロードしてみよう

rolaiotarduino

概要

前回は、センサー情報のアップロード先であるThingSpeakの設定を行いました。

今回は、前回までの総まとめになります。以下のような内容を実施します。

  • DHT11(温度・湿度センサー)とLoRa Mini Devを繋ぎます
  • LoRa Mini DevからLoRa通信を使って、LG01にデータを送信します
  • LG01はデータを受信すると、ThingSpeakにデータをアップするサーバーとして動作させます

LoRa Clientの設定

DHT11とLoRa Mini Devの接続

以下のようにして端子を合わせて接続します。

LoRa Mini Dev 端子 DHT 11端子
3.3V VCC
GND GND
A0 DATA

dht11-connect-min.png

Arduino IDEの設定

  • Arduino IDEを立ち上げます

プログラムを書くまえに、必要なライブラリをインストールしておく必要があります。

ライブラリ名 用途 インストール方法
ThingSpeak ThingSpeakアップロード用のライブラリ ライブラリマネージャーからインストール
DHT sensor library DHT操作用のライブラリ ライブラリマネージャーからインストール
Adafruit Unified Sensor required DHTライブラリが使うライブラリ DHT sensor libraryをインストールすると、自動でインストールできる
RadioHead LoRa通信用のライブラリ https://github.com/dragino/RadioHead/archive/master.zipをダウンロード&解凍して、Arduino IDEのlibrariesフォルダに格納

※ RadioHead については、第4回で既にインストール済なので、他のライブラリがインストールされていなければ
このタイミングでインストールする必要があります。

温度・湿度送信プログラムのデプロイ

  • Arduino IDEを立ち上げます
  • ボードを Arduino Uno を選択します
  • ポートを LoRa Mini Devが繋がれた、USBのポート (画像では、 /dev/cu.usbserial-14430 )を選択します

arduino-ide-simple-client-port-min.png

選択できたら、以下のプログラムを使います。

/*
  Upload Data to IoT Server ThingSpeak (https://thingspeak.com/):
  Support Devices: LoRa Shield + Arduino 
  
  Example sketch showing how to read Temperature and Humidity from DHT11 sensor,  
  Then send the value to LoRa Server, the LoRa Server will send the value to the 
  IoT server

  It is designed to work with the other sketch dht11_server. 

  modified 24 11 2016
  by Edwin Chen <[email protected]>
  Dragino Technology Co., Limited
*/

#include <SPI.h>
#include <RH_RF95.h>
#include <String.h>
#include "DHT.h"

RH_RF95 rf95;

#define dht_dpin A0 // Use A0 pin as Data pin for DHT11. 
#define DHTTYPE DHT11   // DHT 11 

byte bGlobalErr;
char dht_dat[5]; // Store Sensor Data
String stringOne;
float frequency = 923.6;
//float frequency = 868.0;


DHT dht(dht_dpin, DHTTYPE);

void setup()
{
    Serial.begin(9600);
    while(!Serial);
    if (!rf95.init())
        Serial.println("init failed");
    // Setup ISM frequency
    rf95.setFrequency(frequency);
    // Setup Power,dBm
    rf95.setTxPower(13);
    // キャリアセンス
    rf95.setCADTimeout((unsigned long)5);

    dht.begin();
    Serial.println("Humidity and temperature\n\n"); 
}

uint16_t calcByte(uint16_t crc, uint8_t b)
{
    uint32_t i;
    crc = crc ^ (uint32_t)b << 8;
    
    for ( i = 0; i < 8; i++)
    {
        if ((crc & 0x8000) == 0x8000)
            crc = crc << 1 ^ 0x1021;
        else
            crc = crc << 1;
    }
    return crc & 0xffff;
}

uint16_t CRC16(uint8_t *pBuffer,uint32_t length)
{
    uint16_t wCRC16=0;
    uint32_t i;
    if (( pBuffer==0 )||( length==0 ))
    {
      return 0;
    }
    for ( i = 0; i < length; i++)
    { 
      wCRC16 = calcByte(wCRC16, pBuffer[i]);
    }
    return wCRC16;
}

void loop()
{
    float h = dht.readHumidity();
    float t = dht.readTemperature();
    char data[50] = {0} ;
    // Use data[0], data[1],data[2] as Node ID
    data[0] = 1 ;
    data[1] = 1 ;
    data[2] = 1 ;
    data[3] = h ;//dht_dat[0];//Get Humidity
    data[4] = t ;//dht_dat[2];//Get Temperature
    //data[3] = (int)random(0, 100);//
    //data[4] = (int)random(10, 30);//
    switch (bGlobalErr)
    {
      case 0:
          Serial.print("Current humdity = ");
          Serial.print(data[3], DEC);//Show humidity
          Serial.print(".");
          Serial.print(dht_dat[1], DEC);//Show humidity
          Serial.print("%  ");
          Serial.print("temperature = ");
          Serial.print(data[4], DEC);//Show temperature
          Serial.print(".");
          Serial.print(dht_dat[3], DEC);//Show temperature
          Serial.println("C  ");
          break;
       case 1:
          Serial.println("Error 1: DHT start condition 1 not met.");
          break;
       case 2:
          Serial.println("Error 2: DHT start condition 2 not met.");
          break;
       case 3:
          Serial.println("Error 3: DHT checksum error.");
          break;
       default:
          Serial.println("Error: Unrecognized code encountered.");
          break;
    }
    int dataLength = strlen(data);//CRC length for LoRa Data
    //Serial.println(dataLength);
     
    uint16_t crcData = CRC16((unsigned char*)data,dataLength);//get CRC DATA
    //Serial.println(crcData);
       
    unsigned char sendBuf[50]={0};
    int i;
    for(i = 0;i < 8;i++)
    {
        sendBuf[i] = data[i] ;
    }
    
    sendBuf[dataLength] = (unsigned char)crcData; // Add CRC to LoRa Data
    //Serial.println( sendBuf[dataLength] );
    
    sendBuf[dataLength+1] = (unsigned char)(crcData>>8); // Add CRC to LoRa Data
    //Serial.println( sendBuf[dataLength+1] );

    rf95.send(sendBuf, strlen((char*)sendBuf));//Send LoRa Data
    //Serial.print(strlen((char*)sendBuf));
     
    uint8_t buf[RH_RF95_MAX_MESSAGE_LEN];//Reply data array
    uint8_t len = sizeof(buf);//reply data length

    if (rf95.waitAvailableTimeout(3000))// Check If there is reply in 3 seconds.
    {
        // Should be a reply message for us now   
        if (rf95.recv(buf, &len))//check if reply message is correct
       {
            if(buf[0] == 1||buf[1] == 1||buf[2] ==1) // Check if reply message has the our node ID
           {
               pinMode(4, OUTPUT);
               digitalWrite(4, HIGH);
               Serial.print("got reply: ");//print reply
               Serial.println((char*)buf);
              
               delay(400);
               digitalWrite(4, LOW); 
               //Serial.print("RSSI: ");  // print RSSI
               //Serial.println(rf95.lastRssi(), DEC);        
           }    
        }
        else
        {
           Serial.println("recv failed");//
           rf95.send(sendBuf, strlen((char*)sendBuf));//resend if no reply
        }
    }
    else
    {
        Serial.println("No reply, is rf95_server running?");//No signal reply
        Serial.println("test??");
        Serial.println((char*)sendBuf);
        rf95.send(sendBuf, strlen((char*)sendBuf));//resend data
    }
    Serial.println("test?");
    delay(3); // Send sensor data every 30 seconds
}

プログラムの中身

  • 923.0 の周波数でLoRa通信を行います。
  • DHT11(湿度・温度センサー)からのデータを読み取り、LoRa通信を使って、Serverに送信します

スケッチ > マイコンボードに書き込む で先ほどのプログラムをアップロードします。

これでClient側の設定は完了になります。これが完了すると、湿度・温度を一定の間隔でLoRa通信を使って送信し続けるようになります。

LoRa Serverの設定

  • Arduino IDEを立ち上げます
  • ボードを Dragino Yun + UNO or LG01/OLG01 を選択します
  • ポートは LG01 のローカルIPアドレスであるネットワークポートを選択します

arduino-led-port-min.png

選択できたら、以下のプログラムを使います。

/*
  Upload Data to IoT Server ThingSpeak (https://thingspeak.com/):
  Support Devices: LG01 
  
  Example sketch showing how to get data from remote LoRa node, 
  Then send the value to IoT Server

  It is designed to work with the other sketch dht11_client. 

  modified 24 11 2016
  by Edwin Chen <[email protected]>
  Dragino Technology Co., Limited
*/

#include <SPI.h>
#include <RH_RF95.h>
//#include <Console.h>
#include "ThingSpeak.h"
#include "YunClient.h"
YunClient client;
RH_RF95 rf95;

//If you use Dragino IoT Mesh Firmware, uncomment below lines.
//For product: LG01. 
#define BAUDRATE 115200

unsigned long myChannelNumber = 0000000;
const char * myWriteAPIKey = "XXXXXXXXXXXXXXXX";
uint16_t crcdata = 0;
uint16_t recCRCData = 0;
float frequency = 923.6;
//float frequency = 868.0;

void setup()
{
    Bridge.begin(BAUDRATE);
//    Console.begin();// Don't use Console here, since it is conflict with the ThinkSpeak library. 
//    while(!Console);
    ThingSpeak.begin(client);
    
    if (!rf95.init())
//        Console.println("init failed")
    ;
    // Setup ISM frequency
    rf95.setFrequency(frequency);
    // Setup Power,dBm
    rf95.setTxPower(13);
    // �L�����A�Z���X
    rf95.setCADTimeout((unsigned long)5);
//    Console.println("Start Listening ");
}

uint16_t calcByte(uint16_t crc, uint8_t b)
{
    uint32_t i;
    crc = crc ^ (uint32_t)b << 8;
  
    for ( i = 0; i < 8; i++)
    {
      if ((crc & 0x8000) == 0x8000)
        crc = crc << 1 ^ 0x1021;
      else
        crc = crc << 1;
    }
    return crc & 0xffff;
}

uint16_t CRC16(uint8_t *pBuffer, uint32_t length)
{
    uint16_t wCRC16 = 0;
    uint32_t i;
    if (( pBuffer == 0 ) || ( length == 0 ))
    {
        return 0;
    }
    for ( i = 0; i < length; i++)
    {
        wCRC16 = calcByte(wCRC16, pBuffer[i]);
    }
    return wCRC16;
}

uint16_t recdata( unsigned char* recbuf, int Length)
{
    crcdata = CRC16(recbuf, Length - 2); //Get CRC code
    recCRCData = recbuf[Length - 1]; //Calculate CRC Data
    recCRCData = recCRCData << 8; //
    recCRCData |= recbuf[Length - 2];
}
void loop()
{

    if (rf95.waitAvailableTimeout(2000))// Listen Data from LoRa Node
    {
        uint8_t buf[RH_RF95_MAX_MESSAGE_LEN];//receive data buffer
        uint8_t len = sizeof(buf);//data buffer length
        if (rf95.recv(buf, &len))//Check if there is incoming data
        {
            recdata( buf, len);
            if(crcdata == recCRCData) //Check if CRC is correct
            { 
//                Console.println("Get Data from LoRa Node");
//                Console.print(buf[0]);Console.print(buf[1]);Console.println(buf[2]);
                if(buf[0] == 1||buf[1] == 1||buf[2] ==1) //Check if the ID match the LoRa Node ID
                {
                    uint8_t data[] = "   Server ACK";//Reply 
                    data[0] = buf[0];
                    data[1] = buf[1];
                    data[2] = buf[2];
//                    Console.print(data[0]);Console.print(data[1]);Console.println(data[2]);
                    rf95.send(data, sizeof(data));// Send Reply to LoRa Node
                    rf95.waitPacketSent();
                    int newData[4] = {0, 0, 0, 0}; //Store Sensor Data here
                    for (int i = 0; i < 2; i++)
                    {
                        newData[i] = buf[i + 3];
                    }
                    float h = newData[0];
                    float t = newData[1];
//                    Console.print("h:");Console.println(h);
//                    Console.print("t:");Console.println(t);
                    ThingSpeak.setField(2,h); // 
                    ThingSpeak.setField(1,t);
                    ThingSpeak.writeFields(myChannelNumber, myWriteAPIKey);   // Send Data to IoT Server.
                }
            }       
         }
         else
         {
//              Console.println("recv failed");
              ;
          }
     }
}

myChannelNumbermyWriteAPIKey はご自身のThingSpeakのChannelのIDと書き込み用のAPI Keyを使う必要があります。

unsigned long myChannelNumber = 0000000;
const char * myWriteAPIKey = "XXXXXXXXXXXXXXXX";

スケッチ > マイコンボードに書き込む で先ほどのプログラムをアップロードします。

これが完了すると、Clientから送信された湿度や温度がThingSpeakのChannelにアップロードされていきます。

ThingSpeakのWeb View画面にいき、無事アップロードされていることが確認できました。

channel-result-min.png

最後に

プログラムを一から書くことがなく、動作しているものをそのまま流用できたので、一連の作業自体は2日かからず全て完了しました。
Arduino IDEの使い方や、Wifiの設定などでややハマってしまった感はあるので、慣れていればよりスムーズだったかなと思います。

ただ、流用しているがために、特にあまり考えなくてもできてしまったため、あまり身についていない部分もあるかなというのは正直あります。
ここら辺は独自の応用例を挑戦してみて(ハマりポイントにハマって)身につけていこうと思います。

とはいえ、IOT系の開発の一連の作業フローや考え方が少し分かったので、そこはとても良かったと思っています。

開発手順のおさらい

以下のような手順で実施しました。

  1. LG01(Gateway)のwifiの設定 <= 第2回の記事で紹介
  2. Arduino IDEを使って、LG01の動作確認 <= 第3回の記事で紹介
  3. Arduino IDEを使って、LoRa Mini DevとLG01の通信を検証 <= 第4回の記事で紹介
  4. ThingSpeakのアカウントの作成 <= 第5回の記事で紹介
  5. DHT11(湿度・温度センサー)とLoRa Mini Devをつなぎ、LG01を経由してThingSpeakにデータをアップロードする <= 今回の記事のスコープ

以上です。