Skip to content

Instantly share code, notes, and snippets.

@traverseda
Last active May 28, 2022 19:33
Show Gist options
  • Save traverseda/bc3542d7de6fa1b2d8a47c5825cf0f0c to your computer and use it in GitHub Desktop.
Save traverseda/bc3542d7de6fa1b2d8a47c5825cf0f0c to your computer and use it in GitHub Desktop.
My customizations for the stem-minds eduponics mini

image

This is my customized eduponics mini firmware, it might not be compatible with the official app.

The main change I made is to use asyncio for the main loop. I did this so that I can more easily turn on water for just a few seconds.

The other main change is instead of generating a unique UUID I use the device's unique MAC address. Every device as a unique MAC address at creation, so this will still be globally unique but will be a lot less to type in.

I've also included a node-red control panel. I use the aedes broker you can install directly into node-red.

image

[
{
"id": "e93d07e3f8f4822c",
"type": "tab",
"label": "Plant display",
"disabled": false,
"info": "",
"env": []
},
{
"id": "32c7220ebb992e9b",
"type": "mqtt in",
"z": "e93d07e3f8f4822c",
"name": "",
"topic": "24:6f:28:44:cc:9c/plants/environment",
"qos": "2",
"datatype": "json",
"broker": "6c71a0de59e0bc04",
"nl": false,
"rap": true,
"rh": 0,
"inputs": 0,
"x": 280,
"y": 80,
"wires": [
[
"ff1813a155151191",
"811cfee4fcc0d266",
"f558e0e57f219170",
"92fdf2692e0e7611"
]
]
},
{
"id": "4685e7481caac262",
"type": "ui_chart",
"z": "e93d07e3f8f4822c",
"name": "",
"group": "3e90c18ba5ed5691",
"order": 1,
"width": 0,
"height": 0,
"label": "Temperature",
"chartType": "line",
"legend": "false",
"xformat": "HH:mm:ss",
"interpolate": "bezier",
"nodata": "",
"dot": false,
"ymin": "0",
"ymax": "",
"removeOlder": 1,
"removeOlderPoints": "",
"removeOlderUnit": "3600",
"cutout": 0,
"useOneColor": false,
"useUTC": false,
"colors": [
"#1f77b4",
"#aec7e8",
"#ff7f0e",
"#2ca02c",
"#98df8a",
"#d62728",
"#ff9896",
"#9467bd",
"#c5b0d5"
],
"outputs": 1,
"useDifferentColor": false,
"className": "",
"x": 750,
"y": 80,
"wires": [
[]
]
},
{
"id": "ff1813a155151191",
"type": "change",
"z": "e93d07e3f8f4822c",
"name": "",
"rules": [
{
"t": "set",
"p": "payload",
"pt": "msg",
"to": "payload.temp",
"tot": "msg"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 540,
"y": 80,
"wires": [
[
"4685e7481caac262",
"093ddff722115f6a"
]
]
},
{
"id": "65ececa8f8504d11",
"type": "ui_chart",
"z": "e93d07e3f8f4822c",
"name": "",
"group": "3e90c18ba5ed5691",
"order": 2,
"width": 0,
"height": 0,
"label": "Humidity",
"chartType": "line",
"legend": "false",
"xformat": "HH:mm:ss",
"interpolate": "bezier",
"nodata": "",
"dot": false,
"ymin": "0",
"ymax": "100",
"removeOlder": 1,
"removeOlderPoints": "",
"removeOlderUnit": "3600",
"cutout": 0,
"useOneColor": false,
"useUTC": false,
"colors": [
"#1f77b4",
"#aec7e8",
"#ff7f0e",
"#2ca02c",
"#98df8a",
"#d62728",
"#ff9896",
"#9467bd",
"#c5b0d5"
],
"outputs": 1,
"useDifferentColor": false,
"className": "",
"x": 760,
"y": 260,
"wires": [
[]
]
},
{
"id": "811cfee4fcc0d266",
"type": "change",
"z": "e93d07e3f8f4822c",
"name": "",
"rules": [
{
"t": "set",
"p": "payload",
"pt": "msg",
"to": "payload.humidity",
"tot": "msg"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 540,
"y": 260,
"wires": [
[
"65ececa8f8504d11",
"3dee2ee40ec39973"
]
]
},
{
"id": "ed3fd1b670587441",
"type": "ui_led",
"z": "e93d07e3f8f4822c",
"order": 6,
"group": "3e90c18ba5ed5691",
"width": "3",
"height": "1",
"label": "Reservoir Water",
"labelPlacement": "left",
"labelAlignment": "left",
"colorForValue": [
{
"color": "#ff0000",
"value": "1",
"valueType": "num"
},
{
"color": "#008000",
"value": "0",
"valueType": "num"
}
],
"allowColorForValueInMessage": false,
"shape": "circle",
"showGlow": true,
"name": "",
"x": 750,
"y": 380,
"wires": []
},
{
"id": "f558e0e57f219170",
"type": "change",
"z": "e93d07e3f8f4822c",
"name": "",
"rules": [
{
"t": "set",
"p": "payload",
"pt": "msg",
"to": "payload.water_quantity",
"tot": "msg"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 540,
"y": 380,
"wires": [
[
"ed3fd1b670587441"
]
]
},
{
"id": "becc4251c9462313",
"type": "ui_chart",
"z": "e93d07e3f8f4822c",
"name": "",
"group": "3e90c18ba5ed5691",
"order": 3,
"width": 0,
"height": 0,
"label": "Soil Moisture",
"chartType": "line",
"legend": "false",
"xformat": "HH:mm:ss",
"interpolate": "bezier",
"nodata": "",
"dot": false,
"ymin": "0",
"ymax": "100",
"removeOlder": 1,
"removeOlderPoints": "",
"removeOlderUnit": "3600",
"cutout": 0,
"useOneColor": false,
"useUTC": false,
"colors": [
"#1f77b4",
"#aec7e8",
"#ff7f0e",
"#2ca02c",
"#98df8a",
"#d62728",
"#ff9896",
"#9467bd",
"#c5b0d5"
],
"outputs": 1,
"useDifferentColor": false,
"className": "",
"x": 770,
"y": 560,
"wires": [
[]
]
},
{
"id": "8dac352f7abdee13",
"type": "change",
"z": "e93d07e3f8f4822c",
"name": "",
"rules": [
{
"t": "set",
"p": "payload",
"pt": "msg",
"to": "payload.moisture",
"tot": "msg"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 540,
"y": 560,
"wires": [
[
"becc4251c9462313",
"61a643561f6e6d7b"
]
]
},
{
"id": "e3e174d93b82c968",
"type": "mqtt in",
"z": "e93d07e3f8f4822c",
"name": "",
"topic": "24:6f:28:44:cc:9c/plants/soil",
"qos": "2",
"datatype": "json",
"broker": "6c71a0de59e0bc04",
"nl": false,
"rap": true,
"rh": 0,
"inputs": 0,
"x": 300,
"y": 560,
"wires": [
[
"8dac352f7abdee13"
]
]
},
{
"id": "d480d89acb23621d",
"type": "ui_button",
"z": "e93d07e3f8f4822c",
"name": "",
"group": "3e90c18ba5ed5691",
"order": 5,
"width": "3",
"height": "1",
"passthru": false,
"label": "Water",
"tooltip": "",
"color": "",
"bgcolor": "",
"className": "",
"icon": "",
"payload": "planter1_water_time",
"payloadType": "global",
"topic": "topic",
"topicType": "msg",
"x": 550,
"y": 720,
"wires": [
[
"d106f4a7f82e665a"
]
]
},
{
"id": "c243c5837f72b374",
"type": "mqtt out",
"z": "e93d07e3f8f4822c",
"name": "",
"topic": "24:6f:28:44:cc:9c/plants/water",
"qos": "",
"retain": "",
"respTopic": "",
"contentType": "",
"userProps": "",
"correl": "",
"expiry": "",
"broker": "6c71a0de59e0bc04",
"x": 970,
"y": 720,
"wires": []
},
{
"id": "cc465904ddba90fc",
"type": "ui_numeric",
"z": "e93d07e3f8f4822c",
"name": "",
"label": "",
"tooltip": "",
"group": "3e90c18ba5ed5691",
"order": 4,
"width": "3",
"height": "1",
"wrap": false,
"passthru": true,
"topic": "topic",
"topicType": "msg",
"format": "{{value}} Seconds",
"min": 0,
"max": "60",
"step": 1,
"className": "",
"x": 260,
"y": 800,
"wires": [
[
"10f8ba74b6ee1caf"
]
]
},
{
"id": "92fdf2692e0e7611",
"type": "countdown",
"z": "e93d07e3f8f4822c",
"name": "",
"topic": "",
"payloadTimerStart": "true",
"payloadTimerStartType": "bool",
"payloadTimerStop": "false",
"payloadTimerStopType": "bool",
"timer": "10",
"resetWhileRunning": true,
"setTimeToNewWhileRunning": true,
"startCountdownOnControlMessage": false,
"x": 530,
"y": 440,
"wires": [
[
"b68fa5e5a9163ec6"
],
[]
]
},
{
"id": "b68fa5e5a9163ec6",
"type": "ui_led",
"z": "e93d07e3f8f4822c",
"order": 6,
"group": "3e90c18ba5ed5691",
"width": "3",
"height": "1",
"label": "Connected",
"labelPlacement": "left",
"labelAlignment": "left",
"colorForValue": [
{
"color": "#ff0000",
"value": "false",
"valueType": "bool"
},
{
"color": "#008000",
"value": "true",
"valueType": "bool"
}
],
"allowColorForValueInMessage": false,
"shape": "circle",
"showGlow": true,
"name": "",
"x": 750,
"y": 440,
"wires": []
},
{
"id": "d106f4a7f82e665a",
"type": "template",
"z": "e93d07e3f8f4822c",
"name": "",
"field": "payload",
"fieldType": "msg",
"format": "handlebars",
"syntax": "mustache",
"template": "{\"time\":{{global.planter1_water_time}}}",
"output": "str",
"x": 720,
"y": 720,
"wires": [
[
"c243c5837f72b374"
]
]
},
{
"id": "10f8ba74b6ee1caf",
"type": "change",
"z": "e93d07e3f8f4822c",
"name": "",
"rules": [
{
"t": "set",
"p": "planter1_water_time",
"pt": "global",
"to": "payload",
"tot": "msg"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 470,
"y": 800,
"wires": [
[]
]
},
{
"id": "5339d39b6f7b220c",
"type": "ui_chart",
"z": "e93d07e3f8f4822c",
"name": "",
"group": "458af0d74e442447",
"order": 1,
"width": 0,
"height": 0,
"label": "Temperature",
"chartType": "line",
"legend": "false",
"xformat": "HH:mm:ss",
"interpolate": "bezier",
"nodata": "",
"dot": false,
"ymin": "0",
"ymax": "",
"removeOlder": "3000",
"removeOlderPoints": "",
"removeOlderUnit": "604800",
"cutout": 0,
"useOneColor": false,
"useUTC": false,
"colors": [
"#1f77b4",
"#aec7e8",
"#ff7f0e",
"#2ca02c",
"#98df8a",
"#d62728",
"#ff9896",
"#9467bd",
"#c5b0d5"
],
"outputs": 1,
"useDifferentColor": false,
"className": "",
"x": 950,
"y": 120,
"wires": [
[
"9c28897ab8b72a19"
]
]
},
{
"id": "b63ca732e05d8c54",
"type": "ui_chart",
"z": "e93d07e3f8f4822c",
"name": "",
"group": "458af0d74e442447",
"order": 2,
"width": 0,
"height": 0,
"label": "Humidity",
"chartType": "line",
"legend": "false",
"xformat": "dd HH:mm",
"interpolate": "bezier",
"nodata": "",
"dot": false,
"ymin": "0",
"ymax": "100",
"removeOlder": "3000",
"removeOlderPoints": "",
"removeOlderUnit": "604800",
"cutout": 0,
"useOneColor": false,
"useUTC": false,
"colors": [
"#1f77b4",
"#aec7e8",
"#ff7f0e",
"#2ca02c",
"#98df8a",
"#d62728",
"#ff9896",
"#9467bd",
"#c5b0d5"
],
"outputs": 1,
"useDifferentColor": false,
"className": "",
"x": 960,
"y": 320,
"wires": [
[
"e71657bc43ef1fd7"
]
]
},
{
"id": "1dc99a85a38a3df2",
"type": "ui_chart",
"z": "e93d07e3f8f4822c",
"name": "",
"group": "458af0d74e442447",
"order": 3,
"width": 0,
"height": 0,
"label": "Soil Moisture",
"chartType": "line",
"legend": "false",
"xformat": "HH:mm:ss",
"interpolate": "bezier",
"nodata": "",
"dot": false,
"ymin": "0",
"ymax": "100",
"removeOlder": "3000",
"removeOlderPoints": "",
"removeOlderUnit": "604800",
"cutout": 0,
"useOneColor": false,
"useUTC": false,
"colors": [
"#99c1f1",
"#1c71d8",
"#ff7f0e",
"#2ca02c",
"#98df8a",
"#d62728",
"#ff9896",
"#9467bd",
"#c5b0d5"
],
"outputs": 1,
"useDifferentColor": false,
"className": "",
"x": 970,
"y": 620,
"wires": [
[
"ae620b1014d01950"
]
]
},
{
"id": "e71657bc43ef1fd7",
"type": "persist in",
"z": "e93d07e3f8f4822c",
"name": "humidity",
"storageNode": "3e3c4026ab6b3227",
"x": 1120,
"y": 320,
"wires": []
},
{
"id": "e12d7f60fbf4ade3",
"type": "persist out",
"z": "e93d07e3f8f4822c",
"name": "humidity",
"storageNode": "3e3c4026ab6b3227",
"x": 960,
"y": 260,
"wires": [
[
"b63ca732e05d8c54"
]
]
},
{
"id": "fd95369f9ae32593",
"type": "inject",
"z": "e93d07e3f8f4822c",
"name": "",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 960,
"y": 220,
"wires": [
[
"e12d7f60fbf4ade3"
]
]
},
{
"id": "ae620b1014d01950",
"type": "persist in",
"z": "e93d07e3f8f4822c",
"name": "soil-moisture",
"storageNode": "3e3c4026ab6b3227",
"x": 1149,
"y": 622,
"wires": []
},
{
"id": "dbc1eb5db8302d4c",
"type": "persist out",
"z": "e93d07e3f8f4822c",
"name": "soil-moisture",
"storageNode": "3e3c4026ab6b3227",
"x": 970,
"y": 560,
"wires": [
[
"1dc99a85a38a3df2"
]
]
},
{
"id": "41b326100ece18c0",
"type": "inject",
"z": "e93d07e3f8f4822c",
"name": "",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 960,
"y": 520,
"wires": [
[
"dbc1eb5db8302d4c"
]
]
},
{
"id": "84d453482f622626",
"type": "persist out",
"z": "e93d07e3f8f4822c",
"name": "temperature",
"storageNode": "3e3c4026ab6b3227",
"x": 970,
"y": 60,
"wires": [
[
"5339d39b6f7b220c"
]
]
},
{
"id": "5733ea3c10e358db",
"type": "inject",
"z": "e93d07e3f8f4822c",
"name": "",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 960,
"y": 20,
"wires": [
[
"84d453482f622626"
]
]
},
{
"id": "9c28897ab8b72a19",
"type": "persist in",
"z": "e93d07e3f8f4822c",
"name": "temperature",
"storageNode": "3e3c4026ab6b3227",
"x": 1150,
"y": 120,
"wires": []
},
{
"id": "3dee2ee40ec39973",
"type": "rate-avg",
"z": "e93d07e3f8f4822c",
"name": "",
"windowtype": "time",
"timewindow": "6",
"timeunits": "minutes",
"countwindow": "10",
"round": "1",
"x": 760,
"y": 320,
"wires": [
[
"b63ca732e05d8c54"
]
]
},
{
"id": "093ddff722115f6a",
"type": "rate-avg",
"z": "e93d07e3f8f4822c",
"name": "",
"windowtype": "time",
"timewindow": "6",
"timeunits": "minutes",
"countwindow": "10",
"round": "1",
"x": 760,
"y": 140,
"wires": [
[
"5339d39b6f7b220c"
]
]
},
{
"id": "61a643561f6e6d7b",
"type": "rate-avg",
"z": "e93d07e3f8f4822c",
"name": "",
"windowtype": "time",
"timewindow": "6",
"timeunits": "minutes",
"countwindow": "10",
"round": "1",
"x": 760,
"y": 620,
"wires": [
[
"1dc99a85a38a3df2"
]
]
},
{
"id": "74dbb1bab4874123",
"type": "mqtt in",
"z": "e93d07e3f8f4822c",
"name": "",
"topic": "24:6f:28:44:cc:9c/plants/water",
"qos": "2",
"datatype": "json",
"broker": "6c71a0de59e0bc04",
"nl": false,
"rap": true,
"rh": 0,
"inputs": 0,
"x": 300,
"y": 620,
"wires": [
[
"f0364f4de8363bf3"
]
]
},
{
"id": "f0364f4de8363bf3",
"type": "change",
"z": "e93d07e3f8f4822c",
"name": "",
"rules": [
{
"t": "set",
"p": "payload",
"pt": "msg",
"to": "payload.status = \"pending\" ? 100 : 0 ",
"tot": "jsonata"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 540,
"y": 620,
"wires": [
[
"becc4251c9462313",
"61a643561f6e6d7b",
"ffde034e27195fcf"
]
]
},
{
"id": "a201c1058fd6c9b0",
"type": "inject",
"z": "e93d07e3f8f4822c",
"name": "",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"v": "false",
"vt": "bool"
}
],
"repeat": "",
"crontab": "",
"once": true,
"onceDelay": 0.1,
"topic": "",
"payload": "",
"payloadType": "date",
"x": 530,
"y": 500,
"wires": [
[
"b68fa5e5a9163ec6"
]
]
},
{
"id": "ffde034e27195fcf",
"type": "ui_led",
"z": "e93d07e3f8f4822c",
"order": 7,
"group": "3e90c18ba5ed5691",
"width": 0,
"height": 0,
"label": "Pump on",
"labelPlacement": "left",
"labelAlignment": "left",
"colorForValue": [
{
"color": "#63452c",
"value": "0",
"valueType": "num"
},
{
"color": "#008000",
"value": "100",
"valueType": "num"
}
],
"allowColorForValueInMessage": false,
"shape": "circle",
"showGlow": true,
"name": "",
"x": 750,
"y": 660,
"wires": []
},
{
"id": "6c71a0de59e0bc04",
"type": "mqtt-broker",
"name": "built-in broker",
"broker": "localhost",
"port": "1883",
"clientid": "",
"autoConnect": true,
"usetls": false,
"protocolVersion": "4",
"keepalive": "60",
"cleansession": true,
"birthTopic": "",
"birthQos": "0",
"birthPayload": "",
"birthMsg": {},
"closeTopic": "",
"closeQos": "0",
"closePayload": "",
"closeMsg": {},
"willTopic": "",
"willQos": "0",
"willPayload": "",
"willMsg": {},
"sessionExpiry": ""
},
{
"id": "3e90c18ba5ed5691",
"type": "ui_group",
"name": "Planter 1",
"tab": "ac65656392cf4fca",
"order": 1,
"disp": true,
"width": "6",
"collapse": false,
"className": ""
},
{
"id": "458af0d74e442447",
"type": "ui_group",
"name": "Planter 1 historical",
"tab": "ac65656392cf4fca",
"order": 2,
"disp": true,
"width": "6",
"collapse": false,
"className": ""
},
{
"id": "3e3c4026ab6b3227",
"type": "persist-store",
"filename": "planter-1-data.json",
"interval": "60"
},
{
"id": "ac65656392cf4fca",
"type": "ui_tab",
"name": "Plants",
"icon": "fa-leaf",
"order": 1,
"disabled": false,
"hidden": false
}
]
from Eduponics import umqttsimple,bh1750,bme280
import machine
import time
import json
import uasyncio
import network, ubinascii
import ntptime
try:
ntptime.settime()
except:
print("Couldn't set time from time server")
# set adc (analog to digital) on pin 35
adc = machine.ADC(machine.Pin(35))
# set 11dB input attenuation (voltage range roughly 0.0v - 3.6v)
adc.atten(machine.ADC.ATTN_11DB)
# Configure light sensor
light_sensor = bh1750.BH1750()
# Configure BME280
# setup I2C connection
i2c = machine.I2C(scl=machine.Pin(15), sda=machine.Pin(4))
# Initialize BME280 object with default address 0x76
bme_sensor = bme280.BME280(i2c=i2c)
# initialize dht object, DHT11 coonected to IO19
#d = dht.DHT11(machine.Pin(19))
# define water level sensor as INPUT on IO pin number 21
water_level = machine.Pin(21, machine.Pin.IN)
water_task=None
# define pump on pin IO23 as OUTPUT, define pump state
pump = machine.Pin(23, machine.Pin.OUT)
pump_state = False
# MQTT Unique ID
UUID = ubinascii.hexlify(network.WLAN().config('mac'),':').decode() #"planter_1"
# MQTT Topics
topics = ["plants/soil","plants/environment","plants/water",]
def handle_water_level(pin):
global pump_state
global water_task
# water level triggered, turn off the pump
# wait for 0.3 seconds to make sure it's just a little below the water sensor
# else the pump might become unstable
time.sleep(0.3)
pump.value(0)
pump_state = False
if water_task: water_task.cancel()
client.publish("%s/plants/water" % UUID, str({"status":"no_water"}).replace("'",'"'))
def get_soil_moisture():
# sensor min and max values
# can be changed and callibrated
minVal = 500
maxVal = 4095
# read soil moisture sensor data
val = adc.read()
# scale the value based on maxVal and minVal
scale = 100 / (minVal - maxVal)
# get calculated scale
normal_reading = int((val - maxVal) * scale)
# we can also get inverted value if needed
inverted_reading = ("%s%s" % (int((minVal - val) * scale),"%"))
# for this example we'll return only the normal reading
# put everything in a JSON format suitable for the eduponics app
plant = {
"id":0,
"enabled":1,
"moisture":normal_reading,
"raw_moisture": val
}
# return the data
return str(plant).replace("'",'"')
def get_environmental_data():
# get light from the light sensor
lux = int(light_sensor.readLight())
# get bme280 sensor data
bme280_values = bme_sensor.values
temperature = bme280_values[0].replace("C","")
pressure = bme280_values[1]
humidity = bme280_values[2].replace("%","")
# get DHT11 sensor data
# measure sensor data
#d.measure()
# get temperature and humidity
#temperature = d.temperature()
#humidity = d.humidity()
# get water quantity
water_quantity = water_level.value()
# put all this data into a JSON object
data = {
"temp":temperature,
"humidity":humidity,
"sunlight":lux,
"water_quantity":water_quantity
}
return str(data).replace("'",'"')
async def water_plant(seconds):
print("Watering plants")
global pump_state
if water_level.value() == 1:
# turn off the pump
pump.value(0)
pump_state = False
else:
# turn on the pump
pump.value(1)
pump_state = True
client.publish("%s/plants/water" % UUID, str({"status":"pending","remaining":seconds}).replace("'",'"'))
for i in range(int(seconds)):
await uasyncio.sleep(1)
pump.value(0)
pump_state = False
client.publish("%s/plants/water" % UUID, str({"status":"ok","lastrun":time.time()}).replace("'",'"'))
return True
def on_message_callback(topic, msg):
'''
this is a callback, will be called when the app asks for certain information
such as to water the plants when the watering button pressed
'''
global water_task
# convert topic and message byte to string
topic = str(topic, 'utf-8')
try:
msg = json.loads(str(msg, 'utf-8'))
except:
print("Couldn't parse",msg,"at",topic)
return
print(topic,msg)
if(topic == "%s/plants/soil" % UUID or topic == "%s/plants/environment" % UUID):
# Do nothing, we only publish to those topics
pass
elif(topic == "%s/plants/water" % UUID):
# when the app request for plant watering it goes here
if("time" in msg):
# valid request, let's process it
print("starting plant water")
#Cancal old task.
if water_task: water_task.cancel()
# it's waiting for us to water it, let's water it
water_task = uasyncio.create_task(water_plant(msg["time"]))
else:
print((topic, msg))
def connect_and_subscribe():
print("[-] Connecting to MQTT client ...")
# set the MQTT broker object
client = umqttsimple.MQTTClient(server="192.168.2.173",ssl=False)
# set a callback for incoming messages (subscribed topics)
client.set_callback(on_message_callback)
# connect to the broker
client.connect()
# subscribe to the topics
for topic in topics:
client.subscribe("%s/%s" % (UUID,topic))
print("[-] Subscribed to %s successfully" % topic)
print("[-] Connected to %s MQTT broker successfully" % client.server)
return client
def restart_and_reconnect():
# something went wrong, reconnect in 5 seconds ...
print('[-] Failed to connect to MQTT broker. Reconnecting...')
time.sleep(5)
machine.reset()
try:
client = connect_and_subscribe()
except OSError as e:
restart_and_reconnect()
from boot import station
import os
def system_info():
data = {
"uptime":time.ticks_ms(),
"signal_strength":station.status('rssi'),
"freq": machine.freq(),
"time": time.time(),
"mem_free": gc.mem_free(),
"mem_used": gc.mem_alloc(),
}
return str(data).replace("'",'"')
async def main():
# configure few variables
last_message = 0
message_interval = 5
# set callback on the water level sensor, if no water stop the pump
water_level.irq(trigger=machine.Pin.IRQ_RISING, handler=handle_water_level)
while True:
try:
# check if there are new messages pending to be processed
# if there are, redirect them to callback on_message_callback()
client.check_msg()
# check if the last published data wasn't less than message_interval
if (time.time() - last_message) > message_interval:
# get soil moisture
soil_moisture = get_soil_moisture()
# publish soil moisture data
client.publish("%s/plants/soil" % UUID, soil_moisture)
#print("[-] published soil moisture")
# update environmetal data
env = get_environmental_data()
client.publish("%s/plants/environment" % UUID, env)
client.publish("%s/plants/system" % UUID, system_info())
#print("[-] published evironmental data")
# update last message timestamp
last_message = time.time()
except OSError as e:
# if something goes wrong, reconnct to MQTT server
restart_and_reconnect()
machine.idle()
await uasyncio.sleep(0.5)
uasyncio.run(main())
restart_and_reconnect()
@Zontex
Copy link

Zontex commented May 28, 2022

Love it. I will look perhaps to modify Eduponics APP and make it compatible if it doesn't work out-of-the-box already.

@traverseda
Copy link
Author

Mostly the difference is that you need to send a message like {"time": 5} to {UUID}/plants/water in order to get it to run the pump for 5 seconds, or 10 for ten seconds, etc. Changing water/ to "{"status": "Pending"}" will do nothing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment