diff --git a/DHT11_Python/LICENSE.md b/DHT11_Python/LICENSE.md new file mode 100644 index 0000000..6190ee3 --- /dev/null +++ b/DHT11_Python/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2016 Zoltan Szarvas + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/DHT11_Python/README.md b/DHT11_Python/README.md new file mode 100644 index 0000000..f6b59ce --- /dev/null +++ b/DHT11_Python/README.md @@ -0,0 +1,44 @@ +# DHT11 Python library + +This simple class can be used for reading temperature and humidity values from DHT11 sensor on Raspberry Pi. + +# Installation + +To install, just run following: + +``` +pip install dht11 +``` + +# Usage + +1. Instantiate the `DHT11` class with the pin number as constructor parameter. +2. Call `read()` method, which will return `DHT11Result` object with actual values and error code. + +For example: + +```python +import RPi.GPIO as GPIO +import dht11 + +# initialize GPIO +GPIO.setwarnings(False) +GPIO.setmode(GPIO.BCM) +GPIO.cleanup() + +# read data using pin 14 +instance = dht11.DHT11(pin = 14) +result = instance.read() + +if result.is_valid(): + print("Temperature: %-3.1f C" % result.temperature) + print("Humidity: %-3.1f %%" % result.humidity) +else: + print("Error: %d" % result.error_code) +``` + +For working example, see `dht11_example.py` (you probably need to adjust pin for your configuration) + +# License + +This project is licensed under the terms of the MIT license. diff --git a/DHT11_Python/dht11/__init__.py b/DHT11_Python/dht11/__init__.py new file mode 100644 index 0000000..e274a0e --- /dev/null +++ b/DHT11_Python/dht11/__init__.py @@ -0,0 +1,203 @@ +import time +import RPi + + +class DHT11Result: + 'DHT11 sensor result returned by DHT11.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 == DHT11Result.ERR_NO_ERROR + + +class DHT11: + 'DHT11 sensor reader class for Raspberry' + + __pin = 0 + + def __init__(self, pin): + self.__pin = pin + + def read(self): + RPi.GPIO.setup(self.__pin, RPi.GPIO.OUT) + + # send initial high + self.__send_and_sleep(RPi.GPIO.HIGH, 0.05) + + # pull down to low + self.__send_and_sleep(RPi.GPIO.LOW, 0.02) + + # change to input using pull up + RPi.GPIO.setup(self.__pin, RPi.GPIO.IN, RPi.GPIO.PUD_UP) + + # collect data into an array + data = self.__collect_input() + + # 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) + if len(pull_up_lengths) != 40: + return DHT11Result(DHT11Result.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) + + # calculate checksum and check + checksum = self.__calculate_checksum(the_bytes) + if the_bytes[4] != checksum: + return DHT11Result(DHT11Result.ERR_CRC, 0, 0) + + # ok, we have valid data + + # The meaning of the return sensor values + # the_bytes[0]: humidity int + # the_bytes[1]: humidity decimal + # the_bytes[2]: temperature int + # the_bytes[3]: temperature decimal + + temperature = the_bytes[2] + float(the_bytes[3]) / 10 + humidity = the_bytes[0] + float(the_bytes[1]) / 10 + + return DHT11Result(DHT11Result.ERR_NO_ERROR, temperature, humidity) + + def __send_and_sleep(self, output, sleep): + RPi.GPIO.output(self.__pin, output) + time.sleep(sleep) + + 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 = [] + while True: + current = RPi.GPIO.input(self.__pin) + data.append(current) + if last != current: + unchanged_count = 0 + last = current + else: + unchanged_count += 1 + if unchanged_count > max_unchanged_count: + break + + 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_DOWN + + 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 == RPi.GPIO.LOW: + # ok, we got the initial pull down + state = STATE_INIT_PULL_UP + continue + else: + continue + if state == STATE_INIT_PULL_UP: + if current == RPi.GPIO.HIGH: + # 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 == RPi.GPIO.LOW: + # 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 == RPi.GPIO.HIGH: + # 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 == RPi.GPIO.LOW: + # 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 + + return the_bytes + + def __calculate_checksum(self, the_bytes): + return the_bytes[0] + the_bytes[1] + the_bytes[2] + the_bytes[3] & 255 diff --git a/DHT11_Python/example.py b/DHT11_Python/example.py new file mode 100644 index 0000000..107362e --- /dev/null +++ b/DHT11_Python/example.py @@ -0,0 +1,26 @@ +import RPi.GPIO as GPIO +import dht11 +import time +import datetime + +# initialize GPIO +GPIO.setwarnings(True) +GPIO.setmode(GPIO.BCM) + +# read data using pin 14 +instance = dht11.DHT11(pin=14) + +try: + while True: + result = instance.read() + if result.is_valid(): + print("Last valid input: " + str(datetime.datetime.now())) + + print("Temperature: %-3.1f C" % result.temperature) + print("Humidity: %-3.1f %%" % result.humidity) + + time.sleep(6) + +except KeyboardInterrupt: + print("Cleanup") + GPIO.cleanup() \ No newline at end of file diff --git a/DHT11_Python/setup.py b/DHT11_Python/setup.py new file mode 100644 index 0000000..277ddcd --- /dev/null +++ b/DHT11_Python/setup.py @@ -0,0 +1,23 @@ +from setuptools import setup, find_packages + +with open("README.md", "r") as fh: + long_description = fh.read() + +setup( + name="dht11", + version="0.1.0", + author="Zoltán Szarvas", + author_email="", + description="Pure Python library for reading DHT11 sensor on Raspberry Pi", + long_description=long_description, + long_description_content_type="text/markdown", + url="https://github.com/szazo/DHT11_Python", + packages=find_packages(), + install_requires=["RPi.GPIO"], + classifiers=[ + "Programming Language :: Python :: 3", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", + ], + python_requires='>=3.6', +) diff --git a/setup.sh b/setup.sh index 6832fec..a11c0c2 100755 --- a/setup.sh +++ b/setup.sh @@ -56,6 +56,7 @@ if [ ! -d "$PROJECT_DIR" ]; then echo -e "$line\nCreating 'dht11-sensor' in Desktop directory for current user.\n$line\n" mkdir -p "$PROJECT_DIR" cp "./temp-calc.py" "$PROJECT_DIR/" + cp -r "./DHT11_Python/" "$PROJECT_DIR/DHT11_Python" echo -e "$line\nCreated '$PROJECT_DIR' directory.\n$line\n" else echo -e "$line\n$PROJECT_DIR already exists. Please delete the folder before running this script.\n$line\n\nExiting..." @@ -71,6 +72,12 @@ echo -e "$line\nActivating virtual environment...\n$line\n" source $PROJECT_DIR/bin/activate echo -e "$line\nInstalling dependencies...\n$line\n" -pip3 install --upgrade RPi.GPIO setuptools dht11 +pip install --upgrade RPi.GPIO setuptools +# Alternatively, instead of executing the next four lines, you can also run "pip install dht11" +# but I've decided to keep the files for "dht11" package locally available for installation in the script. +cd "$PROJECT_DIR/DHT11_Python" +pip install . +pip show dht11 +cd "$PROJECT_DIR/" echo -e "\n\n\n$line$line$line\nSetup completed.\nExecute the command 'python3 temp-calc.py' to calculate the temperature.\n# DESIGNED AND ENGINEERED BY KSHITIJ.\n# END OF SCRIPT\n$line$line$line\n\n\n"