マイコンで温湿度センサーを使う場合、ArduinoのWireライブラリを使うのが一般的ですが、ATtiny202のような小容量マイコンではメモリ消費が気になることがあります。
そこで今回は、独自に作成した**ソフトウェアI2C(VPORT使用)**を使い、DHT20から温度と湿度を取得する方法を解説します。
さらに、浮動小数点(float)を使わず整数演算(×10)で処理することで、メモリを節約しつつ高速に動作する実装を紹介します。
最終的には、シンプルかつ軽量な構成で温湿度を取得できるようになります。
今回の構成
今回使用する主な部品はこちらです。
- マイコン:ATtiny202 / ATtiny402
- 温湿度センサー:DHT20
- 通信方式:ソフトI2C(VPORT使用)
Wireライブラリは使用せず、自作のI2Cで通信します。
GitHub載せておきましたので、ZIPファイルをダウンロードして、使うことができます。
<SimpleUART.h>
リポジトリUR:https://github.com/asa-lab-bit/SimpleUART
<SoftI2C_VPORT.h>
リポジトリUR:https://github.com/asa-lab-bit/SoftI2C_VPORT_tinyAVR
インストール手順:
Arduino IDEの [スケッチ] > [ライブラリをインクルード] > [.ZIP形式のライブラリをインストール…] からダウンロードしたファイルを選択。
ソフトI2Cの特徴
今回使用するソフトI2Cのポイントは以下の通りです。
- VPORTを使った高速GPIO制御
- 約100kHz相当の通信
- ライブラリサイズが非常に小さい
- ピンを自由に割り当て可能
ATtinyのような小型マイコンでも余裕で動作します。
DHT20の通信手順
DHT20は以下の手順でデータを取得します。
① 測定コマンド送信
② 約80ms待機
③ データ(6バイト)読み出し
[コマンド]
0xAC 0x33 0x00データフォーマット
取得する6バイトのデータ構造は次のようになっています。
buf[0] : ステータス
buf[1] : 湿度 MSB
buf[2] : 湿度
buf[3] : 湿度LSB(上位4bit) + 温度上位4bit
buf[4] : 温度
buf[5] : 温度LSB温度・湿度の計算(整数版)
温度(×10)
temp = ((temp_raw * 2000UL) >> 20) - 500;👉 例:608 → 60.8%
コードのポイント解説
連続読み取り
buf[i] = i2c.read(i < 5);この書き方は、
- 最後の1バイトだけNACK
- それ以外はACK
というI2Cの仕様に合わせたものです。
接続図

書き込み方法については私の下記のサイトで確認してください。
ATtiny202にArduino IDE 1.8.19で書き込む
完成プログラム
#include <SimpleUART.h>
#include <string.h>
#include <SoftI2C_VPORT.h>
#define DHT20_ADDR 0x38
SoftI2C_VPORT i2c(1, 2); // SDA=PA1, SCL=PA2
// Initialize with 16 bytes buffer
SimpleUART<16> myUART;
int8_t dht20_read(int16_t *temp, int16_t *hum)
{
uint8_t buf[6];
// 測定コマンド
i2c.start();
if (!i2c.write(DHT20_ADDR << 1)) // 書き込みモード
{
myUART.println("ADDR NACK (WRITE)");
i2c.stop();
return 0;
}
//dht20 設定
i2c.write(0xAC);
i2c.write(0x33);
i2c.write(0x00);
i2c.stop();
delay(80);
// 読み出し
i2c.start();
if (!i2c.write((DHT20_ADDR << 1) | 1)) // 読みだしモード
{
myUART.println("ADDR NACK (READ)");
i2c.stop();
return 0;
}
for (uint8_t i = 0; i < 6; i++)
{
buf[i] = i2c.read(i < 5); //6バイト読みこみ終わりにする
}
i2c.stop();
// ---- 湿度 ----
uint32_t hum_raw =
((uint32_t)buf[1] << 12) |
((uint32_t)buf[2] << 4) |
(buf[3] >> 4);
*hum = (hum_raw * 1000UL) >> 20; // ×10
// ---- 温度 ----
uint32_t temp_raw =
((uint32_t)(buf[3] & 0x0F) << 16) |
((uint32_t)buf[4] << 8) |
buf[5];
*temp = ((temp_raw * 2000UL) >> 20) - 500;
return 1;
}
// ------------------------------
// 表示
// ------------------------------
void printTemp(int16_t t)
{
// if (t == -999)
// {
// myUART.println("ERROR");
// return;
// }
myUART.print(t / 10);
myUART.print(".");
myUART.print(abs(t % 10));
myUART.println(" C");
}
void printHum(int16_t h)
{
myUART.print(h / 10);
myUART.print(".");
myUART.print(abs(h % 10));
myUART.println(" %");
}
// ------------------------------
void setup()
{
i2c.begin();
myUART.init(9600);
myUART.println("DHT20 TEST START");
}
// ------------------------------
void loop()
{
int16_t temp, hum;
if (dht20_read(&temp, &hum))
{
printTemp(temp);
printHum(hum);
}
else
{
myUART.println("ERROR");
}
delay(2000);
}実際の表示画像

プログラムの簡単な説明および注意点
まずはi2c読み出しモードに設定します。
i2c.start();
if (!i2c.write((DHT20_ADDR << 1) | 1)) // 読みだしモード
{
myUART.println("ADDR NACK (READ)");
i2c.stop();
return 0;
}次に6バイトを読み込みますが、最後に終りですの命令を書かないと、dhd 20(i2c)が 読み込みモードから解放されません。
buf[i] = i2c.read(i < 5); //6バイト読みこみ終わりにする (i<5) iが最後5ですのでFalseです。
for (uint8_t i = 0; i < 6; i++){
buf[i] = i2c.read(i < 5); //6バイト読みこみ終わりにする これです
}
i2c.stop();まとめ
今回は、DHT20を使って、ソフトI2Cによる温湿度取得を実装しました。
Wireライブラリを使わず、VPORTを利用した軽量なI2C通信を構築することで、ATtiny202のような小容量マイコンでも余裕で動作することが確認できました。
また、浮動小数点を使わず整数演算で処理することで、メモリ削減と高速化を両立しています。
今回の手法は他のI2Cセンサーにも応用できるので、ぜひいろいろなデバイスで試してみてください。



