0
\$\begingroup\$

I have an I2C device that I know the address is 0x70 (probed when connected to Raspberry Pi using i2cdetect -y 1). My issue is that the bytes I want to look at need to be called in WiringPi. The output string is seven bytes long and WiringPi looks at the device address and then looks at the byte address I want to read. That being said, would the addressing look like:

Device Addr.= 0x70: First Byte Addr.= 0x01 ----or---- First Byte Addr.=0x71

Does the address need a 'new' address identification (starting from 1) or does the address number get 'appended' (yes, I use that loosely) to the device address?


New Information: I found this documentation after sifting deeper: Upgrade Guide and Short Communication Guide

It seems that from the upgrade sheet the slave's internal address for the address byte is 0xE0 for write. The command byte I want to use the command(#3.5): 0b000001100 (which I assume in hex is: 0x0C). It then seems that the address byte for read is 0xE1. From this point how would I read from the data bytes since their registers aren't explicitly shown (look at the Upgrade Guide)?

Here is my current code in WiringPi (C++):

#include <iostream>
#include <errno.h>
#include <wiringPiI2C.h>

using namespace std;

#define SLAVE_ADDR 0x70
#define DEV_ADDR_W 0XE0
#define DEV_ADDR_R 0XE1
#define DEV_COMMAND 0X0C //GET STATUS


int main(){
    int fd,writeRes,comm_res,readRes;
    fd=wiringPiI2CSetup(SLAVE_ADDR);
    cout<<"Initial Result: "<<fd<<endl;

    for(int i=0;i<1;i++){
        writeRes=wiringPiI2CWriteReg8(fd,DEV_ADDR_W,0);
        cout<<"Write to Address(write): "<<writeRes<<endl;
        comm_res=wiringPiI2CWriteReg8(fd,DEV_COMMAND,0);
        cout<<"Slave Command Acknowledge: "<<comm_res<<endl;
        readRes=wiringPiI2CWriteReg8(fd,DEV_ADDR_R,0);
        cout<<"Write to Recieve Data: "<<readRes<<endl;
        int readD_1=wiringPiI2CReadReg8(fd,0x0D);
        cout<<"Data 1, VOC= "<<readD_1<<endl;
    }

    return 0;
}

\$\endgroup\$
14
  • \$\begingroup\$ 0x70? Is it an I²C mux? \$\endgroup\$
    – Janka
    Commented Mar 1, 2018 at 19:56
  • \$\begingroup\$ @Janka, Unless Raspberry Pi explicity uses a multiplexer the answer is no. I'm still inexperienced with I2C on the hardware side. \$\endgroup\$ Commented Mar 1, 2018 at 20:06
  • \$\begingroup\$ What is the part number of the device you are trying to access? \$\endgroup\$
    – crj11
    Commented Mar 1, 2018 at 20:09
  • 1
    \$\begingroup\$ I highly recommend pigpio over WiringPi. Take a look at my answer here raspberrypi.stackexchange.com/questions/3627/… for an example of how easy it is to do repeated starts \$\endgroup\$
    – crj11
    Commented Mar 1, 2018 at 21:03
  • 1
    \$\begingroup\$ @BrandonWilliams - I can't help with your question regarding WiringPi as I don't use it (so not writing this as an answer) but there is a document for your device which specifically describes its I2C communication protocol (and the fact that there is a required delay). So if you haven't seen it yet, read this: SGX VZ89TE I2C communication. \$\endgroup\$
    – SamGibson
    Commented Mar 1, 2018 at 21:04

2 Answers 2

0
\$\begingroup\$

Please see the follwing C source code, using the standard Linux I²C driver. The key is understanding the ioctl() parameter preparation. The complete I²C transfer is handled by the kernel driver automatically.

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
#include <inttypes.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>

int main(int argc, char* argv[]) {
    struct i2c_msg i2c_msgs[2];
    struct i2c_rdwr_ioctl_data i2c_transfer;
    uint8_t rdata[6];
    int fd;

    /* Open I2C device. */
    fd = open("/dev/i2c-1", O_RDWR);
    if (fd < 0) {
        perror("i2c device open");
        return 2;
    }

    /* Select and read CO2 register. */
    i2c_msgs[0].addr   = 0x70;
    i2c_msgs[0].flags  = 0;
    i2c_msgs[0].len    = 1;
    i2c_msgs[0].buf    = "\x08";

    i2c_msgs[1].addr   = 0x70;
    i2c_msgs[1].flags  = I2C_M_RD;
    i2c_msgs[1].len    = 6;
    i2c_msgs[1].buf    = (char*)&rdata;

    i2c_transfer.msgs  = i2c_msgs;
    i2c_transfer.nmsgs = 2;

    if (ioctl(fd, I2C_RDWR, &i2c_transfer) < 0) {
        perror("i2c CO2 read");
        return 2;
    };

    /* Print result. */
    printf("%02x %02x %02x %02x %02x %02x\n", rdata[0], rdata[1], rdata[2], rdata[3], rdata[4], rdata[5]);

    /* Finish. */
    close(fd);
    return 0;
}
\$\endgroup\$
11
  • \$\begingroup\$ Are we ignoring the 7th byte since you defined length of rdata array as 6? \$\endgroup\$ Commented Mar 1, 2018 at 21:15
  • \$\begingroup\$ The code on that site pocketmagic.net/mics-vz-89-air-quality-sensor says it's 6 bytes to read. \$\endgroup\$
    – Janka
    Commented Mar 1, 2018 at 21:28
  • \$\begingroup\$ If you are confused, what the kernel driver does is S,0x70WR, 0x08,S,0x70RD,xx,xx,xx,xx,xx,xx,P \$\endgroup\$
    – Janka
    Commented Mar 1, 2018 at 21:30
  • \$\begingroup\$ In the code, i get several errors. The first is the struct i2c_msg i2c_msgs[2]; gets an error saying the array type has and incomplete element type. And secondly, did you mean something else than I2C_M_RD like something from here \$\endgroup\$ Commented Mar 1, 2018 at 23:42
  • \$\begingroup\$ Please add #include <sys/ioctl.h> and #include <linux/i2c.h> to the list of includes. Unfortunately, the explicit includes needed change with each API version. \$\endgroup\$
    – Janka
    Commented Mar 2, 2018 at 0:30
0
\$\begingroup\$

[Solved] This module is a useful tool once you learn how to work with it. After emailing Gordon (from wiringPi), it was pointed out that I was trying to look too deep. Raspberry Pi took care of what I was worried about. Knowing that it took some trial and error to get addresses and commands correct but my following code seems to work before close calibration and testing for the CO2 sensitivity (note: the 'cout' output reading out the data buffer is off. But that's not too important right now):

#include <iostream>
#include <unistd.h>
#include <cstdint>
#include <wiringPiI2C.h>
#include <cerrno>

using namespace std;

const uint8_t dev_id = 0x70;
const uint8_t dev_addr = 0b000001100;
const uint8_t dev_comm = 0x0C;
const uint8_t dev_data = 0x00;

uint8_t getCRC(uint8_t bytes_to_send[6]){
    uint8_t byte_sum = 0x00;
    uint8_t crc;
    for(int i=0; i<6 ; i++){
        byte_sum += bytes_to_send[i];
    }
    crc = byte_sum;
    crc += (byte_sum/(0x0100));
    crc = 0xFF - crc;
    return crc;
}

int main(){
    int fd;
    int write_res;
    uint8_t sensor_data[8];
    int read_res;
    uint8_t crc;
    uint8_t out_buff[6]{dev_addr, dev_comm, dev_data, dev_data, 
        dev_data, dev_data};
    crc = getCRC(out_buff);
    cout<<"CRC: "<<(int)crc<<endl;
    uint8_t out_buff_full[7]{dev_addr, dev_comm, dev_data, dev_data, 
        dev_data, dev_data, 0xFF /*crc*/};
    if((fd=wiringPiI2CSetup(0x70)) < 0){
        cout<<"Error Occured in setting up Dev_ID: 0x70"<<endl;
        return 0;
    }
    else{
        cout<<"FD= "<<fd<<endl;
        cout<<"-------------Before Writing, Check Sensor Array---------------"<<endl;
        read_res = read(fd, sensor_data, 8);
        cout<<"1: "<<(int)sensor_data[0]<<endl;
        cout<<"2: "<<(int)sensor_data[1]<<endl;
        cout<<"3: "<<(int)sensor_data[2]<<endl;
        cout<<"4: "<<(int)sensor_data[3]<<endl;
        cout<<"5: "<<(int)sensor_data[4]<<endl;
        cout<<"6: "<<(int)sensor_data[5]<<endl;
        cout<<"7: "<<(int)sensor_data[6]<<endl;
        cout<<"8: "<<(int)sensor_data[7]<<endl;
        cout<<"-------------End Test Before Write---------------"<<endl;
        if((write_res = write(fd, out_buff_full, 7)) != 7){
            cout<<"There could be an issue writing to the file."<<endl;
            cout<<"Write returned: "<<write_res<<endl;
            return 0;
        }
        else{
            sleep(5);
            if((read_res = read(fd, sensor_data, 8)) != 8){
                cout<<"There could be an issue reading from the file."<<endl;
                cout<<"Read returned: "<<read_res<<endl;
                return 0;
            }
            else{
                cout<<"Returned Data {Address}: "<<(int)sensor_data[0]<<endl;
                cout<<"Returned Data {VOC}: "<<(int)sensor_data[1]<<endl;
                cout<<"Returned Data {CO2}: "<<(int)sensor_data[2]<<endl;
                cout<<"Returned Data {Raw Sensor Value[MSB]}: "<<(int)sensor_data[3]<<endl;
                cout<<"Returned Data {Raw Sensor Value}: "<<(int)sensor_data[4]<<endl;
                cout<<"Returned Data {Raw Sensor Value[LSB]}: "<<(int)sensor_data[5]<<endl;
                cout<<"Returned Data {Error Byte}: "<<(int)sensor_data[6]<<endl;
                cout<<"Returned Data {CRC}: "<<(int)sensor_data[7]<<endl;
            }
        close(fd);
        }
    }
    return 0;
}

This code has been verified to work and solves my problem.

Lesson: Raspberry Pi is a full computer (even for comm. protocols) and you can't go too far down the rabbit hole, you'll get lost.

I just hope this helps anyone who may need it.

\$\endgroup\$

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.