5
\$\begingroup\$

EDIT - Corrected code is at the bottom of the OP.

Background

While working on a circuit to log carbon dioxide with a K30 CO2 sensor using an Adafruit Data Logging Shield, I ran into a problem. The I2C address of the RTC on the shield has a collision with the address of the CO2 sensor. The address of the RTC is baked into the silicon and cannot be changed, but the address of the K30 sensor is mutable. According to the I2C guide (see PDF in I2C/I2C_comm_guide, zipped PDF warning!) the I2C address of the sensor can be any value:

0x00 to 0x7F

Precedent

This appears to be a common problem. I have found multiple threads (see a, b, c, d, e, f) which have attempted to solve this challenge. Most either fizzle with positive results or suggest using a different interface. YET, according the the datasheet, it should be possible to hack this change to the EEPROM.

Workarounds I Am Avoiding

  • K30 RX/TX - Since the K30 has both I2C and RX/TX pins, why not switch to using the RX/TX and skip this whole mess? The sensor is mounted on a breakout board with many other parts and a protective shield epoxied onto it. I would like to avoid digging into the hardware to reach these pins.
  • GasLab software - the manufacturer provides the GasLab software to hack the sensors using a GUI over USB. Unfortunately, it only works for Windows.
  • Choose a different RTC - I need something quick and easy to log values as I am time constrained. I don't want to reinvent the wheel when the Adafruit shield works so well.
  • Address 0x7F - According to the datasheet, I should be able to call all K30 sensors using this address, however I could not get it to work in practice when in the same circuit as the RTC.

My Hardware

I am running an Arduino Uno clone wiring the I2C to A4 and A5. The I2C lines have 2.2K pullups. The K30 is running at ~9V.

Sanity Check

Running the Arduino code provided by the manufacturer, I am able to read the sensor response, and it makes sense.

Code to change EEPROM

#include <Wire.h>

byte CheckSum(byte * buf, byte count) {
    byte sum=0;
    while (count>0) {
        sum += *buf;
        buf++;
        count--;
    }
    return sum;
}

void setup() {
    Serial.begin(9600);
    Wire.begin();
    delay(1000);

    byte changebuf[4] = {0x31, 0x00, 0x69, 0x00};
    changebuf[3] = CheckSum(changebuf, 3);
    
    Wire.beginTransmission(0x68<<1);
    for (size_t i = 0; i<sizeof(changebuf); i++) {
        Wire.write(changebuf[i]);
    }
    Wire.endTransmission();
}

void loop() {}

The message is 0xD0, 0x31, 0x00, 0x69, 0x9A. Bytes are:

  1. The default address shifted left by one (I have also tried plain 0x68)
  2. Command to write 1 byte to EEPROM
  3. Address in EEPROM to write
  4. Payload to write
  5. Checksum

These values are based on the datasheet.

Table of byte formatting for EEPROM/RAM access from the K30 sensor datasheet.

After running this, I power cycle the device so the EEPROM values propagate to the RAM per the datasheet.

Observations

After running the above code, the address is not changed. The device still responds to the original default address 0x68 and fails to respond to the new address 0x69.

The Question Restated

Why is my attempt to command the EEPROM of the K30 to change the value associated with the device I2C address failing? Is there something wrong with the byte sequence I send?

SOLUTION - Per accepted solution, I missed the obvious extra LSB in the byte string. I am now able to change the address. Here is the complete corrected code to achieve this:

#include <Wire.h>

byte CheckSum(byte * buf, byte count) {
    byte sum=0;
    while (count>0) {
        sum += *buf;
        buf++;
        count--;
    }
    return sum;
}

void setup() {
    Serial.begin(9600);
    Wire.begin();
    delay(1000);

    byte changebuf[5] = {0x31, 0x00, 0x00, 0x69, 0x00};
    changebuf[4] = CheckSum(changebuf, 4);
    
    Wire.beginTransmission(0x68);
    for (size_t i = 0; i<sizeof(changebuf); i++) {
        Wire.write(changebuf[i]);
    }
    Wire.endTransmission();
}

void loop() {}
\$\endgroup\$

3 Answers 3

4
\$\begingroup\$

The eeprom address is two bytes. You only supplied one. The eeprom address you want is 0x00 0x00

\$\endgroup\$
1
  • 1
    \$\begingroup\$ I love when a problem I struggled for hours is an obvious fix I overlooked. Changing the code passed to 0xD0, 0x31, 0x00, 0x00, 0x69, 0x9A did the trick. Note that I used the original address of 0x68 without bit shifting. I will add corrected code to the OP. \$\endgroup\$
    – WesH
    Commented Aug 10, 2021 at 18:43
0
\$\begingroup\$

One thing, if the default address was 0x68, shifted by one is 0xd0, not 0x69.

\$\endgroup\$
3
  • 1
    \$\begingroup\$ Thanks for the comment. I am trying to change from 0x68 to 0x69. The left shifted value for 0x68 is 0xD0 and upon successful address change to 0x69 the left shifted value will be 0xD2. \$\endgroup\$
    – WesH
    Commented Aug 10, 2021 at 18:31
  • \$\begingroup\$ Thanks @WesH , for not whacking me. :-) I will come back later, and check if you are still wondering around. Meantime, if you need to push, then nudge me here. I will try to help. \$\endgroup\$
    – jay
    Commented Aug 10, 2021 at 18:41
  • \$\begingroup\$ No worries @jay. The solution I accepted shows I have a hard time reading things too! \$\endgroup\$
    – WesH
    Commented Aug 10, 2021 at 18:45
0
\$\begingroup\$

K30 CO2 Sensor changing I2C default address. The reference I’m working from is I2C communications guide 2.15 from Gaslab.com site. http://co2meters.com/Documentation/Other/SenseAirCommGuide.zip The ZIP has manuals for the UART and I2C operation. The end of the I2C guide has syntax examples.

Presuming you are successfully reading the CO2 measurement from the sensor RAM location 0x08, so we know you have operating I2C. Also you can read RAM location 0x20, and EEPPROM address 0x0 for the current address which should be 0x68. Write RAM location 0x20 to your desired address, check you checksum is correct for the message, remember not to include the I2C address byte. see 9.0 When changing the base address, what many miss, is sending the command to write RAM to EEPROM before power cycling the sensor. Use Special command register SCR 0x60 to write RAM to EEPROM see 8.7.

The alternative method I use is to write EEPROM directly. Change the command from writing RAM to writing EEPROM. Write EEPROM address 0 to the desired address then power cycle.

VBR

\$\endgroup\$
1
  • \$\begingroup\$ Thanks for making an account to respond here Ray. In the OP, I included a link to the same doc you suggest and came up with the same solution, directly writing to the EEPROM with 0x3_. It turns out I was missing a 0x00 byte in my original message. At the end of the OP, I posted a corrected version of the simple code to perform this task for future users. \$\endgroup\$
    – WesH
    Commented Aug 17, 2021 at 18:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.