ESP32 In MicroPython: I2C, HTU21D and Slow Reading |
Written by Harry Fairhead & Mike James | |||||
Monday, 10 July 2023 | |||||
Page 4 of 4
Reading HumidityThe nice thing about using I2C devices is that it gets easier. Once you have seen how to do it with one device, the skill generalizes and, once you know how to deal with a particular part of a device, other aspects of the device are usually similar. For this reason let's implement the humidity reading using polling which we know works with the hardware and the software I2C. We write the 0xF5 once to the slave and then repeatedly attempt to read the three-byte response. If the slave isn't ready it simply replies with a NAK which the read method interprets as throwing an exception. Once we have the data, the formula to convert the 16-bit value to percentage humidity is: RH= -6 + 125 * data16 / 2 16 Putting all this together, and reusing some variables from the previous parts of the program, we have: buf = bytearray([0xF5]) i2c0.writeto( 0x40, buf, True) read = bytearray(3) while True: sleep_ms(1) try: i2c0.readfrom_into(0x40,read, True) break except: continue msb = read[0] lsb = read[1] check = read[2] print("msb lsb checksum =", msb, lsb, check) data16 = (msb << 8) | (lsb & 0xFC) hum = -6 + (125.0 * data16) / 65536 print("Humidity ", hum) Checksum CalculationAlthough computing a cyclic redundancy checksum, CRC, isn't specific to I2C, it is another common task. The datasheet explains that the polynomial used is: X8+X5+X4+1 Once you have this information you can work out the divisor by writing a binary number with a one in each location corresponding to a power of X in the polynomial, in this case the 8th, 5th, 4th and 1st bit. Hence the divisor is: 0x0131 What you do next is roughly the same for all CRCs. First you put the data that was used to compute the checksum together with the checksum value as the low-order bits: data32 = (msb << 16)|(lsb <<8)| check Now you have three bytes, i.e 24 bits, in a 32-bit variable. Next you adjust the divisor so that its most significant non-zero bit aligns with the most significant bit of the three bytes. As this divisor has a 1 at bit eight, it needs to be shifted 15 places to the right to move it to be the 24th bit: divisor = 0x0131 <<15 or divisor = 0x988000 Now that you have both the data and the divisor aligned, you step through the topmost 16 bits, i.e. you don't process the low-order eight bits which hold the received checksum. For each bit you check to see if it is a 1. If it is you replace the data with the data XOR divisor. In either case you shift the divisor one place to the right: for i in range(16): if data32 & 1<<(23 - i): data32 ^= divisor divisor>>= 1 When the loop ends, if there was no error, the data32 should be zeroed and the received checksum is correct and as computed on the data received. A complete function to compute the checksum, with some optimization, is: def crcCheck(msb, lsb,check): data32 = (msb << 16)|(lsb <<8)| check divisor = 0x988000 for i in range(16): if data32 & 1<<(23 - i): data32 ^= divisor divisor>>= 1 return data32 It is rare to get a CRC error on an I2C bus unless it is overloaded or subject to a lot of noise. Complete ListingThe complete program for reading temperature and humidity, including checksum, is: from machine import Pin,I2C def crcCheck(msb, lsb,check): data32 = (msb << 16)|(lsb <<8)| check divisor = 0x988000 for i in range(16): if data32 & 1<<(23 - i): data32 ^= divisor divisor>>= 1 return data32 This works but takes more than two seconds to read data that should take less than one second to read. The only solution is to switch to the software I2C implementation. If you do this include a sleep in each of the while loops to reduce the number of times you attempt to read the data. If you move to the software implementation then clock stretching is a better approach. Of course, this is just the start. Once you have the device working and supplying data, it is time to write your code in the form of functions that return the temperature and the humidity and generally make the whole thing more useful and easier to maintain. This is often how this sort of programming goes. First you write a lot of inline code so that it works as fast as it can, then you move blocks of code to functions to make the program more elegant and easy to maintain, checking at each refactoring that it all still works. Not all devices used standard bus protocols. In Chapter 13 we’ll look at a custom serial protocol that we have to implement for ourselves. Summary
Programming the ESP32in MicroPythonBy Harry Fairhead & Mike JamesBuy from Amazon. ContentsPreface
<ASIN:187196282X>
Comments
or email your comment to: comments@i-programmer.info To be informed about new articles on I Programmer, sign up for our weekly newsletter, subscribe to the RSS feed and follow us on Twitter, Facebook or Linkedin. |
|||||
Last Updated ( Tuesday, 11 July 2023 ) |