TOPに戻る

Raspberry Pi Picoでプログラミング ⑫ i2c 湿度センサAHT20

 低価格湿度センサのAHT20をつなぎました。使用したAdafruitのブレークアウト・ボードは、STEMMA QT(JST SH 4ピン)コネクタは2か所に装着されていて、どちらにつないでもかまいません。このコネクタを使ってI2Cで制御する場合、特に、ジャンパ線をつなぐなどは不要です。

湿度センサAHT20のおもなスペック

 データシート

  • 電源電圧  2.0~5.5V
  • 湿度 確度±2%RH、分解能0.024%RH
  • 温度 確度±0.3℃、分解能0.01℃
  • インターフェース I2C(0~400kHz)
  • スレーブ・アドレス 0x38(固定)

接続

 Adafruitのブレークアウト・ボードを利用します。

 [GP9,GP8]のi2c0バスにつなぎます。このボードはSTEMMA QTと呼ばれるI2C専用の4ピンコネクタがついています。2本の信号線以外に、3.3VとGNDをつなぎます。

tmp117のフォルダをコピーして使う

 前回作成したtmp117のフォルダをコピーし、フォルダ名をaht20に変更します。

 /poco/worksにあるCMakeLists.txtを次のように修正します。


cmake_minimum_required(VERSION 3.12)

# Pull in SDK (must be before project)
include(pico_sdk_import.cmake)

project(pico_examples C CXX ASM)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)

set(PICO_EXAMPLES_PATH ${PROJECT_SOURCE_DIR})

# Initialize the SDK
pico_sdk_init()

# Add blink example
add_subdirectory(cmake)
add_subdirectory(blink)
add_subdirectory(serial)
add_subdirectory(clock)
add_subdirectory(i2cscanner)
add_subdirectory(lps25hb)
add_subdirectory(tmp117)
add_subdirectory(aht20)

 /poco/works/aht20にあるCMakeLists.txtを次のように修正します。


add_executable(aht20
        aht20.c
        )

# Pull in our pico_stdlib which pulls in commonly used features
target_link_libraries(aht20 pico_stdlib hardware_i2c)

# create map/bin/hex file etc.
pico_add_extra_outputs(aht20)

 tmp117.cはaht20.cに変更し、内容を次のように書き換えます。


#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/i2c.h"

static int aht20_addr = 0x38;

#define I2C_PORT  i2c0

#define SDA_PIN 8  // GP8
#define SCL_PIN 9  // GP9

static void write_register16(uint8_t reg, uint8_t data0, uint8_t data1) {
    uint8_t buf[3];
    buf[0] = reg; buf[1] = data0; buf[2] = data1;
    i2c_write_blocking(I2C_PORT, aht20_addr, buf, 3, false);
}

static void aht20_read_raw(uint32_t *humi, uint32_t *temp) {
    uint8_t buffer[7];
    i2c_read_blocking(I2C_PORT, aht20_addr, buffer, 7, false);
    *humi = (buffer[1] <<16 | buffer[2] << 8 | (buffer[3] >> 4 )) >> 4;
    *temp = (buffer[3] & 0x0f) << 16 | buffer[4] << 8 | buffer[5];
    // printf("\nhumi 0x%x 0x%x 0x%x\n", buffer[1],buffer[2],buffer[3]);
    // printf("\ntemp 0x%x 0x%x 0x%x\n", buffer[3],buffer[4],buffer[5]);
    printf("\ncrc 0x%x\n", buffer[6]);
}

int main() {
    stdio_init_all();

    printf("\nHello, aht20! Reading raw data from registers via i2c...\n");

    // setup i2c
    int f = i2c_init(I2C_PORT, 400 * 1000);
    gpio_set_function(SDA_PIN, GPIO_FUNC_I2C);
    gpio_set_function(SCL_PIN, GPIO_FUNC_I2C);
    gpio_pull_up(SDA_PIN);
    gpio_pull_up(SCL_PIN);

    // calibration
    write_register16(0xbe, 0x08, 0x00);
    sleep_ms(10);

     uint32_t humi, temp;

    while (1) {
        // trigger measurement
        write_register16(0xac, 0x33, 0x00);
        sleep_ms(80);
        aht20_read_raw(&humi, &temp);
        printf("humidity = %.1f%\n", (float)humi * 100 / 1048576);
        printf("temperature = %.2fC\n", ((float)temp * 200 / 1048576) - 50);
        sleep_ms(1000);
    }

    return 0;
}

 実行結果です。

CRC8の計算

 読み出した、ステータス、湿度、温度の6バイトの後ろにCRCデータが送られてきています。6バイトを読んでCRC8を計算します。

 CRCは、PolynomialがCRC[7:0]=1+x4+x5+x8 のDallas/MaximのCRC8です。初期値は0xffです。読み出したcrc値と計算したcrc値を表示しました。近距離では双方の値が異なることはありません。通信距離が長いとか、ノイズが多い環境のとき、不一致だとデータを捨てるような使いかたになると思います。


#include <stdio.h> 
#include "pico/stdlib.h"
#include "hardware/i2c.h"

static int aht20_addr = 0x38;

#define I2C_PORT  i2c0

#define SDA_PIN 8  // GP8
#define SCL_PIN 9  // GP9

static void write_register16(uint8_t reg, uint8_t data0, uint8_t data1) {
    uint8_t buf[3];
    buf[0] = reg; buf[1] = data0; buf[2] = data1;
    i2c_write_blocking(I2C_PORT, aht20_addr, buf, 3, false);
}

uint8_t CalcCrc(uint8_t data[6]) {
    uint8_t crc = 0xFF;
    for(int i = 0; i < 6; i++) {
        crc ^= data[i];
        for(uint8_t bit = 8; bit > 0; --bit) {
            if(crc & 0x80) {
                crc = (crc << 1) ^ 0x31u;
            } else {
                crc = (crc << 1);
            }
        }
    }
    return crc;
}

static void aht20_read_raw(uint32_t *humi, uint32_t *temp) {
    uint8_t buffer[7];
    i2c_read_blocking(I2C_PORT, aht20_addr, buffer, 7, false);
    *humi = (buffer[1] <<16 | buffer[2] << 8 | (buffer[3] >> 4 )) >> 4;
    *temp = (buffer[3] & 0x0f) << 16 | buffer[4] << 8 | buffer[5];
    printf("\nstatus 0x%x\n", buffer[0]);
    printf("\nhumi 0x%x 0x%x 0x%x\n", buffer[1],buffer[2],buffer[3]);
    printf("\ntemp 0x%x 0x%x 0x%x\n", buffer[3],buffer[4],buffer[5]);
    printf("\ncrc read 0x%x\n", buffer[6]);
    uint8_t data[6];
    data[0] = buffer[0]; data[1] = buffer[1];data[2] = buffer[2]; data[3] = buffer[3];data[4] = buffer[4]; data[5] = buffer[5];
    uint8_t crc8 = CalcCrc(data);
    printf("crc calc 0x%x\n", crc8);
}



int main() {
    stdio_init_all();

    printf("\nHello, aht20! Reading raw data from registers via i2c...\n");

    // setup i2c
    int f = i2c_init(I2C_PORT, 400 * 1000);
    gpio_set_function(SDA_PIN, GPIO_FUNC_I2C);
    gpio_set_function(SCL_PIN, GPIO_FUNC_I2C);
    gpio_pull_up(SDA_PIN);
    gpio_pull_up(SCL_PIN);

    // calibration
    write_register16(0xbe, 0x08, 0x00);
    sleep_ms(10);

     uint32_t humi, temp;

    while (1) {
        // trigger measurement
        write_register16(0xac, 0x33, 0x00);
        sleep_ms(80);
        aht20_read_raw(&humi, &temp);
        printf("humidity = %.1f%%\n", (float)humi * 100 / 1048576);
        printf("temperature = %.2fC\n", ((float)temp * 200 / 1048576) - 50);
        sleep_ms(10000);
    }

    return 0;
}

 実行結果です。

  表示されたstatusからtempの6バイトをこちらのサイトに入力して、計算が正しいかをチェックしました。

連載 Raspberry Pi Picoでプログラミング

(1) ラズパイ4の準備(1) USBブートの設定

(2) ラズパイ4の準備(2) 標準入出力の用意

(3) ラズパイ4の準備(3) LチカとHello, world!の実行

(4) ラズパイ4の準備(4) リモート環境の設定

(5) プログラミングの環境整備とLチカ

(6) Hello, World!

(7) 使用するピンと機能

(8) クロックの値の表示

(9) i2cscanner

(10) i2c APIと気圧センサLPS25

(11) i2c 温度センサTMP117

(12) i2c 湿度センサAHT20

(13) spi APIとA-DコンバータMCP3008

(14) spi A-DコンバータMCP3208

(15) gpioファンクション