arendst / Tasmota

Alternative firmware for ESP8266 and ESP32 based devices with easy configuration using webUI, OTA updates, automation using timers or rules, expandability and entirely local control over MQTT, HTTP, Serial or KNX. Full documentation at
https://tasmota.github.io/docs
GNU General Public License v3.0
21.71k stars 4.72k forks source link

Support of SCD30 I2C CO2 Sensor #3119

Closed downset closed 5 years ago

downset commented 6 years ago

i would like to use an Sensirion SCD30 Sensor with my Sonoff. can anybody add this Sensor?

Sensor description: https://www.sensirion.com/de/umweltsensoren/kohlendioxidsensoren-co2/

I2C Interface here: https://www.sensirion.com/fileadmin/user_upload/customers/sensirion/Dokumente/9.5_CO2/Sensirion_CO2_Sensors_SCD30_Interface_Description.pdf

Benefits of the SCD30:

-Only 3V3 Supply to connect directly to Sonoff VCC -Temperature and humidity Sensor build in -i2c -Self Calibrating -not to expensive

Greets Downset

andrethomas commented 6 years ago

Where do you buy this from?

downset commented 6 years ago

Hi,

Ich got it from mouser:

https://www.mouser.de/ProductDetail/Sensirion/SCD30?qs=rrS6PyfT74fdywu4FxpYjQ%3D%3D

Greets downset

andrethomas commented 6 years ago

Kostspielig :|

If it was cheaper I'd have ordered one and made a driver... maybe someone else will.

stale[bot] commented 5 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

downset commented 5 years ago

Now its possible to load fully functional sample Code on Sensiron homepage:

https://www.sensirion.com/fileadmin/user_upload/customers/sensirion/Dokumente/13_Sample_Codes_Software/CO2_Sensors/Sensirion_CO2_Sensors_SCD30_Sample_Code_STM32VLDISCOVER_STM32F100.zip

I hope this helps someone by implementing it to Tasmota

I can help as Beta Tester and maybe also do some debugging but the Tasmota code is too complex for my Basic programming skills to build it completely by my own.

downset commented 5 years ago

today i got some time to do a "quick and dirty" implementation. i used the senseair file as template because i don't understand how to implement a fully new sensor to tasmota.

maybe someone is interested to use it or maybe also to do a "real" integration the delays are not tested if they are really all needed but at the beginning i had some problems without them.

xsns_17_senseair.zip

ascillato2 commented 5 years ago

Hi, there is someone working on a Tasmota's driver for this sensor?

Frogmore42 commented 5 years ago

I am looking at it. I just got a sensor recently.

sschimmel commented 5 years ago

Looking for it as well.

I have the sensor and have it working with a basic Arduino script. Will give it a try to implement in Tasmota. First time working with Tasmota so may take some time :-)

mi-hol commented 5 years ago

@andrethomas there is another CO2 sensor named SGP30 with a price tag of 10,40 € for quantity = 1. Datasheet

andrethomas commented 5 years ago

@mi-hol Thx for sharing the link - I see there are some module boards on ali for ~$15 which makes it a good candidate for implementation.

I'd have to wait for them to be available from BG - That's the only import option for me at the moment.

For Co2 there is currently MH-Z19 and BME680 which is still incoming from BG and I am not sure to what extent the Co2 measurement is implemented in the BME680 yet (not looked at driver yet)

Thanks for the info though, appreciated.

pawel-smth commented 5 years ago

@mi-hol as of SGP30, Tasmota already has module for it. However if to use CJMCU-30 implementation from Ali with esp8266/wemos, it simply doesn't work. It fails to communicate via i2c - message's CRC results in error. If to disable CRC check in the library, apparently after 10-20 readings it starts to return values that correlates with ones from my mh-z19. I've tested it on 4 different sensors bought from two suppliers combined with 3 wemos. At the same time, it works with Arduino.

fundix commented 5 years ago

I am using this sensor with wemos esp8266 but I must write own particularly working script. I’m using I2C. It’s a great sensor.

Frogmore42 commented 5 years ago

I am starting to look at how to add the SCD30. It has several options, what would people like to see?

It allows setting an altitude correction, setting pressure compensation, or just using defaults. Perhaps some set options?

Single sample at interval or run continuously and pick latest, or average, several choices there too.

fundix commented 5 years ago

I am starting to look at how to add the SCD30. It has several options, what would people like to see?

It allows setting an altitude correction, setting pressure compensation, or just using defaults. Perhaps some set options?

Single sample at interval or run continuously and pick latest, or average, several choices there too.

I like to see calibrating function, because if you drop sensor on the floor, you can get different value then before and you must set offset or recalibrate sensor

downset commented 5 years ago

I would like t o have the opportunity to do a manual calibration via Terminal, cause the Autocalibration dosn`t work correct in my Environment. (cause i never see 400ppm with my fish Tanks in the room) altitude and pressure would be nice to have but not real necessary imho.

Greets downset

Jason2866 commented 5 years ago

@Frogmore42 Nice to see would be a automatic altitude correction, pressure compensation if a BMP280 is connected to

Frogmore42 commented 5 years ago

@Jason2866 that is an interesting idea.

Here is an update on my progress. All of the examples I have seen work poorly with the SCD30 and esp8266. They might work fine for a little bit (mins to hours) but eventually fail with a hung i2c bus.

I have traced this to the "interesting" way the SCD30 has implemented i2c and the challenges it presents to the way the esp8266 implements it.

The SCD30 datasheet says it uses clock stretching and while technically the i2c bus supports an infinite clock stretch time the esp8266 does not appear to like it.

I believe I have been able to work around this by increasing the clock stretch time out significantly and using a delay(1) between writing to and trying to read from the SCD30. Without this, the communication FAILURE rate was close to 100%.

The Adafruit library appears to do little to no error checking on i2c transactions. This will, of course, get things into a bad state in short order.

Adding appropriate error checking things are better, but still fail after some time.

The SCD30 has a soft reset command. The data sheet says it is okay to use this whenever you want. But, it neglected to mention that the first CO2 result after doing that will be 0. Fair enough, that is easy to spot and ignore. It also neglected to mention that the next reading might be significantly low. A five tap median filter seems to address that pretty well.

Not sending a soft reset after every CRC error leads to the communication between the SCD30 and esp8266 getting into a very bad state and they no longer talk to each other. When in this state, the SCD30 is holding SDA low. It has data left in its buffer, so it must have missed a clock. Luckily this is a problem others have seen before and there is even a function in twi to fix it.

I am letting it run and will see what else shows up.

Jason2866 commented 5 years ago

@Frogmore42 I2C clock stretching is not bug free in Arduino Esp core. Sadly the fix from maarten-pennings isnt in 2.5.0... You could take a look at https://github.com/maarten-pennings/I2C-tool/blob/master/I2Ctest8266/README.md#how-to-fix and https://github.com/maarten-pennings/I2Cbus

Frogmore42 commented 5 years ago

Thanks for the info. I suspected there was an underlying issue. I am using 2.5.0, so I agree it still has issues. The good news is that my workarounds, seem to be working.

downset commented 5 years ago

@Frogmore42 if you need some beta testers, i am interested to Exchange my ugly implementation as soon as possible - witch is working, but with everything hardcoded i am stuck at the "run away" auto-calibration :)

pawel-smth commented 5 years ago

@Frogmore42 Nice to see would be a automatic altitude correction, pressure compensation if a BMP280 is connected to

Are you by chance aware of any mechanism that can be used to communicate readings from one sensor to another (to enable such altitude correction, for example)? I couldn't find any command that can communicate data either to sensor's lib or directly to i2c.

Frogmore42 commented 5 years ago

@PavelGorbanj I don't believe it is easy (definitely not trivial) to get information from one sensor to another directly. But, I bet it is possible to do this with rules and that is probably the better way anyway, or going one step higher to your HA system.

Frogmore42 commented 5 years ago

I made some progress on getting commands to work, but this is tricky. It appears that using SensorXX only allows providing a single number which then needs to be interpreted. xsns_15 (the MH-Z19 sensor) uses this method. It works okay for that sensor, but not so well with the SCD30 which has more options. The other model for sending commands is what the MP3 player uses with xdrv_14. But, it doesn't have a sensor. xdrv_03 has both commands and sensors and uses the number (03) for both. I was able to get this method to work for the SCD30, so it can listen to commands and also send sensor reports.

But, now it probably makes sense to assign a number for this sensor/driver. I have it at 92 currently, which is open for both currently, but I suspect this will be confusing.

@arendst is this your intention for how this should be done, i.e. a single file that defines both XSNS_xx and XDRV_xx?

Is there a reason that XSNS and XDRV are separate?

I had it running for 8 days straight and while it had to reset the i2c bus 79 times it did keep running. Looking at the data, it is likely that the underlying core is not handling i2c clock stretching properly. Since that is not likely to be fixed before 2.6.0, having the workaround is good.

andrethomas commented 5 years ago

@Frogmore42

It appears that using SensorXX only allows providing a single number which then needs to be interpreted

You can send multiple parameters using commas for example since the entire parameter to the SensorXX command is transferred to XdrvMailbox

struct XDRVMAILBOX {
  uint16_t      valid;
  uint16_t      index;
  uint16_t      data_len;
  uint16_t      payload16;
  int16_t       payload;
  bool          grpflg;
  uint8_t       notused;
  char         *topic;
  char         *data;
} XdrvMailbox;

Take a peek at xsns_29_mcp230xx.ino to see one way of doing this.

Frogmore42 commented 5 years ago

I saw that too. It looked quite complicated and not clear that it was code space efficient. There is built in command parsing that the main code uses as does DRV_xx. That looked like an easier to understand way to implement it, but it was not clear to me what the intent of the various ways were, ie why does DRV not have sensor calls, why does energy do it with both DRV and SNS?

There are few comments in the code so divining intent is much harder. My suspicion is that the code evolved and was implemented at different times with different needs in mind and different features were added at different times. My take on it is the SCD is quite complex and has its own non-volatile store, so SetOption is not necessarily the best way to configure it (the SCD doesn't need the non-volatile storage that SetOption provides). The simple SensorXX command interface is too simple for the SCD30. The more complex SetOption way seems hard to use and hard to implement. This is why I picked the DRV method as used by the MP3 player. It just requires also using the standard SNS stuff for reporting. Since energy uses this model, it looks like I am not doing something completely new. But, it does require using the same number for SNS and DRV. Since it might be confusing that no one else should use the SNS number (assuming all the code is in the DRV file), it might make sense to have a stub SNS file that does nothing, or split the code up (but that feels wrong and/or messy/hard to do with INO files).

andrethomas commented 5 years ago

@arendst will most likely explain it better but my understanding is that drv is for devices that need to be controlled (i.e. typically output type devices) whereas sns are for sensors or other input devices... although the firmware has evolved somewhat so there are cases where you get sns which have both sensor and driver characteristics.

My point was that the SensorXX command can take more than one parameter.

If you implement using a similar method to the MP3 driver you would still require the same amount of post processing to interpret the data portion of the command and you need to include it as a CMND in sonoff.ino...

Frogmore42 commented 5 years ago

I submitted a PR with my latest code. It is using the drv model so the commands are: scd30fw - returns the FW version scd30alt - sets/gets the altitude in meters scd30auto - sets/gets the auto calibration mode (I recommend turning it off, unless your location gets fresh air regularly) scd30cal - sets/gets the manual calibration level in ppm scd30int - sets/gets the measurement interval in seconds scd30pres - sets/gets the pressure offset in mbar/hPa scd30toff - sets/gets the temperature offset, while it does read, it is not clear setting this is a good idea.

Since the sensor is quite noisy, there is a median filter to remove outliers. The code reports both the current value from the median filter and a digitally filtered version that smooths out the value.

patrickwasp commented 6 months ago

where are the scd30 commands defined? What is the drv model, does that generate commands?

this is what I got without modifying it

17:01:42.368 CMD: scd30toff
17:01:42.382 RSL: RESULT = {"Scd30TOff":1000}

does that mean there's a 1000 degree offset? Or is that a magic number?

I tried adding an offset using 7.4 but it ignores the decimal. Is the offset in C?

17:27:39.417 CMD: scd30toff 7.4
17:27:39.426 RSL: RESULT = {"Scd30TOff":7}
Jason2866 commented 6 months ago

See datasheet https://cdn.sparkfun.com/assets/d/c/0/7/2/SCD30_Interface_Description.pdf Grad Celsius * 100

patrickwasp commented 6 months ago

Thanks @Jason2866

I also found where the commands are defined: https://github.com/arendst/Tasmota/blob/e6515883f0ee5451931b6280ff847b117de5a231/tasmota/tasmota_xsns_sensor/xsns_42_scd30.ino#L41

patrickwasp commented 6 months ago

sorry I got too excited, it's still not working as expected. I don't understand how the command is supposed to work. From looking at the code it looks like it already does the 100X conversion for me? But it doesn't let me pass in a float in the command.

https://github.com/arendst/Tasmota/blob/d6bf19190f3ddad1a338e92f97f0ab2922a973bd/tasmota/tasmota_xsns_sensor/xsns_42_scd30.ino#L281

void CmndScd30TempOffset(void) {
  uint16_t value = 0;
  if (XdrvMailbox.data_len > 0) {
    value = XdrvMailbox.payload;
    scd30.setTemperatureOffset(value);
  } else {
    scd30.getTemperatureOffset(&value);
  }
  ResponseCmndNumber(value);
};

https://github.com/arendst/Tasmota/blob/d6bf19190f3ddad1a338e92f97f0ab2922a973bd/lib/lib_i2c/FrogmoreScd30/FrogmoreScd30.cpp#L470

int FrogmoreScd30::setTemperatureOffset(float offset_degC)
{
    uint16_t offset_centiDegC;
    if (offset_degC >= 0)
    {
        offset_centiDegC = (uint16_t) offset_degC * 100;
        return (sendCommandArguments(COMMAND_SCD30_TEMPERATURE_OFFSET, offset_centiDegC));
    }
    else
    {
        return (ERROR_SCD30_INVALID_VALUE);
    }

}

Can you show me the command to set the offset by +7.4C?

edit:

Oh I see, the function has to definitions, and we're using this one since we're passing in a uint16.

int FrogmoreScd30::setTemperatureOffset(uint16_t offset_centiDegC)
{
    return (sendCommandArguments(COMMAND_SCD30_TEMPERATURE_OFFSET, offset_centiDegC));
}

When I set it to 740 using scd30toff 740 the temperature goes from 28C to -25C. I would expect it to go to 35.4C.

Frogmore42 commented 6 months ago

I wrote the driver code (years ago). I did also write this: scd30toff - sets/gets the temperature offset, while it does read, it is not clear setting this is a good idea.

I don't recall if I tested if the temp offset worked. Looking at the code, there are two methods with the same name (in the driver). One works with floats, the other with integers (16-bit). Tasmota uses integers for the commands. So you need to multiply the value you want by 100. 7.4 becomes 740.