Björn Siby
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Versions and GitHub Sync Note Insights Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       owned this note    owned this note      
    Published Linked with GitHub
    Subscribed
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    Subscribe
    # Remote Green house system The project is about being able to manually control a green house remotely and more specifically a couple of plant watering valves and a couple of ventilation hatches. To support manual control, sensor data like temperature, humidity and weather forecast should be available. This is done via the course 1DT305 - Introduction to Applied IoT summer 2023 and student # Author Björn Siby credentials is bs222vn # Table of contents - [Background](#Background) - [Solution Overview](#Solution-Overview) - [Connection](#Connection) - [Hardware](#Hardware) - [Schematics](#Schematics) - [Wiring](#Wiring) - [GPIO Mapping](#GPIO-mapping) - [Software](#Software) - [Setup](#Setup) - [Procedure](#Procedure) - [Tailscale](#Tailscale) - [Enable Sensors](#Enable-Sensors) - [Docker & Has](#Docker-and-HAS) - [Enable IO in the Docker HAS container](#Enable-IO-in-the-Docker-HAS-container) - [HAS integrations](#HAS-integrations) - [HAS Dashboard](#HAS-Dashboard) - [HAS Configuration](#HAS-Configuration) - [HAS pyscript coding](#HAS-pyscript-coding) - [Backup](#Backup) - [Result and Demonstration](#Result-and-Demonstration) - [Bonus](#Bonus) - [SunGrow SG15rt](#SunGrow-SG15rt) # Termonology | Term | Verbose | |----------------------------------------------|---------------------| | HW | Hardware | | RPI| Raspberry PI | | SW |Software| | IoT |Internet of things| | HAS |Home assistant| | GPIO |General Purpose Input Output| | DNS |Domain Name System| # Background My mother and father in law have built a green house and would like to be able to remotly control the ventilation hatches and the water valves to activate irrigation of plants. Currently watering plants needs to be done every day so this will enable them to go away for shorter periods of time. I have mostly looked into Ardoino and Raspberry based cores and in the end I choose Raspberry. The main reason is the possibility to utilise a posix system which enables configuration, installation and programming remotely in real time via for example ssh. Since I have previous experiance of setting up Home assistant, I think most time will go to harware assembly and specfic python coding, I estimate effort to be around 100 hours. Video of the [green house](https://drive.google.com/file/d/1hDKk1syXcpkWav78L7RhmvITKJ5hofFQ/view?usp=sharing) ![](https://hackmd.io/_uploads/BymnIFeF3.jpg) ![](https://hackmd.io/_uploads/Sk20LFxth.jpg) ![](https://hackmd.io/_uploads/S1UvQtlF3.jpg) ![](https://hackmd.io/_uploads/BJYLXFeY3.jpg) # Solution Overview ![](https://hackmd.io/_uploads/H16rAjzK2.jpg) --- # Connection I went for WLAN because of the bandwith and WLAN availability. The unit does also have grid power supply. The quality of the connection is not great but good enough to handle * a few users via app/browser * ssh connection from computer * the home assitant webserver connection to other services ``` pi@raspberrypi:~ $ seq 5 | xargs -I{} cat /proc/net/wireless Inter-| sta-| Quality | Discarded packets | Missed | WE face | tus | link level noise | nwid crypt frag retry misc | beacon | 22 wlan0: 0000 23. -87. -256 0 0 0 61 0 0 Inter-| sta-| Quality | Discarded packets | Missed | WE face | tus | link level noise | nwid crypt frag retry misc | beacon | 22 wlan0: 0000 23. -87. -256 0 0 0 61 0 0 Inter-| sta-| Quality | Discarded packets | Missed | WE face | tus | link level noise | nwid crypt frag retry misc | beacon | 22 wlan0: 0000 23. -87. -256 0 0 0 61 0 0 Inter-| sta-| Quality | Discarded packets | Missed | WE face | tus | link level noise | nwid crypt frag retry misc | beacon | 22 wlan0: 0000 23. -87. -256 0 0 0 61 0 0 Inter-| sta-| Quality | Discarded packets | Missed | WE face | tus | link level noise | nwid crypt frag retry misc | beacon | 22 wlan0: 0000 23. -87. -256 0 0 0 61 0 0 ``` # Hardware | Part | Picture | Comment | |----------------------------------------------|---------------------|----------------------| | [Raspberry PI Zero W](https://www.raspberrypi.com/products/raspberry-pi-zero-w/) | ![](https://hackmd.io/_uploads/S1J-yznO2.png) | Central computing and IO control || | [32 GB micro sdcard](https://www.teknikdelar.se/produkt/s-uhs-i-class-10?shopping_id_click=SDSQUA4-032G-GN6MN&gclid=Cj0KCQjwnf-kBhCnARIsAFlg491fGy8WYIwXSLQCKZIkMISZNY-zNzr4CC0ErTyH5yYVqrTmbFpI-n8aAu1YEALw_wcB) | ![](https://hackmd.io/_uploads/SJ5R__pO3.png)| Software storage | | [Pibow Zero W Case](https://www.adafruit.com/product/3471) | ![](https://hackmd.io/_uploads/H1_sJzhu3.png) | Reduce risk of damaging Hardware and more stylish | | [Adafruit Perma Proto Bonnet Mini Kit](https://www.adafruit.com/product/3203) | ![](https://hackmd.io/_uploads/HyzEgG3dh.png) | Prototyp board used as connection board and a few soldered passive components | | [AM2302/DHT22](https://www.adafruit.com/product/393) | ![](https://hackmd.io/_uploads/r1mH-fnu2.png) | Temperature and humidity sensor | | [Sen-11050 Temperature Sensor](https://www.sparkfun.com/products/11050) | ![](https://hackmd.io/_uploads/H1rT-f2On.png) | Waterprof temperature sensor, used for redundancy | | 2 x [8 Channel 5V Relay Module](https://hitechchain.se/arduinokompatibel/relay-5v-8-channel-5v-relay-module-with-optocoupler-low-level-trigger-expansion-board-relay-for-arduino) | ![](https://hackmd.io/_uploads/rJc7fG3u3.png) | Relay board with isolation and protection of MCU pins | | [Super Starter Kit for Raspberry Pi](https://hitechchain.se/raspberry-pi/super-starter-kit-for-raspberry-pi) | ![](https://hackmd.io/_uploads/B1FRGf3O2.png) | Start kit used for experimenting etc and wires used in final assembly || | [UPS HAT (C)](https://www.waveshare.com/wiki/UPS_HAT_(C)) | ![](https://hackmd.io/_uploads/SJ3S9XnOn.png) | Backup energy if unit is disconnected from power supply and safe shutdown || | [DC-DC 12V to 5V Step Down Power Supply](https://www.ebay.co.uk/itm/193653785512) | ![](https://hackmd.io/_uploads/HJJFdVnOn.png) | Green house have 24v power supply, need to step down to supply RPI | ## Schematics Schematic of controlling one dc motor which physically moves the hatch ![](https://hackmd.io/_uploads/Sk66xShdh.png) * Init state and off state is illustrated * Switching Relay 1 will spin motor in one direction, instead switching Relay 2 will spin motor in opposite direction, switching both will switch off energy to motor Schematic of controlling one valve which enables water to pass pipe ![](https://hackmd.io/_uploads/Bk_L7S3_3.png) ## Wiring Given the RPI Zero pinout ![](https://hackmd.io/_uploads/SkMTWxlKn.png) The Relayboard has a 10 pin array, where the first is GND and next 8 pins are digital relay switch signals and last is VCC ![](https://hackmd.io/_uploads/Hka8Bget3.png) Adding the prototype board changes the RPI pinout to a long array, which simplifies wiring to the Relayboard ![](https://hackmd.io/_uploads/rygRsleK3.png) Solder Relay connectors to prototypeboard and also a header for connecting GND and 5v or 3v. Connect Sen-11050 to GPIO pin 4 Connect DHT22 to GPIO 17 Connect all relay wires Connect all GND Connect all VCC ![](https://hackmd.io/_uploads/SkXjmgeF3.jpg) Connect wires to relayboard ![](https://hackmd.io/_uploads/BJvNugxKn.jpg) UPS hat mounts under the RPI and connects with 6 Pogo pins to GNC, VCC and to two I2C serial interface pins ![](https://hackmd.io/_uploads/rJP1ybxt3.png) ## GPIO mapping | GPIO | Function | |----------------------------------------------|---------------------| | GPIO 0| Not made available on protoboard | | GPIO 1| Not made available on protoboard | | GPIO 2| UPS I2C | | GPIO 3| UPS I2C | | GPIO 4 |Sen-11050| | GPIO 5| Spare Relay| | GPIO 6| Spare Relay| | GPIO 7 |Not made available on protoboard | | GPIO 8| Not made available on protoboard | | GPIO 9| Hatch 1 Close | | GPIO 10 |Hatch 2 Close| | GPIO 11| Hatch 1 Open | | GPIO 12| Spare Relay | | GPIO 13 |Spare Relay| | GPIO 14| Not made available on protoboard | | GPIO 15| Not made available on protoboard | | GPIO 16 |Valve 4 Switch| | GPIO 17| DHT22| | GPIO 18| DHT22 VCC | | GPIO 19 |Valve 3 Switch | | GPIO 20| Valve 2 Switch| | GPIO 21| Valve 1 Switch| | GPIO 22 |Hatch 2 Close| | GPIO 23| Hatch 3 Open | | GPIO 24| Hatch 3 Close | | GPIO 25 |Hatch 2 Open| | GPIO 26 |Hatch 2 Open| | GPIO 27 |Hatch 4 Close| --- # Software ## Setup * [Raspberry Pi OS Lite](https://www.raspberrypi.com/software/operating-systems/) * The operating system * [Auto reconnect to wifi](https://gist.github.com/carry0987/372b9fefdd8041d0374f4e08fbf052b1) * Bash script to reconnect to wifi * The WLAN is disconnected time to time * [Docker](https://www.docker.com/) * Containerize software * enables upgrades, downgrades, test sandboxing * [Home assistant](https://www.home-assistant.io/) * Open-source homeautomation software platform * It has a very active community, includes phone apps, webserver * [Tailscale](https://tailscale.com/) * Open-source peer-to-peer VPN * simplifies remote connection, no need to add port forward rules in router etc * [Idrive](https://www.idrive.com/) * Cloud Backup, backups the RPI periodically every week * I use Idrive for my everyday backup * [Perl backup scripts](https://github.com/Taryck/idrive) ## Procedure More or less all installation, configuration and coding of software has been done on target in real-time using either [VIM](https://www.vim.org/) and [Nano](https://www.nano-editor.org/). By connecting via ssh using tailscale from a PC via putty (windows) or bash (linux) or actually mobile phone using [Termius](https://termius.com/). This has allowed me to bassically being able to work on the solution whenever i have the possibility to do so. The base is Raspberry PI OS Lite which is * flashed via [etcher.io](https://etcher.balena.io/) on to the sdcard * wifi configured * add wpa_supplicant.conf to sdcard * containing: ``` pi@raspberrypi:~ $ sudo cat /etc/wpa_supplicant/wpa_supplicant.conf ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev update_config=1 country=SE network={ ssid="YOUR-WIFI-SSID" psk="YOUR-WIFI-PASSWORD" scan_ssid=1 priority=1 } ``` * ssh enabled * add ssh.txt to sdcard * Mount the sdcard to the RPI HW * Connect a keyboard and a screen * Boot up RPI * Go through installation process * Setup user & password ### Tailscale ##### Reasoning To simplify remote connection to the RPI a [Tailscale](https://tailscale.com/kb/1151/what-is-tailscale/) VPN network has been used. It enables remote connection from an client on an external network connected to the tailscale network to services like ssh or http without adding router portforwarding rules #### Commands Download client to RPI ``` curl -fsSL https://tailscale.com/install.sh | sh ``` Connect RPI to VPN network and allow others to connect ``` sudo tailscale up ``` It is now possible to connect remotly via ssh, disconnect keyboard, screen and move the RPI to the assembly case within the green house ### Enable Sensors #### Sen-11050 Enable 1-wire support on GPIO 4 ``` sudo nano /boot/config.txt # Enable 1-wire com on GPIO 4 dtoverlay=w1-gpio-pullup ``` To see all 1-wire devices ``` /sys/bus/w1/devices/ ``` To get sensor data from a device ``` cat /sys/bus/w1/devices/YOUR-DEVICE-ID/w1_slave ``` Example ``` pi@raspberrypi:~ $ cat /sys/bus/w1/devices/28-00000c86aa3f/w1_slave 72 01 4b 46 7f ff 0e 10 57 : crc=57 YES 72 01 4b 46 7f ff 0e 10 57 t=23125 ``` #### AM2303 [Adafruit](https://learn.adafruit.com/dht) have a extensive guide, but here are the few steps i used Install python3 ``` sudo apt install python3 idle3 ``` Install python library `sudo pip3 install Adafruit_DHT` Test reading on GPIO 22 ``` bash-5.1# python -c 'import Adafruit_DHT as dht;h, t = dht.read(dht.DHT22,17);print(f"{round(t,2)}");print(f"{round(h,2)}")' 21.8 99.9 ``` #### Waveshare UPS I2C hat [Waveshare](https://www.waveshare.com/wiki/UPS_HAT) have an extensive guide, but here are the steps I used. Enable i2c ``` sudo raspi-config ``` Select Interfacing Options -> I2C ->yes to start the i2C kernel driver ``` sudo reboot ``` Check device availability ``` sudo apt-get install i2c-tools sudo i2cdetect -y 1 ``` ![](https://hackmd.io/_uploads/SkmOqXoF3.png) Test device ``` sudo apt-get install p7zip wget https://www.waveshare.com/w/upload/d/d9/UPS_HAT.7z 7zr x UPS_HAT.7z -r -o./UPS_HAT cd UPS_HAT python3 INA219.py ``` #### Docker and HAS ##### Reasoning To simplify testing upgrades, downgrades, prototyping and backup recovery docker has been used. The downside is that HAS will not be executed in supervisor mode and Home assitant OS cannot be used. There are many discussions regarding this [subject](https://www.google.com/search?q=home+assistant+docker+downside). For my rather small project my choice became a container. In future if the project grows, i will most likely move the HAS platform to a different unit maybe a NUC or a RPI 4 and then refactor current unit to become a MQTT sensor and actuator ##### Commands Get docker ``` curl -fsSL https://get.docker.com -o get-docker.sh ``` Add your user to the docker group to enable Home assistant to get 1-wire sensor data `sudo usermod -aG docker $USER` Home assistant is then installed and booted via an docker image ``` docker run -d -p 8123:8123 -v /home/pi/homeAssProd:/config --restart unless-stopped --privileged --name homeAssProd homeassistant/home-assistant ``` ### Enable IO in the Docker HAS container Get into Docker container command line ``` docker exec -it homeAssProd bash ``` Home assitant image is based on alpine so gcc needs to be installed ``` apk add build-base ``` Install dht22 python library ``` pip install Adafruit_DHT ``` Install RPI Zero GPIO mappings ``` pip install RPi.GPIO ``` Install i2c library for the ups ``` pip install smbus2 ``` ### HAS integrations | Integration | Comment |Installation | |----------------------------------------------|---------------------|---------------------| | ![](https://hackmd.io/_uploads/S1d0xE2d3.png) | Needed by Sen-110501-Wire | Part of core platform so may be installed through HAS GUI | | ![](https://hackmd.io/_uploads/HyLgG4hd2.png)| Enables adding functionality to HAS by python code. Utilised to create services for hatches, valves and dht | Available through [HAS community store](https://hacs.xyz/) or manually by the [installation guide](https://hacs-pyscript.readthedocs.io/en/latest/installation.html#option-2-manual) | [dht-sensor](https://github.com/richardzone/homeassistant-dht) |Needed by the DHT22 sensor, the dht-sensor integration is not part of core platform| Installed as [custom_component](https://community.home-assistant.io/t/dht-sensor-custom-components/390428)| | [freeDNS](https://www.home-assistant.io/integrations/freedns/) | Used to update the DNS record for the green house external ip, father and mother in law do not use Tailscale but instead connects via the external ip which is now and then updated by the internet provider.| Part of the core integration but not via GUI: [guide](https://www.home-assistant.io/integrations/freedns/) | ![](https://hackmd.io/_uploads/BJbLXc6d2.png) | The DNS IP integration will expose the external IP address| Part of the core integration and may be added by the [GUI](https://www.home-assistant.io/integrations/dnsip/) ### HAS Dashboard ![](https://hackmd.io/_uploads/HyUyL43_h.png) #### HAS Configuration ``` cat homeAssProd/configuration.yaml # Configure a default setup of Home Assistant (frontend, api, etc) default_config: # Example configuration.yaml entry logger: default: warning logs: # log level for a custom component custom_components.pyscript: info # Update FreeDns dns record Example configuration.yaml entry freedns: url: http://sync.afraid.org/u/YOUR_TOKEN/ pyscript: allow_all_imports: true hass_is_global: true # Text to speech tts: - platform: google_translate group: !include groups.yaml automation: !include automations.yaml script: !include scripts.yaml scene: !include scenes.yaml homeassistant: allowlist_external_dirs: - /config sensor: - platform: dht sensor: DHT22 pin: 17 name: "AM2303" scan_interval: 10 temperature_offset: 0.0 humidity_offset: 0.0 monitored_conditions: - temperature - humidity - platform: onewire names: 28-00000c86aa3f: SEN-11050 # To be able to monitor and log RPI temperature - platform: command_line name: "Raspberry Pi Temp" command: "cat /sys/class/thermal/thermal_zone0/temp" unit_of_measurement: "°C" value_template: '{{ value | multiply(0.001) | round(1) }}' # Removing incorrect sensor measures - platform: filter name: "filtered am2303 humidity" entity_id: sensor.am2303_humidity filters: - filter: outlier window_size: 10 radius: 5.0 # Removing incorrect sensor measures - platform: filter name: "filtered am2303 temperature" entity_id: sensor.am2303_temperature filters: - filter: outlier window_size: 10 radius: 5.0 # Monitor and log so that the has db size grow´s in a normal way - platform: filesize file_paths: - /config/home-assistant_v2.db scan_interval: 1800 # DNS IP Internet Status: ping host 8.8.8.8 with 2 packets every 600 seconds. binary_sensor: - platform: ping host: 8.8.8.8 count: 2 scan_interval: 600 name: "Internet Connection" # Calibrate the hatches, time to time do they become inconsistent, there is no sensor feedback on posistion - platform: template switches: reset: value_template: > {% if is_state('switch.test','on') %} false {% endif %} turn_on: service: pyscript.hatch_reset turn_off: # Valve switches - platform: template switches: valve1: turn_on: turn_off: - platform: template switches: valve2: turn_on: turn_off: - platform: template switches: valve3: turn_on: turn_off: - platform: template switches: valve4: turn_on: turn_off: - platform: template switches: fan: turn_on: turn_off: - platform: template switches: am2303_supply: turn_on: turn_off: # Hatch input_sliders in procentage of openess input_number: hatch1: name: Hatch1 min: 0 max: 100 step: 10 mode: slider unit_of_measurement: "%" hatch2: name: Hatch2 min: 0 max: 100 step: 10 mode: slider unit_of_measurement: "%" hatch3: name: Hatch3 min: 0 max: 100 step: 10 mode: slider unit_of_measurement: "%" hatch4: name: Hatch4 min: 0 max: 100 step: 10 f mode: slider unit_of_measurement: "%" ``` ### HAS pyscript coding This code is placed in the pyscript directory and is included in the HAS platform. It is the worker code for opening, closing and calibrating the hatches and opening and closing the valves ``` import traceback import RPi.GPIO as GPIO from datetime import datetime, timedelta import board # GPIO mapping ui_to_GPIO = { "input_number.hatch1":(11,9), "input_number.hatch2":(25,10), "input_number.hatch3":(23,24), "input_number.hatch4":(22,27) } valve_to_GPIO = [21,20,19,16] # Movement constant TIME_CONSTANT = 0.4 # Open Valve for 35 minutes VALVE_SLEEP_CONSTANT = 60*35 # 60 sec × 35 min @state_trigger("input_number.hatch1") def ui_hatch1(**kwargs): """ Triggered when a hatch 1 slider has changed its value""" task.unique("ui_hatch1",kill_me=True) hatch_change(kwargs['var_name'],kwargs['value'],kwargs['old_value']) input_number.hatch1 = kwargs['value'] @state_trigger("input_number.hatch2") def ui_hatch2(**kwargs): """ Triggered when a hatch 2 slider has changed its value""" task.unique("ui_hatch2",kill_me=True) hatch_change(kwargs['var_name'],kwargs['value'],kwargs['old_value']) input_number.hatch2 = kwargs['value'] @state_trigger("input_number.hatch3") def ui_hatch3(**kwargs): """ Triggered when a hatch 3 slider has changed its value""" task.unique("ui_hatch3",kill_me=True) hatch_change(kwargs['var_name'],kwargs['value'],kwargs['old_value']) input_number.hatch3 = kwargs['value'] @state_trigger("input_number.hatch4") def ui_hatch4(**kwargs): """ Triggered when a hatch 4 slider has changed its value""" task.unique("ui_hatch4",kill_me=True) hatch_change(kwargs['var_name'],kwargs['value'],kwargs['old_value']) input_number.hatch4 = kwargs['value'] def hatch_change(name=None, value=None, prev=None): """ Calculate how much a hatch should move """ move_hatch(name,float(value)-float(prev)) @service def move_hatch(id=None,value=None): """Move a hatch to procentage of open, Doing so by enable GPIO for a certain time. Only allow one movement at the time """ log.warning(f"move_hatch: id {id} value {value}") if not hatch_moving(id): if id in ui_to_GPIO: if value < 0: GPIO.output(ui_to_GPIO[id][0], GPIO.LOW) task.sleep(abs(value) * TIME_CONSTANT) GPIO.output(ui_to_GPIO[id][0], GPIO.HIGH) else: GPIO.output(ui_to_GPIO[id][1], GPIO.LOW) task.sleep(abs(value) * TIME_CONSTANT) GPIO.output(ui_to_GPIO[id][1], GPIO.HIGH) @service def hatch_init(): GPIO.setmode(GPIO.BCM) for h in ui_to_GPIO.values(): GPIO.setup(h[0], GPIO.OUT) GPIO.setup(h[1], GPIO.OUT) GPIO.output(h[0], GPIO.HIGH) GPIO.output(h[1], GPIO.HIGH) # Check if any hatch is moving def hatch_moving(id=None): res = None if id in ui_to_GPIO: if (not GPIO.input(ui_to_GPIO[id][0])) or (not GPIO.input(ui_to_GPIO[id][1])): res = True else: res = False return res @service def hatch_reset(): """Move all hatches to fully closed only if not already moving""" anyMoving = False for id in ui_to_GPIO.keys(): if hatch_moving(id): log.warning(f"hatch_reset: id {id} moving") anyMoving = True if not anyMoving: for id in ui_to_GPIO.values(): GPIO.output(id[0], GPIO.LOW) task.sleep(abs(120) * TIME_CONSTANT) for id in ui_to_GPIO.values(): GPIO.output(id[0], GPIO.HIGH) input_number.hatch1 = "0" input_number.hatch2 = "0" input_number.hatch3 = "0" input_number.hatch4 = "0" @service def valve_init(): GPIO.setmode(GPIO.BCM) for p in valve_to_GPIO: GPIO.setup(p, GPIO.OUT) GPIO.output(p, GPIO.HIGH) @service def valve_service(valve=None, value=None): log.warning(f"valve_service: {valve} {value}") if int(valve) > 0 and int(valve) <= len(valve_to_GPIO): if value == 'on': log.warning(f"valve_service: {value}") GPIO.output(valve_to_GPIO[int(valve)-1], GPIO.LOW) task.sleep(VALVE_SLEEP_CONSTANT) value='off' if value == 'off': GPIO.output(valve_to_GPIO[int(valve)-1], GPIO.HIGH) log.warning(f"valve_service: {value}") exec("%s = '%s'" %('switch.valve' + valve,'off')) @state_trigger("switch.valve1 or switch.valve2 or switch.valve3 or switch.valve4") def ui_valve(**kwargs): log.warning(f"ui_valve: {kwargs['var_name']} old {kwargs['old_value']} new {kwargs['value']}") valve_service(kwargs['var_name'][-1],kwargs['value']) # Triggered when Home assistant initialises @time_trigger def startup_event(): hatch_init() valve_init() ``` #### UPS The UPS is used to supply the RPI with power even if main supply is disconnected. It will supply for an hour. Home assistant will ask the UPS for the charge precentage every 5 minute. If mean of five consecutive samples is below 40 will home assistant create a shutdown file and then call home assistant stop service to safely stop the instance. Following code: Copy INA219.py to pyscript directory ``` cp UPS_HAT/INA219.py homeAssProd/pyscript ``` Replace smbus with smbus2 import os for the os.open function Replace main function with ``` ina219 = INA219(addr=0x43) q =queue.Queue(5) @service @time_trigger("period(now, 5m)") def ups_precentage(**kwargs): bus_voltage = ina219.getBusVoltage_V() p = (bus_voltage - 3)/1.2*100 if(p > 100):p = 100 if(p < 0):p = 0 log.warning("ups_percentage: {:3.1f}%".format(p)) entity = "sensor.ups_precentage" state.set(entity, float("{:3.1f}".format(p)), {"unit_of_measurement":"%"}) if q.full(): q.get() log.warning(f"ups: {mean(list(q.queue))}") if mean(list(q.queue)) < 40: os.open("shutdown",os.O_WRONLY|os.O_CREAT) homeassistant.stop() q.put(p) ``` Set docker restart policy to no too finish the container if home assistant is stopped ``` docker update --restart no homeAssProd ``` Then schedule a cron job to check for the shutdown file and if it exist shutdown the rpi ``` FILE=/home/pi/homeAssProd/shutdown if [ -f "$FILE" ]; then echo "`date -Is` shutdown due to low battery" >> /home/pi/shutdown.txt mv -f "$FILE" "$FILE".PREV sudo shutdown now fi ``` To get it back on track after a power shortage. Power on the RPI and restart the home assistant container ``` docker container restart homeAssProd ``` ### Backup As backup solution [Idrive](https://www.idrive.com/) is used, main reason is that i use idrive for all my cloud backup. Everything under the user pi is backed up using the idrive [perl backup scripts](https://github.com/Taryck/idrive). It means that all the HAS configurations and custom_components code etc are backed up. But not the HAS container. Iam therefore exporting the container after big changes or upgrades. ``` docker export homeAssProd > HA.tar ``` It is scheduled to backup once every week but is triggered manually for bigger changes. #### Procedure Install git ``` sudo apt-get install git ``` Clone latest perl script base ``` git clone https://github.com/Taryck/idrive.git IDriveForLinux ``` Setup account on RPI ``` sudo ./account_settings.pl ``` Backup files to cloud ``` sudo ./Backup_Script.pl ``` Schedule backup automation ``` sudo ./scheduler.pl ``` # Result and Demonstration Final assembly in the waterproof case Assemblied Hardware except Valves and Hatches and wires to them ![](https://hackmd.io/_uploads/rJnlwV3u2.png) Video of [Irrigation](https://drive.google.com/file/d/1F8C2rLZ70Xyzq51tUXvh61jZLBuPde-M/view?usp=sharing) of plants Video of Opening and Closing [hatches](https://drive.google.com/file/d/19fICo8tYLXeECnyuuU18EW9qhTh0-2mg/view?usp=sharing) # Bonus This section contains extra features which is implemented in the home assistant system but really in relation with the green house ## SunGrow SG15rt My parents in law bought solar panels together with an SunGrow SG15rt inverter. So ofcourse did we want the Home assistant dashboard to include daily and total produced energy ![Produced Energy Dashboard](https://hackmd.io/_uploads/rJuX595nh.jpg) To get this data into Home assistant i reused solutions provided by [bohdan](https://github.com/bohdan-s). I used the SunGrowClient with http SunGrowModbusWebClient. I choosed not to use the SunGather application but instead created a pyscript periodic function which polls data every 30 minute from the inverter websocket. Here are the steps i used to get it going. login to docker container ``` docker exec -it homeAssProd bash ``` Install SunGrowClient ``` pip install SunGrowClient ``` added following code to sg15rt.py in the pyscript folder and a [registers](https://github.com/bohdan-s/SunGather/blob/main/SunGather/registers-sungrow.yaml) file containing all modbus sungrow registers ``` import sys import yaml import os from SungrowClient.SungrowClient import SungrowClient @time_trigger("period(now, 30m)") def sg15rt_scrape(): filename = "pyscript/registers-sungrow.yaml" reg_fd = os.open(filename,os.O_RDONLY) r = os.read(reg_fd, os.stat(filename).st_size) registersfile = yaml.safe_load(r.decode('utf-8')) config_inverter = { "host": "YOUR IP", "connection": "http", "level":1, "slave":1, "scan_interval":30 } inverter = SungrowClient(config_inverter) con = task.executor(inverter.checkConnection) if not con: return con = task.executor(inverter.configure_registers,registersfile) if not con: return inverter.checkConnection() con = task.executor(inverter.scrape) if con and hasattr(inverter,'latest_scrape') and "daily_power_yields" in inverter.latest_scrape.keys(): entity = "sensor.sg15rt_daily" state.set(entity, float("{:3.1f}".format(d)), {"unit_of_measurement":"kWh"}) entity = "sensor.sg15rt_total" t = inverter.latest_scrape["total_power_yields"] state.set(entity, float("{:3.1f}".format(t)), {"unit_of_measurement":"kWh"}) ``` and then finally add following to home assistant configuration.yaml ``` - platform: command_line name: "sg15rt_daily" unit_of_measurement: "kWh" scan_interval: 3600 command: "" - platform: command_line name: "sg15rt_total" unit_of_measurement: "kWh" scan_interval: 3600 command: "" ```

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully