v3.0 first pcb
This commit is contained in:
parent
229f7cda10
commit
214db0cc67
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"board": {
|
"board": {
|
||||||
"active_layer": 31,
|
"active_layer": 36,
|
||||||
"active_layer_preset": "",
|
"active_layer_preset": "",
|
||||||
"auto_track_width": false,
|
"auto_track_width": false,
|
||||||
"hidden_netclasses": [],
|
"hidden_netclasses": [],
|
||||||
@ -8,7 +8,7 @@
|
|||||||
"high_contrast_mode": 0,
|
"high_contrast_mode": 0,
|
||||||
"net_color_mode": 1,
|
"net_color_mode": 1,
|
||||||
"opacity": {
|
"opacity": {
|
||||||
"images": 0.6,
|
"images": 0.4399999976158142,
|
||||||
"pads": 1.0,
|
"pads": 1.0,
|
||||||
"tracks": 1.0,
|
"tracks": 1.0,
|
||||||
"vias": 1.0,
|
"vias": 1.0,
|
||||||
@ -68,7 +68,7 @@
|
|||||||
39,
|
39,
|
||||||
40
|
40
|
||||||
],
|
],
|
||||||
"visible_layers": "ffcc02f_ffffffff",
|
"visible_layers": "ffc7055_fffffff8",
|
||||||
"zone_display_mode": 1
|
"zone_display_mode": 1
|
||||||
},
|
},
|
||||||
"git": {
|
"git": {
|
||||||
|
@ -59,7 +59,23 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"drc_exclusions": [
|
"drc_exclusions": [
|
||||||
"courtyards_overlap|199080001|128865001|67ac55df-4a92-42f8-90ae-0f317d1fcee5|cfde0667-9234-45fc-8eda-14a18003cf8d"
|
"footprint_symbol_mismatch|177050000|59025000|a624af3d-bffa-4ff7-9554-e16d3c677f69|00000000-0000-0000-0000-000000000000",
|
||||||
|
"footprint_symbol_mismatch|237580000|53970000|c9d8d35b-26b7-4992-9d25-be9130d57b1a|00000000-0000-0000-0000-000000000000",
|
||||||
|
"footprint_symbol_mismatch|256580000|49370000|b33af7ef-63da-4a51-8d8a-183cadd974de|00000000-0000-0000-0000-000000000000",
|
||||||
|
"net_conflict|177050000|59025000|09cad967-1882-4dd3-8900-445282e228e5|00000000-0000-0000-0000-000000000000",
|
||||||
|
"net_conflict|177050000|59025000|20ab85c0-b3f3-4826-a86d-065fee01e11f|00000000-0000-0000-0000-000000000000",
|
||||||
|
"net_conflict|177050000|59025000|3da9717d-9800-42f9-97d1-56d23bf085aa|00000000-0000-0000-0000-000000000000",
|
||||||
|
"net_conflict|177050000|59025000|444aab2b-3a9b-444e-b60c-b5b5ff830942|00000000-0000-0000-0000-000000000000",
|
||||||
|
"net_conflict|177050000|59025000|6b067fd3-d374-4937-8779-958994d9163b|00000000-0000-0000-0000-000000000000",
|
||||||
|
"net_conflict|177050000|59025000|9839c562-7672-4ea8-a74d-bea83ae26677|00000000-0000-0000-0000-000000000000",
|
||||||
|
"net_conflict|177050000|59025000|9ce2df19-edf4-40d2-8e85-8c33008b8df0|00000000-0000-0000-0000-000000000000",
|
||||||
|
"net_conflict|177050000|59025000|a8ab716a-cd1e-4842-ad8e-3d6d1db9770b|00000000-0000-0000-0000-000000000000",
|
||||||
|
"net_conflict|177050000|59025000|aaf09ae3-4ace-49d7-a050-44cb4c93d63b|00000000-0000-0000-0000-000000000000",
|
||||||
|
"net_conflict|177050000|59025000|af55e8a2-ba8d-462e-807f-99ca5906f801|00000000-0000-0000-0000-000000000000",
|
||||||
|
"net_conflict|177050000|59025000|c36efd78-869f-40e7-86fc-97e5ed683fec|00000000-0000-0000-0000-000000000000",
|
||||||
|
"net_conflict|177050000|59025000|d668fda0-e4be-4e1f-95b8-8cd59a67cb21|00000000-0000-0000-0000-000000000000",
|
||||||
|
"net_conflict|177050000|59025000|d99401c6-2b75-46f7-8616-cdd7755709ee|00000000-0000-0000-0000-000000000000",
|
||||||
|
"net_conflict|177050000|59025000|f1fd5816-e8bd-4ba6-9d53-54b58d25e2dc|00000000-0000-0000-0000-000000000000"
|
||||||
],
|
],
|
||||||
"meta": {
|
"meta": {
|
||||||
"filename": "board_design_settings.json",
|
"filename": "board_design_settings.json",
|
||||||
@ -98,7 +114,7 @@
|
|||||||
"padstack": "warning",
|
"padstack": "warning",
|
||||||
"pth_inside_courtyard": "ignore",
|
"pth_inside_courtyard": "ignore",
|
||||||
"shorting_items": "error",
|
"shorting_items": "error",
|
||||||
"silk_edge_clearance": "warning",
|
"silk_edge_clearance": "ignore",
|
||||||
"silk_over_copper": "ignore",
|
"silk_over_copper": "ignore",
|
||||||
"silk_overlap": "ignore",
|
"silk_overlap": "ignore",
|
||||||
"skew_out_of_range": "error",
|
"skew_out_of_range": "error",
|
||||||
@ -1131,7 +1147,7 @@
|
|||||||
"group_by": false,
|
"group_by": false,
|
||||||
"label": "LCSC",
|
"label": "LCSC",
|
||||||
"name": "LCSC",
|
"name": "LCSC",
|
||||||
"show": true
|
"show": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"group_by": false,
|
"group_by": false,
|
||||||
@ -1228,7 +1244,7 @@
|
|||||||
"group_symbols": true,
|
"group_symbols": true,
|
||||||
"name": "",
|
"name": "",
|
||||||
"sort_asc": true,
|
"sort_asc": true,
|
||||||
"sort_field": "Reference"
|
"sort_field": "LCSC_PART_NUMBER"
|
||||||
},
|
},
|
||||||
"connection_grid_size": 50.0,
|
"connection_grid_size": 50.0,
|
||||||
"drawing": {
|
"drawing": {
|
||||||
|
File diff suppressed because it is too large
Load Diff
1
board/fabrication-toolkit-options.json
Normal file
1
board/fabrication-toolkit-options.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
{"EXTRA_LAYERS": "", "EXTEND_EDGE_CUT": false, "AUTO TRANSLATE": true, "AUTO FILL": true, "EXCLUDE DNP": true}
|
BIN
board/production/PlantCtrlESP32.zip
Normal file
BIN
board/production/PlantCtrlESP32.zip
Normal file
Binary file not shown.
10
esp32/.gitignore
vendored
10
esp32/.gitignore
vendored
@ -1,10 +0,0 @@
|
|||||||
*.swp
|
|
||||||
.pio
|
|
||||||
.vscode/.browse.c_cpp.db*
|
|
||||||
.vscode/c_cpp_properties.json
|
|
||||||
.vscode/launch.json
|
|
||||||
.vscode/ipch
|
|
||||||
doc/
|
|
||||||
custom_platformio.ini
|
|
||||||
cppcheck-build-dir
|
|
||||||
host/settings.json
|
|
10
esp32/.vscode/extensions.json
vendored
10
esp32/.vscode/extensions.json
vendored
@ -1,10 +0,0 @@
|
|||||||
{
|
|
||||||
// See http://go.microsoft.com/fwlink/?LinkId=827846
|
|
||||||
// for the documentation about the extensions.json format
|
|
||||||
"recommendations": [
|
|
||||||
"platformio.platformio-ide"
|
|
||||||
],
|
|
||||||
"unwantedRecommendations": [
|
|
||||||
"ms-vscode.cpptools-extension-pack"
|
|
||||||
]
|
|
||||||
}
|
|
@ -1,3 +0,0 @@
|
|||||||
cmake_minimum_required(VERSION 3.16.0)
|
|
||||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
|
||||||
project(esp32)
|
|
2522
esp32/Doxyfile
2522
esp32/Doxyfile
File diff suppressed because it is too large
Load Diff
@ -1,31 +0,0 @@
|
|||||||
{
|
|
||||||
"folders": [
|
|
||||||
{
|
|
||||||
"path": "."
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"settings": {
|
|
||||||
"files.associations": {
|
|
||||||
"functional": "cpp",
|
|
||||||
"*.tcc": "cpp",
|
|
||||||
"map": "cpp",
|
|
||||||
"*.cps": "javascript",
|
|
||||||
"bitset": "cpp",
|
|
||||||
"algorithm": "cpp",
|
|
||||||
"istream": "cpp",
|
|
||||||
"limits": "cpp",
|
|
||||||
"streambuf": "cpp",
|
|
||||||
"string": "cpp",
|
|
||||||
"typeinfo": "cpp",
|
|
||||||
"cmath": "cpp",
|
|
||||||
"iterator": "cpp",
|
|
||||||
"array": "cpp",
|
|
||||||
"tuple": "cpp",
|
|
||||||
"utility": "cpp",
|
|
||||||
"fstream": "cpp",
|
|
||||||
"ostream": "cpp",
|
|
||||||
"sstream": "cpp"
|
|
||||||
"system_error": "cpp"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,69 +0,0 @@
|
|||||||
# PlantControl
|
|
||||||
## Hardware
|
|
||||||
|
|
||||||
Main processor
|
|
||||||
* ESP32 with 16MB Flash
|
|
||||||
|
|
||||||
One-Wire
|
|
||||||
* Temperatur Sensor (DS18B20)
|
|
||||||
* Lipo-Monitoring (DS2438)
|
|
||||||
|
|
||||||
Lipo Protection
|
|
||||||
* Open drain 3.3V detector (CN61CN33 @ jlcpcb parts)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### Used Pins:
|
|
||||||
* See '''include/ControllerConfiguration.h'''
|
|
||||||
|
|
||||||
## Software
|
|
||||||
* MQTT topics
|
|
||||||
|
|
||||||
# Hardware
|
|
||||||
## Features
|
|
||||||
* Support for up to
|
|
||||||
* 7 Moister sensors
|
|
||||||
* 7 Pumps
|
|
||||||
* Sensors
|
|
||||||
* Solar powered (voltage)
|
|
||||||
* Lipo-Powered (DS2438 for monitoring)
|
|
||||||
* Temperature
|
|
||||||
* Laser distance sensor [VL53L0X]
|
|
||||||
* Custom GPIO
|
|
||||||
|
|
||||||
## Documentation of Power-Modes
|
|
||||||
https://lastminuteengineers.com/esp32-sleep-modes-power-consumption/#esp32-deep-sleep
|
|
||||||
|
|
||||||
|
|
||||||
gpio 17 only out no hold
|
|
||||||
gpio 16 only out no hold
|
|
||||||
|
|
||||||
## Additional hardware
|
|
||||||
solar charger 2A?
|
|
||||||
https://www.aliexpress.com/item/4000238259949.html?spm=a2g0o.productlist.0.0.7e50231cCWGu0Z&algo_pvid=9ab7b0d3-5026-438b-972b-1d4a81d4dc56&algo_expid=9ab7b0d3-5026-438b-972b-1d4a81d4dc56-11&btsid=0b0a0ac215999246489888249e72a9&ws_ab_test=searchweb0_0,searchweb201602_,searchweb201603_
|
|
||||||
|
|
||||||
MT3608 boost für pumpe
|
|
||||||
https://www.aliexpress.com/item/32925951391.html?spm=a2g0o.productlist.0.0.39e21087nAzH9q&algo_pvid=7db0a849-62f7-4403-88e3-615ee4d99339&algo_expid=7db0a849-62f7-4403-88e3-615ee4d99339-0&btsid=0b0a0ac215999252934777876e7253&ws_ab_test=searchweb0_0,searchweb201602_,searchweb201603_
|
|
||||||
|
|
||||||
DS18B20 one wire temp sensor
|
|
||||||
|
|
||||||
|
|
||||||
# Features
|
|
||||||
## Empires Wunschliste
|
|
||||||
* Pflanze
|
|
||||||
* Pumpe
|
|
||||||
* [x] Zeitspann (wann laufen darf)
|
|
||||||
* [x] Helligkeitstrigger (Um den Morgen zum pumpen zu erkennen)
|
|
||||||
* [-] Maximal Dauer zum Pumpen (als Zeit oder Milliliter)
|
|
||||||
* [x] Zeitspanne zwischen zwei Pumpvorgängen
|
|
||||||
* Moister sensor
|
|
||||||
* [x] Schwellwert für Pumpe
|
|
||||||
* Tank
|
|
||||||
* Füllstand Anzeige (in Liter)
|
|
||||||
* Minimum Wasserstand (in cm damit Pumpen nicht leer laufen; enspricht 0 nutzbaren Liter)
|
|
||||||
* [x] Maximaler Wasserstand des Tanks (in cm & Liter)
|
|
||||||
|
|
||||||
## Masterplan 2.0
|
|
||||||
* Partitionslayout
|
|
||||||
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
|||||||
[env:esp32doit-devkit-v1]
|
|
||||||
platform = espressif32
|
|
||||||
board = esp32doit-devkit-v1
|
|
||||||
framework = arduino
|
|
||||||
build_flags = -DPIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY
|
|
||||||
board_build.partitions = defaultWithSmallerSpiffs.csv
|
|
||||||
|
|
||||||
; the latest development brankitchen-lightch (convention V3.0.x)
|
|
||||||
lib_deps = ArduinoJson@6.16.1
|
|
||||||
OneWire
|
|
||||||
DallasTemperature
|
|
||||||
pololu/VL53L0X
|
|
||||||
https://github.com/homieiot/homie-esp8266.git#develop
|
|
||||||
|
|
||||||
; add additional parameter, like the upload port
|
|
||||||
upload_port=/dev/ttyUSB1
|
|
1
esp32/data/homie/.gitignore
vendored
1
esp32/data/homie/.gitignore
vendored
@ -1 +0,0 @@
|
|||||||
config.json
|
|
@ -1,12 +0,0 @@
|
|||||||
# Filesystem
|
|
||||||
## Configuration
|
|
||||||
Use the config-example.json from the host folder and create here a config.json file.
|
|
||||||
## HowTo upload
|
|
||||||
Start Platform.io
|
|
||||||
Open a new Atom-Terminal and generate the filesystem with the following command :
|
|
||||||
```pio run -t buildfs```
|
|
||||||
Upload this new generated filesystem with:
|
|
||||||
```pio run -t uploadfs```
|
|
||||||
|
|
||||||
## Command pio
|
|
||||||
Can be found at ```~/.platformio/penv/bin/pio```
|
|
Binary file not shown.
@ -1,7 +0,0 @@
|
|||||||
# Name, Type, SubType, Offset, Size, Flags
|
|
||||||
nvs, data, nvs, 0x9000, 0x4000
|
|
||||||
otadata, data, ota, 0xD000, 0x2000
|
|
||||||
phy_init, data, phy, 0xF000, 0x1000
|
|
||||||
ota_0, app, ota_0, 0x10000, 0x1E0000
|
|
||||||
ota_1, app, ota_1, 0x1F0000, 0x1E0000
|
|
||||||
spiffs, data, spiffs, 0x3D0000, 0x30000
|
|
|
@ -1,90 +0,0 @@
|
|||||||
# Configuration
|
|
||||||
## File
|
|
||||||
Generate a file as, described in
|
|
||||||
https://homieiot.github.io/homie-esp8266/docs/3.0.0/configuration/json-configuration-file/
|
|
||||||
For further details have a look at the Readme.md one level above.
|
|
||||||
|
|
||||||
## Upload
|
|
||||||
* Start ESP
|
|
||||||
* Login to Wifi, opened by the ESP
|
|
||||||
* Use the script to upload the configuration file
|
|
||||||
* restart the ESP
|
|
||||||
|
|
||||||
# Remote Upload
|
|
||||||
|
|
||||||
This script will allow you to send an OTA update to your device.
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
Requirements are:
|
|
||||||
* paho-mqtt
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
See ***upload-via-mqtt.sh***
|
|
||||||
|
|
||||||
# Remote Upload - Backend
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
```text
|
|
||||||
usage: ota_updater.py [-h] -l BROKER_HOST -p BROKER_PORT [-u BROKER_USERNAME]
|
|
||||||
[-d BROKER_PASSWORD] [-t BASE_TOPIC] -i DEVICE_ID
|
|
||||||
firmware
|
|
||||||
|
|
||||||
ota firmware update scirpt for ESP8226 implemenation of the Homie mqtt IoT
|
|
||||||
convention.
|
|
||||||
|
|
||||||
positional arguments:
|
|
||||||
firmware path to the firmware to be sent to the device
|
|
||||||
|
|
||||||
arguments:
|
|
||||||
-h, --help show this help message and exit
|
|
||||||
-l BROKER_HOST, --broker-host BROKER_HOST
|
|
||||||
host name or ip address of the mqtt broker
|
|
||||||
-p BROKER_PORT, --broker-port BROKER_PORT
|
|
||||||
port of the mqtt broker
|
|
||||||
-u BROKER_USERNAME, --broker-username BROKER_USERNAME
|
|
||||||
username used to authenticate with the mqtt broker
|
|
||||||
-d BROKER_PASSWORD, --broker-password BROKER_PASSWORD
|
|
||||||
password used to authenticate with the mqtt broker
|
|
||||||
-t BASE_TOPIC, --base-topic BASE_TOPIC
|
|
||||||
base topic of the homie devices on the broker
|
|
||||||
-i DEVICE_ID, --device-id DEVICE_ID
|
|
||||||
homie device id
|
|
||||||
```
|
|
||||||
|
|
||||||
* `BROKER_HOST` and `BROKER_PORT` defaults to 127.0.0.1 and 1883 respectively if not set.
|
|
||||||
* `BROKER_USERNAME` and `BROKER_PASSWORD` are optional.
|
|
||||||
* `BASE_TOPIC` has to end with a slash, defaults to `homie/` if not set.
|
|
||||||
|
|
||||||
### Example:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
python ota_updater.py -l localhost -u admin -d secure -t "homie/" -i "device-id" /path/to/firmware.bin
|
|
||||||
```
|
|
||||||
|
|
||||||
The Parameter can be extracted from the serial console
|
|
||||||
```serial
|
|
||||||
{} Stored configuration
|
|
||||||
• Hardware device ID: 12345abcd
|
|
||||||
• Device ID: MyDeviceId
|
|
||||||
• Name: MyDeviceName
|
|
||||||
• Device Stats Interval: 60 sec
|
|
||||||
• Wi-Fi:
|
|
||||||
◦ SSID: MyWifi
|
|
||||||
◦ Password not shown
|
|
||||||
• MQTT:
|
|
||||||
◦ Host: 192.168.0.2
|
|
||||||
◦ Port: 1883
|
|
||||||
◦ Base topic: /test/
|
|
||||||
◦ Auth? no
|
|
||||||
```
|
|
||||||
|
|
||||||
will result in the following command (when executed in this folder):
|
|
||||||
```bash
|
|
||||||
python ota_updater.py -l 192.168.0.2 -t "/test/" -i "MyDeviceId" ../.pio/build/esp32doit-devkit-v1/firmware.bin
|
|
||||||
```
|
|
||||||
|
|
||||||
### Source
|
|
||||||
https://github.com/homieiot/homie-esp8266/blob/develop/scripts/ota_updater
|
|
@ -1,174 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
from __future__ import division, print_function
|
|
||||||
import paho.mqtt.client as mqtt
|
|
||||||
import base64, sys, math
|
|
||||||
from hashlib import md5
|
|
||||||
|
|
||||||
# The callback for when the client receives a CONNACK response from the server.
|
|
||||||
def on_connect(client, userdata, flags, rc):
|
|
||||||
if rc != 0:
|
|
||||||
print("Connection Failed with result code {}".format(rc))
|
|
||||||
client.disconnect()
|
|
||||||
else:
|
|
||||||
print("Connected with result code {}".format(rc))
|
|
||||||
|
|
||||||
client.subscribe("{base_topic}{device_id}/$state".format(**userdata)) # v3 / v4 devices
|
|
||||||
client.subscribe("{base_topic}{device_id}/$online".format(**userdata)) # v2 devices
|
|
||||||
|
|
||||||
|
|
||||||
print("Waiting for device to come online...")
|
|
||||||
|
|
||||||
|
|
||||||
# The callback for when a PUBLISH message is received from the server.
|
|
||||||
def on_message(client, userdata, msg):
|
|
||||||
# decode string for python2/3 compatiblity
|
|
||||||
msg.payload = msg.payload.decode()
|
|
||||||
|
|
||||||
if msg.topic.endswith('$implementation/ota/status'):
|
|
||||||
status = int(msg.payload.split()[0])
|
|
||||||
|
|
||||||
if userdata.get("published"):
|
|
||||||
if status == 206: # in progress
|
|
||||||
# state in progress, print progress bar
|
|
||||||
progress, total = [int(x) for x in msg.payload.split()[1].split('/')]
|
|
||||||
bar_width = 30
|
|
||||||
bar = int(bar_width*(progress/total))
|
|
||||||
print("\r[", '+'*bar, ' '*(bar_width-bar), "] ", msg.payload.split()[1], end='', sep='')
|
|
||||||
if (progress == total):
|
|
||||||
print()
|
|
||||||
sys.stdout.flush()
|
|
||||||
elif status == 304: # not modified
|
|
||||||
print("Device firmware already up to date with md5 checksum: {}".format(userdata.get('md5')))
|
|
||||||
client.disconnect()
|
|
||||||
elif status == 403: # forbidden
|
|
||||||
print("Device ota disabled, aborting...")
|
|
||||||
client.disconnect()
|
|
||||||
|
|
||||||
elif msg.topic.endswith('$fw/checksum'):
|
|
||||||
checksum = msg.payload
|
|
||||||
|
|
||||||
if userdata.get("published"):
|
|
||||||
if checksum == userdata.get('md5'):
|
|
||||||
print("Device back online. Update Successful!")
|
|
||||||
else:
|
|
||||||
print("Expecting checksum {}, got {}, update failed!".format(userdata.get('md5'), checksum))
|
|
||||||
client.disconnect()
|
|
||||||
else:
|
|
||||||
if checksum != userdata.get('md5'): # save old md5 for comparison with new firmware
|
|
||||||
userdata.update({'old_md5': checksum})
|
|
||||||
else:
|
|
||||||
print("Device firmware already up to date with md5 checksum: {}".format(checksum))
|
|
||||||
client.disconnect()
|
|
||||||
|
|
||||||
elif msg.topic.endswith('ota/enabled'):
|
|
||||||
if msg.payload == 'true':
|
|
||||||
userdata.update({'ota_enabled': True})
|
|
||||||
else:
|
|
||||||
print("Device ota disabled, aborting...")
|
|
||||||
client.disconnect()
|
|
||||||
|
|
||||||
elif msg.topic.endswith('$state') or msg.topic.endswith('$online'):
|
|
||||||
if (msg.topic.endswith('$state') and msg.payload != 'ready') or (msg.topic.endswith('$online') and msg.payload == 'false'):
|
|
||||||
return
|
|
||||||
|
|
||||||
# calcluate firmware md5
|
|
||||||
firmware_md5 = md5(userdata['firmware']).hexdigest()
|
|
||||||
userdata.update({'md5': firmware_md5})
|
|
||||||
|
|
||||||
# Subscribing in on_connect() means that if we lose the connection and
|
|
||||||
# reconnect then subscriptions will be renewed.
|
|
||||||
client.subscribe("{base_topic}{device_id}/$implementation/ota/status".format(**userdata))
|
|
||||||
client.subscribe("{base_topic}{device_id}/$implementation/ota/enabled".format(**userdata))
|
|
||||||
client.subscribe("{base_topic}{device_id}/$fw/#".format(**userdata))
|
|
||||||
|
|
||||||
# Wait for device info to come in and invoke the on_message callback where update will continue
|
|
||||||
print("Waiting for device info...")
|
|
||||||
|
|
||||||
if ( not userdata.get("published") ) and ( userdata.get('ota_enabled') ) and \
|
|
||||||
( 'old_md5' in userdata.keys() ) and ( userdata.get('md5') != userdata.get('old_md5') ):
|
|
||||||
# push the firmware binary
|
|
||||||
userdata.update({"published": True})
|
|
||||||
topic = "{base_topic}{device_id}/$implementation/ota/firmware/{md5}".format(**userdata)
|
|
||||||
print("Publishing new firmware with checksum {}".format(userdata.get('md5')))
|
|
||||||
client.publish(topic, userdata['firmware'])
|
|
||||||
|
|
||||||
|
|
||||||
def main(broker_host, broker_port, broker_username, broker_password, broker_ca_cert, base_topic, device_id, firmware):
|
|
||||||
# initialise mqtt client and register callbacks
|
|
||||||
client = mqtt.Client()
|
|
||||||
client.on_connect = on_connect
|
|
||||||
client.on_message = on_message
|
|
||||||
|
|
||||||
# set username and password if given
|
|
||||||
if broker_username and broker_password:
|
|
||||||
client.username_pw_set(broker_username, broker_password)
|
|
||||||
|
|
||||||
if broker_ca_cert is not None:
|
|
||||||
client.tls_set(
|
|
||||||
ca_certs=broker_ca_cert
|
|
||||||
)
|
|
||||||
|
|
||||||
# save data to be used in the callbacks
|
|
||||||
client.user_data_set({
|
|
||||||
"base_topic": base_topic,
|
|
||||||
"device_id": device_id,
|
|
||||||
"firmware": firmware
|
|
||||||
})
|
|
||||||
|
|
||||||
# start connection
|
|
||||||
print("Connecting to mqtt broker {} on port {}".format(broker_host, broker_port))
|
|
||||||
client.connect(broker_host, broker_port, 60)
|
|
||||||
|
|
||||||
# Blocking call that processes network traffic, dispatches callbacks and handles reconnecting.
|
|
||||||
client.loop_forever()
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
import argparse
|
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(
|
|
||||||
description='ota firmware update scirpt for ESP8226 implemenation of the Homie mqtt IoT convention.')
|
|
||||||
|
|
||||||
# ensure base topic always ends with a '/'
|
|
||||||
def base_topic_arg(s):
|
|
||||||
s = str(s)
|
|
||||||
if not s.endswith('/'):
|
|
||||||
s = s + '/'
|
|
||||||
return s
|
|
||||||
|
|
||||||
# specify arguments
|
|
||||||
parser.add_argument('-l', '--broker-host', type=str, required=False,
|
|
||||||
help='host name or ip address of the mqtt broker', default="127.0.0.1")
|
|
||||||
parser.add_argument('-p', '--broker-port', type=int, required=False,
|
|
||||||
help='port of the mqtt broker', default=1883)
|
|
||||||
parser.add_argument('-u', '--broker-username', type=str, required=False,
|
|
||||||
help='username used to authenticate with the mqtt broker')
|
|
||||||
parser.add_argument('-d', '--broker-password', type=str, required=False,
|
|
||||||
help='password used to authenticate with the mqtt broker')
|
|
||||||
parser.add_argument('-t', '--base-topic', type=base_topic_arg, required=False,
|
|
||||||
help='base topic of the homie devices on the broker', default="homie/")
|
|
||||||
parser.add_argument('-i', '--device-id', type=str, required=True,
|
|
||||||
help='homie device id')
|
|
||||||
parser.add_argument('firmware', type=argparse.FileType('rb'),
|
|
||||||
help='path to the firmware to be sent to the device')
|
|
||||||
|
|
||||||
parser.add_argument("--broker-tls-cacert", default=None, required=False,
|
|
||||||
help="CA certificate bundle used to validate TLS connections. If set, TLS will be enabled on the broker conncetion"
|
|
||||||
)
|
|
||||||
|
|
||||||
# workaround for http://bugs.python.org/issue9694
|
|
||||||
parser._optionals.title = "arguments"
|
|
||||||
|
|
||||||
# get and validate arguments
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
# read the contents of firmware into buffer
|
|
||||||
fw_buffer = args.firmware.read()
|
|
||||||
args.firmware.close()
|
|
||||||
firmware = bytearray()
|
|
||||||
firmware.extend(fw_buffer)
|
|
||||||
|
|
||||||
# Invoke the business logic
|
|
||||||
main(args.broker_host, args.broker_port, args.broker_username,
|
|
||||||
args.broker_password, args.broker_tls_cacert, args.base_topic, args.device_id, firmware)
|
|
@ -1,49 +0,0 @@
|
|||||||
{
|
|
||||||
"settings": {
|
|
||||||
"sleep":600,
|
|
||||||
"nightsleep": 1200,
|
|
||||||
"pumpsleep": 5,
|
|
||||||
"tankmax": 1000,
|
|
||||||
"tankmin": 100,
|
|
||||||
"tankwarn": 200,
|
|
||||||
"tankVolume": 100,
|
|
||||||
"lipoDSAddr": "abcdefghijklmnop",
|
|
||||||
"tankDSAddr": "abcdefghijklmnop",
|
|
||||||
"ntpServer":"pool.ntp.org",
|
|
||||||
"dry0":50,
|
|
||||||
"hourstart0":6,
|
|
||||||
"hourend0":20,
|
|
||||||
"lowLight0": false,
|
|
||||||
"delay0": 30,
|
|
||||||
"dry1":-1,
|
|
||||||
"hourstart1":6,
|
|
||||||
"hourend1":20,
|
|
||||||
"lowLight1": false,
|
|
||||||
"delay1": 30,
|
|
||||||
"dry2":-1,
|
|
||||||
"hourstart2":6,
|
|
||||||
"hourend2":20,
|
|
||||||
"lowLight2": false,
|
|
||||||
"delay2": 30,
|
|
||||||
"dry3":-1,
|
|
||||||
"hourstart3":6,
|
|
||||||
"hourend3":20,
|
|
||||||
"lowLight3": false,
|
|
||||||
"delay3": 30,
|
|
||||||
"dry4":-1,
|
|
||||||
"hourstart4":6,
|
|
||||||
"hourend4":20,
|
|
||||||
"lowLight4": false,
|
|
||||||
"delay4": 30,
|
|
||||||
"dry5":-1,
|
|
||||||
"hourstart5":6,
|
|
||||||
"hourend5":20,
|
|
||||||
"lowLight5": false,
|
|
||||||
"delay5": 30,
|
|
||||||
"dry6":-1,
|
|
||||||
"hourstart6":6,
|
|
||||||
"hourend6":20,
|
|
||||||
"lowLight6": false,
|
|
||||||
"delay6": 30
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,41 +0,0 @@
|
|||||||
#!//bin/bash
|
|
||||||
|
|
||||||
if [ $# -ne 3 ]; then
|
|
||||||
echo "Homie prefex and device index must be specified:"
|
|
||||||
echo "$0 <mqtt host> <prefix> <device index>"
|
|
||||||
echo "e.g."
|
|
||||||
echo "$0 192.168.0.2 test/ MyDeviceId"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
mqttHost=$1
|
|
||||||
mqttPrefix=$2
|
|
||||||
homieId=$3
|
|
||||||
|
|
||||||
maxSteps=6
|
|
||||||
|
|
||||||
settingsFile=settings.json
|
|
||||||
if [ ! -f $settingsFile ]; then
|
|
||||||
echo "$settingsFile missing"
|
|
||||||
echo "check $settingsFile.example"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
mosquitto_pub -h $mqttHost -t "${mqttPrefix}${homieId}/stay/alive/set" -m "1" -r
|
|
||||||
echo "(1 / $maxSteps) Waiting ..."
|
|
||||||
mosquitto_sub -h $mqttHost -t "${mqttPrefix}${homieId}/#" -R -C 1
|
|
||||||
set -e
|
|
||||||
echo "(2 / $maxSteps) Waiting 30 seconds ..."
|
|
||||||
sleep 30
|
|
||||||
mosquitto_pub -h $mqttHost -t "${mqttPrefix}${homieId}/\$implementation/config/set" -f $settingsFile
|
|
||||||
echo "(3 / $maxSteps) Waiting for reboot ..."
|
|
||||||
sleep 1
|
|
||||||
mosquitto_sub -h $mqttHost -t "${mqttPrefix}${homieId}/#" -R -C 1
|
|
||||||
echo "(4 / $maxSteps) Alive"
|
|
||||||
sleep 20
|
|
||||||
echo "(5 / $maxSteps) Create Backup ..."
|
|
||||||
mosquitto_pub -h $mqttHost -t "${mqttPrefix}${homieId}/config/backup/set" -m "true" -r
|
|
||||||
sleep 5
|
|
||||||
echo "(6 / $maxSteps) Shutdown ..."
|
|
||||||
mosquitto_pub -h $mqttHost -t "${mqttPrefix}${homieId}/stay/alive/set" -m "0" -r
|
|
||||||
exit 0
|
|
@ -1,28 +0,0 @@
|
|||||||
#!//bin/bash
|
|
||||||
|
|
||||||
if [ $# -ne 3 ]; then
|
|
||||||
echo "Homie prefex and device index must be specified:"
|
|
||||||
echo "$0 <mqtt host> <prefix> <device index>"
|
|
||||||
echo "e.g."
|
|
||||||
echo "$0 192.168.0.2 test/ MyDeviceId"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
mqttHost=$1
|
|
||||||
mqttPrefix=$2
|
|
||||||
homieId=$3
|
|
||||||
firmwareFile=../.pio/build/esp32doit-devkit-v1/firmware.bin
|
|
||||||
|
|
||||||
if [ ! -f $firmwareFile ]; then
|
|
||||||
echo "the script $0 must be started in host/ sub directory"
|
|
||||||
exit 2
|
|
||||||
fi
|
|
||||||
|
|
||||||
mosquitto_pub -h $mqttHost -t "${mqttPrefix}${homieId}/stay/alive/set" -m "1" -r
|
|
||||||
echo "Waiting ..."
|
|
||||||
mosquitto_sub -h $mqttHost -t "${mqttPrefix}${homieId}/#" -R -C 1
|
|
||||||
set -e
|
|
||||||
python3 ota_updater.py -l $mqttHost -t "$mqttPrefix" -i "$homieId" $firmwareFile
|
|
||||||
|
|
||||||
mosquitto_pub -h $mqttHost -t "${mqttPrefix}${homieId}/stay/alive/set" -m "0" -r
|
|
||||||
exit 0
|
|
@ -1,13 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
echo "Homie device is in AP mode, then the configuration can be uploaded"
|
|
||||||
|
|
||||||
if [ ! -f config.json ]; then
|
|
||||||
echo "Create config file according to :"
|
|
||||||
echo "https://homieiot.github.io/homie-esp8266/docs/3.0.0/configuration/json-configuration-file/"
|
|
||||||
exit 2
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Check connection to Plug in AP-mode"
|
|
||||||
ping -c 4 192.168.123.1
|
|
||||||
|
|
||||||
curl -X PUT http://192.168.123.1/config --header "Content-Type: application/json" -d @config.json
|
|
@ -1,136 +0,0 @@
|
|||||||
/**
|
|
||||||
* @file ControllerConfiguration.h
|
|
||||||
* @author your name (you@domain.com)
|
|
||||||
* @brief
|
|
||||||
* @version 0.1
|
|
||||||
* @date 2020-05-30
|
|
||||||
*
|
|
||||||
* @copyright Copyright (c) 2020
|
|
||||||
*
|
|
||||||
* \mainpage Configuration of the controller
|
|
||||||
* @{
|
|
||||||
* Describe the used PINs of the controller
|
|
||||||
*
|
|
||||||
* @subpage Controller
|
|
||||||
*
|
|
||||||
* @subpage Homie
|
|
||||||
*
|
|
||||||
* @subpage Configuration
|
|
||||||
*
|
|
||||||
* There are several modes in the controller
|
|
||||||
* \dot
|
|
||||||
* digraph Operationmode {
|
|
||||||
* ranksep=.75;
|
|
||||||
* poweroff [ label="off" ];
|
|
||||||
* mode1 [ label="Mode 1 - Sensor only", shape=box, width=2 ];
|
|
||||||
* mode2 [ label="Mode 2 - Wifi enabled", shape=box ];
|
|
||||||
* mode3 [ label="Mode 3 - Stay alive", shape=box ];
|
|
||||||
* mode1 -> mode2 [ label="wakeup reason", fontsize=10 ];
|
|
||||||
* mode1 -> mode2 [ label="Time duration", fontsize=10 ];
|
|
||||||
* mode2 -> mode3 [ label="Over the Air Update", fontsize=10 ];
|
|
||||||
* mode3 -> mode2 [ label="Over the Air Finished", fontsize=10 ];
|
|
||||||
* mode3 -> mode2 [ label="Mqtt Command", fontsize=10 ];
|
|
||||||
* mode2 -> mode3 [ label="Mqtt Command", fontsize=10 ];
|
|
||||||
* poweroff -> mode1 [ label="deep sleep wakeup", fontsize=10 ];
|
|
||||||
* mode1 -> poweroff [ label="enter deep sleep", fontsize=10 ];
|
|
||||||
* mode2 -> poweroff [ label="Mqtt queue empty", fontsize=10 ];
|
|
||||||
* }
|
|
||||||
* \enddot
|
|
||||||
*
|
|
||||||
* Before entering Deep sleep the controller is configured with an wakeup time.
|
|
||||||
*
|
|
||||||
* @}
|
|
||||||
*/
|
|
||||||
#ifndef CONTROLLER_CONFIG_H
|
|
||||||
#define CONTROLLER_CONFIG_H
|
|
||||||
/** \addtogroup GPIO Settings
|
|
||||||
* @{
|
|
||||||
*/
|
|
||||||
#define SENSOR_PLANT0 GPIO_NUM_32 /**< GPIO 32 (ADC1) */
|
|
||||||
#define SENSOR_PLANT1 GPIO_NUM_33 /**< GPIO 33 (ADC1) */
|
|
||||||
#define SENSOR_PLANT2 GPIO_NUM_25 /**< GPIO 25 (ADC2) */
|
|
||||||
#define SENSOR_PLANT3 GPIO_NUM_26 /**< GPIO 26 (ADC2) */
|
|
||||||
#define SENSOR_PLANT4 GPIO_NUM_27 /**< GPIO 27 (ADC2) */
|
|
||||||
#define SENSOR_PLANT5 GPIO_NUM_39 /**< SENSOR_VIN */
|
|
||||||
#define SENSOR_PLANT6 GPIO_NUM_36 /**< SENSOR_VP */
|
|
||||||
|
|
||||||
#define OUTPUT_PUMP0 GPIO_NUM_15 /**< GPIO 15 */
|
|
||||||
#define OUTPUT_PUMP1 GPIO_NUM_5 /**< GPIO 5 */
|
|
||||||
#define OUTPUT_PUMP2 GPIO_NUM_18 /**< GPIO 18 */
|
|
||||||
#define OUTPUT_PUMP3 GPIO_NUM_19 /**< GPIO 19 */
|
|
||||||
#define OUTPUT_PUMP4 GPIO_NUM_21 /**< GPIO 21 */
|
|
||||||
#define OUTPUT_PUMP5 GPIO_NUM_22 /**< GPIO 22 */
|
|
||||||
#define OUTPUT_PUMP6 GPIO_NUM_23 /**< GPIO 23 */
|
|
||||||
|
|
||||||
#define OUTPUT_ENABLE_SENSOR GPIO_NUM_14 /**< GPIO 14 - Enable Sensors */
|
|
||||||
#define OUTPUT_ENABLE_PUMP GPIO_NUM_13 /**< GPIO 13 - Enable Pumps */
|
|
||||||
|
|
||||||
#define SENSOR_ONEWIRE GPIO_NUM_4 /**< GPIO 12 - Temperatur sensor, Battery and other cool onewire stuff */
|
|
||||||
#define SENSOR_TANK_SDA GPIO_NUM_17 /**< GPIO 17 - water sensor SDA */
|
|
||||||
#define SENSOR_TANK_SCL GPIO_NUM_16 /**< GPIO 16 - water sensor SCL */
|
|
||||||
#define BUTTON GPIO_NUM_0 /**< GPIO 0 - Fix button of NodeMCU */
|
|
||||||
|
|
||||||
#define CUSTOM1_PIN1 GPIO_NUM_34 /** direct gpio */
|
|
||||||
#define CUSTOM1_PIN3 GPIO_NUM_35 /** direct gpio */
|
|
||||||
#define CUSTOM1_PIN5 GPIO_NUM_2 /** mosfet controlled */
|
|
||||||
#define CUSTOM1_PIN7 GPIO_NUM_12 /** mosfet controlled */
|
|
||||||
|
|
||||||
/* @} */
|
|
||||||
|
|
||||||
/** \addtogroup Configuration
|
|
||||||
* @{
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef FLOWMETER_PIN
|
|
||||||
#define FLOWMETER_PULSES_PER_ML 2.2
|
|
||||||
#define FIRMWARE_FEATURE1 "Flow"
|
|
||||||
#else
|
|
||||||
#define FIRMWARE_FEATURE1 ""
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef TIMED_LIGHT_PIN
|
|
||||||
#define FIRMWARE_FEATURE2 "Light"
|
|
||||||
#else
|
|
||||||
#define FIRMWARE_FEATURE2 ""
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define FIRMWARE_BASENAME "PlantControl"
|
|
||||||
#define FIRMWARE_NAME FIRMWARE_BASENAME FIRMWARE_FEATURE1 FIRMWARE_FEATURE2
|
|
||||||
#define FIRMWARE_VERSION "3.01 HW0.10b"
|
|
||||||
|
|
||||||
#define MOIST_SENSOR_MAX_FRQ 5200 // 60kHz (500Hz margin)
|
|
||||||
#define MOIST_SENSOR_MIN_FRQ 500 // 0.5kHz (500Hz margin)
|
|
||||||
|
|
||||||
#define ANALOG_SENSOR_MAX_MV 1300 //successive approximation of good range
|
|
||||||
#define ANALOG_SENSOR_MIN_MV 100 //successive approximation of good range
|
|
||||||
|
|
||||||
#define SOLAR_VOLT_FACTOR 11
|
|
||||||
#define BATTSENSOR_INDEX_SOLAR 0
|
|
||||||
#define BATTSENSOR_INDEX_BATTERY 1
|
|
||||||
|
|
||||||
#define MQTT_TIMEOUT (1000 * 60) /**< After 10 seconds, MQTT is expected to be connected */
|
|
||||||
#define ESP_STALE_TIMEOUT (MQTT_TIMEOUT+(700*1000))
|
|
||||||
|
|
||||||
#define MAX_PLANTS 7
|
|
||||||
#define SOLAR_CHARGE_MIN_VOLTAGE 7 /**< Sun is rising (morning detected) */
|
|
||||||
#define SOLAR_CHARGE_MAX_VOLTAGE 9 /**< Sun is shining (noon) */
|
|
||||||
#define SOLAR_MAX_VOLTAGE_POSSIBLE 100 /**< higher values are treated as not connected sensor */
|
|
||||||
#define VOLT_MAX_BATT 4.2f
|
|
||||||
#define VOLT_MIN_BATT 3.0f /**< Minimum battery voltage for normal operation */
|
|
||||||
#define LOWVOLT_SLEEP_FACTOR 3 /**< Factor for nightsleep delay, if the battery drops below minimum (@see VOLT_MIN_BATT) */
|
|
||||||
#define LOWVOLT_SLEEP_MINIMUM 1800 /**< At low voltage sleep at least for 30 minutes */
|
|
||||||
|
|
||||||
#define MAX_CONFIG_SETTING_ITEMS 100 /**< Parameter, that can be configured in Homie */
|
|
||||||
#define MAX_JSON_CONFIG_FILE_SIZE_CUSTOM 2500
|
|
||||||
|
|
||||||
#define TEMPERATUR_TIMEOUT 3000 /**< 3 Seconds timeout for the temperatures sensors */
|
|
||||||
#define WATERSENSOR_TIMEOUT 3000 /**< 3 Seconds timeout for the water distance sensor */
|
|
||||||
#define WATERSENSOR_CYCLE 10 /**< 5 sensor measurement are performed */
|
|
||||||
#define DS18B20_RESOLUTION 9 /**< 9bit temperature resolution -> 0.5°C steps */
|
|
||||||
|
|
||||||
#define UTC_OFFSET_DE 3600 /* UTC offset in seconds for Germany */
|
|
||||||
#define UTF_OFFSET_DE_DST 3600 /* offset in seconds if daylight saving time is active */
|
|
||||||
|
|
||||||
/* @} */
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,111 +0,0 @@
|
|||||||
/**
|
|
||||||
* @file DS2438.h
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef DS2438_h
|
|
||||||
#define DS2438_h
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
|
||||||
#include <OneWire.h>
|
|
||||||
#include "RunningMedian.h"
|
|
||||||
|
|
||||||
#define DS2438_TEMPERATURE_CONVERSION_COMMAND 0x44
|
|
||||||
#define DS2438_VOLTAGE_CONVERSION_COMMAND 0xb4
|
|
||||||
#define DS2438_WRITE_SCRATCHPAD_COMMAND 0x4e
|
|
||||||
#define DS2438_COPY_SCRATCHPAD_COMMAND 0x48
|
|
||||||
#define DS2438_READ_SCRATCHPAD_COMMAND 0xbe
|
|
||||||
#define DS2438_RECALL_MEMORY_COMMAND 0xb8
|
|
||||||
|
|
||||||
#define PAGE_MIN 0
|
|
||||||
#define PAGE_MAX 7
|
|
||||||
|
|
||||||
#define DS2438_CHA 0
|
|
||||||
#define DS2438_CHB 1
|
|
||||||
|
|
||||||
#define DS2438_MODE_CHA 0x01
|
|
||||||
#define DS2438_MODE_CHB 0x02
|
|
||||||
#define DS2438_MODE_TEMPERATURE 0x04
|
|
||||||
|
|
||||||
#define DS2438_TEMPERATURE_DELAY 10
|
|
||||||
#define DS2438_VOLTAGE_CONVERSION_DELAY 8
|
|
||||||
|
|
||||||
#define DS2438_MEDIAN_COUNT 5
|
|
||||||
#define DS2438_MEDIAN_DELAY 50
|
|
||||||
|
|
||||||
#define DEFAULT_PAGE0(var) uint8_t var[8] { \
|
|
||||||
0b00001011 /* X, ADB=0, NVB=0, TB=0, AD=1, EE=0, CA=1, IAD=1 */, \
|
|
||||||
0, /* Temperatur */ \
|
|
||||||
0, /* Temperatur */ \
|
|
||||||
0, /* Voltage */ \
|
|
||||||
0, /* Voltage */ \
|
|
||||||
0, /* Current */ \
|
|
||||||
0, /* Current */ \
|
|
||||||
0b10000000 /* Threshold to 4LSB */ \
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct PageOne {
|
|
||||||
uint8_t eleapsedTimerByte0; /**< LSB of timestamp */
|
|
||||||
uint8_t eleapsedTimerByte1;
|
|
||||||
uint8_t eleapsedTimerByte2;
|
|
||||||
uint8_t eleapsedTimerByte3; /**< MSB of timestamp */
|
|
||||||
uint8_t ICA; /**< Integrated Current Accumulator (current flowing into and out of the battery) */
|
|
||||||
uint8_t offsetRegisterByte0; /**< Offset for ADC calibdation */
|
|
||||||
uint8_t offsetRegisterByte1; /**< Offset for ADC calibdation */
|
|
||||||
uint8_t reserved;
|
|
||||||
} PageOne_t;
|
|
||||||
|
|
||||||
typedef struct PageSeven {
|
|
||||||
uint8_t userByte0;
|
|
||||||
uint8_t userByte1;
|
|
||||||
uint8_t userByte2;
|
|
||||||
uint8_t userByte3;
|
|
||||||
uint8_t CCA0; /**< Charging Current Accumulator (CCA) */
|
|
||||||
uint8_t CCA1; /**< Charging Current Accumulator (CCA) */
|
|
||||||
uint8_t DCA0; /**< Discharge Current Accumulator (DCA) */
|
|
||||||
uint8_t DCA1; /**< Discharge Current Accumulator (DCA) */
|
|
||||||
} PageSeven_t;
|
|
||||||
|
|
||||||
typedef uint8_t DeviceAddress[8];
|
|
||||||
|
|
||||||
class DS2438 {
|
|
||||||
public:
|
|
||||||
DS2438(OneWire *ow, float currentShunt, int retryOnCRCError);
|
|
||||||
|
|
||||||
void begin();
|
|
||||||
void updateMultiple();
|
|
||||||
double getTemperature();
|
|
||||||
float getVoltage(int channel=DS2438_CHA);
|
|
||||||
float getCurrent();
|
|
||||||
long getICA();
|
|
||||||
long getCCA();
|
|
||||||
long getDCA();
|
|
||||||
float getAh();
|
|
||||||
boolean isError();
|
|
||||||
boolean isFound();
|
|
||||||
private:
|
|
||||||
bool validAddress(const uint8_t*);
|
|
||||||
bool validFamily(const uint8_t* deviceAddress);
|
|
||||||
void update(bool firstIteration);
|
|
||||||
bool deviceFound = false;
|
|
||||||
OneWire *_ow;
|
|
||||||
DeviceAddress _address;
|
|
||||||
uint8_t _mode;
|
|
||||||
RunningMedian _temperature = RunningMedian(DS2438_MEDIAN_COUNT*2);
|
|
||||||
RunningMedian _voltageA = RunningMedian(DS2438_MEDIAN_COUNT);
|
|
||||||
RunningMedian _voltageB = RunningMedian(DS2438_MEDIAN_COUNT);
|
|
||||||
RunningMedian _current = RunningMedian(DS2438_MEDIAN_COUNT);
|
|
||||||
float _currentShunt;
|
|
||||||
int _retryOnCRCError;
|
|
||||||
long _CCA;
|
|
||||||
long _DCA;
|
|
||||||
long _ICA;
|
|
||||||
boolean _error;
|
|
||||||
boolean startConversion(int channel, boolean doTemperature);
|
|
||||||
boolean selectChannel(int channel);
|
|
||||||
void writePage(int page, uint8_t *data);
|
|
||||||
boolean readPage(int page, uint8_t *data);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,9 +0,0 @@
|
|||||||
#ifndef FILEUTILS_H
|
|
||||||
#define FILEUTILS_H
|
|
||||||
|
|
||||||
bool doesFileExist(const char *source);
|
|
||||||
bool copyFile(const char *source, const char *target);
|
|
||||||
bool deleteFile(const char *source);
|
|
||||||
void printFile(const char *source);
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,135 +0,0 @@
|
|||||||
/** \addtogroup Homie
|
|
||||||
* @{
|
|
||||||
*
|
|
||||||
* @file HomieConfiguration.h
|
|
||||||
* @author your name (you@domain.com)
|
|
||||||
* @brief
|
|
||||||
* @version 0.1
|
|
||||||
* @date 2020-10-16
|
|
||||||
*
|
|
||||||
* @copyright Copyright (c) 2020
|
|
||||||
* All Settings, configurable in Homie
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
#ifndef HOMIE_PLANT_CONFIG_H
|
|
||||||
#define HOMIE_PLANT_CONFIG_H
|
|
||||||
|
|
||||||
#include "HomieTypes.h"
|
|
||||||
|
|
||||||
#define MAX_PLANTS 7
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @name Homie Attributes
|
|
||||||
* generated Information
|
|
||||||
* @{
|
|
||||||
**/
|
|
||||||
|
|
||||||
#define NUMBER_TYPE "Float" /**< numberic information, published or read in Homie */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @}
|
|
||||||
*
|
|
||||||
* @name Temperatur Node
|
|
||||||
* @{
|
|
||||||
**/
|
|
||||||
|
|
||||||
#define TEMPERATURE_NAME "Temperature"
|
|
||||||
#define TEMPERATURE_UNIT "°C"
|
|
||||||
#define TEMPERATUR_SENSOR_LIPO "lipo" /**< Homie node: temperatur, setting: lipo temperatur (or close to it) */
|
|
||||||
#define TEMPERATUR_SENSOR_CHIP "chip" /**< Homie node: temperatur, setting: battery chip */
|
|
||||||
#define TEMPERATUR_SENSOR_WATER "water" /**< Homie node: temperatur, setting: water temperatur */
|
|
||||||
/** @}
|
|
||||||
*
|
|
||||||
* @name Plant Nodes
|
|
||||||
* @{
|
|
||||||
*/
|
|
||||||
|
|
||||||
HomieNode plant0("plant0", "Plant 0", "Plant"); /**< dynamic Homie information for first plant */
|
|
||||||
HomieNode plant1("plant1", "Plant 1", "Plant"); /**< dynamic Homie information for second plant */
|
|
||||||
HomieNode plant2("plant2", "Plant 2", "Plant"); /**< dynamic Homie information for third plant */
|
|
||||||
HomieNode plant3("plant3", "Plant 3", "Plant"); /**< dynamic Homie information for fourth plant */
|
|
||||||
HomieNode plant4("plant4", "Plant 4", "Plant"); /**< dynamic Homie information for fivth plant */
|
|
||||||
HomieNode plant5("plant5", "Plant 5", "Plant"); /**< dynamic Homie information for sixth plant */
|
|
||||||
HomieNode plant6("plant6", "Plant 6", "Plant"); /**< dynamic Homie information for seventh plant */
|
|
||||||
|
|
||||||
#if defined(TIMED_LIGHT_PIN)
|
|
||||||
HomieNode timedLightNode("timedLight", "TimedLight", "Status");
|
|
||||||
#endif // TIMED_LIGHT_PIN
|
|
||||||
|
|
||||||
|
|
||||||
HomieNode sensorLipo("lipo", "Battery Status", "Lipo");
|
|
||||||
HomieNode sensorSolar("solar", "Solar Status", "Solarpanel");
|
|
||||||
HomieNode sensorWater("water", "WaterSensor", "Water");
|
|
||||||
HomieNode sensorTemp("temperature", "Temperature", "temperature");
|
|
||||||
HomieNode stayAlive("stay", "alive", "alive"); /**< Necessary for Mqtt Active Command */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @}
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @name Settings
|
|
||||||
* General settings for the controller
|
|
||||||
* @{
|
|
||||||
*/
|
|
||||||
HomieSetting<long> deepSleepTime("sleep", "time in seconds to sleep");
|
|
||||||
HomieSetting<long> deepSleepNightTime("nightsleep", "time in seconds to sleep (0 uses same setting: deepsleep at night, too)");
|
|
||||||
HomieSetting<long> pumpIneffectiveWarning("pumpConsecutiveWarn", "if the pump was triggered this amount directly after each cooldown, without resolving dryness, warn");
|
|
||||||
HomieSetting<long> waterLevelMax("tankmax", "distance (mm) at maximum water level");
|
|
||||||
HomieSetting<long> waterLevelMin("tankmin", "distance (mm) at minimum water level (pumps still covered)");
|
|
||||||
HomieSetting<long> waterLevelWarn("tankwarn", "warn (mm) if below this water level %");
|
|
||||||
HomieSetting<long> waterLevelVol("tankVolume", "(ml) between minimum and maximum");
|
|
||||||
HomieSetting<const char *> lipoSensorAddr("lipoDSAddr", "1wire address for lipo temperature sensor");
|
|
||||||
HomieSetting<const char *> waterSensorAddr("tankDSAddr", "1wire address for water temperature sensor");
|
|
||||||
HomieSetting<const char *> ntpServer("ntpServer", "NTP server (pool.ntp.org as default)");
|
|
||||||
|
|
||||||
#if defined(TIMED_LIGHT_PIN)
|
|
||||||
HomieSetting<double> timedLightVoltageCutoff("LightVoltageCutoff", "voltage at wich to disable light");
|
|
||||||
HomieSetting<long> timedLightStart("LightStart", "hour to start light");
|
|
||||||
HomieSetting<long> timedLightEnd("LightEnd", "hour to end light");
|
|
||||||
HomieSetting<bool> timedLightOnlyWhenDark("LightOnlyDark", "only enable light, if solar is low");
|
|
||||||
HomieSetting<long> timedLightPowerLevel("LightPowerLevel", "0-255 power level");
|
|
||||||
#endif // TIMED_LIGHT_PIN
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @}
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @name Plant specific ones
|
|
||||||
* Setting for one plant
|
|
||||||
* @{
|
|
||||||
**/
|
|
||||||
|
|
||||||
#define GENERATE_PLANT(plant, strplant) \
|
|
||||||
HomieSetting<double> mSensorDry##plant = HomieSetting<double>("dry" strplant, "Plant" strplant " - Moist sensor dry %"); \
|
|
||||||
HomieSetting<long> mPumpAllowedHourRangeStart##plant = HomieSetting<long>("hourstart" strplant, "Plant" strplant " - Range pump allowed hour start (0-23)"); \
|
|
||||||
HomieSetting<long> mPumpAllowedHourRangeEnd##plant = HomieSetting<long>("hourend" strplant, "Plant" strplant " - Range pump allowed hour end (0-23)"); \
|
|
||||||
HomieSetting<bool> mPumpOnlyWhenLowLight##plant = HomieSetting<bool>("lowLight" strplant, "Plant" strplant " - Enable the Pump only, when there is no sunlight"); \
|
|
||||||
HomieSetting<long> mPumpCooldownInSeconds##plant = HomieSetting<long>("delay" strplant, "Plant" strplant " - How long to wait until the pump is activated again (sec)"); \
|
|
||||||
HomieSetting<long> pPumpDuration##plant = HomieSetting<long>("pumpDuration" strplant, "Plant" strplant " - time seconds to water when pump is active"); \
|
|
||||||
HomieSetting<long> pPumpMl##plant = HomieSetting<long>("pumpAmount" strplant, "Plant" strplant " - ml (if using flowmeter) to water when pump is active"); \
|
|
||||||
HomieSetting<long> pPowerLevel##plant = HomieSetting<long>("powerLevel" strplant, "Plant" strplant " - pwm duty cycle in percent"); \
|
|
||||||
PlantSettings_t mSetting##plant = {&mSensorDry##plant, &mPumpAllowedHourRangeStart##plant, &mPumpAllowedHourRangeEnd##plant, &mPumpOnlyWhenLowLight##plant, &mPumpCooldownInSeconds##plant, &pPumpDuration##plant, &pPowerLevel##plant, &pPumpMl##plant}; \
|
|
||||||
/**< Generate all settings for one plant \
|
|
||||||
* \
|
|
||||||
* Feature to start pumping only at morning: @link{SOLAR_CHARGE_MIN_VOLTAGE} and @link{SOLAR_CHARGE_MAX_VOLTAGE} \
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @}
|
|
||||||
*/
|
|
||||||
|
|
||||||
GENERATE_PLANT(0, "0"); /**< Homie settings for first plant */
|
|
||||||
GENERATE_PLANT(1, "1"); /**< Homie settings for second Plant */
|
|
||||||
GENERATE_PLANT(2, "2"); /**< Homie settings for third plant */
|
|
||||||
GENERATE_PLANT(3, "3"); /**< Homie settings for fourth plant */
|
|
||||||
GENERATE_PLANT(4, "4"); /**< Homie settings for fifth plant */
|
|
||||||
GENERATE_PLANT(5, "5"); /**< Homie settings for sixth plant */
|
|
||||||
GENERATE_PLANT(6, "6"); /**< Homie settings for seventh plant */
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif /* HOMIE_PLANT_CONFIG_H @} */
|
|
||||||
|
|
@ -1,95 +0,0 @@
|
|||||||
/**
|
|
||||||
* @file HomieTypes.h
|
|
||||||
* @author your name (you@domain.com)
|
|
||||||
* @brief
|
|
||||||
* @version 0.1
|
|
||||||
* @date 2020-10-16
|
|
||||||
*
|
|
||||||
* @copyright Copyright (c) 2020
|
|
||||||
* All Settings, configurable in Homie
|
|
||||||
*/
|
|
||||||
#ifndef HOMIE_PLANT_CFG_CONFIG_H
|
|
||||||
#define HOMIE_PLANT_CFG_CONFIG_H
|
|
||||||
|
|
||||||
#include <Homie.h>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @name Sensor types
|
|
||||||
* possible sensors:
|
|
||||||
* @{
|
|
||||||
**/
|
|
||||||
|
|
||||||
#define FOREACH_SENSOR(SENSOR) \
|
|
||||||
SENSOR(NONE) \
|
|
||||||
SENSOR(FREQUENCY_MOD_RESISTANCE_PROBE) \
|
|
||||||
SENSOR(ANALOG_RESISTANCE_PROBE)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @}
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define GENERATE_ENUM(ENUM) ENUM,
|
|
||||||
#define GENERATE_STRING(STRING) #STRING,
|
|
||||||
|
|
||||||
enum SENSOR_MODE {
|
|
||||||
FOREACH_SENSOR(GENERATE_ENUM)
|
|
||||||
};
|
|
||||||
|
|
||||||
static const char *SENSOR_STRING[] = {
|
|
||||||
FOREACH_SENSOR(GENERATE_STRING)
|
|
||||||
};
|
|
||||||
|
|
||||||
//plant pump is deactivated, but sensor values are still recorded and published
|
|
||||||
#define DEACTIVATED_PLANT -1
|
|
||||||
//special value to indicate a missing sensor when the plant is not deactivated but no valid sensor value was read
|
|
||||||
#define MISSING_SENSOR -2
|
|
||||||
//plant uses only cooldown and duration, moisture is measured but ignored, allowedHours is ignored (eg. make a 30min on 30min off cycle)
|
|
||||||
#define HYDROPONIC_MODE -3
|
|
||||||
//plant uses cooldown and duration and workhours, moisture is measured but ignored
|
|
||||||
#define TIMER_ONLY -4
|
|
||||||
//special value to indicate a shorted sensor when the plant is not deactivated but the sensor reads short circuit value
|
|
||||||
#define SHORT_CIRCUIT_MODE -5
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief State of plants
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
#define PLANTSTATE_NUM_DEACTIVATED -1
|
|
||||||
#define PLANTSTATE_NUM_NO_SENSOR -2
|
|
||||||
#define PLANTSTATE_NUM_WET 0x00
|
|
||||||
#define PLANTSTATE_NUM_SUNNY_ALARM 0x11
|
|
||||||
#define PLANTSTATE_NUM_ACTIVE_ALARM 0x41
|
|
||||||
#define PLANTSTATE_NUM_ACTIVE_SUPESSED -3
|
|
||||||
#define PLANTSTATE_NUM_ACTIVE 0x40
|
|
||||||
#define PLANTSTATE_NUM_SUNNY 0x10
|
|
||||||
#define PLANTSTATE_NUM_COOLDOWN_ALARM 0x21
|
|
||||||
#define PLANTSTATE_NUM_COOLDOWN 0x20
|
|
||||||
#define PLANTSTATE_NUM_AFTERWORK_ALARM 0x31
|
|
||||||
#define PLANTSTATE_NUM_AFTERWORK 0x30
|
|
||||||
|
|
||||||
#define PLANTSTATE_STR_DEACTIVATED "deactivated"
|
|
||||||
#define PLANTSTATE_STR_NO_SENSOR "nosensor"
|
|
||||||
#define PLANTSTATE_STR_WET "wet"
|
|
||||||
#define PLANTSTATE_STR_SUNNY_ALARM "sunny+alarm"
|
|
||||||
#define PLANTSTATE_STR_ACTIVE_ALARM "active+alarm"
|
|
||||||
#define PLANTSTATE_STR_ACTIVE_SUPESSED "active+supressed"
|
|
||||||
#define PLANTSTATE_STR_ACTIVE "active"
|
|
||||||
#define PLANTSTATE_STR_SUNNY "sunny"
|
|
||||||
#define PLANTSTATE_STR_COOLDOWN_ALARM "cooldown+alarm"
|
|
||||||
#define PLANTSTATE_STR_COOLDOWN "cooldown"
|
|
||||||
#define PLANTSTATE_STR_AFTERWORK_ALARM "after-work+alarm"
|
|
||||||
#define PLANTSTATE_STR_AFTERWORK "after-work"
|
|
||||||
|
|
||||||
typedef struct PlantSettings_t
|
|
||||||
{
|
|
||||||
HomieSetting<double> *pSensorDry;
|
|
||||||
HomieSetting<long> *pPumpAllowedHourRangeStart;
|
|
||||||
HomieSetting<long> *pPumpAllowedHourRangeEnd;
|
|
||||||
HomieSetting<bool> *pPumpOnlyWhenLowLight;
|
|
||||||
HomieSetting<long> *pPumpCooldownInSeconds;
|
|
||||||
HomieSetting<long> *pPumpDuration;
|
|
||||||
HomieSetting<long> *pPumpPowerLevel;
|
|
||||||
HomieSetting<long> *pPumpMl;
|
|
||||||
} PlantSettings_t;
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,50 +0,0 @@
|
|||||||
#ifndef LOG_DEFINES_H
|
|
||||||
#define LOG_DEFINES_H
|
|
||||||
|
|
||||||
#define LOG_LEVEL_ERROR 0
|
|
||||||
#define LOG_LEVEL_WARN 1
|
|
||||||
#define LOG_LEVEL_INFO 2
|
|
||||||
#define LOG_LEVEL_DEBUG 3
|
|
||||||
|
|
||||||
|
|
||||||
#define LOG_TANKSENSOR_FAIL_DETECT "Failed to detect and initialize distance sensor!"
|
|
||||||
#define LOG_TANKSENSOR_FAIL_DETECT_CODE -1
|
|
||||||
|
|
||||||
#define LOG_BACKUP_SUCCESSFUL "Backup sucessful"
|
|
||||||
#define LOG_BACKUP_SUCCESSFUL_CODE 1
|
|
||||||
|
|
||||||
#define LOG_BACKUP_FAILED "Backup error"
|
|
||||||
#define LOG_BACKUP_FAILED_CODE -2
|
|
||||||
|
|
||||||
#define LOG_PUMP_BUTNOTANK_MESSAGE "Want to pump but no water"
|
|
||||||
#define LOG_PUMP_BUTNOTANK_CODE -3
|
|
||||||
|
|
||||||
#define LOG_HARDWARECOUNTER_ERROR_MESSAGE "PCNR returned error"
|
|
||||||
#define LOG_HARDWARECOUNTER_ERROR_CODE -4
|
|
||||||
|
|
||||||
#define LOG_SENSORMODE_UNKNOWN "Unknown sensor mode requested"
|
|
||||||
#define LOG_SENSORMODE_UNKNOWN_CODE -5
|
|
||||||
|
|
||||||
#define LOG_SENSOR_MISSING -6
|
|
||||||
|
|
||||||
#define LOG_PUMP_AND_DOWNLOADMODE "Download mode, ignoring pump request"
|
|
||||||
#define LOG_PUMP_AND_DOWNLOADMODE_CODE 2
|
|
||||||
|
|
||||||
#define LOG_VERY_COLD_WATER "Water potentially frozen, ignoring pump request"
|
|
||||||
#define LOG_VERY_COLD_WATER_CODE -7
|
|
||||||
|
|
||||||
#define LOG_PUMP_FULLTANK_MESSAGE "Water Sensor distance unrealistic"
|
|
||||||
#define LOG_PUMP_FULLTANK_CODE 3
|
|
||||||
|
|
||||||
//msg is dynamic defined
|
|
||||||
#define LOG_PUMP_INEFFECTIVE -4
|
|
||||||
#define LOG_PUMP_STARTED_CODE 10
|
|
||||||
#define LOG_DEBUG_CODE 1001
|
|
||||||
#define LOG_SLEEP_NIGHT 100
|
|
||||||
#define LOG_SLEEP_DAY 101
|
|
||||||
#define LOG_SLEEP_LOWVOLTAGE 502
|
|
||||||
#define LOG_SLEEP_CYCLE 102
|
|
||||||
#define LOG_MISSING_PUMP -4
|
|
||||||
#define LOG_BOOT_ERROR_DETECTION 10000
|
|
||||||
#define LOG_SOLAR_CHARGER_MISSING 300
|
|
||||||
#endif
|
|
@ -1,28 +0,0 @@
|
|||||||
#ifndef MQTTUtils_h
|
|
||||||
#define MQTTUtils_h
|
|
||||||
|
|
||||||
#include <Homie.h>
|
|
||||||
|
|
||||||
#define LOG_TOPIC "log\0"
|
|
||||||
#define TEST_TOPIC "roundtrip\0"
|
|
||||||
#define BACKUP_TOPIC "$implementation/config/backup/set\0"
|
|
||||||
|
|
||||||
#define CONFIG_FILE "/homie/config.json"
|
|
||||||
#define CONFIG_FILE_BACKUP "/homie/config.json.bak"
|
|
||||||
|
|
||||||
#define getTopic(test, topic) \
|
|
||||||
char *topic = new char[strlen(Homie.getConfiguration().mqtt.baseTopic) + strlen(Homie.getConfiguration().deviceId) + 1 + strlen(test) + 1]; \
|
|
||||||
strcpy(topic, Homie.getConfiguration().mqtt.baseTopic); \
|
|
||||||
strcat(topic, Homie.getConfiguration().deviceId); \
|
|
||||||
strcat(topic, "/"); \
|
|
||||||
strcat(topic, test);
|
|
||||||
|
|
||||||
bool aliveWasRead();
|
|
||||||
bool mqttReady();
|
|
||||||
void startMQTTRoundtripTest();
|
|
||||||
|
|
||||||
void log(int level, String message, int code);
|
|
||||||
void mqttWrite(HomieNode* target,const char* key, String value);
|
|
||||||
void mqttWrite(HomieNode* target,String key, String value);
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,8 +0,0 @@
|
|||||||
#ifndef MATHUTILS_H
|
|
||||||
#define MATHUTILS_H
|
|
||||||
|
|
||||||
|
|
||||||
bool equalish(double x, double y);
|
|
||||||
double mapf(double x, double in_min, double in_max, double out_min, double out_max);
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,220 +0,0 @@
|
|||||||
/**
|
|
||||||
* @file PlantCtrl.h
|
|
||||||
* @author your name (you@domain.com)
|
|
||||||
* @brief Abstraction to handle the Sensors
|
|
||||||
* @version 0.1
|
|
||||||
* @date 2020-05-27
|
|
||||||
*
|
|
||||||
* @copyright Copyright (c) 2020
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef PLANT_CTRL_H
|
|
||||||
#define PLANT_CTRL_H
|
|
||||||
|
|
||||||
#include "HomieTypes.h"
|
|
||||||
#include <HomieNode.hpp>
|
|
||||||
#include "ControllerConfiguration.h"
|
|
||||||
#include "RunningMedian.h"
|
|
||||||
#include "MathUtils.h"
|
|
||||||
#include "MQTTUtils.h"
|
|
||||||
#include "LogDefines.h"
|
|
||||||
|
|
||||||
#define ANALOG_REREADS 5
|
|
||||||
#define MOISTURE_MEASUREMENT_DURATION 400 /** ms */
|
|
||||||
#define PWM_FREQ 50000
|
|
||||||
#define PWM_BITS 8
|
|
||||||
|
|
||||||
class Plant
|
|
||||||
{
|
|
||||||
|
|
||||||
private:
|
|
||||||
HomieNode *mPlant = NULL;
|
|
||||||
HomieInternals::PropertyInterface mPump;
|
|
||||||
RunningMedian mMoisture_raw = RunningMedian(ANALOG_REREADS);
|
|
||||||
RunningMedian mTemperature_degree = RunningMedian(ANALOG_REREADS);
|
|
||||||
int mPinSensor = 0; /**< Pin of the moist sensor */
|
|
||||||
int mPinPump = 0; /**< Pin of the pump */
|
|
||||||
bool mConnected = false;
|
|
||||||
int mPlantId = -1;
|
|
||||||
SENSOR_MODE mSensorMode;
|
|
||||||
|
|
||||||
|
|
||||||
public:
|
|
||||||
PlantSettings_t *mSetting;
|
|
||||||
/**
|
|
||||||
* @brief Construct a new Plant object
|
|
||||||
*
|
|
||||||
* @param pinSensor Pin of the Sensor to use to measure moist
|
|
||||||
* @param pinPump Pin of the Pump to use
|
|
||||||
*/
|
|
||||||
Plant(int pinSensor, int pinPump,
|
|
||||||
int plantId,
|
|
||||||
HomieNode *plant,
|
|
||||||
PlantSettings_t *setting, SENSOR_MODE mode);
|
|
||||||
|
|
||||||
void postMQTTconnection(void);
|
|
||||||
|
|
||||||
void advertise(void);
|
|
||||||
|
|
||||||
// for sensor that might take any time
|
|
||||||
void blockingMoistureMeasurement(void);
|
|
||||||
// for sensor that need a start and a end in defined timing
|
|
||||||
void startMoistureMeasurement(void);
|
|
||||||
void stopMoistureMeasurement(void);
|
|
||||||
|
|
||||||
void deactivatePump(void);
|
|
||||||
|
|
||||||
void activatePump(void);
|
|
||||||
|
|
||||||
String getSensorModeString()
|
|
||||||
{
|
|
||||||
SENSOR_MODE mode = getSensorMode();
|
|
||||||
return SENSOR_STRING[mode];
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isTimerOnly()
|
|
||||||
{
|
|
||||||
long current = this->mSetting->pSensorDry->get();
|
|
||||||
return equalish(current, TIMER_ONLY);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isHydroponic()
|
|
||||||
{
|
|
||||||
long current = this->mSetting->pSensorDry->get();
|
|
||||||
return equalish(current, HYDROPONIC_MODE);
|
|
||||||
}
|
|
||||||
|
|
||||||
SENSOR_MODE getSensorMode()
|
|
||||||
{
|
|
||||||
return mSensorMode;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Check if a plant is too dry and needs some water.
|
|
||||||
*
|
|
||||||
* @return true
|
|
||||||
* @return false
|
|
||||||
*/
|
|
||||||
bool isPumpRequired()
|
|
||||||
{
|
|
||||||
if (isHydroponic() || isTimerOnly())
|
|
||||||
{
|
|
||||||
// hydroponic only uses timer based controll
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
bool isDry = getCurrentMoisturePCT() < getTargetMoisturePCT();
|
|
||||||
bool isActive = isPumpTriggerActive();
|
|
||||||
return isDry && isActive;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isPumpTriggerActive()
|
|
||||||
{
|
|
||||||
long current = this->mSetting->pSensorDry->get();
|
|
||||||
return !equalish(current, DEACTIVATED_PLANT);
|
|
||||||
}
|
|
||||||
|
|
||||||
float getTargetMoisturePCT()
|
|
||||||
{
|
|
||||||
if (isPumpTriggerActive())
|
|
||||||
{
|
|
||||||
return this->mSetting->pSensorDry->get();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return DEACTIVATED_PLANT;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
float getCurrentMoisturePCT()
|
|
||||||
{
|
|
||||||
switch (getSensorMode())
|
|
||||||
{
|
|
||||||
case NONE:
|
|
||||||
return DEACTIVATED_PLANT;
|
|
||||||
case FREQUENCY_MOD_RESISTANCE_PROBE:
|
|
||||||
return mapf(mMoisture_raw.getMedian(), MOIST_SENSOR_MIN_FRQ, MOIST_SENSOR_MAX_FRQ, 0, 100);
|
|
||||||
case ANALOG_RESISTANCE_PROBE:
|
|
||||||
return mapf(mMoisture_raw.getMedian(), ANALOG_SENSOR_MAX_MV, ANALOG_SENSOR_MIN_MV, 0, 100);
|
|
||||||
}
|
|
||||||
return MISSING_SENSOR;
|
|
||||||
}
|
|
||||||
|
|
||||||
float getCurrentMoistureRaw()
|
|
||||||
{
|
|
||||||
if (getSensorMode() == FREQUENCY_MOD_RESISTANCE_PROBE)
|
|
||||||
{
|
|
||||||
if (mMoisture_raw.getMedian() < MOIST_SENSOR_MIN_FRQ)
|
|
||||||
{
|
|
||||||
return MISSING_SENSOR;
|
|
||||||
}
|
|
||||||
else if (mMoisture_raw.getMedian() > MOIST_SENSOR_MAX_FRQ)
|
|
||||||
{
|
|
||||||
return SHORT_CIRCUIT_MODE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return mMoisture_raw.getMedian();
|
|
||||||
}
|
|
||||||
|
|
||||||
HomieInternals::SendingPromise &setProperty(const String &property) const
|
|
||||||
{
|
|
||||||
return mPlant->setProperty(property);
|
|
||||||
}
|
|
||||||
|
|
||||||
void init(void);
|
|
||||||
void initSensors(void);
|
|
||||||
|
|
||||||
long getCooldownInSeconds()
|
|
||||||
{
|
|
||||||
return this->mSetting->pPumpCooldownInSeconds->get();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get the Hours when pumping should start
|
|
||||||
*
|
|
||||||
* @return hour
|
|
||||||
*/
|
|
||||||
int getHoursStart()
|
|
||||||
{
|
|
||||||
return this->mSetting->pPumpAllowedHourRangeStart->get();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Get the Hours when pumping should end
|
|
||||||
*
|
|
||||||
* @return hour
|
|
||||||
*/
|
|
||||||
int getHoursEnd()
|
|
||||||
{
|
|
||||||
return this->mSetting->pPumpAllowedHourRangeEnd->get();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isAllowedOnlyAtLowLight(void)
|
|
||||||
{
|
|
||||||
if (this->isHydroponic())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return this->mSetting->pPumpOnlyWhenLowLight->get();
|
|
||||||
}
|
|
||||||
|
|
||||||
void publishState(int stateNumber, String stateString);
|
|
||||||
|
|
||||||
bool switchHandler(const HomieRange &range, const String &value);
|
|
||||||
|
|
||||||
void setSwitchHandler(HomieInternals::PropertyInputHandler f);
|
|
||||||
|
|
||||||
long getPumpDuration()
|
|
||||||
{
|
|
||||||
return this->mSetting->pPumpDuration->get();
|
|
||||||
}
|
|
||||||
long getPumpMl()
|
|
||||||
{
|
|
||||||
return this->mSetting->pPumpMl->get();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,39 +0,0 @@
|
|||||||
|
|
||||||
This directory is intended for project header files.
|
|
||||||
|
|
||||||
A header file is a file containing C declarations and macro definitions
|
|
||||||
to be shared between several project source files. You request the use of a
|
|
||||||
header file in your project source file (C, C++, etc) located in `src` folder
|
|
||||||
by including it, with the C preprocessing directive `#include'.
|
|
||||||
|
|
||||||
```src/main.c
|
|
||||||
|
|
||||||
#include "header.h"
|
|
||||||
|
|
||||||
int main (void)
|
|
||||||
{
|
|
||||||
...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Including a header file produces the same results as copying the header file
|
|
||||||
into each source file that needs it. Such copying would be time-consuming
|
|
||||||
and error-prone. With a header file, the related declarations appear
|
|
||||||
in only one place. If they need to be changed, they can be changed in one
|
|
||||||
place, and programs that include the header file will automatically use the
|
|
||||||
new version when next recompiled. The header file eliminates the labor of
|
|
||||||
finding and changing all the copies as well as the risk that a failure to
|
|
||||||
find one copy will result in inconsistencies within a program.
|
|
||||||
|
|
||||||
In C, the usual convention is to give header files names that end with `.h'.
|
|
||||||
It is most portable to use only letters, digits, dashes, and underscores in
|
|
||||||
header file names, and at most one dot.
|
|
||||||
|
|
||||||
Read more about using header files in official GCC documentation:
|
|
||||||
|
|
||||||
* Include Syntax
|
|
||||||
* Include Operation
|
|
||||||
* Once-Only Headers
|
|
||||||
* Computed Includes
|
|
||||||
|
|
||||||
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html
|
|
@ -1,75 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
//
|
|
||||||
// FILE: RunningMedian.h
|
|
||||||
// AUTHOR: Rob dot Tillaart at gmail dot com
|
|
||||||
// PURPOSE: RunningMedian library for Arduino
|
|
||||||
// VERSION: 0.2.1
|
|
||||||
// URL: https://github.com/RobTillaart/RunningMedian
|
|
||||||
// URL: http://arduino.cc/playground/Main/RunningMedian
|
|
||||||
// HISTORY: See RunningMedian.cpp
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "Arduino.h"
|
|
||||||
|
|
||||||
#define RUNNING_MEDIAN_VERSION "0.2.1"
|
|
||||||
|
|
||||||
// prepare for dynamic version
|
|
||||||
// not tested ==> use at own risk :)
|
|
||||||
// #define RUNNING_MEDIAN_USE_MALLOC
|
|
||||||
|
|
||||||
// should at least be 5 to be practical,
|
|
||||||
// odd sizes results in a 'real' middle element and will be a bit faster.
|
|
||||||
// even sizes takes the average of the two middle elements as median
|
|
||||||
#define MEDIAN_MIN_SIZE 5
|
|
||||||
#define MEDIAN_MAX_SIZE 19
|
|
||||||
|
|
||||||
class RunningMedian
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
// # elements in the internal buffer
|
|
||||||
explicit RunningMedian(const uint8_t size);
|
|
||||||
~RunningMedian();
|
|
||||||
|
|
||||||
// resets internal buffer and var
|
|
||||||
void clear();
|
|
||||||
// adds a new value to internal buffer, optionally replacing the oldest element.
|
|
||||||
void add(const float value);
|
|
||||||
// returns the median == middle element
|
|
||||||
float getMedian();
|
|
||||||
|
|
||||||
// returns average of the values in the internal buffer
|
|
||||||
float getAverage();
|
|
||||||
// returns average of the middle nMedian values, removes noise from outliers
|
|
||||||
float getAverage(uint8_t nMedian);
|
|
||||||
|
|
||||||
float getHighest() { return getSortedElement(_cnt - 1); };
|
|
||||||
float getLowest() { return getSortedElement(0); };
|
|
||||||
|
|
||||||
// get n'th element from the values in time order
|
|
||||||
float getElement(const uint8_t n);
|
|
||||||
// get n'th element from the values in size order
|
|
||||||
float getSortedElement(const uint8_t n);
|
|
||||||
// predict the max change of median after n additions
|
|
||||||
float predict(const uint8_t n);
|
|
||||||
|
|
||||||
uint8_t getSize() { return _size; };
|
|
||||||
// returns current used elements, getCount() <= getSize()
|
|
||||||
uint8_t getCount() { return _cnt; };
|
|
||||||
|
|
||||||
protected:
|
|
||||||
boolean _sorted;
|
|
||||||
uint8_t _size;
|
|
||||||
uint8_t _cnt;
|
|
||||||
uint8_t _idx;
|
|
||||||
|
|
||||||
#ifdef RUNNING_MEDIAN_USE_MALLOC
|
|
||||||
float *_ar;
|
|
||||||
uint8_t *_p;
|
|
||||||
#else
|
|
||||||
float _ar[MEDIAN_MAX_SIZE];
|
|
||||||
uint8_t _p[MEDIAN_MAX_SIZE];
|
|
||||||
#endif
|
|
||||||
void sort();
|
|
||||||
};
|
|
||||||
|
|
||||||
// END OF FILE
|
|
@ -1,4 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
long getCurrentTime(void);
|
|
||||||
int getCurrentHour(void);
|
|
@ -1,27 +0,0 @@
|
|||||||
/**
|
|
||||||
* @file WakeReason.h
|
|
||||||
* @author your name (you@domain.com)
|
|
||||||
* @brief
|
|
||||||
* @version 0.1
|
|
||||||
* @date 2020-11-28
|
|
||||||
*
|
|
||||||
* @copyright Copyright (c) 2020
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
#ifndef WAKEUP_REASON_H
|
|
||||||
#define WAKEUP_REASON_H
|
|
||||||
|
|
||||||
#define WAKEUP_REASON_UNDEFINED 0
|
|
||||||
#define WAKEUP_REASON_TEMP1_CHANGE 2
|
|
||||||
#define WAKEUP_REASON_TEMP2_CHANGE 3
|
|
||||||
#define WAKEUP_REASON_BATTERY_CHANGE 4
|
|
||||||
#define WAKEUP_REASON_SOLAR_CHANGE 5
|
|
||||||
#define WAKEUP_REASON_RTC_MISSING 6
|
|
||||||
#define WAKEUP_REASON_TIME_UNSET 7
|
|
||||||
#define WAKEUP_REASON_MODE2_WAKEUP_TIMER 8
|
|
||||||
|
|
||||||
|
|
||||||
#define WAKEUP_REASON_MOIST_CHANGE 20 /**< <code>20-26</code> for plant0 to plant9 */
|
|
||||||
#define WAKEUP_REASON_PLANT_DRY 30 /**< <code>30-36</code> for plant0 to plant9 */
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,177 +0,0 @@
|
|||||||
#ifndef ULP_PWM_h
|
|
||||||
#define ILP_PWM_h
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
|
||||||
#include "driver/rtc_io.h"
|
|
||||||
#include "soc/rtc_cntl_reg.h"
|
|
||||||
#include "soc/rtc.h"
|
|
||||||
#include "esp32/ulp.h"
|
|
||||||
#include "ControllerConfiguration.h"
|
|
||||||
|
|
||||||
#define LBL_START 1
|
|
||||||
#define LBL_DELAY_ON 2
|
|
||||||
#define LBL_DELAY_OFF 3
|
|
||||||
#define LBL_SKIP_ON 4
|
|
||||||
#define LBL_SKIP_OFF 5
|
|
||||||
#define REGISTER_DELAY_LOOP_COUNTER R0
|
|
||||||
#define REGISTER_TICKS_ON R1
|
|
||||||
#define REGISTER_TICKS_OFF R2
|
|
||||||
#define TOTAL_TICKS_DELAY 255
|
|
||||||
#define PIN TIMED_LIGHT_PIN
|
|
||||||
|
|
||||||
//support 20 vars
|
|
||||||
const size_t ulp_var_offset = CONFIG_ULP_COPROC_RESERVE_MEM - 20;
|
|
||||||
//use the first for dimming
|
|
||||||
const size_t ulp_dimm_offset = ulp_var_offset + 1;
|
|
||||||
const size_t ulp_alive_offset = ulp_var_offset + 2;
|
|
||||||
|
|
||||||
//see https://github.com/perseus086/ESP32-notes
|
|
||||||
const uint32_t rtc_bit[40] = {
|
|
||||||
25, //gpio0
|
|
||||||
0, //gpio1
|
|
||||||
26, //gpio2
|
|
||||||
0, //gpio3
|
|
||||||
24, //gpio4
|
|
||||||
0, //gpio5
|
|
||||||
0, //gpio6
|
|
||||||
0, //gpio7
|
|
||||||
0, //gpio8
|
|
||||||
0, //gpio9
|
|
||||||
0, //gpio10
|
|
||||||
0, //gpio11
|
|
||||||
29, //gpio12
|
|
||||||
28, //gpio13
|
|
||||||
30, //gpio14
|
|
||||||
27, //gpio15
|
|
||||||
0, //gpio16
|
|
||||||
31, //gpio17
|
|
||||||
0, //gpio18
|
|
||||||
0, //gpio19
|
|
||||||
0, //gpio20
|
|
||||||
0, //gpio21
|
|
||||||
0, //gpio22
|
|
||||||
0, //gpio23
|
|
||||||
0, //gpio24
|
|
||||||
20, //gpio25
|
|
||||||
21, //gpio26
|
|
||||||
0, //gpio27
|
|
||||||
0, //gpio28
|
|
||||||
0, //gpio29
|
|
||||||
0, //gpio30
|
|
||||||
0, //gpio31
|
|
||||||
23, //gpio32
|
|
||||||
22, //gpio33
|
|
||||||
18, //gpio34
|
|
||||||
19, //gpio35
|
|
||||||
14, //gpio36
|
|
||||||
15, //gpio37
|
|
||||||
16, //gpio38
|
|
||||||
17 //gpio39
|
|
||||||
};
|
|
||||||
|
|
||||||
static inline void ulp_internal_data_write(size_t offset, uint16_t value)
|
|
||||||
{
|
|
||||||
if (offset >= CONFIG_ULP_COPROC_RESERVE_MEM || offset <= ulp_var_offset)
|
|
||||||
{
|
|
||||||
Serial.print("Invalid ULP offset detected, refusing write!");
|
|
||||||
Serial.print(offset);
|
|
||||||
Serial.print("-");
|
|
||||||
Serial.print(ulp_var_offset);
|
|
||||||
Serial.print("-");
|
|
||||||
Serial.println(CONFIG_ULP_COPROC_RESERVE_MEM);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
RTC_SLOW_MEM[offset] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline uint16_t ulp_internal_data_read(size_t offset)
|
|
||||||
{
|
|
||||||
if (offset >= CONFIG_ULP_COPROC_RESERVE_MEM || offset <= ulp_var_offset)
|
|
||||||
{
|
|
||||||
Serial.print("Invalid ULP offset detected");
|
|
||||||
Serial.print(offset);
|
|
||||||
Serial.print("-");
|
|
||||||
Serial.print(ulp_var_offset);
|
|
||||||
Serial.print("-");
|
|
||||||
Serial.println(CONFIG_ULP_COPROC_RESERVE_MEM);
|
|
||||||
}
|
|
||||||
return RTC_SLOW_MEM[offset] & 0xffff;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ulp_internal_start(void)
|
|
||||||
{
|
|
||||||
rtc_gpio_init(PIN);
|
|
||||||
rtc_gpio_set_direction(PIN, RTC_GPIO_MODE_OUTPUT_ONLY);
|
|
||||||
rtc_gpio_set_level(PIN, 0);
|
|
||||||
const uint32_t rtc_gpio = rtc_io_number_get(PIN);
|
|
||||||
|
|
||||||
// Define ULP program
|
|
||||||
const ulp_insn_t ulp_prog[] = {
|
|
||||||
M_LABEL(LBL_START),
|
|
||||||
|
|
||||||
I_MOVI(REGISTER_DELAY_LOOP_COUNTER, 1),
|
|
||||||
I_MOVI(REGISTER_TICKS_ON, 0),
|
|
||||||
I_ST(REGISTER_DELAY_LOOP_COUNTER, REGISTER_TICKS_ON, ulp_alive_offset), //store 1 with 0 offset into alive
|
|
||||||
|
|
||||||
I_LD(REGISTER_TICKS_ON, REGISTER_TICKS_ON, ulp_dimm_offset), //REGISTER_TICKS_ON = RTC_DATA[0+ulp_dimm_offset]
|
|
||||||
//in total there is always 255 delay loop iterations, but in different duty cycle
|
|
||||||
I_MOVI(REGISTER_TICKS_OFF, TOTAL_TICKS_DELAY),
|
|
||||||
I_SUBR(REGISTER_TICKS_OFF, REGISTER_TICKS_OFF, REGISTER_TICKS_ON),
|
|
||||||
|
|
||||||
//on phase
|
|
||||||
I_MOVR(REGISTER_DELAY_LOOP_COUNTER, REGISTER_TICKS_ON),
|
|
||||||
M_BL(LBL_SKIP_ON, 1), //if never on, skip on phase
|
|
||||||
I_WR_REG(RTC_GPIO_OUT_REG, rtc_gpio, rtc_gpio, HIGH), // on
|
|
||||||
M_LABEL(LBL_DELAY_ON),
|
|
||||||
I_DELAY(1), //wait 1 clock
|
|
||||||
I_SUBI(REGISTER_DELAY_LOOP_COUNTER, REGISTER_DELAY_LOOP_COUNTER, 1), // REGISTER_DELAY_LOOP_COUNTER--
|
|
||||||
M_BGE(LBL_DELAY_ON, 1), //if time left, goto start of on loop
|
|
||||||
M_LABEL(LBL_SKIP_ON),
|
|
||||||
|
|
||||||
//off phase
|
|
||||||
I_MOVR(REGISTER_DELAY_LOOP_COUNTER, REGISTER_TICKS_OFF),
|
|
||||||
|
|
||||||
M_BL(LBL_SKIP_OFF, 1), //if never off, skip on phase
|
|
||||||
I_WR_REG(RTC_GPIO_OUT_REG, rtc_gpio, rtc_gpio, LOW), // on
|
|
||||||
M_LABEL(3),
|
|
||||||
I_DELAY(1), //wait 1 clock
|
|
||||||
I_SUBI(REGISTER_DELAY_LOOP_COUNTER, REGISTER_DELAY_LOOP_COUNTER, 1), // REGISTER_DELAY_LOOP_COUNTER--
|
|
||||||
M_BGE(3, 1), //if time left, goto start of on loop
|
|
||||||
M_LABEL(LBL_SKIP_OFF),
|
|
||||||
|
|
||||||
M_BX(LBL_START),
|
|
||||||
};
|
|
||||||
// Run ULP program
|
|
||||||
size_t size = sizeof(ulp_prog) / sizeof(ulp_insn_t);
|
|
||||||
assert(size < ulp_var_offset && "ULP_DATA_OFFSET needs to be greater or equal to the program size");
|
|
||||||
esp_err_t error = ulp_process_macros_and_load(0, ulp_prog, &size);
|
|
||||||
Serial.print("ULP bootstrap status ");
|
|
||||||
Serial.println(error);
|
|
||||||
|
|
||||||
//allow glitchless start
|
|
||||||
ulp_internal_data_write(ulp_alive_offset, 0);
|
|
||||||
|
|
||||||
error = ulp_run(0);
|
|
||||||
Serial.print("ULP start status ");
|
|
||||||
Serial.println(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void ulp_pwm_set_level(uint8_t level)
|
|
||||||
{
|
|
||||||
ulp_internal_data_write(ulp_dimm_offset, level);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void ulp_pwm_init()
|
|
||||||
{
|
|
||||||
ulp_internal_data_write(ulp_alive_offset, 0);
|
|
||||||
delay(10);
|
|
||||||
if (ulp_internal_data_read(ulp_alive_offset) == 0)
|
|
||||||
{
|
|
||||||
ulp_internal_start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,46 +0,0 @@
|
|||||||
|
|
||||||
This directory is intended for project specific (private) libraries.
|
|
||||||
PlatformIO will compile them to static libraries and link into executable file.
|
|
||||||
|
|
||||||
The source code of each library should be placed in a an own separate directory
|
|
||||||
("lib/your_library_name/[here are source files]").
|
|
||||||
|
|
||||||
For example, see a structure of the following two libraries `Foo` and `Bar`:
|
|
||||||
|
|
||||||
|--lib
|
|
||||||
| |
|
|
||||||
| |--Bar
|
|
||||||
| | |--docs
|
|
||||||
| | |--examples
|
|
||||||
| | |--src
|
|
||||||
| | |- Bar.c
|
|
||||||
| | |- Bar.h
|
|
||||||
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
|
|
||||||
| |
|
|
||||||
| |--Foo
|
|
||||||
| | |- Foo.c
|
|
||||||
| | |- Foo.h
|
|
||||||
| |
|
|
||||||
| |- README --> THIS FILE
|
|
||||||
|
|
|
||||||
|- platformio.ini
|
|
||||||
|--src
|
|
||||||
|- main.c
|
|
||||||
|
|
||||||
and a contents of `src/main.c`:
|
|
||||||
```
|
|
||||||
#include <Foo.h>
|
|
||||||
#include <Bar.h>
|
|
||||||
|
|
||||||
int main (void)
|
|
||||||
{
|
|
||||||
...
|
|
||||||
}
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
PlatformIO Library Dependency Finder will find automatically dependent
|
|
||||||
libraries scanning project source files.
|
|
||||||
|
|
||||||
More information about PlatformIO Library Dependency Finder
|
|
||||||
- https://docs.platformio.org/page/librarymanager/ldf.html
|
|
@ -1,40 +0,0 @@
|
|||||||
; PlatformIO Project Configuration File
|
|
||||||
;
|
|
||||||
; Build options: build flags, source filter
|
|
||||||
; Upload options: custom upload port, speed and extra flags
|
|
||||||
; Library options: dependencies, extra library storages
|
|
||||||
; Advanced options: extra scripting
|
|
||||||
;
|
|
||||||
; Please visit documentation for the other options and examples
|
|
||||||
; https://docs.platformio.org/page/projectconf.html
|
|
||||||
|
|
||||||
[env:esp32doit-devkit-v1]
|
|
||||||
platform = espressif32@6.3.2
|
|
||||||
board = esp32doit-devkit-v1
|
|
||||||
framework = arduino
|
|
||||||
build_flags = -DPIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY
|
|
||||||
-DPLANT0_SENSORTYPE=FREQUENCY_MOD_RESISTANCE_PROBE
|
|
||||||
-DPLANT1_SENSORTYPE=FREQUENCY_MOD_RESISTANCE_PROBE
|
|
||||||
-DPLANT2_SENSORTYPE=FREQUENCY_MOD_RESISTANCE_PROBE
|
|
||||||
-DPLANT3_SENSORTYPE=FREQUENCY_MOD_RESISTANCE_PROBE
|
|
||||||
-DPLANT4_SENSORTYPE=FREQUENCY_MOD_RESISTANCE_PROBE
|
|
||||||
-DPLANT5_SENSORTYPE=FREQUENCY_MOD_RESISTANCE_PROBE
|
|
||||||
-DPLANT6_SENSORTYPE=FREQUENCY_MOD_RESISTANCE_PROBE
|
|
||||||
-DTIMED_LIGHT_PIN=CUSTOM1_PIN5
|
|
||||||
-DFLOWMETER_PIN=CUSTOM1_PIN1
|
|
||||||
|
|
||||||
board_build.partitions = defaultWithSmallerSpiffs.csv
|
|
||||||
|
|
||||||
;#https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/partition-tables.html
|
|
||||||
|
|
||||||
|
|
||||||
; the latest development brankitchen-lightch (convention V3.0.x)
|
|
||||||
lib_deps = bblanchon/ArduinoJson@^6.20.1
|
|
||||||
paulstoffregen/OneWire@^2.3.6
|
|
||||||
milesburton/DallasTemperature@^3.11.0
|
|
||||||
pololu/VL53L0X@^1.3.1
|
|
||||||
https://github.com/homieiot/homie-esp8266.git#develop
|
|
||||||
|
|
||||||
[platformio]
|
|
||||||
|
|
||||||
extra_configs = custom_platformio.ini
|
|
File diff suppressed because it is too large
Load Diff
@ -1,6 +0,0 @@
|
|||||||
# This file was automatically generated for projects
|
|
||||||
# without default 'CMakeLists.txt' file.
|
|
||||||
|
|
||||||
FILE(GLOB_RECURSE app_sources ${CMAKE_SOURCE_DIR}/src/*.*)
|
|
||||||
|
|
||||||
idf_component_register(SRCS ${app_sources})
|
|
@ -1,272 +0,0 @@
|
|||||||
/*
|
|
||||||
* DS2438.cpp
|
|
||||||
*
|
|
||||||
* by Joe Bechter
|
|
||||||
*
|
|
||||||
* (C) 2012, bechter.com
|
|
||||||
*
|
|
||||||
* All files, software, schematics and designs are provided as-is with no warranty.
|
|
||||||
* All files, software, schematics and designs are for experimental/hobby use.
|
|
||||||
* Under no circumstances should any part be used for critical systems where safety,
|
|
||||||
* life or property depends upon it. You are responsible for all use.
|
|
||||||
* You are free to use, modify, derive or otherwise extend for your own non-commercial purposes provided
|
|
||||||
* 1. No part of this software or design may be used to cause injury or death to humans or animals.
|
|
||||||
* 2. Use is non-commercial.
|
|
||||||
* 3. Credit is given to the author (i.e. portions © bechter.com), and provide a link to the original source.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "DS2438.h"
|
|
||||||
|
|
||||||
// DSROM FIELDS
|
|
||||||
#define DSROM_FAMILY 0
|
|
||||||
#define DSROM_CRC 7
|
|
||||||
|
|
||||||
#define DS2438MODEL 0x26
|
|
||||||
|
|
||||||
DS2438::DS2438(OneWire *ow, float currentShunt, int retryOnCRCError) {
|
|
||||||
_ow = ow;
|
|
||||||
_currentShunt = currentShunt;
|
|
||||||
_retryOnCRCError = retryOnCRCError;
|
|
||||||
};
|
|
||||||
|
|
||||||
void DS2438::begin(){
|
|
||||||
DeviceAddress searchDeviceAddress;
|
|
||||||
|
|
||||||
_ow->reset_search();
|
|
||||||
memset(searchDeviceAddress,0, 8);
|
|
||||||
_temperature.clear();
|
|
||||||
_voltageA.clear();
|
|
||||||
_voltageB.clear();
|
|
||||||
_error = true;
|
|
||||||
_mode = (DS2438_MODE_CHA | DS2438_MODE_CHB | DS2438_MODE_TEMPERATURE);
|
|
||||||
|
|
||||||
deviceFound = false; // Reset the number of devices when we enumerate wire devices
|
|
||||||
|
|
||||||
while (_ow->search(searchDeviceAddress)) {
|
|
||||||
if (validAddress(searchDeviceAddress)) {
|
|
||||||
if (validFamily(searchDeviceAddress)) {
|
|
||||||
memcpy(_address,searchDeviceAddress,8);
|
|
||||||
DEFAULT_PAGE0(defaultConfig);
|
|
||||||
writePage(0, defaultConfig);
|
|
||||||
deviceFound = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DS2438::isFound(){
|
|
||||||
return deviceFound;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DS2438::validAddress(const uint8_t* deviceAddress) {
|
|
||||||
return (_ow->crc8(deviceAddress, 7) == deviceAddress[DSROM_CRC]);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DS2438::validFamily(const uint8_t* deviceAddress) {
|
|
||||||
switch (deviceAddress[DSROM_FAMILY]) {
|
|
||||||
case DS2438MODEL:
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DS2438::updateMultiple(){
|
|
||||||
for(int i = 0;i< DS2438_MEDIAN_COUNT; i++){
|
|
||||||
update(i==0);
|
|
||||||
if(_error){
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
delay(DS2438_MEDIAN_DELAY);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DS2438::update(bool firstIteration) {
|
|
||||||
uint8_t data[9];
|
|
||||||
_error = true;
|
|
||||||
|
|
||||||
if(!isFound()){
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_mode & DS2438_MODE_CHA || _mode == DS2438_MODE_TEMPERATURE) {
|
|
||||||
boolean doTemperature = _mode & DS2438_MODE_TEMPERATURE;
|
|
||||||
if (!startConversion(DS2438_CHA, doTemperature)) {
|
|
||||||
Serial.println("Error starting temp conversion ds2438 channel a");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!readPage(0, data)){
|
|
||||||
|
|
||||||
Serial.println("Error reading zero page ds2438 channel a");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (doTemperature) {
|
|
||||||
_temperature.add((double)(((((int16_t)data[2]) << 8) | (data[1] & 0x0ff)) >> 3) * 0.03125);
|
|
||||||
}
|
|
||||||
if (_mode & DS2438_MODE_CHA) {
|
|
||||||
_voltageA.add((((data[4] << 8) & 0x00300) | (data[3] & 0x0ff)) / 100.0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (_mode & DS2438_MODE_CHB) {
|
|
||||||
boolean doTemperature = _mode & DS2438_MODE_TEMPERATURE && !(_mode & DS2438_MODE_CHA);
|
|
||||||
if (!startConversion(DS2438_CHB, doTemperature)) {
|
|
||||||
Serial.println("Error starting temp conversion channel b ds2438");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!readPage(0, data)){
|
|
||||||
Serial.println("Error reading zero page ds2438 channel b");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (doTemperature) {
|
|
||||||
int16_t upperByte = ((int16_t)data[2]) << 8;
|
|
||||||
int16_t lowerByte = data[1] >> 3;
|
|
||||||
int16_t fullByte = (upperByte | lowerByte);
|
|
||||||
_temperature.add(((double)fullByte) * 0.03125);
|
|
||||||
}
|
|
||||||
_voltageB.add((((data[4] << 8) & 0x00300) | (data[3] & 0x0ff)) / 100.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
int16_t upperByte = ((int16_t)data[6]) << 8;
|
|
||||||
int16_t lowerByte = data[5];
|
|
||||||
int16_t fullByte = (int16_t)(upperByte | lowerByte);
|
|
||||||
float fullByteb = fullByte;
|
|
||||||
_current.add((fullByteb) / ((4096.0f * _currentShunt)));
|
|
||||||
|
|
||||||
|
|
||||||
if(firstIteration){
|
|
||||||
if (readPage(1, data)){
|
|
||||||
PageOne_t *pOne = (PageOne_t *) data;
|
|
||||||
_ICA = pOne->ICA;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (readPage(7, data)){
|
|
||||||
PageSeven_t *pSeven = (PageSeven_t *) data;
|
|
||||||
_CCA = pSeven->CCA0 | ((int16_t) pSeven->CCA1) << 8;
|
|
||||||
_DCA = pSeven->DCA0 | ((int16_t) pSeven->DCA1) << 8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_error = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
double DS2438::getTemperature() {
|
|
||||||
return _temperature.getMedian();
|
|
||||||
}
|
|
||||||
|
|
||||||
float DS2438::getAh(){
|
|
||||||
return _ICA / (2048.0f * _currentShunt);
|
|
||||||
}
|
|
||||||
|
|
||||||
long DS2438::getICA(){
|
|
||||||
return _ICA;
|
|
||||||
}
|
|
||||||
|
|
||||||
long DS2438::getDCA(){
|
|
||||||
return _DCA;
|
|
||||||
}
|
|
||||||
|
|
||||||
long DS2438::getCCA(){
|
|
||||||
return _CCA;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
float DS2438::getVoltage(int channel) {
|
|
||||||
if (channel == DS2438_CHA) {
|
|
||||||
return _voltageA.getMedian();
|
|
||||||
} else if (channel == DS2438_CHB) {
|
|
||||||
return _voltageB.getMedian();
|
|
||||||
} else {
|
|
||||||
return 0.0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
float DS2438::getCurrent() {
|
|
||||||
return _current.getMedian();
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean DS2438::isError() {
|
|
||||||
return _error;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean DS2438::startConversion(int channel, boolean doTemperature) {
|
|
||||||
if(!isFound()){
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!selectChannel(channel)){
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
_ow->reset();
|
|
||||||
_ow->select(_address);
|
|
||||||
if (doTemperature) {
|
|
||||||
_ow->write(DS2438_TEMPERATURE_CONVERSION_COMMAND, 0);
|
|
||||||
delay(DS2438_TEMPERATURE_DELAY);
|
|
||||||
_ow->reset();
|
|
||||||
_ow->select(_address);
|
|
||||||
}
|
|
||||||
_ow->write(DS2438_VOLTAGE_CONVERSION_COMMAND, 0);
|
|
||||||
delay(DS2438_VOLTAGE_CONVERSION_DELAY);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean DS2438::selectChannel(int channel) {
|
|
||||||
if(!isFound()){
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
uint8_t data[9];
|
|
||||||
if (readPage(0, data)) {
|
|
||||||
if (channel == DS2438_CHB){
|
|
||||||
data[0] = data[0] | 0x08;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
data[0] = data[0] & 0xf7;
|
|
||||||
}
|
|
||||||
writePage(0, data);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
Serial.println("Could not read page zero data");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DS2438::writePage(int page, uint8_t *data) {
|
|
||||||
_ow->reset();
|
|
||||||
_ow->select(_address);
|
|
||||||
_ow->write(DS2438_WRITE_SCRATCHPAD_COMMAND, 0);
|
|
||||||
if ((page >= PAGE_MIN) && (page <= PAGE_MAX)) {
|
|
||||||
_ow->write(page, 0);
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (int i = 0; i < 8; i++){
|
|
||||||
_ow->write(data[i], 0);
|
|
||||||
}
|
|
||||||
_ow->reset();
|
|
||||||
_ow->select(_address);
|
|
||||||
_ow->write(DS2438_COPY_SCRATCHPAD_COMMAND, 0);
|
|
||||||
_ow->write(page, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean DS2438::readPage(int page, uint8_t *data) {
|
|
||||||
bool valid = false;
|
|
||||||
for(int retry = 0;retry < this->_retryOnCRCError && !valid; retry ++){
|
|
||||||
//TODO if all data is 0 0 is a valid crc, but most likly not as intended
|
|
||||||
_ow->reset();
|
|
||||||
_ow->select(_address);
|
|
||||||
_ow->write(DS2438_RECALL_MEMORY_COMMAND, 0);
|
|
||||||
if ((page >= PAGE_MIN) && (page <= PAGE_MAX)) {
|
|
||||||
_ow->write(page, 0);
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
_ow->reset();
|
|
||||||
_ow->select(_address);
|
|
||||||
_ow->write(DS2438_READ_SCRATCHPAD_COMMAND, 0);
|
|
||||||
_ow->write(page, 0);
|
|
||||||
for (int i = 0; i < 9; i++){
|
|
||||||
data[i] = _ow->read();
|
|
||||||
}
|
|
||||||
valid = _ow->crc8(data, 8) == data[8];
|
|
||||||
}
|
|
||||||
return valid;
|
|
||||||
}
|
|
||||||
|
|
@ -1,81 +0,0 @@
|
|||||||
#include <Homie.h>
|
|
||||||
#include "FileUtils.h"
|
|
||||||
|
|
||||||
bool deleteFile(const char *source)
|
|
||||||
{
|
|
||||||
Serial << "deleting file " << source << endl;
|
|
||||||
if (!SPIFFS.begin())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
bool deleted = SPIFFS.remove(source);
|
|
||||||
if (deleted)
|
|
||||||
{
|
|
||||||
Serial << "Deleted " << source << endl;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Serial << "Could not delete " << source << endl;
|
|
||||||
}
|
|
||||||
return deleted;
|
|
||||||
}
|
|
||||||
|
|
||||||
void printFile(const char *source)
|
|
||||||
{
|
|
||||||
Serial << "printing file " << source << endl;
|
|
||||||
if (!SPIFFS.begin())
|
|
||||||
{
|
|
||||||
Serial << "could not start spiffs " << source << endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
File file = SPIFFS.open(source, FILE_READ);
|
|
||||||
if (!file)
|
|
||||||
{
|
|
||||||
Serial << "could not start open " << source << endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Serial << file.readString() << endl;
|
|
||||||
Serial << "Finished printing file " << source << endl;
|
|
||||||
file.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool doesFileExist(const char *source)
|
|
||||||
{
|
|
||||||
Serial << "checking if file exist " << source << endl;
|
|
||||||
if (!SPIFFS.begin())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
bool exists = SPIFFS.exists(source);
|
|
||||||
Serial << "File " << source << (exists ? "" : " not") << " found " << endl;
|
|
||||||
return exists;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool copyFile(const char *source, const char *target)
|
|
||||||
{
|
|
||||||
Serial << "copy started " << source << " -> " << target << endl;
|
|
||||||
if (!SPIFFS.begin())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
File file = SPIFFS.open(source, FILE_READ);
|
|
||||||
File file2 = SPIFFS.open(target, FILE_WRITE);
|
|
||||||
Serial.flush();
|
|
||||||
if (!file)
|
|
||||||
{
|
|
||||||
Serial << "There was an error opening " << source << " for reading" << endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!file2)
|
|
||||||
{
|
|
||||||
Serial << "There was an error opening " << target << " for reading" << endl;
|
|
||||||
file.close();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
file2.println(file.readString());
|
|
||||||
Serial << "copy finished " << source << " -> " << target << endl;
|
|
||||||
file.close();
|
|
||||||
file2.close();
|
|
||||||
return true;
|
|
||||||
}
|
|
@ -1,98 +0,0 @@
|
|||||||
#include "MQTTUtils.h"
|
|
||||||
#include "FileUtils.h"
|
|
||||||
#include "LogDefines.h"
|
|
||||||
|
|
||||||
|
|
||||||
bool volatile mAliveWasRead = false;
|
|
||||||
|
|
||||||
void log(int level, String message, int statusCode)
|
|
||||||
{
|
|
||||||
String buffer;
|
|
||||||
StaticJsonDocument<200> doc;
|
|
||||||
// Read the current time
|
|
||||||
time_t now; // this is the epoch
|
|
||||||
tm tm; // the structure tm holds time information in a more convient way
|
|
||||||
doc["level"] = level;
|
|
||||||
doc["message"] = message;
|
|
||||||
doc["statusCode"] = statusCode;
|
|
||||||
time(&now);
|
|
||||||
localtime_r(&now, &tm);
|
|
||||||
if (tm.tm_year > (2021 - 1970)) { /* Only add the time, if we have at least 2021 */
|
|
||||||
doc["time"] = String(String(1900 + tm.tm_year) + "-" + String(tm.tm_mon + 1) + "-" + String(tm.tm_mday) +
|
|
||||||
" " + String(tm.tm_hour) + ":" + String(tm.tm_min) + ":" + String(tm.tm_sec));
|
|
||||||
}
|
|
||||||
|
|
||||||
serializeJson(doc, buffer);
|
|
||||||
if (mAliveWasRead)
|
|
||||||
{
|
|
||||||
getTopic(LOG_TOPIC, logTopic)
|
|
||||||
Homie.getMqttClient()
|
|
||||||
.subscribe(logTopic, 2);
|
|
||||||
|
|
||||||
Homie.getMqttClient().publish(logTopic, 2, false, buffer.c_str());
|
|
||||||
delete logTopic;
|
|
||||||
}
|
|
||||||
Serial << statusCode << "@" << level << " : " << message << endl;
|
|
||||||
Serial.flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void mqttWrite(HomieNode* target,String key, String value){
|
|
||||||
if(mAliveWasRead){
|
|
||||||
target->setProperty(key).send(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void mqttWrite(HomieNode* target,const char* key, String value){
|
|
||||||
if(aliveWasRead()){
|
|
||||||
target->setProperty(key).send(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void onMQTTMessage(char *incoming, char *payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total)
|
|
||||||
{
|
|
||||||
getTopic(TEST_TOPIC, testTopic);
|
|
||||||
if (strcmp(incoming, testTopic) == 0)
|
|
||||||
{
|
|
||||||
mAliveWasRead = true;
|
|
||||||
}
|
|
||||||
delete testTopic;
|
|
||||||
getTopic(BACKUP_TOPIC, backupTopic);
|
|
||||||
if (strcmp(incoming, backupTopic) == 0)
|
|
||||||
{
|
|
||||||
if (strcmp(payload, "true") == 0)
|
|
||||||
{
|
|
||||||
bool backupSucessful = copyFile(CONFIG_FILE, CONFIG_FILE_BACKUP);
|
|
||||||
printFile(CONFIG_FILE_BACKUP);
|
|
||||||
if (backupSucessful)
|
|
||||||
{
|
|
||||||
log(LOG_LEVEL_INFO, LOG_BACKUP_SUCCESSFUL, LOG_BACKUP_SUCCESSFUL_CODE);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
log(LOG_LEVEL_INFO, LOG_BACKUP_FAILED, LOG_BACKUP_FAILED_CODE);
|
|
||||||
}
|
|
||||||
Homie.getMqttClient().publish(backupTopic, 2, true, "false");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
delete backupTopic;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool aliveWasRead(){
|
|
||||||
return mAliveWasRead;
|
|
||||||
}
|
|
||||||
|
|
||||||
void startMQTTRoundtripTest(){
|
|
||||||
{
|
|
||||||
getTopic(TEST_TOPIC, testopic)
|
|
||||||
Homie.getMqttClient()
|
|
||||||
.subscribe(testopic, 2);
|
|
||||||
Homie.getMqttClient().publish(testopic, 2, false, "ping");
|
|
||||||
Homie.getMqttClient().onMessage(onMQTTMessage);
|
|
||||||
|
|
||||||
getTopic(BACKUP_TOPIC, backupTopic)
|
|
||||||
Homie.getMqttClient()
|
|
||||||
.subscribe(backupTopic, 2);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
|
|
||||||
#include "MathUtils.h"
|
|
||||||
#include <Arduino.h>
|
|
||||||
bool equalish(double x, double y)
|
|
||||||
{
|
|
||||||
return (abs(x - y) < 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
double mapf(double x, double in_min, double in_max, double out_min, double out_max)
|
|
||||||
{
|
|
||||||
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
|
|
||||||
}
|
|
@ -1,276 +0,0 @@
|
|||||||
/**
|
|
||||||
* @file PlantCtrl.cpp
|
|
||||||
* @author your name (you@domain.com)
|
|
||||||
* @brief
|
|
||||||
* @version 0.1
|
|
||||||
* @date 2020-05-27
|
|
||||||
*
|
|
||||||
* @copyright Copyright (c) 2020
|
|
||||||
*
|
|
||||||
|
|
||||||
*/
|
|
||||||
#include "PlantCtrl.h"
|
|
||||||
#include "ControllerConfiguration.h"
|
|
||||||
#include "TimeUtils.h"
|
|
||||||
#include "driver/pcnt.h"
|
|
||||||
#include "MQTTUtils.h"
|
|
||||||
|
|
||||||
Plant::Plant(int pinSensor, int pinPump, int plantId, HomieNode *plant, PlantSettings_t *setting, SENSOR_MODE mode)
|
|
||||||
{
|
|
||||||
this->mPinSensor = pinSensor;
|
|
||||||
this->mPinPump = pinPump;
|
|
||||||
this->mPlant = plant;
|
|
||||||
this->mSetting = setting;
|
|
||||||
this->mPlantId = plantId;
|
|
||||||
this->mSensorMode = mode;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Plant::init(void)
|
|
||||||
{
|
|
||||||
/* Initialize Home Settings validator */
|
|
||||||
this->mSetting->pSensorDry->setDefaultValue(DEACTIVATED_PLANT);
|
|
||||||
this->mSetting->pSensorDry->setValidator([](long candidate)
|
|
||||||
{ return (((candidate >= 0.0) && (candidate <= 100.0)) || equalish(candidate, DEACTIVATED_PLANT) || equalish(candidate, HYDROPONIC_MODE) || equalish(candidate, TIMER_ONLY)); });
|
|
||||||
|
|
||||||
this->mSetting->pPumpAllowedHourRangeStart->setDefaultValue(5); // start at 5:00 UTC or 7:00 ECST
|
|
||||||
this->mSetting->pPumpAllowedHourRangeStart->setValidator([](long candidate)
|
|
||||||
{ return ((candidate >= 0) && (candidate <= 23)); });
|
|
||||||
this->mSetting->pPumpAllowedHourRangeEnd->setDefaultValue(18); // stop pumps at 18 UTC or 20:00 ECST
|
|
||||||
this->mSetting->pPumpAllowedHourRangeEnd->setValidator([](long candidate)
|
|
||||||
{ return ((candidate >= 0) && (candidate <= 23)); });
|
|
||||||
this->mSetting->pPumpOnlyWhenLowLight->setDefaultValue(false);
|
|
||||||
this->mSetting->pPumpCooldownInSeconds->setDefaultValue(60 * 60); // 1 hour
|
|
||||||
this->mSetting->pPumpCooldownInSeconds->setValidator([](long candidate)
|
|
||||||
{ return (candidate >= 0); });
|
|
||||||
|
|
||||||
this->mSetting->pPumpDuration->setDefaultValue(30);
|
|
||||||
this->mSetting->pPumpDuration->setValidator([](long candidate)
|
|
||||||
{ return ((candidate >= 0) && (candidate <= 1000)); });
|
|
||||||
this->mSetting->pPumpMl->setDefaultValue(1000);
|
|
||||||
this->mSetting->pPumpMl->setValidator([](long candidate)
|
|
||||||
{ return ((candidate >= 0) && (candidate <= 5000)); });
|
|
||||||
this->mSetting->pPumpPowerLevel->setDefaultValue(100);
|
|
||||||
this->mSetting->pPumpPowerLevel->setValidator([](long candidate)
|
|
||||||
{ return ((candidate >= 0) && (candidate <= 100)); });
|
|
||||||
|
|
||||||
/* Initialize Hardware */
|
|
||||||
ledcSetup(this->mPlantId, PWM_FREQ, PWM_BITS);
|
|
||||||
ledcAttachPin(mPinPump, this->mPlantId);
|
|
||||||
ledcWrite(this->mPlantId, 0);
|
|
||||||
pinMode(this->mPinSensor, INPUT);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Plant::initSensors(void)
|
|
||||||
{
|
|
||||||
switch (getSensorMode())
|
|
||||||
{
|
|
||||||
case FREQUENCY_MOD_RESISTANCE_PROBE:
|
|
||||||
{
|
|
||||||
|
|
||||||
pcnt_unit_t unit = (pcnt_unit_t)(PCNT_UNIT_0 + this->mPlantId);
|
|
||||||
pcnt_config_t pcnt_config = {}; // Instancia PCNT config
|
|
||||||
|
|
||||||
pcnt_config.pulse_gpio_num = this->mPinSensor; // Configura GPIO para entrada dos pulsos
|
|
||||||
pcnt_config.ctrl_gpio_num = PCNT_PIN_NOT_USED; // Configura GPIO para controle da contagem
|
|
||||||
pcnt_config.unit = unit; // Unidade de contagem PCNT - 0
|
|
||||||
pcnt_config.channel = PCNT_CHANNEL_0; // Canal de contagem PCNT - 0
|
|
||||||
pcnt_config.counter_h_lim = INT16_MAX; // Limite maximo de contagem - 20000
|
|
||||||
pcnt_config.pos_mode = PCNT_COUNT_INC; // Incrementa contagem na subida do pulso
|
|
||||||
pcnt_config.neg_mode = PCNT_COUNT_DIS; // Incrementa contagem na descida do pulso
|
|
||||||
pcnt_config.lctrl_mode = PCNT_MODE_KEEP; // PCNT - modo lctrl desabilitado
|
|
||||||
pcnt_config.hctrl_mode = PCNT_MODE_KEEP; // PCNT - modo hctrl - se HIGH conta incrementando
|
|
||||||
pcnt_unit_config(&pcnt_config); // Configura o contador PCNT
|
|
||||||
|
|
||||||
pcnt_counter_pause(unit); // Pausa o contador PCNT
|
|
||||||
pcnt_counter_clear(unit); // Zera o contador PCNT
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case ANALOG_RESISTANCE_PROBE:
|
|
||||||
{
|
|
||||||
adcAttachPin(this->mPinSensor);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case NONE:
|
|
||||||
{
|
|
||||||
// do nothing
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Plant::blockingMoistureMeasurement(void)
|
|
||||||
{
|
|
||||||
switch (getSensorMode())
|
|
||||||
{
|
|
||||||
case ANALOG_RESISTANCE_PROBE:
|
|
||||||
{
|
|
||||||
for (int i = 0; i < ANALOG_REREADS; i++)
|
|
||||||
{
|
|
||||||
this->mMoisture_raw.add(analogReadMilliVolts(this->mPinSensor));
|
|
||||||
delay(5);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case FREQUENCY_MOD_RESISTANCE_PROBE:
|
|
||||||
case NONE:
|
|
||||||
{
|
|
||||||
// nothing to do here
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Plant::startMoistureMeasurement(void)
|
|
||||||
{
|
|
||||||
switch (getSensorMode())
|
|
||||||
{
|
|
||||||
case FREQUENCY_MOD_RESISTANCE_PROBE:
|
|
||||||
{
|
|
||||||
pcnt_unit_t unit = (pcnt_unit_t)(PCNT_UNIT_0 + this->mPlantId);
|
|
||||||
pcnt_counter_resume(unit);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case ANALOG_RESISTANCE_PROBE:
|
|
||||||
case NONE:
|
|
||||||
{
|
|
||||||
// do nothing here
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Plant::stopMoistureMeasurement(void)
|
|
||||||
{
|
|
||||||
switch (getSensorMode())
|
|
||||||
{
|
|
||||||
case FREQUENCY_MOD_RESISTANCE_PROBE:
|
|
||||||
{
|
|
||||||
int16_t pulses;
|
|
||||||
pcnt_unit_t unit = (pcnt_unit_t)(PCNT_UNIT_0 + this->mPlantId);
|
|
||||||
pcnt_counter_pause(unit);
|
|
||||||
esp_err_t result = pcnt_get_counter_value(unit, &pulses);
|
|
||||||
pcnt_counter_clear(unit);
|
|
||||||
if (result != ESP_OK)
|
|
||||||
{
|
|
||||||
log(LOG_LEVEL_ERROR, LOG_HARDWARECOUNTER_ERROR_MESSAGE, LOG_HARDWARECOUNTER_ERROR_CODE);
|
|
||||||
this->mMoisture_raw.clear();
|
|
||||||
this->mMoisture_raw.add(-1);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this->mMoisture_raw.add(pulses * (1000 / MOISTURE_MEASUREMENT_DURATION));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case ANALOG_RESISTANCE_PROBE:
|
|
||||||
case NONE:
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Plant::postMQTTconnection(void)
|
|
||||||
{
|
|
||||||
const String OFF = String("OFF");
|
|
||||||
this->mConnected = true;
|
|
||||||
this->mPlant->setProperty("switch").send(OFF);
|
|
||||||
|
|
||||||
float pct = getCurrentMoisturePCT();
|
|
||||||
float raw = getCurrentMoistureRaw();
|
|
||||||
if (equalish(raw, MISSING_SENSOR))
|
|
||||||
{
|
|
||||||
pct = 0;
|
|
||||||
}
|
|
||||||
if (pct < 0)
|
|
||||||
{
|
|
||||||
pct = 0;
|
|
||||||
}
|
|
||||||
if (pct > 100)
|
|
||||||
{
|
|
||||||
pct = 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
this->mPlant->setProperty("moist").send(String(pct));
|
|
||||||
this->mPlant->setProperty("sensormode").send(getSensorModeString());
|
|
||||||
this->mPlant->setProperty("moistraw").send(String(raw));
|
|
||||||
this->mPlant->setProperty("moisttrigger").send(String(getTargetMoisturePCT()));
|
|
||||||
}
|
|
||||||
|
|
||||||
void Plant::deactivatePump(void)
|
|
||||||
{
|
|
||||||
int plantId = this->mPlantId;
|
|
||||||
Serial << "deactivating pump " << plantId << endl;
|
|
||||||
ledcWrite(this->mPlantId, 0);
|
|
||||||
if (this->mConnected)
|
|
||||||
{
|
|
||||||
const String OFF = String("OFF");
|
|
||||||
this->mPlant->setProperty("switch").send(OFF);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Plant::publishState(int stateNumber, String stateString)
|
|
||||||
{
|
|
||||||
String buffer;
|
|
||||||
StaticJsonDocument<200> doc;
|
|
||||||
if (this->mConnected)
|
|
||||||
{
|
|
||||||
doc["number"] = stateNumber;
|
|
||||||
doc["message"] = stateString;
|
|
||||||
serializeJson(doc, buffer);
|
|
||||||
this->mPlant->setProperty("state").send(buffer.c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Plant::activatePump(void)
|
|
||||||
{
|
|
||||||
int plantId = this->mPlantId;
|
|
||||||
|
|
||||||
Serial << "activating pump " << plantId << endl;
|
|
||||||
long desiredPowerLevelPercent = this->mSetting->pPumpPowerLevel->get();
|
|
||||||
ledcWrite(this->mPlantId, desiredPowerLevelPercent * PWM_BITS);
|
|
||||||
if (this->mConnected)
|
|
||||||
{
|
|
||||||
const String ON = String("ON");
|
|
||||||
this->mPlant->setProperty("switch").send(ON);
|
|
||||||
this->mPlant->setProperty("lastPump").send(String(getCurrentTime()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Plant::switchHandler(const HomieRange &range, const String &value)
|
|
||||||
{
|
|
||||||
if (range.isRange)
|
|
||||||
{
|
|
||||||
return false; // only one switch is present
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((value.equals("ON")) || (value.equals("On")) || (value.equals("on")) || (value.equals("true")))
|
|
||||||
{
|
|
||||||
this->activatePump();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else if ((value.equals("OFF")) || (value.equals("Off")) || (value.equals("off")) || (value.equals("false")))
|
|
||||||
{
|
|
||||||
this->deactivatePump();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Plant::setSwitchHandler(HomieInternals::PropertyInputHandler f)
|
|
||||||
{
|
|
||||||
this->mPump.settable(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Plant::advertise(void)
|
|
||||||
{
|
|
||||||
// Advertise topics
|
|
||||||
mPump = this->mPlant->advertise("switch").setName("Pump").setDatatype("Boolean");
|
|
||||||
this->mPlant->advertise("lastPump").setName("lastPump").setDatatype("Integer").setUnit("unixtime").setRetained(true);
|
|
||||||
this->mPlant->advertise("moist").setName("Percent").setDatatype("Float").setUnit("%").setRetained(true);
|
|
||||||
this->mPlant->advertise("moistraw").setName("frequency").setDatatype("Float").setUnit("hz").setRetained(true);
|
|
||||||
this->mPlant->advertise("state").setName("state").setDatatype("String").setRetained(true);
|
|
||||||
|
|
||||||
}
|
|
@ -1,183 +0,0 @@
|
|||||||
//
|
|
||||||
// FILE: RunningMedian.cpp
|
|
||||||
// AUTHOR: Rob.Tillaart at gmail.com
|
|
||||||
// VERSION: 0.2.1
|
|
||||||
// PURPOSE: RunningMedian library for Arduino
|
|
||||||
//
|
|
||||||
// HISTORY:
|
|
||||||
// 0.1.00 - 2011-02-16 initial version
|
|
||||||
// 0.1.01 - 2011-02-22 added remarks from CodingBadly
|
|
||||||
// 0.1.02 - 2012-03-15 added
|
|
||||||
// 0.1.03 - 2013-09-30 added _sorted flag, minor refactor
|
|
||||||
// 0.1.04 - 2013-10-17 added getAverage(uint8_t) - kudo's to Sembazuru
|
|
||||||
// 0.1.05 - 2013-10-18 fixed bug in sort; removes default constructor; dynamic memory
|
|
||||||
// 0.1.06 - 2013-10-19 faster sort, dynamic arrays, replaced sorted float array with indirection array
|
|
||||||
// 0.1.07 - 2013-10-19 add correct median if _cnt is even.
|
|
||||||
// 0.1.08 - 2013-10-20 add getElement(), add getSottedElement() add predict()
|
|
||||||
// 0.1.09 - 2014-11-25 float to double (support ARM)
|
|
||||||
// 0.1.10 - 2015-03-07 fix clear
|
|
||||||
// 0.1.11 - 2015-03-29 undo 0.1.10 fix clear
|
|
||||||
// 0.1.12 - 2015-07-12 refactor constructor + const
|
|
||||||
// 0.1.13 - 2015-10-30 fix getElement(n) - kudos to Gdunge
|
|
||||||
// 0.1.14 - 2017-07-26 revert double to float - issue #33
|
|
||||||
// 0.1.15 - 2018-08-24 make runningMedian Configurable #110
|
|
||||||
// 0.2.0 2020-04-16 refactor.
|
|
||||||
// 0.2.1 2020-06-19 fix library.json
|
|
||||||
|
|
||||||
#include "RunningMedian.h"
|
|
||||||
|
|
||||||
RunningMedian::RunningMedian(const uint8_t size)
|
|
||||||
{
|
|
||||||
_size = constrain(size, MEDIAN_MIN_SIZE, MEDIAN_MAX_SIZE);
|
|
||||||
|
|
||||||
#ifdef RUNNING_MEDIAN_USE_MALLOC
|
|
||||||
_ar = (float *)malloc(_size * sizeof(float));
|
|
||||||
_p = (uint8_t *)malloc(_size * sizeof(uint8_t));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
RunningMedian::~RunningMedian()
|
|
||||||
{
|
|
||||||
#ifdef RUNNING_MEDIAN_USE_MALLOC
|
|
||||||
free(_ar);
|
|
||||||
free(_p);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// resets all counters
|
|
||||||
void RunningMedian::clear()
|
|
||||||
{
|
|
||||||
_cnt = 0;
|
|
||||||
_idx = 0;
|
|
||||||
_sorted = false;
|
|
||||||
for (uint8_t i = 0; i < _size; i++)
|
|
||||||
{
|
|
||||||
_p[i] = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// adds a new value to the data-set
|
|
||||||
// or overwrites the oldest if full.
|
|
||||||
void RunningMedian::add(float value)
|
|
||||||
{
|
|
||||||
_ar[_idx++] = value;
|
|
||||||
if (_idx >= _size)
|
|
||||||
_idx = 0; // wrap around
|
|
||||||
if (_cnt < _size)
|
|
||||||
_cnt++;
|
|
||||||
_sorted = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
float RunningMedian::getMedian()
|
|
||||||
{
|
|
||||||
if (_cnt == 0)
|
|
||||||
return NAN;
|
|
||||||
|
|
||||||
if (_sorted == false)
|
|
||||||
sort();
|
|
||||||
|
|
||||||
if (_cnt & 0x01) // is it odd sized?
|
|
||||||
{
|
|
||||||
return _ar[_p[_cnt / 2]];
|
|
||||||
}
|
|
||||||
return (_ar[_p[_cnt / 2]] + _ar[_p[_cnt / 2 - 1]]) / 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
float RunningMedian::getAverage()
|
|
||||||
{
|
|
||||||
if (_cnt == 0)
|
|
||||||
return NAN;
|
|
||||||
|
|
||||||
float sum = 0;
|
|
||||||
for (uint8_t i = 0; i < _cnt; i++)
|
|
||||||
{
|
|
||||||
sum += _ar[i];
|
|
||||||
}
|
|
||||||
return sum / _cnt;
|
|
||||||
}
|
|
||||||
|
|
||||||
float RunningMedian::getAverage(uint8_t nMedians)
|
|
||||||
{
|
|
||||||
if ((_cnt == 0) || (nMedians == 0))
|
|
||||||
return NAN;
|
|
||||||
|
|
||||||
if (_cnt < nMedians)
|
|
||||||
nMedians = _cnt; // when filling the array for first time
|
|
||||||
uint8_t start = ((_cnt - nMedians) / 2);
|
|
||||||
uint8_t stop = start + nMedians;
|
|
||||||
|
|
||||||
if (_sorted == false)
|
|
||||||
sort();
|
|
||||||
|
|
||||||
float sum = 0;
|
|
||||||
for (uint8_t i = start; i < stop; i++)
|
|
||||||
{
|
|
||||||
sum += _ar[_p[i]];
|
|
||||||
}
|
|
||||||
return sum / nMedians;
|
|
||||||
}
|
|
||||||
|
|
||||||
float RunningMedian::getElement(const uint8_t n)
|
|
||||||
{
|
|
||||||
if ((_cnt == 0) || (n >= _cnt))
|
|
||||||
return NAN;
|
|
||||||
|
|
||||||
uint8_t pos = _idx + n;
|
|
||||||
if (pos >= _cnt) // faster than %
|
|
||||||
{
|
|
||||||
pos -= _cnt;
|
|
||||||
}
|
|
||||||
return _ar[pos];
|
|
||||||
}
|
|
||||||
|
|
||||||
float RunningMedian::getSortedElement(const uint8_t n)
|
|
||||||
{
|
|
||||||
if ((_cnt == 0) || (n >= _cnt))
|
|
||||||
return NAN;
|
|
||||||
|
|
||||||
if (_sorted == false)
|
|
||||||
sort();
|
|
||||||
return _ar[_p[n]];
|
|
||||||
}
|
|
||||||
|
|
||||||
// n can be max <= half the (filled) size
|
|
||||||
float RunningMedian::predict(const uint8_t n)
|
|
||||||
{
|
|
||||||
if ((_cnt == 0) || (n >= _cnt / 2))
|
|
||||||
return NAN;
|
|
||||||
|
|
||||||
float med = getMedian(); // takes care of sorting !
|
|
||||||
if (_cnt & 0x01)
|
|
||||||
{
|
|
||||||
return max(med - _ar[_p[_cnt / 2 - n]], _ar[_p[_cnt / 2 + n]] - med);
|
|
||||||
}
|
|
||||||
float f1 = (_ar[_p[_cnt / 2 - n]] + _ar[_p[_cnt / 2 - n - 1]]) / 2;
|
|
||||||
float f2 = (_ar[_p[_cnt / 2 + n]] + _ar[_p[_cnt / 2 + n - 1]]) / 2;
|
|
||||||
return max(med - f1, f2 - med) / 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RunningMedian::sort()
|
|
||||||
{
|
|
||||||
// bubble sort with flag
|
|
||||||
for (uint8_t i = 0; i < _cnt - 1; i++)
|
|
||||||
{
|
|
||||||
bool flag = true;
|
|
||||||
for (uint8_t j = 1; j < _cnt - i; j++)
|
|
||||||
{
|
|
||||||
if (_ar[_p[j - 1]] > _ar[_p[j]])
|
|
||||||
{
|
|
||||||
uint8_t t = _p[j - 1];
|
|
||||||
_p[j - 1] = _p[j];
|
|
||||||
_p[j] = t;
|
|
||||||
flag = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (flag)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
_sorted = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// -- END OF FILE --
|
|
@ -1,18 +0,0 @@
|
|||||||
#include "TimeUtils.h"
|
|
||||||
#include <Homie.h>
|
|
||||||
|
|
||||||
long getCurrentTime()
|
|
||||||
{
|
|
||||||
struct timeval tv_now;
|
|
||||||
gettimeofday(&tv_now, NULL);
|
|
||||||
return tv_now.tv_sec;
|
|
||||||
}
|
|
||||||
|
|
||||||
int getCurrentHour()
|
|
||||||
{
|
|
||||||
struct tm info;
|
|
||||||
time_t now;
|
|
||||||
time(&now);
|
|
||||||
localtime_r(&now, &info);
|
|
||||||
return info.tm_hour;
|
|
||||||
}
|
|
1244
esp32/src/main.cpp
1244
esp32/src/main.cpp
File diff suppressed because it is too large
Load Diff
@ -1,16 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="1">
|
|
||||||
<builddir>cppcheck-build-dir</builddir>
|
|
||||||
<platform>arm32-wchar_t4.xml</platform>
|
|
||||||
<analyze-all-vs-configs>false</analyze-all-vs-configs>
|
|
||||||
<check-headers>true</check-headers>
|
|
||||||
<check-unused-templates>false</check-unused-templates>
|
|
||||||
<max-ctu-depth>10</max-ctu-depth>
|
|
||||||
<paths>
|
|
||||||
<dir name="src"/>
|
|
||||||
<dir name="include"/>
|
|
||||||
</paths>
|
|
||||||
<libraries>
|
|
||||||
<library>cppcheck-lib</library>
|
|
||||||
</libraries>
|
|
||||||
</project>
|
|
@ -1,11 +0,0 @@
|
|||||||
|
|
||||||
This directory is intended for PIO Unit Testing and project tests.
|
|
||||||
|
|
||||||
Unit Testing is a software testing method by which individual units of
|
|
||||||
source code, sets of one or more MCU program modules together with associated
|
|
||||||
control data, usage procedures, and operating procedures, are tested to
|
|
||||||
determine whether they are fit for use. Unit testing finds problems early
|
|
||||||
in the development cycle.
|
|
||||||
|
|
||||||
More information about PIO Unit Testing:
|
|
||||||
- https://docs.platformio.org/page/plus/unit-testing.html
|
|
5
esp32test/Esp32DeepSleepTest/.gitignore
vendored
5
esp32test/Esp32DeepSleepTest/.gitignore
vendored
@ -1,5 +0,0 @@
|
|||||||
.pio
|
|
||||||
.vscode/.browse.c_cpp.db*
|
|
||||||
.vscode/c_cpp_properties.json
|
|
||||||
.vscode/launch.json
|
|
||||||
.vscode/ipch
|
|
@ -1,10 +0,0 @@
|
|||||||
{
|
|
||||||
// See http://go.microsoft.com/fwlink/?LinkId=827846
|
|
||||||
// for the documentation about the extensions.json format
|
|
||||||
"recommendations": [
|
|
||||||
"platformio.platformio-ide"
|
|
||||||
],
|
|
||||||
"unwantedRecommendations": [
|
|
||||||
"ms-vscode.cpptools-extension-pack"
|
|
||||||
]
|
|
||||||
}
|
|
@ -1,28 +0,0 @@
|
|||||||
{
|
|
||||||
"folders": [
|
|
||||||
{
|
|
||||||
"path": "."
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"settings": {
|
|
||||||
"files.associations": {
|
|
||||||
"*.tcc": "cpp",
|
|
||||||
"bitset": "cpp",
|
|
||||||
"algorithm": "cpp",
|
|
||||||
"istream": "cpp",
|
|
||||||
"limits": "cpp",
|
|
||||||
"streambuf": "cpp",
|
|
||||||
"functional": "cpp",
|
|
||||||
"string": "cpp",
|
|
||||||
"typeinfo": "cpp",
|
|
||||||
"cmath": "cpp",
|
|
||||||
"array": "cpp",
|
|
||||||
"deque": "cpp",
|
|
||||||
"unordered_map": "cpp",
|
|
||||||
"unordered_set": "cpp",
|
|
||||||
"vector": "cpp",
|
|
||||||
"initializer_list": "cpp",
|
|
||||||
"regex": "cpp"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,39 +0,0 @@
|
|||||||
|
|
||||||
This directory is intended for project header files.
|
|
||||||
|
|
||||||
A header file is a file containing C declarations and macro definitions
|
|
||||||
to be shared between several project source files. You request the use of a
|
|
||||||
header file in your project source file (C, C++, etc) located in `src` folder
|
|
||||||
by including it, with the C preprocessing directive `#include'.
|
|
||||||
|
|
||||||
```src/main.c
|
|
||||||
|
|
||||||
#include "header.h"
|
|
||||||
|
|
||||||
int main (void)
|
|
||||||
{
|
|
||||||
...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Including a header file produces the same results as copying the header file
|
|
||||||
into each source file that needs it. Such copying would be time-consuming
|
|
||||||
and error-prone. With a header file, the related declarations appear
|
|
||||||
in only one place. If they need to be changed, they can be changed in one
|
|
||||||
place, and programs that include the header file will automatically use the
|
|
||||||
new version when next recompiled. The header file eliminates the labor of
|
|
||||||
finding and changing all the copies as well as the risk that a failure to
|
|
||||||
find one copy will result in inconsistencies within a program.
|
|
||||||
|
|
||||||
In C, the usual convention is to give header files names that end with `.h'.
|
|
||||||
It is most portable to use only letters, digits, dashes, and underscores in
|
|
||||||
header file names, and at most one dot.
|
|
||||||
|
|
||||||
Read more about using header files in official GCC documentation:
|
|
||||||
|
|
||||||
* Include Syntax
|
|
||||||
* Include Operation
|
|
||||||
* Once-Only Headers
|
|
||||||
* Computed Includes
|
|
||||||
|
|
||||||
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html
|
|
@ -1,187 +0,0 @@
|
|||||||
#ifndef ULP_PWM_h
|
|
||||||
#define ILP_PWM_h
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
|
||||||
#include "driver/rtc_io.h"
|
|
||||||
#include "soc/rtc_cntl_reg.h"
|
|
||||||
#include "soc/rtc.h"
|
|
||||||
#include "esp32/ulp.h"
|
|
||||||
|
|
||||||
#define LBL_START 1
|
|
||||||
#define LBL_DELAY_ON 2
|
|
||||||
#define LBL_DELAY_OFF 3
|
|
||||||
#define LBL_SKIP_ON 4
|
|
||||||
#define LBL_SKIP_OFF 5
|
|
||||||
#define REGISTER_DELAY_LOOP_COUNTER R0
|
|
||||||
#define REGISTER_TICKS_ON R1
|
|
||||||
#define REGISTER_TICKS_OFF R2
|
|
||||||
#define TOTAL_TICKS_DELAY 255
|
|
||||||
#define PIN GPIO_NUM_12
|
|
||||||
|
|
||||||
//support 20 vars
|
|
||||||
const size_t ulp_var_offset = CONFIG_ULP_COPROC_RESERVE_MEM - 20;
|
|
||||||
//use the first for dimming
|
|
||||||
const size_t ulp_dimm_offset = ulp_var_offset + 1;
|
|
||||||
const size_t ulp_alive_offset = ulp_var_offset + 2;
|
|
||||||
|
|
||||||
//see https://github.com/perseus086/ESP32-notes
|
|
||||||
const uint32_t rtc_bit[40] = {
|
|
||||||
25, //gpio0
|
|
||||||
0, //gpio1
|
|
||||||
26, //gpio2
|
|
||||||
0, //gpio3
|
|
||||||
24, //gpio4
|
|
||||||
0, //gpio5
|
|
||||||
0, //gpio6
|
|
||||||
0, //gpio7
|
|
||||||
0, //gpio8
|
|
||||||
0, //gpio9
|
|
||||||
0, //gpio10
|
|
||||||
0, //gpio11
|
|
||||||
29, //gpio12
|
|
||||||
28, //gpio13
|
|
||||||
30, //gpio14
|
|
||||||
27, //gpio15
|
|
||||||
0, //gpio16
|
|
||||||
31, //gpio17
|
|
||||||
0, //gpio18
|
|
||||||
0, //gpio19
|
|
||||||
0, //gpio20
|
|
||||||
0, //gpio21
|
|
||||||
0, //gpio22
|
|
||||||
0, //gpio23
|
|
||||||
0, //gpio24
|
|
||||||
20, //gpio25
|
|
||||||
21, //gpio26
|
|
||||||
0, //gpio27
|
|
||||||
0, //gpio28
|
|
||||||
0, //gpio29
|
|
||||||
0, //gpio30
|
|
||||||
0, //gpio31
|
|
||||||
23, //gpio32
|
|
||||||
22, //gpio33
|
|
||||||
18, //gpio34
|
|
||||||
19, //gpio35
|
|
||||||
14, //gpio36
|
|
||||||
15, //gpio37
|
|
||||||
16, //gpio38
|
|
||||||
17 //gpio39
|
|
||||||
};
|
|
||||||
|
|
||||||
static inline void ulp_data_write(size_t offset, uint16_t value)
|
|
||||||
{
|
|
||||||
if (offset >= CONFIG_ULP_COPROC_RESERVE_MEM || offset <= ulp_var_offset)
|
|
||||||
{
|
|
||||||
Serial.print("Invalid ULP offset detected, refusing write!");
|
|
||||||
Serial.print(offset);
|
|
||||||
Serial.print("-");
|
|
||||||
Serial.print(ulp_var_offset);
|
|
||||||
Serial.print("-");
|
|
||||||
Serial.println(CONFIG_ULP_COPROC_RESERVE_MEM);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
RTC_SLOW_MEM[offset] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline uint16_t ulp_data_read(size_t offset)
|
|
||||||
{
|
|
||||||
if (offset >= CONFIG_ULP_COPROC_RESERVE_MEM || offset <= ulp_var_offset)
|
|
||||||
{
|
|
||||||
Serial.print("Invalid ULP offset detected");
|
|
||||||
Serial.print(offset);
|
|
||||||
Serial.print("-");
|
|
||||||
Serial.print(ulp_var_offset);
|
|
||||||
Serial.print("-");
|
|
||||||
Serial.println(CONFIG_ULP_COPROC_RESERVE_MEM);
|
|
||||||
}
|
|
||||||
return RTC_SLOW_MEM[offset] & 0xffff;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline uint32_t rtc_io_number_get(gpio_num_t gpio_num)
|
|
||||||
{
|
|
||||||
assert(rtc_gpio_is_valid_gpio(gpio_num) && "Invalid GPIO for RTC");
|
|
||||||
uint32_t bit = rtc_bit[gpio_num];
|
|
||||||
Serial.print("Resolved GPIO ");
|
|
||||||
Serial.print(gpio_num);
|
|
||||||
Serial.print(" to rtc bit ");
|
|
||||||
Serial.println(bit);
|
|
||||||
return bit;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ulp_pwm_start(void)
|
|
||||||
{
|
|
||||||
rtc_gpio_init(PIN);
|
|
||||||
rtc_gpio_set_direction(PIN, RTC_GPIO_MODE_OUTPUT_ONLY);
|
|
||||||
rtc_gpio_set_level(PIN, 0);
|
|
||||||
const uint32_t rtc_gpio = rtc_io_number_get(PIN);
|
|
||||||
|
|
||||||
// Define ULP program
|
|
||||||
const ulp_insn_t ulp_prog[] = {
|
|
||||||
M_LABEL(LBL_START),
|
|
||||||
|
|
||||||
I_MOVI(REGISTER_DELAY_LOOP_COUNTER, 1),
|
|
||||||
I_MOVI(REGISTER_TICKS_ON, 0),
|
|
||||||
I_ST(REGISTER_DELAY_LOOP_COUNTER, REGISTER_TICKS_ON, ulp_alive_offset), //store 1 with 0 offset into alive
|
|
||||||
|
|
||||||
I_LD(REGISTER_TICKS_ON, REGISTER_TICKS_ON, ulp_dimm_offset), //REGISTER_TICKS_ON = RTC_DATA[0+ulp_dimm_offset]
|
|
||||||
//in total there is always 255 delay loop iterations, but in different duty cycle
|
|
||||||
I_MOVI(REGISTER_TICKS_OFF, TOTAL_TICKS_DELAY),
|
|
||||||
I_SUBR(REGISTER_TICKS_OFF, REGISTER_TICKS_OFF, REGISTER_TICKS_ON),
|
|
||||||
|
|
||||||
//on phase
|
|
||||||
I_MOVR(REGISTER_DELAY_LOOP_COUNTER, REGISTER_TICKS_ON),
|
|
||||||
M_BL(LBL_SKIP_ON, 1), //if never on, skip on phase
|
|
||||||
I_WR_REG(RTC_GPIO_OUT_REG, rtc_gpio, rtc_gpio, HIGH), // on
|
|
||||||
M_LABEL(LBL_DELAY_ON),
|
|
||||||
I_DELAY(1), //wait 1 clock
|
|
||||||
I_SUBI(REGISTER_DELAY_LOOP_COUNTER, REGISTER_DELAY_LOOP_COUNTER, 1), // REGISTER_DELAY_LOOP_COUNTER--
|
|
||||||
M_BGE(LBL_DELAY_ON, 1), //if time left, goto start of on loop
|
|
||||||
M_LABEL(LBL_SKIP_ON),
|
|
||||||
|
|
||||||
//off phase
|
|
||||||
I_MOVR(REGISTER_DELAY_LOOP_COUNTER, REGISTER_TICKS_OFF),
|
|
||||||
|
|
||||||
M_BL(LBL_SKIP_OFF, 1), //if never off, skip on phase
|
|
||||||
I_WR_REG(RTC_GPIO_OUT_REG, rtc_gpio, rtc_gpio, LOW), // on
|
|
||||||
M_LABEL(3),
|
|
||||||
I_DELAY(1), //wait 1 clock
|
|
||||||
I_SUBI(REGISTER_DELAY_LOOP_COUNTER, REGISTER_DELAY_LOOP_COUNTER, 1), // REGISTER_DELAY_LOOP_COUNTER--
|
|
||||||
M_BGE(3, 1), //if time left, goto start of on loop
|
|
||||||
M_LABEL(LBL_SKIP_OFF),
|
|
||||||
|
|
||||||
M_BX(LBL_START),
|
|
||||||
};
|
|
||||||
// Run ULP program
|
|
||||||
size_t size = sizeof(ulp_prog) / sizeof(ulp_insn_t);
|
|
||||||
assert(size < ulp_var_offset && "ULP_DATA_OFFSET needs to be greater or equal to the program size");
|
|
||||||
esp_err_t error = ulp_process_macros_and_load(0, ulp_prog, &size);
|
|
||||||
Serial.print("ULP bootstrap status ");
|
|
||||||
Serial.println(error);
|
|
||||||
|
|
||||||
//allow glitchless start
|
|
||||||
ulp_data_write(ulp_alive_offset, 0);
|
|
||||||
|
|
||||||
error = ulp_run(0);
|
|
||||||
Serial.print("ULP start status ");
|
|
||||||
Serial.println(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void ulp_pwm_set_level(uint8_t level)
|
|
||||||
{
|
|
||||||
ulp_data_write(ulp_dimm_offset, level);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void ulp_pwm_init()
|
|
||||||
{
|
|
||||||
ulp_data_write(ulp_alive_offset, 0);
|
|
||||||
delay(10);
|
|
||||||
if (ulp_data_read(ulp_alive_offset) == 0)
|
|
||||||
{
|
|
||||||
ulp_pwm_start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,46 +0,0 @@
|
|||||||
|
|
||||||
This directory is intended for project specific (private) libraries.
|
|
||||||
PlatformIO will compile them to static libraries and link into executable file.
|
|
||||||
|
|
||||||
The source code of each library should be placed in a an own separate directory
|
|
||||||
("lib/your_library_name/[here are source files]").
|
|
||||||
|
|
||||||
For example, see a structure of the following two libraries `Foo` and `Bar`:
|
|
||||||
|
|
||||||
|--lib
|
|
||||||
| |
|
|
||||||
| |--Bar
|
|
||||||
| | |--docs
|
|
||||||
| | |--examples
|
|
||||||
| | |--src
|
|
||||||
| | |- Bar.c
|
|
||||||
| | |- Bar.h
|
|
||||||
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
|
|
||||||
| |
|
|
||||||
| |--Foo
|
|
||||||
| | |- Foo.c
|
|
||||||
| | |- Foo.h
|
|
||||||
| |
|
|
||||||
| |- README --> THIS FILE
|
|
||||||
|
|
|
||||||
|- platformio.ini
|
|
||||||
|--src
|
|
||||||
|- main.c
|
|
||||||
|
|
||||||
and a contents of `src/main.c`:
|
|
||||||
```
|
|
||||||
#include <Foo.h>
|
|
||||||
#include <Bar.h>
|
|
||||||
|
|
||||||
int main (void)
|
|
||||||
{
|
|
||||||
...
|
|
||||||
}
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
PlatformIO Library Dependency Finder will find automatically dependent
|
|
||||||
libraries scanning project source files.
|
|
||||||
|
|
||||||
More information about PlatformIO Library Dependency Finder
|
|
||||||
- https://docs.platformio.org/page/librarymanager/ldf.html
|
|
@ -1,16 +0,0 @@
|
|||||||
; PlatformIO Project Configuration File
|
|
||||||
;
|
|
||||||
; Build options: build flags, source filter
|
|
||||||
; Upload options: custom upload port, speed and extra flags
|
|
||||||
; Library options: dependencies, extra library storages
|
|
||||||
; Advanced options: extra scripting
|
|
||||||
;
|
|
||||||
; Please visit documentation for the other options and examples
|
|
||||||
; https://docs.platformio.org/page/projectconf.html
|
|
||||||
|
|
||||||
[env:esp32doit-devkit-v1]
|
|
||||||
platform = espressif32
|
|
||||||
board = esp32doit-devkit-v1
|
|
||||||
framework = arduino
|
|
||||||
lib_deps = pololu/VL53L0X@^1.3.1
|
|
||||||
build_flags = -DPIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY -DBOOTLOADER_LOG_LEVEL_VERBOSE -DLOG_DEFAULT_LEVEL_VERBOSE -DCORE_DEBUG_LEVEL=5
|
|
@ -1,83 +0,0 @@
|
|||||||
#include <Arduino.h>
|
|
||||||
#include "driver/pcnt.h"
|
|
||||||
#include <VL53L0X.h>
|
|
||||||
|
|
||||||
#define SENSOR_TANK_SDA GPIO_NUM_16 /**< GPIO 16 - echo feedback of water sensor */
|
|
||||||
#define SENSOR_TANK_SCL GPIO_NUM_17 /**< GPIO 17 - trigger for water sensor */
|
|
||||||
|
|
||||||
|
|
||||||
#define OUTPUT_SENSOR 14
|
|
||||||
#define SENSOR_PLANT 17
|
|
||||||
|
|
||||||
VL53L0X tankSensor;
|
|
||||||
bool distanceReady = false;
|
|
||||||
|
|
||||||
void initializeTanksensor() {
|
|
||||||
Wire.begin(SENSOR_TANK_SDA, SENSOR_TANK_SCL, 100000UL /* 100kHz */);
|
|
||||||
tankSensor.setBus(&Wire);
|
|
||||||
delay(100);
|
|
||||||
tankSensor.setTimeout(500);
|
|
||||||
long start = millis();
|
|
||||||
while (start + 500 > millis())
|
|
||||||
{
|
|
||||||
if (tankSensor.init())
|
|
||||||
{
|
|
||||||
distanceReady = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
delay(20);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((distanceReady) && (!tankSensor.timeoutOccurred()))
|
|
||||||
{
|
|
||||||
Serial.println("Sensor init done");
|
|
||||||
tankSensor.setSignalRateLimit(0.1);
|
|
||||||
// increase laser pulse periods (defaults are 14 and 10 PCLKs)
|
|
||||||
tankSensor.setVcselPulsePeriod(VL53L0X::VcselPeriodPreRange, 18);
|
|
||||||
tankSensor.setVcselPulsePeriod(VL53L0X::VcselPeriodFinalRange, 14);
|
|
||||||
tankSensor.setMeasurementTimingBudget(200000);
|
|
||||||
tankSensor.startContinuous();
|
|
||||||
} else {
|
|
||||||
Serial.println("Sensor init failed");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void setup()
|
|
||||||
{
|
|
||||||
Serial.begin(115200);
|
|
||||||
pinMode(OUTPUT_SENSOR, OUTPUT);
|
|
||||||
|
|
||||||
digitalWrite(OUTPUT_SENSOR, HIGH);
|
|
||||||
Serial.println("Nodemcu ESP32 Start done");
|
|
||||||
|
|
||||||
initializeTanksensor();
|
|
||||||
}
|
|
||||||
|
|
||||||
void loop() {
|
|
||||||
|
|
||||||
delay(500);
|
|
||||||
|
|
||||||
|
|
||||||
if ((distanceReady) && (!tankSensor.timeoutOccurred()))
|
|
||||||
{
|
|
||||||
uint16_t distance = tankSensor.readRangeSingleMillimeters();
|
|
||||||
if (distance > 8000) {
|
|
||||||
Serial.println("Reset due invalid distance: 8 meter");
|
|
||||||
Wire.end();
|
|
||||||
delay(1000);
|
|
||||||
initializeTanksensor();
|
|
||||||
} else {
|
|
||||||
Serial.print("Distance");
|
|
||||||
Serial.println(distance);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Serial.println("Timeout");
|
|
||||||
tankSensor.stopContinuous();
|
|
||||||
Wire.end();
|
|
||||||
delay(100);
|
|
||||||
initializeTanksensor();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
|
|
||||||
This directory is intended for PlatformIO Unit Testing and project tests.
|
|
||||||
|
|
||||||
Unit Testing is a software testing method by which individual units of
|
|
||||||
source code, sets of one or more MCU program modules together with associated
|
|
||||||
control data, usage procedures, and operating procedures, are tested to
|
|
||||||
determine whether they are fit for use. Unit testing finds problems early
|
|
||||||
in the development cycle.
|
|
||||||
|
|
||||||
More information about PlatformIO Unit Testing:
|
|
||||||
- https://docs.platformio.org/page/plus/unit-testing.html
|
|
Binary file not shown.
Before Width: | Height: | Size: 279 KiB |
Binary file not shown.
@ -1,17 +0,0 @@
|
|||||||
[build]
|
|
||||||
target = "riscv32imac-esp-espidf"
|
|
||||||
|
|
||||||
[target.riscv32imac-esp-espidf]
|
|
||||||
linker = "ldproxy"
|
|
||||||
# runner = "espflash --monitor" # Select this runner for espflash v1.x.x
|
|
||||||
runner = "espflash flash --monitor --baud 921600" # Select this runner for espflash v2.x.x
|
|
||||||
rustflags = [ "--cfg", "espidf_time64"] # Extending time_t for ESP IDF 5: https://github.com/esp-rs/rust/issues/110
|
|
||||||
|
|
||||||
[unstable]
|
|
||||||
build-std = ["std", "panic_abort"]
|
|
||||||
|
|
||||||
[env]
|
|
||||||
MCU="esp32c6"
|
|
||||||
# Note: this variable is not used by the pio builder (`cargo build --features pio`)
|
|
||||||
ESP_IDF_VERSION = "v5.1.3"
|
|
||||||
|
|
4
next/.gitignore
vendored
4
next/.gitignore
vendored
@ -1,4 +0,0 @@
|
|||||||
/.vscode
|
|
||||||
/.embuild
|
|
||||||
/target
|
|
||||||
/Cargo.lock
|
|
@ -1,36 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "next"
|
|
||||||
version = "0.1.0"
|
|
||||||
authors = ["Empire <empirephoenix@yahoo.de>"]
|
|
||||||
edition = "2021"
|
|
||||||
resolver = "2"
|
|
||||||
rust-version = "1.71"
|
|
||||||
|
|
||||||
[profile.release]
|
|
||||||
opt-level = "s"
|
|
||||||
|
|
||||||
[profile.dev]
|
|
||||||
debug = true # Symbols are nice and they don't increase the size on Flash
|
|
||||||
opt-level = "z"
|
|
||||||
|
|
||||||
[features]
|
|
||||||
default = ["std", "embassy", "esp-idf-svc/native"]
|
|
||||||
|
|
||||||
pio = ["esp-idf-svc/pio"]
|
|
||||||
std = ["alloc", "esp-idf-svc/binstart", "esp-idf-svc/std"]
|
|
||||||
alloc = ["esp-idf-svc/alloc"]
|
|
||||||
nightly = ["esp-idf-svc/nightly"]
|
|
||||||
experimental = ["esp-idf-svc/experimental"]
|
|
||||||
embassy = ["esp-idf-svc/embassy-sync", "esp-idf-svc/critical-section", "esp-idf-svc/embassy-time-driver"]
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
log = { version = "0.4", default-features = false }
|
|
||||||
esp-idf-svc = { version = "0.48", default-features = false }
|
|
||||||
esp-idf-hal = "0.43.0"
|
|
||||||
embedded-hal = "1.0.0"
|
|
||||||
|
|
||||||
[build-dependencies]
|
|
||||||
embuild = "0.31.3"
|
|
||||||
|
|
||||||
[patch.crates-io]
|
|
||||||
esp-idf-hal = { git = "https://github.com/empirephoenix/esp-idf-hal.git" }
|
|
@ -1,3 +0,0 @@
|
|||||||
fn main() {
|
|
||||||
embuild::espidf::sysenv::output();
|
|
||||||
}
|
|
@ -1,3 +0,0 @@
|
|||||||
[toolchain]
|
|
||||||
channel = "nightly"
|
|
||||||
components = ["rust-src"]
|
|
@ -1,14 +0,0 @@
|
|||||||
# Rust often needs a bit of an extra main task stack size compared to C (the default is 3K)
|
|
||||||
CONFIG_ESP_MAIN_TASK_STACK_SIZE=8000
|
|
||||||
|
|
||||||
# Use this to set FreeRTOS kernel tick frequency to 1000 Hz (100 Hz by default).
|
|
||||||
# This allows to use 1 ms granuality for thread sleeps (10 ms by default).
|
|
||||||
CONFIG_FREERTOS_HZ=1000
|
|
||||||
|
|
||||||
# Workaround for https://github.com/espressif/esp-idf/issues/7631
|
|
||||||
#CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=n
|
|
||||||
#CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_FULL=n
|
|
||||||
#CONFIG_LOG_MAXIMUM_LEVEL_WARN=y
|
|
||||||
CONFIG_LOG_DEFAULT_LEVEL_NONE=y
|
|
||||||
CONFIG_ESP_TASK_WDT_TIMEOUT_S=60
|
|
||||||
CONFIG_ESP_DEBUG_STUBS_ENABLE=n
|
|
@ -1,4 +0,0 @@
|
|||||||
#![allow(dead_code)]
|
|
||||||
extern crate embedded_hal as hal;
|
|
||||||
|
|
||||||
pub mod sipo;
|
|
@ -1,98 +0,0 @@
|
|||||||
use esp_idf_hal::{
|
|
||||||
adc::{
|
|
||||||
attenuation,
|
|
||||||
oneshot::{config::AdcChannelConfig, AdcChannelDriver, AdcDriver},
|
|
||||||
},
|
|
||||||
delay::Delay,
|
|
||||||
gpio::{AnyInputPin, IOPin, InputOutput, PinDriver, Pull},
|
|
||||||
peripherals::Peripherals,
|
|
||||||
sys::{esp_timer_get_time, vTaskDelay},
|
|
||||||
};
|
|
||||||
|
|
||||||
use next::sipo::ShiftRegister8;
|
|
||||||
use esp_idf_hal::pcnt::{
|
|
||||||
PcntChannel, PcntChannelConfig, PcntControlMode, PcntCountMode, PcntDriver, PinIndex,
|
|
||||||
};
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
// It is necessary to call this function once. Otherwise some patches to the runtime
|
|
||||||
// implemented by esp-idf-sys might not link properly. See https://github.com/esp-rs/esp-idf-template/issues/71
|
|
||||||
esp_idf_svc::sys::link_patches();
|
|
||||||
|
|
||||||
// Bind the log crate to the ESP Logging facilities
|
|
||||||
esp_idf_svc::log::EspLogger::initialize_default();
|
|
||||||
|
|
||||||
log::info!("Hello, world!");
|
|
||||||
|
|
||||||
let peripherals = Peripherals::take().unwrap();
|
|
||||||
|
|
||||||
let mut s0 = PinDriver::input_output(peripherals.pins.gpio10.downgrade()).unwrap();
|
|
||||||
s0.set_low();
|
|
||||||
|
|
||||||
|
|
||||||
//s0 = 11
|
|
||||||
//s1 = 8
|
|
||||||
//s2 = 22
|
|
||||||
//s3 = 21
|
|
||||||
|
|
||||||
|
|
||||||
let mut counter_unit1 = PcntDriver::new(
|
|
||||||
peripherals.pcnt0,
|
|
||||||
Some(peripherals.pins.gpio5),
|
|
||||||
Option::<AnyInputPin>::None,
|
|
||||||
Option::<AnyInputPin>::None,
|
|
||||||
Option::<AnyInputPin>::None,
|
|
||||||
).unwrap();
|
|
||||||
|
|
||||||
println!("Channel config start");
|
|
||||||
|
|
||||||
counter_unit1.channel_config(
|
|
||||||
PcntChannel::Channel0,
|
|
||||||
PinIndex::Pin0,
|
|
||||||
PinIndex::Pin1,
|
|
||||||
&PcntChannelConfig {
|
|
||||||
lctrl_mode: PcntControlMode::Keep,
|
|
||||||
hctrl_mode: PcntControlMode::Keep,
|
|
||||||
pos_mode: PcntCountMode::Increment,
|
|
||||||
neg_mode: PcntCountMode::Hold,
|
|
||||||
counter_h_lim: i16::MAX,
|
|
||||||
counter_l_lim: 0,
|
|
||||||
},
|
|
||||||
).unwrap();
|
|
||||||
|
|
||||||
println!("Setup filter");
|
|
||||||
|
|
||||||
//TODO validate filter value! currently max allowed value
|
|
||||||
//counter_unit1.set_filter_value(1023).unwrap();
|
|
||||||
//counter_unit1.filter_enable().unwrap();
|
|
||||||
|
|
||||||
counter_unit1.counter_pause().unwrap();
|
|
||||||
let delay = Delay::new(1);
|
|
||||||
loop {
|
|
||||||
s0.set_low().unwrap();
|
|
||||||
delay.delay_ms(100);
|
|
||||||
let sensor0 = measure(&mut counter_unit1, &delay);
|
|
||||||
|
|
||||||
s0.set_high().unwrap();
|
|
||||||
delay.delay_ms(100);
|
|
||||||
let sensor1 = measure(&mut counter_unit1, &delay);
|
|
||||||
|
|
||||||
println!(
|
|
||||||
"Sensor a {}hz sensor b {}hz",
|
|
||||||
sensor0,
|
|
||||||
sensor1
|
|
||||||
);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn measure (counter_unit1: &mut PcntDriver, delay: &Delay) -> u32{
|
|
||||||
counter_unit1.counter_clear().unwrap();
|
|
||||||
counter_unit1.counter_resume().unwrap();
|
|
||||||
|
|
||||||
delay.delay_ms(100);
|
|
||||||
|
|
||||||
counter_unit1.counter_pause().unwrap();
|
|
||||||
let sensor0 = counter_unit1.get_counter_value().unwrap() as u32 * 10;
|
|
||||||
return sensor0
|
|
||||||
}
|
|
153
next/src/sipo.rs
153
next/src/sipo.rs
@ -1,153 +0,0 @@
|
|||||||
//! Serial-in parallel-out shift register
|
|
||||||
|
|
||||||
use core::cell::RefCell;
|
|
||||||
use core::mem::{self, MaybeUninit};
|
|
||||||
use std::convert::Infallible;
|
|
||||||
|
|
||||||
use hal::digital::OutputPin;
|
|
||||||
|
|
||||||
trait ShiftRegisterInternal {
|
|
||||||
fn update(&self, index: usize, command: bool) -> Result<(), ()>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Output pin of the shift register
|
|
||||||
pub struct ShiftRegisterPin<'a> {
|
|
||||||
shift_register: &'a dyn ShiftRegisterInternal,
|
|
||||||
index: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> ShiftRegisterPin<'a> {
|
|
||||||
fn new(shift_register: &'a dyn ShiftRegisterInternal, index: usize) -> Self {
|
|
||||||
ShiftRegisterPin {
|
|
||||||
shift_register,
|
|
||||||
index,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl embedded_hal::digital::ErrorType for ShiftRegisterPin<'_> {
|
|
||||||
type Error = Infallible;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl OutputPin for ShiftRegisterPin<'_> {
|
|
||||||
fn set_low(&mut self) -> Result<(), Infallible> {
|
|
||||||
self.shift_register.update(self.index, false).unwrap();
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_high(&mut self) -> Result<(), Infallible> {
|
|
||||||
self.shift_register.update(self.index, true).unwrap();
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! ShiftRegisterBuilder {
|
|
||||||
($name: ident, $size: expr) => {
|
|
||||||
/// Serial-in parallel-out shift register
|
|
||||||
pub struct $name<Pin1, Pin2, Pin3>
|
|
||||||
where
|
|
||||||
Pin1: OutputPin,
|
|
||||||
Pin2: OutputPin,
|
|
||||||
Pin3: OutputPin,
|
|
||||||
{
|
|
||||||
clock: RefCell<Pin1>,
|
|
||||||
latch: RefCell<Pin2>,
|
|
||||||
data: RefCell<Pin3>,
|
|
||||||
output_state: RefCell<[bool; $size]>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Pin1, Pin2, Pin3> ShiftRegisterInternal for $name<Pin1, Pin2, Pin3>
|
|
||||||
where
|
|
||||||
Pin1: OutputPin,
|
|
||||||
Pin2: OutputPin,
|
|
||||||
Pin3: OutputPin,
|
|
||||||
{
|
|
||||||
/// Sets the value of the shift register output at `index` to value `command`
|
|
||||||
fn update(&self, index: usize, command: bool) -> Result<(), ()> {
|
|
||||||
self.output_state.borrow_mut()[index] = command;
|
|
||||||
let output_state = self.output_state.borrow();
|
|
||||||
self.latch.borrow_mut().set_low().map_err(|_e| ())?;
|
|
||||||
|
|
||||||
for i in 1..=output_state.len() {
|
|
||||||
if output_state[output_state.len() - i] {
|
|
||||||
self.data.borrow_mut().set_high().map_err(|_e| ())?;
|
|
||||||
} else {
|
|
||||||
self.data.borrow_mut().set_low().map_err(|_e| ())?;
|
|
||||||
}
|
|
||||||
self.clock.borrow_mut().set_high().map_err(|_e| ())?;
|
|
||||||
self.clock.borrow_mut().set_low().map_err(|_e| ())?;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.latch.borrow_mut().set_high().map_err(|_e| ())?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Pin1, Pin2, Pin3> $name<Pin1, Pin2, Pin3>
|
|
||||||
where
|
|
||||||
Pin1: OutputPin,
|
|
||||||
Pin2: OutputPin,
|
|
||||||
Pin3: OutputPin,
|
|
||||||
{
|
|
||||||
/// Creates a new SIPO shift register from clock, latch, and data output pins
|
|
||||||
pub fn new(clock: Pin1, latch: Pin2, data: Pin3) -> Self {
|
|
||||||
$name {
|
|
||||||
clock: RefCell::new(clock),
|
|
||||||
latch: RefCell::new(latch),
|
|
||||||
data: RefCell::new(data),
|
|
||||||
output_state: RefCell::new([false; $size]),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get embedded-hal output pins to control the shift register outputs
|
|
||||||
pub fn decompose(&self) -> [ShiftRegisterPin; $size] {
|
|
||||||
// Create an uninitialized array of `MaybeUninit`. The `assume_init` is
|
|
||||||
// safe because the type we are claiming to have initialized here is a
|
|
||||||
// bunch of `MaybeUninit`s, which do not require initialization.
|
|
||||||
let mut pins: [MaybeUninit<ShiftRegisterPin>; $size] =
|
|
||||||
unsafe { MaybeUninit::uninit().assume_init() };
|
|
||||||
|
|
||||||
// Dropping a `MaybeUninit` does nothing, so if there is a panic during this loop,
|
|
||||||
// we have a memory leak, but there is no memory safety issue.
|
|
||||||
for (index, elem) in pins.iter_mut().enumerate() {
|
|
||||||
elem.write(ShiftRegisterPin::new(self, index));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Everything is initialized. Transmute the array to the
|
|
||||||
// initialized type.
|
|
||||||
unsafe { mem::transmute::<_, [ShiftRegisterPin; $size]>(pins) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Consume the shift register and return the original clock, latch, and data output pins
|
|
||||||
pub fn release(self) -> (Pin1, Pin2, Pin3) {
|
|
||||||
let Self {
|
|
||||||
clock,
|
|
||||||
latch,
|
|
||||||
data,
|
|
||||||
output_state: _,
|
|
||||||
} = self;
|
|
||||||
(clock.into_inner(), latch.into_inner(), data.into_inner())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
ShiftRegisterBuilder!(ShiftRegister8, 8);
|
|
||||||
ShiftRegisterBuilder!(ShiftRegister16, 16);
|
|
||||||
ShiftRegisterBuilder!(ShiftRegister24, 24);
|
|
||||||
ShiftRegisterBuilder!(ShiftRegister32, 32);
|
|
||||||
ShiftRegisterBuilder!(ShiftRegister40, 40);
|
|
||||||
ShiftRegisterBuilder!(ShiftRegister48, 48);
|
|
||||||
ShiftRegisterBuilder!(ShiftRegister56, 56);
|
|
||||||
ShiftRegisterBuilder!(ShiftRegister64, 64);
|
|
||||||
ShiftRegisterBuilder!(ShiftRegister72, 72);
|
|
||||||
ShiftRegisterBuilder!(ShiftRegister80, 80);
|
|
||||||
ShiftRegisterBuilder!(ShiftRegister88, 88);
|
|
||||||
ShiftRegisterBuilder!(ShiftRegister96, 96);
|
|
||||||
ShiftRegisterBuilder!(ShiftRegister104, 104);
|
|
||||||
ShiftRegisterBuilder!(ShiftRegister112, 112);
|
|
||||||
ShiftRegisterBuilder!(ShiftRegister120, 120);
|
|
||||||
ShiftRegisterBuilder!(ShiftRegister128, 128);
|
|
||||||
|
|
||||||
/// 8 output serial-in parallel-out shift register
|
|
||||||
pub type ShiftRegister<Pin1, Pin2, Pin3> = ShiftRegister8<Pin1, Pin2, Pin3>;
|
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@ -1,78 +0,0 @@
|
|||||||
{
|
|
||||||
"board": {
|
|
||||||
"active_layer": 31,
|
|
||||||
"active_layer_preset": "All Layers",
|
|
||||||
"auto_track_width": false,
|
|
||||||
"hidden_netclasses": [],
|
|
||||||
"hidden_nets": [],
|
|
||||||
"high_contrast_mode": 0,
|
|
||||||
"net_color_mode": 1,
|
|
||||||
"opacity": {
|
|
||||||
"images": 0.6,
|
|
||||||
"pads": 1.0,
|
|
||||||
"tracks": 1.0,
|
|
||||||
"vias": 1.0,
|
|
||||||
"zones": 0.6
|
|
||||||
},
|
|
||||||
"ratsnest_display_mode": 0,
|
|
||||||
"selection_filter": {
|
|
||||||
"dimensions": true,
|
|
||||||
"footprints": true,
|
|
||||||
"graphics": true,
|
|
||||||
"keepouts": true,
|
|
||||||
"lockedItems": true,
|
|
||||||
"otherItems": true,
|
|
||||||
"pads": true,
|
|
||||||
"text": true,
|
|
||||||
"tracks": true,
|
|
||||||
"vias": true,
|
|
||||||
"zones": true
|
|
||||||
},
|
|
||||||
"visible_items": [
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
2,
|
|
||||||
3,
|
|
||||||
4,
|
|
||||||
5,
|
|
||||||
8,
|
|
||||||
9,
|
|
||||||
10,
|
|
||||||
11,
|
|
||||||
12,
|
|
||||||
13,
|
|
||||||
15,
|
|
||||||
16,
|
|
||||||
17,
|
|
||||||
18,
|
|
||||||
19,
|
|
||||||
20,
|
|
||||||
21,
|
|
||||||
22,
|
|
||||||
23,
|
|
||||||
24,
|
|
||||||
25,
|
|
||||||
26,
|
|
||||||
27,
|
|
||||||
28,
|
|
||||||
29,
|
|
||||||
30,
|
|
||||||
32,
|
|
||||||
33,
|
|
||||||
34,
|
|
||||||
35,
|
|
||||||
36,
|
|
||||||
39,
|
|
||||||
40
|
|
||||||
],
|
|
||||||
"visible_layers": "fffffff_ffffffff",
|
|
||||||
"zone_display_mode": 0
|
|
||||||
},
|
|
||||||
"meta": {
|
|
||||||
"filename": "plantctrl-extension.kicad_prl",
|
|
||||||
"version": 3
|
|
||||||
},
|
|
||||||
"project": {
|
|
||||||
"files": []
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,495 +0,0 @@
|
|||||||
{
|
|
||||||
"board": {
|
|
||||||
"3dviewports": [],
|
|
||||||
"design_settings": {
|
|
||||||
"defaults": {
|
|
||||||
"board_outline_line_width": 0.09999999999999999,
|
|
||||||
"copper_line_width": 0.19999999999999998,
|
|
||||||
"copper_text_italic": false,
|
|
||||||
"copper_text_size_h": 1.5,
|
|
||||||
"copper_text_size_v": 1.5,
|
|
||||||
"copper_text_thickness": 0.3,
|
|
||||||
"copper_text_upright": false,
|
|
||||||
"courtyard_line_width": 0.049999999999999996,
|
|
||||||
"dimension_precision": 4,
|
|
||||||
"dimension_units": 3,
|
|
||||||
"dimensions": {
|
|
||||||
"arrow_length": 1270000,
|
|
||||||
"extension_offset": 500000,
|
|
||||||
"keep_text_aligned": true,
|
|
||||||
"suppress_zeroes": false,
|
|
||||||
"text_position": 0,
|
|
||||||
"units_format": 1
|
|
||||||
},
|
|
||||||
"fab_line_width": 0.09999999999999999,
|
|
||||||
"fab_text_italic": false,
|
|
||||||
"fab_text_size_h": 1.0,
|
|
||||||
"fab_text_size_v": 1.0,
|
|
||||||
"fab_text_thickness": 0.15,
|
|
||||||
"fab_text_upright": false,
|
|
||||||
"other_line_width": 0.15,
|
|
||||||
"other_text_italic": false,
|
|
||||||
"other_text_size_h": 1.0,
|
|
||||||
"other_text_size_v": 1.0,
|
|
||||||
"other_text_thickness": 0.15,
|
|
||||||
"other_text_upright": false,
|
|
||||||
"pads": {
|
|
||||||
"drill": 0.762,
|
|
||||||
"height": 1.524,
|
|
||||||
"width": 1.524
|
|
||||||
},
|
|
||||||
"silk_line_width": 0.15,
|
|
||||||
"silk_text_italic": false,
|
|
||||||
"silk_text_size_h": 1.0,
|
|
||||||
"silk_text_size_v": 1.0,
|
|
||||||
"silk_text_thickness": 0.15,
|
|
||||||
"silk_text_upright": false,
|
|
||||||
"zones": {
|
|
||||||
"45_degree_only": false,
|
|
||||||
"min_clearance": 0.508
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"diff_pair_dimensions": [
|
|
||||||
{
|
|
||||||
"gap": 0.0,
|
|
||||||
"via_gap": 0.0,
|
|
||||||
"width": 0.0
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"drc_exclusions": [],
|
|
||||||
"meta": {
|
|
||||||
"version": 2
|
|
||||||
},
|
|
||||||
"rule_severities": {
|
|
||||||
"annular_width": "error",
|
|
||||||
"clearance": "error",
|
|
||||||
"connection_width": "warning",
|
|
||||||
"copper_edge_clearance": "error",
|
|
||||||
"copper_sliver": "warning",
|
|
||||||
"courtyards_overlap": "error",
|
|
||||||
"diff_pair_gap_out_of_range": "error",
|
|
||||||
"diff_pair_uncoupled_length_too_long": "error",
|
|
||||||
"drill_out_of_range": "error",
|
|
||||||
"duplicate_footprints": "warning",
|
|
||||||
"extra_footprint": "warning",
|
|
||||||
"footprint": "error",
|
|
||||||
"footprint_type_mismatch": "error",
|
|
||||||
"hole_clearance": "error",
|
|
||||||
"hole_near_hole": "error",
|
|
||||||
"invalid_outline": "error",
|
|
||||||
"isolated_copper": "warning",
|
|
||||||
"item_on_disabled_layer": "error",
|
|
||||||
"items_not_allowed": "error",
|
|
||||||
"length_out_of_range": "error",
|
|
||||||
"lib_footprint_issues": "warning",
|
|
||||||
"lib_footprint_mismatch": "warning",
|
|
||||||
"malformed_courtyard": "error",
|
|
||||||
"microvia_drill_out_of_range": "error",
|
|
||||||
"missing_courtyard": "ignore",
|
|
||||||
"missing_footprint": "warning",
|
|
||||||
"net_conflict": "warning",
|
|
||||||
"npth_inside_courtyard": "ignore",
|
|
||||||
"padstack": "error",
|
|
||||||
"pth_inside_courtyard": "ignore",
|
|
||||||
"shorting_items": "error",
|
|
||||||
"silk_edge_clearance": "warning",
|
|
||||||
"silk_over_copper": "warning",
|
|
||||||
"silk_overlap": "warning",
|
|
||||||
"skew_out_of_range": "error",
|
|
||||||
"solder_mask_bridge": "error",
|
|
||||||
"starved_thermal": "error",
|
|
||||||
"text_height": "warning",
|
|
||||||
"text_thickness": "warning",
|
|
||||||
"through_hole_pad_without_hole": "error",
|
|
||||||
"too_many_vias": "error",
|
|
||||||
"track_dangling": "warning",
|
|
||||||
"track_width": "error",
|
|
||||||
"tracks_crossing": "error",
|
|
||||||
"unconnected_items": "error",
|
|
||||||
"unresolved_variable": "error",
|
|
||||||
"via_dangling": "warning",
|
|
||||||
"zones_intersect": "error"
|
|
||||||
},
|
|
||||||
"rules": {
|
|
||||||
"allow_blind_buried_vias": false,
|
|
||||||
"allow_microvias": false,
|
|
||||||
"max_error": 0.005,
|
|
||||||
"min_clearance": 0.0,
|
|
||||||
"min_connection": 0.0,
|
|
||||||
"min_copper_edge_clearance": 0.0,
|
|
||||||
"min_hole_clearance": 0.25,
|
|
||||||
"min_hole_to_hole": 0.25,
|
|
||||||
"min_microvia_diameter": 0.19999999999999998,
|
|
||||||
"min_microvia_drill": 0.09999999999999999,
|
|
||||||
"min_resolved_spokes": 2,
|
|
||||||
"min_silk_clearance": 0.0,
|
|
||||||
"min_text_height": 0.7999999999999999,
|
|
||||||
"min_text_thickness": 0.08,
|
|
||||||
"min_through_hole_diameter": 0.3,
|
|
||||||
"min_track_width": 0.19999999999999998,
|
|
||||||
"min_via_annular_width": 0.049999999999999996,
|
|
||||||
"min_via_diameter": 0.39999999999999997,
|
|
||||||
"solder_mask_clearance": 0.0,
|
|
||||||
"solder_mask_min_width": 0.0,
|
|
||||||
"solder_mask_to_copper_clearance": 0.0,
|
|
||||||
"use_height_for_length_calcs": true
|
|
||||||
},
|
|
||||||
"teardrop_options": [
|
|
||||||
{
|
|
||||||
"td_allow_use_two_tracks": true,
|
|
||||||
"td_curve_segcount": 5,
|
|
||||||
"td_on_pad_in_zone": false,
|
|
||||||
"td_onpadsmd": true,
|
|
||||||
"td_onroundshapesonly": false,
|
|
||||||
"td_ontrackend": false,
|
|
||||||
"td_onviapad": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"teardrop_parameters": [
|
|
||||||
{
|
|
||||||
"td_curve_segcount": 0,
|
|
||||||
"td_height_ratio": 1.0,
|
|
||||||
"td_length_ratio": 0.5,
|
|
||||||
"td_maxheight": 2.0,
|
|
||||||
"td_maxlen": 1.0,
|
|
||||||
"td_target_name": "td_round_shape",
|
|
||||||
"td_width_to_size_filter_ratio": 0.9
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"td_curve_segcount": 0,
|
|
||||||
"td_height_ratio": 1.0,
|
|
||||||
"td_length_ratio": 0.5,
|
|
||||||
"td_maxheight": 2.0,
|
|
||||||
"td_maxlen": 1.0,
|
|
||||||
"td_target_name": "td_rect_shape",
|
|
||||||
"td_width_to_size_filter_ratio": 0.9
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"td_curve_segcount": 0,
|
|
||||||
"td_height_ratio": 1.0,
|
|
||||||
"td_length_ratio": 0.5,
|
|
||||||
"td_maxheight": 2.0,
|
|
||||||
"td_maxlen": 1.0,
|
|
||||||
"td_target_name": "td_track_end",
|
|
||||||
"td_width_to_size_filter_ratio": 0.9
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"track_widths": [
|
|
||||||
0.0,
|
|
||||||
0.15,
|
|
||||||
0.5,
|
|
||||||
1.0
|
|
||||||
],
|
|
||||||
"via_dimensions": [
|
|
||||||
{
|
|
||||||
"diameter": 0.0,
|
|
||||||
"drill": 0.0
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"zones_allow_external_fillets": false,
|
|
||||||
"zones_use_no_outline": true
|
|
||||||
},
|
|
||||||
"layer_presets": [],
|
|
||||||
"viewports": []
|
|
||||||
},
|
|
||||||
"boards": [],
|
|
||||||
"cvpcb": {
|
|
||||||
"equivalence_files": []
|
|
||||||
},
|
|
||||||
"erc": {
|
|
||||||
"erc_exclusions": [],
|
|
||||||
"meta": {
|
|
||||||
"version": 0
|
|
||||||
},
|
|
||||||
"pin_map": [
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
2
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
2,
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
0,
|
|
||||||
2,
|
|
||||||
2,
|
|
||||||
2,
|
|
||||||
2
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
2
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
1,
|
|
||||||
2,
|
|
||||||
1,
|
|
||||||
1,
|
|
||||||
2
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
2
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
2
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1,
|
|
||||||
1,
|
|
||||||
1,
|
|
||||||
1,
|
|
||||||
1,
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
1,
|
|
||||||
1,
|
|
||||||
1,
|
|
||||||
1,
|
|
||||||
2
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
2
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
2,
|
|
||||||
1,
|
|
||||||
2,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
0,
|
|
||||||
2,
|
|
||||||
2,
|
|
||||||
2,
|
|
||||||
2
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
2,
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
0,
|
|
||||||
2,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
2
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
2,
|
|
||||||
1,
|
|
||||||
1,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
0,
|
|
||||||
2,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
2
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2,
|
|
||||||
2,
|
|
||||||
2,
|
|
||||||
2,
|
|
||||||
2,
|
|
||||||
2,
|
|
||||||
2,
|
|
||||||
2,
|
|
||||||
2,
|
|
||||||
2,
|
|
||||||
2,
|
|
||||||
2
|
|
||||||
]
|
|
||||||
],
|
|
||||||
"rule_severities": {
|
|
||||||
"bus_definition_conflict": "error",
|
|
||||||
"bus_entry_needed": "error",
|
|
||||||
"bus_label_syntax": "error",
|
|
||||||
"bus_to_bus_conflict": "error",
|
|
||||||
"bus_to_net_conflict": "error",
|
|
||||||
"different_unit_footprint": "error",
|
|
||||||
"different_unit_net": "error",
|
|
||||||
"duplicate_reference": "error",
|
|
||||||
"duplicate_sheet_names": "error",
|
|
||||||
"extra_units": "error",
|
|
||||||
"global_label_dangling": "warning",
|
|
||||||
"hier_label_mismatch": "error",
|
|
||||||
"label_dangling": "error",
|
|
||||||
"lib_symbol_issues": "warning",
|
|
||||||
"multiple_net_names": "warning",
|
|
||||||
"net_not_bus_member": "warning",
|
|
||||||
"no_connect_connected": "warning",
|
|
||||||
"no_connect_dangling": "warning",
|
|
||||||
"pin_not_connected": "error",
|
|
||||||
"pin_not_driven": "error",
|
|
||||||
"pin_to_pin": "warning",
|
|
||||||
"power_pin_not_driven": "error",
|
|
||||||
"similar_labels": "warning",
|
|
||||||
"unannotated": "error",
|
|
||||||
"unit_value_mismatch": "error",
|
|
||||||
"unresolved_variable": "error",
|
|
||||||
"wire_dangling": "error"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"libraries": {
|
|
||||||
"pinned_footprint_libs": [],
|
|
||||||
"pinned_symbol_libs": []
|
|
||||||
},
|
|
||||||
"meta": {
|
|
||||||
"filename": "plantctrl-extension.kicad_pro",
|
|
||||||
"version": 1
|
|
||||||
},
|
|
||||||
"net_settings": {
|
|
||||||
"classes": [
|
|
||||||
{
|
|
||||||
"bus_width": 12,
|
|
||||||
"clearance": 0.2,
|
|
||||||
"diff_pair_gap": 0.25,
|
|
||||||
"diff_pair_via_gap": 0.25,
|
|
||||||
"diff_pair_width": 0.2,
|
|
||||||
"line_style": 0,
|
|
||||||
"microvia_diameter": 0.3,
|
|
||||||
"microvia_drill": 0.1,
|
|
||||||
"name": "Default",
|
|
||||||
"pcb_color": "rgba(0, 0, 0, 0.000)",
|
|
||||||
"schematic_color": "rgba(0, 0, 0, 0.000)",
|
|
||||||
"track_width": 0.25,
|
|
||||||
"via_diameter": 0.8,
|
|
||||||
"via_drill": 0.4,
|
|
||||||
"wire_width": 6
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"meta": {
|
|
||||||
"version": 3
|
|
||||||
},
|
|
||||||
"net_colors": null,
|
|
||||||
"netclass_assignments": null,
|
|
||||||
"netclass_patterns": []
|
|
||||||
},
|
|
||||||
"pcbnew": {
|
|
||||||
"last_paths": {
|
|
||||||
"gencad": "",
|
|
||||||
"idf": "",
|
|
||||||
"netlist": "",
|
|
||||||
"specctra_dsn": "",
|
|
||||||
"step": "",
|
|
||||||
"vrml": ""
|
|
||||||
},
|
|
||||||
"page_layout_descr_file": ""
|
|
||||||
},
|
|
||||||
"schematic": {
|
|
||||||
"annotate_start_num": 0,
|
|
||||||
"drawing": {
|
|
||||||
"default_line_thickness": 6.0,
|
|
||||||
"default_text_size": 50.0,
|
|
||||||
"field_names": [],
|
|
||||||
"intersheets_ref_own_page": false,
|
|
||||||
"intersheets_ref_prefix": "",
|
|
||||||
"intersheets_ref_short": false,
|
|
||||||
"intersheets_ref_show": false,
|
|
||||||
"intersheets_ref_suffix": "",
|
|
||||||
"junction_size_choice": 3,
|
|
||||||
"label_size_ratio": 0.375,
|
|
||||||
"pin_symbol_size": 25.0,
|
|
||||||
"text_offset_ratio": 0.15
|
|
||||||
},
|
|
||||||
"legacy_lib_dir": "",
|
|
||||||
"legacy_lib_list": [],
|
|
||||||
"meta": {
|
|
||||||
"version": 1
|
|
||||||
},
|
|
||||||
"net_format_name": "",
|
|
||||||
"ngspice": {
|
|
||||||
"fix_include_paths": true,
|
|
||||||
"fix_passive_vals": false,
|
|
||||||
"meta": {
|
|
||||||
"version": 0
|
|
||||||
},
|
|
||||||
"model_mode": 0,
|
|
||||||
"workbook_filename": ""
|
|
||||||
},
|
|
||||||
"page_layout_descr_file": "",
|
|
||||||
"plot_directory": "",
|
|
||||||
"spice_adjust_passive_values": false,
|
|
||||||
"spice_external_command": "spice \"%I\"",
|
|
||||||
"subpart_first_id": 65,
|
|
||||||
"subpart_id_separator": 0
|
|
||||||
},
|
|
||||||
"sheets": [
|
|
||||||
[
|
|
||||||
"e63e39d7-6ac0-4ffd-8aa3-1841a4541b55",
|
|
||||||
""
|
|
||||||
]
|
|
||||||
],
|
|
||||||
"text_variables": {}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -5,10 +5,10 @@ target = "riscv32imac-esp-espidf"
|
|||||||
[target.riscv32imac-esp-espidf]
|
[target.riscv32imac-esp-espidf]
|
||||||
linker = "ldproxy"
|
linker = "ldproxy"
|
||||||
# runner = "espflash flash --monitor --partition-table partitions.csv -b no-reset" # Select this runner for espflash v2.x.x
|
# runner = "espflash flash --monitor --partition-table partitions.csv -b no-reset" # Select this runner for espflash v2.x.x
|
||||||
runner = "espflash flash --monitor --baud 921600 --partition-table partitions.csv -b no-reset" # Select this runner for espflash v2.x.x
|
# runner = "espflash flash --monitor --baud 921600 --partition-table partitions.csv -b no-reset" # Select this runner for espflash v2.x.x
|
||||||
# runner = espflash erase-parts otadata
|
# runner = espflash erase-parts otadata
|
||||||
|
|
||||||
# runner = "cargo runner"
|
runner = "cargo runner"
|
||||||
rustflags = [ "--cfg", "espidf_time64"] # Extending time_t for ESP IDF 5: https://github.com/esp-rs/rust/issues/110
|
rustflags = [ "--cfg", "espidf_time64"] # Extending time_t for ESP IDF 5: https://github.com/esp-rs/rust/issues/110
|
||||||
|
|
||||||
[unstable]
|
[unstable]
|
||||||
|
@ -386,7 +386,6 @@ fn safe_main() -> anyhow::Result<()> {
|
|||||||
&mut board,
|
&mut board,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
let stay_alive = STAY_ALIVE.load(std::sync::atomic::Ordering::Relaxed);
|
let stay_alive = STAY_ALIVE.load(std::sync::atomic::Ordering::Relaxed);
|
||||||
println!("Check stay alive, current state is {}", stay_alive);
|
println!("Check stay alive, current state is {}", stay_alive);
|
||||||
if stay_alive {
|
if stay_alive {
|
||||||
|
Loading…
Reference in New Issue
Block a user