Outils pour utilisateurs

Outils du site


linux:rpi:installation

Ceci est une ancienne révision du document !


Table des matières

Après avoir installé et mis à jour Raspberry pi OS ( SSH, BashRc, PHP), on va installer un client pour nextcloud, et un environnement python pour monter un poulailler connecté.

Nextcloud CMD

Sources : https://www.aukfood.fr/nextcloud-client-en-ligne-de-commande/

sudo apt install nextcloud-client

ne trouve pas le client.

Il faut ajouter le dépôt https://ppa.launchpadcontent.net/nextcloud-devs

sudo apt install software-properties-common python3-launchpadlib

Ensuite on peut faire l'installation du dépôt et de sa clé GPG :

sudo add-apt-repository ppa:nextcloud-devs/client
Attention la distribution bookworm n'est pas connue sur ce launchpad, il faut éditer le fichier sources-list en donnant l'ancienne distribution jammy :
sudo nano /etc/apt/sources.list.d/nextcloud-devs-ubuntu-client-bookworm.list

Et modifier la ligne en :

deb https://ppa.launchpadcontent.net/nextcloud-devs/client/ubuntu/ jammy main

Et enfin faire l'installation de nextcloud-client :

sudo apt update
sudo apt install nextcloud-client

On peut procéder à une première synchronisation :

nextcloudcmd --user $USER --password "$PASS"  /data/nextcloud/GCheramy https://monurlnextcloud

Liste des options sur : https://docs.nextcloud.com/desktop/latest/advancedusage.html on peut ajouter –non-interactive pour une utilisation dans un cron par exemple.

nextcloudcmd --path /<Directory_that_has_been_created> /home/user/<my_sync_folder> \ https://<username>:<secret>@<server_address>

Python

sudo apt install python3 python3-dev python3-rpi.gpio python3-venv

Python fonctionne avec des environnement (par projet ou par user). On crée un utilisateur dédié :

sudo useradd -m -d /home/$USER $USER
sudo passwd $USER
cd /home/$USER

Puis l'environnement :

python3 -m venv .venv

On l'active :

source /home/pi/.venv/bin/activate

Dans l'environnement .venv :

#pip3 install RPi (n'a pas marché ? non nécessaire ?)
pip3 install RPi.GPIO
#pip3 install Adafruit_DHT --install-option="--force-pi" (on va essayer de se passer de cette lib obsolète et buguée)
pip3 install board

Et enfin pour lancer un script (potentiellement en sudo) :

/home/$USER/.venv/bin/python3 /home/$USER/script.py

Si les pages php n'arrivent pas à executer le script python, cela peut provenir d'un problème de droits. On peut ajouter www-data au sudoers :

sudo nano /etc/sudoers

Ajouter à la fin la ligne :

www-data  ALL = NOPASSWD: ALL

DHT22

Actuellement avec le code trouvé ici : https://www.reddit.com/r/raspberry_pi/comments/13pscin/comment/jlbinb2/?share_id=wWvKIpI7UVC945M2GZ5BK&utm_medium=android_app&utm_name=androidcss&utm_source=share&utm_term=1

Et dont voici le script :

script python DHT

script python DHT

dht.py
  1. # MIT License
  2. #
  3. # Original author: Zoltan Szarvas
  4. # https://github.com/szazo/DHT11_Python
  5. #
  6. # 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:
  7. #
  8. # The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
  9. #
  10. # 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.
  11.  
  12.  
  13. #!/usr/bin/python3
  14. import time
  15. import sys
  16. import RPi.GPIO as GPIO
  17.  
  18. # DHTxx sensor result returned by DHT.read() method'
  19. class DHTResult:
  20.  
  21.  
  22. ERR_NO_ERROR = 0
  23. ERR_MISSING_DATA = 1
  24. ERR_CRC = 2
  25. ERR_NOT_FOUND = 3
  26.  
  27. error_code = ERR_NO_ERROR
  28. temperature = -1
  29. humidity = -1
  30.  
  31. def __init__(self, error_code, temperature, humidity):
  32. self.error_code = error_code
  33. self.temperature = temperature
  34. self.humidity = humidity
  35.  
  36.  
  37. def is_valid(self):
  38. return self.error_code == DHTResult.ERR_NO_ERROR
  39.  
  40.  
  41.  
  42. # DHTxx sensor reader class for Raspberry'
  43. class DHT:
  44.  
  45. __pin = 0
  46. __isDht11 = True
  47.  
  48. def __init__(self, pin, isDht11):
  49. self.__pin = pin
  50. self.__isDht11 = isDht11
  51.  
  52. def read(self):
  53. GPIO.setup(self.__pin, GPIO.OUT)
  54.  
  55. # send initial high
  56. self.__send_and_sleep(GPIO.HIGH, 0.05)
  57.  
  58. # pull down to low
  59. self.__send_and_sleep(GPIO.LOW, 0.02)
  60.  
  61. # change to input using pull up
  62. GPIO.setup(self.__pin, GPIO.IN, GPIO.PUD_UP)
  63.  
  64. # collect data into an array
  65. data = self.__collect_input()
  66.  
  67. # parse lengths of all data pull up periods
  68. pull_up_lengths = self.__parse_data_pull_up_lengths(data)
  69.  
  70. if len(pull_up_lengths) == 0:
  71. return DHTResult(DHTResult.ERR_NOT_FOUND, 0, 0)
  72.  
  73. # if bit count mismatch, return error (4 byte data + 1 byte checksum)
  74. if len(pull_up_lengths) != 40:
  75. return DHTResult(DHTResult.ERR_MISSING_DATA, 0, 0)
  76.  
  77. # calculate bits from lengths of the pull up periods
  78. bits = self.__calculate_bits(pull_up_lengths)
  79.  
  80. # we have the bits, calculate bytes
  81. the_bytes = self.__bits_to_bytes(bits)
  82.  
  83. # calculate checksum and check
  84. checksum = self.__calculate_checksum(the_bytes)
  85. if the_bytes[4] != checksum:
  86. return DHTResult(DHTResult.ERR_CRC, 0, 0)
  87.  
  88. # ok, we have valid data
  89. # The meaning of the return sensor values
  90. # the_bytes[0]: humidity int
  91. # the_bytes[1]: humidity decimal
  92. # the_bytes[2]: temperature int
  93. # the_bytes[3]: temperature decimal
  94. temperature = -1
  95. humidity = -1
  96. if(self.__isDht11):
  97. # DHT11
  98. temperature = the_bytes[2] + float(the_bytes[3]) / 10
  99. humidity = the_bytes[0] + float(the_bytes[1]) / 10
  100. else:
  101. #DHT22
  102. temperature = (the_bytes[2] * 256 + float(the_bytes[3])) / 10
  103. humidity = (the_bytes[0] * 256 + float(the_bytes[1])) / 10
  104. c = (float)(((the_bytes[2]&0x7F)<< 8)+the_bytes[3])/10
  105.  
  106. if ( c > 125 ):
  107. c = the_bytes[2]
  108.  
  109. if (the_bytes[2] & 0x80):
  110. c = -c;
  111.  
  112. temperature = c
  113. humidity = ((the_bytes[0]<<8)+the_bytes[1])/10.00
  114.  
  115. return DHTResult(DHTResult.ERR_NO_ERROR, temperature, humidity)
  116.  
  117.  
  118. def __send_and_sleep(self, output, sleep):
  119. GPIO.output(self.__pin, output)
  120. time.sleep(sleep)
  121.  
  122. def __collect_input(self):
  123. # collect the data while unchanged found
  124. unchanged_count = 0
  125.  
  126. # this is used to determine where is the end of the data
  127. max_unchanged_count = 100
  128.  
  129. last = -1
  130. data = []
  131. while True:
  132. current = GPIO.input(self.__pin)
  133. data.append(current)
  134. if last != current:
  135. unchanged_count = 0
  136. last = current
  137. else:
  138. unchanged_count += 1
  139. if unchanged_count > max_unchanged_count:
  140. break
  141.  
  142. return data
  143.  
  144. def __parse_data_pull_up_lengths(self, data):
  145. STATE_INIT_PULL_DOWN = 1
  146. STATE_INIT_PULL_UP = 2
  147. STATE_DATA_FIRST_PULL_DOWN = 3
  148. STATE_DATA_PULL_UP = 4
  149. STATE_DATA_PULL_DOWN = 5
  150.  
  151. state = STATE_INIT_PULL_DOWN
  152.  
  153. lengths = [] # will contain the lengths of data pull up periods
  154. current_length = 0 # will contain the length of the previous period
  155.  
  156. for i in range(len(data)):
  157.  
  158. current = data[i]
  159. current_length += 1
  160.  
  161. if state == STATE_INIT_PULL_DOWN:
  162. if current == GPIO.LOW:
  163. # ok, we got the initial pull down
  164. state = STATE_INIT_PULL_UP
  165. continue
  166. else:
  167. continue
  168. if state == STATE_INIT_PULL_UP:
  169. if current == GPIO.HIGH:
  170. # ok, we got the initial pull up
  171. state = STATE_DATA_FIRST_PULL_DOWN
  172. continue
  173. else:
  174. continue
  175. if state == STATE_DATA_FIRST_PULL_DOWN:
  176. if current == GPIO.LOW:
  177. # we have the initial pull down, the next will be the data pull up
  178. state = STATE_DATA_PULL_UP
  179. continue
  180. else:
  181. continue
  182. if state == STATE_DATA_PULL_UP:
  183. if current == GPIO.HIGH:
  184. # data pulled up, the length of this pull up will determine whether it is 0 or 1
  185. current_length = 0
  186. state = STATE_DATA_PULL_DOWN
  187. continue
  188. else:
  189. continue
  190. if state == STATE_DATA_PULL_DOWN:
  191. if current == GPIO.LOW:
  192. # pulled down, we store the length of the previous pull up period
  193. lengths.append(current_length)
  194. state = STATE_DATA_PULL_UP
  195. continue
  196. else:
  197. continue
  198.  
  199. return lengths
  200.  
  201. def __calculate_bits(self, pull_up_lengths):
  202. # find shortest and longest period
  203. shortest_pull_up = 1000
  204. longest_pull_up = 0
  205.  
  206. for i in range(0, len(pull_up_lengths)):
  207. length = pull_up_lengths[i]
  208. if length < shortest_pull_up:
  209. shortest_pull_up = length
  210. if length > longest_pull_up:
  211. longest_pull_up = length
  212.  
  213. # use the halfway to determine whether the period it is long or short
  214. halfway = shortest_pull_up + (longest_pull_up - shortest_pull_up) / 2
  215. bits = []
  216.  
  217. for i in range(0, len(pull_up_lengths)):
  218. bit = False
  219. if pull_up_lengths[i] > halfway:
  220. bit = True
  221. bits.append(bit)
  222.  
  223. return bits
  224.  
  225. def __bits_to_bytes(self, bits):
  226. the_bytes = []
  227. byte = 0
  228.  
  229. for i in range(0, len(bits)):
  230. byte = byte << 1
  231. if (bits[i]):
  232. byte = byte | 1
  233. else:
  234. byte = byte | 0
  235. if ((i + 1) % 8 == 0):
  236. the_bytes.append(byte)
  237. byte = 0
  238.  
  239. return the_bytes
  240.  
  241. def __calculate_checksum(self, the_bytes):
  242. return the_bytes[0] + the_bytes[1] + the_bytes[2] + the_bytes[3] & 255
  243.  
  244.  
  245.  
  246.  
  247.  
  248.  
  249.  
  250.  
  251.  
  252.  
  253.  
  254.  
  255. # Parse command line parameters
  256. if len(sys.argv) == 3:
  257. sensor = sys.argv[1]
  258. if sensor not in ("11", "22", "2302"):
  259. print('Sensor "{}" is not valid. Use 11, 22 or 2302'.format(sensor))
  260. exit(1)
  261. isDht11 = sensor == "11"
  262. try:
  263. pin = int(sys.argv[2])
  264. if (pin < 2 or pin > 27):
  265. raise ValueError
  266. except:
  267. print('Gpio {} is not valid'.format(pin))
  268. exit(1)
  269. else:
  270. print('usage: dht.py [11|22|2302] [gpio#]')
  271. exit(1)
  272.  
  273.  
  274. # initialize GPIO
  275. GPIO.setwarnings(False)
  276. GPIO.setmode(GPIO.BCM)
  277.  
  278. # read data
  279. MAX_ATTEMPTS = 15
  280. MAX_NOT_FOUND_ATTEMPTS = 3
  281. dht = DHT(pin, isDht11)
  282. result = dht.read()
  283. not_found_attempts = 0
  284.  
  285. # make some attempts, because someone may not be successful
  286. for x in range(0, MAX_ATTEMPTS):
  287. if result.is_valid() or not_found_attempts == MAX_NOT_FOUND_ATTEMPTS:
  288. break
  289. else:
  290. time.sleep(2)
  291. result = dht.read()
  292. if result.error_code == DHTResult.ERR_NOT_FOUND:
  293. not_found_attempts += 1
  294. else:
  295. not_found_attempts = 0
  296.  
  297.  
  298. # print result
  299. if result.is_valid():
  300. print('Temp: {0:0.1f} C Humidity: {1:0.1f} %'.format(result.temperature, result.humidity))
  301. else:
  302. print('Failed to get reading. Is the sensor connected? Is the pin number correct?')
  303.  
  304. # clean the gpio
  305. GPIO.cleanup()

Je peux lancer le script en faisant :

source /home/pi/.venv/bin/activate
cd /home/pi
sudo .venv/bin/python3 ./dht.py 22 25

(ici 22 pour DHT22, et 25 pour le GPIO25)

@TODO :

  • Modifier dht.py pour formater et renvoyer temp+humid en json
  • Créer https://poul.fr.nf/info_dht.php qui récupère en json les infos de dht.py
  • Modifier https://poul.fr.nf/info_proc.php pour qu'elle récupère les infos proc uniquement (plus la partie dht)
  • Modifier l'interface index.php pour :
    • Lancer un “pisto/put_info_proc_in_bdd_post.php” (tant qu'à faire ça donne un enregistrement plus récent) »> finalement non, ça va ajouter pas mal de données en BDD pour rien (1 data par heure ça donne déjà 88k ligne en 10 ans, et si on reste sur le site ça ajoute des lignes toutes les 15s…)
    • Récupérer température/humidité par info_dht.php
  • Rapatrier les données via nextcloud-cmd
  • Migrer sur le pi “en prod” (attention au path de .venv !)
  • Modifier pisto/put_info_proc_in_bdd_post.php pour pointer sur cette nouvelle page info_dht.php

@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)
linux/rpi/installation.1746909461.txt.gz · Dernière modification : de tutospisto