Sensirion / raspberry-pi-i2c-scd4x

RaspberryPi driver for Sensirion SCD4x sensors
BSD 3-Clause "New" or "Revised" License
20 stars 9 forks source link

Sensor recognized, but no data #6

Closed ullix closed 8 months ago

ullix commented 1 year ago

I have my SCD41 connected to Raspi, compiled the driver (OK), and started the example_usage. This is the outcome:

serial: 0x0d41bf0734f Waiting for first measurement... (5 sec)

I am now waiting for several minutes. No additional output. Now what???

LeonieFierz commented 1 year ago

Hi @ullix SCD41 needs enough power to perform a measurement (~200mA). Please make sure to connect the sensor to the 5V pin and that you have an external power supply for your RasperryPi. Hope this helps you further.

ullix commented 1 year ago

Could I safely have the 5V from the Raspi connect to the VDD pin of the SCD41 and still have SCL and SDA at 3.3V? If not I would fry my Raspi, or not?

But I am also using the very same SCD41 at the USB-ISS dongle with 3.3V and it works perfectly. I wrote a review on this use.

So, what is different between Raspi's and USB-ISS's voltage of 3.3V?

LeonieFierz commented 1 year ago

Hello @ullix

I quickly checked on my setup with a Raspberry Pi 3 B V1.2 to connect the SCD42. I can read the measurements when I connect the sensor to the 3V3 Pin and an external power supply is connected.

According to https://pinout.xyz/pinout/3v3_power# some older Raspberry Pi Models do only provide up to 50mV on the 3V3 pin, but up to 500mA in newer versions. So it should not be necessary to use the 5V pin. I tested if it would work as well with the 5V pin for the VDD, but there seems to be an issue in voltage level for i2c, so sorry for the wrong indication there.

ullix commented 1 year ago

Puuuh, thanks for checking!

I have the Raspberry Pi 4 Model B, so it should give enough juice. But you also have the SCD42, while I have the SCD41. Difference?

psachs commented 1 year ago

Hi @ullix

There is no electrical difference between SCD41 and SCD42. It's a different calibration and range of CO2 that can be measured.

I just tested out the driver with an SCD41 and a Raspberry Pi 4 Model B and everything works fine.

As you don't seem to get any error messages, it looks like data read is never set to true.

Can you check if it works if you don't check the data ready but just read out the measurement every 5 seconds with the following for loop?

    for (;;) {
        // Read Measurement
        sensirion_i2c_hal_sleep_usec(5000000);

        uint16_t co2;
        float temperature;
        float humidity;
        error = scd4x_read_measurement(&co2, &temperature, &humidity);
        if (error) {
            printf("Error executing scd4x_read_measurement(): %i\n", error);
        } else if (co2 == 0) {
            printf("Invalid sample detected, skipping.\n");
        } else {
            printf("CO2: %u ppm\n", co2);
            printf("Temperature: %.2f °C\n", temperature);
            printf("Humidity: %.2f RH\n", humidity);
        }
    }
ullix commented 1 year ago

@psachs: thanks, but my Raspi universe is based on Python. Never tried C on a Raspi, so, I can't test that code clip.

I also would prefer to not use any compiled driver, but use plain Python with the smbus or smbus2 modules. My Python code works with every I2C model I ever tried, but on the Raspi NOT with SCD30 and NOT with SCD41.

Above I had referred to my usage of the USB-ISS dongle. And these very same two SCD modules still work with that dongle with my Python code without problems.

As far as I know the smbus Python code is just a wrapper for the OS's kernel code for I2C. Is your C code also using this kernel code, or do implement something different? If so, what is the difference?

psachs commented 1 year ago

Hi @ullix

Thanks for clarification. As you opened this issue on the raspberry pi driver I assumed you use this driver and not the python one.

Our c code uses the user space i2c interface of the linux kernel (/dev/i2c-1 on raspi). If you use python code to connect to the same I2C interface you have to ensure that I2C is enabled on the raspberry pi using e.g. the raspi-config and that your user has access to it. I assume you already got this part working.

If you are looking into using smbus2 you might be interested into the python snippets for raspberry pi. There is a minimal example for SCD4x that you can try out: https://github.com/Sensirion/raspberrypi-snippets/blob/main/SCD4x_I2C_PYTHON_minimal_example.py

Let me know if this is what you are looking for.

Best regards, Pascal

ullix commented 1 year ago

Failure:

`$ i2cdetect 1 WARNING! This program can confuse your I2C bus, cause data loss and worse! I will probe file /dev/i2c-1. I will probe address range 0x08-0x77. Continue? [Y/n] 0 1 2 3 4 5 6 7 8 9 a b c d e f 00: -- -- -- -- -- -- -- -- 10: 10 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 20: -- -- -- 23 -- -- -- -- -- 29 -- -- -- -- -- -- 30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 40: -- -- -- -- -- -- -- -- 48 -- -- -- -- -- -- -- 50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 60: -- 61 62 -- -- -- -- -- -- -- -- -- -- -- -- -- 70: -- -- -- -- -- -- -- 77

$ ./test_scd.py # your code unchanged Traceback (most recent call last): File "/home/pi/dataserver/./test_scd.py", line 109, in bus.i2c_rdwr(msg) File "/home/pi/.local/lib/python3.9/site-packages/smbus2/smbus2.py", line 658, in i2c_rdwr ioctl(self.fd, I2C_RDWR, ioctl_data) OSError: [Errno 121] Remote I/O error `

ullix commented 1 year ago

Just for completeness, here is my Raspi running with various I2C chips. My scanning is done by reading an I2C-address I2Chandle.read_byte(addr) and checking for exception. ` 47.961 TECH : .....6 Scanning I2C Bus: 47.961 TECH : .....7 0 1 2 3 4 5 6 7 8 9 A B C D E F 47.964 TECH : .....8 0x00 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 47.967 TECH : ....10 0x10 10 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 47.972 TECH : ....14 0x20 -- -- -- 23 -- -- -- -- 28 29 -- -- -- -- -- -- 47.975 TECH : ....15 0x30 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 47.978 TECH : ....17 0x40 -- -- -- -- -- -- -- -- 48 -- -- -- -- -- -- -- 47.981 TECH : ....18 0x50 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 47.985 TECH : ....20 0x60 -- 61 -- -- -- -- -- -- -- -- -- -- -- -- -- -- 47.988 TECH : ....22 0x70 -- -- -- -- -- -- -- 77 -- -- -- -- -- -- -- -- 47.988 TECH : ....23 Found a total of 7 I2C device(s) in 27.7 ms 47.989 DEBUG : ....24 initBME280: 47.989 OK : ....25 Sensor BME280 at address 0x77 has proper ID: 0x60 48.098 VERBOSE: ....26 - getValBME280: Temp:25.840, Press:1023.716, Humid:24.135 dur:1.94 ms 48.101 DEBUG : ....28 initBH1750: 48.102 OK : ....29 Sensor BH1750 at address 0x23 has responded with: [1, 45] 48.103 VERBOSE: ....30 - getValBH1750: Lux : 250.833 raw:[1, 45] dur:0.83 ms

` All 6 I2C chips are recognized. The 7th (SCD30 @61) is seen, the 8th(SCD41 @62) is present (see previous post) but not even seen. Active are BME280 and BH1750, both working flawlessly.

The SCD** chips are not activated because the SCD30 never becomes ready (and delivers crap when its "ready" is ignored) and the SCD41 does not even respond.

If you want the full software, sure, it is open source.

psachs commented 1 year ago

As you know what I2C devices should be attached, I suggest you use a known command for the SCD sensors and not just read some bytes as this might not work for those sensors. Since ic2detect sees both SCD sensors I suspect that the issue is in how you communicate with the sensors.

Using the python snippet for SCD4x works on my Raspberry Pi 4 fine. I only have the SCD41 connected to i2c channel 1.

Please also ensure that non of the other drivers or your code calls a general reset on the i2c channel, else you have to make sure that the measurement is started again.

For the SCD41, if you ignore the data ready, it should not give you wrong/weird results, but just the same result as the last time, if no new measurement was triggered between the reads.

ullix commented 1 year ago

Reading a single byte has so far worked on all systems I tried it, except for the SCD41 on Raspi. The I2C rules do not exclude reading a byte, and doing so should not run havoc on that I2C device. If it does, then there is a problem with this device's design!

And as for checking for a specific device, I suggest to take a look into the BOSCH BME280. It returns an identifier on a specific call, which is very helpful to ensure that the right device is in place, and communication works!

Checking for specific devices is actually the way I start, but here I wanted to document what systems are connected.

But as you see in my posted outputs, I am not even getting to the point where I can command the SCD41 to do anything, because it fails already with OSError: [Errno 121] Remote I/O error exactly as it does with YOUR script! The scd41 cannot be found!

I think the question is: what is different between the calls by i2cdetect and by smbus from Python?

Is there somehow some clock-stretching involved?

ullix commented 1 year ago

Tried one more thing: I removed all I2C modules except the SCD41, rebooted Raspi and did NOT start anything but this mini program:

# !/usr/bin/python
# -*- coding: UTF-8 -*-

import smbus as smbus
# import smbus2 as smbus

I2Chandle = smbus.SMBus(1)
rtest = I2Chandle.read_byte(0x62)     # SCD41:  FAILS with ERRNO 121

But it also failed with with OSError: [Errno 121] Remote I/O error . Using smbus or smbus2 did not make a difference.

$ i2cdetect 1
WARNING! This program can confuse your I2C bus, cause data loss and worse!
I will probe file /dev/i2c-1.
I will probe address range 0x08-0x77.
Continue? [Y/n] 
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:                         -- -- -- -- -- -- -- -- 
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
60: -- -- 62 -- -- -- -- -- -- -- -- -- -- -- -- -- 
70: -- -- -- -- -- -- -- --    

I am using the 32 bit Raspi version, not 64 bit. Does this matter?

pi@raspberrypi:~/ $ lsb_release -a
No LSB modules are available.
Distributor ID: Raspbian
Description:    Raspbian GNU/Linux 11 (bullseye)
Release:    11
Codename:   bullseye

pi@raspberrypi:~/ $ uname -a
Linux raspberrypi 5.15.84-v7l+ #1613 SMP Thu Jan 5 12:01:26 GMT 2023 armv7l GNU/Linux
psachs commented 1 year ago

Hi, thanks for the update and sorry for my late response. I failed to press the Comment button and did not send out my last response.

I further did a couple of tests with the Python driver on Raspberry Pi and we found an issue that leads to the 121 error. If the SCD4x sensors are measuring they will not respond to any other commands like read out of serial number or start measurement. Furthermore after starting up (reaching 2.25V) the sensor needs 1s to enter the idle state and is ready to receive I2C commands. This is especially relevant if you send a reset (reinit) on the I2C bus.

Please also note the following from the datasheet: "While a periodic measurement is running, no other commands must be issued with the exception of read measurement, get data ready status, stop periodic measurement and set ambient pressure."

To check if the sensor is on the bus and ready I suggest to use the get_data_ready_status command (0xe4b8). If data is available to be read it will respond with 0x8000 (and CRC 0xa2). You can also ignore the output of this command if you are just interested if the sensor is available.

Furthermore I suggest you always make sure that the measurement is stopped (through stop measurement or reinit) before issuing any other commands (like get serial number or start command)

Following script worked reliable on my setup with a Raspberry Pi and the sensirion-i2c-scd4x Python driver.

import time
from sensirion_i2c_driver import LinuxI2cTransceiver, I2cConnection
from sensirion_i2c_scd import Scd4xI2cDevice

# Connect to the I²C 1 port /dev/i2c-1
with LinuxI2cTransceiver('/dev/i2c-1') as i2c_transceiver:
    # Create SCD4x device
    scd4x = Scd4xI2cDevice(I2cConnection(i2c_transceiver))

    # ensure SCD4x is in idle state
    time.sleep(1)

    # Make sure measurement is stopped, else we can't read serial number or
    # start a new measurement
    scd4x.stop_periodic_measurement()

    print("scd4x Serial Number: {}".format(scd4x.read_serial_number()))

    scd4x.start_periodic_measurement()

    # Measure every second for one minute
    for _ in range(60):
        time.sleep(5)
        co2, temperature, humidity = scd4x.read_measurement()
        # use default formatting for printing output:
        print("{}, {}, {}".format(co2, temperature, humidity))
qfisch commented 8 months ago

I will close this issue as it seems resolved. Feel free to re-open it if you still have issue