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