Code for Garden watering

Discussion about programs, libraries and tools that work with MicroPython. Mostly these are provided by a third party.
Target audience: All users and developers of MicroPython.
Post Reply
DeathKidSkinner
Posts: 1
Joined: Thu Jun 03, 2021 6:03 pm

Code for Garden watering

Post by DeathKidSkinner » Thu Jun 03, 2021 6:32 pm

Hello Guys,

I'm new to the world of MicroPython and I need your help. I'm super unskilled with programming. I learned C 5 years ago in school. But it's all lost memories now. I need a simple Code. I wanted to make a watering machine for my balcony plants. I have an Raspberry Pi Pico at Home. The Programm should be work like this:
  • Pushing a button (external) should start the routine.
  • The Programm should count for like 30 seconds and set an Output to 1
  • After the 30 seconds the Output should switch to 0 again
  • If I push the button again, the routine should start again

The Output should trigger a 5V Relais which powers over an PSU a Water pump (12V) which is delevering the water through a long tube to the plants.

Maybe someone can help me with this simple thing. :D

kevinkk525
Posts: 969
Joined: Sat Feb 03, 2018 7:02 pm

Re: Code for Garden watering

Post by kevinkk525 » Tue Jun 08, 2021 5:02 am

I'm sure it can be done simpler but using my existing libraries and uasyncio something like this could work (Not that I didn't actually test the main code, the abutton library definitely works):

Save this file as abutton.py:

Code: Select all

# Author: Kevin Köck
# Copyright Kevin Köck 2019-2020 Released under the MIT license
# Based on Peter Hinch's aswitch.py. Useable as a drop-in replacement.
# Queue overflow issue in Peter Hinch's aswitch fixed by now so this code
# only provides an alternative with less RAM usage.
# Created on 2019-10-19 

__updated__ = "2019-10-19"
__version__ = "0.1"

import uasyncio as asyncio
import time

type_gen = type((lambda: (yield))())  # Generator type


# If a callback is passed, run it and return.
# If a coro is passed initiate it and return.
# coros are passed by name i.e. not using function call syntax.
def launch(func, tup_args):
    res = func(*tup_args)
    if isinstance(res, type_gen):
        loop = asyncio.get_event_loop()
        loop.create_task(res)


class Pushbutton:
    debounce_ms = 50
    long_press_ms = 1000
    double_click_ms = 400

    def __init__(self, pin, suppress=False):
        self.pin = pin
        self._supp = suppress  # don't call release func after long press
        self._tf = None  # pressed function
        self._ff = None  # released function
        self._df = None  # double pressed function
        self._lf = None  # long pressed function
        self.sense = pin.value()  # Convert from electrical to logical value
        self.state = self.rawstate()  # Initial state
        loop = asyncio.get_event_loop()
        loop.create_task(self.buttoncheck())  # Thread runs forever

    def press_func(self, func, args=()):
        self._tf = func
        self._ta = args

    def release_func(self, func, args=()):
        self._ff = func
        self._fa = args

    def double_func(self, func, args=()):
        self._df = func
        self._da = args

    def long_func(self, func, args=()):
        self._lf = func
        self._la = args

    # Current non-debounced logical button state: True == pressed
    def rawstate(self):
        return bool(self.pin.value() ^ self.sense)

    # Current debounced state of button (True == pressed)
    def __call__(self):
        return self.state

    async def buttoncheck(self):
        t_change = None
        supp = False
        clicks = 0
        lpr = False  # long press ran
        ####
        # local functions for performance improvements
        deb = self.debounce_ms
        dcms = self.double_click_ms
        lpms = self.long_press_ms
        raw = self.rawstate
        ticks_diff = time.ticks_diff
        ticks_ms = time.ticks_ms
        #
        while True:
            state = raw()
            if state is False and self.state is False and self._supp and \
                    ticks_diff(ticks_ms(), t_change) > dcms and clicks > 0 and self._ff:
                clicks = 0
                launch(self._ff, self._fa)
            elif state is True and self.state is True:
                if clicks > 0 and ticks_diff(ticks_ms(), t_change) > dcms:
                    # double click timeout
                    clicks = 0
                if self._lf and lpr is False:  # check long press
                    if ticks_diff(ticks_ms(), t_change) >= lpms:
                        lpr = True
                        clicks = 0
                        if self._supp is True:
                            supp = True
                        launch(self._lf, self._la)
            elif state != self.state:  # state changed
                lpr = False
                self.state = state
                if state is True:  # Button pressed: launch pressed func
                    if ticks_diff(ticks_ms(), t_change) > dcms:
                        clicks = 0
                    if self._df:
                        clicks += 1
                    if clicks == 2:  # double click
                        clicks = 0
                        if self._supp is True:
                            supp = True
                        launch(self._df, self._da)
                    elif self._tf:
                        launch(self._tf, self._ta)
                else:  # Button released. launch release func
                    if supp is True:
                        supp = False
                    elif clicks and self._supp > 0:
                        pass
                    elif self._ff:  # not after a long press with suppress
                        launch(self._ff, self._fa)
                t_change = ticks_ms()
            # Ignore state changes until switch has settled
            await asyncio.sleep_ms(deb)
Main program as main.py:

Code: Select all

from abutton import Pushbutton
import uasyncio as asyncio
import machine

#############
### change these
pin_button = machine.Pin(1, machine.Pin.IN) # change to your pin!
pin_relais = machine.Pin(2, machine.Pin.OUT) # change to your pin!
on_time = 30 # 30 seconds on_time for relais
#############

_routine_task = None
pin_relais.value(0)

async def routine():
    try:
        print("Started routine")
        pin_relais.value(1)
        await asyncio.sleep(on_time)
        # pin_relais.value(0) # not needed because it is done in finally: 
    except asyncio.CancelledError:
        print("Canceled routine")
    finally:
        pin_relais.value(0)
        global _routine_task
        _routine_task = None
        print("Routine exited")

def start_routine():
    global _routine_task
    if _routine_task is None:
        _routine_task = asyncio.create_task(routine())
    else:
        _routine_task.cancel()

button = Pushbutton(pin)
button.release_func(start_routine)
loop = asyncio.get_event_loop()
loop.run_forever()
With this the routine starts every time you press the button. However, if a routine is already running and you press the button, the routine will be canceled. Hope it works and helps.

You need to change in the main.py your pin definitions to match your pin numbers. Also it assumes that your relais is active if the pin outputs HIGH.
Kevin Köck
Micropython Smarthome Firmware (with Home-Assistant integration): https://github.com/kevinkk525/pysmartnode

Post Reply