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": {
|
||||
"active_layer": 31,
|
||||
"active_layer": 36,
|
||||
"active_layer_preset": "",
|
||||
"auto_track_width": false,
|
||||
"hidden_netclasses": [],
|
||||
@ -8,7 +8,7 @@
|
||||
"high_contrast_mode": 0,
|
||||
"net_color_mode": 1,
|
||||
"opacity": {
|
||||
"images": 0.6,
|
||||
"images": 0.4399999976158142,
|
||||
"pads": 1.0,
|
||||
"tracks": 1.0,
|
||||
"vias": 1.0,
|
||||
@ -68,7 +68,7 @@
|
||||
39,
|
||||
40
|
||||
],
|
||||
"visible_layers": "ffcc02f_ffffffff",
|
||||
"visible_layers": "ffc7055_fffffff8",
|
||||
"zone_display_mode": 1
|
||||
},
|
||||
"git": {
|
||||
|
@ -59,7 +59,23 @@
|
||||
}
|
||||
],
|
||||
"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": {
|
||||
"filename": "board_design_settings.json",
|
||||
@ -98,7 +114,7 @@
|
||||
"padstack": "warning",
|
||||
"pth_inside_courtyard": "ignore",
|
||||
"shorting_items": "error",
|
||||
"silk_edge_clearance": "warning",
|
||||
"silk_edge_clearance": "ignore",
|
||||
"silk_over_copper": "ignore",
|
||||
"silk_overlap": "ignore",
|
||||
"skew_out_of_range": "error",
|
||||
@ -1131,7 +1147,7 @@
|
||||
"group_by": false,
|
||||
"label": "LCSC",
|
||||
"name": "LCSC",
|
||||
"show": true
|
||||
"show": false
|
||||
},
|
||||
{
|
||||
"group_by": false,
|
||||
@ -1228,7 +1244,7 @@
|
||||
"group_symbols": true,
|
||||
"name": "",
|
||||
"sort_asc": true,
|
||||
"sort_field": "Reference"
|
||||
"sort_field": "LCSC_PART_NUMBER"
|
||||
},
|
||||
"connection_grid_size": 50.0,
|
||||
"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]
|
||||
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 --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 = "cargo runner"
|
||||
runner = "cargo runner"
|
||||
rustflags = [ "--cfg", "espidf_time64"] # Extending time_t for ESP IDF 5: https://github.com/esp-rs/rust/issues/110
|
||||
|
||||
[unstable]
|
||||
|
@ -386,7 +386,6 @@ fn safe_main() -> anyhow::Result<()> {
|
||||
&mut board,
|
||||
);
|
||||
|
||||
|
||||
let stay_alive = STAY_ALIVE.load(std::sync::atomic::Ordering::Relaxed);
|
||||
println!("Check stay alive, current state is {}", stay_alive);
|
||||
if stay_alive {
|
||||
|
Loading…
Reference in New Issue
Block a user