How to read 7 bytes over I2C with wiringX

I’m very new to programming and I’m playing with a aht21 temp and humidity sensor with my duo. I’m working off the C sdk. I’ve been able to recognize the sensor and send the 3 byte trigger measurement command with WriteReg16 . The sensor returns 7 bytes of information and I need to read all of them. WiringX doesn’t seem to have a way to do this. If I try to use 7 individual wiringXI2CRead(), I just get the 1st byte over and over. Like I said I’m new to all of this so I hope this is easy. Thanks

1 Like

I tried to do it with WiringX, but unless I am mistaken it can’t be done with WiringX. I ported some of my old MicroPython i2c bitbang code over to C and got it working. It can definitely be improved, but it works. I start it with a shell script to pinmux the pins I used as GPIO. Don’t forget your pullup resistors on the SDA and SCL lines. Have fun with it. (Oh ya, if you’re trying to copy my breadboard wiring a lot of those wires are leftovers from the Sharp Memory LCD. I didn’t feel like pulling them all out yet.)

aht21b.c

#include <stdio.h>
#include <unistd.h>
#include <stdint.h>
#include <wiringx.h>
#include "aht21b.h"

unsigned char i2c_address = 0x38;

int dat = 10;
int clk = 11;
int dely = 10;
int delyhalf = 5;

unsigned char tri[3];
unsigned long int six[6];
unsigned char temp[1];

int main(){
  float temperature_c,temperature_f,humidity;

  init();
  
  while(1){
    i2c_address = i2c_address & 0xfe; //clear low bit to write
    i2c_start();
    write3(0xac,0x33,0x00);           //trigger measurement
    i2c_stop();
    
    usleep(100000);                   //wait for measurement
    //should check status here, but delay is long enough
    
    i2c_address = i2c_address | 0x01; //set low bit to read
    i2c_start();
    for(int i=0;i<5;i++)      //read first 5 bytes of measurement
      six[i] = i2c_read();
    six[5] = i2c_read_nack(); //read 6th byte with nack to end (skip crc)
    i2c_stop();
    
    //convert temperature
    float i = ((six[3] & 0x0f) << 16) | (six[4] << 8) | six[5];
    temperature_c = ((i / 1048576) * 200) -50;
    temperature_f = ((temperature_c * 9) / 5) + 32;
    printf("Temperature: %.1fF - %.1fC\n",temperature_f,temperature_c);
    
    //convert humidity
    i = ((six[1] << 16) | (six[2] << 8) | six[3]) >> 4;
    humidity = (i / 1048576) * 100;
    printf("Humidity: %.1f\n\n",humidity);
    
    sleep(2);
  }
}

void write3(unsigned char one,unsigned char two,unsigned char three){
  tri[0] = one;
  tri[1] = two;
  tri[2] = three;
  for(int i=0;i<3;i++)
    i2c_write(tri[i]);
}

int init(void){
  if(wiringXSetup("duo", NULL) == -1) {
    wiringXGC();
    return -1;
  }

  // shift address over to high bits and slide in write bit at 0
  i2c_address = i2c_address << 1;

  pinMode(dat,PINMODE_OUTPUT);
  pinMode(clk,PINMODE_OUTPUT);
  digitalWrite(dat, LOW);       //dat == 0 when set as output
  digitalWrite(clk, LOW);       //clk == 0 when set as output
  pinMode(dat,PINMODE_INPUT);   //dat pin high
  pinMode(clk,PINMODE_INPUT);   //clk pin high

  usleep(100000);                     //stabilization delay
}

//*************************
//* bitbang I2C functions *
//*************************
unsigned char i2c_write(unsigned char x){
  unsigned char i;
  usleep(dely);
  for(i=0;i<8;i++){               //clock out data byte
    pinMode(dat,PINMODE_OUTPUT);  //set data bit low
    if(x &  0x80)                 //if output bit is high
      pinMode(dat,PINMODE_INPUT); //then set data bit high
    i2c_clock();                  //clock it out
    x <<= 1;                      //shift next bit into position
  }
  //get ack
  pinMode(dat,PINMODE_INPUT);     //set data high
  pinMode(clk,PINMODE_INPUT);     //set clock high
  usleep(delyhalf);               //wait half a clock pulse
  if(digitalRead(dat))            //sample the data bit
    return(1);                    //if high then nack error
  usleep(delyhalf);               //ack good, wait other half of clock pulse
  pinMode(clk,PINMODE_OUTPUT);    //set clock low
  usleep(dely);
  pinMode(dat,PINMODE_INPUT);     //set data high
  return(0);
} 

void i2c_start(void){             //send start condition
  pinMode(dat,PINMODE_OUTPUT);    //set data low
  usleep(dely);
  pinMode(clk,PINMODE_OUTPUT);    //set clock low
  usleep(dely);
  i2c_write(i2c_address);
}

void i2c_stop(void){              //send stop condition
  pinMode(dat,PINMODE_OUTPUT);    //set data low
  usleep(dely);
  pinMode(clk,PINMODE_INPUT);    //set clock high
  usleep(dely);                   //stop delay
  pinMode(dat,PINMODE_INPUT);     //set data high
  usleep(dely);
}

void i2c_clock(void){
  pinMode(clk,PINMODE_INPUT);     //set clock high
  usleep(dely);
  pinMode(clk,PINMODE_OUTPUT);    //set clock low
  usleep(dely);
}

void i2c_restart(void){           //send start condition
  pinMode(dat,PINMODE_INPUT);     //release data
  usleep(dely);
  pinMode(clk,PINMODE_INPUT);
  usleep(dely);
  pinMode(dat,PINMODE_OUTPUT);
  usleep(dely);
  pinMode(clk,PINMODE_OUTPUT);
  usleep(dely);
}

unsigned char i2c_read(void){
  unsigned char i,tmp = 0;
  static unsigned char mbit = 0;
  usleep(dely);                   //10uS delay
  for(i=0;i<8;i++){
    pinMode(dat,PINMODE_INPUT);   //data pin high
    usleep(dely);                 //minimum clock low time
    pinMode(clk,PINMODE_INPUT);   //clk high
    usleep(delyhalf);             //1/2 min clock high time
    mbit = digitalRead(dat);      //read the data bit
    if(mbit)                      //store it in tmp
      tmp = tmp | 0x01;
    usleep(delyhalf);             //last 1/2 min clock high time
    if(i < 7)
      tmp <<= 1;                  //shift left for next bit
    pinMode(clk,PINMODE_OUTPUT);  //clk low
    usleep(dely);                 //minimum clock low time
  }
  pinMode(dat,PINMODE_OUTPUT);
  digitalWrite(dat, 0);           //clear data & send ACK
  usleep(dely);                   //data settle time
  i2c_clock();                    //pulse the clock
  pinMode(dat,PINMODE_INPUT);     //release ACK
  usleep(dely);                   //gap between next byte
  return(tmp);
}

unsigned char i2c_read_nack(void){
  unsigned char i,tmp = 0;
  static unsigned char mbit;
  usleep(dely);
  for(i=0;i<8;i++){               
    pinMode(dat,PINMODE_INPUT);   //data high
    usleep(dely);                 //minimum clock low time
    pinMode(clk,PINMODE_INPUT);   //clk high
    usleep(delyhalf);             //1/2 min clock high time
    mbit = digitalRead(dat);      //read the data bit
    if(mbit)                      //store it in tmp
      tmp = tmp | 0x01;
    usleep(delyhalf);             //last 1/2 min clock high time
    if(i < 7)
      tmp <<= 1;                  //shift left for next bit
    pinMode(clk,PINMODE_OUTPUT);  //clk low
    usleep(dely);                 //minimum clock low time
  }
  pinMode(dat,PINMODE_OUTPUT);
  digitalWrite(dat,LOW);          //clear data
  pinMode(dat,PINMODE_INPUT);     //send NACK
  usleep(dely);                   //data settle time
  i2c_clock();                    //pulse the clock
  return(tmp);
}

aht21b.h

void write3(unsigned char,unsigned char,unsigned char);
int init(void);
unsigned char i2c_read(void);
unsigned char i2c_read_nack(void);
unsigned char i2c_write(unsigned char);
void i2c_start(void);
void i2c_restart(void);
void i2c_stop(void);
void i2c_clock(void);

aht21b-script

duo-pinmux -w GP10/GP10
duo-pinmux -w GP11/GP11
./aht21b
1 Like

Thanks. It totally answers my question, it’s too bad you can’t use wiringX to read the aht21. I’ll give you code a try. I should be able to get it running.

2 Likes

See if you think temperature readings seem a little bit high with the AHT21B. If you’ve read my web page about it, I was musing about whether it reads high (probably not), or the conversion isn’t quite right (maybe), or if it’s spot on and my thermostats are both off. But last night I had thermostat set to 70F - house seemed comfortable. And the AHT21B program said it was 72.9F! I don’t think so, but maybe. I might tinker with calibration later.

1 Like

I got a DHT-11 sort of working on the Duo last night. According to its measurements the AHT21B is dead on. My Honeywell T4 Pro thermostat reads almost 3 degrees F too low. Guess I should open it up and see if there’s a calibration trimpot in there somewhere.

Getting DHT-11 working with no access to timers is a bit hard, and so far it’s not reliable. My crappy code gets stuck pretty often. Using C clock is too slow, as the DHT-11 uses timing of 26 to 28uS. Maybe I can fix it today. Maybe not.

1 Like

I guess my other thermometers are reading low as well because I was getting readings that were a little high as well.

1 Like

You got it working with your library?
Does this mean that wiringX library is broken?

1 Like

I got the aht21 working on my rpi pico, I havn’t been able to get to work with the duo and wiring x. You cloud bitbang it with wiringx or write driver that controls the i2c peripheral on the chip directly . As far as that temp sensor, yes wiringx is broken. I haven’t explored using i2c tools and the linux system to control that sensor. It would be nice if we could get the source code for wiringx as I don’t think it would be that much of a change to get it to read 3 bytes in a row. In general I’ve found wiringx to be a disappointment if you want to do anything more than turn a gpio on and off.

1 Like

I have found this: GitHub - wiringX/wiringX: Modular GPIO interface , but it doesn’t support duo…

1 Like

Yeah, I’ve used that a lot to try and figure out how to use some features of wiringx, but we really need the code for the port to the duo.

1 Like

Maybe we don’t even need that wiringx library
Check this doc @ page 20

1 Like

Thats very interesting, I I wondered if wiringx was going thru the kernel space to access the peripherals or addressing the hardware it’s self? I did make a program to run ws2812 and I used the pwm directly and I still had trouble getting the speed I needed. I highly doubt going thru the kernel would have been fast enough. Either way I’ll have to look at it some more.

1 Like

Did you get the WS2812s working? I wanted to try to control 4 of them on my board.

1 Like

yes I did for the 64. it should work on pin GP2/PWM10 if I recall correctly.

2 Likes