Read the statement by Michael Teeuw here.
AHT20 Humidity + Temperature Sensor
-
@rkorell Ralf: OK, I have a chance to work on this today - Thanks for your help.
To ease troubleshooting, I have temporarily disabled MMM-Temperature and am only running MMM-BME280. Below are the following:
- Config for this module
- Error as captured using pm2 logs
- Code listing of bme280.js, which is called by the module
Config:
{module: 'MMM-BME280', // https://github.com/awitwicki/MMM-BME280 disabled: false, position: 'top_right', config: { titleText: "Office - MMM-BME280", updateInterval: 100, //seconds deviceAddress: "0x38", //0x76 = default temperatureScaleType: 1, // 0=Celsius pressureScaleType: 1, // 0 = hPa } },
Errors [from pm2 logs]:
0|MagicMirror | [2025-04-15 12:57:48.599] [ERROR] exec error: Error: Command failed: python3 ./modules/MMM-BME280/bme280.py 0x38
0|MagicMirror | Traceback (most recent call last):
0|MagicMirror | File “/home/pi/MagicMirror/./modules/MMM-BME280/bme280.py”, line 176, in
0|MagicMirror | main()
0|MagicMirror | File “/home/pi/MagicMirror/./modules/MMM-BME280/bme280.py”, line 171, in main
0|MagicMirror | temperature,pressure,humidity = readBME280All()
0|MagicMirror | File “/home/pi/MagicMirror/./modules/MMM-BME280/bme280.py”, line 82, in readBME280All
0|MagicMirror | bus.write_byte_data(addr, REG_CONTROL_HUM, OVERSAMPLE_HUM)
0|MagicMirror | OSError: [Errno 121] Remote I/O error
0|MagicMirror |Code for bme280.py:
Module.register("MMM-BME280", { // Default module config. defaults: { updateInterval: 100, // Seconds titleText: "Home weather", deviceAddress: "0x76", temperatureScaleType: 0, // Celsuis pressureScaleType: 0 // hPa }, // Define start sequence. start: function () { Log.info("Starting module: " + this.name); this.temperature = 'Loading...'; this.humidity = 'Loading...'; this.pressure = 'Loading...'; this.update(); setInterval( this.update.bind(this), this.config.updateInterval * 1000); }, update: function () { this.sendSocketNotification('REQUEST', this.config); }, getStyles: function () { return ['MMM-BME280.css']; }, // Override dom generator. getDom: function () { var wrapper = document.createElement("div"); var header = document.createElement("div"); var label = document.createTextNode(this.config.titleText); header.className = 'bme-header'; header.appendChild(label) wrapper.appendChild(header); var table = document.createElement("table"); var tbdy = document.createElement('tbody'); for (var i = 0; i < 3; i++) { var val = ""; var sufix = ""; var icon_img = ""; switch (i) { case 0: switch (this.config.temperatureScaleType) { case 0: // Celsius val = this.temperature; sufix = "°C"; break; case 1: // Fahrenheit val = Math.round(this.temperature * 9.0 / 5.0 + 32.0); sufix = "°F"; break; } icon_img = "temperature-high"; break; case 1: val = this.humidity; icon_img = "tint"; sufix = "%"; break; case 2: switch (this.config.pressureScaleType) { case 0: // hPa val = this.pressure; sufix = " hPa"; break; case 1: // inHg val = Math.round(this.pressure * 100 / 33.864) / 100; sufix = " inHg"; break; } icon_img = "tachometer-alt"; break; } var tr = document.createElement('tr'); var icon = document.createElement("i"); icon.className = 'fa fa-' + icon_img + ' bme-icon'; var text_div = document.createElement("div"); var text = document.createTextNode(" " + val + sufix); text_div.className = 'bme-text'; text_div.appendChild(text); var td = document.createElement('td'); td.className = 'bme-td-icon'; td.appendChild(icon) tr.appendChild(td) var td = document.createElement('td'); td.appendChild(text_div) tr.appendChild(td) tbdy.appendChild(tr); } table.appendChild(tbdy); wrapper.appendChild(table); return wrapper; }, socketNotificationReceived: function (notification, payload) { if (notification === 'DATA') { this.temperature = payload.temp; this.humidity = payload.humidity; this.pressure = payload.press; this.updateDom(); } }, });
-
@JohnGalt the module also has a node_helper.js , this is where the python script is launched
browsers cannot access hardware or files directly (for security reasons)
-
@sdetweil – Hi Sam, thanks for looking in on this.
I guess I don’t understand… If the module is calling a python script, and the script is polling the sensor - then it would seem to me that the browser isn’t accessing the hardware or files directly. Obviously I’m missing something here.
-
@JohnGalt the browser (electron) wants to display the data from the sensor, but it cant read it directly. so the module sends a request to the helper to get the data from device 0x38
the module uses a python script to get the data
the python script is reporting an errorthat is what needs to be looked at.
you posted the code from the module/browser side which is not involved in getting the actual data
-
the python script you are looking for is bme280.py
#!/usr/bin/python #-------------------------------------- # ___ ___ _ ____ # / _ \/ _ \(_) __/__ __ __ # / , _/ ___/ /\ \/ _ \/ // / # /_/|_/_/ /_/___/ .__/\_, / # /_/ /___/ # # bme280.py # Read data from a digital pressure sensor. # # Official datasheet available from : # https://www.bosch-sensortec.com/bst/products/all_products/bme280 # # Author : Matt Hawkins # Date : 21/01/2018 # # https://www.raspberrypi-spy.co.uk/ # #-------------------------------------- import smbus import sys import time from ctypes import c_short from ctypes import c_byte from ctypes import c_ubyte DEVICE = 0x76 # Default device I2C address try: #override device address like '0x77' DEVICE = int(sys.argv[1], 16) except: pass bus = smbus.SMBus(1) # Rev 2 Pi, Pi 2 & Pi 3 uses bus 1 # Rev 1 Pi uses bus 0 def getShort(data, index): # return two bytes from data as a signed 16-bit value return c_short((data[index+1] << 8) + data[index]).value def getUShort(data, index): # return two bytes from data as an unsigned 16-bit value return (data[index+1] << 8) + data[index] def getChar(data,index): # return one byte from data as a signed char result = data[index] if result > 127: result -= 256 return result def getUChar(data,index): # return one byte from data as an unsigned char result = data[index] & 0xFF return result def readBME280ID(addr=DEVICE): # Chip ID Register Address REG_ID = 0xD0 (chip_id, chip_version) = bus.read_i2c_block_data(addr, REG_ID, 2) return (chip_id, chip_version) def readBME280All(addr=DEVICE): # Register Addresses REG_DATA = 0xF7 REG_CONTROL = 0xF4 REG_CONFIG = 0xF5 REG_CONTROL_HUM = 0xF2 REG_HUM_MSB = 0xFD REG_HUM_LSB = 0xFE # Oversample setting - page 27 OVERSAMPLE_TEMP = 2 OVERSAMPLE_PRES = 2 MODE = 1 # Oversample setting for humidity register - page 26 OVERSAMPLE_HUM = 2 bus.write_byte_data(addr, REG_CONTROL_HUM, OVERSAMPLE_HUM) control = OVERSAMPLE_TEMP<<5 | OVERSAMPLE_PRES<<2 | MODE bus.write_byte_data(addr, REG_CONTROL, control) # Read blocks of calibration data from EEPROM # See Page 22 data sheet cal1 = bus.read_i2c_block_data(addr, 0x88, 24) cal2 = bus.read_i2c_block_data(addr, 0xA1, 1) cal3 = bus.read_i2c_block_data(addr, 0xE1, 7) # Convert byte data to word values dig_T1 = getUShort(cal1, 0) dig_T2 = getShort(cal1, 2) dig_T3 = getShort(cal1, 4) dig_P1 = getUShort(cal1, 6) dig_P2 = getShort(cal1, 8) dig_P3 = getShort(cal1, 10) dig_P4 = getShort(cal1, 12) dig_P5 = getShort(cal1, 14) dig_P6 = getShort(cal1, 16) dig_P7 = getShort(cal1, 18) dig_P8 = getShort(cal1, 20) dig_P9 = getShort(cal1, 22) dig_H1 = getUChar(cal2, 0) dig_H2 = getShort(cal3, 0) dig_H3 = getUChar(cal3, 2) dig_H4 = getChar(cal3, 3) dig_H4 = (dig_H4 << 24) >> 20 dig_H4 = dig_H4 | (getChar(cal3, 4) & 0x0F) dig_H5 = getChar(cal3, 5) dig_H5 = (dig_H5 << 24) >> 20 dig_H5 = dig_H5 | (getUChar(cal3, 4) >> 4 & 0x0F) dig_H6 = getChar(cal3, 6) # Wait in ms (Datasheet Appendix B: Measurement time and current calculation) wait_time = 1.25 + (2.3 * OVERSAMPLE_TEMP) + ((2.3 * OVERSAMPLE_PRES) + 0.575) + ((2.3 * OVERSAMPLE_HUM)+0.575) time.sleep(wait_time/1000) # Wait the required time # Read temperature/pressure/humidity data = bus.read_i2c_block_data(addr, REG_DATA, 8) pres_raw = (data[0] << 12) | (data[1] << 4) | (data[2] >> 4) temp_raw = (data[3] << 12) | (data[4] << 4) | (data[5] >> 4) hum_raw = (data[6] << 8) | data[7] #Refine temperature var1 = ((((temp_raw>>3)-(dig_T1<<1)))*(dig_T2)) >> 11 var2 = (((((temp_raw>>4) - (dig_T1)) * ((temp_raw>>4) - (dig_T1))) >> 12) * (dig_T3)) >> 14 t_fine = var1+var2 temperature = float(((t_fine * 5) + 128) >> 8); # Refine pressure and adjust for temperature var1 = t_fine / 2.0 - 64000.0 var2 = var1 * var1 * dig_P6 / 32768.0 var2 = var2 + var1 * dig_P5 * 2.0 var2 = var2 / 4.0 + dig_P4 * 65536.0 var1 = (dig_P3 * var1 * var1 / 524288.0 + dig_P2 * var1) / 524288.0 var1 = (1.0 + var1 / 32768.0) * dig_P1 if var1 == 0: pressure=0 else: pressure = 1048576.0 - pres_raw pressure = ((pressure - var2 / 4096.0) * 6250.0) / var1 var1 = dig_P9 * pressure * pressure / 2147483648.0 var2 = pressure * dig_P8 / 32768.0 pressure = pressure + (var1 + var2 + dig_P7) / 16.0 # Refine humidity humidity = t_fine - 76800.0 humidity = (hum_raw - (dig_H4 * 64.0 + dig_H5 / 16384.0 * humidity)) * (dig_H2 / 65536.0 * (1.0 + dig_H6 / 67108864.0 * humidity * (1.0 + dig_H3 / 67108864.0 * humidity))) humidity = humidity * (1.0 - dig_H1 * humidity / 524288.0) if humidity > 100: humidity = 100 elif humidity < 0: humidity = 0 return temperature/100.0,pressure/100.0,humidity def main(): # (chip_id, chip_version) = readBME280ID() # print("Chip ID :", chip_id) # print("Version :", chip_version) temperature,pressure,humidity = readBME280All() print(round(temperature,1),round(humidity,1),round(pressure,1)) if __name__=="__main__": main()
@JohnGalt said in AHT20 Humidity + Temperature Sensor:
Remote I/O error
The given error seems to by not a program but an I/O problem.
I’m not familiar with this sensor so I do not know how to connect and - most important - how to figure out the right “deviceAddress” - it seems that there is the error located.Either the address isn’t the correct one or the IO channel is somehow broken.
For the latter came in my mind: if this is a I2C conected device - do you have enabled I2C on your Raspi?I’ve tried to “understand” what happens in the python script above as well as in the C-exmaple program on the Bosch product page for bme280 sensor, but didn’t got it…
Regards,
Ralf -
@JohnGalt
just seen while re-reading my last posting:nodehelper.js (the part of the module which is communicating with the python program and so with the sensor) is calling python3 :
exec(`python3 ./modules/MMM-BME280/bme280.py ${deviceAddr}`, (error, stdout) => {
but the python script itself is referencing python 2 (see above post, source code of bme280.py, 1st line:
#!/usr/bin/python
I guess this must match and doesn’t …
So may this is the cause of your problems.And if this is true it is may really hard to get this working because you may run in several incompatibilities and library-conflicts …
Regards,
Ralf -
-
@rkorell – Hi Ralf:
Yes, I2C is enabled and functioning. There is a test script that does return current temperature and humidity:
Invoking “python AHT20_test.py” does launch this script (Note - I launch from the terminal using ‘python’, not ‘python3’, telling me the system is indeed defaulting to python3):# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries # SPDX-License-Identifier: MIT """ Basic `AHTx0` example test """ import time import board import adafruit_ahtx0 # Create sensor object, communicating over the board's default I2C bus i2c = board.I2C() # uses board.SCL and board.SDA # i2c = board.STEMMA_I2C() # For using the built-in STEMMA QT connector on a microcontroller sensor = adafruit_ahtx0.AHTx0(i2c) while True: print("\nTemperature: %0.1f C" % sensor.temperature) print("Humidity: %0.1f %%" % sensor.relative_humidity) time.sleep(2)
-
@rkorell + @sdetweil - Thanks for the support. I think I have fixed this for myself by adapting an existing module (“MMM-Temperature”, // https://github.com/Tom-Hirschberger/MMM-Temperature).
That module calls scripts for various sensors to capture the data. I was able to adapt an existing script for use with this sensor, so I think I am good for now.
-
@JohnGalt cool.
congratulations.
It’s may be worth to show the community what you have done?Regards,
Ralf