2015年1月16日 星期五

[ Wireless-RF@Arduino ] 無線天氣資料傳輸 - Arduino Manchester 函式庫應用

網頁最後修改時間:2016/09/04 

延續上一篇 Manchester ( 曼徹斯特 ) 函式庫的介紹,這一篇將繼續深入一點點,連接 BH1750 ( 光照度感測器 ) 和 DHT11 ( 溫溼度感測器 ) 在 Arduino 板子上,所收到的溫溼度以及環境照度資料經由 RF433 以及 ASK433 ( 如下圖 ) 無線發射模組傳送到遠端的無線接收端。

本篇除了使用與上一篇同樣的方法傳送資料之外,還介紹了使用陣列傳送的方式,使用這方法可以擴展更多的 ID 與資料串列,用來接收更多不同無線發射端的資料 !
賣場的無線發射接收模組 ( 左邊:本文稱 RF433;右邊:本為稱 ASK433 )
*********************************************************************************
本篇網頁中所使用到的零件可到下面商品網址訂購:
    *********************************************************************************
    接線電路圖:

    這部分的接線如下圖所示。除了無線發射接收模組的接線之外,就是發射端的溫溼度感測器和光照度感測器的接線。
    Arduino 天氣資料接線電路圖

    無線發射端實際的接線照片:
    未插上無線發射端實際接線照片( RF433 與 ASK433 無線接收端可共用 )

    無線接收端實際的接線照片:
    下面是使用 ASK433 時的接線,CS 接腳要跨接到 GND;若使用 RF433 就拿掉黑色跨接線。
    ASK433MHz 無線接收端實際接線照片

    無線發射接收模組實際接線完成圖照片:
    未插 ASK433MHz 無線發射接收模組實際接線照片
    插上 ASK433MHz 無線發射接收模組實際接線照片

    使用 RF433 無線發射接收模組,發射端的跨接線要拔掉,實際接線照片如下。
    插上 RF433MHz 無線發射接收模組實際接線照片

    將線都接好之後,就可以開始程式的撰寫!

    如果有購買賣場無線發射接收模組的話,程式碼已同步更新至雲端硬碟,可直接下載,不需花費複製再貼上的時間。


    程式碼:

    程式中需要將 DHT11 與 BH1750 的數據抓出來,並且將資料做處理後使用 Manchester 編碼發送出去,所以需要下面幾個函式庫:
    • DHT Library
      解壓縮到 { Arduino }\libraries 目錄下,並更名為 DHT
    • Manchester Library
      解壓縮到 { Arduino }\libraries 目錄下,並更名為 Manchester

    這些函式庫解壓縮之後,Arduino IDE 必須重新開啟才會載入剛剛複製的函式庫。


    使用 ID 編碼方式:

    加入 ID 的方式可以讓傳送的資料或是節點 ( 一個發送資料的微控制器 ) 擁有可識別的標誌,而這個標誌可以方便多資料或多節點的數據處理,非常的方便!

    Manchester_weather_tx.ino 是無線發射端的 Arduino 程式,它會取得 DHT11 ( 溫溼度感測器 ) 與 BH1750 ( 光照度感測器 ) 資料,再依據程式開頭 ID 的定義編碼資料
    • DHT11, Temperature ID:1
    • DHT11, Humidity          :3
    • BH1750, Lux H-byte     :5
    • BH1750, Lux L-byte      :6
    BH1750 預設的 I2C 位址是 0x23 ( ADD 浮接 ),Wire 函式庫使用這位址讀寫 BH1750。DHT11 資料通訊接腳接 Arduino 的 D8,並設定 DHT 函式庫使用 DHT11 的資料類型讀取溫濕度資料。
    ID, 發射端, Manchester_weather_tx.ino, Part 1 of 3
    #include <Wire.h>
    #include "Manchester.h"
    #include "DHT.h"
    
    #define DEBUG
    
    //*-- Manchester library
    #define _txpin      2
    #define _ledpin     13
    
    //*-- DHT11
    #define _dhtpin     8
    #define _dhttype    DHT11
    #define  _tempid    1
    #define _humiid     3
    
    DHT dht11( _dhtpin, _dhttype );
    uint8_t dhtbuf[2];
    
    //*-- BH1750
    // I2C, SDA = A4
    // I2C, SCL = A5
    #define _bh1750addr 0x23
    #define _luxhbyte   5
    #define _luxlbyte   6
    uint8_t buff[2];
    
    

    setup() 負責所有函式庫的基本初始化設定,包括:Manchester 傳送速度與接腳的設定,BH1750 I2C 位址的設定...等,傳送端基本就是這些設定。

    ID, 發射端, Manchester_weather_tx.ino, Part 2 of 3
    void setup() {
        Serial.begin(9600);
        Serial.println("Machester Transmit Ready!");
        // Manchester
        man.setupTransmit( _txpin, MAN_1200 );
        // DHT11
        dht11.begin();
        // BH1750
        Wire.begin();
        bh1750_init( _bh1750addr ); 
    
        pinMode( _ledpin, OUTPUT );
    }
    

    bh1750_init() 是 BH1750 的寫入初始化指令,可以修改的部分就是解析度的調整; bh1750_read() 讀回 BH1750 的光強度值 ( Lx )。基本上都不會需要去修改此處的程式碼,只要直接使用即可。

    loop() 會先讀取 DHT 溫度與濕度的數值,只要是有效數據,就會直接利用上面指定的 ID 與資料進行編碼,並且直接由無線發射模組傳送出去。最後呼叫 bh1750_read() 確認是否回傳 2 個位元組資料,將光強度值與指定的 ID 進行編碼再傳送出去。

    所以溫溼度與光強度是兩個分開的組別,只有回傳的數據是有效的,該組才會被編碼送出。

    ID, 發射端, Manchester_weather_tx.ino, Part 3 of 3
    void loop() {
        // DHT11 溫溼度不需要小數位,也沒有小數位
        dhtbuf[0] = dht11.readHumidity();
        dhtbuf[1] = dht11.readTemperature();
    
        // 確認取回的溫溼度數據可用
        if( isnan(dhtbuf[0]) || isnan(dhtbuf[1]) )
        {
            Serial.println( "Failed to read form DHT11" );
        }
        else
        {
            // DHT11 溫度傳送
            //man.transmit( man.encodeMessage( _tempid, dhtbuf[0] ) );
            //man.transmit( man.encodeMessage( _humiid, dhtbuf[1] ) );
            // 2016/09/03 修正接收端溫度與濕度數值相反的問題
            // 感謝網友 addison 的細心提醒 !
            man.transmit( man.encodeMessage( _humiid, dhtbuf[0] ) );
            man.transmit( man.encodeMessage( _tempid, dhtbuf[1] ) );
            #ifdef DEBUG
                Serial.print("Humidity: "); 
                Serial.print( dhtbuf[0] );
                Serial.print(" %\t");
                Serial.print("Temperature: "); 
                Serial.print( dhtbuf[1] );
                Serial.print(" *C\t");
            #endif
        }
        //---------------------------------------------------------------
        // BH1750 回傳兩個 byte
        // lux = ( [0] << 8 + [1] ) / 1.2
        // 不先作計算,由接收端做計算
        if( 2 == bh1750_read( _bh1750addr ) )
        {
            man.transmit( man.encodeMessage( _luxhbyte, buff[0] ) );
            man.transmit( man.encodeMessage( _luxlbyte, buff[1] ) );
            #ifdef DEBUG
                float val = ( ( buff[0] << 8 ) + buff[1] ) / 1.2;
                Serial.print( "Brightness: " );
                Serial.print( val );
                Serial.println(" lux");
            #endif
        }
        //---------------------------------------------------------------
    
    delay(100);
    }
    
    void bh1750_init( int address )
    {
        Wire.beginTransmission( address );
        Wire.write( 0x01 ); // Power on
        Wire.endTransmission();
        Wire.beginTransmission( address );
        Wire.write( 0x10 ); // 1 lx resolution, 120ms
        Wire.endTransmission();
    }
    
    int bh1750_read( int address )
    {
        int i = 0;
        Wire.beginTransmission( address );
        Wire.requestFrom( address, 2 );
        while( Wire.available() )
        {
            buff[i] = Wire.read();  // receive one byte
            i++;
        }
        Wire.endTransmission();
        return i;
    }
    

    ----------
    接收端程式相對容易!

    無線接收接腳是 D3;每次收到有效數據時 D13 ( 板上的 ) LED 就會亮起直到資料被處理完畢。

    Manchester 函式庫的接收端要接收資料,在 setup() 中必須指定無線接收模組所使用的接收接腳號碼以及速度 ( man.setupReceive() ),並緊接著下一行輸入開始接收的指令 man.beginReceive()

    ID, 接收端, Manchester_weather_rx.ino, Part 1 of 2
    #include "Manchester.h"
    
    //#define DEBUG
    
    #define _rxpin      3
    
    #define _ledpin     13
    
    bool tm, ud, hb, lb;
    uint16_t dval;
    uint8_t temperature, humidity;
    
    void setup() {
        Serial.begin(9600);
        Serial.println("Manchester Receive Ready!");
        man.setupReceive( _rxpin, MAN_1200 );
        man.beginReceive();
    
        pinMode( _ledpin, OUTPUT );
        digitalWrite( _ledpin, LOW );
    
        tm = ud = hb = lb = false;
        dval = 0;
        temperature = humidity = 0;
    }
    

    loop() 會重複確認數據是否完整的被接收 ( man.receiveComplete() ),一但確認數據被完整接收到就可以將數據取出 ( man.getMessage() ),再開始繼續下一輪的數據接收 ( man.beginReceive() )。
    該處理都處理了之後,現在就可以慢慢地對取得的資料進行分析。man.decodeMessage() 會分解出數據中的 ID 號碼和 DATA 資料,switch ... case 會以 ID 做為資料類型的判別構成輸出到 Serial Monitor 的字串。為了確保光強度資料是在完整收到兩個數據位元組之後才做計算,增加了條件判斷式在 case 5 中。

    ID, 接收端, Manchester_weather_rx.ino, Part 2 of 2
    uint8_t bufidx = 0;
    uint8_t data;
    uint8_t id;
    char buf[25];
    
    void loop() {
    
        if (man.receiveComplete()) { //received something
            uint16_t m = man.getMessage();
            man.beginReceive(); //start listening for next message right after you retrieve the message
            if (man.decodeMessage(m, id, data) ) {
                digitalWrite( _ledpin, HIGH );
                switch(id)
                {
                    case 1: // dht11 temperature
                        tm = true;
                        temperature = data;
                        #ifdef DEBUG
                            sprintf( buf, "%2d, temp=%2dC", id, data );
                        #endif
                    break;
                    case 3: // dht11 humidity
                        ud = true;
                        humidity = data;
                        #ifdef DEBUG
                            sprintf( buf, "%2d, humi=%2d%", id, data );
                        #endif
                    break;
                    case 5: // bh1750 high-byte
                        if( hb == false && lb == true ) lb = false;
                        hb = true;
                        dval = data << 8;
                        #ifdef DEBUG
                            sprintf( buf, "%2d, hbyte=0x%2x", id, data );
                        #endif
                    break;
                    case 6: // bh1750 low-byte
                        lb = true;
                        dval += data;
                        #ifdef DEBUG
                            sprintf( buf, "%2d, lbyte=0x%2x", id, data );
                        #endif
                    break;
                }
                #ifdef DEBUG
                    Serial.println( buf );
                #endif
                digitalWrite( _ledpin, LOW );
            }
    
            if( tm ) // 溫度有變化可以顯示
            {
                sprintf( buf, "temp:%2dC\n", temperature );
                Serial.print(buf);
                tm = false;
            }
    
            if( ud ) // 濕度有變化
            {
                sprintf( buf, "humi:%2d%c\n", humidity, '%' );
                Serial.print(buf);
                ud = false;
            }
    
            if( hb == true && lb == true )
            {
                float luxval = dval / 1.2;
                uint16_t dp = luxval;  // 取整數部分
                    uint16_t fp = (float)(luxval - dp ) * 100;  // 取小數部分
                sprintf( buf, "BR:%5d.%2d lx\n", dp, fp );
                Serial.print(buf);
                hb = lb = false;
                dval = 0.0;
            }
        }
    }
    
    

    使用上面兩個程式傳送與接收數據的方式,最多可以傳送 15 個不同 ID 的數據。對於發射端節點量或是數據點少的情況之下,很適用這種方式 !

    不過在接下來介紹的另一種方法:Arrary 傳送編碼數據,可以一次傳送更多的 ID 以及資料;也就是說,不但可以增加 ID 數目,而且每一個 ID 所能傳送的資料位元組數量也可以變多!


    使用 Array 傳送編碼方式:

    使用陣列傳送的方式,可以自行定義 ID 的位元數 ( bit ) 以及一次要傳送的資料位元組 ( byte ) 數量,因此依據傳送端要傳送的資料,可以決定陣列如下,

    _databuf[0], ID        :8-bit   ( 1-byte ),可以設定的範圍就變成 0 - 255

    _databuf[1], check sum:8-bit ( 1-byte ),其餘 5 個資料的 OR 運算索求出的結果。

    _databuf[2], 溫度    :8-bit   ( 1-byte )
    _databuf[3], 濕度    :8-bit   ( 1-byte )

    _databuf[4], 光照度高位元組:8-bit ( 1-byte )
    _databuf[5], 光照度低位元組:8-bit ( 1-byte )

    大小 ( BUFFER_SIZE ) 為 6,ID 設定為 88,其餘的都與 Manchester_weather_tx.ino 類似。

    Array, 發射端, Manchester_weather_tx_array.ino, Part 1 of 2
    #include <Wire.h>
    #include "Manchester.h"
    #include "DHT.h"
    
    #define DEBUG
    
    #define BUFFER_SIZE 6
    #define SENSOR_ID   88
    
    //*-- Manchester library
    #define _txpin      2
    //#define _ledpin       13
    
    //*-- DHT11
    #define _dhtpin     8
    #define _dhttype    DHT11
    
    //*-- BH1750
    // I2C, SDA = A4
    // I2C, SCL = A5
    #define _bh1750addr 0x23
    uint8_t _bhbuf[2];
    
    // data
    //{ ID, checksum, temperature, humidity, lux_highbyte, lux_lowbyte }
    uint8_t _databuf[BUFFER_SIZE];
    
    DHT dht11( _dhtpin, _dhttype );
    
    void setup() {
        Serial.begin(9600);
        Serial.println("Manchester Transmit Ready!");
        // Manchester
        man.setupTransmit( _txpin, MAN_1200 );
        // DHT11
        dht11.begin();
        // BH1750
        Wire.begin();
        bh1750_init( _bh1750addr ); 
    
        memset( _databuf, 0, 6*sizeof(uint8_t) );
        _databuf[0] = SENSOR_ID;
    }
    

    Array 與 ID 編碼方式在 loop() 裡面變更的部分,就是在傳送的部分改成先將資料丟進 _databuf 陣列中,依據前面所定義的資料位置儲存,而_databuf[1] 是其他 5 個陣列資料的 OR 運算,最後下 man.transmitArray() 傳送陣列出去。

    所以與 ID 傳送程式的部分不同之處就如同上面所說的,其餘的就都一樣了!

    Array, 發射端, Manchester_weather_tx_array.ino, Part 2 of 2
    void loop() {
        // DHT11 溫溼度不需要小數位,也沒有小數位
        float h = dht11.readHumidity();
        float t = dht11.readTemperature();
    
        // 確認取回的溫溼度數據可用
        if( isnan(h) || isnan(t) )
        {
            Serial.println( "Failed to read form DHT11" );
        }
        else
        {
            // DHT11 溫濕度數據儲存
            _databuf[2] = (uint8_t)t;
            _databuf[3] = (uint8_t)h;
        }
        //---------------------------------------------------------------
        // BH1750 回傳兩個 byte
        // lux = ( [0] << 8 + [1] ) / 1.2
        // 不先作計算,由接收端做計算
        if( 2 == bh1750_read( _bh1750addr ) )
        {
            _databuf[4] = _bhbuf[0];
            _databuf[5] = _bhbuf[1];
        }
        //---------------------------------------------------------------
    
        // 8-bit checksum
        _databuf[1] = _databuf[0] | _databuf[2] | _databuf[3] | _databuf[4] | _databuf[5];
    
        man.transmitArray( BUFFER_SIZE, _databuf );
    
        #ifdef DEBUG
            Serial.print("Temperature: "); 
            Serial.print( _databuf[2] );
            Serial.print(" *C\t\t");
            Serial.print("Humidity: "); 
            Serial.print( _databuf[3] );
            Serial.print(" %\t");
            float val = ( ( _databuf[4] << 8 ) + _databuf[5] ) / 1.2;
            Serial.print( "Brightness: " );
            Serial.print( val );
            Serial.println(" lux");
        #endif
        
        delay(500);
    }
    
    void bh1750_init( int address )
    {
        Wire.beginTransmission( address );
        Wire.write( 0x01 ); // Power on
        Wire.endTransmission();
        Wire.beginTransmission( address );
        Wire.write( 0x10 ); // 1 lx resolution, 120ms
        Wire.endTransmission();
    }
    
    int bh1750_read( int address )
    {
        int i = 0;
        Wire.beginTransmission( address );
        Wire.requestFrom( address, 2 );
        while( Wire.available() )
        {
            _bhbuf[i] = Wire.read();    // receive one byte
            i++;
        }
        Wire.endTransmission();
        return i;
    }
    
    

    ----------
    Arrary 接收程式省略掉 ID 接收程式在 loop() 中的資料接收的方式,取而代之的是換成接收陣列的方式。先在 setup() 裡設定接收的腳位和速度 ( man.setupReceive() ),並設定開始接收的數據位元數和陣列名稱 ( man.beginReceiveArray() )。

    Array, 接收端, Manchester_weather_rx_array.ino, Part 1 of 2
    #include "Manchester.h"
    
    //#define DEBUG
    
    #define BUFFER_SIZE 6
    #define SENSOR_ID   88
    
    #define _rxpin      3
    #define _ledpin     13
    
    bool tm, ud, hb, lb;
    // data
    //{ ID, checksum, temperature, humidity, lux_highbyte, lux_lowbyte }
    uint8_t _databuf[BUFFER_SIZE];
    
    void setup() {
        Serial.begin(9600);
        Serial.println("Manchester Receive Ready!");
        man.setupReceive( _rxpin, MAN_1200 );
        man.beginReceiveArray( BUFFER_SIZE, _databuf );
    
        pinMode( _ledpin, OUTPUT );
        digitalWrite( _ledpin, LOW );
    }
    

    進入到 loop() 之後,首先確認陣列接收的數據是否完成 ( man.receiveComplete() ),若條件判斷是 OK 的,就打開 D13 ( 板子的 ) LED 直到所有數據處理完畢再關閉。

    首先確認陣列的第一個元素 _databuf[0] 的數值是否與我們在程式開頭處所設定的 SENSOR_ID 是相同的,可以去除掉其他節點輸入進來的數據。第二個是確認陣列的第二個元素數值是不是與收到的數據進行 OR 運算之後的數值相同,這可以保障所收進來的數據是正確的。

    如果上面的條件都是對的,根據我們所定義的陣列元素位置,就可以得到

    _databuf[2]:DHT11 溫度資料
    _databuf[3]:DHT11 溼度資料

    ( _databuf[4] << 8 + _databuf[5] ) / 1.2:光強度資料 ( Lx )

    得到這些資料後,要從 UART 輸出或是顯示在 LCD 上都可以 !

    最後要離開前,別忘了再繼續接收資料 ( man.beginReceiveArray() ) 與關掉 D13 LED。

    Array, 接收端, Manchester_weather_rx_array.ino, Part 2 of 2
    uint8_t bufidx = 0;
    uint8_t data;
    uint8_t id;
    char buf[30];
    
    void loop() {
      if (man.receiveComplete()) {
        digitalWrite( _ledpin, HIGH );
        if( _databuf[0] == SENSOR_ID )
        {
            uint8_t checksum = _databuf[0] | _databuf[2] | _databuf[3] | _databuf[4] | _databuf[5];
            if( _databuf[1] == checksum )
            {
                float luxval = ( ( _databuf[4] << 8 ) + _databuf[5] ) / 1.2;
                uint16_t dp = luxval;  // 取整數部分
                uint16_t fp = (float)(luxval - dp ) * 100;  // 取小數部分
                sprintf( buf, "t:%2dC\th:%2d%c\tb:%5d.%2d lx\n", 
                        _databuf[2], _databuf[3], '%',
                        dp, fp);
                Serial.print(buf);
            }
            else
            {
                Serial.println( "CHECKSUM ERROR!" );
            }
        }
        man.beginReceiveArray( BUFFER_SIZE, _databuf );
        digitalWrite( _ledpin, LOW );
      }
    }
    
    


    測試結果:

    建議測試步驟如下:

    1. 插上 Arduino 無線接收端的 USB 插頭到電腦,
    2. 再開啟 COM 埠軟體 ( ex. AccessPort 或 SSCOM32E )
    3. 等個幾秒鐘
    4. 將無線發射端的插上電源 ( 如果使用 ASK433MHZ 建議離遠一點測 )

    使用上面的程式碼並將各部份結合在一起,使用 ID 傳送碼方式,傳送端與接收端的輸出結果如下所示 ( 左邊是發射端,右邊是接收端 )
    使用 ID 編碼方式,無線發射與接收端輸出結果2016/09/03 修正接收端溫度與濕度數值相反的問題,感謝網友 addison 的細心提醒 !
    使用 Array 串送編碼方式,傳送端與接收端的輸出結果如下所示 ( 左邊是發射端,右邊是接收端 )
    使用 Array 傳送編碼方式,無線發射與接收端輸出結果

    這些數據只是測試時的結果,實際使用上,像這麼漂亮的接收端輸出是理想中的狀態。要知道!無線通訊是一種不可靠的傳輸,能夠完全接收到發射端所傳送出來的一堆數據,靠的是雙方的通訊協定 ( 像藍牙或是無線網路通訊 ),不要認為簡單的無線通訊方式傳什麼就能收到什麼!

    溫溼度與光照度的資料在例子中,每一次間隔傳送的時間很短。實際上這些數值在環境中不會變化太快,所以每隔幾分鐘甚至更久都是可以接受的。這裡要說明的是,只要能夠讓資料在每次傳送的時候可以讓接收端收到傳送出去的數據,即使多傳送幾次都是可以接收的!因為這些傳送出去的數據都是一樣的,就算是亂槍打鳥一樣,多射幾發就能增加打到的機率 ! 看這篇網頁 "[ Wireless-RF] 使用樹莓派模擬 HT12E 遙控器編碼晶片的編碼格式" 中 "程式架構"的部分,就可以清楚了解剛剛說的意思!


    結論:

    對我來說,無線通訊並不像有線通訊好搞!就算程式都寫好了,有時只是因為硬體上的一些限制或是程式碼的不協調,就會被弄得暈頭轉向的!但是當問題被解決的那一瞬間,其實從中就會得到很多調整或是測試的小細節,所以若是處理的時候遇到問題,多點耐心想想那個細節疏忽了!

    在上面的程式中,發射端每一次取得裝置的資料只會發送一次數據出去,因此容易被外在環境或是硬體影響了傳送與接收的效果,所以可以針對這地方加強,就是多發送幾次!畢竟我們的目的就是讓接收端接收到溫溼度和光強度的資料。

    Machester 函式庫不但適合 Arduino 也適用於 ATtiny 晶片,只要同樣 Arduino IDE 做開發幾乎不需要移植程式碼,直接就可以使用並燒錄在 ATtiny 晶片上,這可以減輕整個項目應用的體積和費用。試想,不需要拿一片 Arduino 用在只需要 6 支接腳以下的應用,因為 ATTiny 45/85 晶片就可以了。Manchester 函示庫適用在 Arduino 板子也適用多種 ATTiny 晶片,為何不學習呢 ?



    << 部落格相關文章 >>

    24 則留言:

    1. 你好,我想請問一下我是直接使用購買後提供的程式碼(沒有array的那個)給aduino,可以成功燒錄,在讀取溫度和濕度的時候也成功(我沒裝感光的),只是接收端的部分一直沒辦法收到訊號,發射端的部分rx會不停閃爍,接收端則是一直沒反應(兩邊在啟動時都有跳出ready的訊息),請問會有可能是哪裡出問題??電路的部分我是照白色底那張電路圖去接,(接收的部分會有一個沒插嗎?!data下面有兩根針),另外我有用電表去測天線端跟輸入輸出訊號端的針是不是會逼逼叫,只有接收的部分有反應,發射的部分顯示不是通路,這樣的結果是正常的嗎??
      抱歉,問題有點多。。。

      回覆刪除
    2. ASK_RX 接收端的接腳跟 RF_RX 的不同,ASK 接收端的接線要用圖面上虛線那一個 !

      回覆刪除
    3. 請問我是使用綠色的那個模組(rf433),所以是用實線的部分吧??

      回覆刪除
      回覆
      1. 如果 RF433 的話是用實線那一個 !

        因為沒有使用光線偵測,所以程式碼的部分需要移除掉 BH1750 這部分,但這部是由您完成,所以我不清楚是否是哪裡出現了問題 ?

        所以若是您有修改過程式碼,那您可以先做實線測試,也就是直接將傳送邊的接腳 TX 與接收編接收接腳 RX 接在一起做測試 ( 兩邊 GND 要接在一起 ),成功了表示您的程式修改正確;如果不成功,那就是您的程式出現問題,您必須先搞好兩邊可以正常溝通,這樣接上無線模組才會正常。

        刪除
      2. 我在程式中沒有做任何修改,會有可能是我沒把感光的部分刪除造成的嗎?

        我等等去試試實線測試,看看能不能傳送,謝謝你~

        刪除
      3. 請問有可能是因為我一端是用arduino nano 另端用uno ,所以才沒辦法傳送訊號嗎??

        刪除
      4. 網頁上面也是使用兩個板子,所以不至於吧!
        總之,最快的確認方法就是直接連接測試看問題出在那裡。

        刪除
      5. 你好,這是我的接線圖,可以麻煩你幫我看一下嗎? 謝謝
        程式的部分我沒有做變動,直接燒入,也有試過把光度的部分註解掉,但是都還是沒有辦法接收到訊號。另外我也有用直接接線的方式,結果也是一樣。
        https://drive.google.com/file/d/0B-cCh4vNm-Z2elM2NnJNblhhSE0/view?usp=sharing

        刪除
      6. 因為線路的部分跟網頁中的不同,所以程式碼的部分必須要移除掉 BH1750 的部分,然後進行直接接線測試直到兩邊都可以正確收到訊號,然後再去接無線模組做測試才會成功。程式碼修改的部分要先完成,並完成直接接線測試。

        Manchester 函示庫裡面有簡單範例可以做傳送與接收的測試,這個可先用來測試無線傳送模組是否正常,把焦點放在程式碼修改上面。

        刪除
    4. 請問如果是要把BH1750套到ESP8266+thinkspeak
      程式碼部分如何修改呢

      回覆刪除
    5. 沒什麼太大不同 ! 將 BH1750 處理的部分取代原本 ESP8266+thingspeak 處理 DHT11 的部分就可以,稍微再修改一下就可以!

      回覆刪除
    6. 我是想將BH1750跟DHT11的資料一起上傳到thingspeak
      請問是需要哪個PART的程式碼呢? 謝謝

      回覆刪除
      回覆
      1. 看懂另一個網頁上面關於 thingspeak 設定的操作,增加一個欄位給 bh1750 用。再將 bh1750 處理亮度的程式碼增加到另一個網頁的程式碼裡面稍作修改就可以了 !
        讀懂這兩個網頁上面的東西,就會很容易!

        刪除
      2. 可以麻煩您幫我看一下程式碼嘛 謝謝!
        #include
        #include "DHT.h"
        #include
        #include


        #define DEBUG
        #define _ledpin 13

        //*-- Hardware Serial
        #define _baudrate 9600

        //*-- Software Serial
        //
        #define _rxpin 2
        #define _txpin 3
        SoftwareSerial debug( _rxpin, _txpin ); // RX, TX

        //*-- DHT11
        #define _dhtpin 8
        #define _dhttype DHT11

        DHT dht11( _dhtpin, _dhttype );
        uint8_t dhtbuf[2];

        //*-- BH1750
        // I2C, SDA = A4
        // I2C, SCL = A5
        #define _luxhbyte 5
        #define _luxlbyte 6
        int BH1750address = 0x23;
        byte buff[2];





        //*-- IoT Information
        #define SSID "****"
        #define PASS "***********"
        #define IP "184.106.153.149" // ThingSpeak IP Address: 184.106.153.149
        // 使用 GET 傳送資料的格式
        // GET /update?key=[THINGSPEAK_KEY]&field1=[data 1]&filed2=[data 2]...;
        String GET = "GET /update?key=U67HEACBFB3T6BTC";

        void setup() {
        Serial.begin( _baudrate );
        debug.begin( _baudrate );

        sendDebug("AT");
        delay(5000);
        if(Serial.find("OK"))
        {
        debug.println("RECEIVED: OK\nData ready to sent!");
        connectWiFi();
        }

        // DHT11
        dht11.begin();
        // BH1750
        Wire.begin();



        pinMode( _ledpin, OUTPUT );
        digitalWrite( _ledpin, LOW );

        }

        void loop() {
        dhtbuf[0] = dht11.readHumidity();
        dhtbuf[1] = dht11.readTemperature();

        // 確認取回的溫溼度數據可用
        if( isnan(dhtbuf[0]) || isnan(dhtbuf[1]) )
        {
        debug.println( "Failed to read form DHT11" );
        }
        else
        {
        digitalWrite( _ledpin, HIGH );
        char buf[3];
        String HH, TT;
        buf[0] = 0x30 + dhtbuf[1] / 10;
        buf[1] = 0x30 + dhtbuf[1] % 10;
        TT = (String(buf)).substring( 0, 2 );
        buf[0] = 0x30 + dhtbuf[0] / 10;
        buf[1] = 0x30 + dhtbuf[0] % 10;
        HH = (String(buf)).substring( 0, 2 );

        updateDHT11( TT, HH );
        #ifdef DEBUG
        debug.print("Humidity: ");
        debug.print( HH );
        debug.print(" %\t");
        debug.print("Temperature: ");
        debug.print( TT );
        debug.println(" *C\t");
        #endif
        digitalWrite( _ledpin, LOW );
        }
        //--------------------------------------------------------------------------------
        //BH1750
        int i;
        uint16_t val = 0;
        BH1750_Init(BH1750address);
        delay(1000);
        if (2 == BH1750_Read(BH1750address))
        {
        val = ((buff[0] << 8) | buff[1]) / 1.2;
        //Serial.println("==================================");
        LuxToEV(val);

        #ifdef DEBUG
        Serial.println(" ");
        Serial.print(val, DEC);
        Serial.println(" lux");
        Serial.print("==================================");
        #endif

        // LuxToShutterSpeed(val);
        }
        //----------------------------------------------------------------------------
        delay(60000); // 60 second
        }

        void bh1750_init( int address )
        {
        Wire.beginTransmission( address );
        Wire.write( 0x01 ); // Power on
        Wire.endTransmission();
        Wire.beginTransmission( address );
        Wire.write( 0x10 ); // 1 lx resolution, 120ms
        Wire.endTransmission();
        }

        int bh1750_read( int address )
        {
        int i = 0;
        Wire.beginTransmission( address );
        Wire.requestFrom( address, 2 );
        while( Wire.available() )
        {
        buff[i] = Wire.read(); // receive one byte
        i++;
        }
        Wire.endTransmission();
        return i;
        }

        刪除
      3. void updateDHT11( String T, String H )

        {
        // 設定 ESP8266 作為 Client 端
        String cmd = "AT+CIPSTART=\"TCP\",\"";
        cmd += IP;
        cmd += "\",80";
        sendDebug(cmd);
        delay(2000);
        if( Serial.find( "Error" ) )
        {
        debug.print( "RECEIVED: Error\nExit1" );
        return;
        }

        cmd = GET + "&field1=" + T + "&field2=" + H +"&fiel31=" + i +"\r\n";
        Serial.print( "AT+CIPSEND=" );
        Serial.println( cmd.length() );
        if(Serial.find( ">" ) )
        {
        debug.print(">");
        debug.print(cmd);
        Serial.print(cmd);
        }
        else
        {
        sendDebug( "AT+CIPCLOSE" );
        }
        if( Serial.find("OK") )
        {
        debug.println( "RECEIVED: OK" );
        }
        else
        {
        debug.println( "RECEIVED: Error\nExit2" );
        }
        }

        void sendDebug(String cmd)
        {
        debug.print("SEND: ");
        debug.println(cmd);
        Serial.println(cmd);
        }

        boolean connectWiFi()
        {
        Serial.println("AT+CWMODE=1");
        delay(2000);
        String cmd="AT+CWJAP=\"";
        cmd+=SSID;
        cmd+="\",\"";
        cmd+=PASS;
        cmd+="\"";
        sendDebug(cmd);
        delay(5000);
        if(Serial.find("OK"))
        {
        debug.println("RECEIVED: OK");
        return true;
        }
        else
        {
        debug.println("RECEIVED: Error");
        return false;
        }

        cmd = "AT+CIPMUX=0";
        sendDebug( cmd );
        if( Serial.find( "Error") )
        {
        debug.print( "RECEIVED: Error" );
        return false;
        }
        }

        編譯一直卡在BH1750_Init(BH1750address);

        刪除
      4. 你的編譯錯誤輸出是什麼 ?
        如果不知道怎麼用,步驟:點選 File/Preferences,在開啟的視窗,點選 Show verbose output during: [v]compilation 和 [v]upload 兩個選項,關閉視窗後重新編譯就會出現了!
        --------------
        另外,如果你是使用 Arduino + ESP8266 來做這個測試,程式一開始的 Serial.begin(_baudrate) 有可能是錯誤的速率設定,因為原程式是使用藍色板子 9600 bps;現在是用黑板,所以要用 115200 bps,這裡也要順便改 !
        -------------

        刪除
      5. 之前的輸出是BH1750_Init not in this scope
        我把處理BH1750的程式改成:
        int i;
        uint16_t val=0;
        BH1750_Init(BH1750address);
        if(2==BH1750_Read(BH1750address))
        {
        val=((buff[0]<<8)|buff[1])/1.2;
        debug.print(val,DEC);
        debug.println("[lx]");

        int BH1750_Read(int address) //
        {
        int i=0;
        Wire.beginTransmission(address);
        Wire.requestFrom(address, 2);
        while(Wire.available()) //
        {
        buff[i] = Wire.read(); // receive one byte
        i++;
        }
        Wire.endTransmission();
        return i;
        }

        void BH1750_Init(int address)
        {
        Wire.beginTransmission(address);
        Wire.write(0x10);//1lx reolution 120ms
        Wire.endTransmission();
        }

        已經可以在序列阜視窗看到亮度的值
        但不知道該怎麼把亮度值 (i) 加到(cmd = GET + "&field1=" + T + "&field2=" + H +"&fiel31="+"\r\n";)中

        刪除
      6. 你在 Thingspeak 建立了三個資料欄位了嗎?
        如果可以取出所有需要的數值,接下來就是要在 thingspeak 建立好要上傳的欄位;利用手動輸入網址的方法手動輸入三個數值,如果可以正常輸入並且看到三個數值出現在 thingspeak 就代表 OK。最後只要再增加第三個欄位進去 cmd = ..... 就可以了!
        這設定的方式到另一個網頁看就有詳細的說明。

        刪除
      7. THINGSPEAK已經建立第三個欄位給BH1750囉
        手動輸入也可以了
        請問新增第三個欄位到cmd補上("&field3=" + i)之外還需要加什麼東西嗎

        刪除
      8. 只要的程式碼就是這裡要加入第三個欄位資料
        cmd = GET + "&field1=" + T + "&field2=" + H + "&field3=" + BR +\r\n";
        BR 就是你的亮度值
        -----------
        原本 UpdateDHT11 是更新 DHT11 溫度與濕度的函式,因為加入了第三個欄位,所以要另外再加入一個參數。
        剩下的就是細部修改整個程式使得上面的變更可以通過編譯與上傳 !

        刪除
      9. 加了參數 更改一些設定之後溫濕度跟亮度都可以上傳到THINGSPEAK了
        感謝您耐心的回覆 網站讓我受益良多 謝謝^^

        刪除
    7. 你好

      接收端 humi % 與 temp c 兩個值好相對調了,怎溫度都在5~60度,而顯度卻在2~30%之間,跟發射端的溫度與濕度數值跟接收端相反了,你網頁上輸出圖好像也是如此

      另外請教下為何收接端大多只能接收到 humi % 這欄數值,偶而能接收到 temp c欄數值,而光照完全讀不到呢,完全按照文章程式碼上傳

      回覆刪除
      回覆
      1. ID 方法的輸出畫面的確有問題,我再找時間重新輸出一次畫面,謝謝你的提醒!
        你可以開啟兩邊的 DEBUG 選項看實際的輸出去做比對就知道問題出在哪裡。光照值讀不到檢查一下 I2C 的接線和其位址是否需要修改(如果用不一樣的光照模組,確認它的 I2C 位址)。

        刪除

    留言屬名為"Unknown"或"不明"的用戶,大多這樣的留言都會直接被刪除掉,不會得到任何回覆!

    發問問題,請描述清楚你(妳)的問題,別人回答前不會想去 "猜" 問題是什麼?

    不知道怎麼發問,請看 [公告] 部落格提問須知 - 如何問問題 !