I've been trying to learn how to connect sensors to Arduino with I2C to read data.
With this setup, I was able to get correct temperature/humidity reading from using the sparkfun library for SHTC3 source. So I know my wiring is correctly done. However, I don't want to rely on using a library to get the data. I want to be able to writer my own function to fetch for it.
I've read a lot about I2C and the general setup is as follows using the Wire.h Library:
Wire.requestFrom(device_address, 1);
while(Wire.available() == 0);
int temp = Wire.read();
Serial.print(temp);
In our case, the device_address is 0x70, while the temperature data register is 0x7CA2. SHTC3 datasheet
Here's my Arduino code:
#include "Wire.h"
int address = 112; //0x70 in decimal
int temp_reg = 31906; //0x7CA2 in decimal
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
Wire.begin();
}
void loop() {
Wire.beginTransmission(address);
Wire.write(temp_reg);
Wire.endTransmission();
Wire.requestFrom(address,1);
while(Wire.available() == 0);
int temp = Wire.read();
Serial.print("temperature");
Serial.print(temp);
delay(1000);
}
Right now, I'm not getting any reading in my output. Can someone offer some insight on what I can do to get the correct readings?
On the datasheet, they talk about sleep/wake mode. And clock stretching which I do no understand.
I've tried referencing the sparkfun library but it's beyond my level to understand what they were doing.
However, I don't want to rely on using a library to get the data. I want to be able to writer my own function to fetch for it.
Why? In your code you use the Wire library so if you don't want to use a library why is the Wire library OK?
Why do you use the Arduino IDE at all if you don't want to use libraries?
pylon:
Why? In your code you use the Wire library so if you don't want to use a library why is the Wire library OK?
Why do you use the Arduino IDE at all if you don't want to use libraries?
Wire.requestFrom(device_address, 1);
while(Wire.available() == 0);
Bad code. If no device is connected this results in an endless loop.
Why not make use of the existing library as a tool? Open up a new project and add the library cpp and h files that you do not want to use and study how the libraries creator did it. After replicating the original project, see if you can strike out on your own by looking at the device spec sheet and do things your way. This will ease you into learning and expanding.
Remember, you will be learning 2 things Wire and the device. Wire is the thing, I am guessing, you want to learn so you can use it in other projects. The making the device run will give you experience in how to read spec sheets.
That's what I started with. However, the library is a bit complex to understand. There's a lot that goes on because they do a lot more than just reading sensor value.
I just wanted a simple example to read the sensor data.
Thanks for the suggestion. So instead of Wire.available() == 0, I should do Wire.available() <1?
No, if anything goes wrong in the I2C transfer, Wire.available() will be 0 and will stay 0 forever. The Wire.requestFrom() call returns how many bytes it read. If that return value is not 1 (you requested 1 byte) the call failed. The whole while loop is wrong and should be eliminated.
If write sends one byte and the temperature value is 1 byte, I dont see the issue?
The definition of the value you send:
int temp_reg = 31906; //0x7CA2 in decimal
is an integer (2 bytes), so guess what will be sent.
pylon:
No, if anything goes wrong in the I2C transfer, Wire.available() will be 0 and will stay 0 forever. The Wire.requestFrom() call returns how many bytes it read. If that return value is not 1 (you requested 1 byte) the call failed. The whole while loop is wrong and should be eliminated.
The definition of the value you send:
int temp_reg = 31906; //0x7CA2 in decimal
is an integer (2 bytes), so guess what will be sent.
Okay, just so we're on the same page. I should remove while (Wire.available() ==0);
And change the new code to:
Wire.beginTransmission(address);
Wire.write(temp_reg >> 8);
Wire.write(temp_reg & 0x00FF); // since temp_reg is more than 1 byte
Wire.endTransmission();
Wire.requestFrom(address,2); //change request to 2 bytes
int temp1 = Wire.read();
int temp2 = Wire.read();
Thanks guys for the suggestion. I went back to the Sparkfun library and tried to figure out what was going on. It's still super confusing to me but I figured out how to write a minimal code to get data sensor readings. This sensor is special because you need to turn on "certain" modes first before getting data. You simply run a Wire.write(cmd) to give it commands. Here's a working code that get's me temp + humidity:
#define I2C_Address 0x70
#define reset 0x905D
#define sleep_mode 0xB098
#define wake_mode 0x3517
#define measure_mode 0x7CA2
#include "Wire.h"
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
Wire.begin();
// soft reset
Wire.beginTransmission(I2C_Address);
Wire.write((int)reset >> 8);
Wire.write((int)reset & 0x00FF);
Wire.endTransmission();
delay(1000);
// sleep
Wire.beginTransmission(I2C_Address);
Wire.write((int)sleep_mode >> 8);
Wire.write((int)sleep_mode & 0x00FF);
Wire.endTransmission();
delay(1000);
// wake
Wire.beginTransmission(I2C_Address);
Wire.write((int)wake_mode >> 8);
Wire.write((int)wake_mode & 0x00FF);
Wire.endTransmission();
delay(1000);
}
void loop() {
// measure_mode
Wire.beginTransmission(I2C_Address);
Wire.write((int)measure_mode >> 8);
Wire.write((int)measure_mode & 0x00FF);
Wire.endTransmission();
delay(1000);
Wire.requestFrom(I2C_Address,5);
int temp1 = Wire.read();
int temp2 = Wire.read();
int check1 = Wire.read(); // Do not care. Just a place holder
int hum1 = Wire.read();
int hum2 = Wire.read();
//int check2 = Wire.read();
float temp = (temp1 << 8) | (temp2 << 0);
float humidity = (hum1 << 8) | (hum2 << 0);
//Convert data to Celsius
temp = -45 + 175 * ((float)temp/65535); //2^16 = 65535
float fahrenheit = temp * (9.0/5) + 32.0;
//Convert humidity to percent
humidity = 100 * (humidity/65535);
Serial.print("Temperature: ");
Serial.print(temp);
Serial.print(" C \n");
Serial.print("Temperature: ");
Serial.print(fahrenheit);
Serial.print(" F \n");
Serial.print("Humidity: ");
Serial.print(humidity);
Serial.print(" % \n");
Serial.print(" \n");
delay(1000);
}
I can clean it up with functions and better variable naming, but as for now, I'm super happy its working!
I2C is a byte oriented communication protocol, which handles data 1-byte at a time. Your sensor is designed to receive higher byte first. For 16-bit address of the reset register (for example) you have shifted the address value to the right by 8-bit; alternatively, you could do:
GolamMostafa:
I2C is a byte oriented communication protocol, which handles data 1-byte at a time. Your sensor is designed to receive higher byte first. For 16-bit address of the reset register (for example) you have shifted the address value to the right by 8-bit; alternatively, you could do:
Wire.write(highByte(reset));
Wire.write(lowByte(reset));
So since you can only take 1 byte at a time, this time, I'm testing the LSM6DS3 sensor. They have low bit first it seems. So the code will be as follows: Is this the right idea?
int temp1 = Wire.read();
int temp2 = Wire.read();
float output = (int16_t)temp1 | int16_t(temp2 << 8);
So your output variable will store the actually 16 bit temperature with the bitwise operations performed?