Edited at
SORACOMDay 23

親子で楽しむ SORACOM Harvest

Crystal Dew Worldのひよひよです。普段は、ストレージ関連のソフトウェア(CrystalDiskMark、CrystalDiskInfo)を開発しておりますが、今年からIoTにチャレンジしています。


センサーボックス(温度、湿度、電波強度、位置情報)

Grove IoTスターターキット for SORACOM を使ってセンサーボックス(温度、湿度、電波強度、位置情報)を開発しました。子供たちと家中の温度や電波強度の測定をしたのですが、場所や外的要因によって大きく変化するグラフを眺めながらとても喜んでおりました。モバイルバッテリーさえあれば、どこにでも持ち運べるので旅行のお供にも使えそうです。

Grove接続のセンサーは、半田ごてやブレッドボードでの複雑な配線が不要でお手軽にIoTにチャレンジできるのが良いですね。


実装内容


ベーススケッチ

soracom-harvest


追加機能


  • 電波強度(RSSI)取得


一部抜粋

  int rssi;

rssi = Wio.GetReceivedSignalStrength();


  • 位置情報(GPS)取得

TinyGPS++を使用


一部抜粋

  while(GpsSerial->available()){

if(gps.encode(GpsSerial->read())){
if(gps.location.isUpdated()){
sat = gps.satellites.value();
lat = gps.location.lat();
lng = gps.location.lng();
SerialUSB.println("");
SerialUSB.print("SAT= "); SerialUSB.println(gps.satellites.value());
SerialUSB.print("LAT= "); SerialUSB.println(gps.location.lat(), 6);
SerialUSB.print("LON= "); SerialUSB.println(gps.location.lng(), 6);
}
}
}

※soracom-harvestスケッチをカスタマイズして作った今回のスケッチ全体は最後に掲載しています。


製作物


写真1. 子供たちと楽しく遊びました!

soracom_grove.jpg

ドライヤーで温めると一気に温度が上がり、緩やかに温度が下がって、すぐにグラフに反映される。こういったわかりやすい単純な動きが子供の興味を引くのかもしれません。


写真2. センサーボックスで収集したデータをSORACOM Harvestでグラフ化

soracom_rssi.jpg


写真3. センサーボックスで収集したデータをSORACOM Harvestで地図上にプロット

soracom_maps-1.jpg


写真4. 専用ケースに格納

soracom_gps.jpg


工夫したポイント


  • GPSがロックしたらLEDの色が変わる!

GPSのロック状況が視覚的にわかるので、センサーボックスだけを持ち出して測定する際にとても便利でした。まぁ、当たり前の工夫ですけど。


一部抜粋

  if(sat >= 4){ // GPS衛星を4つ以上捕捉

Wio.LedSetRGB(0, 0, 255); // WioLTEのLEDを青に変更
}else{
Wio.LedSetRGB(255, 0, 0); // WioLTEのLEDを赤に変更
}


写真5. GPS衛星捕捉中(LED:赤色)

gps_ng_red.jpg


写真6. GPS衛星捕捉完了(LED:青色)

gps_ok_blue.jpg


まとめ

Grove IoT スターターキット for SORACOM を使って、センサーボックス(温度、湿度、電波強度、位置情報)を開発しました。SORACOM Harvestを使えば、収集したセンサー情報を簡単に可視化できるのでとても楽しいです。子供たちも大喜び間違いなし!?


参考サイト


スケッチ全体


soracom-harvest-sensor-box.ino

#include <WioLTEforArduino.h>

#include <stdio.h>
#include <TinyGPS++.h>

#define INTERVAL (60000)
#define RECEIVE_TIMEOUT (10000)
#define SENSOR_PIN (WIOLTE_D38)

TinyGPSPlus gps;
WioLTE Wio;

// for GPS module
HardwareSerial* GpsSerial;
char GpsDataLength;

void setup() {
delay(200);

SerialUSB.println("");
SerialUSB.println("--- START ---------------------------------------------------");

SerialUSB.println("### I/O Initialize.");
Wio.Init();

// for GPS module
GpsBegin(&Serial);

SerialUSB.println("### Power supply ON.");
Wio.PowerSupplyLTE(true);
delay(500);
Wio.PowerSupplyGrove(true);
delay(500);

SerialUSB.println("### Turn on or reset.");
if (!Wio.TurnOnOrReset()) {
SerialUSB.println("### ERROR! ###");
return;
}

SerialUSB.println("### Connecting to \"soracom.io\".");
if (!Wio.Activate("soracom.io", "sora", "sora")) {
SerialUSB.println("### ERROR! ###");
return;
}

TemperatureAndHumidityBegin(SENSOR_PIN);

SerialUSB.println("### Setup completed.");
}

void loop() {
char data[100];
float temp;
float humi;
int rssi;
int sat;
double lat, lng;

if (!TemperatureAndHumidityRead(&temp, &humi)) {
SerialUSB.println("ERROR!");
goto err;
}

SerialUSB.print("Current humidity = ");
SerialUSB.print(humi);
SerialUSB.print("% ");
SerialUSB.print("temperature = ");
SerialUSB.print(temp);
SerialUSB.println("C");

rssi = Wio.GetReceivedSignalStrength();
while(GpsSerial->available()){
if(gps.encode(GpsSerial->read())){
if(gps.location.isUpdated()){
sat = gps.satellites.value();
lat = gps.location.lat();
lng = gps.location.lng();
SerialUSB.println("");
SerialUSB.print("SAT= "); SerialUSB.println(gps.satellites.value());
SerialUSB.print("LAT= "); SerialUSB.println(gps.location.lat(), 6);
SerialUSB.print("LON= "); SerialUSB.println(gps.location.lng(), 6);
}
}
}
if(sat >= 4){
Wio.LedSetRGB(0, 0, 255);
}else{
Wio.LedSetRGB(255, 0, 0);
}

sprintf(data,"{\"temp\":%.1f,\"humi\":%.1f, \"rssi\":%d, \"lat\":%f, \"lng\":%f}", temp, humi, rssi, lat, lng);

SerialUSB.println("### Open.");
int connectId;
connectId = Wio.SocketOpen("harvest.soracom.io", 8514, WIOLTE_UDP);
if (connectId < 0) {
SerialUSB.println("### ERROR! ###");
goto err;
}

SerialUSB.println("### Send.");
SerialUSB.print("Send:");
SerialUSB.print(data);
SerialUSB.println("");
if (!Wio.SocketSend(connectId, data)) {
SerialUSB.println("### ERROR! ###");
goto err_close;
}

SerialUSB.println("### Receive.");
int length;
length = Wio.SocketReceive(connectId, data, sizeof (data), RECEIVE_TIMEOUT);
if (length < 0) {
SerialUSB.println("### ERROR! ###");
goto err_close;
}
if (length == 0) {
SerialUSB.println("### RECEIVE TIMEOUT! ###");
goto err_close;
}
SerialUSB.print("Receive:");
SerialUSB.print(data);
SerialUSB.println("");

err_close:
SerialUSB.println("### Close.");
if (!Wio.SocketClose(connectId)) {
SerialUSB.println("### ERROR! ###");
goto err;
}

err:
delay(INTERVAL);
}

////////////////////////////////////////////////////////////////////////////////////////
//

int TemperatureAndHumidityPin;

void TemperatureAndHumidityBegin(int pin)
{
TemperatureAndHumidityPin = pin;
DHT11Init(TemperatureAndHumidityPin);
}

bool TemperatureAndHumidityRead(float* temperature, float* humidity)
{
byte data[5];

DHT11Start(TemperatureAndHumidityPin);
for (int i = 0; i < 5; i++) data[i] = DHT11ReadByte(TemperatureAndHumidityPin);
DHT11Finish(TemperatureAndHumidityPin);

if(!DHT11Check(data, sizeof (data))) return false;
if (data[1] >= 10) return false;
if (data[3] >= 10) return false;

*humidity = (float)data[0] + (float)data[1] / 10.0f;
*temperature = (float)data[2] + (float)data[3] / 10.0f;

return true;
}

void DHT11Init(int pin)
{
digitalWrite(pin, HIGH);
pinMode(pin, OUTPUT);
}

void DHT11Start(int pin)
{
// Host the start of signal
digitalWrite(pin, LOW);
delay(18);

// Pulled up to wait for
pinMode(pin, INPUT);
while (!digitalRead(pin)) ;

// Response signal
while (digitalRead(pin)) ;

// Pulled ready to output
while (!digitalRead(pin)) ;
}

byte DHT11ReadByte(int pin)
{
byte data = 0;

for (int i = 0; i < 8; i++) {
while (digitalRead(pin)) ;

while (!digitalRead(pin)) ;
unsigned long start = micros();

while (digitalRead(pin)) ;
unsigned long finish = micros();

if ((unsigned long)(finish - start) > 50) data |= 1 << (7 - i);
}

return data;
}

void DHT11Finish(int pin)
{
// Releases the bus
while (!digitalRead(pin)) ;
digitalWrite(pin, HIGH);
pinMode(pin, OUTPUT);
}

bool DHT11Check(const byte* data, int dataSize)
{
if (dataSize != 5) return false;

byte sum = 0;
for (int i = 0; i < dataSize - 1; i++) {
sum += data[i];
}

return data[dataSize - 1] == sum;
}

////////////////////////////////////////////////////////////////////////////////////////

void GpsBegin(HardwareSerial* serial)
{
GpsSerial = serial;
GpsSerial->begin(9600);
GpsDataLength = 0;
}