I2C error 4 for Wire.endTransmission(true)

Status
Not open for further replies.
I have a "Senseair Sunrise" CO2 sensor and i got a reference code for that.
It works perfect an an arduino UNO with the following connections
Clipboard_20200111.jpg
I soldered the two connections on the sensor itself directly there. So just 5 wires go to the controller.
Then i connected the same sensor to a teensy 3.2:

Sensor -> Teensy:
GND -> GND (next to pin 0) (black)
VCC -> 3.3V (next to pin 23) (red) (Voltage is there. I checked it)
SCL -> SCL / pin19 (yellow) (i tripple checked it)
SDA -> SDA / Pin 18 (blue) (i tripple checked it)
Enable -> pin 8 (green) (same pin as the arduino)

I permanemtly get an error 4 for Wire.endTransmission(true);
I reduced the code to the minimum for analyses here
It still gives the same error:

Console text:
Error 4
Error 4
Error 4
Error 4
Error 4



Code:
#include <Wire.h>

const int       SUNRISE_EN         = 8;     //serial EN pin
const uint8_t   SUNRISE_ADDR       = 0x68;  // communication address
const int       ATTEMPTS           = 5;     // Amount of wakeup attempts before time-out

/* Delays in milliseconds*/
const int STABILIZATION_MS         = 35;

//███████████████████████████████████████████████████████████████
void  reInitI2C() { // Initialize I2C
  Wire.begin();
  Wire.setClock(100000);  // Setup I2C clock to 100kHz
}
//███████████████████████████████████████████████████████████████
bool _wakeup(uint8_t target) // Wakes up the sensor by initializing a write operation with no data.
{
  int attemps = ATTEMPTS;
  int error;

  do {
    Wire.beginTransmission(target);
    error = Wire.endTransmission(true);
    Serial.print("Error  ");
    Serial.println(error);
  } while (((error != 0 /*success */) && (error != 2 /*Received NACK on transmit of address*/)) && (--attemps > 0));
  if (error == 4) {
    reInitI2C();
    return false;
  }
  return (attemps > 0);
}
//███████████████████████████████████████████████████████████████
void setup()
{
  pinMode(SUNRISE_EN, OUTPUT);
  digitalWrite(SUNRISE_EN, HIGH);

  delay(STABILIZATION_MS); // Wait for sensor start-up and stabilization

  reInitI2C(); // Initialize I2C

  Serial.begin(115200);

  _wakeup(SUNRISE_ADDR);
}
//███████████████████████████████████████████████████████████████
void loop() {
}

I used no external resistors for pullup etc.
I tis confusing, because i used so many I2C devices without any trouble on teensys up to now.
Does anybody have an idea for solution or is further information needed?
Regards
 
Complete code?

I just started working with the SenseAir Sunrise. Is it possible to have the complete i2c code? Perhaps I can then also help find the bug.
 
The Wire library is almost ever the base layer. I was asking about a higher level interface to the sensor.
 
The above stuff is just showing using the basic Wire library.

The first thing I would do when I hooked it up would be to first see if the Wire scanner sketch sees this device or not. Examples->Wire->Scanner

If not found (or even if it was), I would try adding external Pull-up resistors. As unclear if they have any on both SCL/SDA
Code:
4 RxD/SDA1/0 Sensor UART receive input / 12 C bidirectional serial data; True Open-Drain when operating as output.
5 TxD/SCL1/0Sensor UART transmit output / 12 C clock input;True Open-Drain when operating as output, 100kO internal Pull-Up to VDDIO.

Error code 4 I believe is that the Teensy timed out waiting for it to become the master...
 
This is my complete code wich runs on the arduino uno.


Code:
#include <Wire.h>
#include <SoftwareSerial.h>

const int       SUNRISE_EN              = 8; //serial EN pin
const uint8_t   SUNRISE_ADDR            = 0x68; // communication address
const int       ATTEMPTS                 = 5; // Amount of wakeup attempts before time-out

const uint8_t ERROR_STATUS             = 0x01; // Register Addresses
const uint8_t MEASUREMENT_MODE         = 0x95;
const uint8_t START_MEASUREMENT        = 0xC3;
const uint8_t ABC_TIME                 = 0xC4;

const uint16_t CONTINUOUS               = 0x0000; // Measurement modes
const uint16_t SINGLE                   = 0x0001;

/* Delays in milliseconds*/
const int STABILIZATION_MS              = 35;
const int WAIT_FOR_PIN_MS               = 2000;

int readPeriodMs = 4000; // Reading period, in milliseconds. Default is 4 seconds

uint8_t state[24]; // Array for storing sensor state data


//▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀
void  reInitI2C() { // Initialize I2C bus and pins
  /* Initialize I2C and use default pins defined for the board */
  Wire.begin();
  /* Setup I2C clock to 100kHz */
  Wire.setClock(100000);  
}
//▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀
bool _wakeup(uint8_t target) // Wakes up the sensor by initializing a write operation with no data.
{
  int attemps = ATTEMPTS;
  int error;
 
  do {
    uint8_t byte_0;    
    /* */
    Wire.beginTransmission(target);
    error = Wire.endTransmission(true);
  } while(((error != 0 /*success */) && (error != 2 /*Received NACK on transmit of address*/) && (error != 1 /* BUG in STM32 library*/)) && (--attemps > 0)); 
  /* STM32 driver can stack under some conditions */
  if(error == 4) {
    /* Reinitialize I2C*/
    reInitI2C();
    return false;
  } 
  return (attemps > 0);
}
//▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀
void read_sensor_config(uint8_t target) {  // Reads and prints the sensor's current measurement mode, measurement period and number of samples.
  /* Function variables */
  int error;
  int numBytes = 5;

  /* Wakeup */
  if(!(_wakeup(target))) {
    Serial.print("Failed to wake up sensor.");
    return;
  }

  /* Request values */
  error = Wire.requestFrom((uint8_t)target, (uint8_t)numBytes /* how many bytes */, (uint32_t)MEASUREMENT_MODE /* from address*/, (uint8_t)1/* Address size - 1 byte*/, true /* STOP*/);    
  if(error != numBytes ) {
    Serial.print("Failed to write to target. Error code : ");
    Serial.println(error);
    return;
  }

  /* Read values */
  /* Measurement mode */
  uint8_t measMode = Wire.read();

  /* Measurement period */
  uint8_t byteHi = Wire.read();
  uint8_t byteLo = Wire.read();
  uint16_t measPeriod = ((int16_t)(int8_t) byteHi << 8) | (uint16_t)byteLo;

  /* Number of samples */
  byteHi = Wire.read();
  byteLo = Wire.read();
  uint16_t numSamples = ((int16_t)(int8_t) byteHi << 8) | (uint16_t)byteLo;

  Serial.print("Measurement Mode: ");
  Serial.println(measMode);
  readPeriodMs = measPeriod * 1000;

  Serial.print("Measurement Period: ");
  Serial.println(measPeriod);

  Serial.print("Number of Samples: ");
  Serial.println(numSamples);  
}
//▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀
void change_measurement_mode(uint8_t target) { // Changes the sensor's current measurement mode, if it's currently in single mode.
  /* Function variables */
  int error;
  int numBytes = 1;
  
  /* Wakeup */
  if(!(_wakeup(target))) {
    Serial.print("Failed to wake up sensor.");
    /* FATAL ERROR */
    while(true);
  }

  /* Read Value */
  error = Wire.requestFrom((uint8_t)target, (uint8_t)numBytes /* how many bytes */, (uint32_t)MEASUREMENT_MODE /* from address*/, (uint8_t)1/* Address size - 1 byte*/, true /* STOP*/);    
  if(error != numBytes ) {  
    Serial.print("Failed to read measurement mode. Error code: ");
    Serial.println(error);
    /* FATAL ERROR */
    while(true);
  }

  /* Change mode if continuous */
  if(Wire.read() != SINGLE) {
    Serial.println("Changing Measurement Mode to Single...");
    /* Wakeup */
    if(!_wakeup(target)) {
      Serial.print("Failed to wake up sensor.");
      /* FATAL ERROR */
      while(true);
   }
    Wire.beginTransmission(target);
    Wire.write(MEASUREMENT_MODE);
    Wire.write(SINGLE);
    error = Wire.endTransmission(true);
    
    if(error != 0) {
      Serial.print("Failed to send request. Error code: ");
      Serial.println(error); 
      /* FATAL ERROR */
      while(true);
    }
    Serial.println("Sensor restart is required to apply changes");
     /* Turn-off sensor */
    digitalWrite(SUNRISE_EN, LOW);
    /* Wait for sensor restart */
    delay(STABILIZATION_MS);
    /* Turn-on sensor */
    digitalWrite(SUNRISE_EN, HIGH);
  }
}
//▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀
void save_state(uint8_t target) { // Reads the sensors current state data
  /* Function variables */
  int error;

  int numBytes = 24;

  digitalWrite(SUNRISE_EN, HIGH); // Drive EN pin HIGH

  delay(STABILIZATION_MS); // Wait for sensor start-up and stabilization

  if(!(_wakeup(target))) { // Wakeup
    Serial.print("Failed to wake up sensor.");
    /* FATAL ERROR */
    digitalWrite(SUNRISE_EN, LOW);
    while(true);
  }

  /* Request state data */
  error = Wire.requestFrom((uint8_t)target, (uint8_t)numBytes /* how many bytes */, (uint32_t)ABC_TIME /* from address*/, (uint8_t)1/* Address size - 1 byte*/, true /* STOP*/);    
  if(error != numBytes ) { 
    Serial.print("Failed to read measurements command. Error code: ");
    Serial.println(error);
    digitalWrite(SUNRISE_EN, LOW);
    /* FATAL ERROR */
    while(true);
  }

  for(int n = 0 ; n < numBytes ; n++) { // Read and save state data
    state[n] = Wire.read();
  }

  digitalWrite(SUNRISE_EN, LOW); // Drive EN pin LOW

  Serial.println("Saved Sensor State Successfully\n");
}
//▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀
void read_sensor_measurements(uint8_t target) { // Reads and prints the sensor's current CO2 value and error status.
  int error;

  int numRegCmd = 25;
  int numRegRead = 7;
  int numRegState = 24;

  uint8_t cmdArray[25];

  cmdArray[0] = 0x01;

  for(int n = 1 ; n < numRegCmd ; n++) {
    cmdArray[n] = state[n-1];
  }

  digitalWrite(SUNRISE_EN, HIGH); // Drive EN pin HIGH

  delay(STABILIZATION_MS); // Wait for sensor start-up and stabilization

  if(!_wakeup(target)) { // Wakeup
    Serial.print("Failed to wake up sensor.");
    return;
  }

  Wire.beginTransmission(target); // rite measurement command and sensor state to 0xC3
  Wire.write(START_MEASUREMENT);
  for(int reg_n =0; reg_n < numRegCmd; reg_n++) {
    Wire.write(cmdArray[reg_n]);
  }
  error = Wire.endTransmission(true);
  if(error != 0) {
    Serial.print("Failed to send measurement command. Error code: ");
    Serial.println(error);
    digitalWrite(SUNRISE_EN, LOW);
    return;
  }

  delay(WAIT_FOR_PIN_MS); // Wait until ready pin goes low

  if(!(_wakeup(target))) { // Wakeup
    Serial.print("Failed to wake up sensor.");
    digitalWrite(SUNRISE_EN, LOW);
    return;
  }

  /* Request values */
  error = Wire.requestFrom((uint8_t)target, (uint8_t)numRegRead /* how many bytes */, (uint32_t)ERROR_STATUS /* from address*/, (uint8_t)1/* Address size - 1 byte*/, true /* STOP*/);    
  if(error != numRegRead ) {  
    Serial.print("Failed to read values. Error code: ");
    Serial.println(error);
    digitalWrite(SUNRISE_EN, LOW);
    return;
  }

  uint8_t eStatus = Wire.read(); // Error status

  uint8_t byteHi = Wire.read(); // Reserved
  uint8_t byteLo = Wire.read();

  byteHi = Wire.read();
  byteLo = Wire.read();

  byteHi = Wire.read(); // CO2 value
  byteLo = Wire.read();
  uint16_t co2Val = ((int16_t)(int8_t) byteHi << 8) | (uint16_t)byteLo;

  if(!_wakeup(target)) { // Wakeup
    Serial.print("Failed to wake up sensor.");
    digitalWrite(SUNRISE_EN, LOW);
    return;
  }

  /* Read sensor state data from 0xC4-0xDB and save it for next measurement */
  error = Wire.requestFrom((uint8_t)target, (uint8_t)numRegState /* how many bytes */, (uint32_t)ABC_TIME /* from address*/, (uint8_t)1/* Address size - 1 byte*/, true /* STOP*/); 
  if(error != numRegState) {
    Serial.print("Failed to read measurements command. Error code: ");
    Serial.println(error);
    digitalWrite(SUNRISE_EN, LOW);
    return;
  }
  
  for(int n = 0 ; n < numRegState ; n++) {
    state[n] = Wire.read();
  }

  digitalWrite(SUNRISE_EN, LOW); // Drive EN pin LOW

  Serial.print("CO2: "); // Print values
  Serial.print(co2Val);
  Serial.println(" ppm");

  Serial.print("Error Status: 0x");
  Serial.println(eStatus, HEX);
  Serial.println();
}
//███████████████████████████████████████████████████████████████
void setup()
{
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, HIGH);

  pinMode(SUNRISE_EN, OUTPUT);
  digitalWrite(SUNRISE_EN, HIGH);

  delay(STABILIZATION_MS); // Wait for sensor start-up and stabilization

  reInitI2C(); // Initialize I2C and use default pins defined for the board
 
  Serial.begin(115200);

  Serial.println("Initialization complete\n");

  Serial.println("Sensor Measurement Configurations:");
  read_sensor_config(SUNRISE_ADDR); // Read the sensor's configs
  Serial.println();

  change_measurement_mode(SUNRISE_ADDR); // Change measurement mode if continuous
 
  digitalWrite(SUNRISE_EN, LOW); 

  Serial.println("Saving Sensor State");
  save_state(SUNRISE_ADDR); // Initial measurement

  delay(readPeriodMs);
}
//███████████████████████████████████████████████████████████████
void loop() {
  static int pin_value = HIGH;
  static unsigned long last_abc_stamp = 0;

  if(3600000 < (unsigned long)((long)millis() - (long)last_abc_stamp)) { // When an hour has passed, increase ABC Time
    /* Use ABC time stored in the sensor state */
    uint16_t abc_time = ((int16_t)(int8_t) state[0] << 8) | (uint16_t)state[1];
    abc_time = abc_time + 1;
    state[0] = abc_time >> 8;
    state[1] = abc_time & 0x00FF;
    
    last_abc_stamp = millis();
    Serial.println("ABC time incremented.");
  }
  read_sensor_measurements(SUNRISE_ADDR); // Read measurements

  Serial.println("\nWaiting...\n");
  delay(readPeriodMs); // Delay between readings

  digitalWrite(LED_BUILTIN, pin_value); // Indicate working state
  pin_value  = ((pin_value == HIGH) ? LOW : HIGH);
}
 
One of the documents referenced by Senseair says this:
Since Senseair Sunrise only provides a 100kΩ pull-up resistor on the SCL line an external pull-up resistor must be used for SDA. To be able to use data rates up to 100kbit it is in most cases suitable to use pull-up resistors in the range 5kΩ-15kΩ on both SCL and SDA.
You need pullups on both pins.

Pete
 
Status
Not open for further replies.
Back
Top