Pa.HUB: use any port for connection.
-
I have a working script that reads and displays temperature data from the SHT30 and SHT40 sensors. Both sensors are connected to the AtomS3 through Pa.HUB because they share the same I2C address.
Here is my setup:
As shown in the picture, if I connect the sensors to a specific port in the hub, I can retrieve readings from them. However, I want both sensors to work on any port I plug them into. I have been continuously trying to modify the script to achieve this, but all attempts have failed.
Here is my failing script:
#include <M5AtomS3.h> #include <M5Unified.h> #include <SPI.h> #include <SSLClient.h> #include <M5_Ethernet.h> #include <PubSubClient.h> #include "certificates.h" #include <ArduinoJson.h> #include <SensirionI2CSht4x.h> #include "M5UnitENV.h" // Header files that contain the icons #include "air.h" #include "liquid.h" #include "thermometer.h" #define SCK 5 #define MISO 7 #define MOSI 8 #define CS 6 SHT3X sht3x; SensirionI2cSht4x sht4x; float temperature, pressure, humidity, liquid_temperature; uint16_t error; char errorMessage[256]; // The MQTT topics that this device should publish/subscribe #define AWS_IOT_PUBLISH_TOPIC "AtomS3/env" #define AWS_IOT_SUBSCRIBE_TOPIC "AtomS3/msg" const char my_cert[] = "-----BEGIN CERTIFICATE-----\n" "MIIDxxxx...\n" "-----END CERTIFICATE-----\n"; const char my_key[] = "-----BEGIN RSA PRIVATE KEY-----\n" "MIIExxxx...\n" "-----END RSA PRIVATE KEY-----\n"; SSLClientParameters mTLS = SSLClientParameters::fromPEM(my_cert, sizeof my_cert, my_key, sizeof my_key); byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0x89}; const char *mqttServer = "a1xxxx....amazonaws.com"; void callback(char *topic, byte *payload, unsigned int length) { Serial.print("Message arrived ["); Serial.print(topic); Serial.print("] "); for (int i = 0; i < length; i++) { Serial.print((char)payload[i]); } Serial.println(); } EthernetClient ethClient; SSLClient ethClientSSL(ethClient, TAs, (size_t)TAs_NUM, GPIO_NUM_10); PubSubClient client(mqttServer, 8883, callback, ethClientSSL); void reconnect() { // Loop until we're reconnected while (!client.connected()) { Serial.print("Attempting MQTT connection..."); if (client.connect("arduinoClient")) { Serial.println("connected"); client.subscribe("ENV_TEST"); } else { Serial.print("failed, rc="); Serial.print(client.state()); Serial.println(" try again in 5 seconds"); delay(5000); } } } void publishMessage() { StaticJsonDocument<200> doc; doc["air-temperature"] = temperature; doc["air-humidity"] = humidity; doc["liquid-temperature"] = liquid_temperature; char jsonBuffer[512]; serializeJson(doc, jsonBuffer); client.publish(AWS_IOT_PUBLISH_TOPIC, jsonBuffer); } void setup() { M5.begin(); M5.Power.begin(); M5.Lcd.setTextSize(2); M5.Lcd.println("Init.."); SPI.begin(SCK, MISO, MOSI, -1); Wire.begin(); Serial.begin(115200); for (uint8_t port = 1; port < 6; port++) { Wire.beginTransmission(0x70); Wire.write(port); Wire.endTransmission(); delay(1000); if (sht3x.begin(&Wire, SHT3X_I2C_ADDR, 2, 1, 400000U)) { Serial.print("SHT3X sensor found on port "); Serial.println(port); } sht4x.begin(Wire, SHT40_I2C_ADDR_44); } Ethernet.init(CS); M5.Lcd.println("Connecting ethernet..."); while (Ethernet.begin(mac) != 1) { Serial.println("Error getting IP address via DHCP, trying again..."); delay(1000); } // Check for Ethernet hardware present if (Ethernet.hardwareStatus() == EthernetNoHardware) { Serial.println( "Ethernet shield was not found. Sorry, can't run without " "hardware. :("); while (true) { delay(100); } } if (Ethernet.linkStatus() == LinkOFF) { Serial.println("Ethernet cable is not connected."); } delay(1000); ethClientSSL.setMutualAuthParams(mTLS); client.connect("ENV_TEST"); } void loop() { temperature = humidity = pressure = liquid_temperature = NULL; if (!client.connected()) { M5.Lcd.clear(); M5.Lcd.setCursor(0, 0); M5.Lcd.println("reconnecting..."); reconnect(); } else { for (uint8_t port = 1; port < 6; port++) { Wire.beginTransmission(0x70); Wire.write(port); Wire.endTransmission(); delay(500); if (liquid_temperature == NULL) { if (sht3x.update()) { liquid_temperature = sht3x.fTemp; Serial.print("Temperature(L): "); Serial.print(liquid_temperature); Serial.print("\t"); } } if (temperature == NULL && humidity == NULL) { error = sht4x.measureHighPrecision(temperature, humidity); if (!error) { Serial.print("Temperature(A):"); Serial.print(temperature); Serial.print("\t"); Serial.print("Humidity:"); Serial.println(humidity); } /**else { errorToString(error, errorMessage, 256); Serial.print("Error: "); Serial.println(errorMessage); }**/ } } // Celsius to Fahrenheit conversion if (temperature != 0) { temperature = (temperature * 9 / 5) + 32; } M5.Lcd.clear(); M5.Lcd.setSwapBytes(true); // Draw the icons M5.Lcd.pushImage(0, 0, airWidth, airHeight, air); M5.Lcd.pushImage(32, 0, thermometerWidth, thermometerHeight, thermometer); M5.Lcd.pushImage(0, 45, airWidth, airHeight, air); M5.Lcd.pushImage(32, 45, liquidWidth, liquidHeight, liquid); M5.Lcd.pushImage(0, 90, liquidWidth, liquidHeight, liquid); M5.Lcd.pushImage(32, 90, thermometerWidth, thermometerHeight, thermometer); M5.Lcd.setCursor(70, 10); if (temperature != 0) { M5.Lcd.printf("%2.1f", temperature); M5.Lcd.setTextSize(1); M5.Lcd.print("o"); M5.Lcd.setTextSize(2); } else { M5.Lcd.print("-"); } M5.Lcd.setCursor(70, 55); if (humidity != 0) { M5.Lcd.printf("%2.0f%%", humidity); } else { M5.Lcd.print("-"); } M5.Lcd.setCursor(70, 100); if (liquid_temperature != 0) { M5.Lcd.printf("%2.1f", liquid_temperature); M5.Lcd.setTextSize(1); M5.Lcd.print("o"); M5.Lcd.setTextSize(2); } else { M5.Lcd.print("-"); } delay(100); publishMessage(); } client.loop(); delay(3000); }
Can you guys help me with this? Any advice or alternative approach would be greatly appreciated.
-
@Surya they have to be connected to the lowest free number port for some reason
-
@ajb2k3 Thanks for your prompt response.
If so, is there any way to detect which sensor is connected to which port? (Basically, I don't want a sensor to be statically assigned to a specific port)
This way, I can detect the sensor port and get the temperature data using an appropriate command. -
Hello @Surya
I think you might be selecting the channels/ports incorrectly. If you have a look at the datasheet - section 8.5.4 Control Register - you see that each channel/port is one bit of the control register byte.
E.g. your for loop going from 1 to 5 actually selects channel/port 1, 2, 1+2, 3, 1+3, 2+3. Not exactly what you want.
BTW: you can also see how the channels/ports are selected in the Adafruit library here.
Thanks
Felix -
Hello @felmue
I'll investigate and give it a try. Thank you! -
void initializeSensors() { for (int port = 0; port < 6; port++) { Wire.beginTransmission(0x70); Wire.write(1 << port); Wire.endTransmission(); delay(10); Wire.beginTransmission(SHT40_I2C_ADDR_44); if (Wire.endTransmission() == 0) { float temp, hum; sht4x.begin(Wire, SHT40_I2C_ADDR_44); uint16_t error = sht4x.measureHighPrecision(temp, hum); if (error == 0) { sht4xPort = port; sht4x.begin(Wire, SHT40_I2C_ADDR_44); Serial.print("SHT4X found on port "); Serial.println(port); continue; } } } for (int port = 0; port < 6; port++) { if (port == sht4xPort) { continue; } Wire.beginTransmission(0x70); Wire.write(1 << port); Wire.endTransmission(); delay(10); Wire.beginTransmission(SHT3X_I2C_ADDR); if (Wire.endTransmission() == 0) { // Try to read a unique characteristic of SHT3X if (sht3x.begin(&Wire, SHT3X_I2C_ADDR, 2, 1, 400000U)) { sht3xPort = port; Serial.print("SHT3X found on port "); Serial.println(port); continue; } } } } void setup() { M5.begin(); M5.Power.begin(); // Init power M5.Lcd.setTextSize(2); M5.Lcd.println("Init.."); SPI.begin(SCK, MISO, MOSI, -1); Wire.begin(); Serial.begin(115200); delay(3000); initializeSensors(); Ethernet.init(CS); M5.Lcd.println("Connecting ethernet..."); while (Ethernet.begin(mac) != 1) { Serial.println("Error getting IP address via DHCP, trying again..."); delay(1000); } // Check for Ethernet hardware present if (Ethernet.hardwareStatus() == EthernetNoHardware) { Serial.println( "Ethernet shield was not found. Sorry, can't run without " "hardware. :("); while (true) { delay(100); } } if (Ethernet.linkStatus() == LinkOFF) { Serial.println("Ethernet cable is not connected."); } delay(1000); ethClientSSL.setMutualAuthParams(mTLS); client.connect("ENV_TEST"); } void loop() { temperature = humidity = pressure = liquid_temperature = 0; if (!client.connected()) { M5.Lcd.clear(); M5.Lcd.setCursor(0, 0); M5.Lcd.println("reconnecting..."); reconnect(); } else { uint16_t error; char errorMessage[256]; Serial.println(sht3xPort); Serial.println(sht4xPort); if (sht3xPort != -1) { Wire.beginTransmission(0x70); Wire.write(1 << sht3xPort); Wire.endTransmission(); delay(10); if (sht3x.update()) { liquid_temperature = sht3x.fTemp; Serial.print("Temperature(L): "); Serial.print(liquid_temperature); Serial.print("\t"); } } if (sht4xPort != -1) { Wire.beginTransmission(0x70); Wire.write(1 << sht4xPort); Wire.endTransmission(); delay(10); uint16_t error = sht4x.measureHighPrecision(temperature, humidity); if (error) { char errorMessage[256]; errorToString(error, errorMessage, 256); Serial.print("Error: "); Serial.println(errorMessage); } else { Serial.print("Temperature(A): "); Serial.print(temperature); Serial.print("\tHumidity: "); Serial.println(humidity); } // Convert Celsius to Fahrenheit if (temperature != 0) { temperature = (temperature * 9 / 5) + 32; } } M5.Lcd.clear(); // Swap the colour byte order when rendering M5.Lcd.setSwapBytes(true); // Draw the icons M5.Lcd.pushImage(0, 0, airWidth, airHeight, air); M5.Lcd.pushImage(32, 0, thermometerWidth, thermometerHeight, thermometer); M5.Lcd.pushImage(0, 45, airWidth, airHeight, air); M5.Lcd.pushImage(32, 45, liquidWidth, liquidHeight, liquid); M5.Lcd.pushImage(0, 90, liquidWidth, liquidHeight, liquid); M5.Lcd.pushImage(32, 90, thermometerWidth, thermometerHeight, thermometer); M5.Lcd.setCursor(70, 10); if (temperature != 0) { M5.Lcd.printf("%2.1f", temperature); M5.Lcd.setTextSize(1); M5.Lcd.print("o"); M5.Lcd.setTextSize(2); } else { M5.Lcd.print("-"); } M5.Lcd.setCursor(70, 55); if (humidity != 0) { M5.Lcd.printf("%2.0f%%", humidity); } else { M5.Lcd.print("-"); } M5.Lcd.setCursor(70, 100); if (liquid_temperature != 0) { M5.Lcd.printf("%2.1f", liquid_temperature); M5.Lcd.setTextSize(1); M5.Lcd.print("o"); M5.Lcd.setTextSize(2); } else { M5.Lcd.print("-"); } delay(100); publishMessage(); } client.loop(); delay(3000); }
This script collects sensor data wherever I connect it to the hub. However, I'm facing an issue: when I first connect it to the power supply, it works fine, but if I press the reset button on the AtomS3, I get this error: "[Wire.cpp:513] requestFrom(): i2cRead returned Error 263."
I found that this error is caused by the SHT3X sensor. When this happens, unplugging and re-plugging the sensor and then pressing the reset button makes it work again. I suspect that pressing the reset button breaks the loop without properly resetting the SHT3X sensor.
I'm looking for a way to reset the SHT3X sensor before reinitializing it but haven't found a solution. Please help me with this.
-
-
Unplugging and replugging the power supply also fixes the issue. Do you guys know how to programmatically restart the AtomS3, I2C bus, or sensor?
-
Here are some additional details:
When I initially scan for the I2C bus, it says:
Scanning...
I2C device found at address 0x70After pressing the reset button in AtomS3, it says:
Scanning...
I2C device found at address 0x44
I2C device found at address 0x70
I2C device found at address 0x76I tried using Wire.end() to end all I2C communication, but the addresses still remains open. How to close them?
void scanI2CBus() { byte error, address; Serial.println("Scanning..."); for (address = 1; address < 127; address++) { // The I2C address 0x00 is reserved and the addresses should be between 0x01 and 0x7F Wire.beginTransmission(address); error = Wire.endTransmission(); if (error == 0) { Serial.print("I2C device found at address 0x"); if (address < 16) { Serial.print("0"); } Serial.print(address, HEX); } } }