Fixed RMS calculation
This commit is contained in:
29
README.md
Normal file
29
README.md
Normal file
@@ -0,0 +1,29 @@
|
||||
## Flashing Micropython
|
||||
|
||||
Download the latest firmware from
|
||||
https://micropython.org/download/?port=esp8266
|
||||
(ESP8266 with 2MiB+ flash)
|
||||
|
||||
Flash Wemos D1 mini with (note the -fm dout option)
|
||||
|
||||
esptool.py --port /dev/ttyUSB0 --baud 460800 write_flash -fm dout --flash_size=detect 0 esp8266-20220618-v1.19.1.bin
|
||||
|
||||
## Access webrepl
|
||||
|
||||
minicom -D /dev/ttyUSB0
|
||||
|
||||
Password for webrepl: badkamer
|
||||
|
||||
When the program is running, webrepl is not responsive. Get the prompt >>> with Ctrl+C.
|
||||
|
||||
|
||||
## Flashing source code
|
||||
|
||||
ampy --port /dev/ttyUSB0 put . /
|
||||
|
||||
Flash Wemos D1 mini with (note the -fm dout option)
|
||||
|
||||
esptool.py --port /dev/ttyUSB0 --baud 460800 write_flash -fm dout --flash_size=detect 0 esp8266-20220618-v1.19.1.bin
|
||||
|
||||
|
||||
|
||||
11
boot.py
Normal file
11
boot.py
Normal file
@@ -0,0 +1,11 @@
|
||||
# Configuration is stored in a separate config.py
|
||||
from config import WIFI_ESSID, WIFI_PASSWORD, HOSTNAME
|
||||
from wifi import wifi_connect, disable_wifi_ap
|
||||
import webrepl
|
||||
|
||||
# setup network
|
||||
disable_wifi_ap()
|
||||
wifi_connect(WIFI_ESSID, WIFI_PASSWORD,HOSTNAME)
|
||||
|
||||
# Enabel webrepl
|
||||
webrepl.start()
|
||||
4
config.py
Normal file
4
config.py
Normal file
@@ -0,0 +1,4 @@
|
||||
WIFI_ESSID='van-halteren-iot'
|
||||
WIFI_PASSWORD='M0squ1ttoFh@m'
|
||||
HOSTNAME = 'badkamer'
|
||||
DELAY=160000
|
||||
91
delayedswitch.py
Normal file
91
delayedswitch.py
Normal file
@@ -0,0 +1,91 @@
|
||||
import machine
|
||||
import time
|
||||
from machine import Timer
|
||||
import urequests as requests
|
||||
|
||||
|
||||
#
|
||||
# Class for handling alarms and calling FHEM
|
||||
#
|
||||
class DelayedSwitch:
|
||||
|
||||
def __init__(self,delay,rtc):
|
||||
self.__delay = delay
|
||||
self.__alarm = None
|
||||
self.__is_light_on = False
|
||||
self.__rtc = rtc
|
||||
|
||||
|
||||
def _get_switch_status(self):
|
||||
# check status of light
|
||||
r = self._call_ccu3("http://10.0.10.81/addons/red/switch/state")
|
||||
try:
|
||||
data = r.json()
|
||||
self.__is_light_on = data.get('state', False)
|
||||
except:
|
||||
print("Error parsing JSON response")
|
||||
self.__is_light_on = False
|
||||
r.close()
|
||||
if self.__is_light_on:
|
||||
print("Light is already on.")
|
||||
else:
|
||||
print("Light is off.")
|
||||
|
||||
#
|
||||
# Switch of the light through FHEM
|
||||
def _switch_off_handler(self, alarm):
|
||||
print("Switching light off")
|
||||
r = self._call_ccu3("http://10.0.10.81/addons/red/switch/off")
|
||||
self.__is_light_on = False
|
||||
# close the response object
|
||||
r.close()
|
||||
|
||||
#
|
||||
# Call CCU3
|
||||
#
|
||||
def _call_ccu3(self,url):
|
||||
try:
|
||||
r = requests.get(url)
|
||||
except OSError as oer:
|
||||
print("Resetting. We received OSSeror: ", oer)
|
||||
machine.reset()
|
||||
return r
|
||||
|
||||
def is_light_on(self):
|
||||
return self.__is_light_on
|
||||
|
||||
def set_delay(self,d):
|
||||
self.__delay = d
|
||||
|
||||
def absence(self):
|
||||
# cancel any previous time (if any)
|
||||
if self.__alarm != None:
|
||||
print("Canceling previous alarm")
|
||||
self.__alarm.deinit()
|
||||
|
||||
print("Alarm started to switch off in:", self.__delay)
|
||||
self.__alarm = Timer(-1)
|
||||
self.__alarm.init(period=self.__delay, mode=Timer.ONE_SHOT, callback=self._switch_off_handler)
|
||||
#self.__alarm = Timer.Alarm(self._switch_off_handler, self.__delay, periodic=False)
|
||||
|
||||
|
||||
def presence(self):
|
||||
# cancel any previous time (if any)
|
||||
if self.__alarm != None:
|
||||
print("Canceling previous alarm")
|
||||
self.__alarm.deinit()
|
||||
|
||||
# Check if we light is really on
|
||||
# (in case somebody manually switched it off)
|
||||
if self.__is_light_on:
|
||||
self._get_switch_status()
|
||||
|
||||
#
|
||||
# Switch light on
|
||||
#
|
||||
if not self.__is_light_on:
|
||||
print("Switching light on")
|
||||
r = self._call_ccu3("http://10.0.10.81/addons/red/switch/on")
|
||||
self.__is_light_on = True
|
||||
# close the response object
|
||||
r.close()
|
||||
75
main.py
Normal file
75
main.py
Normal file
@@ -0,0 +1,75 @@
|
||||
import utime
|
||||
import gc
|
||||
from delayedswitch import DelayedSwitch
|
||||
from ntptime import settime
|
||||
from config import DELAY
|
||||
from machine import ADC
|
||||
|
||||
import machine
|
||||
|
||||
from machine import Pin
|
||||
# Wemos D1 pin14 = D5
|
||||
d5 = Pin(14, machine.Pin.IN, machine.Pin.PULL_UP)
|
||||
|
||||
# voltage meter ZMPT101B is attached to pin A0
|
||||
ad0 = ADC(0)
|
||||
|
||||
def read_voltage():
|
||||
nsamples = 50
|
||||
total = 0
|
||||
sq_total = 0
|
||||
for _ in range(nsamples):
|
||||
v = ad0.read()
|
||||
total += v
|
||||
sq_total += v * v
|
||||
utime.sleep_ms(2)
|
||||
mean = total / nsamples
|
||||
return (sq_total / nsamples - mean * mean) ** 0.5
|
||||
|
||||
# check if voltage is over a threshold
|
||||
# read_voltage returns between 2-3 when there is no AC input
|
||||
# read_voltage returns between 123-127 when there is AC input
|
||||
# it seems 100 is a reasonable threshold
|
||||
def high_voltage():
|
||||
v = read_voltage()
|
||||
hv = v > 100
|
||||
# if hv:
|
||||
# print("High voltage: ", v)
|
||||
return hv
|
||||
|
||||
# setup rtc
|
||||
settime()
|
||||
rtc = machine.RTC()
|
||||
utime.sleep_ms(750)
|
||||
print('\nRTC Set from NTP to UTC:', rtc.datetime())
|
||||
|
||||
def pir_thread(switch):
|
||||
pir_on = False
|
||||
while True: # change this!
|
||||
utime.sleep_ms(20)
|
||||
if high_voltage():
|
||||
# was PIR on in previous cycle?
|
||||
if not pir_on:
|
||||
# print some debug info
|
||||
if not switch.is_light_on():
|
||||
d = "%d %d %d %d, %02d:%02d:%02d" % rtc.datetime()[0:7]
|
||||
print("Presence detected at: %s." % d)
|
||||
|
||||
switch.presence()
|
||||
pir_on = True
|
||||
else:
|
||||
if pir_on:
|
||||
d = "%d %d %d %d, %02d:%02d:%02d" % rtc.datetime()[0:7]
|
||||
print("Absense detected at: %s." % d)
|
||||
switch.absence()
|
||||
pir_on = False
|
||||
|
||||
gc.collect()
|
||||
|
||||
|
||||
print("Starting pir thread")
|
||||
switch = DelayedSwitch(DELAY,rtc) # delay in ms
|
||||
pir_thread(switch)
|
||||
|
||||
# Not reached
|
||||
print("Done")
|
||||
114
urequests.py
Normal file
114
urequests.py
Normal file
@@ -0,0 +1,114 @@
|
||||
import usocket
|
||||
|
||||
class Response:
|
||||
|
||||
def __init__(self, f):
|
||||
self.raw = f
|
||||
self.encoding = "utf-8"
|
||||
self._cached = None
|
||||
|
||||
def close(self):
|
||||
if self.raw:
|
||||
self.raw.close()
|
||||
self.raw = None
|
||||
self._cached = None
|
||||
|
||||
@property
|
||||
def content(self):
|
||||
if self._cached is None:
|
||||
self._cached = self.raw.read()
|
||||
self.raw.close()
|
||||
self.raw = None
|
||||
return self._cached
|
||||
|
||||
@property
|
||||
def text(self):
|
||||
return str(self.content, self.encoding)
|
||||
|
||||
def json(self):
|
||||
import ujson
|
||||
return ujson.loads(self.content)
|
||||
|
||||
|
||||
def request(method, url, data=None, json=None, headers={}, stream=None):
|
||||
try:
|
||||
proto, dummy, host, path = url.split("/", 3)
|
||||
except ValueError:
|
||||
proto, dummy, host = url.split("/", 2)
|
||||
path = ""
|
||||
if proto == "http:":
|
||||
port = 80
|
||||
elif proto == "https:":
|
||||
import ussl
|
||||
port = 443
|
||||
else:
|
||||
raise ValueError("Unsupported protocol: " + proto)
|
||||
|
||||
if ":" in host:
|
||||
host, port = host.split(":", 1)
|
||||
port = int(port)
|
||||
|
||||
ai = usocket.getaddrinfo(host, port)
|
||||
addr = ai[0][-1]
|
||||
s = usocket.socket()
|
||||
s.connect(addr)
|
||||
if proto == "https:":
|
||||
s = ussl.wrap_socket(s, server_hostname=host)
|
||||
s.write(b"%s /%s HTTP/1.0\r\n" % (method, path))
|
||||
if not "Host" in headers:
|
||||
s.write(b"Host: %s\r\n" % host)
|
||||
# Iterate over keys to avoid tuple alloc
|
||||
for k in headers:
|
||||
s.write(k)
|
||||
s.write(b": ")
|
||||
s.write(headers[k])
|
||||
s.write(b"\r\n")
|
||||
if json is not None:
|
||||
assert data is None
|
||||
import ujson
|
||||
data = ujson.dumps(json)
|
||||
if data:
|
||||
s.write(b"Content-Length: %d\r\n" % len(data))
|
||||
s.write(b"\r\n")
|
||||
if data:
|
||||
s.write(data)
|
||||
|
||||
l = s.readline()
|
||||
protover, status, msg = l.split(None, 2)
|
||||
status = int(status)
|
||||
#print(protover, status, msg)
|
||||
while True:
|
||||
l = s.readline()
|
||||
if not l or l == b"\r\n":
|
||||
break
|
||||
#print(l)
|
||||
if l.startswith(b"Transfer-Encoding:"):
|
||||
if b"chunked" in l:
|
||||
raise ValueError("Unsupported " + l)
|
||||
elif l.startswith(b"Location:") and not 200 <= status <= 299:
|
||||
raise NotImplementedError("Redirects not yet supported")
|
||||
|
||||
resp = Response(s)
|
||||
resp.status_code = status
|
||||
resp.reason = msg.rstrip()
|
||||
return resp
|
||||
|
||||
|
||||
def head(url, **kw):
|
||||
return request("HEAD", url, **kw)
|
||||
|
||||
def get(url, **kw):
|
||||
return request("GET", url, **kw)
|
||||
|
||||
def post(url, **kw):
|
||||
return request("POST", url, **kw)
|
||||
|
||||
def put(url, **kw):
|
||||
return request("PUT", url, **kw)
|
||||
|
||||
def patch(url, **kw):
|
||||
return request("PATCH", url, **kw)
|
||||
|
||||
def delete(url, **kw):
|
||||
return request("DELETE", url, **kw)
|
||||
|
||||
42
wifi.py
Normal file
42
wifi.py
Normal file
@@ -0,0 +1,42 @@
|
||||
import network
|
||||
import utime
|
||||
|
||||
def wifi_connect(essid, password,hostname):
|
||||
# Connect to the wifi. Based on the example in the micropython
|
||||
# documentation.
|
||||
wlan = network.WLAN(network.STA_IF)
|
||||
wlan.active(True)
|
||||
# Set DHCP host name to recognize the device in your router
|
||||
wlan.config(dhcp_hostname=hostname)
|
||||
if not wlan.isconnected():
|
||||
print('connecting to network ' + essid + '...')
|
||||
wlan.connect(essid, password)
|
||||
# connect() appears to be async - waiting for it to complete
|
||||
while not wlan.isconnected():
|
||||
print('waiting for connection...')
|
||||
utime.sleep(4)
|
||||
print('checking connection...')
|
||||
print('Wifi connect successful, network config: %s' % repr(wlan.ifconfig()))
|
||||
else:
|
||||
# Note that connection info is stored in non-volatile memory. If
|
||||
# you are connected to the wrong network, do an explicity disconnect()
|
||||
# and then reconnect.
|
||||
print('Wifi already connected, network config: %s' % repr(wlan.ifconfig()))
|
||||
|
||||
def wifi_disconnect():
|
||||
# Disconnect from the current network. You may have to
|
||||
# do this explicitly if you switch networks, as the params are stored
|
||||
# in non-volatile memory.
|
||||
wlan = network.WLAN(network.STA_IF)
|
||||
if wlan.isconnected():
|
||||
print("Disconnecting...")
|
||||
wlan.disconnect()
|
||||
else:
|
||||
print("Wifi not connected.")
|
||||
|
||||
def disable_wifi_ap():
|
||||
# Disable the built-in access point.
|
||||
wlan = network.WLAN(network.AP_IF)
|
||||
wlan.active(False)
|
||||
print('Disabled access point, network status is %s' %
|
||||
wlan.status())
|
||||
Reference in New Issue
Block a user