LoginSignup
1
0

More than 1 year has passed since last update.

【BluetoothSerial入門】M5AtomUにENVIII_UnitでPCにデータ転送して温度湿度気圧のリアルタイム描画と記録♪

Last updated at Posted at 2022-01-20

記事の推し

・BluetoothSerial通信で複数数値データ転送と測定トリガーで読み間違いを減らした
・M5AtomUとENVIIIでI2Cで簡単に温湿度気圧測定
・PC側のPythonで温湿度気圧データをリアルタイム描画とファイル出力
・連続測定でトンガの衝撃波を記録できるかも...
・M5AtomUはraspi4のUSBに挿して、ArduinoIDEで開発
・PythonはPC上(ペアリング済)のVscodeで開発・実行
・BluetoothSerial利用なので、開発できた時点でUSBから電源供給すれば、電波が届く範囲なら自由に計測ができる
・BME280センサーと同時測定してみた
・記録は、(毎日)世代管理できるようにした

M5AtomU側

#include "Wire.h"
#include "UNIT_ENV.h"

SHT3X sht30;
QMP6988 qmp6988;

#include "BluetoothSerial.h"

#if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED)
#error Bluetooth is not enabled! Please run `make menuconfig` to and enable it
#endif

BluetoothSerial SerialBT;

void setup() {
  Serial.begin(115200);
  Wire.begin(26, 32);  //21, 22); SDA, SCLのpin番号
  qmp6988.init();
  SerialBT.begin("ESP32test"); //Bluetooth device name
  Serial.println("The device started, now you can pair it with bluetooth!");  
}

void loop() {
  if (sht30.get() != 0) {
    return;
  }
  Serial.printf("T\r\n");  //測定開始位置の特定のため
  Serial.printf("%2.2f\r\n", sht30.cTemp);
  Serial.printf("%0.2f\r\n", sht30.humidity);
  Serial.printf("%0.2f\r\n", qmp6988.calcPressure());
  delay(2000); //2秒間隔で測定のため、応答速度を考慮
}

pairing_ESP32test.png

PC側pythonプログラム

import serial
import time
import matplotlib.pyplot as plt
import numpy as np
import csv

COM="COM3"
bitRate=115200
ser = serial.Serial(COM, bitRate, timeout=0.1)

with open('temp_hum_press.csv', 'w', newline='') as csvfile:
    fieldnames = ['Time', 'Temperature', 'Humidity', 'Pressure']
    spamwriter = csv.DictWriter(csvfile, fieldnames=fieldnames)
    spamwriter.writeheader()

    fig, (ax1, ax2, ax3) = plt.subplots(3, 1, figsize=(8,16))
    x = np.arange(0,1000,1)
    y = 0*np.arange(0,1000,1)

    data_start = ser.readline()
    while data_start != b'T\r\n':
        data_start = ser.readline()
        #print(data_start)
    data_temp = ser.readline()    
    data_humid = ser.readline()
    data_pressure = ser.readline()
    print(data_temp)

    fd_temp = float(data_temp.decode())
    fd_humid = float(data_humid.decode())
    fd_pressure = float(data_pressure.decode())

    ax1.plot(x,y+fd_temp, lw = 2)
    ax2.plot(x,y+fd_humid, lw = 2)
    ax3.plot(x,y+fd_pressure, lw = 2)
    ax1.set_ylim(fd_temp-10,fd_temp+10)
    ax2.set_ylim(fd_humid-10,fd_humid+10)
    ax3.set_ylim(fd_pressure-50,fd_pressure+50)

    ax1.plot(0, fd_temp, color = 'red', marker = 'o',linestyle = '-', markersize = 3, label = "Temperature")
    ax2.plot(0, fd_humid,color = 'blue', marker = 'o',linestyle = '-', markersize = 3, label = "Humidity")
    ax3.plot(0, fd_pressure,color = 'black', marker = 'o',linestyle = '-', markersize = 3, label = "Pressure")
    ax1.legend()
    ax2.legend()
    ax3.legend()

    plt.grid(True)
    plt.pause(1)
    start = time.time()

    tm0 = start
    tm = start
    while 1:
        try:
            data_start = ser.readline()
            while data_start != b'T\r\n':
                data_start = ser.readline()
                #print(data_start)
            data_temp = ser.readline()
            data_humid = ser.readline()
            data_pressure = ser.readline()
            fd_temp = float(data_temp.decode())
            fd_humid = float(data_humid.decode())
            fd_pressure = float(data_pressure.decode())
        except:
            #time.sleep(1)
            print('error')

        spamwriter.writerow({'Time': time.ctime(time.time()), 'Temperature': fd_temp, 'Humidity': fd_humid, 'Pressure': fd_pressure})
        print( time.ctime(time.time()), fd_temp, fd_humid, fd_pressure)
        ax1.plot(tm-start, fd_temp,color = 'red', marker = 'o',linestyle = '-', markersize = 3, label = "Temperature")
        ax2.plot(tm-start, fd_humid,color = 'blue', marker = 'o',linestyle = '-', markersize = 3, label = "Humidity")
        ax3.plot(tm-start, fd_pressure,color = 'black', marker = 'o',linestyle = '-', markersize = 3, label = "Pressure")
        plt.pause(0.05)
        while tm-tm0 < 2:
            tm = time.time()
        tm0 = tm

print('program end')
ser.close()

測定の様子

measurement.png

最終的なプログラム

測定値が不安定なので、以下では、ENVIIIとBME280を同時測定して並べてみました。

AtomU測定側プログラム

 #include "Wire.h"
#include "UNIT_ENV.h"
#include "Seeed_BME280.h"

SHT3X sht30;
QMP6988 qmp6988;
BME280 bme280;

#include "BluetoothSerial.h"

#if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED)
#error Bluetooth is not enabled! Please run `make menuconfig` to and enable it
#endif

BluetoothSerial SerialBT;

float tmp =0.0;
float hum =0.0;
float pressure =0.0;
float tmp_ =0.0;
float hum_ =0.0;
float pressure_ =0.0;

 void setup() {
  Serial.begin(115200);
  Wire.begin(26, 32);  //21, 22);
  qmp6988.init();
  bme280.init();
  SerialBT.begin("ESP32test"); //Bluetooth device name
  Serial.println("The device started, now you can pair it with bluetooth!");  
}

void loop() {
  pressure = bme280.getPressure(); //qmp6988.calcPressure();
  tmp = bme280.getTemperature(); //sht30.cTemp;
  hum = bme280.getHumidity(); //sht30.humidity;
  pressure_ = qmp6988.calcPressure();
  if (sht30.get() == 0) {
    tmp_ = sht30.cTemp;
    hum_ = sht30.humidity;
  }else{
    tmp_ = 0.0;
    hum_ = 0.0;
  }  

  Serial.print(tmp);
  Serial.print(hum);
  Serial.print(pressure);
  Serial.print('\n');
  Serial.print(tmp_);
  Serial.print(hum_);
  Serial.print(pressure_);
  Serial.print('\n');

  SerialBT.printf("T\r\n");
  SerialBT.printf("%2.1f\r\n", tmp);
  SerialBT.printf("%2.0f\r\n", hum);
  SerialBT.printf("%2.0f\r\n", pressure);
  SerialBT.printf("%2.1f\r\n", tmp_);
  SerialBT.printf("%2.0f\r\n", hum_);
  SerialBT.printf("%2.0f\r\n", pressure_);  
  delay(10000);
}  

上記のコードを動かすには、BME280センサーを利用しているため、ArduinoIDEの右上の▼をクリックして、以下のコードを追加します。
【参考】
Seeed-Studio/Grove_BME280

Seeed_288BME.cpp
Seeed_288BME.cpp
#include "Seeed_BME280.h"

bool BME280::init(int i2c_addr) {
    uint8_t retry = 0;
    uint8_t chip_id = 0;


    _devAddr = i2c_addr;
    Wire.begin();

    while ((retry++ < 5) && (chip_id != 0x60)) {
        chip_id = BME280Read8(BME280_REG_CHIPID);
        #ifdef BMP280_DEBUG_PRINT
        Serial.print("Read chip ID: ");
        Serial.println(chip_id);
        #endif
        delay(100);
    }
    if (chip_id != 0x60){
        Serial.println("Read Chip ID fail!");
        return false;
    }

    dig_T1 = BME280Read16LE(BME280_REG_DIG_T1);
    dig_T2 = BME280ReadS16LE(BME280_REG_DIG_T2);
    dig_T3 = BME280ReadS16LE(BME280_REG_DIG_T3);

    dig_P1 = BME280Read16LE(BME280_REG_DIG_P1);
    dig_P2 = BME280ReadS16LE(BME280_REG_DIG_P2);
    dig_P3 = BME280ReadS16LE(BME280_REG_DIG_P3);
    dig_P4 = BME280ReadS16LE(BME280_REG_DIG_P4);
    dig_P5 = BME280ReadS16LE(BME280_REG_DIG_P5);
    dig_P6 = BME280ReadS16LE(BME280_REG_DIG_P6);
    dig_P7 = BME280ReadS16LE(BME280_REG_DIG_P7);
    dig_P8 = BME280ReadS16LE(BME280_REG_DIG_P8);
    dig_P9 = BME280ReadS16LE(BME280_REG_DIG_P9);

    dig_H1 = BME280Read8(BME280_REG_DIG_H1);
    dig_H2 = BME280Read16LE(BME280_REG_DIG_H2);
    dig_H3 = BME280Read8(BME280_REG_DIG_H3);
    dig_H4 = (BME280Read8(BME280_REG_DIG_H4) << 4) | (0x0F & BME280Read8(BME280_REG_DIG_H4 + 1));
    dig_H5 = (BME280Read8(BME280_REG_DIG_H5 + 1) << 4) | (0x0F & BME280Read8(BME280_REG_DIG_H5) >> 4);
    dig_H6 = (int8_t)BME280Read8(BME280_REG_DIG_H6);

    writeRegister(BME280_REG_CONTROLHUMID, 0x05);  //Choose 16X oversampling
    writeRegister(BME280_REG_CONTROL, 0xB7);  //Choose 16X oversampling

    return true;
}

float BME280::getTemperature(void) {
    int32_t var1, var2;

    int32_t adc_T = BME280Read24(BME280_REG_TEMPDATA);
    // Check if the last transport successed
    if (!isTransport_OK) {
        return 0;
    }
    adc_T >>= 4;
    var1 = (((adc_T >> 3) - ((int32_t)(dig_T1 << 1))) *
            ((int32_t)dig_T2)) >> 11;

    var2 = (((((adc_T >> 4) - ((int32_t)dig_T1)) *
              ((adc_T >> 4) - ((int32_t)dig_T1))) >> 12) *
            ((int32_t)dig_T3)) >> 14;

    t_fine = var1 + var2;
    float T = (t_fine * 5 + 128) >> 8;
    return T / 100;
}

uint32_t BME280::getPressure(void) {
    int64_t var1, var2, p;

    // Call getTemperature to get t_fine
    getTemperature();
    // Check if the last transport successed
    if (!isTransport_OK) {
        return 0;
    }

    int32_t adc_P = BME280Read24(BME280_REG_PRESSUREDATA);
    adc_P >>= 4;

    var1 = ((int64_t)t_fine) - 128000;
    var2 = var1 * var1 * (int64_t)dig_P6;
    var2 = var2 + ((var1 * (int64_t)dig_P5) << 17);
    var2 = var2 + (((int64_t)dig_P4) << 35);
    var1 = ((var1 * var1 * (int64_t)dig_P3) >> 8) + ((var1 * (int64_t)dig_P2) << 12);
    var1 = (((((int64_t)1) << 47) + var1)) * ((int64_t)dig_P1) >> 33;
    if (var1 == 0) {
        return 0; // avoid exception caused by division by zero
    }
    p = 1048576 - adc_P;
    p = (((p << 31) - var2) * 3125) / var1;
    var1 = (((int64_t)dig_P9) * (p >> 13) * (p >> 13)) >> 25;
    var2 = (((int64_t)dig_P8) * p) >> 19;
    p = ((p + var1 + var2) >> 8) + (((int64_t)dig_P7) << 4);
    return (uint32_t)p / 256;
}

uint32_t BME280::getHumidity(void) {
    int32_t v_x1_u32r, adc_H;

    // Call getTemperature to get t_fine
    getTemperature();
    // Check if the last transport successed
    if (!isTransport_OK) {
        return 0;
    }

    adc_H = BME280Read16(BME280_REG_HUMIDITYDATA);

    v_x1_u32r = (t_fine - ((int32_t)76800));
    v_x1_u32r = (((((adc_H << 14) - (((int32_t)dig_H4) << 20) - (((int32_t)dig_H5) * v_x1_u32r)) + ((
                       int32_t)16384)) >> 15) * (((((((v_x1_u32r * ((int32_t)dig_H6)) >> 10) * (((v_x1_u32r * ((int32_t)dig_H3)) >> 11) + ((
                                   int32_t)32768))) >> 10) + ((int32_t)2097152)) * ((int32_t)dig_H2) + 8192) >> 14));
    v_x1_u32r = (v_x1_u32r - (((((v_x1_u32r >> 15) * (v_x1_u32r >> 15)) >> 7) * ((int32_t)dig_H1)) >> 4));
    v_x1_u32r = (v_x1_u32r < 0 ? 0 : v_x1_u32r);
    v_x1_u32r = (v_x1_u32r > 419430400 ? 419430400 : v_x1_u32r);
    return (uint32_t)(v_x1_u32r >> 12) / 1024.0;
}

float BME280::calcAltitude(float pressure) {
    if (!isTransport_OK) {
        return 0;
    }

    float A = pressure / 101325;
    float B = 1 / 5.25588;
    float C = pow(A, B);
    C = 1.0 - C;
    C = C / 0.0000225577;
    return C;
}

uint8_t BME280::BME280Read8(uint8_t reg) {
    Wire.beginTransmission(_devAddr);
    Wire.write(reg);
    Wire.endTransmission();

    Wire.requestFrom(_devAddr, 1);
    // return 0 if slave didn't response
    if (Wire.available() < 1) {
        isTransport_OK = false;
        return 0;
    } else {
        isTransport_OK = true;
    }

    return Wire.read();
}

uint16_t BME280::BME280Read16(uint8_t reg) {
    uint8_t msb, lsb;

    Wire.beginTransmission(_devAddr);
    Wire.write(reg);
    Wire.endTransmission();

    Wire.requestFrom(_devAddr, 2);
    // return 0 if slave didn't response
    if (Wire.available() < 2) {
        isTransport_OK = false;
        return 0;
    } else {
        isTransport_OK = true;
    }
    msb = Wire.read();
    lsb = Wire.read();

    return (uint16_t) msb << 8 | lsb;
}

uint16_t BME280::BME280Read16LE(uint8_t reg) {
    uint16_t data = BME280Read16(reg);
    return (data >> 8) | (data << 8);
}

int16_t BME280::BME280ReadS16(uint8_t reg) {
    return (int16_t)BME280Read16(reg);
}

int16_t BME280::BME280ReadS16LE(uint8_t reg) {
    return (int16_t)BME280Read16LE(reg);
}

uint32_t BME280::BME280Read24(uint8_t reg) {
    uint32_t data;

    Wire.beginTransmission(_devAddr);
    Wire.write(reg);
    Wire.endTransmission();

    Wire.requestFrom(_devAddr, 3);
    // return 0 if slave didn't response
    if (Wire.available() < 3) {
        isTransport_OK = false;
        return 0;
    } else if (isTransport_OK == false) {
        isTransport_OK = true;
        if (!init(_devAddr)) {
            #ifdef BMP280_DEBUG_PRINT
            Serial.println("Device not connected or broken!");
            #endif
        }
    }
    data = Wire.read();
    data <<= 8;
    data |= Wire.read();
    data <<= 8;
    data |= Wire.read();

    return data;
}

void BME280::writeRegister(uint8_t reg, uint8_t val) {
    Wire.beginTransmission(_devAddr); // start transmission to device
    Wire.write(reg);       // send register address
    Wire.write(val);         // send value to write
    Wire.endTransmission();     // end transmission
}

同様にタブを追加して、書き込みます。

Seeed_BME280.h
Seeed_BME280.h
#pragma once

#ifndef _SEEED_BME280_H_
#define _SEEED_BME280_H_

#include <Arduino.h>
#include <Wire.h>

#define BME280_ADDRESS   0x76

#define BME280_REG_DIG_T1    0x88
#define BME280_REG_DIG_T2    0x8A
#define BME280_REG_DIG_T3    0x8C

#define BME280_REG_DIG_P1    0x8E
#define BME280_REG_DIG_P2    0x90
#define BME280_REG_DIG_P3    0x92
#define BME280_REG_DIG_P4    0x94
#define BME280_REG_DIG_P5    0x96
#define BME280_REG_DIG_P6    0x98
#define BME280_REG_DIG_P7    0x9A
#define BME280_REG_DIG_P8    0x9C
#define BME280_REG_DIG_P9    0x9E

#define BME280_REG_DIG_H1    0xA1
#define BME280_REG_DIG_H2    0xE1
#define BME280_REG_DIG_H3    0xE3
#define BME280_REG_DIG_H4    0xE4
#define BME280_REG_DIG_H5    0xE5
#define BME280_REG_DIG_H6    0xE7

#define BME280_REG_CHIPID          0xD0
#define BME280_REG_VERSION         0xD1
#define BME280_REG_SOFTRESET       0xE0

#define BME280_REG_CAL26           0xE1

#define BME280_REG_CONTROLHUMID    0xF2
#define BME280_REG_CONTROL         0xF4
#define BME280_REG_CONFIG          0xF5
#define BME280_REG_PRESSUREDATA    0xF7
#define BME280_REG_TEMPDATA        0xFA
#define BME280_REG_HUMIDITYDATA    0xFD

class BME280 {
  public:
    bool init(int i2c_addr = BME280_ADDRESS);
    float getTemperature(void);
    uint32_t getPressure(void);
    uint32_t getHumidity(void);
    float calcAltitude(float pressure);
  private:
    int _devAddr;
    bool isTransport_OK;

    // Calibration data
    uint16_t dig_T1;
    int16_t dig_T2;
    int16_t dig_T3;
    uint16_t dig_P1;
    int16_t dig_P2;
    int16_t dig_P3;
    int16_t dig_P4;
    int16_t dig_P5;
    int16_t dig_P6;
    int16_t dig_P7;
    int16_t dig_P8;
    int16_t dig_P9;
    uint8_t dig_H1;
    int16_t dig_H2;
    uint8_t dig_H3;
    int16_t dig_H4;
    int16_t dig_H5;
    int8_t  dig_H6;
    int32_t t_fine;

    // private functions
    uint8_t BME280Read8(uint8_t reg);
    uint16_t BME280Read16(uint8_t reg);
    uint16_t BME280Read16LE(uint8_t reg);
    int16_t BME280ReadS16(uint8_t reg);
    int16_t BME280ReadS16LE(uint8_t reg);
    uint32_t BME280Read24(uint8_t reg);
    void writeRegister(uint8_t reg, uint8_t val);
};

#endif

PC側プログラム

from datetime import date
import serial
import time, datetime
import matplotlib.pyplot as plt
import numpy as np
import csv

COM="COM3"
bitRate=115200
ser = serial.Serial(COM, bitRate, timeout=0.1)

def measure_(filename):
    with open(filename, 'w', newline='') as csvfile:
        fieldnames = ['Time', 'Temperature', 'Humidity', 'Pressure']
        spamwriter = csv.DictWriter(csvfile, fieldnames=fieldnames)
        spamwriter.writeheader()

        fig, (ax1, ax2, ax3) = plt.subplots(3, 1, figsize=(8,16))
        x = np.arange(0,1000,1)
        y = 0*np.arange(0,1000,1)

        data_start = ser.readline()
        while data_start != b'T\r\n':
            data_start = ser.readline()
            print(data_start)
        data_temp = ser.readline()    
        data_humid = ser.readline()
        data_pressure = ser.readline()
        data_temp_ = ser.readline()    
        data_humid_ = ser.readline()
        data_pressure_ = ser.readline()
        print(data_temp)

        fd_temp = float(data_temp.decode())
        fd_humid = float(data_humid.decode())
        fd_pressure = float(data_pressure.decode())
        fd_temp_ = float(data_temp_.decode())
        fd_humid_ = float(data_humid_.decode())
        fd_pressure_ = float(data_pressure_.decode())

        ax1.plot(x,y+fd_temp, lw = 2)
        ax2.plot(x,y+fd_humid, lw = 2)
        ax3.plot(x,y+fd_pressure, lw = 2)

        ax1.set_ylim(fd_temp-10,fd_temp+10)
        ax2.set_ylim(fd_humid-20,fd_humid+20)
        ax3.set_ylim(fd_pressure-500,fd_pressure+500)

        ax1.plot(0, fd_temp, color = 'red', marker = 'o',linestyle = '-', markersize = 3, label = "Temperature")
        ax2.plot(0, fd_humid,color = 'red', marker = 'o',linestyle = '-', markersize = 3, label = "Humidity")
        ax3.plot(0, fd_pressure,color = 'red', marker = 'o',linestyle = '-', markersize = 3, label = "Pressure")
        ax1.plot(0, fd_temp_, color = 'blue', marker = 'o',linestyle = '-', markersize = 3, label = "Temperature_")
        ax2.plot(0, fd_humid_,color = 'blue', marker = 'o',linestyle = '-', markersize = 3, label = "Humidity_")
        ax3.plot(0, fd_pressure_,color = 'blue', marker = 'o',linestyle = '-', markersize = 3, label = "Pressure_")
        ax1.legend()
        ax2.legend()
        ax3.legend()

        plt.grid(True)
        plt.pause(1)
        start = time.time()

        tm0 = start
        tm = start
        while 1:
            try:
                data_start = ser.readline()
                while data_start != b'T\r\n':
                    data_start = ser.readline()
                    #print(data_start)
                data_temp = ser.readline()
                data_humid = ser.readline()
                data_pressure = ser.readline()
                data_temp_ = ser.readline()
                data_humid_ = ser.readline()
                data_pressure_ = ser.readline()

                fd_temp = float(data_temp.decode())
                fd_humid = float(data_humid.decode())
                fd_pressure = float(data_pressure.decode())
                fd_temp_ = float(data_temp_.decode())
                fd_humid_ = float(data_humid_.decode())
                fd_pressure_ = float(data_pressure_.decode())
            except:
                #time.sleep(1)
                print('error')

            spamwriter.writerow({'Time': time.ctime(time.time()), 'Temperature': fd_temp, 'Humidity': fd_humid, 'Pressure': fd_pressure})
            print( time.ctime(time.time()), fd_temp, fd_humid, fd_pressure)
            print( time.ctime(time.time()), fd_temp_, fd_humid_, fd_pressure_)
            ax1.plot(tm-start, fd_temp,color = 'red', marker = 'o',linestyle = '-', markersize = 3, label = "Temperature")
            ax2.plot(tm-start, fd_humid,color = 'red', marker = 'o',linestyle = '-', markersize = 3, label = "Humidity")
            ax3.plot(tm-start, fd_pressure,color = 'red', marker = 'o',linestyle = '-', markersize = 3, label = "Pressure")
            ax1.plot(tm-start, fd_temp_,color = 'blue', marker = 'o',linestyle = '-', markersize = 3, label = "Temperature_")
            ax2.plot(tm-start, fd_humid_,color = 'blue', marker = 'o',linestyle = '-', markersize = 3, label = "Humidity_")
            ax3.plot(tm-start, fd_pressure_,color = 'blue', marker = 'o',linestyle = '-', markersize = 3, label = "Pressure_")
            plt.pause(0.05)
            figfilename = 'fig_Temp_Humid_Pres_'+ str(datetime.date.today())+ '.png'
            plt.savefig(figfilename) 
            while tm-tm0 < 10:
                tm = time.time()
            tm0 = tm
            filename1 = 'temp_hum_press' + str(datetime.date.today())+ '.csv' 
            if filename != filename1:
                print(filename, filename1)
                break
            else:
                continue
    return


if __name__ == '__main__':
    filename1 = 'temp_hum_press' + str(datetime.date.today())+ '.csv' 
    while 1:
        filename = 'temp_hum_press' + str(datetime.date.today())+ '.csv' 
        if filename == filename1:
            measure_(filename)
        else:
            filename1 = 'temp_hum_press' + str(datetime.date.today())+ '.csv' 

    print('program end')
    ser.close()

測定結果

上記では、長期間計測が可能なように、毎日ファイルを更新して結果を残すこととしている。
結果は、以下のとおりのグラフが得られた
横軸は、秒である。二つの測定値がほぼ平行に動いているのがわかる。
なお、測定は窓際でソーラー充電器から給電し、データはBlootooth_serialでPCに転送してPC側で保存している。また、データは、ENVIIIのデータのみ保存している。

fig_Temp_Humid_Pres_2022-01-29.png

fig_Temp_Humid_Pres_2022-01-29.png

fig_Temp_Humid_Pres_2022-01-28.png

fig_Temp_Humid_Pres_2022-01-28.png

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0