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);
}