Read the statement by Michael Teeuw here.
AHT20 Humidity + Temperature Sensor
-
Does anyone know of a module that will display the output from AHT20 temperature + humidity sensors? I got these after having used the older DHTxx sensors in the past. These new sensors use I2C, and I have confirmed the sensor is wired up correctly and sending data by using an example script.
Now I’d like to get the output on the Magic Mirror. While I do see some modules that read from I2C compatible sensors, I haven’t seen one that reads these.
Currently I an working with these two modules, both to no avail:
MMMiTemperature: https://github.com/Tom-Hirschberger/MMM-Temperature
and
MMM-BME280: https://github.com/awitwicki/MMM-BME280 -
@JohnGalt Good morning,
do you have connected the sensors locally to the same PI where the mirror is running?
What script are you using and what output this sends?
If you are able to modify these scripts to deliver a JSON output you may can use MMM-JsonValue for displaying the results…Regards,
Ralf -
@rkorell – Thanks for responding, answers follow:
-
Yes, the first use case is a sensor connected directly to the Raspberry Pi 4 where the Magic Mirror is running. It will be great if I can expand this in the future to display on my office Magic Mirror the outputs from sensors on three (3) other Pis (they run Magic Mirror, NEMS network monitor and Pi-Aware flight tracking, respectively).
-
I am just running the two Magic Mirror modules listed above. I don’t know what scripts they run. At this point I have just looked and don’t see any obvious way to adapt them. I did try changing the address of the device from their default to 0x38, which is what I think is the address for this sensor attached to my Pi - but I still don’t get output. I should have some time this afternoon to SSH into that machine, and I can locate the scripts and log the error messages.
-
-
@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 -