AHT10 Humidity/Temp module with PIC16F1614. Can't handle all the bits? - Page 1
Forum section: electronics / general

author: guitchess
author_rank: Regular Contributor
timestamp: March 27, 2022, 12:51:22 pm
content: Hello.As stated in the subject, I'm using I2C to interface the AHT10 module(datasheet sucks). I got the module working communicating without much trouble, thanks to advice from a previous question(logic probe). The issue is that it took a lot of fiddling with the conversion code to get the output to show what was expected. Long story short, I ended up having to ditch 8 bits of the raw 20bit data.Question: My assumption is that the 8 bit PIC doesn't have a space/page large enough to handle the equation with the full 20bit data. Possible?equation: Rel Hum = (data/1048576)*100 Data is supposed to be 20bits, but I have to use 12 MSB.PIC = 16F1614I know I'm leaving out info you may want, but it's time to go to work. will post more later if needed.

author: Ian.M
author_rank: Super Contributor
timestamp: March 27, 2022, 01:57:26 pm
content: 1048576 is 0x100000, so the equation is equivalent to shifting right by 20 bits then multiplying by 100. One would expect a clear(ish) error message at build time if your compiler was actually running out of memory. I therefore suspect that either your code is defective or you are running into number range issues, but as my crystal ball is down of maintenance and you have failed to provide pertinent details, I'm only guessing. . . .http://catb.org/~esr/faqs/smart-questions.htmlapplies!

author: Siwastaja
author_rank: Super Contributor
timestamp: March 27, 2022, 02:29:59 pm
content: Are you programming in C? Seems like a C problem.Post the actual code. Specifically, what type is "data". int32_t? uint32_t? float?

author: NorthGuy
author_rank: Super Contributor
timestamp: March 27, 2022, 02:37:43 pm
content: I didn't look at the datasheet, but I would expect the relative humidity measurements to have low accuracy (in the likes of 3-5%). You can verify the actual accuracy with the sensor datasheet.With such low accuracy, only a handful of bits are correct (certainly not 20). The lower bits are bogus (random), so you can drop them anyway.8-bit CPU can handle longer numbers. If you use C the compiler will do this for you. It just takes more time.

author: woofy
author_rank: Frequent Contributor
timestamp: March 27, 2022, 04:27:18 pm
content: Quote from: guitchess on March 27, 2022, 12:51:22 pmThat cannot be correct. Shifting 20 bit data right by 20 bits leaves nothing.Try this:https://github.com/Thinary/AHT10

author: NorthGuy
author_rank: Super Contributor
timestamp: March 27, 2022, 05:00:53 pm
content: Quote from: woofy on March 27, 2022, 04:27:18 pmThis is a formula, not C code

author: Ian.M
author_rank: Super Contributor
timestamp: March 27, 2022, 06:09:05 pm
content: Of course, naïve C code using integer math directly based on the formula won't work as Woofy noted. However use 32 bit math and reverse the order of operations to multiply first and it should work well enough, returning an integer % RH, which is (optimistically) about all the sensor is good for.Its also worth noting that C++ compilers for 8 bit PICs are not in general use, and the one I am aware of only implements a rather idiosyncratic subset of the STL, so porting Arduino libraries to an 8 bit PIC is far from easy, even for an experienced PIC and Arduino developer.

author: DavidAlfa
author_rank: Super Contributor
timestamp: March 27, 2022, 08:20:11 pm
content: Yep, really bad datasheet. This seems to be the response structure:Code:Byte Content0 1 [HUMIDITY 19:12]2 [HUMIDITY 11:4]3 [HUMIDITY 3:0 + TEMPERATURE 19:16]4 [TEMPERATURE 15:8]5 [TEMPERATURE 7:0]CheckthisUse an array, not a single variable, ex. uint8_t r_data.Once you read the 6 bytes, conversion is as follows:Code:float humidity, temperature;humidity = (r_data << 12) | (r_data << 4) | ((r_data & 0xF0) >> 4);humidity = (humidity/(1 << 20)) * 100.0;temperature = ((r_data & 0xf) << 16) | (r_data << 8) | r_data;temperature = (temperature * 200.0 / (1 << 20)) - 50;Remember you don't have FPU, processing floats will take a lot of cpu power.You can avoid them by using fixed point and multiplying first, in detriment of precision.This should output temp*10 (ex 552=55.2ºC) and humidity in %*10(Ex. 765=76.5%).You cannot get any better precision using uint32_t, as it's close to overflowing, 2^20*2000 uses 31 of 32 bits.Code:uint32_t humidity, temperature;humidity = (r_data << 12) | (r_data << 4) | ((r_data & 0xF0) >> 4);humidity = (humidity*1000) >>20;temperature = ((r_data & 0xf) << 16) | (r_data << 8) | r_data;temperature = ((temperature * 2000.0)>>20) - 500;I never used this sensor, so there might be some errors!

author: guitchess
author_rank: Regular Contributor
timestamp: March 27, 2022, 10:49:22 pm
content: Thank you all for your input. Now some details about what I was doing(wrong?//reading bytes // I tried this several ways based on internet research.data = raw1;data = (data<< 8 )+raw2;data = (data << 4) + (raw3>>4);// I know my code was working at this point because I could print "data" at any point.relhum = (data/1048576)*100;//Here is where I had trouble. relhum wouldn't output anything but 0x00//FYI "output" is an 8bit led binary display.What made me think that it couldn't handle the size of the equation was when I tried this:calc1 = data/4096; //I could print thiscalc2 = (calc1/256)*100 //but couldn't print this.//Tried several variations on this theme.//Finally settled ondata = (raw1<<4) +(raw2>>4);relhum = data/40.96; // This prints perfectly every time. Have not tried to use 16bits yet. Going to try that tonight.I realize my code is far from elegant/efficient. Please keep in mind, I'm a 48 year old carpenter that is self taught at programming/EE. In short, I know nothing.Usually, I would just live with it, but this time I wanted to know if I was missing something obvious. Wouldn't be the first time.

author: MarkF
author_rank: Super Contributor
timestamp: March 28, 2022, 01:00:41 am
content: Quote from: guitchess on March 27, 2022, 10:49:22 pmIn the first part, you are shiftingdataleft 12 bits. This will require thatdatawill need to be at least 3 bytes long to be able to hold all the bits you're using.In the last statement, you divide by 1048576 and then multiply by 100. Here you're doing integer math. First,datais divided by 1048576 and then truncated (most likely the result will be zero). You will lose any fractional part. At least multiply before doing the divide. However, the multiply will require a 4th byte to hold the result before the divide. Once again, this will also truncate the fractional part.If you do integer math, you MUST insure that the result and all intermediate results will fit in the variable you defined. Although it will cost much more program space and execution time, you are better off doing the calculation in floating point math with float variables. You need to pack the data into a large enough integer and then move the data into a float before doing the calculations.F.Y.I. An "int" on the PIC16F1614 is two bytes (i.e. 16 bits).

author: guitchess
author_rank: Regular Contributor
timestamp: March 28, 2022, 01:35:32 am
content: Quote from: MarkF on March 28, 2022, 01:00:41 amI bet that's the issue. I didn't realize a PIC int is 2 bytes. WHere did I miss that? Datasheet?Also, sorry I didn't specify that I was using a float for the decimal point math.I was shifting the top 4 bits out of the int, then dividing by 20bits. That would leave the result so low that it would always show up as 0x00. Does that seem plausible?So, does that mean on a PIC I would need to use a float for any value needing 4 bytes?

author: guitchess
author_rank: Regular Contributor
timestamp: March 28, 2022, 02:25:18 am
content: Doh! That was the issue. Didn't realize that a PIC int wasn't an int. After changing to a long(4bytes), it works perfectly.All that time wasted because of the wrong size variable.Thanks again for all your input.

author: guitchess
author_rank: Regular Contributor
timestamp: March 28, 2022, 03:44:44 am
content: Quote from: DavidAlfa on March 27, 2022, 08:20:11 pmI tried to implement your code for the temperature section. I couldn't get it to work. My compiler threw a warning saying:bit shift >= variable size. I have it declared as a long, which should be 4 bytes. Any thoughts?Here is code that seems to work. I did use some of the more efficient code details though I haven't used an array.Code:tempdata = raw3 & 0x0f;tempdata = (tempdata << 8) + raw4;tempdata = (tempdata << 8) + raw5;tempC = ((tempdata*200)>>20)-50;tempdata is a long. tempC is a float. This method shifts raw3 16bits, but the compiler doesn't complain and result is normal.I have raw3-4-5 as CHARs, but I changed them to INTs when the warning popped up with no difference.Thanks.

author: DavidAlfa
author_rank: Super Contributor
timestamp: March 28, 2022, 10:16:30 am
content: Yep that's normal because it's shifting a 8-bit variable more bits than it can hold.Easy fix:Code:humidity = (uint32_t)raw0 << 12) | ((uint16_t)raw1 << 4) | (raw2 >> 4);temperature = (((uint32_t)raw2 & 0xf) << 16) | ((uint16_t)raw3 << 8) | raw4;To use uints** remember to include <stdint.h>Otherwise,:uint8_t = unsigned charuint16_t = unsigned intuint32_t = unsigned long

author: MarkF
author_rank: Super Contributor
timestamp: March 28, 2022, 12:47:30 pm
content: Quote from: guitchess on March 28, 2022, 01:35:32 amThe sizes of anintand alongdepends on the processor. For instance, a PIC32MX170F256B is a 32-bit processor and theintis 4 bytes and alongis 8 bytes.As @DavidAlfa said, you can use the signed and unsigned data types defined instdint.hif you want to make it obvious.unsigned types:uint8_t, uint16_t, uint32_t and uint64_tsigned types:int8_t, int16_t, int32_t and int64_t

author: Siwastaja
author_rank: Super Contributor
timestamp: March 28, 2022, 02:39:53 pm
content: Size of int does not matter. This crap was fixed in the C99 standard,over 20 years ago. Use stdint.h and the related types: int32_t, uint32_t, etc.