Raspberry Pi 3とBME280,RPR-0521RSで百葉箱を製作

#初めに
お久しぶりです、もしくは初めまして、「Radian_N」（ラジアン）と申します。
今回の記事は、[先日の記事](https://qiita.com/Radian_N/items/4ced68cee55032964ae2)でも紹介しました、Raspberry Pi 3を用いて製作した温湿度計付き時計を応用して、小学校にも置いてある百葉箱を製作しました。
ここでは、BME280で温度・湿度・気圧の計測をするだけでなく、新たにRPR0521-RSを用いて照度・近接検知の計測も行い、CSVファイルに計測データを出力するシステムについて紹介します。
完成したものはこちら

https://twitter.com/RadianN_kswg/status/1427194019197587458


***
#ハードウェア（動作環境構築）
##使用パーツ
* Raspberry Pi 3 model B+　（この記事では初期設定方法については触れません）
* [スイッチサイエンス　BME280搭載 温湿度・気圧センサモジュール](https://www.switch-science.com/catalog/2236/)
* [Conta™ 照度・近接一体型センサモジュール RPR-0521RS搭載](https://www.switch-science.com/catalog/2770/)
* ACアダプタ 5V2A　（5V3Aほどだと望ましいです）

##はんだ付けや初期設定について
BME280搭載モジュールの接続と計測方法は、 [こちら][BME280参照] を参照および引用しました。
Raspberry Pi 3用にRPR-0521RSの接続方法は、[こちら][RPR-0521RS参照-接続]を参照し、計測方法は[こちらのArduinoのライブラリ][RPR-0521RS参照-Arduinoライブラリ]を解読しました。

また、使用したBME280搭載モジュールについてですが、はんだ付け済みのものが在庫切れする傾向にあるようです。
はんだ付けなしのものは十分に在庫があるようなので、そちらを代用し併せてはんだ付け作業が必要になることも念頭に置いておきましょう。

#ソフトウェア（Cソースコードなど）
##ソースコード
参考にしたサイトおよびソースコード引用先など

* [BME280の計測値読み取り][BME280参照]
* [RPR-0521RSの接続方法][RPR-0521RS参照-接続]
* [RPR-0521RSの計測用][RPR-0521RS参照-Arduinoライブラリ]　など

```c:c-source
// https://tomosoft.jp/design/?p=6963
// tomosoft.jp/design/?p=10477#pigpioBME280
// https://www.denshi.club/pc/raspi/5raspberry-pi-zeroiot41.html
// https://www.mm2d.net/main/prog/c/time-01.html

#include <stdio.h>
#include <unistd.h>
#include <pigpio.h>

#define BME280_ADDRESS 0x76
#define RPR0521RS_ADDRESS 0x38
#define RPR0521RS_CONFIG 0xc6

#include <stdlib.h>
#include <time.h>

unsigned long int hum_raw,temp_raw,pres_raw;
signed long int t_fine;

unsigned int dig_T1;
int dig_T2;
int dig_T3;
unsigned int dig_P1;
int dig_P2;
int dig_P3;
int dig_P4;
int dig_P5;
int dig_P6;
int dig_P7;
int dig_P8;
int dig_P9;
char  dig_H1;
int dig_H2;
char  dig_H3;
int dig_H4;
int dig_H5;
char  dig_H6;

static int dev;
static int dev_RPR;

//char* filepath = "./spybuster_log.txt";
char* filename = "./spybuster_log(";
char* filesfx  = ").csv";
FILE *fp;

static int init_dev(void)
{
	if (gpioInitialise() < 0)
	{
		return 1;
	}
	
	dev = i2cOpen(1, BME280_ADDRESS, 0);
	dev_RPR = i2cOpen(1, RPR0521RS_ADDRESS, 0);
	if (dev < 0 || dev_RPR < 0)
	{
		return 1;
	}
    return 0;
}
/* BME280計測用 */
void getRegisters(char address, int numData, unsigned char *data) {
	i2cReadI2CBlockData (dev, address, data, numData);
}

void readTrim()
{
	unsigned char data[32],i=0;

	getRegisters(0x88, 24, &data[0]);
	i+=24;
	
	getRegisters(0xA1, 1, &data[i]);
	i+=1;
	
	getRegisters(0xE1, 7, &data[i]);
	i+=7;
	
	dig_T1 = (data[1] << 8) | data[0];
	dig_T2 = (data[3] << 8) | data[2];
	dig_T3 = (data[5] << 8) | data[4];
	dig_P1 = (data[7] << 8) | data[6];
	dig_P2 = (data[9] << 8) | data[8];
	dig_P3 = (data[11]<< 8) | data[10];
	dig_P4 = (data[13]<< 8) | data[12];
	dig_P5 = (data[15]<< 8) | data[14];
	dig_P6 = (data[17]<< 8) | data[16];
	dig_P7 = (data[19]<< 8) | data[18];
	dig_P8 = (data[21]<< 8) | data[20];
	dig_P9 = (data[23]<< 8) | data[22];
	dig_H1 = data[24];
	dig_H2 = (data[26]<< 8) | data[25];
	dig_H3 = data[27];
	dig_H4 = (data[28]<< 4) | (0x0F & data[29]);
	dig_H5 = (data[30] << 4) | ((data[29] >> 4) & 0x0F);
	dig_H6 = data[31];
}

void writeReg(unsigned char address, unsigned char data) {
	char adr;
	int i;
	
	adr = address;
	i2cWriteByteData (dev, adr, data);
	// printf("adr:%x data:%x\n",adr, data);
}

void readData()
{
	int i = 0;
	unsigned char data[8];
	
	getRegisters(0xF7, 8, &data[0]);
	
	pres_raw = data[0];
	pres_raw = (pres_raw<<8) | data[1];
	pres_raw = (pres_raw<<4) | (data[2] >> 4);
	
	temp_raw = data[3];
	temp_raw = (temp_raw<<8) | data[4];
	temp_raw = (temp_raw<<4) | (data[5] >> 4);
	
	hum_raw  = data[6];
	hum_raw  = (hum_raw << 8) | data[7];
	// printf("TEMP :%x  DegC  PRESS :%x  hPa  HUM :%x \n",temp_raw,pres_raw,hum_raw);
}

signed long int calibration_T(signed long int adc_T)
{
	signed long int var1, var2, T;
	
	var1 = ((((adc_T >> 3) - ((signed long int)dig_T1<<1))) * ((signed long int)dig_T2)) >> 11;
	var2 = (((((adc_T >> 4) - ((signed long int)dig_T1)) * ((adc_T>>4) - ((signed long int)dig_T1))) >> 12) * ((signed long int)dig_T3)) >> 14;
	
	t_fine = var1 + var2;
	T = (t_fine * 5 + 128) >> 8;
	return T;
}

unsigned long int calibration_P(signed long int adc_P)
{
	signed long int var1, var2;
	unsigned long int P;
	
	var1 = (((signed long int)t_fine)>>1) - (signed long int)64000;
	var2 = (((var1>>2) * (var1>>2)) >> 11) * ((signed long int)dig_P6);
	var2 = var2 + ((var1*((signed long int)dig_P5))<<1);
	var2 = (var2>>2)+(((signed long int)dig_P4)<<16);
	var1 = (((dig_P3 * (((var1>>2)*(var1>>2)) >> 13)) >>3) + ((((signed long int)dig_P2) * var1)>>1))>>18;
	var1 = ((((32768+var1))*((signed long int)dig_P1))>>15);
	if (var1 == 0)
	{
		return 0;
	}
	P = (((unsigned long int)(((signed long int)1048576)-adc_P)-(var2>>12)))*3125;
	if(P<0x80000000)
	{
		P = (P << 1) / ((unsigned long int) var1);
	}
	else
	{
		P = (P / (unsigned long int)var1) * 2;
	}
	var1 = (((signed long int)dig_P9) * ((signed long int)(((P>>3) * (P>>3))>>13)))>>12;
	var2 = (((signed long int)(P>>2)) * ((signed long int)dig_P8))>>13;
	P = (unsigned long int)((signed long int)P + ((var1 + var2 + dig_P7) >> 4));
	return P;
}

unsigned long int calibration_H(signed long int adc_H)
{
	signed long int v_x1;
	
	v_x1 = (t_fine - ((signed long int)76800));
	v_x1 = (((((adc_H << 14) -(((signed long int)dig_H4) << 20) - (((signed long int)dig_H5) * v_x1)) +
		((signed long int)16384)) >> 15) * (((((((v_x1 * ((signed long int)dig_H6)) >> 10) * 
		(((v_x1 * ((signed long int)dig_H3)) >> 11) + ((signed long int) 32768))) >> 10) + (( signed long int)2097152)) * 
		((signed long int) dig_H2) + 8192) >> 14));
	v_x1 = (v_x1 - (((((v_x1 >> 15) * (v_x1 >> 15)) >> 7) * ((signed long int)dig_H1)) >> 4));
	v_x1 = (v_x1 < 0 ? 0 : v_x1);
	v_x1 = (v_x1 > 419430400 ? 419430400 : v_x1);
	return (unsigned long int)(v_x1 >> 12);
}

/* RPR-0521RS計測用 */
void get_temppresshumval(double *temp, double *press, double *hum){
	signed long int temp_cal;
	unsigned long int press_cal,hum_cal;
	readData();
	
	temp_cal = calibration_T(temp_raw);
	press_cal = calibration_P(pres_raw);
	hum_cal = calibration_H(hum_raw);
	*temp = (double)temp_cal / 100.0;
	*press = (double)press_cal / 100.0;
	*hum = (double)hum_cal / 1024.0;
}

void configulate_RPR()
{
	unsigned char osrs_t = 1; // Temperature oversampling x 1
	unsigned char osrs_p = 1; // Pressure oversampling x 1
	unsigned char osrs_h = 1; //　Humidity oversampling x 1
	unsigned char mode = 3;   //　Normal mode
	unsigned char t_sb = 5;   //　Tstandby 1000ms
	unsigned char filter = 0; //　Filter off
	unsigned char spi3w_en = 0;	//　3-wire SPI Disable
	
	unsigned char ctrl_meas_reg = (osrs_t << 5) | (osrs_p << 2) | mode;
	unsigned char config_reg    = (t_sb << 5) | (filter << 2) | spi3w_en;
	unsigned char ctrl_hum_reg  = osrs_h;
	writeReg(0xF2,ctrl_hum_reg);
	writeReg(0xF4,ctrl_meas_reg);
	writeReg(0xF5,config_reg);
	i2cWriteByteData (dev_RPR, 0x40, 0x80); // reset
	i2cWriteByteData (dev_RPR, 0x41, RPR0521RS_CONFIG);
}

double convert_lx(unsigned int *data)
{
	double lx;
	double d0, d1, d1_d0;

	d0 = (double)data[0];
	d1 = (double)data[1];

	if (d0 == 0) {
		lx = 0;
		return (lx);
	}

	d1_d0 = d1 / d0;

	if (d1_d0 < 0.595) {
		lx = (1.682 * d0 - 1.877 * d1);
	} else if (d1_d0 < 1.015) {
		lx = (0.644 * d0 - 0.132 * d1);
	} else if (d1_d0 < 1.352) {
		lx = (0.756 * d0 - 0.243 * d1);
	} else if (d1_d0 < 3.053) {
		lx = (0.766 * d0 - 0.25 * d1);
	} else {
		lx = 0;
	}

	return (lx);
}

void get_psalsval(unsigned int *ps, double *als) {
	unsigned char data_RPR[6];
	i2cReadI2CBlockData (dev_RPR, 0x44, &data_RPR[0], 6);

	unsigned int rawps = ((unsigned int)data_RPR[1]<<8)|(unsigned int)data_RPR[0];
	unsigned int rawals[2] = {
		((unsigned int)data_RPR[3]<<8)|(unsigned int)data_RPR[2],
		((unsigned int)data_RPR[5]<<8)|(unsigned int)data_RPR[4]
	};
	*ps = rawps;
	*als = convert_lx(rawals);
}

/* 出力ファイル操作用 */
void openlogfile(char *ts, time_t t, FILE *fp,
	char *path, char *filename, char *filesfx) {
	strftime(ts, sizeof(ts), "%Y%m%d", localtime(&t));
	sprintf(path, "%s%s%s", filename, ts, filesfx);
	fp = fopen(path, "w");
}

void fprinttimestump(char *ts, time_t t, FILE *fp) {
	strftime(ts, sizeof(ts), "%Y/%m/%d %H:%M:%S", localtime(&t));
	fprintf(fp, "%s,", ts);
	fprintf(fp, "TMP [\'C],HUM  [%%],PRS[kPa],DST[cnt],LUM [lx],NEAR FLAG");
}

void printallvals(FILE *fp,
	double temp, double press, double hum, unsigned int ps, double lum){
		printf("TEMP:%6.2f[\'C] HUM:%6.2f[%%] PRESS:%7.1f[hPa]\n",temp,hum,press);
		printf("DIST:%4d[count] LUM:%7.2f[lx]\n\n", ps, lum);
		
		fprintf(fp, "%8.3f,%8.3f,%8.2f,",temp,hum,press);
		fprintf(fp, "%8d,%8.3f,", ps, lum);
}

void loggingdist2file(FILE *fp, unsigned int ps) {
	if(ps >= 10) {
		if(ps >= 1000) fprintf(fp, "<REACHED>");
		else if(ps >= 100) fprintf(fp, "<NEAR>");
		else fprintf(fp, "<CLOSE>");
		usleep(1000000);
	}
}

/* メイン関数 */
void main(int argc, char **argv)
{
	double temp_act = 0.0, press_act = 0.0,hum_act=0.0;
	unsigned int ps_act = 0;
	double lum_act = 0.0;
	
	if(init_dev()==1)  return;
	
	readTrim();	// BME280をセットアップ
	configulate_RPR();	// RPR-0521RSをセットアップ
	
	/* 出力ファイル操作の準備 */
	//fp = fopen(filepath, "w");
	
	//fprintf(fp, "%s", ctime(&t));
	time_t t0 = time(NULL);
	time_t t = t0;
	char ts[64], filepathall[128];
	
	strftime(ts, sizeof(ts), "%Y%m%d", localtime(&t));
	sprintf(filepathall, "%s%s%s", filename, ts, filesfx);
	fp = fopen(filepathall, "w");
	printf("\nOpen File : %s\n\n", filepathall);
	
	/* 出力開始 */
	strftime(ts, sizeof(ts), "%Y/%m/%d %H:%M:%S", localtime(&t));
	fprintf(fp, "%s,", ts);
	fprintf(fp, "TMP [\'C],HUM  [%%],PRS[kPa],DST[cnt],LUM [lx],NEAR FLAG");
	
	while(1)
	{
		get_temppresshumval(&temp_act, &press_act, &hum_act);	// BME280で計測
		get_psalsval(&ps_act, &lum_act);	// RPR-0521RSで計測
		
		/* 24時間経過したらファイルを新規作成 */
		if(time(NULL) >= t0 + (time_t)86400) {
			fclose(fp);
			
			t0 = time(NULL);
			strftime(ts, sizeof(ts), "%Y%m%d", localtime(&t));
			sprintf(filepathall, "%s%s%s", filename, ts, filesfx);
			fp = fopen(filepathall, "w");
			printf("\nReopen File : %s\n\n", filepathall);
			
			strftime(ts, sizeof(ts), "%Y/%m/%d %H:%M:%S", localtime(&t));
			fprintf(fp, "%s,", ts);
			fprintf(fp, "TMP [\'C],HUM  [%%],PRS[kPa],DST[cnt],LUM [lx],NEAR FLAG");
		}
		
		/* 1秒経過するごとにデータを出力 */
		if(time(NULL) > t) {
			t = time(NULL);
			strftime(ts, sizeof(ts), "%Y/%m/%d %H:%M:%S", localtime(&t));
			fprintf(fp, "\n%s,", ts);
			printallvals(fp,temp_act,press_act,hum_act,ps_act,lum_act);
		}
		loggingdist2file(fp, ps_act);
	}
	
	fclose(fp);
	gpioTerminate();
}
```
***
#あとがき
今回はBME280で温度・湿度・気圧の計測、RPR0521-RSで照度などの計測を行い、百葉箱のような観測システムを製作しました。
I2Cセンサを用いて計測する方法についてもしこの投稿で少しでも参考になる人がいればいいなと思っております。

##参考・引用URLまとめ

* [完成品][]
* [BME280との接続][BME280参照]
* [RPR-0521RSとの接続][RPR-0521RS参照-接続]
* [RPR-0521RSのArduinoライブラリ][RPR-0521RS参照-Arduinoライブラリ]　など

[完成品]:https://twitter.com/RadianN_kswg/status/1427194019197587458 
[BME280参照]:https://tomosoft.jp/design/?p=6963
[RPR-0521RS参照-接続]:https://www.denshi.club/pc/raspi/5raspberry-pi-zeroiot41.html
[RPR-0521RS参照-Arduinoライブラリ]:https://github.com/SWITCHSCIENCE/samplecodes/blob/master/Conta/2770_RPR0521RS_module/RPR-0521RS.zip