Raspberry Pi Zero Gateway with local Sensor



  • After finding little documentation on how to do this and spending most of a day hitting my head against a wall, I finally have this working. I was able to get my Raspberry Pi Zero Gateway to also provide sensor data for an si7021 connected locally via i2c. There's likely an easier way to do this, but this worked for me.

    Proof of Concept

    The MySensors code already has the setup, presentation, and loop function blocks in examples_linux/mysgw.cpp. Adding some test code shows it's possible for the gateway to present and send sensor data. Using the code below, compile the gateway normally, and a new sensor Counter will show up that repeatedly counts to 10.

    #include <iostream>
    #include <cstdio>
    #include <unistd.h>
    
    #define MY_GATEWAY_MAX_CLIENTS 10
    
    #include <MySensors.h>
    
    #define ARDUINO 100
    // This space is intended to be used to include arduino libraries
    #undef ARDUINO
    
    #define CHILD_ID_TEST       50
    static MyMessage msgTest(CHILD_ID_TEST, V_CUSTOM);
    
    void setup() {
    }
    
    void presentation() {
    	present(CHILD_ID_TEST, S_DUST, "Counter");
    }
    
    int val = 0;
    void loop() {
    	send(msgTest.set(val++));
    	if (val > 10) {
    		val = 0;
    	}
    	delay(5000);
    }
    

    At a high level, that's it. Though I quickly ran into issues trying to use external libraries...

    Using the si7021

    Skipping a whole bunch of trial and error, here's what worked.

    High Level Steps

    • Attach the si7021 sensor to the Raspberry Pi using i2c
    • Install the Wiring Pi library
    • Add sensor code to the mysgw.cpp file
    • Copy the si7021 library files to examples_linux
    • Modify the MySensors Makefile to include the additional libraries
    • Compile!

    Detail

    I'm going to skip over the first two items above, there are plenty of guides online.

    This is the complete mysgw.cpp file:

    #include <iostream>
    #include <cstdio>
    #include <unistd.h>
    #include "si7021.h"
    
    #define MY_GATEWAY_MAX_CLIENTS 10
    
    #include <MySensors.h>
    
    #define ARDUINO 100
    // This space is intended to be used to include arduino libraries
    #undef ARDUINO
    
    si7021 env;
    #define CHILD_ID_TEMP       10
    #define CHILD_ID_RH         11
    static MyMessage msgTemp(CHILD_ID_TEMP, V_TEMP);
    static MyMessage msgRH(CHILD_ID_RH, V_HUM);
    
    void setup() {
    }
    
    void presentation() {
    	present(CHILD_ID_RH, S_HUM,   "Humidity");
      	present(CHILD_ID_TEMP, S_TEMP, "Temperature");
    }
    
    void loop() {
      	env.open();
     	send(msgTemp.set(env.getTemp(), 2));
    	send(msgRH.set(env.getRH(), 2));
      	env.fdclose();
    	delay(1000);
    }
    

    I cobbled together an si7021 library based on several around the Internet (heavily borrowed from here, here). The challenge here is that we're compiling for Linux on Raspberry Pi, not an Arduino, so libraries don't behave equally.

    The two si7021 library files (si7021.cpp, si7021.h) can be found at the bottom of this post and need to go in examples_linux. There's certainly a better place for these, but I was too tired to care.

    The Makefile needs to know about this new file: add examples_linux/si7021.cpp to line 24. It should now read:

    GATEWAY_CPP_SOURCES=$(wildcard hal/architecture/Linux/drivers/core/*.cpp) examples_linux/mysgw.cpp examples_linux/si7021.cpp
    

    Run ./configure with the usual flags and include --extra-ldflags="-lwiringPi"

    And finally: make. The resulting binary should act as expected as the gateway, and also send data from the sensor.

    Library

    si7021.h

    #ifndef _SI7021
    #define _SI7021
    
    class si7021 {
        public:
            si7021();
            float getTemp();
            float getRH();
            void open();
            void fdclose();
        private:
            int _fd;
    };
    
    #endif
    

    si7021.cpp

    #include <stdio.h>
    #include <stdlib.h>
    #include <stdint.h>
    #include <unistd.h>
    #include <time.h>
    #include <wiringPiI2C.h>
    #include <wiringPi.h>
    #include "si7021.h"
    
    #define measureTemp 0xF3
    #define measureRH   0xF5
    #define SI7021ADDR  0x40
    
    si7021::si7021() {
    
    }
    
    void si7021::open() {
        _fd = wiringPiI2CSetup(SI7021ADDR);
    }
    
    void si7021::fdclose() {
        close(_fd);
    }
    
    float si7021::getTemp() {
        float cTemp, fTemp;
        uint8_t data [5];
    
        if (wiringPiI2CWrite (_fd, measureTemp) < 0)
            return (-1);    
        while ((read (_fd, data, 3) < 0))
            delay (10) ;
    
        unsigned int msb = data[0];
        unsigned int lsb = data[1];
        lsb &= 0xFC;
        unsigned int meas = msb << 8 | lsb;
    
        cTemp = (175.72 * meas / 65536) - 46.85;
        fTemp = (cTemp * 9 / 5) + 32;
    
        return (fTemp);
    }
    
    float si7021::getRH() {   
        float humidity;
        uint8_t data [2] ;        
    
         if (wiringPiI2CWrite (_fd, measureRH) < 0)
            return(-1);
        while ((read (_fd, data, 3) < 0))
            delay (10) ;
    
        humidity = (((data[0] * 256 + data[1]) * 125.0) / 65536.0) - 6;
        
        if(humidity >=100.0) 
            humidity =99.9;
    
        return (humidity);
    }
    


  • Bah! Well that didn't last long. I'm noticing significant degradation in communication between the gateway and other sensors (communicating via rfm69hcw) when presenting and sending sensor data from the gateway as described above. I haven't had a chance to dig in, but fair warning I may have counted my chickens before they hatched.


  • Hero Member

    @rex I've no experience with the Raspberry code, but my guess is, the delay() function is a problem. Replace it with a non blocking code.



  • @TimO Nice! Thank you for the suggestion. It seems to be performing much better using wait.


Log in to reply
 

Suggested Topics

147
Online

9.6k
Users

10.2k
Topics

106.1k
Posts