LoRa モジュール LRA1 のデータを 「IoT データ可視化サービス Ambient」 へ送信

2022/12/27 : hexToInt() を改良, binToInt() を追加

■概要
 LRA1 評価ボードの温湿度、気圧センサ BME280 のデータをデータ可視化サービス Ambient に送信します。
 送信側 LRA1 では温度、湿度、気圧データを送信。
 受信側 LRA1 でそれを受け取り、温度-湿度から露点温度を計算。WiFi 経由で各データを Ambient に送信します。
 WiFi 接続、露点計算に Seeed Studio XIAO ESP32C3 を使っています。

■データ可視化サービス Ambient

 Ambient については、こちらです。
 


■LRA1 送信側

'' BME280Send.bas 277 Bytes
'' 参照:LRA1  UART/BASIC チュートリアル
Do
	'' BME280 値取得
	Bme T, H, P
'	Print "T=";T
'	Print "H=";H
'	Print "P=";P
	'' LCD に表示
	LClr
	LPrint Form("2",T/10);".";Abs(T)%10;" ";H/10;"%"
	LPos = 64
	LPrint P/10;".";P%10;"hP"
	'' 送信
	Txd = wChr(T);wChr(H);wChr(P) 
	Send
	
	Delay 500
Loop
End

■LRA1 受信側
'' BME280Recv.bas 315 Bytes
'' 参照:LRA1  UART/BASIC チュートリアル
UBaud = 19200

LClr
Do
	'' 受信
	Recv 1000
	If Stat = 10 Then
		'' 温度、湿度、気圧の値を取得
		T = Int16(Rxdw(8))
		H = Rxdw(10)
		P = Rxdw(12)
		
'		Print "T=";T
'		Print "H=";H
'		Print "P=";P

		While UInkey >= 0: Loop ''UART2 受信バッファをクリア
				
		'' UART2 送信(16進4桁 x 3 + CrLf) XAIO ESP32C3 へ送信
		UPrint Form("04X", T); Form("04X", H); Form("04X", P)
'		Print "send:";Form("04X", T); Form("04X", H); Form("04X", P)
				
		'' UART2 露点計算値を受信(16進4桁 + CrLf)
		^ = UGets(100) '' XAIO ESP32C3 から受信
'		Print "len=";^[] '' 受信文字列の長さ(CrLfを含まない)
'		Print ^
		'' LCD に表示
		LClr
		LPrint Form("2",T/10);".";Abs(T)%10;" ";H/10;"%"
		LPos = 64
		''LPrint P/10;".";P%10;"hP"	'' 気圧(未使用)	
		
		If ^[] = 4 Then ''制御文字を含まない文字数
			D = "$";Chr(^[0]);Chr(^[1]);Chr(^[2]);Chr(^[3])
			'' AQM0802A-RN-GBW データシート
			'' https://akizukidenshi.com/download/ds/xiamen/AQM0802.pdf
			LPrint D/10;".";Abs(D)%10;Chr($F2);"CDP" ''="℃DP"
'			Print "DP ";D/10;".";Abs(D)%10
		EndIf
	EndIf
Loop
End

■XIAO ESP32C3 (受信側 LRA1 の UART2 に接続)

 XIAO ESP32C3 のシリアルポートは USB と兼用なので、IDE のシリアルモニタで確認しながらデバッグ・・・
 ということが行えません。
 Arduino UNO、ESP32 等である程度確認してから、ESP32C3 に実装するのが良いみたいです。
/*
 *  温度、湿度から露点温度を計算し、IoT データ可視化サービス Ambient へ送信する
 */

#include "Ambient.h"

WiFiClient client;
Ambient ambient;

const char* ssid = "TP-LINK_****";  //"your ssid";
const char* password = "3641****";  //"your password";

unsigned int channelId = 59***;             //100; // AmbientのチャネルID
const char* writeKey = "9ea686d2********";  //"writeKey"; // ライトキー

String recvStr = "";
unsigned long ticks;

void setup() {
  // LRA1 との通信
  Serial.begin(19200);
  delay(100);

  WiFi.begin(ssid, password);              //  Wi-Fi APに接続
  while (WiFi.status() != WL_CONNECTED) {  //  Wi-Fi AP接続待ち
    delay(100);
  }

  //Serial.print("WiFi connected\r\nIP address: ");
  //Serial.println(WiFi.localIP());

  ambient.begin(channelId, writeKey, &client);  // チャネルIDとライトキーを指定してAmbientの初期化
  
  ticks = millis();  
}

void loop() {
  float temp, humi, dewp;
  int index = 0;
  // UART0 受信 (LRA1から温度、湿度、気圧を受信) 
  if (Serial.available()){
    recvStr = Serial.readStringUntil(0x0A);
    recvStr.trim();
    index = recvStr.length(); 
  }
  if (index >= 12) {
    temp = hexToInt(recvStr.substring(0, 4)) / 10.0; // 温度
    humi = hexToInt(recvStr.substring(4, 8)) / 10.0; // 湿度
    String hpas = recvStr.substring(8, 12); // 気圧(未使用)
    // 露点温度計算
    dewp = dewPointC(temp, humi);
    String DP = String((int)(dewp * 10.0), HEX);
    strDigits(DP, 4);  // 桁合わせ
    Serial.println(DP); // UART0 送信(LRA1へ露点温度を送信)
    // Ambientへのデータ送信(約30秒ごと)
    if ((millis() - ticks) >= 30000) { // 30秒
      ticks = millis();
      // 温度、湿度、露点温度を Ambient に送信する
      ambient.set(1, String(temp).c_str());
      ambient.set(2, String(humi).c_str());
      ambient.set(3, String(dewp).c_str());
      ambient.send();
    }    
  }
}

// 桁そろえ(文字列の前に"0"を追加)
void strDigits(String& str, int digits) {
  int len = str.length();
  if (len < digits) {
    for (int i = len; i < digits; i++) {
      str = "0" + str;
    }
  }
}

// hex ="0D0A" or "0x0D0A"
long hexToInt(String hex) {
  int l = hex.length();
  unsigned char buf[l + 1];
  hex.getBytes(buf, l + 1);                   // char[] に
  return strtol((const char*)buf, NULL, 16);  // HexToInt
}
// bin = "1111" or "0b1111"
long binToInt(String bin) {
  int l = bin.length();
  if (l > 2){ 
    String c = bin.substring(1, 2);
    c.toUpperCase();    
    if (c == "B"){
      bin = bin.substring(2);
      l = bin.length();
    }
  }
  unsigned char buf[l + 1];
  bin.getBytes(buf, l + 1);                  // char[] に
  return strtol((const char*)buf, NULL, 2);  // BinToInt
}

// BME280:dewPointC()を流用(かなり誤差がある)
double dewPointC(double celsius, double humidity) {
  //double celsius = readTempC();
  //double humidity = readFloatHumidity();
  // (1) Saturation Vapor Pressure = ESGG(T)
  double RATIO = 373.15 / (273.15 + celsius);
  double RHS = -7.90298 * (RATIO - 1);
  RHS += 5.02808 * log10(RATIO);
  RHS += -1.3816e-7 * (pow(10, (11.344 * (1 - 1 / RATIO))) - 1);
  RHS += 8.1328e-3 * (pow(10, (-3.49149 * (RATIO - 1))) - 1);
  RHS += log10(1013.246);
  // factor -3 is to adjust units - Vapor Pressure SVP * humidity
  double VP = pow(10, RHS - 3) * humidity;
  // (2) DEWPOINT = F(Vapor Pressure)
  double T = log(VP / 0.61078);  // temp var
  return (241.88 * T) / (17.558 - T);
}