Example project: PIR sensor and Domoticz API



  • I've done a small project on my WiPy 1.0 with a PIR sensor to turn on a lamp in my home using my existing Domoticz setup. I'm using this sensor. This project can be found on this repo. Below a small report of the project and the code.

    The setup
    For every motion trigger, a message is sent to the domoticz api to update a 'user variable'. The domoticz installation, which is running on a RasPi, has a lua script that triggers when this variable changes and turns one lamp on for 2 minutes. (It uses the rfxcom 433 tranceiver and cheap pieces of hardware in my lamps, but that's not relevant now) My kitchen seemed like a good place to implement this, since the lights there only need to be on when there is motion.

    To make it run from the 3.3v out of the WiPy, I soldered a wire to bypass the 5v to 3.3v stepdown on the back of the PIR board. When the sensor triggers, it pulls the pin up for about 5 or 6 seconds, after which it drops back, ready for more triggers. This means I don't have to build in any mechanism to prevent sending http requests too often, which is nice.

    Main.py
    I kept everything very simple, so it's easy for others to grab and adept for their own purposes. Besides the sensor, I also listen to a button press on the expansion board to break the main loop, which I found useful for testing. To make it work with your domoticz, just replace <ip> and <basic hash> with your values.

    import time
    from network import WLAN
    from machine import Pin
    from domoticz import Domoticz
    
    wl = WLAN(WLAN.STA)
    d = Domoticz("<ip>", 8080 ,"<basic hash>")
    
    #flags
    running = True
    button_pressed = False
    pir_triggered = False
    
    #callbacks
    def pirTriggered(pin):
        global pir_triggered
        pir_triggered = True
    
    def buttonPressed(pin):
        global button_pressed
        button_pressed = True
    
    
    pir = Pin('GP4',mode=Pin.IN,pull=Pin.PULL_UP)
    pir.irq(trigger=Pin.IRQ_RISING, handler=pirTriggered)
        
    pir = Pin('GP17',mode=Pin.IN,pull=Pin.PULL_UP)
    pir.irq(trigger=Pin.IRQ_FALLING, handler=buttonPressed)
    
    # main loop
    print("Starting main loop")
    while running:
        time.sleep_ms(500)
        if pir_triggered:
            pir_triggered = False
            result = d.setVariable('Presence:LivingRoom','1')
            print("HTTP Status: "+str(result))
        elif button_pressed:
            button_pressed = False
            running = False
    
    print("Exited main loop")
    

    Domoticz class
    This implements a simple http get request and 2 domoticz api endpoints. Implementation consists of setting values of devices and user-variables.

    I tried implementing the micropython-http-client class from balloob (I simply copied the class into my project), but I found that after 4 successful http requests it stopped working for some reason. So for now, I'm just using the bare sockets in the simplest way possible.

    import socket
    class Domoticz:
        
        def __init__(self, ip, port,  basic):
            self.basic = basic
            self.ip = ip
            self.port = port
        
        def setDevice(self, idx, command):
            print("Setting device "+idx+" to "+command)
            return self.sendRequest("type=command&param=switchlight&idx="+idx+"&switchcmd="+command)
    
        def setVariable(self, name, value):
            print("Setting variable "+name+" to "+value)
            return self.sendRequest("type=command&param=updateuservariable&vtype=0&vname="+name+"&vvalue="+value)
    
        def sendRequest(self, path):
            
            try:
                s = socket.socket()
                s.connect((self.ip,self.port))
                s.send(b"GET /json.htm?"+path+" HTTP/1.1\r\nHost: pycom.io\r\nAuthorization: Basic "+self.basic+"\r\n\r\n")
                status = str(s.readline(), 'utf8')
                code = status.split(" ")[1]
                s.close()
                return code
                
            except Exception:
                print("HTTP request failed")
                return 0
    

    Boot.py
    I slightly adapted the wifi code to support fixed IP on different wifi networks

    import os
    import machine
    
    uart = machine.UART(0, 115200)
    os.dupterm(uart)
    
    known_nets = {
        '<net>': {'pwd': '<password>'}, 
        '<net>': {'pwd': '<password>', 'wlan_config':  ('10.0.0.114', '255.255.0.0', '10.0.0.1', '10.0.0.1')}, # (ip, subnet_mask, gateway, DNS_server)
    }
    
    if machine.reset_cause() != machine.SOFT_RESET:
        from network import WLAN
        wl = WLAN()
        wl.mode(WLAN.STA)
        original_ssid = wl.ssid()
        original_auth = wl.auth()
    
        print("Scanning for known wifi nets")
        available_nets = wl.scan()
        nets = frozenset([e.ssid for e in available_nets])
    
        known_nets_names = frozenset([key for key in known_nets])
        net_to_use = list(nets & known_nets_names)
        try:
            net_to_use = net_to_use[0]
            net_properties = known_nets[net_to_use]
            pwd = net_properties['pwd']
            sec = [e.sec for e in available_nets if e.ssid == net_to_use][0]
            if 'wlan_config' in net_properties:
                wl.ifconfig(config=net_properties['wlan_config']) 
            wl.connect(net_to_use, (sec, pwd), timeout=10000)
            while not wl.isconnected():
                machine.idle() # save power while waiting
            print("Connected to "+net_to_use+" with IP address:" + wl.ifconfig()[0])
            
        except Exception as e:
            print("Failed to connect to any known network")
            wl.init(mode=WLAN.AP, ssid=original_ssid, auth=original_auth, channel=6, antenna=WLAN.INT_ANT)
    

    Plans:

    • Making the http library from Balloob wor properly, or implementing a new http wrapper class
    • Add more sensors like temperature and lux to this same project and send these values to domoticz
    • Expand domoticz class to support the full api

    EDIT
    The API on the esp32 devices for Pin has changed. The pir.irq method has been replaced by pin.callback. The correct implementation for these devices would be:

    pir = Pin('G4',mode=Pin.IN,pull=Pin.PULL_UP)
    pir.callback(trigger=Pin.IRQ_RISING, handler=pirTriggered)
    
    pir = Pin('G17',mode=Pin.IN,pull=Pin.PULL_UP)
    pir.callback(trigger=Pin.IRQ_FALLING, handler=buttonPressed)
    


  • @Ralph OK great. Not sure if you took this project any further (?) but I am working on something almost identical. https://forum.pycom.io/topic/1308/wipy-motion-and-ambient-conditions-sensor-node
    Cheers



  • @robmarkcole Correct, thanks for pointing this out. I added a note to this post now with the correct code.



  • @Ralph Does this need to be updated since callbacks are not handled with pin.irq (deprecated?) but pin.callback? https://docs.pycom.io/pycom_esp32/library/machine.Pin.html#machine.Pin



  • @Xykon You are right, I might as well have left it on the 5V.
    I'm running it without the expansion board and only soldered one connector to the 5v out of an old usb cable that I stripped. I could have added another connector on hindsight, but at that point it seemed easier to use the free 3.3v out pin on the wipy and bypass the stepdown.


  • Global Moderator

    Thanks a lot for sharing the project

    I soldered a wire to bypass the 5v to 3.3v stepdown on the back of the PIR board

    I'm curious why you did that instead of powering the PIR sensor from 5V through VIN? The only reason I could think of is that you are using a 3.7V LIPO to power the Wipy2. But if it's installed in an area where you have power anyway I'm wondering why you would rely on a battery powered solution.


Log in to reply
 

Looks like your connection to Pycom Forum was lost, please wait while we try to reconnect.