質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.47%
M5Stack

M5Stackは、小型のマイコンモジュールです。拡張モジュールが豊富に用意されており、センサと組み合わせることで測定機能を自由に追加することができます。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Arduino

Arduinoは、AVRマイコン、単純なI/O(入出力)ポートを備えた基板、C言語を元としたArduinoのプログラム言語と、それを実装した統合開発環境から構成されたシステムです。

Q&A

解決済

3回答

442閲覧

M5StickC PlusでI2Cの競合を回避する方法

tera25

総合スコア11

M5Stack

M5Stackは、小型のマイコンモジュールです。拡張モジュールが豊富に用意されており、センサと組み合わせることで測定機能を自由に追加することができます。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Arduino

Arduinoは、AVRマイコン、単純なI/O(入出力)ポートを備えた基板、C言語を元としたArduinoのプログラム言語と、それを実装した統合開発環境から構成されたシステムです。

0グッド

1クリップ

投稿2023/11/14 10:26

編集2023/11/17 08:18

実現したいこと

  • SHT30(温湿度センサ)とSGP30(ガスセンサ)の値を競合を回避して両方取得したい

前提

M5StickC PlusのHat端子にENV II Hat(SHT30温湿度センサ)、Grove端子にSGP30ガスセンサを同時に接続しています。まず、ENV II Hatの公式サンプルから不要な気圧部分を削除した下記のコードで温湿度単体で正常に値を取得できることを確認しました。

cpp

1#include <M5StickCPlus.h> 2#include "M5_ENV.h" 3 4SHT3X sht30; 5 6float tmp = 0.0; 7float hum = 0.0; 8 9void setup() { 10 M5.begin(); 11 M5.Lcd.setRotation(1); 12 Wire.begin(0, 26); 13 sht30.init(); 14 M5.Lcd.println(F("ENVII Unit(SHT30) test...\n")); 15} 16 17void loop() { 18 sht30.get(); 19 tmp = sht30.cTemp; 20 hum = sht30.humidity; 21 M5.Lcd.setCursor(0, 20); 22 M5.Lcd.fillRect(0, 20, 100, 60, BLACK); 23 M5.Lcd.printf("Temp: %2.1f \r\nHumi: %2.0f%% \r\n", tmp, hum); 24 delay(2000); 25}

次にSGP30の公式サンプルを参考にしながら、画面表示処理を上記のコードに似せた下記のコードでガスセンサ単体でも正常に値を取得できることを確認しました。

cpp

1#include <M5StickCPlus.h> 2#include "Adafruit_SGP30.h" 3 4Adafruit_SGP30 sgp; 5 6uint16_t tvoc = 0; 7uint16_t eco2 = 0; 8 9void setup() { 10 M5.begin(); 11 M5.Lcd.setRotation(1); 12 sgp.begin(); 13 M5.Lcd.println(F("SGP30 test...\n")); 14} 15 16void loop() { 17 sgp.IAQmeasure(); 18 tvoc = sgp.TVOC; 19 eco2 = sgp.eCO2; 20 M5.Lcd.setCursor(0, 20); 21 M5.Lcd.fillRect(0, 20, 100, 60, BLACK); 22 M5.Lcd.printf("TVOC: %4.0u ppb \r\neCO2: %4.0u ppm \r\n", tvoc, eco2); 23 delay(2000); 24}

そして温湿度どガスセンサ両方の値を得るために上記2つのコードを組み合わせた下記のコードでは、温湿度は正常に取得できてガスセンサの値は取得できないという結果になりました。

cpp

1#include <M5StickCPlus.h> 2#include "M5_ENV.h" 3#include "Adafruit_SGP30.h" 4 5SHT3X sht30; 6Adafruit_SGP30 sgp; 7 8float tmp = 0.0; 9float hum = 0.0; 10uint16_t tvoc = 0; 11uint16_t eco2 = 0; 12 13void setup() { 14 M5.begin(); 15 M5.Lcd.setRotation(1); 16 Wire.begin(0, 26); 17 sht30.init(); 18 sgp.begin(); 19 M5.Lcd.println(F("SHT30 & SGP30 test...\n")); 20} 21 22void loop() { 23 sht30.get(); 24 tmp = sht30.cTemp; 25 hum = sht30.humidity; 26 sgp.IAQmeasure(); 27 tvoc = sgp.TVOC; 28 eco2 = sgp.eCO2; 29 M5.Lcd.setCursor(0, 20); 30 M5.Lcd.fillRect(0, 20, 100, 60, BLACK); 31 M5.Lcd.printf("Temp: %2.1f \r\nHumi: %2.0f%% \r\n", tmp, hum); 32 M5.Lcd.printf("TVOC: %4.0u ppb \r\neCO2: %4.0u ppm \r\n", tvoc, eco2); 33 delay(2000); 34} 35

setup関数内の
Wire.begin(0, 26);
sht30.init();

sgp.begin();
の順序を入れ替えると逆にガスセンサの値は正常に取得できて温湿度が取得できないという状態になったため、おそらくI2Cまわりで競合が発生していることまでは分かったのですが、その修正方法が分からない状態です。

試したこと

このサイトを参考に都度Wire.begin()する下記のコードを試したのですが、結果は温湿度が正常でガスセンサは値なしでした。

cpp

1#include <M5StickCPlus.h> 2#include "M5_ENV.h" 3#include "Adafruit_SGP30.h" 4 5SHT3X sht30; 6Adafruit_SGP30 sgp; 7 8float tmp = 0.0; 9float hum = 0.0; 10uint16_t tvoc = 0; 11uint16_t eco2 = 0; 12 13void setup() { 14 M5.begin(); 15 M5.Lcd.setRotation(1); 16 Wire.begin(0, 26); 17 sht30.init(); 18 Wire.begin(32, 33); 19 sgp.begin(); 20 M5.Lcd.println(F("SHT30 & SGP30 test...\n")); 21} 22 23void loop() { 24 Wire.begin(0, 26); 25 sht30.get(); 26 tmp = sht30.cTemp; 27 hum = sht30.humidity; 28 Wire.begin(32, 33); 29 sgp.IAQmeasure(); 30 tvoc = sgp.TVOC; 31 eco2 = sgp.eCO2; 32 M5.Lcd.setCursor(0, 20); 33 M5.Lcd.fillRect(0, 20, 100, 60, BLACK); 34 M5.Lcd.printf("Temp: %2.1f \r\nHumi: %2.0f%% \r\n", tmp, hum); 35 M5.Lcd.printf("TVOC: %4.0u ppb \r\neCO2: %4.0u ppm \r\n", tvoc, eco2); 36 delay(2000); 37}

このサイトを参考にWireとWire1を使用する下記のコードを試したのですが、結果は温湿度が0でガスセンサの値は正常でした。

cpp

1#include <M5StickCPlus.h> 2#include "M5_ENV.h" 3#include "Adafruit_SGP30.h" 4 5SHT3X sht30; 6Adafruit_SGP30 sgp; 7 8float tmp = 0.0; 9float hum = 0.0; 10uint16_t tvoc = 0; 11uint16_t eco2 = 0; 12 13void setup() { 14 M5.begin(); 15 M5.Lcd.setRotation(1); 16 Wire1.begin(0, 26); 17 sht30.init(); 18 Wire.begin(32, 33); 19 sgp.begin(); 20 M5.Lcd.println(F("SHT30 & SGP30 test...\n")); 21} 22 23void loop() { 24 sht30.get(); 25 tmp = sht30.cTemp; 26 hum = sht30.humidity; 27 sgp.IAQmeasure(); 28 tvoc = sgp.TVOC; 29 eco2 = sgp.eCO2; 30 M5.Lcd.setCursor(0, 20); 31 M5.Lcd.fillRect(0, 20, 100, 60, BLACK); 32 M5.Lcd.printf("Temp: %2.1f \r\nHumi: %2.0f%% \r\n", tmp, hum); 33 M5.Lcd.printf("TVOC: %4.0u ppb \r\neCO2: %4.0u ppm \r\n", tvoc, eco2); 34 delay(2000); 35}

・TwoWireを事前に2つ宣言してsht30.init()とsgp.begin()の引数に渡す下記のコードを試したのですが、結果は温湿度が0でガスセンサの値は正常でした。

cpp

1#include <M5StickCPlus.h> 2#include "M5_ENV.h" 3#include "Adafruit_SGP30.h" 4 5SHT3X sht30; 6Adafruit_SGP30 sgp; 7 8TwoWire wireSGP30 = TwoWire(0); 9TwoWire wireSHT30 = TwoWire(1); 10 11float tmp = 0.0; 12float hum = 0.0; 13uint16_t tvoc = 0; 14uint16_t eco2 = 0; 15 16void setup() { 17 M5.begin(); 18 M5.Lcd.setRotation(1); 19 sht30.init(0x44, &wireSHT30); 20 sgp.begin(&wireSGP30); 21 M5.Lcd.println(F("SHT30 & SGP30 test...\n")); 22} 23 24void loop() { 25 sht30.get(); 26 tmp = sht30.cTemp; 27 hum = sht30.humidity; 28 sgp.IAQmeasure(); 29 tvoc = sgp.TVOC; 30 eco2 = sgp.eCO2; 31 M5.Lcd.setCursor(0, 20); 32 M5.Lcd.fillRect(0, 20, 100, 60, BLACK); 33 M5.Lcd.printf("Temp: %2.1f \r\nHumi: %2.0f%% \r\n", tmp, hum); 34 M5.Lcd.printf("TVOC: %4.0u ppb \r\neCO2: %4.0u ppm \r\n", tvoc, eco2); 35 delay(2000); 36}

・y_waiwaiさんの回答を受けてTwoWireを1つにした下記のコードを試したのですが、結果は温湿度が0でガスセンサの値は正常でした。

cpp

1#include <M5StickCPlus.h> 2#include "M5_ENV.h" 3#include "Adafruit_SGP30.h" 4 5SHT3X sht30; 6Adafruit_SGP30 sgp; 7 8TwoWire wire = TwoWire(0); 9 10float tmp = 0.0; 11float hum = 0.0; 12uint16_t tvoc = 0; 13uint16_t eco2 = 0; 14 15void setup() { 16 M5.begin(); 17 M5.Lcd.setRotation(1); 18 sht30.init(0x44, &wire); 19 sgp.begin(&wire); 20 M5.Lcd.println(F("SHT30 & SGP30 test...\n")); 21} 22 23void loop() { 24 sht30.get(); 25 tmp = sht30.cTemp; 26 hum = sht30.humidity; 27 sgp.IAQmeasure(); 28 tvoc = sgp.TVOC; 29 eco2 = sgp.eCO2; 30 M5.Lcd.setCursor(0, 20); 31 M5.Lcd.fillRect(0, 20, 100, 60, BLACK); 32 M5.Lcd.printf("Temp: %2.1f \r\nHumi: %2.0f%% \r\n", tmp, hum); 33 M5.Lcd.printf("TVOC: %4.0u ppb \r\neCO2: %4.0u ppm \r\n", tvoc, eco2); 34 delay(2000); 35}

・y_waiwaiさんの回答を受けてTwoWireを1つにしてセンサの初期化処理をloop内に移した下記のコードを試したのですが、結果は温湿度が0でガスセンサの値はTVOCが非表示でeCO2が400でフリーズしている状態でした。

cpp

1#include <M5StickCPlus.h> 2#include "M5_ENV.h" 3#include "Adafruit_SGP30.h" 4 5SHT3X sht30; 6Adafruit_SGP30 sgp; 7 8TwoWire wire = TwoWire(0); 9 10float tmp = 0.0; 11float hum = 0.0; 12uint16_t tvoc = 0; 13uint16_t eco2 = 0; 14 15void setup() { 16 M5.begin(); 17 M5.Lcd.setRotation(1); 18 M5.Lcd.println(F("SHT30 & SGP30 test...\n")); 19} 20 21void loop() { 22 sht30.init(0x44, &wire); 23 sht30.get(); 24 tmp = sht30.cTemp; 25 hum = sht30.humidity; 26 sgp.begin(&wire); 27 delay(15000); // SGP30初期化の推奨待機時間15秒 28 sgp.IAQmeasure(); 29 tvoc = sgp.TVOC; 30 eco2 = sgp.eCO2; 31 M5.Lcd.setCursor(0, 20); 32 M5.Lcd.fillRect(0, 20, 100, 60, BLACK); 33 M5.Lcd.printf("Temp: %2.1f \r\nHumi: %2.0f%% \r\n", tmp, hum); 34 M5.Lcd.printf("TVOC: %4.0u ppb \r\neCO2: %4.0u ppm \r\n", tvoc, eco2); 35 delay(2000); 36}

解決したコード

こちらのサイトを参考にWire1を正常に使えるように修正し、tmpさんの回答と組み合わて無事動作するようになったコードは下記の通りです。

cpp

1#include <M5Unified.h> 2#include "M5_ENV.h" 3#include "Adafruit_SGP30.h" 4 5SHT3X sht30; 6Adafruit_SGP30 sgp30; 7 8float tmp = 0.0; 9float hum = 0.0; 10uint16_t tvoc = 0; 11uint16_t eco2 = 0; 12 13void setup() { 14 // Wire1を使うために内部I2Cを無効にする処理 15 auto cfg = M5.config(); 16 cfg.pmic_button = false; // これで電源ボタン状態取得を行わなくなる 17 cfg.internal_imu = false; // これで内蔵IMUを使わなくなる 18 cfg.internal_rtc = false; // これで内蔵RTCを使わなくなる 19 M5.begin(cfg); 20 Wire1.end(); 21 22 Wire1.begin(0, 26); 23 sht30.init(0x44, &Wire1); 24 sgp30.begin(); // デフォルトでスレーブアドレス0x58と&Wireを使用 25 M5.Lcd.setRotation(1); 26 M5.Lcd.println(F("SHT30 & SGP30 test...\n")); 27} 28 29void loop() { 30 sht30.get(); 31 tmp = sht30.cTemp; 32 hum = sht30.humidity; 33 sgp30.IAQmeasure(); 34 tvoc = sgp30.TVOC; 35 eco2 = sgp30.eCO2; 36 M5.Lcd.setCursor(0, 20); 37 M5.Lcd.fillRect(0, 20, 100, 60, BLACK); 38 M5.Lcd.printf("Temp: %2.1f \r\nHumi: %2.0f%% \r\n", tmp, hum); 39 M5.Lcd.printf("TVOC: %4.0u ppb \r\neCO2: %4.0u ppm \r\n", tvoc, eco2); 40 delay(2000); 41}

補足情報(FW/ツールのバージョンなど)

Arduino IDE 2.2.1

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

ベストアンサー

センサーの商品リンクからもわかるけど、あなたのソースからと動作説明から
sh30 SDA:0 SCL:26
sgp  SDA:32 SCL:33 デフォルトSDA,SCLのPIN
I2Cは二線で複数つなげることが可能だから、同じ線につながってると思ってしまう。

○Wire.begin でピンSDA,SCLを指定できるのは最初だけでもう一度行いたいのならendで終了する必要がある。
○sgpもsh30も、TwoWireを指定しなければ、既存のWireを使う。
○sh30は、Wire.beginは行わない。自分で行う必要がある。
○sgpのbeginは、SDA,SCLデフォルトピンでWire.begin(SDA,SCL)も行う。

上記を踏まえると

cpp

1TwoWire wireSGP30 = TwoWire(0); 2TwoWire wireSHT30 = TwoWire(1); 34wireSHT30.begin(0,26); 5sht30.init(0x44, &wireSHT30); 6sgp.begin(&wireSGP30);

またはWire,Wire1を使うと(sgpは、ピンがそのままなのでWireの方を使うとします)

cpp

1Wire1.begin(0,26); 2sht30.init(0x44, &Wire1); 3sgp.begin();

検証 ハードがないので
あなたのコードを上から順に、コード1,2とすると、1,2は単体動作。
基本的に先にWireのbeginしたほうが通信できる。あとのほうが通信のピンを変更できない為
それぞれの動作を確認
コード3は、最初にWire.begin(0,26)で初期化したのでsgpは、すでに初期化されているのでピンを変更できないのでsgp通信できない。
コード4は、sgp.beginの前に、Wire.beginしたところで、すでに初期化されているのでピンの変更はできないのでsgpは通信できない。ループ内で何度も行っても意味がない。
コード5は、せっかくsht30にWire1を使おうとしているが、sht30.initで指定しないなのでWireが使われてしまうので、sgpが先にWire.beginをしてしまうのでsht30が通信できない。
コード6は、Wireを指定するのと変わらないのであとのsgpが通信できない。

上記のあなたの動作報告と辻褄が合ってると思います。

あと1つのWireを使い回すとしたら、コード4のloop内に
2つのWire.beginの前にWire.end()を入れてみては、どうでしょうか?
しかし、あまり見たことのないコードです。

投稿2023/11/17 03:43

tmp

総合スコア277

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tera25

2023/11/17 08:19

回答ありがとうございます!結論から書くと両方の値が取得できるようになりました。 頂いたコードでも最初は動かず、適当に書き換えながら色々と試していたところ、まずWire1がそもそも単体でも正常に動作していないことに気づきました。そして下記のサイトにたどり着き、M5StickC PlusでWire1を使用する際は事前に特殊な処理が必要なことを知りました。 https://qiita.com/B-SKY-Lab/items/7f7577384a170ea06ac8 これとtmpさんの2つめのコードを組み合わせた結果、無事両方の値が取得できるようになりました。動作するようになったコードは本文末尾に追記しました。
guest

0

各ライブラリ内に収まってしまっているせいで分かりづらくなっていますが、
M5Stack(Arduino)の場合、
Wire.beginTransmission(スレーブ・アドレス);でI2C接続先のスレーブアドレスを設定します。
SHT3Xの場合は以下でスレーブアドレスを設定しデータを受信していると思われますが、

sht30.init(0x44, &wire); sht30.get();

Adafruit_SGP30に関してはアドレスを設定している箇所がありません。
少なくともAdafruit_SGP30のデータを取得際にスレーブアドレスを設定し直す必要があります。
sgp.begin(&wire);で初期化しているようなのでwireにスレーブアドレスを指定する必要があるかと思います。未確認ですが、恐らく以下のようにすれば良いかと。

wire->beginTransmission(0x58) sgp.begin(&wire); delay(15000); // SGP30初期化の推奨待機時間15秒 sgp.IAQmeasure();

投稿2023/11/16 04:28

YOshim

総合スコア1085

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tera25

2023/11/16 08:03

回答ありがとうございます。 頂いたコードの wire->beginTransmission(0x58) をコンパイルが通るように wire.beginTransmission(0x58); に修正して試した結果、温湿度が0でガスセンサの値はTVOCが非表示でeCO2が400でフリーズしている状態でした。 スレーブアドレスについて調べていたのですが、SHT30はSHT3X.h内に bool init(uint8_t slave_addr_in=0x44, TwoWire* wire_in = &Wire); という行があり、デフォルトで0x44が割り当てられており、SGP30はAdafruit_SGP30.h内に #define SGP30_I2CADDR_DEFAULT 0x58 という行と、Adafruit_SGP30.cpp内に boolean Adafruit_SGP30::begin(TwoWire *theWire, boolean initSensor) { _i2caddr = SGP30_I2CADDR_DEFAULT; という行があり、デフォルトで0x58が割り当てられていました。そのため、スレーブアドレスは特に指定しなくても最初から別々のアドレスが使われているように見えるので、今回の問題の本質とはあまり関係なさそうなことが分かってきました。引き続き調査中です。
guest

0

双方のセンサを区別するのは、スレーブアドレスです。
I2Cポートを共用しているのなら、ToWireは一つだけ、で、順番に片方のセンサの値を取得したあと、もう片方のセンサの値を取得すればいいです

投稿2023/11/14 11:08

y_waiwai

総合スコア87784

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tera25

2023/11/14 13:49

回答ありがとうございます。 >双方のセンサを区別するのは、スレーブアドレスです。 これを知らなかったのでスレーブアドレスを確認してきました。SHT30はSHT3X.h内で0x44に、SGP30はAdafruit_SGP30.h内で0x58に、デフォルトでそれぞれ別のアドレスが割り当てられていることを確認しました。そして本文末尾に追記した2つのコードを試したのですが、どちらも温湿度が0でした。 >順番に片方のセンサの値を取得したあと、もう片方のセンサの値を取得すればいいです おそらく私がこの意味を正しく理解できていないことが原因だと思うのですが、これについてもう少し分かりやすく教えていただくことは可能でしょうか。今の私の理解力だと「デフォルトのスレーブアドレスが別々でTwoWireが一つでいいなら追記した2つのコードか上から3つ目のコードで動きそう」と思ってしまっている状態です。 (以降の返信が睡眠を挟んで遅くなる可能性があります。すみません。)
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.47%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問