Ci-dessous, les différences entre deux révisions de la page.
Les deux révisions précédentesRévision précédenteProchaine révision | Révision précédente | ||
linux:rpi:installation [2025/04/26 16:37] – tutospisto | linux:rpi:installation [2025/05/11 10:16] (Version actuelle) – tutospisto | ||
---|---|---|---|
Ligne 1: | Ligne 1: | ||
- | Après avoir installé et mis à jour Raspberry pi OS ( [[Linux: | + | Après avoir installé et mis à jour Raspberry pi OS ( [[Linux: |
====== Nextcloud CMD ====== | ====== Nextcloud CMD ====== | ||
Ligne 7: | Ligne 7: | ||
https:// | https:// | ||
- | <code bash> | + | <code bash> |
Il faut ajouter le dépôt https:// | Il faut ajouter le dépôt https:// | ||
- | <code bash> | + | <code bash> |
- | sudo apt-get install software-properties-common | + | |
- | sudo apt-get install | + | |
- | </ | + | |
Ensuite on peut faire l' | Ensuite on peut faire l' | ||
Ligne 21: | Ligne 18: | ||
<code bash> | <code bash> | ||
+ | Et modifier la ligne en : | ||
<code bash>deb https:// | <code bash>deb https:// | ||
Et enfin faire l' | Et enfin faire l' | ||
- | <code bash> | + | <code bash> |
+ | sudo apt update | ||
+ | sudo apt install nextcloud-client</ | ||
On peut procéder à une première synchronisation : | On peut procéder à une première synchronisation : | ||
Ligne 33: | Ligne 33: | ||
on peut ajouter --non-interactive pour une utilisation dans un cron par exemple. | on peut ajouter --non-interactive pour une utilisation dans un cron par exemple. | ||
- | nextcloudcmd --path /< | + | <code bash>nextcloudcmd --path /< |
+ | |||
+ | |||
====== Python ====== | ====== Python ====== | ||
+ | |||
+ | <code bash> | ||
+ | |||
+ | |||
+ | Python fonctionne avec des environnement (par projet ou par user). On crée un utilisateur dédié : | ||
+ | <code bash> | ||
+ | sudo passwd $USER | ||
+ | cd / | ||
+ | |||
+ | Puis l' | ||
+ | <code bash> | ||
+ | |||
+ | On l' | ||
+ | <code bash> | ||
+ | |||
+ | Dans l' | ||
+ | <code bash> | ||
+ | #pip3 install RPi (n'a pas marché ? non nécessaire ?) | ||
+ | pip3 install RPi.GPIO | ||
+ | #pip3 install Adafruit_DHT --install-option=" | ||
+ | pip3 install board</ | ||
+ | |||
+ | Et enfin pour lancer un script (potentiellement en sudo) : | ||
+ | <code bash>/ | ||
+ | |||
+ | Si les pages php n' | ||
+ | On peut ajouter www-data au sudoers : | ||
+ | <code bash> | ||
+ | Ajouter à la fin la ligne : | ||
+ | <code bash> | ||
+ | |||
+ | ====== DHT22 ====== | ||
+ | |||
+ | |||
+ | Actuellement avec le code trouvé ici : https:// | ||
+ | |||
+ | Et dont voici le script : | ||
+ | <hidden script python DHT > | ||
+ | <code bash dht.py[enable_line_numbers=1, | ||
+ | # MIT License | ||
+ | # | ||
+ | # Original author: Zoltan Szarvas | ||
+ | # https:// | ||
+ | # | ||
+ | # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the " | ||
+ | # | ||
+ | # 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, | ||
+ | |||
+ | |||
+ | # | ||
+ | import time | ||
+ | import sys | ||
+ | import RPi.GPIO as GPIO | ||
+ | |||
+ | # DHTxx sensor result returned by DHT.read() method' | ||
+ | class DHTResult: | ||
+ | | ||
+ | |||
+ | ERR_NO_ERROR = 0 | ||
+ | ERR_MISSING_DATA = 1 | ||
+ | ERR_CRC = 2 | ||
+ | ERR_NOT_FOUND = 3 | ||
+ | |||
+ | error_code = ERR_NO_ERROR | ||
+ | temperature = -1 | ||
+ | humidity = -1 | ||
+ | |||
+ | def __init__(self, | ||
+ | self.error_code = error_code | ||
+ | self.temperature = temperature | ||
+ | self.humidity = humidity | ||
+ | | ||
+ | |||
+ | def is_valid(self): | ||
+ | return self.error_code == DHTResult.ERR_NO_ERROR | ||
+ | | ||
+ | | ||
+ | | ||
+ | # DHTxx sensor reader class for Raspberry' | ||
+ | class DHT: | ||
+ | | ||
+ | __pin = 0 | ||
+ | __isDht11 = True | ||
+ | |||
+ | def __init__(self, | ||
+ | self.__pin = pin | ||
+ | self.__isDht11 = isDht11 | ||
+ | |||
+ | def read(self): | ||
+ | GPIO.setup(self.__pin, | ||
+ | |||
+ | # send initial high | ||
+ | self.__send_and_sleep(GPIO.HIGH, | ||
+ | |||
+ | # pull down to low | ||
+ | self.__send_and_sleep(GPIO.LOW, | ||
+ | |||
+ | # change to input using pull up | ||
+ | GPIO.setup(self.__pin, | ||
+ | |||
+ | # 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 len(pull_up_lengths) == 0: | ||
+ | return DHTResult(DHTResult.ERR_NOT_FOUND, | ||
+ | |||
+ | # if bit count mismatch, return error (4 byte data + 1 byte checksum) | ||
+ | if len(pull_up_lengths) != 40: | ||
+ | return DHTResult(DHTResult.ERR_MISSING_DATA, | ||
+ | |||
+ | # 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 DHTResult(DHTResult.ERR_CRC, | ||
+ | |||
+ | # ok, we have valid data | ||
+ | # The meaning of the return sensor values | ||
+ | # the_bytes[0]: | ||
+ | # the_bytes[1]: | ||
+ | # the_bytes[2]: | ||
+ | # the_bytes[3]: | ||
+ | temperature = -1 | ||
+ | humidity = -1 | ||
+ | if(self.__isDht11): | ||
+ | # DHT11 | ||
+ | temperature = the_bytes[2] + float(the_bytes[3]) / 10 | ||
+ | humidity = the_bytes[0] + float(the_bytes[1]) / 10 | ||
+ | else: | ||
+ | #DHT22 | ||
+ | temperature = (the_bytes[2] * 256 + float(the_bytes[3])) / 10 | ||
+ | humidity = (the_bytes[0] * 256 + float(the_bytes[1])) / 10 | ||
+ | c = (float)(((the_bytes[2]& | ||
+ | | ||
+ | if ( c > 125 ): | ||
+ | c = the_bytes[2] | ||
+ | | ||
+ | if (the_bytes[2] & 0x80): | ||
+ | c = -c; | ||
+ | | ||
+ | temperature = c | ||
+ | humidity = ((the_bytes[0]<< | ||
+ | |||
+ | return DHTResult(DHTResult.ERR_NO_ERROR, | ||
+ | | ||
+ | |||
+ | def __send_and_sleep(self, | ||
+ | GPIO.output(self.__pin, | ||
+ | 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 = 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, | ||
+ | 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 == 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 == 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 == 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 == 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 == 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, | ||
+ | # 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, | ||
+ | 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, | ||
+ | return the_bytes[0] + the_bytes[1] + the_bytes[2] + the_bytes[3] & 255 | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | # Parse command line parameters | ||
+ | if len(sys.argv) == 3: | ||
+ | sensor = sys.argv[1] | ||
+ | if sensor not in (" | ||
+ | print(' | ||
+ | exit(1) | ||
+ | isDht11 = sensor == " | ||
+ | try: | ||
+ | pin = int(sys.argv[2]) | ||
+ | if (pin < 2 or pin > 27): | ||
+ | raise ValueError | ||
+ | except: | ||
+ | print(' | ||
+ | exit(1) | ||
+ | else: | ||
+ | print(' | ||
+ | exit(1) | ||
+ | | ||
+ | | ||
+ | # initialize GPIO | ||
+ | GPIO.setwarnings(False) | ||
+ | GPIO.setmode(GPIO.BCM) | ||
+ | |||
+ | # read data | ||
+ | MAX_ATTEMPTS = 15 | ||
+ | MAX_NOT_FOUND_ATTEMPTS = 3 | ||
+ | dht = DHT(pin, isDht11) | ||
+ | result = dht.read() | ||
+ | not_found_attempts = 0 | ||
+ | |||
+ | # make some attempts, because someone may not be successful | ||
+ | for x in range(0, MAX_ATTEMPTS): | ||
+ | if result.is_valid() or not_found_attempts == MAX_NOT_FOUND_ATTEMPTS: | ||
+ | break | ||
+ | else: | ||
+ | time.sleep(2) | ||
+ | result = dht.read() | ||
+ | if result.error_code == DHTResult.ERR_NOT_FOUND: | ||
+ | not_found_attempts += 1 | ||
+ | else: | ||
+ | not_found_attempts = 0 | ||
+ | |||
+ | | ||
+ | # print result | ||
+ | if result.is_valid(): | ||
+ | print(' | ||
+ | else: | ||
+ | print(' | ||
+ | | ||
+ | # clean the gpio | ||
+ | GPIO.cleanup() | ||
+ | |||
+ | |||
+ | </ | ||
+ | |||
+ | |||
+ | Je peux lancer le script en faisant : | ||
+ | |||
+ | <code bash> | ||
+ | cd /home/pi | ||
+ | sudo .venv/ | ||
+ | (ici 22 pour DHT22, et 25 pour le GPIO25) | ||
+ | |||
+ | |||
+ | @TODO : | ||
+ | * < | ||
+ | * < | ||
+ | * < | ||
+ | * < | ||
+ | * < | ||
+ | * < | ||
+ | * < | ||
+ | * Migrer sur le pi "en prod" (attention au path de .venv !) | ||
+ | * Modifier pisto/ | ||
+ | |||
+ | @TODO : | ||
+ | * Motion | ||
+ | * Mise en place caméra | ||
+ | * Mise en place timelapse (on move ?) | ||
+ | * MEP IR | ||
+ | @TODO : | ||
+ | * Récupérer les tutos de pistodocs\Perso\configs\Scripts\RPI-Poulailler | ||
+ | * log2ram (et modifs des fichiers logs pour /tmp) | ||