# Tutorial on how to build a temperature and humidity sensor [ToC] ###### tags: `Tag(Temperature, Humidity, Lopy4, Pycom)` > This is my tutorial on how to create a temperature and humidity censor using Pycom. > My name is Jesper Olausson and my student credentials are: ==jo222yr== - [ ] Title - [ ] Name and Student credentials ## :memo: Background I've been studying from home for about a year now and during this hot early summer I relaised that my office has gotten pretty unbearable to stay on for longer periods of time. Therefore I wanted to figure out why by measuring my humidity rates and temperature. If you also want to do this it shouldn't take longer than ==about 6 hours== as a newbie. ~~Atleast if you don't run in to any troubles like I did.~~ Considering the results of my measures I've realised that I during the summer should move my office-setup to another room when an extremly warm week is forcasted. - [ ] Short project overview - [ ] How much time it might take to do (approximation) - [ ] Why I choose this project - [ ] What purpose does it serve - [ ] What insights you think it will give --- ### :wrench: Material - [ ] List of material - [ ] What the different things (sensors, wires, controllers) do - short specifications - [ ] Where you bought them and how much they cost | IoT equipment | Purpose | Where to buy | | ----------------- |:----------------------- |:----------------------- | | Pycom Extensionboard 3.1|Use to power up Lopy4, Connect to your computer and, if needed, connect to battery pack to make it run portably |https://www.m.nu/pycom/expansion-board-31 199 Kr | Lopy4 - With Headers | Great IoT platform to connect via multiple communication chanels including: LoRa, Sigfox, WiFi and Bluetooth. | https://pycom.io/product/lopy4/ €34.95 | | Cables |Used to connect Expansionboard to DHT11 Sensor | https://www.electrokit.com/produkt/labsladd-1-pin-hane-hona-150mm-10-pack/ 29 Kr| | USB-Cable 2.0 A- 2.0 Micro B |Connects the Expansionboard and Lopy4 to the computer.| You get this anywhere of the internet for 39 Kr, or maybe you already have one at home. | DHT11 Sensor |Measures humidity and degrees when connected to the expansionboard and with the right code| https://www.kjell.com/se/produkter/el-verktyg/arduino/tillbehor/temperatur-och-luftfuktighetssensor-for-arduino-p87086?gclid=CjwKCAjwxev3BRBBEiwAiB_PWPXrYrrq0zrbEfEK6RXjTnFlivTNSyVUhePE_krIjCHsmKkHE_3NmRoCe-MQAvD_BwE&gclsrc=aw.ds 99.90 Kr | ![](https://i.imgur.com/yhG8cXc.png =200x200) *1. Pycom Expansion Board 3.1* ![](https://i.imgur.com/PBOmF9D.png =200x200) *2. Lopy4 - With Headers* ![](https://i.imgur.com/O17JXeu.png =200x200) *3. Cables* ![](https://i.imgur.com/p6sAI7z.png =200x200) *4. USB-Cable 2.0 A- 2.0 Micro B* ![](https://i.imgur.com/f11nlvY.png =200x200) *5. DHT11 Sensor* ### :desktop_computer: Computer setup - [ ] Chosen IDE - [ ] How the code is uploaded #### IDE For this project I've used Atom with the extensions Python and Pymakr. Atom is a great tool for beginners with a lot of extensions available to help make the programming experience more fun, and also more practial! The code is uploaded via the Pymakr console to the Pycom device. The device then runs the code to do various things (for my example measure temperature and humidity). ## Steps to set up what you need to start coding - [ ] Steps that you needed to do for your computer. Installation of Node.js, extra drivers, etc. 1. Download Atom 2. Download and install node.js 3. Download the REPL console Pymakr 4. Download and install Python to your computer 5. Set up an account at Pycoms website 6. Download Pycom Firmware updater 7. Connect Lopy4 to your expansionboard (make sure the led is at the same side as usb-port) 8. Connect USB-cable to the computer and expansionboard 9. Check devicemanager to make sure that your device is connected to your computer 11. Follow instructions to connect the device to Pybytes (available at pycoms website) 12. Make sure that your device is connected 13. Start Atom and connect the device there by checking the Pymakr Console 14. And you are done! You have succesfully connected the Expansionboard and Lopy4 to your computer. --- ### Connecting the DHT11 Sensor With this rough sketch you can see how the wiring is connected with the blue side of the DHT11 sensor facing towards you. The orange cable is connected between GND and the third pin of the sensor. Ground is a reference point for all signals or a common path in an electrical circit where all the voltages can be measured from. The red cable is the voltage cable, connected from 3V3 and the center pin of the sensor, 3v3 is 3.3 Volts. The final brown cable is connected between P23 and the first pin of the sensor. This is used to communicate with the expansionboard, it could be any other P as well but this one is closest. Remember this pin for programming purposes later on. - [ ] Circuit diagram (can be hand drawn) ![](https://i.imgur.com/zvszgaO.jpg =400x500) *Circuit diagram* --- ### The code The following code is the main.py. Here we first import time, then the pincode from the machine and finally the DHT from another document stored in a lib folder which we'll get into soon. After this we create a variable with the P23 in it and set mode to open drain and chose which DHT device we have, 0 for DHT11 and 1 for DHT22 (why we can do this will be explained in the next code snippet). While True the result is read via the th.read method and collected in variable result. Then we print the Temp and RH along with the result of said measurements. Finally we send the signals to the Pybytes dashboard här kan skapa visuella diagram för datan som samlas in samt använda sig av exempelvis LoRa-nätverket iställlet för wifi om det skulle behövas. When all is done the function is set to sleep 900 seconds or 15 minutes since ==we don't need to collect data more than that and therefore save data.== For this project data isn't that important since we're using wifi but with order methods like LoRa every bit of data is important to minimalize. As a transport protocol I used MQTT. - [ ] How often is the data sent? - [ ] Which wireless protocols did you use (WiFi, LoRa, etc …) - [ ] Which transport protocols were used (MQTT, webhook, etc …) - [ ] Describe platform in terms of functionality ###### Data is sent to Pybytes. Needs to flashed with Pybyte firmware ```python= import time from machine import Pin from dht import DHT ###### Type 0 = dht11 ###### Type 1 = dht22 th = DHT(Pin('P23', mode=Pin.OPEN_DRAIN), 0) time.sleep(2) while True: result = th.read() while not result.is_valid(): time.sleep(5) result = th.read() print('Temp:', result.temperature) print('RH:', result.humidity) pybytes.send_signal(1,result.temperature) pybytes.send_signal(2,result.humidity) time.sleep(900) ``` --- This is the dht.py document placed in the lib folder. This file is basically to make the sensor available to use in the main.py file. It also makes it possible to use other DHT devices such as the DHT22. ```python= import time import pycom from machine import enable_irq, disable_irq, Pin class DTHResult: 'DHT sensor result returned by DHT.read() method' ERR_NO_ERROR = 0 ERR_MISSING_DATA = 1 ERR_CRC = 2 error_code = ERR_NO_ERROR temperature = -1 humidity = -1 def __init__(self, error_code, temperature, humidity): self.error_code = error_code self.temperature = temperature self.humidity = humidity def is_valid(self): return self.error_code == DTHResult.ERR_NO_ERROR class DTH: 'DHT sensor (dht11, dht21,dht22) reader class for Pycom' #__pin = Pin('P3', mode=Pin.OPEN_DRAIN) __dhttype = 0 def __init__(self, pin, sensor=0): self.__pin = Pin(pin, mode=Pin.OPEN_DRAIN) self.__dhttype = sensor self.__pin(1) time.sleep(1.0) def read(self): # pull down to low self.__send_and_sleep(0, 0.019) data = pycom.pulses_get(self.__pin,100) self.__pin.init(Pin.OPEN_DRAIN) self.__pin(1) #print(data) bits = [] for a,b in data: if a ==1 and 18 <= b <= 28: bits.append(0) if a ==1 and 65 <= b <= 75: bits.append(1) #print("longueur bits : %d " % len(bits)) if len(bits) != 40: return DTHResult(DTHResult.ERR_MISSING_DATA, 0, 0) #print(bits) # we have the bits, calculate bytes the_bytes = self.__bits_to_bytes(bits) # calculate checksum and check checksum = self.__calculate_checksum(the_bytes) if the_bytes[4] != checksum: return DTHResult(DTHResult.ERR_CRC, 0, 0) # ok, we have valid data, return it [int_rh, dec_rh, int_t, dec_t, csum] = the_bytes if self.__dhttype==0: #dht11 rh = int_rh #dht11 20% ~ 90% t = int_t #dht11 0..50°C else: #dht21,dht22 rh = ((int_rh * 256) + dec_rh)/10 t = (((int_t & 0x7F) * 256) + dec_t)/10 if (int_t & 0x80) > 0: t *= -1 return DTHResult(DTHResult.ERR_NO_ERROR, t, rh) def __send_and_sleep(self, output, mysleep): self.__pin(output) time.sleep(mysleep) def __bits_to_bytes(self, bits): the_bytes = [] byte = 0 for i in range(0, len(bits)): byte = byte << 1 if (bits[i]): byte = byte | 1 else: byte = byte | 0 if ((i + 1) % 8 == 0): the_bytes.append(byte) byte = 0 #print(the_bytes) return the_bytes def __calculate_checksum(self, the_bytes): return the_bytes[0] + the_bytes[1] + the_bytes[2] + the_bytes[3] & 255 ``` --- Finally we have the dht.py file where the class DHT is created which you refer to in the main.py file. Here you basically read the data collected from the sensor and convert it into a readable result. ```python= import time from machine import enable_irq, disable_irq class DHTResult: 'DHT sensor result returned by DHT.read() method' ERR_NO_ERROR = 0 ERR_MISSING_DATA = 1 ERR_CRC = 2 error_code = ERR_NO_ERROR temperature = -1 humidity = -1 def __init__(self, error_code, temperature, humidity): self.error_code = error_code self.temperature = temperature self.humidity = humidity def is_valid(self): return self.error_code == DHTResult.ERR_NO_ERROR class DHT: 'DHT sensor (dht11, dht21,dht22) reader class for Pycom' # __pin = Pin('P3', mode=Pin.OPEN_DRAIN) __dhttype = 0 def __init__(self, pin, sensor=0): self.__pin = pin self.__dhttype = sensor self.__pin(1) time.sleep(1.0) def read(self): # time.sleep(1) # send initial high # self.__send_and_sleep(1, 0.025) # pull down to low self.__send_and_sleep(0, 0.019) # collect data into an array data = self.__collect_input() # print(data) # parse lengths of all data pull up periods pull_up_lengths = self.__parse_data_pull_up_lengths(data) # if bit count mismatch, return error (4 byte data + 1 byte checksum) # print(pull_up_lengths) # print(len(pull_up_lengths)) if len(pull_up_lengths) != 40: return DHTResult(DHTResult.ERR_MISSING_DATA, 0, 0) # calculate bits from lengths of the pull up periods bits = self.__calculate_bits(pull_up_lengths) # we have the bits, calculate bytes the_bytes = self.__bits_to_bytes(bits) # print(the_bytes) # calculate checksum and check checksum = self.__calculate_checksum(the_bytes) if the_bytes[4] != checksum: return DHTResult(DHTResult.ERR_CRC, 0, 0) # ok, we have valid data, return it [int_rh, dec_rh, int_t, dec_t, csum] = the_bytes if self.__dhttype == 0: # dht11 rh = int_rh # dht11 20% ~ 90% t = int_t # dht11 0..50°C else: # dht21,dht22 rh = ((int_rh * 256) + dec_rh)/10 t = (((int_t & 0x7F) * 256) + dec_t)/10 if (int_t & 0x80) > 0: t *= -1 return DHTResult(DHTResult.ERR_NO_ERROR, t, rh) def __send_and_sleep(self, output, mysleep): self.__pin(output) time.sleep(mysleep) def __collect_input(self): # collect the data while unchanged found unchanged_count = 0 # this is used to determine where is the end of the data max_unchanged_count = 100 last = -1 data = [] m = bytearray(800) # needs long sample size to grab all the bits from the DHT # noqa irqf = disable_irq() self.__pin(1) for i in range(len(m)): m[i] = self.__pin() # sample input and store value enable_irq(irqf) for i in range(len(m)): current = m[i] data.append(current) if last != current: unchanged_count = 0 last = current else: unchanged_count += 1 if unchanged_count > max_unchanged_count: break # print(data) return data def __parse_data_pull_up_lengths(self, data): STATE_INIT_PULL_DOWN = 1 STATE_INIT_PULL_UP = 2 STATE_DATA_FIRST_PULL_DOWN = 3 STATE_DATA_PULL_UP = 4 STATE_DATA_PULL_DOWN = 5 state = STATE_INIT_PULL_UP lengths = [] # will contain the lengths of data pull up periods current_length = 0 # will contain the length of the previous period for i in range(len(data)): current = data[i] current_length += 1 if state == STATE_INIT_PULL_DOWN: if current == 0: # ok, we got the initial pull down state = STATE_INIT_PULL_UP continue else: continue if state == STATE_INIT_PULL_UP: if current == 1: # ok, we got the initial pull up state = STATE_DATA_FIRST_PULL_DOWN continue else: continue if state == STATE_DATA_FIRST_PULL_DOWN: if current == 0: # we have the initial pull down, # the next will be the data pull up state = STATE_DATA_PULL_UP continue else: continue if state == STATE_DATA_PULL_UP: if current == 1: # data pulled up, the length of this pull up # will determine whether it is 0 or 1 current_length = 0 state = STATE_DATA_PULL_DOWN continue else: continue if state == STATE_DATA_PULL_DOWN: if current == 0: # pulled down, we store the length of # the previous pull up period lengths.append(current_length) state = STATE_DATA_PULL_UP continue else: continue return lengths def __calculate_bits(self, pull_up_lengths): # find shortest and longest period shortest_pull_up = 1000 longest_pull_up = 0 for i in range(0, len(pull_up_lengths)): length = pull_up_lengths[i] if length < shortest_pull_up: shortest_pull_up = length if length > longest_pull_up: longest_pull_up = length # use the halfway to determine whether the period it is long or short halfway = shortest_pull_up + (longest_pull_up - shortest_pull_up) / 2 bits = [] for i in range(0, len(pull_up_lengths)): bit = False if pull_up_lengths[i] > halfway: bit = True bits.append(bit) return bits def __bits_to_bytes(self, bits): the_bytes = [] byte = 0 for i in range(0, len(bits)): byte = byte << 1 if (bits[i]): byte = byte | 1 else: byte = byte | 0 if ((i + 1) % 8 == 0): the_bytes.append(byte) byte = 0 # print(the_bytes) return the_bytes def __calculate_checksum(self, the_bytes): return the_bytes[0] + the_bytes[1] + the_bytes[2] + the_bytes[3] & 255 ``` ## Presenting the data any my final design - [ ] Provide visual examples on how the dashboard looks. Pictures needed. - [ ] How often is data saved in the database. - [ ] Show final results of the project - [ ] Pictures This is the result of my dashboard. It shows my temperature and humidity. you can also enter a more detailed view with the edit option on pybytes dashboards. And as explained earlier the data is saved every 15 minutes into the database. The reason I used Pybytes for this project is that it's a simple way to easily describe how to work with collecting data and presenting it. ![](https://i.imgur.com/7FPuvaO.png) *Dashboard.* As a final note I think this project is a really great way of learning how the process works with IoT. Now that I've learned this I feel ready to use other sensors, connect them and use more advanced dashboards to create a clear view of what I'm trying to measure. ---