DIY Gartenbewässerung

DIY Gartenbewässerung

Mit ESP32 und toit

Wenn es im Sommer trocken wird, müssen auch Maker ihre Pflanzen und den Rasen bewässern. Um entscheiden zu können, ob der Boden zu trocken ist, kann ein Bodenfeuchtesensor verwendet werden. Um den Boden zu bewässern können Ventile und Pumpen genutzt werden. Wie man diese Sachen an einen ESP32 anschließt, besprechen wir in diesem Video!

TOIT installieren

Zunächst sollte man die toit Programmiersprache installiert haben und folgendes Tutorial verstanden haben: https://www.digitale-dinge.de/blog/episode24_toit/

ADC auslesen

Nun können wir den Bodenfeuchtesensor mit dem ADC des ESP32 auslesen. Der folgende Source-Code erledigt dies in wenigen Codezeilen:

import gpio
import gpio.adc show Adc
import encoding.json as json

pinnumber_soil ::= 34

main:
    pin_soil := gpio.Pin pinnumber_soil
    adc_soil := Adc (pin_soil)

    (Duration --s=1).periodic:
        soil := adc_soil.get

        json_str := "{'soil':'$soil'}"
        print json_str

Natürlich kann auch jeder andere ADC-Pin anstelle von Pin 34 verwendet werden. Das Beispiel gibt ein JSON Objekt auf der Kommandozeile aus. Im Video zeige ich, wie man das Programm auf einen ESP32 lädt.

MQTT

Im folgenden Beispiel werden die aktuellen Messwert von Bodenfeuchte und Batteriespannung jeweils in Volt an einen MQTT Broker geschickt. Die Adresse des Brokers steckt in der Konstante HOSTund die MQTT topics sind in den Konstanten TOPIC_SOIL, TOPIC_BATTERY, TOPIC_LED und TOPIC_VALVE hinterlegt. Die Pins von LED, Bodenfeuchtesensor und Ventil sind leicht über folgende Konstanten zu ändern: pinnumber_led, pinnumber_soil, pinnumber_valve Da in diesem Beispiel ein Roboheart Hercules Board der Firma Augmented Robotics verwendet wird, enthält der Code weitere Konstanten für die Pins des Motortreibers und die Methoden valve_init,valve_sleep,valve_close,valve_open müssen für andere Boards mit anderen Motortreibern/ICs angepasst werden, um das Ventil zu steuern.

Um die MQTT Library nutzen zu können, muss sie mit folgendem Befehl hinzugefügt werden:

jag pkg install github.com/toitware/mqtt

Hier der Beispielcode aus dem Livestream:

import gpio
import gpio.adc show Adc
import encoding.json as json

import net
//jag pkg install github.com/toitware/mqtt
import mqtt

OKAKI_DEVICE_ID ::= "device2"
CLIENT_ID ::= "okaki32-$(OKAKI_DEVICE_ID)"
HOST      ::= "192.168.100.110"

TOPIC_SOIL ::= "okaki/$(OKAKI_DEVICE_ID)/soil"
TOPIC_BATTERY ::= "okaki/$(OKAKI_DEVICE_ID)/battery"
TOPIC_LED ::= "okaki/$(OKAKI_DEVICE_ID)/led"
TOPIC_VALVE ::= "okaki/$(OKAKI_DEVICE_ID)/valve"

pinnumber_led ::= 14
pinnumber_soil ::= 34
pinnumber_battery ::= 36

GPIO_MA_PH_IN1 ::= 25  //  PHASE/IN1  //DAC1
GPIO_MA_EN_IN2 ::= 26  //  ENABLE/IN2  //DAC2
GPIO_MA_MODE ::= 2  //  MODE        
GPIO_MA_SLEEP ::= 0  //  nSLEEP

pin_ma_ph_in1 := gpio.Pin GPIO_MA_PH_IN1 --output
pin_ma_en_in2 := gpio.Pin GPIO_MA_EN_IN2 --output
pin_ma_mode := gpio.Pin GPIO_MA_MODE --output
pin_ma_sleep := gpio.Pin GPIO_MA_SLEEP --output

valve_init:  
  pin_ma_ph_in1.set 0
  pin_ma_en_in2.set 0
  pin_ma_mode.set 0
  pin_ma_sleep.set 1

valve_sleep:
  //coast mode
  pin_ma_ph_in1.set 0
  pin_ma_en_in2.set 0
  pin_ma_sleep.set 0

valve_close:
  pin_ma_sleep.set 1
  pin_ma_ph_in1.set 1
  pin_ma_en_in2.set 0
  sleep --ms=100
  valve_sleep

valve_open:
  pin_ma_sleep.set 1
  pin_ma_ph_in1.set 0
  pin_ma_en_in2.set 1  
  sleep --ms=100
  valve_sleep

main:
  pin_soil := gpio.Pin pinnumber_soil
  pin_battery := gpio.Pin pinnumber_battery
  pin_led := gpio.Pin pinnumber_led --output

  adc_soil := Adc (pin_soil)
  adc_battery := Adc (pin_battery)

  valve_init

  network := net.open
  transport := mqtt.TcpTransport network --host=HOST
  client := mqtt.Client --transport=transport
  client.start --client_id=CLIENT_ID

  // MQTT broker is now connected.

  client.subscribe TOPIC_LED:: | topic payload |
    print "Received: $topic: $payload.to_string_non_throwing"
    if payload.to_string_non_throwing == "1": 
      print "LED ON"
      pin_led.set 1
    else:
      print "LED OFF"
      pin_led.set 0 

  client.subscribe TOPIC_VALVE:: | topic payload |
    print "Received: $topic: $payload.to_string_non_throwing"
    if payload.to_string_non_throwing == "1": 
      print "VALVE ON"
      valve_open
    else:
      print "VALVE OFF"
      valve_close
  

  (Duration --s=10).periodic:
    soil := adc_soil.get
    battery := adc_battery.get

    json_str := "{'soil':'$soil','battery':'$battery'}"
    print json_str

    client.publish TOPIC_SOIL "$soil".to_byte_array
    client.publish TOPIC_BATTERY "$battery".to_byte_array

OKAKI nutzen

Das OKAKI Open-Source Projekt ist noch nicht fertig, aber folgender Beispielcode sendet Messwerte an die REST API der Okaki-Device-Bridge:

import gpio
import gpio.adc show Adc
import encoding.json as json

//jag pkg install github.com/toitware/http
import http
import net
import certificate_roots
import esp32
import http.headers

CERTIFICATE ::= certificate_roots.ISRG_ROOT_X1
network := net.open
client := http.Client.tls network --root_certificates=[CERTIFICATE]

URL ::= "appwrite.okaki.org"
PORT ::= 8079
PATH ::= "/measurements"

DEVICE_ID ::= "device2"
KEY ::= "my-secret"

pinnumber_led ::= 14
pinnumber_soil ::= 34
pinnumber_battery ::= 36
GPIO_MA_PH_IN1 ::= 25  //  PHASE/IN1  //DAC1
GPIO_MA_EN_IN2 ::= 26  //  ENABLE/IN2  //DAC2
GPIO_MA_MODE ::= 2  //  MODE        
GPIO_MA_SLEEP ::= 0  //  nSLEEP
pin_ma_ph_in1 := gpio.Pin GPIO_MA_PH_IN1 --output
pin_ma_en_in2 := gpio.Pin GPIO_MA_EN_IN2 --output
pin_ma_mode := gpio.Pin GPIO_MA_MODE --output
pin_ma_sleep := gpio.Pin GPIO_MA_SLEEP --output

valve_init:  
  pin_ma_ph_in1.set 0
  pin_ma_en_in2.set 0
  pin_ma_mode.set 0
  pin_ma_sleep.set 1

valve_sleep:
  //coast mode
  pin_ma_ph_in1.set 0
  pin_ma_en_in2.set 0
  pin_ma_sleep.set 0

valve_close:
  pin_ma_sleep.set 1
  pin_ma_ph_in1.set 1
  pin_ma_en_in2.set 0
  sleep --ms=100
  valve_sleep

valve_open:
  pin_ma_sleep.set 1
  pin_ma_ph_in1.set 0
  pin_ma_en_in2.set 1  
  sleep --ms=100
  valve_sleep

main:
  pin_soil := gpio.Pin pinnumber_soil
  pin_battery := gpio.Pin pinnumber_battery
  pin_led := gpio.Pin pinnumber_led --output

  adc_soil := Adc (pin_soil)
  adc_battery := Adc (pin_battery)

  valve_init

  
  (Duration --s=60).periodic:
    soil := adc_soil.get
    battery := adc_battery.get
    json_str := {"deviceID":"$DEVICE_ID","key":"$KEY","measurements":[{"sensorID":"647f3cf0e1c62af05311","sensorTypeID":"soilmoisture","value":"$soil"},{"sensorID":"647f3d06599e879074f8","sensorTypeID":"battery","value":"$battery"}]}

    print "sending POST REQUEST TO $URL:$PORT$PATH"
    print json_str
    response := client.post_json --host=URL --port=PORT --path=PATH json_str
  
    print response.status_code
    print response.status_message
    data := response.body.stringify
    //data := json.decode_stream response.body
    client.close
    print data

Damit das Beispiel funktionieren kann, müssen alle Pakete aus dieser package.yaml Datei installiert sein:

dependencies:
  certificate_roots:
    url: github.com/toitware/toit-cert-roots
    version: ^1.4.0
  dhtxx:
    url: github.com/toitware/toit-dhtxx
    version: ^1.3.1
  http:
    url: github.com/toitlang/pkg-http
    version: ^2.1.0
  mqtt:
    url: github.com/toitware/mqtt
    version: ^2.2.0

Um die Pakete zu installieren, genügt dieser Befehl:

jag pkg install

Links zur Sendung