mirror of
https://github.com/KevinMidboe/hivemonitor-esp32-firmware.git
synced 2025-10-29 09:30:26 +00:00
Sender for reading sensor outputs and broadcasting to gateway using ESPNOW
This commit is contained in:
199
src/sender.py
Normal file
199
src/sender.py
Normal file
@@ -0,0 +1,199 @@
|
||||
from network import WLAN, STA_IF
|
||||
from ubinascii import hexlify, unhexlify
|
||||
from machine import Pin, reset
|
||||
from esp32 import NVS
|
||||
import dht, onewire, ds18x20
|
||||
import espnow
|
||||
import time, utime
|
||||
|
||||
from configuration_server import serveSetupServer, getStorageVar
|
||||
|
||||
|
||||
# peer = b'\x0c\xdc\x7e\x3c\x1b\xf0' # MAC address of peer's wifi interface
|
||||
# peer = b'\xe0\x5a\x1b\x0c\xc6\x1c' # MAC address of peer's wifi interface
|
||||
REBOOT_DELAY = 2
|
||||
|
||||
class ESPNowClient():
|
||||
def __init__(self):
|
||||
self.mac = hexlify(WLAN().config('mac'),':').decode()
|
||||
self.peer = None
|
||||
self.sta = None
|
||||
self.client = None
|
||||
|
||||
def enablePowerSavings(self):
|
||||
self.sta.config(pm=self.sta.PM_NONE)
|
||||
|
||||
def setup(self):
|
||||
self.sta = WLAN(STA_IF)
|
||||
self.client = espnow.ESPNow()
|
||||
self.sta.active(True)
|
||||
self.client.active(True)
|
||||
|
||||
self.enablePowerSavings()
|
||||
|
||||
def addPeer(self, peer):
|
||||
print('adding peer: {}'.format(peer))
|
||||
peer = unhexlify(peer.replace(':', ''))
|
||||
peer = peer.replace(b'Z', b'\x5a')
|
||||
|
||||
self.client.add_peer(peer) # Must add_peer() before send()
|
||||
self.peer = peer
|
||||
|
||||
print('peer added')
|
||||
|
||||
def transmitForAcknowledgement(self):
|
||||
# self.sta.active(True)
|
||||
ack = self.client.send(self.peer, b'ack {}'.format(self.mac), True)
|
||||
return ack
|
||||
|
||||
def transmitEnd(self):
|
||||
self.client.send(self.peer, b'end')
|
||||
# self.sta.active(False)
|
||||
|
||||
def send(self, msg):
|
||||
ack = self.transmitForAcknowledgement()
|
||||
if ack is True:
|
||||
if type(msg) is not list:
|
||||
msg = [msg]
|
||||
|
||||
for m in msg:
|
||||
self.client.send(self.peer, str(m))
|
||||
|
||||
self.transmitEnd()
|
||||
else:
|
||||
print('No ack from gateway')
|
||||
|
||||
|
||||
class BaseSensor():
|
||||
def __init__(self, pin, hiveName, interval):
|
||||
self.pin = pin
|
||||
self.hiveName = hiveName
|
||||
self.interval = interval
|
||||
|
||||
@property
|
||||
def info(self):
|
||||
data = {
|
||||
'hive_name': self.hiveName,
|
||||
'temperature': "{0:.2f}".format(self.temp),
|
||||
}
|
||||
|
||||
if hasattr(self, 'humidity'):
|
||||
data['humidity'] = "{0:.2f}".format(self.humidity)
|
||||
|
||||
if hasattr(self, 'pressure'):
|
||||
data['pressure'] = "{0:.2f}".format(self.pressure)
|
||||
|
||||
return data
|
||||
|
||||
|
||||
class DHT11Sensor(BaseSensor):
|
||||
def __init__(self, pin, hiveName, interval):
|
||||
super().__init__(pin, hiveName, interval)
|
||||
self.temperature = 0
|
||||
self.sensor = dht.DHT11(Pin(self.pin))
|
||||
self.lastMeasurement = 0
|
||||
|
||||
def refreshMeasurement(self):
|
||||
now = time.time()
|
||||
if now - self.lastMeasurement > self.interval:
|
||||
self.sensor.measure()
|
||||
self.lastMeasurement = now
|
||||
|
||||
@property
|
||||
def temp(self):
|
||||
self.refreshMeasurement()
|
||||
|
||||
try:
|
||||
self.temperature = self.sensor.temperature() or self.temperature
|
||||
return self.temperature
|
||||
except RuntimeError as error:
|
||||
telemetry = {
|
||||
'hive_name': self.hiveName,
|
||||
'error': str(error),
|
||||
'exception': error.__class__.__name__,
|
||||
'temperature': self.temperature
|
||||
}
|
||||
print('DHT sensor got invalid checksum, returning last value.')
|
||||
print(telemetry)
|
||||
return self.temperature
|
||||
|
||||
@property
|
||||
def humidity(self):
|
||||
self.refreshMeasurement()
|
||||
return self.sensor.humidity()
|
||||
|
||||
|
||||
class DS28B20Sensor(BaseSensor):
|
||||
def __init__(self, pin, hiveName, interval):
|
||||
super().__init__(pin, hiveName, interval)
|
||||
self.temperature = 0
|
||||
self.lastMeasurement = 0
|
||||
|
||||
wire = onewire.OneWire(Pin(self.pin))
|
||||
self.ds = ds18x20.DS18X20(wire)
|
||||
self.sensor = self.ds.scan()[0]
|
||||
|
||||
def refreshMeasurement(self):
|
||||
now = time.time()
|
||||
if now - self.lastMeasurement > self.interval:
|
||||
self.ds.convert_temp()
|
||||
self.lastMeasurement = now
|
||||
|
||||
@property
|
||||
def temp(self):
|
||||
self.refreshMeasurement()
|
||||
temp = self.ds.read_temp(self.sensor)
|
||||
if temp == 85:
|
||||
temp = self.temperature
|
||||
|
||||
self.temperature = temp
|
||||
return temp
|
||||
|
||||
|
||||
def reboot(delay = REBOOT_DELAY):
|
||||
print (f'Rebooting device in {delay} seconds (Ctrl-C to escape).')
|
||||
utime.sleep(delay)
|
||||
reset()
|
||||
|
||||
|
||||
def setupAndTransmitTelemetry():
|
||||
espNowClient = ESPNowClient()
|
||||
espNowClient.setup()
|
||||
espNowClient.addPeer(getStorageVar('peer'))
|
||||
# net.addPeer(b'\xe0\x5a\x1b\x0c\xc6\x1c')
|
||||
|
||||
dht11Pin = int(getStorageVar('dht11_pin'))
|
||||
hive1 = DHT11Sensor(dht11Pin, 'Christine', 1)
|
||||
hive1.refreshMeasurement()
|
||||
|
||||
DS28B20Pin = int(getStorageVar('ds28b20_pin'))
|
||||
hive2 = DS28B20Sensor(DS28B20Pin, 'Elisabeth', 0.75)
|
||||
hive2.refreshMeasurement()
|
||||
|
||||
while True:
|
||||
messages = [hive1.info, hive2.info]
|
||||
espNowClient.send(messages)
|
||||
|
||||
time.sleep_ms(2 * 1000)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
modeSwitch = Pin(26, Pin.IN, Pin.PULL_UP)
|
||||
mode = modeSwitch.value()
|
||||
print(mode)
|
||||
|
||||
try:
|
||||
if mode == 1:
|
||||
print('setupAndTransmitTelemetry')
|
||||
setupAndTransmitTelemetry()
|
||||
else:
|
||||
print('serveSetupServer')
|
||||
serveSetupServer()
|
||||
|
||||
except KeyboardInterrupt as err:
|
||||
raise err # use Ctrl-C to exit to micropython repl
|
||||
except Exception as err:
|
||||
# all other exceptions cause a reboot
|
||||
print ('Error during execution:', err)
|
||||
# raise
|
||||
reboot()
|
||||
87
src/setup/sender.html
Normal file
87
src/setup/sender.html
Normal file
@@ -0,0 +1,87 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width,initial-scale=1,maximum-scale=1"
|
||||
/>
|
||||
<title></title>
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="./styles.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>Hive setup page</h1>
|
||||
|
||||
<section>
|
||||
<p>Configure your microcontroller with the input fields below.</p>
|
||||
|
||||
<button class="light" id="device-info">View device info</button>
|
||||
<button class="light" id="identify">Identify</button>
|
||||
|
||||
<form action="/save" method="POST">
|
||||
<h3>General</h3>
|
||||
|
||||
<div>
|
||||
<label for="name">Hive name</label>
|
||||
<input id="name" name="name" enterkeyhint="send" value="{{ name }}" />
|
||||
</div>
|
||||
|
||||
<h3>Connectivity</h3>
|
||||
<div>
|
||||
<label for="peer">Peer MAC</label>
|
||||
<input
|
||||
id="mapeerc"
|
||||
name="peer"
|
||||
enterkeyhint="send"
|
||||
value="{{ peer }}"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<h3>Sensors</h3>
|
||||
<div>
|
||||
<label for="dht11_pin">DHT11 pin</label>
|
||||
<input
|
||||
id="dht11_pin"
|
||||
name="dht11_pin"
|
||||
enterkeyhint="send"
|
||||
type="number"
|
||||
value="{{ dht11_pin }}"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="ds28b20_pin">DS28B20 pin</label>
|
||||
<input
|
||||
id="ds28b20_pin"
|
||||
name="ds28b20_pin"
|
||||
enterkeyhint="send"
|
||||
type="number"
|
||||
value="{{ ds28b20_pin }}"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<button type="submit">Save</button>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<dialog id="dialog">
|
||||
<form>
|
||||
<p><b>MAC address: </b><span>{{ mac }}</span></p>
|
||||
<p><b>CPU freq: </b><span>{{ freq }} Mhz</span></p>
|
||||
|
||||
<button value="cancel" formmethod="dialog">Close</button>
|
||||
</form>
|
||||
</dialog>
|
||||
</body>
|
||||
|
||||
<script>
|
||||
const deviceBtn = document.getElementById('device-info');
|
||||
const identifyBtn = document.getElementById('identify');
|
||||
const dialog = document.getElementById("dialog");
|
||||
|
||||
deviceBtn.addEventListener('click', () => dialog.showModal());
|
||||
identifyBtn.addEventListener('click', () => fetch('identify', { method: 'POST' }));
|
||||
</script>
|
||||
</html>
|
||||
Reference in New Issue
Block a user