Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 8 of 8

Thread: I2C error 4 for Wire.endTransmission(true)

  1. #1
    Junior Member
    Join Date
    Apr 2015
    Posts
    15

    I2C error 4 for Wire.endTransmission(true)

    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
    Click image for larger version. 

Name:	Clipboard_20200111.jpg 
Views:	60 
Size:	78.6 KB 
ID:	18704
    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

  2. #2
    Junior Member
    Join Date
    Apr 2015
    Posts
    15
    I tried another teensy 3.2 too to be shure it's not the controller.
    Same result there.

  3. #3
    Junior Member
    Join Date
    Apr 2020
    Posts
    2

    Question 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.

  4. #4
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    13,206
    WIRE i2c code for Teensy? With TeensyDuino installed it is : ...\hardware\teensy\avr\libraries\Wire

  5. #5
    Junior Member
    Join Date
    Apr 2020
    Posts
    2
    The Wire library is almost ever the base layer. I was asking about a higher level interface to the sensor.

  6. #6
    Senior Member+ KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    8,221
    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...

  7. #7
    Junior Member
    Join Date
    Apr 2015
    Posts
    15
    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);
    }

  8. #8
    Senior Member
    Join Date
    Nov 2012
    Posts
    1,528
    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

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •