cleanup the mess, add missing logo files

This commit is contained in:
Marcus (DeltaLima) 2025-03-21 02:29:08 +01:00
parent 28df687bf9
commit 5e36bb9765
87 changed files with 0 additions and 117547 deletions
Arduino/CanGrow
KiCad/CanGrow
Logo

View file

@ -1,61 +0,0 @@
[editor]
line_wrapping=false
line_break_column=72
auto_continue_multiline=true
[file_prefs]
final_new_line=true
ensure_convert_new_lines=false
strip_trailing_spaces=false
replace_tabs=false
[indentation]
indent_width=2
indent_type=0
indent_hard_tab_width=8
detect_indent=false
detect_indent_width=false
indent_mode=2
[project]
name=CanGrow
base_path=./
description=
file_patterns=.ino,;.h;
[long line marker]
long_line_behaviour=1
long_line_column=72
[files]
current_page=0
FILE_NAME_0=493;Sh;0;EUTF-8;0;1;0;.%2Fcangrow.sh;0;2
FILE_NAME_1=0;Arduino;0;EUTF-8;0;1;0;.%2FCanGrow.ino;0;2
FILE_NAME_2=0;C++;0;EUTF-8;0;1;0;.%2Finclude%2FCanGrow.h;0;2
FILE_NAME_3=0;C++;0;EUTF-8;0;1;0;.%2Finclude%2FCanGrow_Core.h;0;2
FILE_NAME_4=0;C++;0;EUTF-8;0;1;0;.%2Finclude%2FCanGrow_ESP32.h;0;2
FILE_NAME_5=0;C++;0;EUTF-8;0;1;0;.%2Finclude%2FCanGrow_ESP8266.h;0;2
FILE_NAME_6=0;C++;0;EUTF-8;0;1;0;.%2Finclude%2FCanGrow_LittleFS.h;0;2
FILE_NAME_7=0;C++;0;EUTF-8;0;1;0;.%2Finclude%2FCanGrow_Logo.h;0;2
FILE_NAME_8=0;C++;0;EUTF-8;0;1;0;.%2Finclude%2FCanGrow_Version.h;0;2
[build-menu]
C++FT_00_LB=_Compile
C++FT_00_CM=cd .. ; ./cangrow.sh build
C++FT_00_WD=
filetypes=C++;Arduino;Sh;
ArduinoFT_00_LB=_Build
ArduinoFT_00_CM=./cangrow.sh build
ArduinoFT_00_WD=
ArduinoFT_01_LB=Build & Upload
ArduinoFT_01_CM=./cangrow.sh upload
ArduinoFT_01_WD=
C++FT_01_LB=_Build & Upload
C++FT_01_CM=cd .. ; ./cangrow.sh upload
C++FT_01_WD=
ShFT_00_LB=Build
ShFT_00_CM=./cangrow.sh build
ShFT_00_WD=
ShFT_01_LB=Build & Upload
ShFT_01_CM=./cangrow.sh upload
ShFT_01_WD=

View file

@ -1,216 +0,0 @@
/*
*
* CanGrow - an OpenSource growcontroller firmware (for cannabis)
*
*
* MIT License
*
* Copyright (c) 2024 DeltaLima
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
/*
* Libraries include
*/
#include "Arduino.h"
// * ESP8266 *
#ifdef ESP8266
#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>
#include <ESP8266HTTPClient.h>
#include <WiFiClient.h>
#endif
// * ESP32 *
#ifdef ESP32
#include <WiFi.h>
#include <AsyncTCP.h>
#include <Update.h>
#include <HTTPClient.h>
#endif
#include <WiFiUdp.h>
// https://github.com/thijse/Arduino-Log/
#include <ArduinoLog.h>
// https://github.com/mathieucarbou/ESPAsyncWebServer
#include <ESPAsyncWebServer.h>
// LittleFS filesystem
#include "FS.h"
// arduino-core for esp8266 and esp32
#include "LittleFS.h"
//#include <SPI.h>
#include <Wire.h>
// https://github.com/bblanchon/ArduinoJson
#include <ArduinoJson.h>
#include "AsyncJson.h"
// https://github.com/PaulStoffregen/Time
#include <TimeLib.h>
// https://github.com/arduino-libraries/NTPClient/
#include <NTPClient.h>
// https://github.com/nusabot-iot/NusabotSimpleTimer/
#include <NusabotSimpleTimer.h>
// https://github.com/adafruit/RTClib/
#include "RTClib.h"
/*
* CanGrow includes
*/
/* main header file, where all variables, consts and structs get defined */
#include "include/CanGrow.h"
/* CanGrow platform specific includes */
#include "include/Architecture/ESP8266.h"
#include "include/Architecture/ESP32.h"
#include "include/Architecture/ESP32_LOLIN_S2_MINI.h"
#include "include/Architecture/ESP32_MAKERGO_C3_SUPERMINI.h"
/* CanGrow header with all functions
* order is important - I need to learn how to do it right, so order is not important */
#include "include/CanGrow_ConfigHelper.h"
#include "include/CanGrow_Sensor.h"
#include "include/CanGrow_Output.h"
#include "include/CanGrow_Core.h"
#include "include/CanGrow_Wifi.h"
#include "include/CanGrow_LittleFS.h"
#include "include/CanGrow_Control.h"
#include "include/CanGrow_Timer.h"
#include "include/CanGrow_Webserver.h"
void setup() {
/* Measure start up time */
unsigned long millisFinish;
// define output for onboard LED/WIPE pin
pinMode(PinWIPE, OUTPUT);
// Start Serial
Serial.begin(115200);
// Write a line before doing serious output, because before there is some garbage in serial
// whats get the cursor somewhere over the place
Serial.println("420");
// initiate ArduinoLog
Log.setPrefix(LogPrefix);
Log.begin(LOG_LEVEL_VERBOSE, &Serial);
// disable show loglevel, we do it in Prefix
Log.setShowLevel(false);
// set Log Location, to tell user at which part of the code we are
const char LogLoc[] = "[SETUP]";
//Serial.printf(".:: CanGrow firmware v%s build %s starting ::.\n", CANGROW_VER, CANGROW_BUILD);
Log.notice(F("CanGrow firmware v%s build %s starting ::" CR), CANGROW_VER, CANGROW_BUILD);
Log.warning(F("%s To format / factory reset LittleFS, pull GPIO %d (PinWIPE) to %d - NOW! (2 seconds left)" CR), LogLoc, PinWIPE, 1 - PinWIPE_default );
// blink with the onboard LED on D4/GPIO2 (PinWIPE)
for(byte i = 0; i <= 6 ; i++) {
if(i % 2) {
digitalWrite(PinWIPE, 1 - PinWIPE_default);
} else {
digitalWrite(PinWIPE, PinWIPE_default);
}
delay(333);
}
// set PinWIPE back to its default
digitalWrite(PinWIPE, PinWIPE_default);
// read status from PinWIPE to WIPE
// when PinWIPE is set to LOW, format LittleFS
if(digitalRead(PinWIPE) != PinWIPE_default) {
LFS_Format();
Restart();
}
/* for ESP32-C3 supermini board compatibility, we initiate I2C here and not at the beginning
* ESP32-C3 supermini board shares GPIO 8 Internal LED with I2C SDA */
/* I2C init*/
Wire.begin(Pin_I2C_SDA, Pin_I2C_SCL);
LFS_Init();
LoadConfig();
Wifi_Init();
Webserver_Init();
Log.notice(F("%s Usable Pins: %d" CR), LogLoc, GPIOindex_length);
// List all available pins
for(byte i = 1; i <= GPIOindex_length; i++) {
Log.notice(F("%s Pin Index: %d, GPIO: %d, Notes: %s" CR), LogLoc, i , GPIOindex[i].gpio, GPIO_Index_note_descr[GPIOindex[i].note]);
}
// time init
Time_Init();
TimeR_Init();
#ifdef ESP8266
/* set pwm frequency global for ESP8266.
* ESP32 pwm frequency setting is done withing CanGrow_Output / Init */
analogWriteFreq(config.system.pwmFreq);
#endif
Output_Init();
Sensor_Init();
Log.notice(F("%s Done. Startup took : %u ms" CR), LogLoc, millis());
}
bool alrdySaved = false;
void loop() {
const char LogLoc[] = "[LOOP]";
/* Execute main timer, runs Timer_1s, Timer_3s, Timer_5s by default */
timer.run();
// if global var doRestart is true, perform a restart
if(doRestart == true) {
/* wait 100ms after Restart got triggered. This should workaround some crash problems with AsyncWebserver stuff
* for example when updating the firmware by web upload */
Log.verbose(F("%s Restart got triggered. Waiting 100ms before doing it" CR), LogLoc);
timer.setTimeout(100, Restart);
//Restart();
}
// does ntp offset need an update?
if(updateNtpOffset) {
/* doing ntp offset update here, because when doing it in the webserver:system function
* where the new value gets entered, it sometimes crashed */
NTP_OffsetUpdate();
updateNtpOffset = false;
}
}

View file

@ -1,53 +0,0 @@
# CanGrow - An OpenSource grow controller firmware for ESP8266 / ESP32
![Screenshot_montage.png](Screenshot_montage.png)
## Build environment
The helper script `cangrow.sh` is written for a Debian 12 system.
To install all dependencies you need for building the firmware, run the cangrow.sh setup:
```sh
$ ./cangrow.sh help
./cangrow.sh [setup|build|upload|webupload|monitor]
setup: setup build environment, download arduino-cli, install all dependencies for arduino ide
build: build firmware binary. will be saved into build/
upload: upload firmware by serial connection /dev/ttyUSB0
webupload: upload firmware with webupload to 192.168.4.20
monitor: serial monitor /dev/ttyUSB0
# Install all dependencies for build environment
$ ./cangrow.sh setup
```
The script installs [arduino-cli](https://github.com/arduino/arduino-cli) to `~/.local/bin/arduino-cli`.
## Compile
```sh
# compile and output to build/CanGrow_v0.2...bin
# Default Target is ESP8266 D1 Mini
$ ./cangrow.sh build
# Compile for ESP32 D1 Mini
$ export BOARD="esp32:esp32:d1_mini32"
$ ./cangrow.sh build
# Build and webupload to IP
$ export IP="192.168.4.69"
$ ./cangrow.sh build # need to make .bin first
$ ./cangrow.sh webupload # upload
# listen to serial monitor on /dev/ttyUSB2
$ export TTY="/dev/ttyUSB2"
./cangrow.sh monitor
```
I wrote this project using [Geany IDE. ](https://www.geany.org/). The Geany Projectfile is also included, just run
```sh
$ geany CanGrow.geany
```
**F8 compiles** the project, **F9 uploads** firmware to /dev/ttyUSB0. You can change these settings for .ino and .h files
in Project -> Settings -> Create/Make.

Binary file not shown.

Before

(image error) Size: 940 KiB

View file

@ -1,4 +0,0 @@
board_manager:
additional_urls:
- http://arduino.esp8266.com/stable/package_esp8266com_index.json
- https://espressif.github.io/arduino-esp32/package_esp32_index.json

View file

@ -1,167 +0,0 @@
#!/bin/bash
#
test -z $TTY && TTY="/dev/ttyUSB0"
test -z $IP && IP="192.168.4.20"
test -z $VER && VER="$(grep "define CANGROW_VER" include/CanGrow.h | cut -d \" -f2 |sed -e 's/\"//g')" #VER="0.2-dev"
test -z $BOARD && BOARD="esp8266:esp8266:d1_mini_clone"
#test -z $BOARD && BOARD="esp32:esp32:d1_mini32"
BUILD="$(git rev-parse --short HEAD)-$(echo $BOARD | cut -d : -f1)_$(echo $BOARD | cut -d : -f3)-$(date '+%Y%m%d%H%M%S')"
# arduino-cli path and version
ACLI="$HOME/.local/bin/arduino-cli"
ACLI_VER="1.2.0"
ACLI_CMD="$ACLI --config-file arduino-cli.yml"
test -z $BUILDDIR && BUILDDIR="build"
function help() {
echo "$0 [setup|build|upload|webupload|monitor]"
echo "setup: setup build environment, download arduino-cli, install all dependencies for arduino ide"
echo "build: build firmware binary. will be saved into ${BUILDDIR}/"
echo "upload: upload firmware by serial connection $TTY"
echo "webupload: upload firmware with webupload to $IP"
echo "monitor: serial monitor $TTY"
exit 1
}
function check_acli() {
if [ ! -x $ACLI ]
then
echo "$ACLI does not exist nor is executable. Please run '$0 setup' first"
exit 1
fi
}
test -z $1 && help
case $1 in
s|setup)
ACLI_DIR="$(dirname $ACLI)"
ALIB_DIR="${HOME}/Arduino/libraries/"
declare -a CORES=(
"esp8266:esp8266@3.1.2"
"esp32:esp32@3.0.7"
)
declare -a LIBS=(
"Adafruit SSD1306@2.5.12"
"Adafruit BME280 Library@2.2.4"
"ArduinoJson@7.3.0"
"NTPClient@3.2.1"
"Time@1.6.1"
"ESP Async WebServer@3.6.0"
"Async TCP@3.3.2"
"Nusabot Simple Timer@1.0.0"
"ArduinoLog@1.1.1"
"RTClib@2.1.4"
"Adafruit BME680 Library@2.0.5"
"Adafruit ADS1X15@2.5.0"
"Adafruit SHT31 Library@2.2.2"
"Adafruit MCP4725@2.0.2"
"Adafruit TCS34725@1.4.4"
"Adafruit MLX90614 Library@2.1.5"
"I2CSoilMoistureSensor@1.1.4"
"DFRobot_GP8XXX@1.0.1"
"Adafruit CCS811 Library@1.1.3"
)
echo ":: Setting up build environment for CanGrow Firmware."
echo " This will download the binary for arduino-cli and install"
echo " the packages for the arduino ide from the debian repository."
echo " !! This script is meant to be executed on a Debian stable (bookworm) system !!"
echo ""
echo ":: Press Enter to continue"
read
echo ""
echo ":: Installing Arduino IDE packages with apt, please enter sudo password:"
sudo apt update || exit 1
sudo apt install arduino python3 python3-serial wget curl xxd || exit 1
echo ":: Ensure directory ${ACLI_DIR} is present"
test -d ${ACLI_DIR} || mkdir -p ${ACLI_DIR}
echo ":: Please ensure ${ACLI_DIR} is in your \$PATH, I wont do it."
echo ""
echo ":: Downloading arduino-cli ${ACLI_VER} into ${ACLI_DIR}/"
wget -O - "https://github.com/arduino/arduino-cli/releases/download/v${ACLI_VER}/arduino-cli_${ACLI_VER}_Linux_64bit.tar.gz" | tar -C ${ACLI_DIR} -zxvf - arduino-cli
chmod +x ${ACLI}
echo ""
echo ":: Installing ESP8266 and ESP32 cores for Arduino"
for core in ${!CORES[@]}
do
${ACLI_CMD} core install ${CORES[$core]}
done
echo ":: Installing Arduino libraries"
${ACLI_CMD} lib update-index || exit 1
for lib in ${!LIBS[@]}
do
echo " - ${LIBS[$lib]}"
done
for lib in ${!LIBS[@]}
do
${ACLI_CMD} lib install "${LIBS[$lib]}" || exit 1
done
echo ""
echo ":: fetching ESPAsyncTCP-esphome from GIT"
wget -q https://github.com/mathieucarbou/esphome-ESPAsyncTCP/archive/refs/tags/v2.0.0.tar.gz -O - | tar -xzf - -C $ALIB_DIR
mv $ALIB_DIR/esphome-ESPAsyncTCP-2.0.0 $ALIB_DIR/ESPAsyncTCP-esphome
echo ":: Patching ArduinoLog (https://github.com/thijse/Arduino-Log/pull/28/commits/57d350a25428376935b793a2138210320cf3801c)"
sed -i -e 's/register//g' $ALIB_DIR/ArduinoLog/ArduinoLog.cpp
echo ":: Setup build environment done! You can now build the firmware"
echo " with: $0 build"
;;
b|build)
check_acli
ACLI_CMD="${ACLI_CMD} --output-dir ${BUILDDIR}"
echo ":: Building firmware $VER $BUILD, target dir: ${BUILDDIR}/"
test -d ${BUILDDIR} || mkdir ${BUILDDIR}
# esp8266 and esp32 compiler have to use different compile flags for VER and BUILD
if [ "$(echo $BOARD | cut -d : -f1)" == "esp8266" ]
then
${ACLI_CMD} --no-color compile -b ${BOARD} --build-property "build.extra_flags=-DCANGROW_VER=\"${VER}\" -DCANGROW_BUILD=\"${BUILD}\"" "CanGrow.ino" || exit 1
elif [ "$(echo $BOARD | cut -d : -f1)" == "esp32" ]
then
${ACLI_CMD} --no-color compile -b ${BOARD} --build-property "build.defines=-DCANGROW_VER=\"${VER}\" -DCANGROW_BUILD=\"${BUILD}\"" "CanGrow.ino" || exit 1
fi
cp ${BUILDDIR}/CanGrow.ino.bin ${BUILDDIR}/CanGrow_v${VER}_${BUILD}.bin
;;
u|upload)
check_acli
echo ":: Build and upload firmware $VER $BUILD to $TTY"
test -d build || mkdir build
# esp8266 and esp32 compiler have to use different compile flags for VER and BUILD
if [ "$(echo $BOARD | cut -d : -f1)" == "esp8266" ]
then
${ACLI_CMD} --no-color compile -b ${BOARD} --build-property "build.extra_flags=-DCANGROW_VER=\"${VER}\" -DCANGROW_BUILD=\"${BUILD}\"" ${ACLI_BUILD_OPTS} -u -p $TTY "CanGrow.ino"
elif [ "$(echo $BOARD | cut -d : -f1)" == "esp32" ]
then
${ACLI_CMD} --no-color compile -b ${BOARD} --build-property "build.defines=-DCANGROW_VER=\"${VER}\" -DCANGROW_BUILD=\"${BUILD}\"" ${ACLI_BUILD_OPTS} -u -p $TTY "CanGrow.ino"
fi
;;
w|webupload)
test -z "$2" && UPLOAD_FILE="${BUILDDIR}/CanGrow.ino.bin"
test -n "$2" && UPLOAD_FILE="$2"
echo ":: Uploading $UPLOAD_FILE to $IP"
curl -v http://$IP/system/update -X POST -H 'Content-Type: multipart/form-data' -F "firmware=@${UPLOAD_FILE}"
echo
;;
m|mon|monitor)
check_acli
echo ":: Open serial monitor $TTY"
${ACLI_CMD} monitor -c baudrate=115200 -b ${BOARD} -p $TTY
;;
*)
help
;;
esac

View file

@ -1,69 +0,0 @@
/*
*
* include/CanGrow_ESP32.h - ESP32 specific header file for generic ESP32_DEV board
*
*
*
*
*/
#if defined(ARDUINO_ESP32_DEV) || defined(ARDUINO_D1_MINI32)
#define PinWIPE 2
#define PinWIPE_default LOW
#define Pin_I2C_SCL 22
#define Pin_I2C_SDA 21
/* https://randomnerdtutorials.com/esp32-pinout-reference-gpios/
*
* free usable pins
* - GPIO 0 PU OK outputs PWM signal at boot, must be LOW to enter flashing mode
* - GPIO 4 OK OK
* - GPIO 5 OK OK outputs PWM signal at boot, strapping pin
* - GPIO 12 OK OK boot fails if pulled high, strapping pin
* - GPIO 13 OK OK
* - GPIO 14 OK OK outputs PWM signal at boot
* - GPIO 15 OK OK outputs PWM signal at boot, strapping pin
* - GPIO 16 OK OK
* - GPIO 17 OK OK
* - GPIO 18 OK OK
* - GPIO 19 OK OK
* - GPIO 23 OK OK
* - GPIO 25 OK OK
* - GPIO 26 OK OK
* - GPIO 27 OK OK
* - GPIO 32 OK OK
* - GPIO 33 OK OK
* - GPIO 34 OK input only
* - GPIO 35 OK input only
* - GPIO 36 OK input only
* - GPIO 39 OK input only
*/
//
const byte GPIOindex_length = 21;
// initialize pinIndex with all usable GPIOs
GPIO_Index GPIOindex[] = {{ 255, 255 },
{ 0, FLASHMODE_LOW },
{ 4 },
{ 5 },
{ 12, BOOTFAILS_HIGH },
{ 13 },
{ 14 },
{ 15 },
{ 16 },
{ 17 },
{ 18 },
{ 19 },
{ 23 },
{ 25, INT_DAC },
{ 26, INT_DAC },
{ 27 },
{ 32, INT_ADC },
{ 33, INT_ADC },
{ 34, INPUT_ONLY },
{ 35, INPUT_ONLY },
{ 36, INPUT_ONLY },
{ 39, INPUT_ONLY }
};
#endif

View file

@ -1,74 +0,0 @@
/*
*
* include/CanGrow_ESP32.h - ESP32 specific header file for Lolin S2 Mini
*
*
*
*/
#ifdef ARDUINO_LOLIN_S2_MINI
#define PinWIPE 15
#define PinWIPE_default LOW
#define Pin_I2C_SCL 33
#define Pin_I2C_SDA 35
/* https://done.land/components/microcontroller/families/esp/esp32/developmentboards/esp32-s2/s2mini/
*
* free usable pins
Pin Remark Description
EN Reset button
3V3 direct power supply to CPU
VBUS connected to ME6211C33 voltage regulator
0 not exposed Boot button pulls it low
1-6 general purpose: analog input (ADC1) and digital in/output
7 SPI SCK general purpose: analog input (ADC1) and digital in/output
8 general purpose: analog input (ADC1) and digital in/output
9 SPI MISO general purpose: analog input (ADC1) and digital in/output
10 general purpose: analog input (ADC1) and digital in/output
11 SPI MOSI general purpose: analog input (ADC2) and digital in/output
12 SPI SS general purpose: analog input (ADC2) and digital in/output
13-14 general purpose: analog input (ADC2) and digital in/output
15 internal LED general purpose: analog input (ADC2) and digital in/output
16 general purpose: analog input (ADC2) and digital in/output
17 DAC1 general purpose: analog input (ADC2) and digital in/output
18 DAC2 general purpose: analog input (ADC2) and digital in/output
19, 20 not exposed USB D1/D2, connected to the USB C connector
21 general purpose digital in/output
33 I2C SDA general purpose digital in/output
34 general purpose digital in/output
35 I2C SCL general purpose digital in/output
36-40 general purpose digital in/output
*/
//
const byte GPIOindex_length = 24;
// initialize pinIndex with all usable GPIOs
GPIO_Index GPIOindex[] = {{ 255, 255 },
{ 1, INT_ADC },
{ 2, INT_ADC },
{ 3, INT_ADC },
{ 4, INT_ADC },
{ 5, INT_ADC },
{ 6, INT_ADC },
{ 7, INT_ADC },
{ 8, INT_ADC },
{ 9, INT_ADC },
{ 10, INT_ADC },
{ 11 },
{ 12 },
{ 13 },
{ 14 },
{ 16 },
{ 17, INT_DAC },
{ 18, INT_DAC },
{ 21 },
{ 34 },
{ 36 },
{ 37 },
{ 38 },
{ 39 },
{ 40 }
};
#endif

View file

@ -1,48 +0,0 @@
/*
*
* include/Platform/ESP32_MAKERGO_C3_SUPERMINI.h - ESP32 specific header file
*
*
*
*/
#ifdef ARDUINO_MAKERGO_C3_SUPERMINI
#define PinWIPE 8
#define PinWIPE_default HIGH
#define Pin_I2C_SCL 9
#define Pin_I2C_SDA 8
/* https://www.sudo.is/docs/esphome/boards/esp32c3supermini/
*
* free usable pins
0 GPIO0 ADC1
1 GPIO1 ADC1
2 GPIO2 ADC1, boot mode / strapping pin
3 GPIO3 ADC1
4 GPIO4 ADC1, JTAG
5 GPIO5 JTAG
6 GPIO6 JTAG
7 GPIO7 JTAG
8 GPIO8 Blue status_led (inverted), boot mode / strapping pin
9 GPIO9 Boot mode / strapping pin, boot button
10 GPIO10
20 GPIO20 RX
21 GPIO21 TX
*/
//
const byte GPIOindex_length = 9;
// initialize pinIndex with all usable GPIOs
GPIO_Index GPIOindex[] = {{ 255, 255 },
{ 0, INT_ADC },
{ 1, INT_ADC },
{ 2, INT_ADC },
{ 3, INT_ADC },
{ 4, INT_ADC },
{ 5 },
{ 6 },
{ 7 },
{ 10 }
};
#endif

View file

@ -1,51 +0,0 @@
/*
*
* include/CanGrow_ESP8266.h - ESP8266 specific header file
*
*
*
*/
#ifdef ESP8266
// GPIO 2 Boot fails if pulled to LOW
#define PinWIPE 2
#define PinWIPE_default HIGH
#define Pin_I2C_SCL 5
#define Pin_I2C_SDA 4
/* https://randomnerdtutorials.com/esp8266-pinout-reference-gpios/
*
* free usable pins
* - GPIO 0 / D3 boot fails if pulled LOW
* - GPIO 12 / D6
* - GPIO 13 / D7
* - GPIO 14 / D5
* - GPIO 15 / D8 Boot fails if pulled HIGH
* - GPIO 16 / D0
*/
const byte GPIOindex_length = 6;
// initialize pinIndex with all usable GPIOs
GPIO_Index GPIOindex[] = {{ 255, 255 },
{ 0, BOOTFAILS_LOW },
{ 12 },
{ 13 },
{ 14 },
{ 15, BOOTFAILS_HIGH },
{ 16, NO_PWM } };
#endif
/* CanGrow 12V PCB v0.6 Pin assignment
*
*
* LED - D6 (GPIO 12)
* FAN1 - D5 (GPIO 14)
* FAN2 - D3 (GPIO 0)
* PUMP - D0 (GPIO 16)
*
* WaterlevelVCC - D7 (GPIO 13)
* SoilmoistureVCC - D8 (GPIO 15)
*
*/

View file

@ -1,411 +0,0 @@
/*
*
* include/CanGrow.h - main header file
*
*
*
*/
/* If you need detailed debug output, uncomment the following lines.
* DEBUG is less noisy messages
* DEBUG2 are noisy messages
* DEBUG3 are super noisy messages */
//#define DEBUG
//#define DEBUG2
//#define DEBUG3
/* ensure the code will also compile when CANGROW_VER and CANGROW_BUILD
* are not defined by the compiler arguments
* like -DCANGROW_VER="0.x-dev" or -DCANGROW_BUILD="commitid-core-timestamp"
*/
/*
*
*
* Constants
*
*
*/
#ifndef CANGROW_VER
#define CANGROW_VER "0.2-dev2"
#endif
#ifndef CANGROW_BUILD
#define CANGROW_BUILD "0420"
#endif
#ifndef CANGROW_BUILDTIME
#define CANGROW_BUILDTIME "1711922400" // 1.4.2024
#endif
#define CANGROW_DEFAULT_WIFI_SSID "CanGrow-unconfigured"
#define CANGROW_DEFAULT_WIFI_PASSWORD "letitgrow!"
#define CANGROW_CFG "/config.json"
#define TIME2FS "/time"
/* define Max limits for outputs and sensors */
const byte Max_Outputs = 16;
const byte Max_Sensors = 16;
/* How much values can a sensor contain at max */
const byte Max_Sensors_Read = 6;
/* how much GPIOs a Sensor can use */
const byte Max_Sensors_GPIO = 2;
/* actual structure initialization for GPIO_Index is done within the header files
* for ESP32 and ESP8266
*
* GPIO_Index.note explenation:
* 1 - BOOTFAILS_LOW: BootFails when LOW
* 2 - BOOTFAILS_HIGH: BootFails when HIGH
* 3 - FLASHMODE_LOW: FlashMode needs LOW to enter
* 4 - INPUT_ONLY: Input Only
* 5 - NO_PWM: No PWM output
* 6 - PWM_BOOT: PWM at boot time
* 7 - INT_ADC Pin for internal ADC (only ESP32, ESP8266 only has one Pin, A0)
*/
const byte BOOTFAILS_LOW = 1;
const byte BOOTFAILS_HIGH = 2;
const byte FLASHMODE_LOW = 3;
const byte INPUT_ONLY = 4;
const byte NO_PWM = 5;
const byte HIGH_BOOT = 6;
const byte INT_ADC = 7;
const byte INT_DAC = 8;
//const char signMessage[] PROGMEM = {"I AM PREDATOR, UNSEEN COMBATANT. CREATED BY THE UNITED STATES DEPART"};
const char BOOTFAILS_LOW_descr[] PROGMEM = {"BF_LOW"};
const char BOOTFAILS_HIGH_descr[] PROGMEM = {"BF_HIGH"};
const char FLASMODE_LOW_descr[] PROGMEM = {"FM_LOW"};
const char INPUT_ONLY_descr[] PROGMEM = {"IN_ONLY"};
const char NO_PWM_descr[] PROGMEM = {"NO_PWM"};
const char HIGH_BOOT_descr[] PROGMEM = {"B_HIGH"};
const char INT_ADC_descr[] PROGMEM = {"INT_ADC"};
const char INT_DAC_descr[] PROGMEM = {"INT_DAC"};
const char * GPIO_Index_note_descr[] = {
NULL, // 0 - no note
BOOTFAILS_LOW_descr, // 1
BOOTFAILS_HIGH_descr, // 2
FLASMODE_LOW_descr, // 3
INPUT_ONLY_descr, // 4
NO_PWM_descr, // 5
HIGH_BOOT_descr, // 6
INT_ADC_descr, // 7
INT_DAC_descr, // 8
};
/*
* RTCs available
*/
// 0 is unconfigured
const byte RTCs_total = 4;
const byte RTCs_DS1307 = 1;
const byte RTCs_DS3231 = 2;
const byte RTCs_PCF8523 = 3;
const byte RTCs_PCF8563 = 4;
const char RTCs_DS1307_descr[] PROGMEM = {"DS1307"};
const char RTCs_DS3231_descr[] PROGMEM = {"DS3231"};
const char RTCs_PCF8523_descr[] PROGMEM = {"PCF8523"};
const char RTCs_PCF8563_descr[] PROGMEM = {"PCF8563"};
const char * RTCs_descr[] = {
NULL, // unconfigured
RTCs_DS1307_descr,
RTCs_DS3231_descr,
RTCs_PCF8523_descr,
RTCs_PCF8563_descr,
};
/*
* Time scales
*/
// 0 is unconfigured
const byte TIMESCALE_total = 7;
const byte TIMESCALE_SECOND = 0;
const byte TIMESCALE_MINUTE = 1;
const byte TIMESCALE_HOUR = 2;
const byte TIMESCALE_DAY = 3;
const byte TIMESCALE_WEEK = 4;
const byte TIMESCALE_MONTH = 5;
const byte TIMESCALE_YEAR = 6;
const char TIMESCALE_SECOND_descr[] PROGMEM = {"Second"};
const char TIMESCALE_MINUTE_descr[] PROGMEM = {"Minute"};
const char TIMESCALE_HOUR_descr[] PROGMEM = {"Hour"};
const char TIMESCALE_DAY_descr[] PROGMEM = {"Day"};
const char TIMESCALE_WEEK_descr[] PROGMEM = {"Week"};
const char TIMESCALE_MONTH_descr[] PROGMEM = {"Month"};
const char TIMESCALE_YEAR_descr[] PROGMEM = {"Year"};
const char * Timescale_descr[] = {
TIMESCALE_SECOND_descr,
TIMESCALE_MINUTE_descr,
TIMESCALE_HOUR_descr,
TIMESCALE_DAY_descr,
TIMESCALE_WEEK_descr,
TIMESCALE_MONTH_descr,
TIMESCALE_YEAR_descr,
};
/* GPIO Index struct
* filled with CanGrow_ESP8266.h and CanGrow_ESP32.h
*/
struct GPIO_Index {
const byte gpio;
const byte note;
};
/*
*
* Config
*
* Note: when adding/removing/changing a saved Config variable
* you have to touch the config struct, LoadConfig() and SaveConfig() at least too!
*/
/*
* Config WiFi
*/
struct Config_WiFi {
char ssid[32];
char password[64];
bool dhcp;
byte ip[4] = {192,168,4,20};
byte netmask[4] = {255,255,255,0};
byte gateway[4] = {0,0,0,0};
byte dns[4] = {0,0,0,0};
};
/*
* Config System
*/
struct Config_System_Output {
/*
* Config System Output
*
* - type: output type like GPIO, I2C, URL
* 1 - GPIO
* 2 - I2C
* 3 - Web
* - device: what this output is connected to
* 1 - Light
* 2 - Fan
* 3 - Pump
* 4 - Humudifier
* 5 - Dehumidifier
* 6 - Heating
* - name: name of output
* - enabled: enable output
* - gpio: which gpio is used
* - invert: invert output
* - gpio_pwm: enable pwm for output
* - i2c:
* - webcall_host: ip to smart plug (tasmota e.g.)
* - webcall_path_on: GET request path to turn ON
* - webcall_path_off: GET request path to turn OFF
*
*/
byte type[Max_Outputs];
byte device[Max_Outputs];
char name[Max_Outputs][32];
bool enabled[Max_Outputs];
byte gpio[Max_Outputs];
bool gpio_pwm[Max_Outputs];
bool invert[Max_Outputs];
byte i2c_type[Max_Outputs];
byte i2c_addr[Max_Outputs];
byte i2c_port[Max_Outputs];
char webcall_host[Max_Outputs][32];
char webcall_path_on[Max_Outputs][32];
char webcall_path_off[Max_Outputs][32];
char webcall_user[Max_Outputs][32];
char webcall_password[Max_Outputs][32];
};
struct Config_System_Sensor {
/*
* Config System Sensor
* - type: Index ID of SensorIndex, which Sensor to use (ADC, BME280, Chirp, ...)
* - name: nice name
* - gpio[]: gpio to use for RPM reading, builtin ADC, OneWire, TwoWire
*/
byte type[Max_Sensors];
char name[Max_Sensors][32];
byte i2c_addr[Max_Sensors];
byte gpio[Max_Sensors][Max_Sensors_GPIO];
float offset[Max_Sensors][Max_Sensors_Read];
unsigned int low[Max_Sensors][Max_Sensors_Read];
unsigned int high[Max_Sensors][Max_Sensors_Read];
byte rawConvert[Max_Sensors][Max_Sensors_Read];
};
/* main System struct */
struct Config_System {
bool ntp = true;
byte rtc;
bool time2fs;
short ntpOffset;
unsigned short maintenanceDuration;
char esp32cam[16];
char httpUser[32];
char httpPass[32];
bool httpLogSerial;
unsigned short schedulerInterval = 1000;
unsigned short pwmFreq = 13370;
Config_System_Output output;
Config_System_Sensor sensor;
};
/*
* Config Grow
*/
struct Config_Grow_Light {
bool configured[Max_Outputs];
byte output[Max_Outputs];
byte sunriseHourVeg[Max_Outputs];
byte sunriseMinuteVeg[Max_Outputs];
byte sunsetHourVeg[Max_Outputs];
byte sunsetMinuteVeg[Max_Outputs];
byte sunriseHourBloom[Max_Outputs];
byte sunriseMinuteBloom[Max_Outputs];
byte sunsetHourBloom[Max_Outputs];
byte sunsetMinuteBloom[Max_Outputs];
byte power[Max_Outputs];
bool fade[Max_Outputs];
byte fadeDuration[Max_Outputs];
};
struct Config_Grow_Air {
bool configured[Max_Outputs];
byte output[Max_Outputs];
byte power[Max_Sensors];
byte controlSensor[Max_Outputs];
byte controlRead[Max_Outputs];
byte controlMode[Max_Outputs];
float min[Max_Outputs];
float max[Max_Outputs];
};
struct Config_Grow_Water {
bool configured[Max_Outputs];
byte output[Max_Outputs];
byte controlSensor[Max_Outputs];
byte controlRead[Max_Outputs];
byte controlMode[Max_Outputs];
byte onTime[Max_Sensors];
byte min[Max_Sensors];
byte max[Max_Sensors];
byte interval[Max_Sensors];
byte intervalUnit[Max_Sensors];
};
struct Config_Grow_Dashboard {
bool configured[Max_Sensors][Max_Sensors_Read];
byte sensor[Max_Sensors][Max_Sensors_Read];
};
struct Config_Grow {
char name[64] = "CanGrow";
unsigned long start;
byte daysVeg = 42;
byte daysBloom = 69;
Config_Grow_Light light;
Config_Grow_Air air;
Config_Grow_Water water;
Config_Grow_Dashboard dashboard;
//unsigned short dayOfGrow;
//byte daysSeed;
//byte lightHoursVeg;
//byte lightHoursBloom;
//byte sunriseHour;
//byte sunriseMinute;
//bool sunFade;
//byte sunFadeDuration;
};
/*
* main Config struct
*/
struct Config {
char test[16] = "123";
Config_WiFi wifi;
Config_System system;
Config_Grow grow;
};
Config config;
/*
*
*
* Global Runtime variables
*
*
*/
// do we need a restart? (e.g. after wifi settings change)
bool needRestart = false;
// this triggers Restart() from the main loop
bool doRestart = false;
// previous value of millis within the scheduler loop
unsigned long schedulerPrevMillis = 0;
/* in which time status is the system
* 0 - OK
* 1 - RTC fallback is used
* 2 - Time2FS fallback is used
*/
byte timeSrcStatus;
/* rtcError - false no Error, true had error while init */
bool rtcError = false;
// did ntp offset got changed?
bool updateNtpOffset = false;
/* sensorStatus[] to keep track if sensor init succeeded or not, true is OK */
bool sensorStatus[Max_Sensors];
/* outputStatus[] to keep track if output init succeeded or not, true is OK */
bool outputStatus[Max_Outputs];
/* outputState[] gets read by Output_Update() */
byte outputState[Max_Outputs];
/* keep track how often a http call failed */
byte outputWebcallFailed[Max_Outputs];
/* remember timestamp when pump was turned on to turn it off after config.grow.water.onTime */
unsigned long controlWaterLastStarted[Max_Outputs];
/* remember timestamp when last water cycle was done.*/
unsigned long controlWaterLast[Max_Outputs];

View file

@ -1,22 +0,0 @@
/*
*
* include/CanGrow_Core.h - core stuff header file
*
*
*
*/
/* Give free grow.light id */
byte Give_Free_Grow_LightId() {
byte freeId;
for(byte i = 0; i < Max_Outputs; i++) {
if(config.grow.light.configured[i] == true) {
// here i define that 255 stands for "no more free outputs"
freeId = 255;
} else {
freeId = i;
break;
}
}
return freeId;
}

View file

@ -1,407 +0,0 @@
/*
*
* include/CanGrow_Control.h - control stuff for light,air,water header file
*
*
*
*/
/*
*
* Light stuff
*
*/
/* Light fade */
byte Light_Power(byte id, unsigned int sunriseSec, unsigned int sunsetSec, unsigned int nowSec, bool shifted) {
const static char LogLoc[] PROGMEM = "[Control:Light_Power]";
if(config.grow.light.fade[id] == true) {
unsigned int fadeDurationSec = config.grow.light.fadeDuration[id] * 60;
byte power_tmp;
//byte power_tmp; // = (durationSec - ((sunriseSec + durationSec) - nowSec) * config.grow.light.power[id] / durationSec);
/* rising sun */
if(nowSec <= sunriseSec + fadeDurationSec) {
/* calculate fade power value */
//power_tmp = ( ( (nowSec - sunriseSec) / (fadeDurationSec / 255) ) * config.grow.light.power[id] ) / 255;
power_tmp = (fadeDurationSec - ((sunriseSec + fadeDurationSec) - nowSec)) * config.grow.light.power[id] / fadeDurationSec;
/* setting sun */
} else if((nowSec >= sunsetSec - fadeDurationSec) && (nowSec <= sunsetSec)) {
/* calculate fade power value */
//power_tmp = ( ( (sunsetSec - nowSec) / (fadeDurationSec / 255) ) * config.grow.light.power[id] ) / 255;
power_tmp = (sunsetSec - nowSec) * config.grow.light.power[id] / fadeDurationSec;
} else {
/* otherwise just turn the light on with configured value */
power_tmp = config.grow.light.power[id];
}
//if(shifted == false) {
//} else {
//}
#ifdef DEBUG
Log.verbose(F("%s Light %d - power_tmp %d" CR), LogLoc, id, power_tmp);
#endif
return power_tmp;
} else {
return config.grow.light.power[id];
}
//return 0;
}
/* Function to set light based on time */
void Control_Light() {
const static char LogLoc[] PROGMEM = "[Control:Light]";
//Log.verbose(F("%s start %s %s" CR), LogLoc, Str_DateNow(), Str_TimeNow());
/* iterate through all configured lights */
for(byte i = 0; i < Max_Outputs; i++) {
if(config.grow.light.configured[i] == true) {
unsigned int nowSec = (hour() * 60 * 60) + (minute() * 60) + second();
unsigned int sunriseSec;
unsigned int sunsetSec;
/* check if veg or bloom */
if((config.grow.start < 1) || (now() - config.grow.start <= config.grow.daysVeg * 24 * 60 * 60)) {
sunriseSec = (config.grow.light.sunriseHourVeg[i] * 60 * 60) + (config.grow.light.sunriseMinuteVeg[i] * 60);
sunsetSec = (config.grow.light.sunsetHourVeg[i] * 60 * 60) + (config.grow.light.sunsetMinuteVeg[i] * 60);
#ifdef DEBUG
Log.verbose(F("%s Veg" CR), LogLoc);
#endif
/* now > than veg = bloom */
} else if(now() - config.grow.start > config.grow.daysVeg * 24 * 60 * 60) {
sunriseSec = (config.grow.light.sunriseHourBloom[i] * 60 * 60) + (config.grow.light.sunriseMinuteBloom[i] * 60);
sunsetSec = (config.grow.light.sunsetHourBloom[i] * 60 * 60) + (config.grow.light.sunsetMinuteBloom[i] * 60);
#ifdef DEBUG
Log.verbose(F("%s Bloom" CR), LogLoc);
#endif
/* now > than veg+bloom = harvest*/
} //else if(now() - config.grow.start > (config.grow.daysVeg + config.grow.daysBloom) * 24 * 60 * 60)) {
//}
/*
* Sunrise / Day
*/
/* when now is greater than sunrise AND sunsetTime is greater than sunrise */
if((nowSec >= sunriseSec) && (nowSec < sunsetSec) && (sunsetSec > sunriseSec)) {
//outputState[i] = config.grow.light.power[i];
outputState[i] = Light_Power(i, sunriseSec, sunsetSec, nowSec, false);
//Log.verbose(F("%s Light %d - nowSec %d - sunriseSec %d - sunsetSec %d - %s %s Day" CR), LogLoc, i, nowSec, sunriseSec, sunsetSec, Str_DateNow(), Str_TimeNow());
/* when now is greater than sunrise OR */
} else if(((nowSec >= sunriseSec) && (sunsetSec < sunriseSec)) ||
/* when now is smaller than sunset AND sunset is
* smaller than sunrise - this is a shifted daytime */
((nowSec <= sunsetSec) && (sunsetSec < sunriseSec))) {
//outputState[i] = config.grow.light.power[i];
outputState[i] = Light_Power(i, sunriseSec, sunsetSec, nowSec, true);
//Log.verbose(F("%s Light %d - nowSec %d - sunriseSec %d - sunsetSec %d - %s %s Day (shifted)" CR), LogLoc, i, nowSec, sunriseSec, sunsetSec, Str_DateNow(), Str_TimeNow());
} else {
/* otherwise its night, turn off the light */
outputState[i] = 0;
//Log.verbose(F("%s Light %d - nowSec %d - sunriseSec %d - sunsetSec %d - %s %s Night" CR), LogLoc, i, nowSec, sunriseSec, sunsetSec, Str_DateNow(), Str_TimeNow());
}
}
}
}
/*
*
* Air stuff
*
*/
/*
* Output Device
*/
/* Air Mode definitions */
// 0 is unconfigured
const byte CONTROL_AIR_MODE__TOTAL = 3;
const byte CONTROL_AIR_MODE_ONOFF = 1;
const byte CONTROL_AIR_MODE_LINEAR = 2;
const byte CONTROL_AIR_MODE_STEPS = 3;
const char CONTROL_AIR_MODE_ONOFF_descr[] PROGMEM = {"On/Off"};
const char CONTROL_AIR_MODE_LINEAR_descr[] PROGMEM = {"Linear"};
const char CONTROL_AIR_MODE_STEPS_descr[] PROGMEM = {"Steps"};
const char * Control_Air_Mode_descr[] = {
NULL, // 0 - no description because 0 means unconfigured
CONTROL_AIR_MODE_ONOFF_descr,
CONTROL_AIR_MODE_LINEAR_descr,
CONTROL_AIR_MODE_STEPS_descr,
};
/* Air control modes themselfs */
byte Control_Air_Mode_OnOff(byte id) {
/* turns the output on or off, depending if the is within min and max */
/* if only min is set (max = 0), turn on when above it */
if((config.grow.air.min[id] > 0) && (config.grow.air.max[id] == 0)) {
/* check if Sensor reading is above min value, then turn on */
if(Sensor_getCalibratedValue(config.grow.air.controlSensor[id], config.grow.air.controlRead[id]) >= config.grow.air.min[id]) {
return config.grow.air.power[id];
} else {
return 0;
}
/* if only max is set (min = 0), turn off when above */
} else if((config.grow.air.min[id] == 0) && (config.grow.air.max[id] > 0)) {
/* check if Sensor reading is under max value, then turn on */
if(Sensor_getCalibratedValue(config.grow.air.controlSensor[id], config.grow.air.controlRead[id]) <= config.grow.air.max[id]) {
return config.grow.air.power[id];
} else {
return 0;
}
/* when min and max are set (> 0) turn output on when within the given values */
} else if((config.grow.air.min[id] > 0) && (config.grow.air.max[id] > 0)) {
if((Sensor_getCalibratedValue(config.grow.air.controlSensor[id], config.grow.air.controlRead[id]) >= config.grow.air.min[id]) && (Sensor_getCalibratedValue(config.grow.air.controlSensor[id], config.grow.air.controlRead[id]) <= config.grow.air.max[id])) {
return config.grow.air.power[id];
} else {
return 0;
}
}
return 0;
}
byte Control_Air_Mode_Linear(byte id) {
/* if min and max are set */
if((config.grow.air.min[id] > 0) && (config.grow.air.max[id] > 0)) {
/* return power value calculated with map() and contrain()
* multiply by 100 to "convert" the float to int. With constrain we prevent returning negative or out of range values */
return map(constrain(Sensor_getCalibratedValue(config.grow.air.controlSensor[id], config.grow.air.controlRead[id]) * 100, config.grow.air.min[id] * 100, config.grow.air.max[id] * 100),
config.grow.air.min[id] * 100,
config.grow.air.max[id] * 100,
0,
config.grow.air.power[id]);
} else {
return Control_Air_Mode_OnOff(id);
}
return 0;
}
byte Control_Air_Mode_Steps(byte id) {
/* if min and max are set */
if((config.grow.air.min[id] > 0) && (config.grow.air.max[id] > 0)) {
/* return power value calculated with map() and contrain()
* multiply by 100 to "convert" the float to int. With constrain we prevent returning negative or out of range values */
byte power_tmp = map(constrain(Sensor_getCalibratedValue(config.grow.air.controlSensor[id], config.grow.air.controlRead[id]) * 100, config.grow.air.min[id] * 100, config.grow.air.max[id] * 100),
config.grow.air.min[id] * 100,
config.grow.air.max[id] * 100,
0,
config.grow.air.power[id]);
if(power_tmp == 0) {
return 0;
} else if(power_tmp < 64) {
return 64;
} else if(power_tmp < 128) {
return 128;
} else if(power_tmp < 192) {
return 192;
} else if(power_tmp < 255) {
return 192;
} else {
return 255;
}
} else {
return Control_Air_Mode_OnOff(id);
}
return 0;
}
/* Function to set air devices */
void Control_Air() {
const static char LogLoc[] PROGMEM = "[Control:Air]";
/* iterate through all configured air devices */
for(byte i = 0; i < Max_Outputs; i++) {
if(config.grow.air.configured[i] == true) {
/* check if a control Sensor reading is set. As SensorIndex starts by 0, 255 is "unset" */
if((config.grow.air.controlSensor[i] < 255) && (config.grow.air.controlRead[i] < 255)) {
/* switch for control modes */
switch(config.grow.air.controlMode[i]) {
case CONTROL_AIR_MODE_ONOFF:
outputState[i] = Control_Air_Mode_OnOff(i);
break;
case CONTROL_AIR_MODE_LINEAR:
outputState[i] = Control_Air_Mode_Linear(i);
break;
case CONTROL_AIR_MODE_STEPS:
outputState[i] = Control_Air_Mode_Steps(i);
break;
}
/* if there is no control sensor reading selected, just set power */
} else {
outputState[i] = config.grow.air.power[i];
}
}
}
}
/*
*
* Water stuff
*
*/
/*
* Output Device
*/
/* Water Mode definitions */
// 0 is unconfigured
const byte CONTROL_WATER_MODE__TOTAL = 3;
const byte CONTROL_WATER_MODE_TIMEINTERVAL = 1;
const byte CONTROL_WATER_MODE_SENSOR_MIN_THRESHOLD = 2;
const byte CONTROL_WATER_MODE_SENSMIN_TIMEINT_COMBINED = 3;
const char CONTROL_WATER_MODE_TIMEINTERVAL_descr[] PROGMEM = {"Timeinterval"};
const char CONTROL_WATER_MODE_SENSOR_MIN_THRESHOLD_descr[] PROGMEM = {"Sensor min threshold"};
const char CONTROL_WATER_MODE_SENSMIN_TIMEINT_COMBINED_descr[] PROGMEM = {"Sensor min + Timeinterval"};
const char * Control_Water_Mode_descr[] = {
NULL, // 0 - no description because 0 means unconfigured
CONTROL_WATER_MODE_TIMEINTERVAL_descr,
CONTROL_WATER_MODE_SENSOR_MIN_THRESHOLD_descr,
CONTROL_WATER_MODE_SENSMIN_TIMEINT_COMBINED_descr,
};
/* Function to set water devices */
void Control_Water() {
const static char LogLoc[] PROGMEM = "[Control:Water]";
/* iterate through all configured water devices which have a control mode set */
for(byte i = 0; i < Max_Outputs; i++) {
if((config.grow.water.configured[i] == true) && (config.grow.water.controlMode[i] > 0)) {
/* which mode was set in config.grow.water.controlMode */
switch(config.grow.water.controlMode[i]) {
case CONTROL_WATER_MODE_TIMEINTERVAL:
// when diff of time now and time pumpLastOn is greater then water.interval, do some watering (Or manual watering)
if( (now() - controlWaterLast[i]) >= (config.grow.water.interval[i] * Timescale(config.grow.water.intervalUnit[i])) ) {
/* check if output is already on. If not so, we begin a watering cycle and remember the timestamp of it. */
if(outputState[i] == 0) {
controlWaterLastStarted[i] = now();
}
/* when diff of now and controlWaterLastStarted is smaller than onTime, turn output on */
if((now() - controlWaterLastStarted[i]) < config.grow.water.onTime[i]) {
/* at the moment i think PWM is not necessary here, so we set the output to 255 */
outputState[i] = 255;
/* when onTime is exceeded, turn output off */
} else {
outputState[i] = 0;
/* remember when we finished watering */
controlWaterLast[i] = now();
/* Todo, write controlWaterLast to LittleFS. */
}
} else {
/* turn output off when interval is not exceeded */
outputState[i] = 0;
}
break;
case CONTROL_WATER_MODE_SENSOR_MIN_THRESHOLD:
/* when sensor reading config.grow.water.controlSensor is lower then config.grow.water.min , do some watering */
if( (Sensor_getCalibratedValue(config.grow.water.controlSensor[i], config.grow.water.controlRead[i]) < config.grow.water.min[i]) ||
/* or when the sensor value is larger than min but onTime has not exceeded yet */
((Sensor_getCalibratedValue(config.grow.water.controlSensor[i], config.grow.water.controlRead[i]) >= config.grow.water.min[i]) && ( (now() - controlWaterLastStarted[i]) < config.grow.water.onTime[i]))
) {
/* check if output is already on. If not so, we begin a watering cycle and remember the timestamp of it. */
if(outputState[i] == 0) {
controlWaterLastStarted[i] = now();
}
/* when diff of now and controlWaterLastStarted is smaller than onTime, turn output on */
if((now() - controlWaterLastStarted[i]) < config.grow.water.onTime[i]) {
/* at the moment i think PWM is not necessary here, so we set the output to 255 */
outputState[i] = 255;
/* when onTime is exceeded, turn output off */
} else {
outputState[i] = 0;
}
/* turn output off when water conditions are not met */
} else {
outputState[i] = 0;
}
break;
case CONTROL_WATER_MODE_SENSMIN_TIMEINT_COMBINED:
// when diff of time now and time pumpLastOn is greater then water.interval AND sensor read value is below min
if( ( (now() - controlWaterLast[i]) >= (config.grow.water.interval[i] * Timescale(config.grow.water.intervalUnit[i])) ) &&
( (Sensor_getCalibratedValue(config.grow.water.controlSensor[i], config.grow.water.controlRead[i]) < config.grow.water.min[i]) ||
/* or when the sensor value is larger than min but onTime has not exceeded yet */
((Sensor_getCalibratedValue(config.grow.water.controlSensor[i], config.grow.water.controlRead[i]) >= config.grow.water.min[i]) && ( (now() - controlWaterLastStarted[i]) < config.grow.water.onTime[i])) )
) {
/* check if output is already on. If not so, we begin a watering cycle and remember the timestamp of it. */
if(outputState[i] == 0) {
controlWaterLastStarted[i] = now();
}
/* when diff of now and controlWaterLastStarted is smaller than onTime, turn output on */
if((now() - controlWaterLastStarted[i]) < config.grow.water.onTime[i]) {
/* at the moment i think PWM is not necessary here, so we set the output to 255 */
outputState[i] = 255;
/* when onTime is exceeded, turn output off */
} else {
outputState[i] = 0;
/* remember when we finished watering */
controlWaterLast[i] = now();
/* Todo, write controlWaterLast to LittleFS. */
}
} else {
/* turn output off when interval is not exceeded */
outputState[i] = 0;
}
break;
default:
/* when no mode is selected, turn output off */
outputState[i] = 0;
break;
}
}
/* if neither configured or mode set, force output being off */
}
}

View file

@ -1,642 +0,0 @@
/*
*
* include/CanGrow_Core.h - core stuff header file
*
*
*
*/
/*
* NTP Stuff
*/
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP);
/*
* RTC Stuff
*/
/* I would more like not to define four individual globals for each RTC type
* but Adafruit lib seems to work only this way - and i am too lazyscared to use
* some other lib or do it myself - so i hope this will not eat up my ram */
RTC_DS1307 rtc_ds1307;
RTC_DS3231 rtc_ds3231;
RTC_PCF8523 rtc_pcf8523;
RTC_PCF8563 rtc_pcf8563;
/*
* Timer stuff
*/
NusabotSimpleTimer timer;
/*
* Logging stuff
*
* Example Log call
* const static char LogLoc[] PROGMEM= "[Some:Stuff:Happening]"
* Log.notice(F("%s This is %d" CR), LogLoc, i);
*
* LogLoc stands for "LogLocation"
*/
/* Logging prefix */
void LogPrefix(Print* _logOutput, int logLevel) {
//_logOutput->print(":: TEST");
switch (logLevel)
{
default:
// silent
case 0:_logOutput->print("--" ); break;
// fatal
case 1:_logOutput->print("!!!! " ); break;
// error
case 2:_logOutput->print("!! " ); break;
// warning
case 3:_logOutput->print("!: "); break;
// info / notice
case 4:_logOutput->print(":: " ); break;
// trace
case 5:_logOutput->print("T: " ); break;
// verbose / debug
case 6:_logOutput->print("DB "); break;
}
}
/* System core stuff , like restart , give free Id of xy, .. */
void Restart() {
const static char LogLoc[] PROGMEM = "[Core:Restart]";
Log.notice(F("%s got triggered, restarting in 2 seconds" CR), LogLoc);
// blink fast with the built in LED in an infinite loop
byte i = 0;
while(i <= 16) {
if(i % 2) {
digitalWrite(PinWIPE, 1 - PinWIPE_default);
} else {
digitalWrite(PinWIPE, PinWIPE_default);
}
i++;
delay(125);
}
ESP.restart();
}
// IP2Char helper function to convert ip arrarys to char arrays
char* IP2Char(IPAddress ipaddr){
// https://forum.arduino.cc/t/trouble-returning-char-array-string/473246/6
static char buffer[18];
sprintf(buffer, "%d.%d.%d.%d", ipaddr[0], ipaddr[1], ipaddr[2], ipaddr[3] );
return buffer;
}
byte Give_Free_OutputId() {
const static char LogLoc[] PROGMEM = "[Core:Give_Free_OutputId]";
byte outputId_free;
for(byte i=0; i < Max_Outputs; i++) {
if(config.system.output.type[i] > 0) {
// here i define that 255 stands for "no more free outputs"
outputId_free = 255;
} else {
outputId_free = i;
break;
}
}
#ifdef DEBUG
Log.verbose(F("%s next free output id: %d" CR), LogLoc, outputId_free);
#endif
return outputId_free;
}
byte Give_Free_SensorId() {
const static char LogLoc[] PROGMEM = "[Core:Give_Free_SensorId]";
byte sensorId_free;
for(byte i=0; i < Max_Sensors; i++) {
if(config.system.sensor.type[i] > 0) {
// here i define that 255 stands for "no more free outputs"
sensorId_free = 255;
} else {
sensorId_free = i;
break;
}
}
#ifdef DEBUG
Log.verbose(F("%s next free sensor id: %d" CR), LogLoc, sensorId_free);
#endif
return sensorId_free;
}
// checks if GPIO is already in use by output or sensor
bool Check_GPIOindex_Used(byte gpio) {
const static char LogLoc[] PROGMEM = "[Core:Check_GPIOindex_Used]";
bool used;
//Log.verbose(F("%s check GPIO: %d" CR), LogLoc, gpio);
// go through each outputid
for(byte i=0; i < Max_Outputs; i++) {
// check if output type is gpio
if(config.system.output.type[i] == OUTPUT_TYPE_GPIO) {
#ifdef DEBUG
Log.verbose(F("%s OutputId: %d is GPIO (type %d)" CR), LogLoc, i, config.system.output.type[i]);
#endif
// check if gpio id is already in use
if(config.system.output.gpio[i] == gpio) {
#ifdef DEBUG
Log.verbose(F("%s output.gpio[%d](%d) == GPIO %d" CR), LogLoc, i, config.system.output.gpio[i], gpio);
#endif
used = true;
break;
} else {
used = false;
}
}
}
if(used == false) {
for(byte i=0; i < Max_Sensors; i++) {
// check if sensor type uses gpio
if((SensorIndex[config.system.sensor.type[i]].type == SENSOR_TYPE_INTADC) || (SensorIndex[config.system.sensor.type[i]].type == SENSOR_TYPE_ONEWIRE) || (SensorIndex[config.system.sensor.type[i]].type == SENSOR_TYPE_TWOWIRE)) {
#ifdef DEBUG
Log.verbose(F("%s SensorId: %d is using GPIO (type %d)" CR), LogLoc, i, config.system.sensor.type[i]);
#endif
// check if gpio id is already in use
for(byte j = 0; j < Max_Sensors_GPIO; j++) {
if(config.system.sensor.gpio[i][j] == gpio) {
#ifdef DEBUG
Log.verbose(F("%s sensor.gpio[%d][%d](%d) == GPIO %d" CR), LogLoc, i, j, config.system.sensor.gpio[i][j], gpio);
#endif
used = true;
break;
} else {
used = false;
}
}
}
}
}
#ifdef DEBUG
Log.verbose(F("%s GPIO: %d, used: %d" CR), LogLoc, gpio, used);
#endif
return used;
}
/*
*
* Time related stuff
*
*/
/*
* NTP stuff
*/
void NTP_OffsetUpdate() {
const static char LogLoc[] PROGMEM = "[Core:NTP_OffsetUpdate]";
#ifdef DEBUG
Log.verbose(F("%s updating time with offset %dh" CR), LogLoc, config.system.ntpOffset);
#endif
timeClient.setTimeOffset(config.system.ntpOffset * 60 * 60);
if( (config.system.ntp == true) && (timeSrcStatus < 1) ) {
timeClient.update();
setTime(timeClient.getEpochTime());
}
#ifdef DEBUG
else {
Log.verbose(F("%s update requirements not met, timeSrcStatus %d > 0" CR), LogLoc, timeSrcStatus);
}
#endif
}
bool NTP_Init() {
const static char LogLoc[] PROGMEM = "[Core:NTP_Init]";
bool result;
timeClient.begin();
NTP_OffsetUpdate();
// when NTP update failes (e.g. no connection to internet)
Log.notice(F("%s updating " ), LogLoc);
byte i = 0;
while( (! timeClient.isTimeSet()) && ( i < 5 )) {
timeClient.update();
delay(100);
Serial.print(".");
i++;
}
Serial.println();
if( ! timeClient.isTimeSet()) {
Log.error(F("%s FAILED" CR), LogLoc);
//Serial.println("!! [Core:NTP_Init] update failed");
result = false;
} else {
Log.notice(F("%s Success! Time: %s (%u), Offset: %d h" CR), LogLoc, timeClient.getFormattedTime(), timeClient.getEpochTime(), config.system.ntpOffset);
result = true;
}
return result;
}
time_t NTP_getEpochTime() {
/* convert epoch from ntp (UL) to time_t */
const static char LogLoc[] PROGMEM = "[Core:NTP_getEpochTime]";
unsigned long epochTime = timeClient.getEpochTime();
Log.verbose(F("%s epochTime: %u" CR), LogLoc, epochTime);
return epochTime;
}
/*
* RTC stuff
*/
void RTC_Init() {
const static char LogLoc[] PROGMEM = "[Core:RTC_Init]";
switch(config.system.rtc) {
case RTCs_DS1307:
if (! rtc_ds1307.begin()) {
Log.warning(F("%s Couldn't find RTC DS1307" CR), LogLoc);
rtcError = true;
} else {
Log.notice(F("%s RTC DS1307 found" CR), LogLoc);
if (rtc_ds1307.isrunning()) {
Log.warning(F("%s RTC DS1307 is not running, let's set the time!" CR), LogLoc);
rtcError = true;
}
}
break;
case RTCs_DS3231:
if (! rtc_ds3231.begin()) {
Log.warning(F("%s Couldn't find RTC DS3231" CR), LogLoc);
rtcError = true;
} else {
Log.notice(F("%s RTC DS3231 found" CR), LogLoc);
if (rtc_ds3231.lostPower()) {
Log.warning(F("%s RTC DS3231 lost power, let's set the time!" CR), LogLoc);
rtcError = true;
}
}
break;
case RTCs_PCF8563:
if (! rtc_pcf8563.begin()) {
Log.warning(F("%s Couldn't find RTC PCF8563" CR), LogLoc);
rtcError = true;
} else {
Log.notice(F("%s RTC PCF8563 found" CR), LogLoc);
if (rtc_pcf8563.lostPower()) {
Log.warning(F("%s RTC PCF8563 lost power, let's set the time!" CR), LogLoc);
rtcError = true;
}
}
rtc_pcf8563.start();
break;
case RTCs_PCF8523:
if (! rtc_pcf8523.begin()) {
Log.warning(F("%s Couldn't find RTC PCF8523" CR), LogLoc);
rtcError = true;
} else {
Log.notice(F("%s RTC PCF8523 found" CR), LogLoc);
if ( ! rtc_pcf8523.initialized() || rtc_pcf8523.lostPower()) {
Log.warning(F("%s RTC PCF8523 lost power, let's set the time!" CR), LogLoc);
rtcError = true;
}
}
rtc_pcf8523.start();
break;
default:
break;
}
}
time_t RTC_getEpochTime() {
/* convert epoch from RTC (UL) to time_t */
const static char LogLoc[] PROGMEM = "[Core:RTC_getEpochTime]";
unsigned long epochTime; // = timeClient.getEpochTime();
DateTime TimeNow;
switch(config.system.rtc) {
case RTCs_DS1307:
TimeNow = rtc_ds1307.now();
break;
case RTCs_DS3231:
TimeNow = rtc_ds3231.now();
break;
case RTCs_PCF8523:
TimeNow = rtc_pcf8523.now();
break;
case RTCs_PCF8563:
TimeNow = rtc_pcf8563.now();
break;
default:
break;
}
epochTime = TimeNow.unixtime();
Log.verbose(F("%s epochTime: %u" CR), LogLoc, epochTime);
return epochTime;
}
void RTC_SaveTime() {
const static char LogLoc[] PROGMEM = "[Core:RTC_SaveTime]";
unsigned int TimeNow = now();
bool saved = true;
switch(config.system.rtc) {
case RTCs_DS1307:
rtc_ds1307.adjust(DateTime(TimeNow));
break;
case RTCs_DS3231:
rtc_ds3231.adjust(DateTime(TimeNow));
break;
case RTCs_PCF8523:
rtc_pcf8523.adjust(DateTime(TimeNow));
break;
case RTCs_PCF8563:
rtc_pcf8563.adjust(DateTime(TimeNow));
break;
default:
/* only when not in case, we consider not saved */
saved = false;
break;
}
#ifdef DEBUG
if(saved == true)
Log.verbose(F("%s Time (%u) saved to %S" CR), LogLoc, TimeNow, RTCs_descr[config.system.rtc]);
#endif
}
/*
* Main Time stuff
*
*/
String Str_TimeNow() {
/* simple helper function to return a String with HH:MM:SS */
String str_time;
if(hour() < 10)
str_time += F("0");
str_time += hour();
str_time += F(":");
if(minute() < 10)
str_time += F("0");
str_time += minute();
str_time += F(":");
if(second() < 10)
str_time += F("0");
str_time += second();
return str_time;
}
String Str_DateNow() {
/* simple helper function to return a String with HH:MM:SS */
String str_date;
if(day() < 10)
str_date += F("0");
str_date += day();
str_date += F(".");
if(month() < 10)
str_date += F("0");
str_date += month();
str_date += F(".");
str_date += year();
return str_date;
}
String Str_Epoch2Date(unsigned long epochTime) {
String dateStr;
byte Day = day(epochTime);
byte Month = month(epochTime);
unsigned int Year = year(epochTime);
dateStr = Year;
dateStr += "-";
if(Month < 10) {
dateStr += "0";
dateStr += Month;
} else {
dateStr += Month;
}
dateStr += "-";
if(Day < 10) {
dateStr += "0";
dateStr += Day;
} else {
dateStr += Day;
}
return dateStr;
}
/* Those two functions should be in LittleFS file, but because dependency and lazyness */
void Time2FS_Save() {
const static char LogLoc[] PROGMEM = "[Core:Time2FS_Save]";
unsigned long TimeNow;
#ifdef ESP8266
File file = LittleFS.open(TIME2FS, "w");
#endif
#ifdef ESP32
fs::FS &fs = LittleFS;
File file = fs.open(TIME2FS, FILE_WRITE);
#endif
if (!file) {
Log.error(F("%s FAILED to open file for writing: %s" CR), LogLoc, TIME2FS);
return;
}
TimeNow = now();
if (!file.print(TimeNow)) {
Log.error(F("%s writing time FAILED" CR), LogLoc);
}
#ifdef DEBUG
else {
Log.verbose(F("%s time (%u) written: %s" CR), LogLoc, TimeNow, TIME2FS);
}
#endif
//delay(2000); // Make sure the CREATE and LASTWRITE times are different
file.close();
}
void Time2FS_Read() {
const static char LogLoc[] PROGMEM = "[Core:Time2FS_Read]";
String TimeRead;
#ifdef ESP8266
File file = LittleFS.open(TIME2FS, "r");
#endif
#ifdef ESP32
fs::FS &fs = LittleFS;
File file = fs.open(TIME2FS);
#endif
if (!file) {
Log.error(F("%s FAILED to open time file: %s" CR), LogLoc, TIME2FS);
return;
}
//Log.notice(F("%s file content: %s" CR), LogLoc, TIME2FS);
//Log.notice(F("%s ----------" CR), LogLoc);
//while (file.available()) { Serial.write(file.read()); }
//Log.notice(F("%s ----------" CR), LogLoc);
while (file.available()) { TimeRead = file.readString(); }
#ifdef DEBUG
Log.verbose(F("%s applying time (%u) to system" CR), LogLoc, TimeRead.toInt());
#endif
setTime(TimeRead.toInt());
file.close();
}
/* Time_Init - Main function for time initialization */
void Time_Init() {
const static char LogLoc[] PROGMEM = "[Core:Time_Init]";
/* first check if RTC is configured and init if */
if(config.system.rtc > 0)
RTC_Init();
/* check if ntp is enabled */
if(config.system.ntp == true) {
Log.notice(F("%s Using NTP" CR), LogLoc);
/* initialize NTP and check */
if(NTP_Init()) {
#ifdef DEBUG
Log.verbose(F("%s set NTP as TimeLib SyncProvider" CR), LogLoc);
#endif
setSyncProvider(NTP_getEpochTime);
/* when having a RTC, update it now with new not time */
if(config.system.rtc > 0) {
RTC_SaveTime();
}
if(config.system.time2fs == true) {
true;
//writeFile(TIME2FS, now());
Time2FS_Save();
}
} else if((config.system.rtc > 0) && (rtcError == false)) {
#ifdef DEBUG
Log.verbose(F("%s set RTC as TimeLib SyncProvider" CR), LogLoc);
#endif
setSyncProvider(RTC_getEpochTime);
//setTime(RTC_getEpochTime());
timeSrcStatus = 1;
} else {
Log.warning(F("%s no TimeLib SyncProvider available. Reading last Timestamp from flash memory" CR), LogLoc);
Time2FS_Read();
timeSrcStatus = 2;
}
}
/* how often TimeLib should sync with source
* 10 minutes is ok i guess
*/
setSyncInterval(600);
Log.notice(F("%s Time initialization done. Fallback status %d, %s %s (%u)" CR), LogLoc, timeSrcStatus, Str_DateNow(), Str_TimeNow(), now());
}
/*
* semi random string generator
* https://arduino.stackexchange.com/a/86659
*/
const byte RANDOMSTRING_MAX = 16;
const char * RandomString(){
/* Change to allowable characters */
const char possible[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!?_-=()%&.,;:";
static char str[RANDOMSTRING_MAX + 1];
for(byte p = 0, i = 0; i < RANDOMSTRING_MAX; i++){
byte r = random(0, strlen(possible));
str[p++] = possible[r];
}
str[RANDOMSTRING_MAX] = '\0';
return str;
}
/*
* Timescale()
* returns timescale unit (seconds, minutes, hours,...) in seconds
*/
unsigned long Timescale(byte unit) {
switch(unit) {
case TIMESCALE_SECOND:
return 1;
break;
case TIMESCALE_MINUTE:
return 60;
break;
case TIMESCALE_HOUR:
//return 60*60;
return 3600;
break;
case TIMESCALE_DAY:
//return 60*60*24;
return 86400;
break;
case TIMESCALE_WEEK:
//return 60*60*24*7;
return 604800;
break;
case TIMESCALE_MONTH:
//return 60*60*24*7*4;
return 2419200;
break;
case TIMESCALE_YEAR:
//return 60*60*24*7*4*52;
return 125798400;
break;
default:
return 0;
break;
}
}

View file

@ -1,855 +0,0 @@
/*
*
* include/CanGrow_LittleFS.h - LittleFS handling header file
*
*
*
*/
// LittleFS auto format
#define FORMAT_LITTLEFS_IF_FAILED true
void LFS_Init() {
const static char LogLoc[] PROGMEM = "[LittleFS:Init]";
Log.notice(F("%s" CR), LogLoc);
// ESP8266 crashes with first argument set
#ifdef ESP8266
if(!LittleFS.begin()) {
#endif
// ESP32 works, do autoformat if mount fails
#ifdef ESP32
if(!LittleFS.begin(FORMAT_LITTLEFS_IF_FAILED)) {
#endif
Log.notice(F("%s FAILED initializing. You have to format LittleFS manually. Will now restart." CR), LogLoc);
Restart();
}
}
void LFS_Format() {
const static char LogLoc[] PROGMEM = "[LittleFS:Format]";
Log.notice(F("%s formatting ..." CR), LogLoc);
// ESP32 LittleFS needs begin() first, otherwise it would crash
// ESP8266 does not need it, so we leave it
#ifdef ESP32
LittleFS.begin();
#endif
if(LittleFS.format()) {
Log.notice(F("%s done!" CR), LogLoc);
} else {
Log.error(F("%s FAILED" CR), LogLoc);
}
}
bool existFile(const char *path) {
const static char LogLoc[] PROGMEM = "[LittleFS]";
#ifdef ESP8266
File file = LittleFS.open(path, "r");
#endif
#ifdef ESP32
fs::FS &fs = LittleFS;
File file = fs.open(path);
#endif
if (!file) {
Log.notice(F("%s file exists: %s" CR), LogLoc, path);
file.close();
return false;
} else {
Log.warning(F("%s file does not exist: %s" CR), LogLoc, path);
file.close();
return true;
}
}
String readFile(const char *path) {
const static char LogLoc[] PROGMEM = "[LittleFS]";
String fileContent;
#ifdef ESP8266
File file = LittleFS.open(path, "r");
#endif
#ifdef ESP32
fs::FS &fs = LittleFS;
File file = fs.open(path);
#endif
if (!file) {
Log.error(F("%s FAILED to open file for reading: %s" CR), LogLoc, path);
return String(F("ERROR CANNOT OPEN"));
}
Log.notice(F("%s file content: %s" CR), LogLoc, path);
Log.notice(F("%s ----------" CR), LogLoc);
while (file.available()) { Serial.write(file.read()); }
Log.notice(F("%s ----------" CR), LogLoc);
fileContent = file.readString();
file.close();
return fileContent;
}
void writeFile(const char *path, const char *message) {
const static char LogLoc[] PROGMEM = "[LittleFS]";
#ifdef ESP8266
File file = LittleFS.open(path, "w");
#endif
#ifdef ESP32
fs::FS &fs = LittleFS;
File file = fs.open(path, FILE_WRITE);
#endif
if (!file) {
Log.error(F("%s FAILED to open file for reading: %s" CR), LogLoc, path);
return;
}
if (file.print(message)) {
Log.notice(F("%s file written: %s" CR), LogLoc, path);
} else {
Log.error(F("%s writing file FAILED: %s" CR), LogLoc, path);
}
//delay(2000); // Make sure the CREATE and LASTWRITE times are different
file.close();
}
void deleteFile(const char *path) {
const static char LogLoc[] PROGMEM = "[LittleFS]";
#ifdef ESP32
fs::FS &fs = LittleFS;
File file = fs.open(path, FILE_WRITE);
#endif
Log.notice(F("%s deleting file: %s" CR), LogLoc, path);
#ifdef ESP8266
if (LittleFS.remove(path)) {
#endif
#ifdef ESP32
if (fs.remove(path)) {
#endif
Log.notice(F("%s deleted file: %s" CR), LogLoc, path);
} else {
Log.error(F("%s deleting file FAILED: %s" CR), LogLoc, path);
}
}
// https://arduinojson.org/v7/example/config/
// https://arduinojson.org/v7/assistant/
bool LoadConfig() {
const static char LogLoc[] PROGMEM = "[LittleFS:LoadConfig]";
#ifdef ESP8266
File file = LittleFS.open(CANGROW_CFG, "r");
#endif
#ifdef ESP32
fs::FS &fs = LittleFS;
File file = fs.open(CANGROW_CFG);
#endif
Log.notice(F("%s loading config from: %s" CR), LogLoc, CANGROW_CFG);
JsonDocument doc;
// Deserialize the JSON document
DeserializationError error = deserializeJson(doc, file);
if(error) {
Log.error(F("%s FAILED to load config: %s" CR), LogLoc, CANGROW_CFG);
if (existFile(CANGROW_CFG)) {
readFile(CANGROW_CFG);
}
return false;
}
/*
* put json values into config structs
*/
// Copy strings from the JsonDocument to the Config struct as char
strlcpy(config.test, doc["test"], sizeof(config.test));
/* WiFi */
JsonObject objWifi = doc["wifi"][0];
if(objWifi.containsKey("ssid"))
strlcpy(config.wifi.ssid, objWifi["ssid"], sizeof(config.wifi.ssid));
if(objWifi.containsKey("password"))
strlcpy(config.wifi.password, objWifi["password"], sizeof(config.wifi.password));
// Copy bool / int directly into struct
if(objWifi.containsKey("dhcp"))
config.wifi.dhcp = objWifi["dhcp"];
// load the ip addresses as array
if(objWifi.containsKey("ip")) {
for(byte i=0; i < 4 ; i++) {
config.wifi.ip[i] = objWifi["ip"][i];
config.wifi.netmask[i] = objWifi["netmask"][i];
config.wifi.gateway[i] = objWifi["gateway"][i];
config.wifi.dns[i] = objWifi["dns"][i];
}
}
/* System */
JsonObject objSystem = doc["system"][0];
if(objSystem.containsKey("ntpOffset"))
config.system.ntpOffset = objSystem["ntpOffset"];
if(objSystem.containsKey("maintenanceDuration"))
config.system.maintenanceDuration = objSystem["maintenanceDuration"];
if(objSystem.containsKey("esp32cam"))
strlcpy(config.system.esp32cam, objSystem["esp32cam"], sizeof(config.system.esp32cam));
if(objSystem.containsKey("httpUser"))
strlcpy(config.system.httpUser, objSystem["httpUser"], sizeof(config.system.httpUser));
if(objSystem.containsKey("httpPass"))
strlcpy(config.system.httpPass, objSystem["httpPass"], sizeof(config.system.httpPass));
if(objSystem.containsKey("httpLogSerial"))
config.system.httpLogSerial = objSystem["httpLogSerial"];
if(objSystem.containsKey("schedulerInterval"))
config.system.schedulerInterval = objSystem["schedulerInterval"];
if(objSystem.containsKey("ntp"))
config.system.ntp = objSystem["ntp"];
if(objSystem.containsKey("rtc"))
config.system.rtc = objSystem["rtc"];
if(objSystem.containsKey("time2fs"))
config.system.time2fs = objSystem["time2fs"];
if(objSystem.containsKey("pwmFreq"))
config.system.pwmFreq = objSystem["pwmFreq"];
/* System Outputs */
JsonObject objSystemOutput = objSystem["output"][0];
for(byte i=0; i < Max_Outputs; i++) {
if(objSystemOutput["type"][i] > 0) {
if(objSystemOutput.containsKey("type"))
config.system.output.type[i] = objSystemOutput["type"][i];
if(objSystemOutput.containsKey("device"))
config.system.output.device[i] = objSystemOutput["device"][i];
if(objSystemOutput.containsKey("name"))
strlcpy(config.system.output.name[i], objSystemOutput["name"][i], sizeof(config.system.output.name[i]));
if(objSystemOutput.containsKey("enabled"))
config.system.output.enabled[i] = objSystemOutput["enabled"][i];
// gpio
if(objSystemOutput.containsKey("gpio"))
config.system.output.gpio[i] = objSystemOutput["gpio"][i];
if(objSystemOutput.containsKey("invert"))
config.system.output.invert[i] = objSystemOutput["invert"][i];
if(objSystemOutput.containsKey("gpio_pwm"))
config.system.output.gpio_pwm[i] = objSystemOutput["gpio_pwm"][i];
// i2c type
if(objSystemOutput.containsKey("i2c_type"))
config.system.output.i2c_type[i] = objSystemOutput["i2c_type"][i];
// i2c addr
if(objSystemOutput.containsKey("i2c_addr"))
config.system.output.i2c_addr[i] = objSystemOutput["i2c_addr"][i];
// i2c port
if(objSystemOutput.containsKey("i2c_port"))
config.system.output.i2c_port[i] = objSystemOutput["i2c_port"][i];
// web
if(objSystemOutput.containsKey("webcall_host"))
strlcpy(config.system.output.webcall_host[i], objSystemOutput["webcall_host"][i], sizeof(config.system.output.webcall_host[i]));
if(objSystemOutput.containsKey("webcall_path_on"))
strlcpy(config.system.output.webcall_path_on[i], objSystemOutput["webcall_path_on"][i], sizeof(config.system.output.webcall_path_on[i]));
if(objSystemOutput.containsKey("webcall_path_off"))
strlcpy(config.system.output.webcall_path_off[i], objSystemOutput["webcall_path_off"][i], sizeof(config.system.output.webcall_path_off[i]));
}
}
/* System Sensors */
JsonObject objSystemSensor = objSystem["sensor"][0];
for(byte i=0; i < Max_Sensors; i++) {
if(objSystemSensor["type"][i] > 0) {
if(objSystemSensor.containsKey("type"))
config.system.sensor.type[i] = objSystemSensor["type"][i];
if(objSystemSensor.containsKey("name"))
strlcpy(config.system.sensor.name[i], objSystemSensor["name"][i], sizeof(config.system.sensor.name[i]));
if(objSystemSensor.containsKey("i2c_addr"))
//strlcpy(config.system.sensor.i2c_addr[i], objSystemSensor["i2c_addr"][i], sizeof(config.system.sensor.i2c_addr[i]));
config.system.sensor.i2c_addr[i] = objSystemSensor["i2c_addr"][i];
// gpio
if(objSystemSensor.containsKey("gpio")) {
for(byte j = 0; j < Max_Sensors_GPIO; j++) {
config.system.sensor.gpio[i][j] = objSystemSensor["gpio"][i][j];
}
}
// offset
if(objSystemSensor.containsKey("offset")) {
for(byte j = 0; j < Max_Sensors_Read; j++) {
config.system.sensor.offset[i][j] = objSystemSensor["offset"][i][j];
}
}
// low
if(objSystemSensor.containsKey("low")) {
for(byte j = 0; j < Max_Sensors_Read; j++) {
config.system.sensor.low[i][j] = objSystemSensor["low"][i][j];
}
}
// high
if(objSystemSensor.containsKey("high")) {
for(byte j = 0; j < Max_Sensors_Read; j++) {
config.system.sensor.high[i][j] = objSystemSensor["high"][i][j];
}
}
// rawConvert
if(objSystemSensor.containsKey("rawConvert")) {
for(byte j = 0; j < Max_Sensors_Read; j++) {
config.system.sensor.rawConvert[i][j] = objSystemSensor["rawConvert"][i][j];
}
}
}
}
/* Grow */
JsonObject objGrow = doc["grow"][0];
if(objGrow.containsKey("name"))
strlcpy(config.grow.name, objGrow["name"], sizeof(config.grow.name));
if(objGrow.containsKey("start"))
config.grow.start = objGrow["start"];
if(objGrow.containsKey("daysVeg"))
config.grow.daysVeg = objGrow["daysVeg"];
if(objGrow.containsKey("daysBloom"))
config.grow.daysBloom = objGrow["daysBloom"];
/* Grow Light */
JsonObject objLight = objGrow["light"][0];
for(byte i = 0; i < Max_Outputs; i++) {
/* get light.configured */
if(objLight.containsKey("configured"))
config.grow.light.configured[i] = objLight["configured"][i];
/* check if light is configured */
if(config.grow.light.configured[i] == true) {
/* get the rest of the config */
if(objLight.containsKey("output"))
config.grow.light.output[i] = objLight["output"][i];
if(objLight.containsKey("sunriseHourVeg"))
config.grow.light.sunriseHourVeg[i] = objLight["sunriseHourVeg"][i];
if(objLight.containsKey("sunriseMinuteVeg"))
config.grow.light.sunriseMinuteVeg[i] = objLight["sunriseMinuteVeg"][i];
if(objLight.containsKey("sunsetHourVeg"))
config.grow.light.sunsetHourVeg[i] = objLight["sunsetHourVeg"][i];
if(objLight.containsKey("sunsetMinuteVeg"))
config.grow.light.sunsetMinuteVeg[i] = objLight["sunsetMinuteVeg"][i];
if(objLight.containsKey("sunriseHourBloom"))
config.grow.light.sunriseHourBloom[i] = objLight["sunriseHourBloom"][i];
if(objLight.containsKey("sunriseMinuteBloom"))
config.grow.light.sunriseMinuteBloom[i] = objLight["sunriseMinuteBloom"][i];
if(objLight.containsKey("sunsetHourBloom"))
config.grow.light.sunsetHourBloom[i] = objLight["sunsetHourBloom"][i];
if(objLight.containsKey("sunsetMinuteBloom"))
config.grow.light.sunsetMinuteBloom[i] = objLight["sunsetMinuteBloom"][i];
if(objLight.containsKey("power"))
config.grow.light.power[i] = objLight["power"][i];
if(objLight.containsKey("fade"))
config.grow.light.fade[i] = objLight["fade"][i];
if(objLight.containsKey("fadeDuration"))
config.grow.light.fadeDuration[i] = objLight["fadeDuration"][i];
}
}
/* Grow Air */
JsonObject objAir = objGrow["air"][0];
for(byte i = 0; i < Max_Outputs; i++) {
/* get air.configured */
if(objAir.containsKey("configured"))
config.grow.air.configured[i] = objAir["configured"][i];
/* check if air is configured */
if(config.grow.air.configured[i] == true) {
/* get the rest of the config */
if(objAir.containsKey("output"))
config.grow.air.output[i] = objAir["output"][i];
if(objAir.containsKey("power"))
config.grow.air.power[i] = objAir["power"][i];
if(objAir.containsKey("controlSensor"))
config.grow.air.controlSensor[i] = objAir["controlSensor"][i];
if(objAir.containsKey("controlRead"))
config.grow.air.controlRead[i] = objAir["controlRead"][i];
if(objAir.containsKey("controlMode"))
config.grow.air.controlMode[i] = objAir["controlMode"][i];
if(objAir.containsKey("min"))
config.grow.air.min[i] = objAir["min"][i];
if(objAir.containsKey("max"))
config.grow.air.max[i] = objAir["max"][i];
}
}
/* Grow Water */
JsonObject objWater = objGrow["water"][0];
for(byte i = 0; i < Max_Outputs; i++) {
/* get air.configured */
if(objWater.containsKey("configured"))
config.grow.water.configured[i] = objWater["configured"][i];
/* check if air is configured */
if(config.grow.water.configured[i] == true) {
/* get the rest of the config */
if(objWater.containsKey("output"))
config.grow.water.output[i] = objWater["output"][i];
if(objWater.containsKey("onTime"))
config.grow.water.onTime[i] = objWater["onTime"][i];
if(objWater.containsKey("controlSensor"))
config.grow.water.controlSensor[i] = objWater["controlSensor"][i];
if(objWater.containsKey("controlRead"))
config.grow.water.controlRead[i] = objWater["controlRead"][i];
if(objWater.containsKey("controlMode"))
config.grow.water.controlMode[i] = objWater["controlMode"][i];
if(objWater.containsKey("min"))
config.grow.water.min[i] = objWater["min"][i];
if(objWater.containsKey("max"))
config.grow.water.max[i] = objWater["max"][i];
if(objWater.containsKey("interval"))
config.grow.water.interval[i] = objWater["interval"][i];
if(objWater.containsKey("intervalUnit"))
config.grow.water.intervalUnit[i] = objWater["intervalUnit"][i];
}
}
// Close the file (Curiously, File's destructor doesn't close the file)
file.close();
Log.notice(F("%s config successfully loaded" CR), LogLoc);
#ifdef DEBUG
Log.verbose(F("%s --- runtime config ---" CR), LogLoc);
serializeJsonPretty(doc, Serial);
// Json output does not end with NewLine
Serial.println("");
Log.verbose(F("%s ----------------------" CR), LogLoc);
#endif
return true;
}
bool SaveConfig(bool writeToSerial = false) {
const static char LogLoc[] PROGMEM = "[LittleFS:SaveConfig]";
/*
* Building config.json here
*/
JsonDocument doc;
/* Root */
doc["test"] = config.test;
/* WiFi */
JsonObject objWifi = doc["wifi"].add<JsonObject>();
objWifi["ssid"] = config.wifi.ssid;
objWifi["password"] = config.wifi.password;
// save the ip addressess as array
int i;
for(i=0; i <4 ; i++) {
objWifi["ip"][i] = config.wifi.ip[i];
objWifi["netmask"][i] = config.wifi.netmask[i];
objWifi["gateway"][i] = config.wifi.gateway[i];
objWifi["dns"][i] = config.wifi.dns[i];
}
objWifi["dhcp"] = config.wifi.dhcp;
/* System */
JsonObject objSystem = doc["system"].add<JsonObject>();
objSystem["ntpOffset"] = config.system.ntpOffset;
objSystem["maintenanceDuration"] = config.system.maintenanceDuration;
objSystem["esp32cam"] = config.system.esp32cam;
objSystem["httpUser"] = config.system.httpUser;
objSystem["httpPass"] = config.system.httpPass;
objSystem["httpLogSerial"] = config.system.httpLogSerial;
objSystem["schedulerInterval"] = config.system.schedulerInterval;
objSystem["ntp"] = config.system.ntp;
objSystem["rtc"] = config.system.rtc;
objSystem["time2fs"] = config.system.time2fs;
objSystem["pwmFreq"] = config.system.pwmFreq;
/* System Outputs */
JsonObject objSystemOutput = objSystem["output"].add<JsonObject>();
for(byte i=0; i < Max_Outputs; i++) {
if(config.system.output.type[i] > 0) {
objSystemOutput["type"][i] = config.system.output.type[i];
objSystemOutput["device"][i] = config.system.output.device[i];
objSystemOutput["name"][i] = config.system.output.name[i];
objSystemOutput["enabled"][i] = config.system.output.enabled[i];
// gpio
objSystemOutput["gpio"][i] = config.system.output.gpio[i];
objSystemOutput["invert"][i] = config.system.output.invert[i];
objSystemOutput["gpio_pwm"][i] = config.system.output.gpio_pwm[i];
// i2c type
objSystemOutput["i2c_type"][i] = config.system.output.i2c_type[i];
objSystemOutput["i2c_addr"][i] = config.system.output.i2c_addr[i];
objSystemOutput["i2c_port"][i] = config.system.output.i2c_port[i];
// web
objSystemOutput["webcall_host"][i] = config.system.output.webcall_host[i];
objSystemOutput["webcall_path_on"][i] = config.system.output.webcall_path_on[i];
objSystemOutput["webcall_path_off"][i] = config.system.output.webcall_path_off[i];
}
}
/* System Sensors */
JsonObject objSystemSensor = objSystem["sensor"].add<JsonObject>();
for(byte i=0; i < Max_Sensors; i++) {
if(config.system.sensor.type[i] > 0) {
objSystemSensor["type"][i] = config.system.sensor.type[i];
objSystemSensor["name"][i] = config.system.sensor.name[i];
objSystemSensor["i2c_addr"][i] = config.system.sensor.i2c_addr[i];
for(byte j = 0; j < Max_Sensors_GPIO; j++) {
objSystemSensor["gpio"][i][j] = config.system.sensor.gpio[i][j];
}
/* offset reading */
for(byte j = 0; j < Max_Sensors_Read; j++) {
objSystemSensor["offset"][i][j] = config.system.sensor.offset[i][j];
}
/* low reading */
for(byte j = 0; j < Max_Sensors_Read; j++) {
objSystemSensor["low"][i][j] = config.system.sensor.low[i][j];
}
/* high reading */
for(byte j = 0; j < Max_Sensors_Read; j++) {
objSystemSensor["high"][i][j] = config.system.sensor.high[i][j];
}
/* rawConvert reading */
for(byte j = 0; j < Max_Sensors_Read; j++) {
objSystemSensor["rawConvert"][i][j] = config.system.sensor.rawConvert[i][j];
}
}
}
/* Grow */
JsonObject objGrow = doc["grow"].add<JsonObject>();
objGrow["name"] = config.grow.name;
objGrow["start"] = config.grow.start;
objGrow["daysVeg"] = config.grow.daysVeg;
objGrow["daysBloom"] = config.grow.daysBloom;
/* Grow Light */
JsonObject objLight = objGrow["light"].add<JsonObject>();
for(byte i = 0; i < Max_Outputs; i++) {
#ifdef DEBUG
Log.verbose(F("%s LightId %d, Max_Outputs %d, light.configured %T" CR), LogLoc, i, Max_Outputs, config.grow.light.configured[i]);
#endif
if(config.grow.light.configured[i] == true) {
objLight["configured"][i] = config.grow.light.configured[i];
objLight["output"][i] = config.grow.light.output[i];
objLight["sunriseHourVeg"][i] = config.grow.light.sunriseHourVeg[i];
objLight["sunriseMinuteVeg"][i] = config.grow.light.sunriseMinuteVeg[i];
objLight["sunsetHourVeg"][i] = config.grow.light.sunsetHourVeg[i];
objLight["sunsetMinuteVeg"][i] = config.grow.light.sunsetMinuteVeg[i];
objLight["sunriseHourBloom"][i] = config.grow.light.sunriseHourBloom[i];
objLight["sunriseMinuteBloom"][i] = config.grow.light.sunriseMinuteBloom[i];
objLight["sunsetHourBloom"][i] = config.grow.light.sunsetHourBloom[i];
objLight["sunsetMinuteBloom"][i] = config.grow.light.sunsetMinuteBloom[i];
objLight["power"][i] = config.grow.light.power[i];
objLight["fade"][i] = config.grow.light.fade[i];
objLight["fadeDuration"][i] = config.grow.light.fadeDuration[i];
}
}
/* Grow Air */
JsonObject objAir = objGrow["air"].add<JsonObject>();
for(byte i = 0; i < Max_Outputs; i++) {
//Log.verbose(F("%s LightId %d, Max_Outputs %d, light.configured %T" CR), LogLoc, i, Max_Outputs, config.grow.light.configured[i]);
if(config.grow.air.configured[i] == true) {
objAir["configured"][i] = config.grow.air.configured[i];
objAir["output"][i] = config.grow.air.output[i];
objAir["power"][i] = config.grow.air.power[i];
objAir["controlSensor"][i] = config.grow.air.controlSensor[i];
objAir["controlRead"][i] = config.grow.air.controlRead[i];
objAir["controlMode"][i] = config.grow.air.controlMode[i];
objAir["min"][i] = config.grow.air.min[i];
objAir["max"][i] = config.grow.air.max[i];
}
}
/* Grow Water */
JsonObject objWater = objGrow["water"].add<JsonObject>();
for(byte i = 0; i < Max_Outputs; i++) {
//Log.verbose(F("%s LightId %d, Max_Outputs %d, light.configured %T" CR), LogLoc, i, Max_Outputs, config.grow.light.configured[i]);
if(config.grow.water.configured[i] == true) {
objWater["configured"][i] = config.grow.water.configured[i];
objWater["output"][i] = config.grow.water.output[i];
objWater["onTime"][i] = config.grow.water.onTime[i];
objWater["controlSensor"][i] = config.grow.water.controlSensor[i];
objWater["controlRead"][i] = config.grow.water.controlRead[i];
objWater["controlMode"][i] = config.grow.water.controlMode[i];
objWater["min"][i] = config.grow.water.min[i];
objWater["max"][i] = config.grow.water.max[i];
objWater["interval"][i] = config.grow.water.interval[i];
objWater["intervalUnit"][i] = config.grow.water.intervalUnit[i];
}
}
/*
* END Building config.json here
*/
// if writeToSerial is true, output json to serial, but do not write to LittleFS
if(writeToSerial == false) {
#ifdef ESP8266
File file = LittleFS.open(CANGROW_CFG, "w");
#endif
#ifdef ESP32
fs::FS &fs = LittleFS;
File file = fs.open(CANGROW_CFG, FILE_WRITE);
#endif
if (!file) {
//Log.notice(F("%s loading config from: %s" CR), LogLoc, CANGROW_CFG);
Log.error(F("%s FAILED to open configfile for writing: %s" CR), LogLoc, CANGROW_CFG);
return false;
} else {
Log.notice(F("%s opened for writing %s" CR), LogLoc, CANGROW_CFG);
}
// Serialize JSON to file
if (serializeJson(doc, file) == 0) {
Log.error(F("%s FAILED to write configfile: %s" CR), LogLoc, CANGROW_CFG);
} else {
Log.notice(F("%s successfully written %s" CR), LogLoc, CANGROW_CFG);
}
file.close();
} else {
Log.notice(F("%s --- %s ---" CR), LogLoc, CANGROW_CFG);
serializeJsonPretty(doc, Serial);
Serial.println("");
Log.notice(F("%s ----------------------" CR), LogLoc, CANGROW_CFG);
}
/* every time config get saved, we save the actual time too
* so when ntp is not available, we hopefully do not lack behind too much
* (better then nothing) */
Time2FS_Save();
return true;
}
///*
//* ESP8266 functions
//*/
///*functions from https://github.com/esp8266/Arduino/blob/master/libraries/LittleFS/examples/LittleFS_Timestamp/LittleFS_Timestamp.ino*/
//#ifdef ESP8266
//void listDir(const char *dirname) {
//Serial.printf("Listing directory: %s\n", dirname);
//Dir root = LittleFS.openDir(dirname);
//while (root.next()) {
//File file = root.openFile("r");
//Serial.print(" FILE: ");
//Serial.print(root.fileName());
//Serial.print(" SIZE: ");
//Serial.print(file.size());
//time_t cr = file.getCreationTime();
//time_t lw = file.getLastWrite();
//file.close();
//struct tm *tmstruct = localtime(&cr);
//Serial.printf(" CREATION: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct->tm_year) + 1900, (tmstruct->tm_mon) + 1, tmstruct->tm_mday, tmstruct->tm_hour, tmstruct->tm_min, tmstruct->tm_sec);
//tmstruct = localtime(&lw);
//Serial.printf(" LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct->tm_year) + 1900, (tmstruct->tm_mon) + 1, tmstruct->tm_mday, tmstruct->tm_hour, tmstruct->tm_min, tmstruct->tm_sec);
//}
//}
//void readFile(const char *path) {
//Serial.printf("Reading file: %s\n", path);
//File file = LittleFS.open(path, "r");
//if (!file) {
//Serial.println("Failed to open file for reading");
//return;
//}
//Serial.print("Read from file: ");
//while (file.available()) { Serial.write(file.read()); }
//file.close();
//}
//void writeFile(const char *path, const char *message) {
//Serial.printf("Writing file: %s\n", path);
//File file = LittleFS.open(path, "w");
//if (!file) {
//Serial.println("Failed to open file for writing");
//return;
//}
//if (file.print(message)) {
//Serial.println("File written");
//} else {
//Serial.println("Write failed");
//}
//delay(2000); // Make sure the CREATE and LASTWRITE times are different
//file.close();
//}
//void appendFile(const char *path, const char *message) {
//Serial.printf("Appending to file: %s\n", path);
//File file = LittleFS.open(path, "a");
//if (!file) {
//Serial.println("Failed to open file for appending");
//return;
//}
//if (file.print(message)) {
//Serial.println("Message appended");
//} else {
//Serial.println("Append failed");
//}
//file.close();
//}
//void renameFile(const char *path1, const char *path2) {
//Serial.printf("Renaming file %s to %s\n", path1, path2);
//if (LittleFS.rename(path1, path2)) {
//Serial.println("File renamed");
//} else {
//Serial.println("Rename failed");
//}
//}
//void deleteFile(const char *path) {
//Serial.printf("Deleting file: %s\n", path);
//if (LittleFS.remove(path)) {
//Serial.println("File deleted");
//} else {
//Serial.println("Delete failed");
//}
//}
//#endif
///*
//* ESP32 functions
//*/
///*functions from https://github.com/espressif/arduino-esp32/blob/master/libraries/LittleFS/examples/LITTLEFS_time/LITTLEFS_time.ino*/
//#ifdef ESP32
//void listDir(fs::FS &fs, const char *dirname, uint8_t levels) {
//Serial.printf("Listing directory: %s\n", dirname);
//File root = fs.open(dirname);
//if (!root) {
//Serial.println("Failed to open directory");
//return;
//}
//if (!root.isDirectory()) {
//Serial.println("Not a directory");
//return;
//}
//File file = root.openNextFile();
//while (file) {
//if (file.isDirectory()) {
//Serial.print(" DIR : ");
//Serial.print(file.name());
//time_t t = file.getLastWrite();
//struct tm *tmstruct = localtime(&t);
//Serial.printf(
//" LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct->tm_year) + 1900, (tmstruct->tm_mon) + 1, tmstruct->tm_mday, tmstruct->tm_hour,
//tmstruct->tm_min, tmstruct->tm_sec
//);
//if (levels) {
//listDir(fs, file.path(), levels - 1);
//}
//} else {
//Serial.print(" FILE: ");
//Serial.print(file.name());
//Serial.print(" SIZE: ");
//Serial.print(file.size());
//time_t t = file.getLastWrite();
//struct tm *tmstruct = localtime(&t);
//Serial.printf(
//" LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct->tm_year) + 1900, (tmstruct->tm_mon) + 1, tmstruct->tm_mday, tmstruct->tm_hour,
//tmstruct->tm_min, tmstruct->tm_sec
//);
//}
//file = root.openNextFile();
//}
//}
//void removeDir(fs::FS &fs, const char *path) {
//Serial.printf("Removing Dir: %s\n", path);
//if (fs.rmdir(path)) {
//Serial.println("Dir removed");
//} else {
//Serial.println("rmdir failed");
//}
//}
//void readFile(fs::FS &fs, const char *path) {
//Serial.printf("Reading file: %s\n", path);
//File file = fs.open(path);
//if (!file) {
//Serial.println("Failed to open file for reading");
//return;
//}
//Serial.print("Read from file: ");
//while (file.available()) {
//Serial.write(file.read());
//}
//file.close();
//}
//void writeFile(fs::FS &fs, const char *path, const char *message) {
//Serial.printf("Writing file: %s\n", path);
//File file = fs.open(path, FILE_WRITE);
//if (!file) {
//Serial.println("Failed to open file for writing");
//return;
//}
//if (file.print(message)) {
//Serial.println("File written");
//} else {
//Serial.println("Write failed");
//}
//file.close();
//}
//void appendFile(fs::FS &fs, const char *path, const char *message) {
//Serial.printf("Appending to file: %s\n", path);
//File file = fs.open(path, FILE_APPEND);
//if (!file) {
//Serial.println("Failed to open file for appending");
//return;
//}
//if (file.print(message)) {
//Serial.println("Message appended");
//} else {
//Serial.println("Append failed");
//}
//file.close();
//}
//void renameFile(fs::FS &fs, const char *path1, const char *path2) {
//Serial.printf("Renaming file %s to %s\n", path1, path2);
//if (fs.rename(path1, path2)) {
//Serial.println("File renamed");
//} else {
//Serial.println("Rename failed");
//}
//}
//void deleteFile(fs::FS &fs, const char *path) {
//Serial.printf("Deleting file: %s\n", path);
//if (fs.remove(path)) {
//Serial.println("File deleted");
//} else {
//Serial.println("Delete failed");
//}
//}
//#endif

View file

@ -1,41 +0,0 @@
// 'CanGrow_Logo', 128x32px
const unsigned char bmpCanGrow_Logo [] PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x03, 0xc0, 0x00, 0x0f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00,
0x07, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x03, 0xe0, 0x00, 0x1f, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00,
0x0e, 0x03, 0x00, 0x00, 0x00, 0x00, 0x03, 0xe0, 0x00, 0x38, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00,
0x1c, 0x03, 0x00, 0x00, 0x00, 0x00, 0x07, 0xe0, 0x00, 0x70, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00,
0x18, 0x03, 0x00, 0x00, 0x00, 0x04, 0x07, 0xe0, 0x20, 0x60, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00,
0x18, 0x03, 0x00, 0x00, 0x00, 0x06, 0x07, 0xe0, 0xe0, 0x60, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00,
0x18, 0x00, 0x00, 0x00, 0x00, 0x03, 0x87, 0xe1, 0xc0, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x30, 0x00, 0x3f, 0xc3, 0xff, 0x03, 0xc7, 0xe3, 0xc0, 0xcf, 0xf9, 0xff, 0xe3, 0xfc, 0xc1, 0x83,
0x30, 0x00, 0x7f, 0xe3, 0xff, 0x83, 0xe7, 0xe7, 0xc0, 0xcf, 0xfb, 0xff, 0xe7, 0xfe, 0xc3, 0x87,
0x30, 0x00, 0xe0, 0x73, 0x80, 0xc1, 0xf7, 0xef, 0xc0, 0xc0, 0x1b, 0x80, 0x0e, 0x03, 0xc3, 0x86,
0x30, 0x00, 0xc0, 0x33, 0x00, 0xc1, 0xff, 0xff, 0x80, 0xc0, 0x1b, 0x00, 0x0c, 0x03, 0xc7, 0x8e,
0x30, 0x01, 0xc0, 0x37, 0x00, 0xc0, 0xff, 0xff, 0x80, 0xc0, 0x3b, 0x00, 0x1c, 0x03, 0xc7, 0x8c,
0x60, 0x01, 0xc0, 0x37, 0x00, 0xc0, 0xff, 0xff, 0x01, 0x80, 0x3f, 0x00, 0x18, 0x03, 0xcf, 0x9c,
0x60, 0x00, 0x00, 0x37, 0x00, 0xc0, 0x7f, 0xfe, 0x01, 0x80, 0x37, 0x00, 0x18, 0x03, 0xcf, 0x9c,
0x60, 0x00, 0x00, 0x76, 0x01, 0xc0, 0x1f, 0xfc, 0x01, 0x80, 0x36, 0x00, 0x18, 0x06, 0xdf, 0xb8,
0x60, 0x00, 0x7f, 0xe6, 0x01, 0x9f, 0x9f, 0xfc, 0xf9, 0x80, 0x36, 0x00, 0x18, 0x06, 0xdd, 0xb8,
0x60, 0x00, 0xff, 0xe6, 0x01, 0x87, 0xff, 0xff, 0xf1, 0x80, 0x76, 0x00, 0x18, 0x06, 0xdd, 0xb0,
0xc0, 0x01, 0xc0, 0xee, 0x01, 0x83, 0xff, 0xff, 0xc3, 0x00, 0x7e, 0x00, 0x30, 0x06, 0xf9, 0xf0,
0xc0, 0x0b, 0x80, 0x6e, 0x01, 0x81, 0xff, 0xff, 0x83, 0x00, 0x6e, 0x00, 0x30, 0x06, 0xf9, 0xe0,
0xc0, 0x1b, 0x00, 0xec, 0x01, 0x80, 0x1f, 0xf8, 0x03, 0x00, 0x6c, 0x00, 0x30, 0x0e, 0xf1, 0xe0,
0xc0, 0x3b, 0x00, 0xcc, 0x03, 0x80, 0x3f, 0xfc, 0x03, 0x00, 0xec, 0x00, 0x30, 0x0c, 0xf1, 0xc0,
0xc0, 0x7b, 0x01, 0xcc, 0x03, 0x00, 0x7f, 0xfe, 0x03, 0x01, 0xec, 0x00, 0x30, 0x1c, 0xe1, 0xc0,
0x7f, 0xf1, 0xff, 0xdc, 0x03, 0x00, 0xf0, 0x8f, 0x01, 0xff, 0xfc, 0x00, 0x1f, 0xf8, 0xe1, 0xc0,
0x3f, 0xe0, 0xff, 0xcc, 0x03, 0x00, 0x00, 0x80, 0x00, 0xff, 0xcc, 0x00, 0x0f, 0xf0, 0xc1, 0x80,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
// Array of all bitmaps for convenience. (Total bytes used to store images in PROGMEM = 528)
const int bmpallArray_LEN = 1;
const unsigned char* bmpallArray[1] = {
bmpCanGrow_Logo
};

View file

@ -1,540 +0,0 @@
/*
*
* include/CanGrow_Output.h - Output header file
*
*
*
*/
#include "Output/Output_Common.h"
#include "Output/Output_I2C_01_MCP4725.h"
#include "Output/Output_I2C_02_GP8403.h"
/* OutputI2C index struct */
struct OutputI2C_Index {
const char name[16];
const byte port[OUTPUT_TYPE_I2C_MAX_PORTS];
const byte max;
};
OutputI2C_Index OutputI2Cindex[] {
/* OutputI2C 00 is unset */
{ "unset", {
{},
}},
/* OutputI2C 01 */
{
OUTPUT_I2C_01_NAME,
{
OUTPUT_TYPE_I2C_PORT_BYTE
},
sizeof(Output_I2C_01_MCP4725_Addr),
},
/* 02 - DFRobot Gravity (GP8403) */
{
OUTPUT_I2C_02_NAME,
{
OUTPUT_TYPE_I2C_PORT_BYTE,
OUTPUT_TYPE_I2C_PORT_BYTE
},
sizeof(Output_I2C_02_GP8403_Addr),
}
};
/* Dont forget to increase the index counter after you added a new output module */
const byte OutputI2Cindex_length = 2;
byte Output_I2C_Addr_Init_Update(const byte OutputI2CindexId, const byte AddrId, const byte PortId, const byte Mode, const bool Invert = false, const byte Value = 0) {
const static char LogLoc[] PROGMEM = "[Output:I2C:Addr_Init_Update]";
/* Multi purpose function.
*
* Modes:
* 0 - return the i2c address as byte
* 1 - init the output i2c module, returns true (1) if succeeded
* 2 - update i2 module data
*/
//byte Dummy_Addr[] = { 0x42, 0x69 };
/* invert Value if set, save it to Value_tmp */
byte Value_tmp = Value;
if(Invert == true)
/* Value comes from outputState[] which is only type byte (max 255) */
Value_tmp = 255 - Value;
switch(OutputI2CindexId) {
/* I2C Output module 01 */
case 1:
switch(Mode) {
case OUPUT_I2C_AIU_MODE_ADDR:
return Output_I2C_01_MCP4725_Addr[AddrId];
break;
case OUPUT_I2C_AIU_MODE_INIT:
return Output_I2C_01_MCP4725_Init(AddrId, PortId);
break;
case OUPUT_I2C_AIU_MODE_UPDATE:
Output_I2C_01_MCP4725_Update(AddrId, PortId, Value_tmp);
break;
}
break;
/* I2C Output module 02 */
case 2:
switch(Mode) {
case OUPUT_I2C_AIU_MODE_ADDR:
return Output_I2C_02_GP8403_Addr[AddrId];
break;
case OUPUT_I2C_AIU_MODE_INIT:
return Output_I2C_02_GP8403_Init(AddrId, PortId);
break;
case OUPUT_I2C_AIU_MODE_UPDATE:
Output_I2C_02_GP8403_Update(AddrId, PortId, Value_tmp);
break;
}
break;
/* 02 - dummy*/
//case 2:
//switch(Mode) {
//case 0:
//return Dummy_Addr[AddrId];
//break;
//case 1:
//return true;
//break;
//case 2:
//true;
//break;
//}
//break;
/* unknown i2c output module id */
default:
Log.error(F("%s OutputI2Cindex ID %d not found" CR), LogLoc, OutputI2CindexId);
break;
}
return 0;
}
byte Output_GPIO_Init_Update(const byte GPIOindexId, const bool PWM, const bool Invert, const byte Mode, const byte Value = 0) {
const static char LogLoc[] PROGMEM = "[Output:GPIO:Init_Update]";
bool Value_bool = Value;
switch(Mode) {
case OUTPUT_GPIO_IU_MODE_INIT:
pinMode(GPIOindex[GPIOindexId].gpio, OUTPUT);
if(Invert == true) {
digitalWrite(GPIOindex[GPIOindexId].gpio, 1 - Value_bool);
} else {
digitalWrite(GPIOindex[GPIOindexId].gpio, Value_bool);
}
return true;
break;
case OUTPUT_GPIO_IU_MODE_UPDATE:
if((PWM == false) || (GPIOindex[GPIOindexId].note == NO_PWM)) {
if(Invert == true) {
digitalWrite(GPIOindex[GPIOindexId].gpio, 1 - Value_bool);
} else {
digitalWrite(GPIOindex[GPIOindexId].gpio, Value);
}
} else {
/* output inverted? */
if(Invert == true) {
/* when output is set to 0 (off), use digitalWrite LOW to prevent spikes */
if(GPIOindex[GPIOindexId].gpio < 255) {
analogWrite(GPIOindex[GPIOindexId].gpio, 255 - Value);
} else {
digitalWrite(GPIOindex[GPIOindexId].gpio, HIGH);
#ifdef DEBUG
Log.verbose(F("%s digitalWrite HIGH" CR), LogLoc);
#endif
}
/* not inverted */
} else {
/* when output is set to 0 (off), use digitalWrite LOW to prevent spikes */
if(GPIOindex[GPIOindexId].gpio < 255) {
analogWrite(GPIOindex[GPIOindexId].gpio, Value);
} else {
digitalWrite(GPIOindex[GPIOindexId].gpio, LOW);
#ifdef DEBUG
Log.verbose(F("%s digitalWrite LOW" CR), LogLoc);
#endif
}
}
}
break;
}
return 0;
}
bool Output_Webcall_Init_Update(const byte OutputId, const bool Value = false) {
const static char LogLoc[] PROGMEM = "[Output:Webcall:Init_Update]";
/* here we invert the value if set. First we hand it to a tmp var, Value_tmp */
bool Value_tmp = Value;
/* check if output has inverted flagged */
if(config.system.output.invert[OutputId] == true)
Value_tmp = 1 - Value;
String url;
url += F("http://");
url += config.system.output.webcall_host[OutputId];
url += F("/");
WiFiClient client;
HTTPClient http;
///* set timeout to three seconds */
//#ifdef ESP8266
///* on ESP8266 http.setTimeout accepts miliseconds */
//http.setTimeout(3000);
//#endif
//#ifdef ESP32
///* on ESP32 http.setTimeout() accepts only seconds - https://github.com/espressif/arduino-esp32/issues/3732 */
//http.setTimeout(3);
//#endif
switch(Value_tmp) {
/* turn on */
case true:
url += config.system.output.webcall_path_on[OutputId];
break;
/* turn off */
case false:
url += config.system.output.webcall_path_off[OutputId];
break;
}
/* build request */
http.begin(client, url);
/* fire request and check result, */
int httpResponseCode = http.GET();
/* if 200 , OK */
if(httpResponseCode > 0) {
#ifdef DEBUG2
Log.verbose(F("%s GET %s (%d)" CR), LogLoc, url.c_str(), httpResponseCode);
#endif
return true;
} else {
#ifdef DEBUG2
Log.verbose(F("%s FAILED GET %s (%d)" CR), LogLoc, url.c_str(), httpResponseCode);
#endif
return false;
}
return 0;
}
bool Output_Check_PWM(const byte OutputId) {
/* when we verify output is GPIO PWM OR */
if(((config.system.output.type[OutputId] == OUTPUT_TYPE_GPIO) && (config.system.output.gpio_pwm[OutputId] == true) && (GPIOindex[config.system.output.type[OutputId]].note != NO_PWM)) ||
/* Output is type I2C */
(config.system.output.type[OutputId] == OUTPUT_TYPE_I2C)
) {
/* return true */
return true;
} else {
return false;
}
}
/*
* Output_Device
*
* add, remove, (modify) config.grow.light[] objects
*/
void Output_Device_Grow_AddRemove(const byte OutputId, const byte mode) {
/* switch on device type of the output
*
* Modes:
* 0 - add - receive OutputId
* 1 - remove - receive LightId
* 2 - modify - receive LightId
* */
switch(config.system.output.device[OutputId]) {
case OUTPUT_DEVICE_LIGHT:
//byte LightId;
switch(mode) {
/* add */
case 0:
config.grow.light.configured[OutputId] = true;
config.grow.light.output[OutputId] = OutputId;
config.grow.light.sunriseHourVeg[OutputId] = 6;
config.grow.light.sunriseMinuteVeg[OutputId] = 0;
config.grow.light.sunsetHourVeg[OutputId] = 23;
config.grow.light.sunsetMinuteVeg[OutputId] = 59;
config.grow.light.sunriseHourBloom[OutputId] = 6;
config.grow.light.sunriseMinuteBloom[OutputId] = 0;
config.grow.light.sunsetHourBloom[OutputId] = 18;
config.grow.light.sunsetMinuteBloom[OutputId] = 0;
config.grow.light.power[OutputId] = 0;
if(Output_Check_PWM(OutputId)) {
config.grow.light.fade[OutputId] = true;
} else {
config.grow.light.fade[OutputId] = false;
}
config.grow.light.fadeDuration[OutputId] = 30;
//SaveConfig()
break;
/* remove */
case 1:
/* get LightId for this Output */
//for(byte i = 0; i < Max_Outputs; i++) {
//if((config.grow.light.configured[i] == true) && (config.grow.light.output[i] == OutputId)) {
//LightId = i;
//break;
//}
//}
config.grow.light.configured[OutputId] = 0;
config.grow.light.output[OutputId] = 0;
config.grow.light.sunriseHourVeg[OutputId] = 0;
config.grow.light.sunriseMinuteVeg[OutputId] = 0;
config.grow.light.sunsetHourVeg[OutputId] = 0;
config.grow.light.sunsetMinuteVeg[OutputId] = 0;
config.grow.light.sunriseHourBloom[OutputId] = 0;
config.grow.light.sunriseMinuteBloom[OutputId] = 0;
config.grow.light.sunsetHourBloom[OutputId] = 0;
config.grow.light.sunsetMinuteBloom[OutputId] = 0;
config.grow.light.power[OutputId] = 0;
config.grow.light.fade[OutputId] = 0;
config.grow.light.fadeDuration[OutputId] = 0;
//SaveConfig();
break;
//case 2:
//true;
//break;
}
break;
case OUTPUT_DEVICE_FAN:
case OUTPUT_DEVICE_HUMIDIFIER:
case OUTPUT_DEVICE_DEHUMIDIFIER:
case OUTPUT_DEVICE_HEATING:
switch(mode) {
case 0:
config.grow.air.configured[OutputId] = true;
config.grow.air.output[OutputId] = OutputId;
config.grow.air.power[OutputId] = 0;
config.grow.air.controlSensor[OutputId] = 255; // 255 means unconfigured, because SensorId begins at 0
config.grow.air.controlRead[OutputId] = 255; // same here
config.grow.air.controlMode[OutputId] = 0;
config.grow.air.min[OutputId] = 0;
config.grow.air.max[OutputId] = 0;
break;
case 1:
config.grow.air.configured[OutputId] = false;
config.grow.air.output[OutputId] = 0;
config.grow.air.power[OutputId] = 0;
config.grow.air.controlSensor[OutputId] = 0;
config.grow.air.controlRead[OutputId] = 0;
config.grow.air.controlMode[OutputId] = 0;
config.grow.air.min[OutputId] = 0;
config.grow.air.max[OutputId] = 0;
break;
}
break;
case OUTPUT_DEVICE_PUMP:
switch(mode) {
case 0:
config.grow.water.configured[OutputId] = true;
config.grow.water.output[OutputId] = OutputId;
config.grow.water.onTime[OutputId] = 0;
config.grow.water.controlSensor[OutputId] = 255; // 255 means unconfigured, because SensorId begins at 0
config.grow.water.controlRead[OutputId] = 255; // same here
config.grow.water.controlMode[OutputId] = 0;
config.grow.water.min[OutputId] = 0;
config.grow.water.max[OutputId] = 0;
config.grow.water.interval[OutputId] = 0;
config.grow.water.intervalUnit[OutputId] = 0;
break;
case 1:
config.grow.water.configured[OutputId] = false;
config.grow.water.output[OutputId] = 0;
config.grow.water.onTime[OutputId] = 0;
config.grow.water.controlSensor[OutputId] = 0;
config.grow.water.controlRead[OutputId] = 0;
config.grow.water.controlMode[OutputId] = 0;
config.grow.water.min[OutputId] = 0;
config.grow.water.max[OutputId] = 0;
config.grow.water.interval[OutputId] = 0;
config.grow.water.intervalUnit[OutputId] = 0;
break;
}
break;
//case OUTPUT_DEVICE_HUMIDIFIER:
//break;
//case OUTPUT_DEVICE_DEHUMIDIFIER:
//break;
//case OUTPUT_DEVICE_HEATING:
//break;
}
}
/*
* Output main functions
*
*/
void Output_Init() {
/* initialize all configured outputs */
const static char LogLoc[] PROGMEM = "[Output:Init]";
#ifdef DEBUG
Log.verbose(F("%s == configured outputs ==" CR), LogLoc);
#endif
/* interate through all available Output IDs */
for(byte i = 0; i < Max_Outputs; i++) {
/* if configured */
if(config.system.output.type[i] > 0) {
/* get the configured output type */
switch(config.system.output.type[i]) {
case OUTPUT_TYPE_GPIO:
#ifdef DEBUG
Log.verbose(F("%s Output ID %d: %s - Device %s (%d), GPIO %d (%d), Enabled %T, PWM %T, Invert %T" CR),
LogLoc,
i, config.system.output.name[i],
Output_Device_descr[config.system.output.device[i]], config.system.output.device[i],
GPIOindex[config.system.output.gpio[i]].gpio, config.system.output.gpio[i],
config.system.output.enabled[i], config.system.output.gpio_pwm[i], config.system.output.invert[i]);
#endif
/* TODO implement gpio init */
outputStatus[i] = Output_GPIO_Init_Update(config.system.output.gpio[i], config.system.output.gpio_pwm[i], config.system.output.invert[i], OUTPUT_GPIO_IU_MODE_INIT);
break;
case OUTPUT_TYPE_I2C:
#ifdef DEBUG
Log.verbose(F("%s Output ID %d: %s - Device %s (%d), I2C type %s (%d), I2C addr 0x%x (%d), Module port %d, Enabled %T, PWM %T, Invert %T" CR),
LogLoc,
i, config.system.output.name[i],
Output_Device_descr[config.system.output.device[i]], config.system.output.device[i],
OutputI2Cindex[config.system.output.i2c_type[i]].name, config.system.output.i2c_type[i],
config.system.output.enabled[i], config.system.output.gpio_pwm[i], config.system.output.invert[i]);
#endif
outputStatus[i] = Output_I2C_Addr_Init_Update(config.system.output.i2c_type[i], config.system.output.i2c_addr[i], config.system.output.i2c_port[i], OUPUT_I2C_AIU_MODE_INIT, config.system.output.invert[i]);
break;
case OUTPUT_TYPE_WEB:
#ifdef DEBUG
Log.verbose(F("%s Output ID %d: %s - Device %s (%d), Webcall host %s, Webcall Path ON '%s', Webcall Path OFF '%s', Enabled %T, PWM %T, Invert %T" CR),
LogLoc,
i, config.system.output.name[i],
Output_Device_descr[config.system.output.device[i]], config.system.output.device[i],
config.system.output.webcall_host[i], config.system.output.webcall_path_on[i], config.system.output.webcall_path_off[i],
config.system.output.enabled[i], config.system.output.gpio_pwm[i], config.system.output.invert[i]);
#endif
/* TODO implement webcall init */
outputStatus[i] = Output_Webcall_Init_Update(i);
break;
default:
Log.error(F("%s (%d) Output type %d not found" CR), LogLoc, i, config.system.output.type[i]);
break;
}
}
}
}
void Output_Update() {
const static char LogLoc[] PROGMEM = "[Output:Update]";
#ifdef DEBUG2
unsigned long mStart = millis();
unsigned long mStop;
Log.verbose(F("%s Start %u" CR), LogLoc, mStart);
#endif
/* interate through all available Output IDs */
for(byte i = 0; i < Max_Outputs; i++) {
/* if configured and enabled */
if((config.system.output.type[i] > 0) && (config.system.output.enabled[i] == true)) {
/* get the configured output type */
switch(config.system.output.type[i]) {
/*******
* GPIO
* *****/
case OUTPUT_TYPE_GPIO:
/* update GPIO output */
Output_GPIO_Init_Update(config.system.output.gpio[i], config.system.output.gpio_pwm[i], config.system.output.invert[i], OUTPUT_GPIO_IU_MODE_UPDATE, outputState[i]);
break;
/*******
* I2C
* *****/
case OUTPUT_TYPE_I2C:
/* perform I2C output update only, when outputStatus is OK (true) */
if(outputStatus[i] == true)
Output_I2C_Addr_Init_Update(config.system.output.i2c_type[i], config.system.output.i2c_addr[i], config.system.output.i2c_port[i], OUPUT_I2C_AIU_MODE_UPDATE, config.system.output.invert[i], outputState[i]);
break;
/*******
* WEB
* ****/
case OUTPUT_TYPE_WEB:
/* check how often webcall output failed. if limit exceeded, do not update anymore */
if((outputStatus[i] == false) && (outputWebcallFailed[i] > 5)) {
/* if webcall fail counter has reached limit of 255, reset to 0
* so we retry a call. */
if(outputWebcallFailed[i] >= 255) {
outputWebcallFailed[i] = 0;
} else {
/* increment webcall failed counter. */
outputWebcallFailed[i]++;
}
} else {
/* update webcall output */
outputStatus[i] = Output_Webcall_Init_Update(i, outputState[i]);
if(outputStatus[i] == false) {
/* increment webcall failed counter */
outputWebcallFailed[i]++;
} else {
/* otherwise set to 0 */
outputWebcallFailed[i] = 0;
}
}
true;
break;
default:
Log.error(F("%s (%d) Output type %d not found" CR), LogLoc, i, config.system.output.type[i]);
break;
}
}
}
#ifdef DEBUG2
mStop = millis();
Log.verbose(F("%s Stop %u (%u)" CR), LogLoc, mStop, mStop - mStart);
#endif
}

View file

@ -1,758 +0,0 @@
/*
*
* include/CanGrow_Sensor.h - sensor header file
*
*
*
* ADD A NEW SENSOR
* ****************
* If you want to add a new sensor, you have to to following things:
*
* Check what it's the last used SensorIndex ID. If it's Sensor 08, you have to
* take 09 as next.
*
* Copy Sensor/00_Example.h to Sensor/09_YourSensor.h and rename everything in it from
* "00_Example" to "09_YourSensor" and edit all the needed functions and variables to your needs.
*
* Add a new include line to CanGrow_Sensor.h (this file)
* #include "Sensor/09_YourSensor.h"
*
* Add a new Entry to the SensorIndex Array, like:
* ***** SensorIndex[] *****
* ,{
* // 9 - YourSensor
* // Sensor Name
* SENSOR_09_NAME,
* {
* // Sensor Readings
* SENSOR_READ_TYPE_TEMP,
* SENSOR_READ_TYPE_HUMIDITY,
* SENSOR_READ_TYPE_RAW
* },
* // Maximal Sensor Units (most time the Sum of available Addresses)
* sizeof(Sensor_09_YourSensor_Addr),
* }
* ************************
*
* If you are done with that, you have to add a new Switch case for the new Sensor ID
* to Sensor_Addr_Init_Update() and Sensor_getValue() like:
*
* ***** Sensor_Addr_Init_Update() *****
* // Sensor 09
* case 9:
* switch(mode) {
* case 0:
* return Sensor_09_YourSensor_Addr[AddrId];
* break;
*
* case 1:
* return Sensor_09_YourSensor_Init(AddrId);
* break;
*
* case 2:
* Sensor_09_YourSensor_Update(AddrId);
* break;
* }
* break;
* *************************************
*
* ***** Sensor_getValue() *****
* // Sensor 09
* case 9:
* return Sensor_09_YourSensor[AddrId][ReadValId];
* break;
* *****************************
*/
/* should come as dependency with all adafruit sensor libs. If not, see here:
* https://github.com/adafruit/Adafruit_Sensor */
#include <Adafruit_Sensor.h>
#include "Sensor/Sensor_Common.h"
#include "Sensor/01_ADC_builtin.h"
#include "Sensor/02_BME280.h"
#include "Sensor/03_BME680.h"
#include "Sensor/04_SHT3x.h"
#include "Sensor/05_MLX90614.h"
#include "Sensor/06_TCS34725.h"
#include "Sensor/07_ADS1115.h"
#include "Sensor/08_ADS1015.h"
#include "Sensor/09_Chirp.h"
#include "Sensor/10_CCS811.h"
/*
* Sensor Todo list:
*
* - CCS811 CO2 sensor, will have type SENSOR_TYPE_I2C_WITH_GPIO, it needs signal on pin WAK
* cheap - ~ 8 on Aliexpress
* - HX711 for weight sensor, this sensor needs two GPIOs for communication
* - SCD30/40 CO2 sensor, expensive, >70
*/
struct Sensor_Index {
/*
* Sensor Index
* - name
* - readings (array, up to 8 entries)
* - 0 unset
* - 1 Raw
* - 2 Temp
* - 3 Humidity
* - 4 Moisture
* - 5 Pressure
* - 6 Gas restistance
* - max units
*
*/
const char name[16];
const byte type;
const byte read[Max_Sensors_Read];
const byte max;
const byte gpioMax;
};
Sensor_Index SensorIndex[] {
/*
* Example:
*
* // 0 - Example
* { SENSOR_00_NAME,
* {
* SENSOR_READ_TYPE_TEMP,
* SENSOR_READ_TYPE_HUMIDITY,
* SENSOR_READ_TYPE_RAW,
* SENSOR_READ_TYPE_RAW
* },
* // max nr of sensor units by nr of available addresses
* sizeof(Sensor_00_Example_Addr),
* },
*
*/
/* 0 is for unset in config */
{ "unset", 255, {
{},
}},
// 1 - internal ADC
{ SENSOR_01_NAME,
SENSOR_TYPE_INTADC,
{
SENSOR_READ_TYPE_RAW
},
SENSOR_01_MAX,
},
// 2 - BME280
{ SENSOR_02_NAME,
SENSOR_TYPE_I2C,
{
SENSOR_READ_TYPE_TEMP,
SENSOR_READ_TYPE_HUMIDITY,
SENSOR_READ_TYPE_PRESSURE,
SENSOR_READ_TYPE_ALTITUDE
},
// max nr of sensor units by nr of available addresses
sizeof(Sensor_02_BME280_Addr),
},
// 3 - BME680
{ SENSOR_03_NAME,
SENSOR_TYPE_I2C,
{
SENSOR_READ_TYPE_TEMP,
SENSOR_READ_TYPE_HUMIDITY,
SENSOR_READ_TYPE_PRESSURE,
SENSOR_READ_TYPE_ALTITUDE,
SENSOR_READ_TYPE_GAS_RESISTANCE
},
sizeof(Sensor_03_BME680_Addr),
},
// 4 - SHT3x
{ SENSOR_04_NAME,
SENSOR_TYPE_I2C,
{
SENSOR_READ_TYPE_TEMP,
SENSOR_READ_TYPE_HUMIDITY
},
sizeof(Sensor_04_SHT3X_Addr),
},
// 5 - MLX90614
{ SENSOR_05_NAME,
SENSOR_TYPE_I2C,
{
/* Ambient temp */
SENSOR_READ_TYPE_TEMP,
/* Object temp */
SENSOR_READ_TYPE_TEMP
},
sizeof(Sensor_05_MLX90614_Addr),
},
// 6 - TCS34725
{ SENSOR_06_NAME,
SENSOR_TYPE_I2C,
{
SENSOR_READ_TYPE_COLOR_TEMP,
SENSOR_READ_TYPE_LUX,
SENSOR_READ_TYPE_COLOR_RED,
SENSOR_READ_TYPE_COLOR_GREEN,
SENSOR_READ_TYPE_COLOR_BLUE
},
sizeof(Sensor_06_TCS34725_Addr),
},
{
// 7 - ADS1115
SENSOR_07_NAME,
SENSOR_TYPE_I2C,
{
/* A0 */
SENSOR_READ_TYPE_RAW,
/* A1 */
SENSOR_READ_TYPE_RAW,
/* A2 */
SENSOR_READ_TYPE_RAW,
/* A3 */
SENSOR_READ_TYPE_RAW
},
sizeof(Sensor_07_ADS1115_Addr),
},
{
// 8 - ADS1015
SENSOR_08_NAME,
SENSOR_TYPE_I2C,
{
SENSOR_READ_TYPE_RAW,
SENSOR_READ_TYPE_RAW,
SENSOR_READ_TYPE_RAW,
SENSOR_READ_TYPE_RAW
},
sizeof(Sensor_08_ADS1015_Addr),
},
{
// 9 - I2C Chirp soilmoisture/temperature sensor
SENSOR_09_NAME,
SENSOR_TYPE_I2C,
{
// raw soilmoisture value
SENSOR_READ_TYPE_RAW,
// temperature
SENSOR_READ_TYPE_TEMP,
/* raw light value takes 3s to use, so we dont use it. if you need it,
* uncomment it here and in Sensor/09_Chirp Sensor_09_Chirp_Update() */
// SENSOR_READ_TYPE_RAW
},
sizeof(Sensor_09_Chirp_Addr),
},
{
/* 10 - CCS811 CO2 I2C sensor */
SENSOR_10_NAME,
SENSOR_TYPE_I2C,
{
/* CO2 as parts per million */
SENSOR_READ_TYPE_PARTS_PER_MILLION,
/* TVOC value (Total Volatile Organic Compouds)*/
SENSOR_READ_TYPE_TVOC,
},
sizeof(Sensor_10_CCS811_Addr),
}
};
/* sum up of number of sensors. Dont forget to increment if you add one :) */
const byte SensorIndex_length = 10;
byte Sensor_Addr_Init_Update(const byte SensorIndexId, const byte AddrId, const byte mode, const byte Gpio2 = 0) {
const static char LogLoc[] PROGMEM = "[Sensor:Addr_Init_Update]";
/* Multi purpose function.
*
* Modes:
* 0 - get the address as byte (i2c_addr index, gpio index)
* 1 - init the sensor, returns true (1) if succeeded
* 2 - update sensors data
*
* When using a sensor which is using bare GPIOs like int ADC, or some 1- or 2-Wire sensors
* AddrId is used for the first GPIO and gpio2 for the second.
* Maybe i come up later with a better idea, but for now...
*/
#ifdef DEBUG3
if(mode > 0)
Log.verbose(F("%s Mode: %d, SensorIndexId: %d, AddrId: %d" CR), LogLoc, mode, SensorIndexId, AddrId);
#endif
switch(SensorIndexId) {
/*
* Example:
*
* case 0:
* if(!onlyReturn)
* Sensor_00_Example_Init(AddrId);
* return Sensor_00_Example_Addr[AddrId];
* break;
*/
/* Sensor 01 */
/* Internal ADC is an exception. Its clearly not an I2C device, but as I let the ADC
* have for both, 8266 and 32, configurable GPIOs, it kinda has.
* AddrId with int ADC is GPIOindex[].type */
case 1:
switch(mode) {
case 0:
/* internal ADC does not has a address here */
return 0;
break;
case 1:
return Sensor_01_ADC_Init(AddrId);
break;
case 2:
Sensor_01_ADC_Update(AddrId);
break;
}
break;
/* Sensor 02 */
case 2:
switch(mode) {
case 0:
return Sensor_02_BME280_Addr[AddrId];
break;
case 1:
return Sensor_02_BME280_Init(AddrId);
break;
case 2:
Sensor_02_BME280_Update(AddrId);
break;
}
break;
/* Sensor 03 */
case 3:
switch(mode) {
case 0:
return Sensor_03_BME680_Addr[AddrId];
break;
case 1:
return Sensor_03_BME680_Init(AddrId);
break;
case 2:
Sensor_03_BME680_Update(AddrId);
break;
}
break;
/* Sensor 04 */
case 4:
switch(mode) {
case 0:
return Sensor_04_SHT3X_Addr[AddrId];
break;
case 1:
return Sensor_04_SHT3X_Init(AddrId);
break;
case 2:
Sensor_04_SHT3X_Update(AddrId);
break;
}
break;
/* Sensor 05 */
case 5:
switch(mode) {
case 0:
return Sensor_05_MLX90614_Addr[AddrId];
break;
case 1:
return Sensor_05_MLX90614_Init(AddrId);
break;
case 2:
Sensor_05_MLX90614_Update(AddrId);
break;
}
break;
/* Sensor 06 */
case 6:
switch(mode) {
case 0:
return Sensor_06_TCS34725_Addr[AddrId];
break;
case 1:
return Sensor_06_TCS34725_Init(AddrId);
break;
case 2:
Sensor_06_TCS34725_Update(AddrId);
break;
}
break;
/* Sensor 07 */
case 7:
switch(mode) {
case 0:
return Sensor_07_ADS1115_Addr[AddrId];
break;
case 1:
return Sensor_07_ADS1115_Init(AddrId);
break;
case 2:
Sensor_07_ADS1115_Update(AddrId);
break;
}
break;
/* Sensor 08 */
case 8:
switch(mode) {
case 0:
return Sensor_08_ADS1015_Addr[AddrId];
break;
case 1:
return Sensor_08_ADS1015_Init(AddrId);
break;
case 2:
Sensor_08_ADS1015_Update(AddrId);
break;
}
break;
/* Sensor 09 */
case 9:
switch(mode) {
case 0:
return Sensor_09_Chirp_Addr[AddrId];
break;
case 1:
return Sensor_09_Chirp_Init(AddrId);
break;
case 2:
Sensor_09_Chirp_Update(AddrId);
break;
}
break;
/* Sensor 10 */
case 10:
switch(mode) {
case 0:
return Sensor_10_CCS811_Addr[AddrId];
break;
case 1:
return Sensor_10_CCS811_Init(AddrId);
break;
case 2:
Sensor_10_CCS811_Update(AddrId);
break;
}
break;
/* unknown sensor id */
default:
Log.error(F("%s SensorIndex ID %d not found" CR), LogLoc, config.system.sensor.type[AddrId]);
break;
}
return 0;
}
float Sensor_getValue(const byte SensorIndexId, const byte AddrId, const byte ReadValId = 0) {
const static char LogLoc[] PROGMEM = "[Sensor:getValue]";
/* not the best solution, but solution for the moment
* i hope i can come up in future with a way, where i do not have to
* maintain three different places with huge switch cases
* and return everything as float, even when it could be easy an int
*
* here we read the value ReadVal from the given SensorIndexId and its AddrId
* In case of RAW readings, like from ADCs , There is an Index ReadValId as well
*/
switch(SensorIndexId) {
/* Sensor 01 */
case 1:
#ifdef ESP8266
return Sensor_01_ADC[AddrId];
#endif
#ifdef ESP32
/* */
return Sensor_01_ADC[Sensor_01_ADC_ArrId(AddrId)];
#endif
break;
/* Sensor 02 */
case 2:
return Sensor_02_BME280[AddrId][ReadValId];
break;
/* Sensor 03 */
case 3:
return Sensor_03_BME680[AddrId][ReadValId];
break;
/* Sensor 04 */
case 4:
return Sensor_04_SHT3X[AddrId][ReadValId];
break;
/* Sensor 05 */
case 5:
return Sensor_05_MLX90614[AddrId][ReadValId];
break;
/* Sensor 06 */
case 6:
return Sensor_06_TCS34725[AddrId][ReadValId];
break;
/* Sensor 07 */
case 7:
return Sensor_07_ADS1115[AddrId][ReadValId];
break;
/* Sensor 08 */
case 8:
return Sensor_08_ADS1015[AddrId][ReadValId];
break;
/* Sensor 09 */
case 9:
return Sensor_09_Chirp[AddrId][ReadValId];
break;
/* Sensor 10 */
case 10:
return Sensor_10_CCS811[AddrId][ReadValId];
break;
/* unknown sensor id */
default:
Log.error(F("%s SensorIndex ID %d not found" CR), LogLoc, SensorIndexId);
break;
}
return 0;
}
/*
* *********************************************************************************************
* From here on you do not need to touch any code (hopefully) if you want to add a new sensor!
* *********************************************************************************************
*/
float Sensor_getCalibratedValue(const byte SensorId, const byte ReadId) {
float valueRaw;
float value;
/* if SensorId is configured and there is a reading on ReadingId */
if((config.system.sensor.type[SensorId] > 0) && (SensorIndex[config.system.sensor.type[SensorId]].read[ReadId] > 0)) {
/* first, get the raw / original value */
if(SensorIndex[config.system.sensor.type[SensorId]].type == SENSOR_TYPE_INTADC) {
valueRaw = Sensor_getValue(config.system.sensor.type[SensorId], config.system.sensor.gpio[SensorId][0], ReadId);
} else if(SensorIndex[config.system.sensor.type[SensorId]].type == SENSOR_TYPE_I2C) {
valueRaw = Sensor_getValue(config.system.sensor.type[SensorId], config.system.sensor.i2c_addr[SensorId], ReadId);
}
/* if reading is RAW, check what to do with it */
if(SensorIndex[config.system.sensor.type[SensorId]].read[ReadId] == SENSOR_READ_TYPE_RAW) {
/* config.system.sensor.rawConvert
* 0 - unconfigured, return raw value
* 1 - soilmoisture, return percentage
* 2 - other TBD */
switch(config.system.sensor.rawConvert[SensorId][ReadId]) {
/* soilmoisture as percentage */
case SENSOR_CONVERT_RAW_TYPE_SOILMOISTURE:
/* dont use map when both , low and high, are 0 - this causes a crash */
if((config.system.sensor.low[SensorId][ReadId] > 0) || (config.system.sensor.high[SensorId][ReadId] > 0)) {
/* use map to calculate percentage value */
value = map(valueRaw, config.system.sensor.low[SensorId][ReadId], config.system.sensor.high[SensorId][ReadId], 0, 100);
} else {
value = 0;
}
return value;
break;
default:
return valueRaw;
break;
}
} else {
/* if not a RAW value, return the value with the offset */
return valueRaw + config.system.sensor.offset[SensorId][ReadId];
}
}
return 0;
}
void Sensor_Log_Readings(const byte SensorIndexId , const byte AddrId) {
const static char LogLoc[] PROGMEM = "[Sensor:Log_Readings]";
Log.verbose(F("%s Sensor %s (%d)" CR), LogLoc, SensorIndex[SensorIndexId].name, AddrId);
/* iterate through the SensorIndex readings */
for(byte j = 0; j < Max_Sensors_Read; j++) {
/* if SensorIndex[].read[] > 0 (means there is a value to read) */
if(SensorIndex[SensorIndexId].read[j] > 0 ) {
if(SensorIndex[SensorIndexId].type == SENSOR_TYPE_INTADC) {
Log.verbose(F("%s - %s: %F %s" CR), LogLoc, Sensor_Read_descr[SensorIndex[SensorIndexId].read[j]],
Sensor_getValue(SensorIndexId, AddrId), Sensor_Read_unit[SensorIndex[SensorIndexId].read[j]]);
} else {
Log.verbose(F("%s - %s: %F %s" CR), LogLoc, Sensor_Read_descr[SensorIndex[SensorIndexId].read[j]],
Sensor_getValue(SensorIndexId, AddrId, j), Sensor_Read_unit[SensorIndex[SensorIndexId].read[j]]);
}
}
}
}
void Sensor_Init() {
/* main function that does initialize all configured sensors at once. called from setup()*/
const static char LogLoc[] PROGMEM = "[Sensor:Init]";
/* Go through all configured sensors and initialize them */
#ifdef DEBUG
Log.verbose(F("%s == Sensor drivers ==" CR), LogLoc);
for(byte i = 1; i <= SensorIndex_length; i++) {
Log.verbose(F("%s Sensor_Index %d, Name %s, Readings" CR), LogLoc, i, SensorIndex[i].name );
for(byte j = 0; j < Max_Sensors_Read; j++) {
if(SensorIndex[i].read[j] > 0 ) {
Log.verbose(F("%s %d: %S %S (%d)" CR), LogLoc, j, Sensor_Read_descr[SensorIndex[i].read[j]], Sensor_Read_unit[SensorIndex[i].read[j]], SensorIndex[i].read[j]);
}
}
}
Log.verbose(F("%s == configured Sensors ==" CR), LogLoc);
#endif
/* iterate through configured sensors */
for(byte i = 0; i < Max_Sensors; i++) {
if(config.system.sensor.type[i] > 0) {
/* if sensor type is internal ADC */
if(SensorIndex[config.system.sensor.type[i]].type == SENSOR_TYPE_INTADC) {
#ifdef DEBUG
Log.verbose(F("%s Sensor ID %d: %s - %s (GPIO ID %d, GPIO %d), offering" CR), LogLoc, i,config.system.sensor.name[i],
SensorIndex[config.system.sensor.type[i]].name, config.system.sensor.gpio[i][0], GPIOindex[config.system.sensor.gpio[i][0]].gpio);
#endif
/* initialize */
sensorStatus[i] = Sensor_Addr_Init_Update(config.system.sensor.type[i], config.system.sensor.gpio[i][0], SENSOR_AIU_MODE_INIT);
#ifdef DEBUG
/* when init was successful, list the sensor values */
if(sensorStatus[i] == true) {
Sensor_Log_Readings(config.system.sensor.type[i], config.system.sensor.gpio[i][0]);
}
#endif
} else if(SensorIndex[config.system.sensor.type[i]].type == SENSOR_TYPE_I2C) {
/* when SensorIndex[].type is == I2C sensor*/
/* get only the I2C Address */
byte Addr = Sensor_Addr_Init_Update(config.system.sensor.type[i], config.system.sensor.i2c_addr[i], SENSOR_AIU_MODE_ADDR);
#ifdef DEBUG
Log.verbose(F("%s Sensor ID %d: %s - %s (I2C %d, 0x%x), offering" CR), LogLoc, i,config.system.sensor.name[i],
SensorIndex[config.system.sensor.type[i]].name, config.system.sensor.i2c_addr[i], Addr);
#endif
/* initialize */
sensorStatus[i] = Sensor_Addr_Init_Update(config.system.sensor.type[i], config.system.sensor.i2c_addr[i], SENSOR_AIU_MODE_INIT);
#ifdef DEBUG
/* when init was successful, list the sensor values */
if(sensorStatus[i] == true) {
Sensor_Log_Readings(config.system.sensor.type[i], config.system.sensor.i2c_addr[i]);
}
#endif
}
}
}
}
void Sensor_Update() {
/* Update all configured sensors Values */
const static char LogLoc[] PROGMEM = "[Sensor:Update]";
#ifdef DEBUG2
unsigned long mStart = millis();
unsigned long mStop;
Log.verbose(F("%s Start %u" CR), LogLoc, mStart);
#endif
/* go through all possible existing Sensor configurations */
for(byte i = 0; i < Max_Sensors; i++) {
/* every configured one */
if(config.system.sensor.type[i] > 0) {
/* if internal ADC */
if(SensorIndex[config.system.sensor.type[i]].type == SENSOR_TYPE_INTADC) {
if(sensorStatus[i] == true) {
#ifdef DEBUG2
Log.verbose(F("%s (%d) %s: %s (%d)" CR), LogLoc, i, config.system.sensor.name[i], SensorIndex[config.system.sensor.type[i]].name, config.system.sensor.gpio[i][0]);
#endif
/* perform update of sensor values */
Sensor_Addr_Init_Update(config.system.sensor.type[i], config.system.sensor.gpio[i][0], SENSOR_AIU_MODE_UPDATE);
#ifdef DEBUG2
Sensor_Log_Readings(config.system.sensor.type[i], config.system.sensor.gpio[i][0]);
#endif
}
#ifdef DEBUG2
else {
Log.verbose(F("%s Sensor %d (%s, %d) not initialized." CR), LogLoc, i, SensorIndex[config.system.sensor.type[i]].name, Sensor_Addr_Init_Update(config.system.sensor.type[i], config.system.sensor.gpio[i][0], SENSOR_AIU_MODE_ADDR));
}
#endif
/* Everything above 1 is an I2C sensor */
} else if(SensorIndex[config.system.sensor.type[i]].type == SENSOR_TYPE_I2C) {
if(sensorStatus[i] == true) {
#ifdef DEBUG2
Log.verbose(F("%s (%d) %s: %s (%d)" CR), LogLoc, i, config.system.sensor.name[i], SensorIndex[config.system.sensor.type[i]].name, config.system.sensor.i2c_addr[i]);
#endif
/* perform update of sensor values */
Sensor_Addr_Init_Update(config.system.sensor.type[i], config.system.sensor.i2c_addr[i], SENSOR_AIU_MODE_UPDATE);
#ifdef DEBUG2
Sensor_Log_Readings(config.system.sensor.type[i], config.system.sensor.i2c_addr[i]);
#endif
}
#ifdef DEBUG2
else {
Log.verbose(F("%s Sensor %d (%s, 0x%x) not initialized." CR), LogLoc, i, SensorIndex[config.system.sensor.type[i]].name, Sensor_Addr_Init_Update(config.system.sensor.type[i], config.system.sensor.i2c_addr[i], SENSOR_AIU_MODE_ADDR));
}
#endif
}
}
}
#ifdef DEBUG2
mStop = millis();
Log.verbose(F("%s Stop %u (%u)" CR), LogLoc, mStop, mStop - mStart);
#endif
}

View file

@ -1,61 +0,0 @@
/*
*
* include/CanGrow_Timer.h - timer header file
*
*
*
*/
/*
* Timer stuff
*/
void Timer_Sensor() {
Sensor_Update();
}
void Timer_Output() {
/* Update the outputs (switching GPIOs, sending webcall, etc) */
Output_Update();
}
void Timer_Control() {
const static char LogLoc[] PROGMEM = "[Core:Timer_1s]";
#ifdef DEBUG2
Log.verbose(F("%s - trigger [Sensor:Update]" CR), LogLoc);
#endif
/* Updating Light output states in memory */
Control_Light();
/* Updating Air output states in memory */
Control_Air();
/* Updating Water output sates in memory */
Control_Water();
}
void Timer_3s() {
const static char LogLoc[] PROGMEM = "[Core:Timer_3s]";
#ifdef DEBUG2
Log.verbose(F("%s" CR), LogLoc);
#endif
}
void Timer_5s() {
const static char LogLoc[] PROGMEM = "[Core:Timer_5s]";
#ifdef DEBUG2
Log.verbose(F("%s" CR), LogLoc);
#endif
}
void TimeR_Init() {
timer.setInterval(1000, Timer_Output);
timer.setInterval(1000, Timer_Sensor);
timer.setInterval(100, Timer_Control);
}

View file

@ -1,131 +0,0 @@
/*
*
* include/CanGrow_Webserver.h - webserver header file
*
*
*
*/
/*
* include static files files
*/
#include "Webserver/File_cangrow_CSS.h"
#include "Webserver/File_cangrow_JS.h"
#include "Webserver/File_favicon_ico.h"
/*
* include webpages header files
*/
#include "Webserver/Header.h"
#include "Webserver/Footer.h"
#include "Webserver/Webserver_Common.h"
#include "Webserver/Page_404.h"
#include "Webserver/Page_root.h"
#include "Webserver/Page_wifi.h"
#include "Webserver/Page_system.h"
#include "Webserver/Page_grow.h"
/*
* include Api header files
*/
#include "Webserver/Api_sensor.h"
AsyncWebServer webserver(80);
// load requestLogger middleware
AsyncLoggingMiddleware requestLogger;
/*
* setup all the webhandlers
*/
void Webserver_Init() {
const static char LogLoc[] PROGMEM = "[Webserver]";
Log.notice(F("%s initializing" CR), LogLoc);
/* url handler definition */
webserver.on("/", HTTP_GET, WebPage_root);
webserver.on("/cangrow.css", HTTP_GET, WebFile_cangrow_CSS);
webserver.on("/cangrow.js", HTTP_GET, WebFile_cangrow_JS);
webserver.on("/favicon.ico", HTTP_GET, WebFile_favicon_ico);
webserver.on("/wifi/", HTTP_GET, WebPage_wifi);
webserver.on("/wifi/", HTTP_POST, WebPage_wifi);
webserver.on("/system/", HTTP_GET, WebPage_system);
webserver.on("/system/", HTTP_POST, WebPage_system);
webserver.on("/system/update", HTTP_GET, WebPage_system_update);
webserver.on("/system/update", HTTP_POST, WebPage_system_update, WebPage_system_update_ApplyUpdate);
webserver.on("/system/restart", HTTP_GET, WebPage_system_restart);
webserver.on("/system/restart", HTTP_POST, WebPage_system_restart);
webserver.on("/system/wipe", HTTP_GET, WebPage_system_wipe);
webserver.on("/system/wipe", HTTP_POST, WebPage_system_wipe);
webserver.on("/system/output/", HTTP_GET, WebPage_system_output);
webserver.on("/system/output/", HTTP_POST, WebPage_system_output);
webserver.on("/system/output/add", HTTP_GET, WebPage_system_output_add);
webserver.on("/system/output/add", HTTP_POST, WebPage_system_output_add);
webserver.on("/system/sensor/", HTTP_GET, WebPage_system_sensor);
webserver.on("/system/sensor/", HTTP_POST, WebPage_system_sensor);
webserver.on("/system/sensor/add", HTTP_GET, WebPage_system_sensor_add);
webserver.on("/system/sensor/add", HTTP_POST, WebPage_system_sensor_add);
webserver.on("/system/sensor/calibrate", HTTP_GET, WebPage_system_sensor_calibrate);
webserver.on("/system/sensor/calibrate", HTTP_POST, WebPage_system_sensor_calibrate);
/* grow */
webserver.on("/grow/", HTTP_GET, WebPage_grow);
webserver.on("/grow/", HTTP_POST, WebPage_grow);
webserver.on("/grow/light/", HTTP_GET, WebPage_grow_light);
webserver.on("/grow/light/", HTTP_POST, WebPage_grow_light);
webserver.on("/grow/air/", HTTP_GET, WebPage_grow_air);
webserver.on("/grow/air/", HTTP_POST, WebPage_grow_air);
webserver.on("/grow/water/", HTTP_GET, WebPage_grow_water);
webserver.on("/grow/water/", HTTP_POST, WebPage_grow_water);
webserver.on("/grow/dashboard/", HTTP_GET, WebPage_grow_dashboard);
webserver.on("/grow/dashboard/", HTTP_POST, WebPage_grow_dashboard);
/* api */
//webserver.on("/api/sensor", HTTP_GET, Api_sensor_data);
webserver.on("/api/sensor/", HTTP_GET, Api_sensor_data);
webserver.on("/api/sensor/raw", HTTP_GET, Api_sensor_data_raw);
webserver.on("/api/sensor/driver", HTTP_GET, Api_sensor_driver);
/* DEBUG only - offer config for direct download */
#ifndef DEBUG
webserver.serveStatic(CANGROW_CFG, LittleFS, CANGROW_CFG);
#endif
/* 404 Error page */
webserver.onNotFound(WebserverNotFound);
// this activates the middleware
if(config.system.httpLogSerial == true) {
requestLogger.setOutput(Serial);
Log.notice(F("%s serial logging: enabled" CR), LogLoc);
webserver.addMiddleware(&requestLogger);
} else {
Log.notice(F("%s serial logging: disabled" CR), LogLoc);
}
// Workaround, see comment at
// https://github.com/mathieucarbou/ESPAsyncWebServer/blob/main/docs/index.md#scanning-for-available-wifi-networks
// call the network scan once, so there are some values at the first call
// of the wifi settings page. otherwise the first call of the wifi scan would return
// an empty list of networks
Log.notice(F("%s call [wifi:ScanNetworks] to workaround empty scan results bug" CR), LogLoc);
WebPage_wifi_ScanNetworks();
webserver.begin();
Log.notice(F("%s Ready to serve" CR), LogLoc);
}

View file

@ -1,136 +0,0 @@
/*
*
* include/CanGrow_Wifi.h - Wifi stuff header file
*
*
*
*/
void Wifi_AP() {
const static char LogLoc[] PROGMEM = "[WiFi:AP]";
char randNr[5];
itoa(random(9999), randNr, 10);
//WiFi.softAPConfig(config.wifi.ip, config.wifi.gateway, config.wifi.netmask);
IPAddress ip(192,168,4,20);
IPAddress gateway(0,0,0,0);
IPAddress netmask(255,255,255,0);
WiFi.softAPConfig(ip, gateway, netmask);
/* when no ssid is configured, we assume here cangrow is in a fresh factory reset mode
* when a ssid is already configured, we seem not to be able to connect to it. so we protect
* our already configured cangrow controller with setting a temporary wifi ap password
* and log it to serial. */
if(strlen(config.wifi.ssid) > 0) {
const char * password = RandomString();
/* growName[64] + 8 */
char ssid[20+5];
strcpy(ssid, "CanGrow-FAILED-WIFI-");
/* random maximum 4 digit number for ssid
* https://arduino.stackexchange.com/a/42987*/
strcat(ssid, randNr);
Log.error(F("%s create access point" CR), LogLoc);
Log.error(F("%s SSID : %s" CR), LogLoc, ssid);
Log.error(F("%s Password: %s" CR), LogLoc, password);
WiFi.softAP(ssid, password);
} else {
char ssid[21+4];
strcpy(ssid, CANGROW_DEFAULT_WIFI_SSID);
//strcat(ssid, "-");
/* random maximum 4 digit number for ssid
* https://arduino.stackexchange.com/a/42987*/
//strcat(ssid, randNr);
/* start access point default password when being unconfigured */
Log.notice(F("%s create access point" CR), LogLoc);
Log.notice(F("%s SSID : %S" CR), LogLoc, ssid);
Log.notice(F("%s Password: %S" CR), LogLoc, CANGROW_DEFAULT_WIFI_PASSWORD);
WiFi.softAP(ssid, CANGROW_DEFAULT_WIFI_PASSWORD);
//WiFi.softAP(CANGROW_DEFAULT_WIFI_SSID);
}
Log.notice(F("%s access point started." CR), LogLoc);
Log.notice(F("%s IP : %s" CR), LogLoc, IP2Char(ip));
Log.notice(F("%s Netmask : %s" CR), LogLoc, IP2Char(netmask));
}
void Wifi_Connect() {
const static char LogLoc[] PROGMEM = "[WiFi:Connect]";
Log.notice(F("%s connecting to SSID: %s" CR), LogLoc, config.wifi.ssid);
WiFi.begin(config.wifi.ssid, config.wifi.password);
if(config.wifi.dhcp == false) {
Log.notice(F("%s using static ip configuration:" CR), LogLoc);
Log.notice(F("%s IP : %s" CR), LogLoc, IP2Char(config.wifi.ip));
Log.notice(F("%s Netmask: %s" CR), LogLoc, IP2Char(config.wifi.netmask));
Log.notice(F("%s Gateway: %s" CR), LogLoc, IP2Char(config.wifi.gateway));
Log.notice(F("%s DNS : %s" CR), LogLoc, IP2Char(config.wifi.dns));
WiFi.config(config.wifi.ip, config.wifi.dns, config.wifi.gateway, config.wifi.netmask);
} else {
Log.notice(F("%s using DHCP for ip configuration" CR), LogLoc);
}
Log.notice("%s ", LogLoc);
const byte max = 30;
byte count = 0;
// wait until WiFi connection is established
while (count < max) {
/* check connection stations */
if(WiFi.status() != WL_CONNECTED) {
/* if not connected, print dot and increment count */
delay(500);
Serial.print(".");
count++;
} else {
/* if connected, set count to 10 to exit loop*/
count = max+1;
}
}
/* check connection status. */
if(WiFi.status() != WL_CONNECTED) {
/* if connection failed, create AP */
Log.error(F("FAILED! Fallback to AP mode" CR), LogLoc);
WiFi.disconnect();
/*
* TODO / BUG
*
* On ESP32 there are no scan results shown in wifi tab, when connect to
* a saved network failed and the esp created then its own network.
*
* without trying to connect it works fine, like when doing a factory reset.
*
* switch mode to softAP
/* WiFi.mode(WIFI_AP_STA);
*/
Wifi_AP();
} else {
Serial.println("CONNECTED!");
if(config.wifi.dhcp == true) {
Log.notice(F("%s DHCP offered ip configuration:" CR), LogLoc);
Log.notice(F("%s IP : %s" CR), LogLoc, IP2Char(WiFi.localIP()));
Log.notice(F("%s Netmask: %s" CR), LogLoc, IP2Char(WiFi.subnetMask()));
Log.notice(F("%s Gateway: %s" CR), LogLoc, IP2Char(WiFi.gatewayIP()));
Log.notice(F("%s DNS : %s" CR), LogLoc, IP2Char(WiFi.dnsIP()));
}
}
}
void Wifi_Init() {
const static char LogLoc[] PROGMEM = "[WiFi:Init]";
Log.notice(F("%s" CR), LogLoc);
if(strlen(config.wifi.ssid) == 0) {
Log.notice(F("%s config.wifi.ssid is unset" CR), LogLoc);
Wifi_AP();
} else {
Wifi_Connect();
}
}

View file

@ -1,84 +0,0 @@
/*
*
* include/Output/Output_Common.h - Output common header file
*
*
*
*/
/*
* Output Type
*/
// How many output types exist
const byte OUTPUT_TYPE__TOTAL = 3;
const byte OUTPUT_TYPE_GPIO = 1;
const byte OUTPUT_TYPE_I2C = 2;
const byte OUTPUT_TYPE_WEB = 3;
const char OUTPUT_TYPE_GPIO_descr[] PROGMEM = {"GPIO"};
const char OUTPUT_TYPE_I2C_descr[] PROGMEM = {"I2C"};
const char OUTPUT_TYPE_WEB_descr[] PROGMEM = {"Webcall"};
const char * Output_Type_descr[] = {
NULL, // 0 - no description because 0 means unconfigured
OUTPUT_TYPE_GPIO_descr,
OUTPUT_TYPE_I2C_descr,
OUTPUT_TYPE_WEB_descr,
};
/* Output_GPIO_Addr_Init_Update() modes */
const byte OUTPUT_GPIO_IU_MODE_INIT = 0;
const byte OUTPUT_GPIO_IU_MODE_UPDATE = 1;
/* Output_Webcall_Addr_Init_Update() modes */
const byte OUTPUT_WEB_IU_MODE_INIT = 0;
const byte OUTPUT_WEB_IU_MODE_UPDATE = 1;
/*
* OutputI2C types / modules
*/
const byte OUTPUT_TYPE_I2C_MAX_PORTS = 2;
/* Total number of I2C PORT Types */
const byte OUTPUT_TYPE_I2C_PORT__TOTAL = 1;
/* port type for percentage. Those ports receive an int from 0 up to 100 to set their output value */
const byte OUTPUT_TYPE_I2C_PORT_BYTE = 1;
/* Output_I2C_Addr_Init_Update() modes */
const byte OUPUT_I2C_AIU_MODE_ADDR = 0;
const byte OUPUT_I2C_AIU_MODE_INIT = 1;
const byte OUPUT_I2C_AIU_MODE_UPDATE = 2;
/*
* Output Device
*/
// 0 is unconfigured
const byte OUTPUT_DEVICE__TOTAL = 6;
const byte OUTPUT_DEVICE_LIGHT = 1;
const byte OUTPUT_DEVICE_FAN = 2;
const byte OUTPUT_DEVICE_PUMP = 3;
const byte OUTPUT_DEVICE_HUMIDIFIER = 4;
const byte OUTPUT_DEVICE_DEHUMIDIFIER = 5;
const byte OUTPUT_DEVICE_HEATING = 6;
const char OUTPUT_DEVICE_LIGHT_descr[] PROGMEM = {"&#x1F4A1; Light"};
const char OUTPUT_DEVICE_FAN_descr[] PROGMEM = {"&#x1F300; Fan"};
const char OUTPUT_DEVICE_PUMP_descr[] PROGMEM = {"&#x1F4A7; Pump"};
const char OUTPUT_DEVICE_HUMIDIFIER_descr[] PROGMEM = {"&#x1F300; Humidifier"};
const char OUTPUT_DEVICE_DEHUMIDIFIER_descr[] PROGMEM = {"&#x1F300; Dehumidifier"};
const char OUTPUT_DEVICE_HEATING_descr[] PROGMEM = {"&#x1F300; Heating"};
const char * Output_Device_descr[] = {
NULL, // 0 - no description because 0 means unconfigured
OUTPUT_DEVICE_LIGHT_descr,
OUTPUT_DEVICE_FAN_descr,
OUTPUT_DEVICE_PUMP_descr,
OUTPUT_DEVICE_HUMIDIFIER_descr,
OUTPUT_DEVICE_DEHUMIDIFIER_descr,
OUTPUT_DEVICE_HEATING_descr,
};

View file

@ -1,45 +0,0 @@
/*
*
* include/Output/OutputI2C_01_MCP4725.h - sensor header for I2C Output MCP4725 sensor
*
*
*
*/
#include <Adafruit_MCP4725.h>
#define OUTPUT_I2C_01_NAME "MCP4725"
const byte Output_I2C_01_MCP4725_Addr[] = { 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67 };
Adafruit_MCP4725 MCP4725[sizeof(Output_I2C_01_MCP4725_Addr)];
const byte Output_I2C_01_MCP4725_Ports = 1;
void Output_I2C_01_MCP4725_Update(const byte AddrId, const byte PortId, const byte Value) {
/* Update Output Port of I2C Module */
const static char LogLoc[] PROGMEM = "[Output:I2C:01_MCP4725:Update]";
/* 'Value' , which comes from outputState[], is byte, so 0-255. So we need to map this to the MCPs 0-4095 */
MCP4725[AddrId].setVoltage(map(Value, 0, 255, 0, 4095), false);
#ifdef DEBUG
Log.verbose(F("%s 0x%x Port %d, Value %d, MCP4725_Value %d" CR), LogLoc, Output_I2C_01_MCP4725_Addr[AddrId], PortId, Value, map(Value, 0, 255, 0, 4095));
#endif
}
bool Output_I2C_01_MCP4725_Init(const byte AddrId, const byte PortId) {
/* Initialize I2C Module, return true when successful */
const static char LogLoc[] PROGMEM = "[Output:I2C:01_MCP4725:Init]";
bool returnCode;
if(MCP4725[AddrId].begin(Output_I2C_01_MCP4725_Addr[AddrId])) {
Log.notice(F("%s found at addr 0x%x" CR), LogLoc, Output_I2C_01_MCP4725_Addr[AddrId]);
Output_I2C_01_MCP4725_Update(AddrId, PortId, 50);
returnCode = true;
} else {
Log.error(F("%s FAILED! Not found at addr 0x%x" CR), LogLoc, Output_I2C_01_MCP4725_Addr[AddrId]);
returnCode = false;
}
return returnCode;
}

View file

@ -1,52 +0,0 @@
/*
*
* include/Output/OutputI2C_Output_I2C_02_GP8403.h - sensor header for I2C Output GP8403 (DFR Gravity) sensor
*
*
*
*/
#include <DFRobot_GP8XXX.h>
#define OUTPUT_I2C_02_NAME "GP8403"
const byte Output_I2C_02_GP8403_Addr[] = { 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F };
DFRobot_GP8403 GP8403[sizeof(Output_I2C_02_GP8403_Addr)];
const byte Output_I2C_02_GP8403_Ports = 2;
void Output_I2C_02_GP8403_Update(const byte AddrId, const byte PortId, const byte Value) {
/* Update Output Port of I2C Module */
const static char LogLoc[] PROGMEM = "[Output:I2C:02_GP8403:Update]";
/* 'Value' , which comes from outputState[], is byte, so 0-255. So we need to map this to the GP8403 0-4095 */
GP8403[AddrId].setDACOutVoltage(map(Value, 0, 255, 0, 4095), PortId);
#ifdef DEBUG
Log.verbose(F("%s 0x%x Port %d, Value %d, GP8403_Value %d" CR), LogLoc, Output_I2C_02_GP8403_Addr[AddrId], PortId, Value, map(Value, 0, 255, 0, 4095));
#endif
}
bool Output_I2C_02_GP8403_Init(const byte AddrId, const byte PortId) {
/* Initialize I2C Module, return true when successful */
const static char LogLoc[] PROGMEM = "[Output:I2C:02_GP8403:Init]";
bool returnCode;
/* Overwrite the default address of the library 0x58 with configured one */
GP8403[AddrId] = DFRobot_GP8403(Output_I2C_02_GP8403_Addr[AddrId]);
if(GP8403[AddrId].begin() == 0) {
/* Set output to 0-10V - this is what most grow devices are using as control standard */
GP8403[AddrId].setDACOutRange(GP8403[AddrId].eOutputRange10V);
Log.notice(F("%s found at addr 0x%x" CR), LogLoc, Output_I2C_02_GP8403_Addr[AddrId]);
Output_I2C_02_GP8403_Update(AddrId, PortId, 0);
returnCode = true;
} else {
Log.error(F("%s FAILED! Not found at addr 0x%x" CR), LogLoc, Output_I2C_02_GP8403_Addr[AddrId]);
returnCode = false;
}
return returnCode;
}

View file

@ -1,46 +0,0 @@
/*
*
* include/Sensor/00_Example.h - example sensor header I2C device
*
*
*
*/
#include <Adafruit_WhateverLib.h>
#define SENSOR_00_NAME "Example sensor"
const byte Sensor_00_Example_Addr[] = { 0x00, 0x01 };
Adafruit_WhateverLib Whatever[sizeof(Sensor_00_Example_Addr)];
/* Create main data array specifying max amount of readings */
float Sensor_00_Example[sizeof(Sensor_00_Example_Addr)][4];
void Sensor_00_Example_Update(const byte AddrId) {
/* keep the same order as in SensorIndex[].read[] !! */
Sensor_00_Example[AddrId][0] = Whatever[AddrId].temperature();
Sensor_00_Example[AddrId][1] = Whatever[AddrId].humidity();
Sensor_00_Example[AddrId][2] = Whatever[AddrId].raw1();
Sensor_00_Example[AddrId][3] = Whatever[AddrId].raw2();
}
bool Sensor_00_Example_Init(const byte AddrId) {
/* Sensor Init function
*
* returns true (1) when Init was successful
* returns false (0) if not.
*/
const static char LogLoc[] PROGMEM = "[Sensor:00_Example:Init]";
bool returnCode;
if(Whatever[AddrId].begin(Sensor_00_Example_Addr[AddrId])) {
Log.notice(F("%s found at addr 0x%x" CR), LogLoc, Sensor_00_Example_Addr[AddrId]);
Sensor_00_Example_Update(AddrId);
returnCode = true;
} else {
Log.error(F("%s FAILED! Not found at addr 0x%x" CR), LogLoc, Sensor_00_Example_Addr[AddrId]);
returnCode = false;
}
return returnCode;
}

View file

@ -1,112 +0,0 @@
/*
*
* include/Sensor/01_ADC_builtin.h - sensor header for builtin ADC
*
*
* "Driver" for the internal ADC of the ESP8266 and ESP32
*
* Bit dirty hacky workaround to support both boards ADC
*
* ESP8266 only has one ADC onboard. For "Multiplexing" I have added
* the to "add a GPIO" to it. It simply turns on the given GPIO for
* 100ms, and then turns it off.
* This is kinda a poor (wo)mans multiplexer. Control the supply voltage
* of your analog sensor with the GPIO, put a diode to the AOUT and enjoy.
*
* You can theoretically use all available pins as "multiplexer", thats why
* the Sensor_01_ADC[] array is as large as GPIOindex_length
* ************
*
* ESP32 has a bunch of ADCs onboard. So it is not needed to go this hacky way,
* we can just use all the nice ADCs available.
*
* in GPIOindex.note we get the info if the GPIO is an ADC or not
* INT_ADC and INPUT_ONLY tells us this.
* To save memory, I build an own "index" for the available
* ADCs. Thats wahat Sensor_01_ADC_ArrId() is for.
*
* It returns which Index / slot in the array the given GPIO ID
* from GPIOindex has.
*/
#define SENSOR_01_NAME "ADC builtin"
/* Create main data array specifying max amount of readings */
#ifdef ESP8266
const byte SENSOR_01_MAX = GPIOindex_length + 1;
#endif
#ifdef ESP32
/* indexing function for our ADC GPIO pins we could theoretically all use */
byte Sensor_01_ADC_ArrId(const byte GPIOid) {
const static char LogLoc[] PROGMEM = "[Sensor:01_ADC:Slot]";
byte count = 0;
//Log.verbose(F("%s GPIO %d (%d) START" CR), LogLoc, GPIOindex[GPIOid].gpio, GPIOid);
for(byte i = 1; i <= GPIOindex_length; i++) {
//Log.verbose(F("%s GPIO %d (%d) NOTE %d - %S" CR), LogLoc, GPIOindex[GPIOid].gpio, GPIOid, GPIOindex[i].note, GPIO_Index_note_descr[GPIOindex[i].note]);
if((GPIOindex[i].note == INPUT_ONLY) || (GPIOindex[i].note == INT_ADC)) {
if(i == GPIOid) {
//Log.verbose(F("%s ??? GPIO %d (%d) i %d" CR), LogLoc, GPIOindex[GPIOid].gpio, GPIOid, i);
return count;
} else {
count++;
//Log.verbose(F("%s GPIO %d (%d) i %d count" CR), LogLoc, GPIOindex[GPIOid].gpio, GPIOid, i, count);
}
}
}
return 0;
}
/* this dumb, but yeah - i counted the avail ADC manually (INT_ADC or INPUT_ONLY) */
const byte SENSOR_01_MAX = 6;
#endif
int Sensor_01_ADC[SENSOR_01_MAX];
void Sensor_01_ADC_Update(const byte GPIOid) {
const static char LogLoc[] PROGMEM = "[Sensor:01_ADC:Update]";
#ifdef ESP8266
if(GPIOid > 0) {
//digitalWrite(GPIOindex[GPIOid].gpio, HIGH);
//Log.notice(F("%s GPIO %d (%d) delay ON" CR), LogLoc, GPIOindex[GPIOid].gpio, GPIOid);
digitalWrite(GPIOindex[GPIOid].gpio, HIGH);
delay(50);
Sensor_01_ADC[GPIOid] = analogRead(A0);
digitalWrite(GPIOindex[GPIOid].gpio, LOW);
//Log.notice(F("%s GPIO %d (%d) delay OFF" CR), LogLoc, GPIOindex[GPIOid].gpio, GPIOid);
} else {
//Log.notice(F("%s GPIO %d (%d) READ" CR), LogLoc, GPIOindex[GPIOid].gpio, GPIOid);
Sensor_01_ADC[GPIOid] = analogRead(A0);
}
#endif
#ifdef ESP32
byte slot = Sensor_01_ADC_ArrId(GPIOid);
//Log.notice(F("%s GPIO %d (%d) READ - slot %d" CR), LogLoc, GPIOindex[GPIOid].gpio, GPIOid, slot);
Sensor_01_ADC[slot] = analogRead(GPIOindex[GPIOid].gpio);
//Log.notice(F("%s GPIO %d (%d) READ - slot %d Val %d" CR), LogLoc, GPIOindex[GPIOid].gpio, GPIOid, slot, Sensor_01_ADC[slot]);
#endif
}
bool Sensor_01_ADC_Init(const byte GPIOid) {
/* Sensor Init function
*
* returns true (1) when Init was successful
* returns false (0) if not.
*/
const static char LogLoc[] PROGMEM = "[Sensor:01_ADC:Init]";
#ifdef ESP8266
//Log.notice(F("%s setting GPIO ID %d (%d) as OUTPUT for internal ADC" CR), LogLoc, GPIOid, GPIOindex[GPIOid].gpio);
pinMode(GPIOindex[GPIOid].gpio, OUTPUT);
#endif
#ifdef ESP32
//Log.notice(F("%s GPIO ID %d (%d) as INPUT for internal ADC slot %d" CR), LogLoc, GPIOid, GPIOindex[GPIOid].gpio, Sensor_01_ADC_ArrId(GPIOid));
//pinMode(GPIOindex[GPIOid].gpio, INPUT);
#endif
Sensor_01_ADC_Update(GPIOid);
return true;
}

View file

@ -1,54 +0,0 @@
/*
*
* include/Sensor/00_ADC_builtin.h - sensor header for BME280 I2C sensor
*
*
*
*/
#include <Adafruit_BME280.h>
#define SENSOR_02_NAME "BME280"
//#define SENSOR_02_MAXUNITS 2
/* available addresses in byte array, default is at 0 */
const byte Sensor_02_BME280_Addr[] = { 0x76, 0x77 };
Adafruit_BME280 BME280[sizeof(Sensor_02_BME280_Addr)];
/* creation of BME280 Value Struct, as many as addresses */
//Sensor_02_BME280 Sensor_02_BME280_Data[sizeof(Sensor_02_BME280_Addr)];
/* main data array */
float Sensor_02_BME280[sizeof(Sensor_02_BME280_Addr)][4];
void Sensor_02_BME280_Update(const byte AddrId) {
/* Temp */
Sensor_02_BME280[AddrId][0] = BME280[AddrId].readTemperature();
/* Humidity */
Sensor_02_BME280[AddrId][1] = BME280[AddrId].readHumidity();
/* Pressure */
Sensor_02_BME280[AddrId][2] = BME280[AddrId].readPressure() / 1000.00;
/* Altitude */
Sensor_02_BME280[AddrId][3] = BME280[AddrId].readAltitude(SEALEVELPRESSURE_HPA);
}
bool Sensor_02_BME280_Init(const byte AddrId) {
const static char LogLoc[] PROGMEM = "[Sensor:02_BME280:Init]";
bool returnCode;
//Log.notice(F("%s Init at addr 0x%x (%d)" CR), LogLoc, Sensor_02_BME280_Addr[AddrId], AddrId);
if(BME280[AddrId].begin(Sensor_02_BME280_Addr[AddrId])) {
Log.notice(F("%s found at addr 0x%x" CR), LogLoc, Sensor_02_BME280_Addr[AddrId]);
//Log.notice(F("%s Temp: %F°C Humidity: %F % Pressure: %FhPa, Appr. Altitude %Fm" CR), LogLoc, BME280[AddrId].readTemperature(), BME280[AddrId].readHumidity(), BME280[AddrId].readPressure() / 1000.00, BME280[AddrId].readAltitude(SEALEVELPRESSURE_HPA));
Sensor_02_BME280_Update(AddrId);
returnCode = true;
} else {
Log.error(F("%s FAILED! Not found at addr 0x%x" CR), LogLoc, Sensor_02_BME280_Addr[AddrId]);
returnCode = false;
}
return returnCode;
}

View file

@ -1,98 +0,0 @@
/*
*
* include/Sensor/00_ADC_builtin.h - sensor header for BME680 I2C sensor
*
*
*
*/
#include <Adafruit_BME680.h>
#define SENSOR_03_NAME "BME680"
/* available addresses in byte array, default is at 0 */
const byte Sensor_03_BME680_Addr[] = { 0x77, 0x76 };
Adafruit_BME680 BME680[sizeof(Sensor_03_BME680_Addr)];
unsigned long BME680_endtime[sizeof(Sensor_03_BME680_Addr)];
/*struct Sensor_03_BME680 {
float humidity;
float temperature;
float pressure;
float altitude;
float gas_resistance;
};
*/
/* creation of BME680 Value Struct, as many as addresses */
/*Sensor_03_BME680 Sensor_03_BME680_Data[sizeof(Sensor_03_BME680_Addr)];*/
float Sensor_03_BME680[sizeof(Sensor_03_BME680_Addr)][5];
/* for async read of BME680 we need to trigger a new reading cycle (as adafruit doc says) */
void Sensor_03_BME680_BeginReading(const byte AddrId) {
const static char LogLoc[] PROGMEM = "[Sensor:03_BME680:BeginReading]";
#ifdef DEBUG3
Log.warning(F("%s Start reading %u , finishing %u (0x%x)" CR), LogLoc, millis(), BME680_endtime[AddrId], Sensor_03_BME680_Addr[AddrId]);
#endif
// Tell BME680 to begin measurement.
BME680_endtime[AddrId] = BME680[AddrId].beginReading();
if(BME680_endtime[AddrId] == 0) {
Log.warning(F("%s Failed to begin reading (0x%x)" CR), LogLoc, Sensor_03_BME680_Addr[AddrId]);
}
}
void Sensor_03_BME680_Update(const byte AddrId) {
const static char LogLoc[] PROGMEM = "[Sensor:03_BME680:Update]";
#ifdef DEBUG3
Log.warning(F("%s Start reading %u , finishing %u (0x%x)" CR), LogLoc, millis(), BME680_endtime[AddrId], Sensor_03_BME680_Addr[AddrId]);
#endif
if(!BME680[AddrId].endReading()) {
Log.warning(F("%s Failed to complete reading (0x%x)" CR), LogLoc, Sensor_03_BME680_Addr[AddrId]);
return;
}
Sensor_03_BME680[AddrId][0] = BME680[AddrId].readTemperature();
Sensor_03_BME680[AddrId][1] = BME680[AddrId].readHumidity();
Sensor_03_BME680[AddrId][2] = BME680[AddrId].readPressure() / 1000;
Sensor_03_BME680[AddrId][3] = BME680[AddrId].readAltitude(SEALEVELPRESSURE_HPA);
Sensor_03_BME680[AddrId][4] = BME680[AddrId].gas_resistance / 1000.0;
/* begin new reading cycle */
Sensor_03_BME680_BeginReading(AddrId);
}
bool Sensor_03_BME680_Init(const byte AddrId) {
const static char LogLoc[] PROGMEM = "[Sensor:03_BME680:Init]";
bool returnCode;
//Log.notice(F("%s Init at addr 0x%x (%d)" CR), LogLoc, Sensor_03_BME680_Addr[AddrId], AddrId);
if(BME680[AddrId].begin(Sensor_03_BME680_Addr[AddrId])) {
Log.notice(F("%s found at addr 0x%x" CR), LogLoc, Sensor_03_BME680_Addr[AddrId]);
// Set up oversampling and filter initialization
BME680[AddrId].setTemperatureOversampling(BME680_OS_8X);
BME680[AddrId].setHumidityOversampling(BME680_OS_2X);
BME680[AddrId].setPressureOversampling(BME680_OS_4X);
BME680[AddrId].setIIRFilterSize(BME680_FILTER_SIZE_3);
BME680[AddrId].setGasHeater(320, 150); // 320*C for 150 ms
/* start to do readings here, like shown in async example
* https://github.com/adafruit/Adafruit_BME680/blob/master/examples/bme680async/bme680async.ino */
Sensor_03_BME680_BeginReading(AddrId);
Sensor_03_BME680_Update(AddrId);
returnCode = true;
} else {
Log.error(F("%s FAILED! Not found at addr 0x%x" CR), LogLoc, Sensor_03_BME680_Addr[AddrId]);
returnCode = false;
}
return returnCode;
}

View file

@ -1,43 +0,0 @@
/*
*
* include/Sensor/04_SHT3X.h - SHT3X I2C temp/humidity sensor
*
*
*
*/
#include <Adafruit_SHT31.h>
#define SENSOR_04_NAME "SHT3x"
const byte Sensor_04_SHT3X_Addr[] = { 0x44, 0x45 };
Adafruit_SHT31 SHT3X[sizeof(Sensor_04_SHT3X_Addr)];
/* Create main data array specifying max amount of readings */
float Sensor_04_SHT3X[sizeof(Sensor_04_SHT3X_Addr)][2];
void Sensor_04_SHT3X_Update(const byte AddrId) {
Sensor_04_SHT3X[AddrId][0] = SHT3X[AddrId].readTemperature();
Sensor_04_SHT3X[AddrId][1] = SHT3X[AddrId].readHumidity();
}
bool Sensor_04_SHT3X_Init(const byte AddrId) {
/* Sensor Init function
*
* returns true (1) when Init was successful
* returns false (0) if not.
*/
const static char LogLoc[] PROGMEM = "[Sensor:04_SHT3X:Init]";
bool returnCode;
if(SHT3X[AddrId].begin(Sensor_04_SHT3X_Addr[AddrId])) {
Log.notice(F("%s found at addr 0x%x" CR), LogLoc, Sensor_04_SHT3X_Addr[AddrId]);
Sensor_04_SHT3X_Update(AddrId);
returnCode = true;
} else {
Log.error(F("%s FAILED! Not found at addr 0x%x" CR), LogLoc, Sensor_04_SHT3X_Addr[AddrId]);
returnCode = false;
}
return returnCode;
}

View file

@ -1,44 +0,0 @@
/*
*
* include/Sensor/05_MLX90614.h - MLX90614 I2C IR temp sensor
*
*
*
*/
#include <Adafruit_MLX90614.h>
#define SENSOR_05_NAME "MLX90614"
const byte Sensor_05_MLX90614_Addr[] = { 0x5A, 0x5B, 0x5C, 0x5D };
Adafruit_MLX90614 MLX90614[sizeof(Sensor_05_MLX90614_Addr)];
/* Create main data array specifying max amount of readings */
float Sensor_05_MLX90614[sizeof(Sensor_05_MLX90614_Addr)][2];
void Sensor_05_MLX90614_Update(const byte AddrId) {
/* keep the same order as in SensorIndex[].read[] !! */
Sensor_05_MLX90614[AddrId][0] = MLX90614[AddrId].readAmbientTempC();
Sensor_05_MLX90614[AddrId][1] = MLX90614[AddrId].readObjectTempC();
}
bool Sensor_05_MLX90614_Init(const byte AddrId) {
/* Sensor Init function
*
* returns true (1) when Init was successful
* returns false (0) if not.
*/
const static char LogLoc[] PROGMEM = "[Sensor:05_MLX90614:Init]";
bool returnCode;
if(MLX90614[AddrId].begin(Sensor_05_MLX90614_Addr[AddrId])) {
Log.notice(F("%s found at addr 0x%x - emissivity set to %F" CR), LogLoc, Sensor_05_MLX90614_Addr[AddrId], MLX90614[AddrId].readEmissivity());
Sensor_05_MLX90614_Update(AddrId);
returnCode = true;
} else {
Log.error(F("%s FAILED! Not found at addr 0x%x" CR), LogLoc, Sensor_05_MLX90614_Addr[AddrId]);
returnCode = false;
}
return returnCode;
}

View file

@ -1,72 +0,0 @@
/*
*
* include/Sensor/06_TCS34725.h - header for I2C color sensor TCS34725
*
*
*
*/
//#include "TCS34725.h"
#include "Adafruit_TCS34725.h"
#define SENSOR_06_NAME "TCS34725"
const byte Sensor_06_TCS34725_Addr[] = { 0x29 };
Adafruit_TCS34725 TCS34725[sizeof(Sensor_06_TCS34725_Addr)];
/* This library causes a 240ms (or greater if chosen) delay when reading the values from the sensor
* this is not optimal, and there are libs workarounding this behaviour.
* But unfortunatelly the other libs wont connect successful by i2c to the sensor,
* which only the adafruit lib does reliably 240MS integration time and 4x gain
* seems to be the sweet spot between delay and value resolutin */
/* Create main data array specifying max amount of readings */
float Sensor_06_TCS34725[sizeof(Sensor_06_TCS34725_Addr)][5];
void Sensor_06_TCS34725_Update(const byte AddrId) {
uint16_t r, g, b, c, colorTemp, lux;
TCS34725[AddrId].getRawData(&r, &g, &b, &c);
colorTemp = TCS34725[AddrId].calculateColorTemperature_dn40(r, g, b, c);
lux = TCS34725[AddrId].calculateLux(r, g, b);
Sensor_06_TCS34725[AddrId][0] = colorTemp;
Sensor_06_TCS34725[AddrId][1] = lux;
Sensor_06_TCS34725[AddrId][2] = r;
Sensor_06_TCS34725[AddrId][3] = g;
Sensor_06_TCS34725[AddrId][4] = b;
}
bool Sensor_06_TCS34725_Init(const byte AddrId) {
/* Sensor Init function
*
* returns true (1) when Init was successful
* returns false (0) if not.
*/
const static char LogLoc[] PROGMEM = "[Sensor:06_TCS34725:Init]";
bool returnCode;
if(TCS34725[AddrId].begin()) {
/* Here I hardcoded here the values for Integration time and Gain.
* For calibration I used my desk lamp and a lux smartphone app.
* I fooled around until the smartphone app reading was kinda the
* same as the TCS34725 ones. Yay!
*
* Comes out TCS34725_INTEGRATIONTIME_240MS and TCS34725_GAIN_16X
* seem to be good values. Smartphone reading of my desk lamp is
* 3507lx and on the exakt same spot, height, angle and so on the
* TCS34725 measures 3487lx. I guess this is fine. */
Log.notice(F("%s found at addr 0x%x - Integration time: 240ms Gain: 16x" CR), LogLoc, Sensor_06_TCS34725_Addr[AddrId]);
TCS34725[AddrId].setIntegrationTime(TCS34725_INTEGRATIONTIME_240MS);
TCS34725[AddrId].setGain(TCS34725_GAIN_16X);
Sensor_06_TCS34725_Update(AddrId);
returnCode = true;
} else {
Log.error(F("%s FAILED! Not found at addr 0x%x" CR), LogLoc, Sensor_06_TCS34725_Addr[AddrId]);
returnCode = false;
}
return returnCode;
}

View file

@ -1,38 +0,0 @@
/*
*
* include/Sensor/07_ADS1115.h - ADS1115 16 bit ADC I2C driver
*
*
*
*/
#include <Adafruit_ADS1X15.h>
#define SENSOR_07_NAME "ADS1115"
const byte Sensor_07_ADS1115_Addr[] = { 0x48, 0x49, 0x4A, 0x4B };
Adafruit_ADS1115 ADS1115[sizeof(Sensor_07_ADS1115_Addr)];
int Sensor_07_ADS1115[sizeof(Sensor_07_ADS1115_Addr)][4];
void Sensor_07_ADS1115_Update(const byte AddrId) {
for(byte i = 0; i < 4; i++) {
Sensor_07_ADS1115[AddrId][i] = ADS1115[AddrId].readADC_SingleEnded(i);
}
}
bool Sensor_07_ADS1115_Init(const byte AddrId) {
const static char LogLoc[] PROGMEM = "[Sensor:07_ADS1115:Init]";
bool returnCode;
if(ADS1115[AddrId].begin(Sensor_07_ADS1115_Addr[AddrId])) {
Log.notice(F("%s found at addr 0x%x" CR), LogLoc, Sensor_07_ADS1115_Addr[AddrId]);
Sensor_07_ADS1115_Update(AddrId);
returnCode = true;
} else {
Log.error(F("%s FAILED! Not found at addr 0x%x" CR), LogLoc, Sensor_07_ADS1115_Addr[AddrId]);
returnCode = false;
}
return returnCode;
}

View file

@ -1,41 +0,0 @@
/*
*
* include/Sensor/08_ADS1015.h - ADS1115 16 bit ADC I2C driver
*
*
*
*/
/*
* #include <Adafruit_ADS1X15.h>
* This already got included in Sensor_07_ADS1115.h
*/
#define SENSOR_08_NAME "ADS1015"
const byte Sensor_08_ADS1015_Addr[] = { 0x48, 0x49, 0x4A, 0x4B };
Adafruit_ADS1015 ADS1015[sizeof(Sensor_08_ADS1015_Addr)];
int Sensor_08_ADS1015[sizeof(Sensor_08_ADS1015_Addr)][4];
void Sensor_08_ADS1015_Update(const byte AddrId) {
for(byte i = 0; i < 4; i++) {
Sensor_08_ADS1015[AddrId][i] = ADS1015[AddrId].readADC_SingleEnded(i);
}
}
bool Sensor_08_ADS1015_Init(const byte AddrId) {
const static char LogLoc[] PROGMEM = "[Sensor:08_ADS1015:Init]";
bool returnCode;
if(ADS1015[AddrId].begin(Sensor_08_ADS1015_Addr[AddrId])) {
Log.notice(F("%s found at addr 0x%x" CR), LogLoc, Sensor_08_ADS1015_Addr[AddrId]);
Sensor_08_ADS1015_Update(AddrId);
returnCode = true;
} else {
Log.error(F("%s FAILED! Not found at addr 0x%x" CR), LogLoc, Sensor_08_ADS1015_Addr[AddrId]);
returnCode = false;
}
return returnCode;
}

View file

@ -1,88 +0,0 @@
/*
*
* include/Sensor/09_Chirp.h - example sensor header I2C device
*
*
*
*/
#include <I2CSoilMoistureSensor.h>
#define SENSOR_09_NAME "I2C-Chirp"
const byte Sensor_09_Chirp_Addr[] = { 0x20, 0x21, 0x22, 0x23 };
I2CSoilMoistureSensor Chirp[sizeof(Sensor_09_Chirp_Addr)];
/* Create main data array specifying max amount of readings */
float Sensor_09_Chirp[sizeof(Sensor_09_Chirp_Addr)][3];
void Sensor_09_Chirp_Update(const byte AddrId) {
const static char LogLoc[] PROGMEM = "[Sensor:09_Chirp:Update]";
#ifdef DEBUG
unsigned long mStart = millis();
unsigned long mStop;
Log.verbose(F("%s Start %u" CR), LogLoc, mStart);
#endif
/* keep the same order as in SensorIndex[].read[] !! */
#ifdef DEBUG
Log.verbose(F("%s capacitance (%u)" CR), LogLoc, millis());
#endif
Sensor_09_Chirp[AddrId][0] = Chirp[AddrId].getCapacitance();
#ifdef DEBUG
Log.verbose(F("%s temperature (%u)" CR), LogLoc, millis());
#endif
Sensor_09_Chirp[AddrId][1] = Chirp[AddrId].getTemperature()/(float)10;
/* light sensor is disabled, because it takes 3s to read, which is just too much */
//#ifndef DEBUG
//Log.verbose(F("%s light (%u)" CR), LogLoc, millis());
//#endif
//Sensor_09_Chirp[AddrId][2] = Chirp[AddrId].getLight(true);
Chirp[AddrId].sleep();
#ifdef DEBUG
mStop = millis();
Log.verbose(F("%s Stop %u (%u)" CR), LogLoc, mStop, mStop - mStart);
#endif
}
bool Sensor_09_Chirp_Init(const byte AddrId) {
/* Sensor Init function
*
* returns true (1) when Init was successful
* returns false (0) if not.
*/
const static char LogLoc[] PROGMEM = "[Sensor:09_Chirp:Init]";
bool returnCode;
/* manually check if I2C address answers on bus, i2c chirp lib does not return a value */
Wire.beginTransmission(Sensor_09_Chirp_Addr[AddrId]);
short i2cError = Wire.endTransmission();
/* when i2c sensor answered to our previous init request */
if(i2cError == 0) {
Log.notice(F("%s found at addr 0x%x" CR), LogLoc, Sensor_09_Chirp_Addr[AddrId]);
#ifdef ESP8266
/* maybe its not the best idea to place it here, but for the moment.. */
Wire.setClockStretchLimit(2500);
#endif
/* change chirp library I2C address, it will also trigger .begin() afterwards */
Chirp[AddrId].changeSensor(Sensor_09_Chirp_Addr[AddrId], false);
Sensor_09_Chirp_Update(AddrId);
returnCode = true;
} else {
Log.error(F("%s FAILED! Not found at addr 0x%x" CR), LogLoc, Sensor_09_Chirp_Addr[AddrId]);
returnCode = false;
}
return returnCode;
}

View file

@ -1,58 +0,0 @@
/*
*
* include/Sensor/10_CCS811_.h - CCS811 CO2 I2C sensor
*
*
*
*/
#include "Adafruit_CCS811.h"
#define SENSOR_10_NAME "CCS811"
const byte Sensor_10_CCS811_Addr[] = { 0x5a, 0x5b };
Adafruit_CCS811 CCS811[sizeof(Sensor_10_CCS811_Addr)];
/* Create main data array specifying max amount of readings */
float Sensor_10_CCS811[sizeof(Sensor_10_CCS811_Addr)][4];
void Sensor_10_CCS811_Update(const byte AddrId) {
const static char LogLoc[] PROGMEM = "[Sensor:10_CCS811:Update]";
if(CCS811[AddrId].available()){
if(!CCS811[AddrId].readData()){
/* keep the same order as in SensorIndex[].read[] !! */
/* CO2 in ppm */
Sensor_10_CCS811[AddrId][0] = CCS811[AddrId].geteCO2();
/* TVOC (Total Volatile Organic Compouds) */
Sensor_10_CCS811[AddrId][1] = CCS811[AddrId].getTVOC();
}
#ifndef DEBUG
else {
Log.error(F("%s 0x%x ERROR getting new data" CR), LogLoc, Sensor_10_CCS811_Addr[AddrId]);
}
#endif
}
}
bool Sensor_10_CCS811_Init(const byte AddrId) {
/* Sensor Init function
*
* returns true (1) when Init was successful
* returns false (0) if not.
*/
const static char LogLoc[] PROGMEM = "[Sensor:10_CCS811:Init]";
bool returnCode;
if(CCS811[AddrId].begin(Sensor_10_CCS811_Addr[AddrId])) {
Log.notice(F("%s found at addr 0x%x" CR), LogLoc, Sensor_10_CCS811_Addr[AddrId]);
Sensor_10_CCS811_Update(AddrId);
returnCode = true;
} else {
Log.error(F("%s FAILED! Not found at addr 0x%x" CR), LogLoc, Sensor_10_CCS811_Addr[AddrId]);
returnCode = false;
}
return returnCode;
}

View file

@ -1,152 +0,0 @@
/*
*
* include/Sensor/Common.h - common sensor header file
*
*
*
*/
/*
* Common used consts and variables, used within the Sensor header for example
*/
// for bme280 and bme680
#define SEALEVELPRESSURE_HPA (1013.25)
/* sensor types, int ADC, i2c, one wire , two wire, ...*/
const byte SENSOR_TYPE__TOTAL = 5;
const byte SENSOR_TYPE_INTADC = 0;
const byte SENSOR_TYPE_I2C = 1;
const byte SENSOR_TYPE_ONEWIRE = 2;
const byte SENSOR_TYPE_TWOWIRE = 3;
const byte SENSOR_TYPE_I2C_WITH_GPIO = 4;
/* How many different read types exists */
const byte SENSOR_READ_TYPE__TOTAL = 14;
const byte SENSOR_READ_TYPE_RAW = 1;
const char SENSOR_READ_TYPE_RAW_descr[] PROGMEM = {"Raw value"};
const char SENSOR_READ_TYPE_RAW_unit[] PROGMEM = {""};
const byte SENSOR_READ_TYPE_TEMP = 2;
const char SENSOR_READ_TYPE_TEMP_descr[] PROGMEM = {"Temperature"};
const char SENSOR_READ_TYPE_TEMP_unit[] PROGMEM = {"°C"};
const byte SENSOR_READ_TYPE_HUMIDITY = 3;
const char SENSOR_READ_TYPE_HUMIDITY_descr[] PROGMEM = {"Humidity"};
const char SENSOR_READ_TYPE_HUMIDITY_unit[] PROGMEM = {"%"};
const byte SENSOR_READ_TYPE_SOILMOISTURE = 4;
const char SENSOR_READ_TYPE_SOILMOISTURE_descr[] PROGMEM = {"Moisture"};
const char SENSOR_READ_TYPE_SOILMOISTURE_unit[] PROGMEM = {"%"};
const byte SENSOR_READ_TYPE_PRESSURE = 5;
const char SENSOR_READ_TYPE_PRESSURE_descr[] PROGMEM = {"Pressure"};
const char SENSOR_READ_TYPE_PRESSURE_unit[] PROGMEM = {"Pa"};
const byte SENSOR_READ_TYPE_ALTITUDE = 6;
const char SENSOR_READ_TYPE_ALTITUDE_descr[] PROGMEM = {"Altitude"};
const char SENSOR_READ_TYPE_ALTITUDE_unit[] PROGMEM = {"m"};
const byte SENSOR_READ_TYPE_GAS_RESISTANCE = 7;
const char SENSOR_READ_TYPE_GAS_RESISTANCE_descr[] PROGMEM = {"Gas resistance"};
const char SENSOR_READ_TYPE_GAS_RESISTANCE_unit[] PROGMEM = {"KOhm"};
const byte SENSOR_READ_TYPE_COLOR_TEMP = 8;
const char SENSOR_READ_TYPE_COLOR_TEMP_descr[] PROGMEM = {"Color temperature"};
const char SENSOR_READ_TYPE_COLOR_TEMP_unit[] PROGMEM = {"K"};
const byte SENSOR_READ_TYPE_LUX = 9;
const char SENSOR_READ_TYPE_LUX_descr[] PROGMEM = {"Lux"};
const char SENSOR_READ_TYPE_LUX_unit[] PROGMEM = {"lx"};
const byte SENSOR_READ_TYPE_COLOR_RED = 10;
const char SENSOR_READ_TYPE_COLOR_RED_descr[] PROGMEM = {"Color red"};
const char SENSOR_READ_TYPE_COLOR_RED_unit[] PROGMEM = {""};
const byte SENSOR_READ_TYPE_COLOR_GREEN = 11;
const char SENSOR_READ_TYPE_COLOR_GREEN_descr[] PROGMEM = {"Color green"};
const char SENSOR_READ_TYPE_COLOR_GREEN_unit[] PROGMEM = {""};
const byte SENSOR_READ_TYPE_COLOR_BLUE = 12;
const char SENSOR_READ_TYPE_COLOR_BLUE_descr[] PROGMEM = {"Color blue"};
const char SENSOR_READ_TYPE_COLOR_BLUE_unit[] PROGMEM = {""};
const byte SENSOR_READ_TYPE_PARTS_PER_MILLION = 13;
const char SENSOR_READ_TYPE_PARTS_PER_MILLION_descr[] PROGMEM = {"Part per million"};
const char SENSOR_READ_TYPE_PARTS_PER_MILLION_unit[] PROGMEM = {"ppm"};
const byte SENSOR_READ_TYPE_TVOC = 14;
const char SENSOR_READ_TYPE_TVOC_descr[] PROGMEM = {"TVOC"};
const char SENSOR_READ_TYPE_TVOC_unit[] PROGMEM = {""};
const char * Sensor_Read_descr[] = {
NULL, // 0 is unset
SENSOR_READ_TYPE_RAW_descr,
SENSOR_READ_TYPE_TEMP_descr,
SENSOR_READ_TYPE_HUMIDITY_descr,
SENSOR_READ_TYPE_SOILMOISTURE_descr,
SENSOR_READ_TYPE_PRESSURE_descr,
SENSOR_READ_TYPE_ALTITUDE_descr,
SENSOR_READ_TYPE_GAS_RESISTANCE_descr,
SENSOR_READ_TYPE_COLOR_TEMP_descr,
SENSOR_READ_TYPE_LUX_descr,
SENSOR_READ_TYPE_COLOR_RED_descr,
SENSOR_READ_TYPE_COLOR_GREEN_descr,
SENSOR_READ_TYPE_COLOR_BLUE_descr,
SENSOR_READ_TYPE_PARTS_PER_MILLION_descr,
SENSOR_READ_TYPE_TVOC_descr
};
const char * Sensor_Read_unit[] = {
NULL, // 0 is unset
SENSOR_READ_TYPE_RAW_unit,
SENSOR_READ_TYPE_TEMP_unit,
SENSOR_READ_TYPE_HUMIDITY_unit,
SENSOR_READ_TYPE_SOILMOISTURE_unit,
SENSOR_READ_TYPE_PRESSURE_unit,
SENSOR_READ_TYPE_ALTITUDE_unit,
SENSOR_READ_TYPE_GAS_RESISTANCE_unit,
SENSOR_READ_TYPE_COLOR_TEMP_unit,
SENSOR_READ_TYPE_LUX_unit,
SENSOR_READ_TYPE_COLOR_RED_unit,
SENSOR_READ_TYPE_COLOR_GREEN_unit,
SENSOR_READ_TYPE_COLOR_BLUE_unit,
SENSOR_READ_TYPE_PARTS_PER_MILLION_unit,
SENSOR_READ_TYPE_TVOC_unit
};
/* How many different read convert types exists */
const byte SENSOR_CONVERT_RAW_TYPE__TOTAL = 1;
const byte SENSOR_CONVERT_RAW_TYPE_SOILMOISTURE = 1;
const char SENSOR_CONVERT_RAW_TYPE_SOILMOISTURE_descr[] PROGMEM = {"Soilmoisture"};
const char SENSOR_CONVERT_RAW_TYPE_SOILMOISTURE_unit[] PROGMEM = {"%"};
//const byte SENSOR_CONVERT_RAW_TYPE_OTHER = 2;
//const char SENSOR_CONVERT_RAW_TYPE_OTHER_descr[] PROGMEM = {"Other"};
//const char SENSOR_CONVERT_RAW_TYPE_OTHER_unit[] PROGMEM = {"n/a"};
const char * Sensor_Convert_Raw_descr[] = {
NULL, // 0 is unset
SENSOR_CONVERT_RAW_TYPE_SOILMOISTURE_descr,
//SENSOR_CONVERT_RAW_TYPE_OTHER_descr
};
const char * Sensor_Convert_Raw_unit[] = {
NULL, // 0 is unset
SENSOR_CONVERT_RAW_TYPE_SOILMOISTURE_unit,
//SENSOR_CONVERT_RAW_TYPE_OTHER_unit
};
// Addr_Init_Update modes
const byte SENSOR_AIU_MODE_ADDR = 0;
const byte SENSOR_AIU_MODE_INIT = 1;
const byte SENSOR_AIU_MODE_UPDATE = 2;

View file

@ -1,124 +0,0 @@
/*
*
* include/Webserver/Api_Sensor.h - Sensor API header file
*
*
*
*/
void Api_sensor_data(AsyncWebServerRequest* request) {
AsyncJsonResponse* response = new AsyncJsonResponse();
JsonObject root = response->getRoot().to<JsonObject>();
//root["hello"] = "world";
for(byte i = 0 ; i < Max_Sensors ; i++) {
if(config.system.sensor.type[i] > 0) {
JsonObject objSensor = root["sensor"].add<JsonObject>();
objSensor["id"] = i;
objSensor["name"] = config.system.sensor.name[i];
objSensor["type"] = SensorIndex[config.system.sensor.type[i]].name;
objSensor["status"] = sensorStatus[i];
if(SensorIndex[config.system.sensor.type[i]].type == SENSOR_TYPE_I2C)
objSensor["i2c_addr"] = "0x" + String(Sensor_Addr_Init_Update(config.system.sensor.type[i], config.system.sensor.i2c_addr[i], SENSOR_AIU_MODE_ADDR), HEX);
if(SensorIndex[config.system.sensor.type[i]].type == SENSOR_TYPE_INTADC)
objSensor["gpio"] = GPIOindex[config.system.sensor.gpio[i][0]].gpio;
for(byte j = 0; j < Max_Sensors_Read; j++) {
if(SensorIndex[config.system.sensor.type[i]].read[j] > 0) {
JsonObject objReading = objSensor["reading"].add<JsonObject>();
/* when for a RAW reading rawConvert is set, return the converted description and unit */
if((SensorIndex[config.system.sensor.type[i]].read[j] == SENSOR_READ_TYPE_RAW) && (config.system.sensor.rawConvert[i][j] > 0)) {
objReading["descr"] = FPSTR(Sensor_Convert_Raw_descr[config.system.sensor.rawConvert[i][j]]);
objReading["unit"] = FPSTR(Sensor_Convert_Raw_unit[config.system.sensor.rawConvert[i][j]]);
} else {
objReading["descr"] = FPSTR(Sensor_Read_descr[SensorIndex[config.system.sensor.type[i]].read[j]]);
objReading["unit"] = FPSTR(Sensor_Read_unit[SensorIndex[config.system.sensor.type[i]].read[j]]);
}
/* read RAW values
when internal ADC */
if(SensorIndex[config.system.sensor.type[i]].type == SENSOR_TYPE_INTADC) {
objReading["raw"] = Sensor_getValue( config.system.sensor.type[i], config.system.sensor.gpio[i][0]);
} else if(SensorIndex[config.system.sensor.type[i]].type == SENSOR_TYPE_I2C) {
objReading["raw"] = Sensor_getValue( config.system.sensor.type[i], config.system.sensor.i2c_addr[i], j);
}
objReading["value"] = Sensor_getCalibratedValue(i, j);
}
}
}
}
response->setLength();
request->send(response);
}
void Api_sensor_data_raw(AsyncWebServerRequest* request) {
/* Api_sensor_data_raw returns the raw reading value of a specific reading of a sensor
* you can call it with GET http://<IP>/api/sensor/raw?sensor=1&reading=2*/
AsyncJsonResponse* response = new AsyncJsonResponse();
JsonObject root = response->getRoot().to<JsonObject>();
//root["hello"] = "world";
if((request->hasParam("sensor")) && (request->hasParam("reading"))) {
const AsyncWebParameter* paramSensor = request->getParam("sensor");
byte sensorId = paramSensor->value().toInt();
const AsyncWebParameter* paramReading = request->getParam("reading");
byte readingId = paramReading->value().toInt();
root["sensorId"] = sensorId;
root["readingId"] = readingId;
/* when reading is RAW */
if(SensorIndex[config.system.sensor.type[sensorId]].read[readingId] == SENSOR_READ_TYPE_RAW) {
/* when internal ADC */
if(SensorIndex[config.system.sensor.type[sensorId]].type == SENSOR_TYPE_INTADC) {
root["value"] = Sensor_getValue( config.system.sensor.type[sensorId], config.system.sensor.gpio[sensorId][0]);
} else if(SensorIndex[config.system.sensor.type[sensorId]].type == SENSOR_TYPE_I2C) {
root["value"] = Sensor_getValue( config.system.sensor.type[sensorId], config.system.sensor.i2c_addr[sensorId], readingId);
}
} else {
root["msg"] = String(F("not a RAW reading"));
}
} else {
root["msg"] = String(F("sensor or reading not given"));
}
response->setLength();
request->send(response);
}
void Api_sensor_driver(AsyncWebServerRequest* request) {
/* Api_sensor_data_raw returns the raw reading value of a specific reading of a sensor
* you can call it with GET http://<IP>/api/sensor/raw?sensor=1&reading=2*/
AsyncJsonResponse* response = new AsyncJsonResponse();
JsonObject root = response->getRoot().to<JsonObject>();
//root["hello"] = "world";
root["drivers"] = SensorIndex_length;
root["maxReadings"] = Max_Sensors_Read;
/* empty driver because 0 is unconfigured */
JsonObject objSensor = root["sensor"].add<JsonObject>();
for(byte i = 1; i <= SensorIndex_length; i++) {
//Log.verbose(F("%s Sensor_Index %d, Name %s, Readings" CR), LogLoc, i, SensorIndex[i].name );
JsonObject objSensor = root["sensor"].add<JsonObject>();
objSensor["index"] = i;
objSensor["name"] = FPSTR(SensorIndex[i].name);
for(byte j = 0; j < Max_Sensors_Read; j++) {
if(SensorIndex[i].read[j] > 0 ) {
//Log.verbose(F("%s %d: %s (%d %d)" CR), LogLoc, j, Sensor_Read_descr[SensorIndex[i].read[j]], SensorIndex[i].read[j], Sensor_Read_unit[SensorIndex[i].read[j]], SensorIndex[i].read[j]);
JsonObject objReading = objSensor["reading"].add<JsonObject>();
objReading["index"] = j;
objReading["descr"] = FPSTR(Sensor_Read_descr[SensorIndex[i].read[j]]);
objReading["unit"] = FPSTR(Sensor_Read_unit[SensorIndex[i].read[j]]);
}
}
}
response->setLength();
request->send(response);
}

View file

@ -1,257 +0,0 @@
/*
*
* include/Webserver/File_cangrow_CSS.h - /cangrow.css header file
*
*
*
*/
const char File_cangrow_CSS[] PROGMEM = R"(body {
color: #cae0d0;
background-color: #1d211e;
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
text-align: center;
}
.footer {
color: #343B35;
}
.center {
/*width: 100; */
margin: auto;
}
.centered {
margin-left: auto;
margin-right: auto;
}
h1 {
margin: 15px;
}
h2 {
margin: 10px;
}
h3 {
margin: 5px;
}
td {
text-align: left;
vertical-align: middle;
border-bottom: 1px solid #262B27;
}
hr {
height: 1px;
border-width: 0;
color: #262B27;
background-color: #262B27;
margin-top: 0.5em;
margin-bottom: 0.5em;
margin-left: auto;
margin-right: auto;
border-style: inset;
width: 320px;
}
a:link, a:visited {
color: #04AA6D;
}
a:hover {
color: #64AA6D;
}
a:active {
color: #04AA6D;
}
.infomsg , .warnmsg {
color: #fff;
border-radius: 3px;
padding: 4px;
/*width: fit-content; min-width: 200px; max-width: 420px;*/
display: inline-block;
margin: auto;
margin-bottom: 5px;
font-weight: bold;
/*text-align: center;*/
text-decoration: none;
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.5);
}
.infomsg {
background: #04AA6D;
}
.warnmsg {
background: #aa4204;
}
.inputShort {
width: 42px;
}
.sensorReading {
font-style: italic;
color: #64AA6D;
}
.helpbox {
font-size: 0.8em;
margin-left: 15px;
margin-top: 5px;
margin-bottom: 5px;
}
.nav {
background: #333;
/*width: 100; */
margin: auto;
margin-bottom: 10px;
padding: 0;
position: relative;
border-radius: 3px;
display: inline-block;
text-align: left;
}
.subnav {
/*text-align: center;*/
margin: auto;
margin-bottom: 10px;
padding: 0;
position: relative;
border-radius: 3px;
}
.subnavTitle {
font-size: 1em;
/*font-weight: bold;*/
margin-top: -10px;
margin-bottom: 10px;
/*text-align: center;*/
}
.nav li {
display: inline-block;
list-style: none;
border-radius: 3px;
}
.subnav li {
background: #262B27;
list-style: none;
border-radius: 3px;
margin-bottom: 3px;
display: inline-block;
}
.nav li:first-of-type {
background: #026b45;
border-top-left-radius: 3px;
border-bottom-left-radius: 3px;
}
.nav li a, .nav span, .subnav li a, .subnav span, .button, .button:link, input[type=button], input[type=submit],
input[type=reset], .linkForm input[type=submit] {
color: #ddd;
display: block;
font-family: 'Lucida Sans Unicode', 'Lucida Grande', sans-serif;
font-size:0.8em;
padding: 10px 20px;
text-decoration: none;
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.5);
}
.subnav li a, .subnav span {
padding: 5px 10px;
}
.nav li a:hover, .subnav li a:hover, .activeNav, .button:link:hover, .button:visited:hover, input[type=button]:hover,
input[type=submit]:hover, input[type=reset]:hover, .linkForm input[type=submit]:hover {
background: #04AA6D;
color: #fff;
border-radius: 3px;
}
.nav li a:active, .subnav li a:active {
background: #026b45;
color: #cae0d0;
}
.activeNav {
background: #444;
}
.navTime {
background: #292929;
}
.button, .button:link, .button:visited, input[type=button], input[type=submit],input[type=reset],
.linkForm input[type=submit] {
background: #026b45;
color: #fff;
border-radius: 3px;
padding: 6px 12px;
/*text-align: center;*/
text-decoration: none;
display: inline-block;
border: none;
}
.button:link:active, .button:visited:active, input[type=button]:active, input[type=submit]:active,
input[type=reset]:active, .linkForm input[type=submit]:active {
background: #026b45;
color: #cae0d0;
}
input[type=text], input[type=date], input[type=number], input[type=password], select {
background: #cae0d0;
color: #1d211e;
border: 1px solid #026b45;
border-radius: 3px;
}
.linkForm {
display: inline-block;
}
.linkForm input[type=submit] {
background: #262B27;
padding: 5px;
}
.hidden {
display: none;
}
.force_hide {
display: none !important;
}
.visible {
display: inline;
/*justify-content: center!important;*/
}
/* a disabled class */
a.disabled {
pointer-events: none;
}
@media only screen and (min-width: 1820px) {
/*.center, .nav {
width: 60; min-width: 420px;
}*/
.subnav li {
display: '';
margin-bottom: 3px;
}
}
/*@media only screen and (min-width: 640px) {
}*/)";
void WebFile_cangrow_CSS(AsyncWebServerRequest *request) {
AsyncWebServerResponse *response = request->beginResponse_P(200, F("text/css"), File_cangrow_CSS);
response->addHeader(F("Cache-control"), F("max-age=600"));
request->send(response);
//request->send_P(200, "text/css", File_cangrow_CSS);
}

View file

@ -1,241 +0,0 @@
/*
*
* include/Webserver/File_cangrow_JS.h - /cangrow.js header file
*
*
*
*/
const char File_cangrow_JS[] PROGMEM = R"(function toggleDisplay(id) {
let el = document.getElementById(id);
let el_cs = getComputedStyle(el);
if (el_cs.getPropertyValue('display') === 'none') {
el.style.display = 'inline';
} else {
el.style.display = 'none';
}
}
function hideAllClass(classname) {
const el = document.getElementsByClassName(classname);
for(let i = 0; i < el.length ; i++) {
el[i].style.display = 'none';
}
}
function showSelect(selectId, prefix, hideClass = '') {
if(hideClass != '') {
hideAllClass(hideClass);
}
let selVal = document.getElementById(selectId).value;
toggleId = prefix + selVal;
if(document.getElementById(toggleId) !== null ) {
toggleDisplay(toggleId);
}
}
function confirmDelete(name) {
return confirm('Delete ' + name + '?');
}
function SystemOutputAddselectRequired(selectId) {
let selVal = document.getElementById(selectId).value;
//hideAllClass('hidden');
console.log('selectReq Status: ' + selVal);
switch(selVal) {
case '1':
document.getElementById('gpio').required = true;
document.getElementById('gpio_pwm').required = true;
document.getElementById('i2c_type').required = false;
document.getElementById('i2c_addr').required = false;
document.getElementById('i2c_port').required = false;
document.getElementById('webcall_host').required = false;
document.getElementById('webcall_path_on').required = false;
document.getElementById('webcall_path_off').required = false;
break;
case '2':
document.getElementById('gpio').required = false;
document.getElementById('gpio_pwm').required = false;
document.getElementById('i2c_type').required = true;
document.getElementById('i2c_addr').required = true;
document.getElementById('i2c_port').required = true;
document.getElementById('webcall_host').required = false;
document.getElementById('webcall_path_on').required = false;
document.getElementById('webcall_path_off').required = false;
break;
case '3':
document.getElementById('gpio').required = false;
document.getElementById('gpio_pwm').required = false;
document.getElementById('i2c_type').required = false;
document.getElementById('i2c_addr').required = false;
document.getElementById('i2c_port').required = false;
document.getElementById('webcall_host').required = true;
document.getElementById('webcall_path_on').required = true;
document.getElementById('webcall_path_off').required = true;
break;
default:
break;
}
}
// https://stackoverflow.com/a/67412019
function SystemOutputAdd_replaceI2cAddr(selectId, replaceId) {
let sel = document.querySelector('#' + replaceId);
let selVal = document.getElementById(selectId).value;
// Remove existing options
Array.from(sel).forEach((option) => {
sel.removeChild(option)
});
// get or set your new options here.
if(selVal) {
addr[selVal].map((optionData) => {
let opt = document.createElement('option');
let PortsUsed = 0;
let label = optionData[0];
opt.value = optionData[1];
// iterate through i2c modules available ports
for(i = 0; i < optionData[2].length; i++) {
if(optionData[2][i] > 0) {
PortsUsed++;
}
}
if(PortsUsed >= optionData[2].length) {
opt.disabled = true;
label = label + ' (used)';
}
opt.appendChild(document.createTextNode(label));
sel.appendChild(opt);
});
SystemOutputAdd_replaceI2cPort('i2c_type', 'i2c_addr', 'i2c_port');
}
}
//////////////////////////////////////
function SystemOutputAdd_replaceI2cPort(selectTypeId, selectAddrId, replaceId) {
let repl = document.querySelector('#' + replaceId);
let selValType = document.getElementById(selectTypeId).value;
let selValAddr = document.getElementById(selectAddrId).value;
// Remove existing options
Array.from(repl).forEach((option) => {
repl.removeChild(option)
});
if(selValAddr) {
console.log('true');
// iterate through i2c modules available ports
for(i = 0; i < addr[selValType][selValAddr][2].length; i++) {
let opt = document.createElement('option');
let label = 'Port ' + i;
opt.value = i;
if(addr[selValType][selValAddr][2][i] > 0) {
label = label + ' (used)';
opt.disabled = true;
}
opt.appendChild(document.createTextNode(label));
repl.appendChild(opt);
console.log('PortID ' + i + ' Port sum ' + addr[selValType][selValAddr][2].length);
}
} else {
let opt = document.createElement('option');
opt.appendChild(document.createTextNode('n/a'));
opt.disabled = true;
repl.appendChild(opt);
}
}
//javascript is my passion
function SystemSensorAddGpioI2cSel(selectId) {
let selVal = document.getElementById(selectId).value;
hideAllClass('hidden');
if(selVal == 1) {
document.getElementById('type_1').style.display = 'inline';
document.getElementById('i2c_addr').required = false;
if(ESP == '32') {
document.getElementById('gpio').required = true;
}
} else if(selVal > 1) {
document.getElementById('type_2').style.display = 'inline';
document.getElementById('i2c_addr').required = true;
if(ESP == '32') {
document.getElementById('gpio').required = false;
}
}
}
function convertDateToEpoch(src, dst) {
var val = document.getElementById(src).value ;
document.getElementById(dst).value = new Date(val).getTime() / 1000;
}
function GrowSelectControlSensorRead(selectId, inputSensor, inputRead) {
let selVal = document.getElementById(selectId).value;
let sensor = selVal.split(':')[0];
let read = selVal.split(':')[1];
document.getElementById(inputSensor).value = sensor;
document.getElementById(inputRead).value = read;
}
function GetSensorJson(callback) {
let path = '/api/sensor/';
//let path = '/api/sensor/raw_' + sensor + '_' + reading;
var xobj = new XMLHttpRequest();
xobj.overrideMimeType('application/json');
xobj.open('GET', path, true);
xobj.onreadystatechange = function() {
if (xobj.readyState == 4 && xobj.status == "200") {
callback(xobj.responseText);
}
}
xobj.send(null);
}
/* propably not the best place, but this as global as it can get i guess */
var SensorJson;
function SensorJsonRefresh() {
GetSensorJson(function(response) {
/* needs to be a global */
SensorJson = JSON.parse(response);
});
//console.log('Refresh SensorJson');
}
function rawRefresh(sensor, reading, id) {
let element = id + sensor + '-' + reading;
document.getElementById(element).textContent = SensorJson.sensor[sensor].reading[reading].raw;
//console.log(SensorJson.sensor[sensor].reading[reading].raw);
//console.log('sensor:' + sensor + ';reading:' + reading + ';id:' + id + ';element:' + element);
}
function sensorRefresh(sensor, reading, id) {
let element = id + sensor + '-' + reading;
document.getElementById(element).textContent = SensorJson.sensor[sensor].reading[reading].value + ' ' + SensorJson.sensor[sensor].reading[reading].unit;
//console.log(SensorJson.sensor[sensor].reading[reading].value + SensorJson.sensor[sensor].reading[reading].unit);
//console.log('sensor:' + sensor + ';reading:' + reading + ';id:' + id + ';element:' + element);
}
)";
void WebFile_cangrow_JS(AsyncWebServerRequest *request) {
AsyncWebServerResponse *response = request->beginResponse_P(200, F("text/javascript"), File_cangrow_JS);
response->addHeader(F("Cache-control"), F("max-age=600"));
request->send(response);
//request->send_P(200, "text/javascript", File_cangrow_JS);
}

View file

@ -1,38 +0,0 @@
unsigned char File_favicon_ico_gz[] = {
0x1f, 0x8b, 0x08, 0x08, 0x11, 0x71, 0x19, 0x67, 0x00, 0x03, 0x43, 0x61,
0x6e, 0x47, 0x72, 0x6f, 0x77, 0x5f, 0x66, 0x61, 0x76, 0x69, 0x63, 0x6f,
0x2e, 0x69, 0x63, 0x6f, 0x00, 0xed, 0x94, 0x49, 0x4b, 0xc3, 0x40, 0x18,
0x86, 0xdf, 0xd8, 0xc5, 0xaa, 0xe9, 0x12, 0xa7, 0xcd, 0xd2, 0x26, 0x99,
0x7c, 0x89, 0x76, 0x45, 0xb4, 0x2a, 0xb6, 0xa2, 0x42, 0xb1, 0x52, 0x73,
0x11, 0xd4, 0x83, 0xdb, 0xc1, 0x8b, 0x08, 0x75, 0xf9, 0xff, 0x67, 0xbf,
0x49, 0x3c, 0x58, 0xa4, 0x17, 0xc1, 0x5b, 0x9e, 0xe4, 0x1d, 0xe6, 0xf9,
0x86, 0x61, 0x32, 0x03, 0x19, 0x40, 0xe3, 0xa7, 0x56, 0x03, 0xb7, 0x25,
0xcc, 0x0b, 0x80, 0x09, 0xa0, 0xcb, 0xe1, 0x12, 0x02, 0xa4, 0xf5, 0x65,
0x44, 0xed, 0x08, 0x51, 0x27, 0xc2, 0x56, 0x2f, 0xc2, 0x76, 0x9f, 0x33,
0x88, 0xd0, 0xde, 0x09, 0xd1, 0xd9, 0x0b, 0xd1, 0x1d, 0x86, 0xe8, 0x1d,
0x86, 0xe8, 0x8f, 0x08, 0x83, 0x31, 0x61, 0x77, 0x12, 0x60, 0x38, 0x0d,
0xb0, 0x3f, 0x93, 0x38, 0x88, 0x25, 0x8e, 0xae, 0x7c, 0x8c, 0xae, 0x7d,
0x8c, 0x6f, 0x3c, 0x8e, 0x8f, 0xe3, 0x5b, 0x0f, 0x27, 0x77, 0x2e, 0x4e,
0xef, 0x39, 0x0f, 0x2e, 0xce, 0x1e, 0x39, 0x4f, 0x2d, 0x4c, 0x9e, 0x9b,
0x38, 0x7f, 0x71, 0x30, 0xe5, 0x5c, 0xbc, 0xda, 0x98, 0xcd, 0x2d, 0xcc,
0xde, 0x2c, 0x5c, 0xbe, 0x5b, 0x88, 0x3f, 0x4c, 0xc4, 0x9f, 0xe6, 0xd2,
0xef, 0xcb, 0xc8, 0xc8, 0xf8, 0x7f, 0x7e, 0xfc, 0x81, 0x45, 0x61, 0xe4,
0x57, 0xac, 0xbc, 0x21, 0x8a, 0xa9, 0x6b, 0x82, 0xa4, 0x0c, 0x24, 0x09,
0x2d, 0x55, 0x43, 0x78, 0xc4, 0x78, 0xc2, 0x50, 0x85, 0x9c, 0x4d, 0x92,
0x12, 0x24, 0xd9, 0x1b, 0x7c, 0xe9, 0x08, 0x51, 0x72, 0x13, 0x5f, 0xaf,
0x8a, 0x4d, 0x35, 0x61, 0xd5, 0x4b, 0xc7, 0xfd, 0xef, 0x5b, 0xa8, 0x4e,
0x14, 0x24, 0xaf, 0x9f, 0xe8, 0x5a, 0x40, 0x75, 0x87, 0xc8, 0xe1, 0xb2,
0xae, 0xbc, 0x2c, 0x85, 0xa6, 0x9c, 0x97, 0x2d, 0x2b, 0x6f, 0x15, 0x34,
0x28, 0x87, 0x56, 0x6d, 0x2a, 0xcf, 0x71, 0x2a, 0xca, 0xd3, 0x6e, 0x0a,
0x51, 0x65, 0x61, 0x3b, 0x44, 0x8b, 0xdb, 0x6b, 0x34, 0x16, 0x5d, 0xd7,
0xff, 0x74, 0x4a, 0xbf, 0xf9, 0x02, 0x31, 0x98, 0x4b, 0x6b, 0x7e, 0x05,
0x00, 0x00
};
unsigned int File_favicon_ico_gz_len = 326;
void WebFile_favicon_ico(AsyncWebServerRequest *request) {
AsyncWebServerResponse *response = request->beginResponse_P(200, F("image/x-icon"), File_favicon_ico_gz, File_favicon_ico_gz_len);
response->addHeader(F("Content-Encoding"), F("gzip"));
response->addHeader(F("Cache-control"), F("max-age=600"));
request->send(response);
}

View file

@ -1,9 +0,0 @@
/*
*
* include/Webserver/footer_HTML.h - footer page HTML header file
*
*
*
*/
const char Footer_HTML[] PROGMEM = R"(<div class='footer'><span>Build: %CGBUILD%</span></div></div></body></html>)";

View file

@ -1,28 +0,0 @@
/*
*
* include/Webserver/header_HTML.h - header page HTML header file
*
*
*
*/
const char Header_HTML[] PROGMEM = R"(<!DOCTYPE html>
<html>
<head>
<meta charset='UTF-8'>
<meta name='viewport' content='width=device-width, initial-scale=1.0'>
<title>%GROWNAME% - CanGrow v%CGVER%</title>
<link rel='stylesheet' href='/cangrow.css'>
<script type='text/javascript' src='/cangrow.js'></script>
</head>
<body>
<ul class='nav'><li><a href='/'>&#x1F331; %GROWNAME%</a></li>
<li><a class='%ACTIVE_NAV_GROW%' href='/grow/' >&#128262; Grow settings</a></li>
<li><a class='%ACTIVE_NAV_SYSTEM%' href='/system/' >&#9881; System settings</a></li>
<li><a class='%ACTIVE_NAV_WIFI%' href='/wifi/' >&#128225; WiFi settings</a></li>
<li><a class='%ACTIVE_NAV_HELP%' href='/help' >&#x2753; Help</a></li>
<li><span class='navTime'>%TIME%</span></li>
<li><a href='https://git.la10cy.net/DeltaLima/CanGrow' target='_blank'>CanGrow v%CGVER%</a></li>
</ul>
<div class='center'>
%NEED_RESTART%)";

View file

@ -1,26 +0,0 @@
/*
* 404 error page begins
*/
// 404 page is a good page template btw
const char Page_404_HTML[] PROGMEM = R"EOF(%HEADER%
<div class='warnmsg'><h1>&#10071; &#65039; 404 - not found</h1></div>
%FOOTER% )EOF";
/* processor */
String Proc_WebPage_404(const String& var) {
if(TestHeaderFooter(var)) {
return AddHeaderFooter(var);
} else {
return String();
}
}
// https://github.com/mathieucarbou/ESPAsyncWebServer/blob/main/examples/SimpleServer/SimpleServer.ino
void WebserverNotFound(AsyncWebServerRequest* request) {
request->send_P(404, TEXT_HTML, Page_404_HTML, Proc_WebPage_404);
}
/*
* 404 error page ends
*/

File diff suppressed because it is too large Load diff

View file

@ -1,102 +0,0 @@
/*
*
* include/Webserver/Page_grow_HTML.h - grow page HTML header file
*
*
*
*/
/* submenu SUBNAV */
const char Page_grow_HTML_SUBNAV[] PROGMEM = R"(<ul class='subnav'>
<li><a class='%ACTIVE_SUBNAV_GENERAL%' href='/grow/'>&#x1F6E0;&#xFE0F; General</a></li>
<li><a class='%ACTIVE_SUBNAV_LIGHT%' href='/grow/light/'>&#x1F4A1; Light</a></li>
<li><a class='%ACTIVE_SUBNAV_AIR%' href='/grow/air/'>&#x1F300; Air</a></li>
<li><a class='%ACTIVE_SUBNAV_WATER%' href='/grow/water/'>&#x1F4A7; Water</a></li>
<li><a class='%ACTIVE_SUBNAV_DASHBOARD%' href='/grow/dashboard/' >&#x1F5A5;&#xFE0F; Dashboard</a></li>
</ul>)";
/* /grow/ main page */
const char Page_grow_HTML[] PROGMEM = R"(%HEADER%
%SUBNAV%
%SAVE_MSG%
<p>here you can set grow stuff<br></p><form method='post' action='/grow/'>
<u>Grow name:</u><br>
<input type='text' name='name' maxlength='31' value='%GROWNAME%' required><br>
<input type='hidden' id='start' name='start' value='%GROWSTART_EPOCH%' required>
<u>Grow start date:</u><br>
<input type='date' id='GrowStart_sel' onChange='convertDateToEpoch("GrowStart_sel", "start");' value='%GROWSTART%' ><br>
<u>Vegetation duration:</u><br>
<input class='inputShort' type='number' name='daysVeg' min='0' max='255' value='%DAYS_VEG%' required> Days<br>
<u>Bloom duration:</u><br>
<input class='inputShort' type='number' name='daysBloom' min='0' max='255' value='%DAYS_BLOOM%' required> Days<br>
<br>
<input type='submit' value='&#x1F4BE; Save settings'>
</form>
%FOOTER% )";
/* /grow/light/ page */
const char Page_grow_light_HTML[] PROGMEM = R"(%HEADER%
%SUBNAV%
%SAVE_MSG%
<p>here you can set light stuff<br></p>
%LIGHT%
%FOOTER% )";
/* /grow/air/ page */
const char Page_grow_air_HTML[] PROGMEM = R"(%HEADER%
%SUBNAV%
%SAVE_MSG%
<p>here you can set air stuff<br></p>
%AIR%
%FOOTER% )";
/* /grow/water/ page */
const char Page_grow_water_HTML[] PROGMEM = R"(%HEADER%
%SUBNAV%
%SAVE_MSG%
<p>here you can set water stuff<br></p>
%WATER%
%FOOTER% )";
/* /grow/dashboard/ page */
const char Page_grow_dashboard_HTML[] PROGMEM = R"(%HEADER%
%SUBNAV%
%SAVE_MSG%
<p>here you can set dashboard stuff<br></p>
%DASHBOARD%
%FOOTER% )";

View file

@ -1,28 +0,0 @@
/*
*
* include/Webserver/Page_root.h - root page header file
*
*
*
*/
#include "Page_root_HTML.h"
// https://techtutorialsx.com/2018/07/23/esp32-arduino-http-server-template-processing-with-multiple-placeholders/
String Proc_WebPage_root(const String& var) {
if(TestHeaderFooter(var)) {
return AddHeaderFooter(var);
} else if(var == "LOL") {
return String("Nice");
} else if(var == "LOL") {
return String("Jojoojo :)");
} else {
return String();
}
}
void WebPage_root(AsyncWebServerRequest *request) {
request->send_P(200, TEXT_HTML, Page_root_HTML, Proc_WebPage_root);
}

View file

@ -1,13 +0,0 @@
/*
*
* include/Webserver/Page_root_HTML.h - root page HTML header file
*
*
*
*/
const char Page_root_HTML[] PROGMEM = R"EOF(%HEADER%
<h2>&#x1F331; Hello world!</h2>
<a href='/api/sensor/'>Sensor data -> /api/sensor/</a>
%FOOTER% )EOF";

File diff suppressed because it is too large Load diff

View file

@ -1,396 +0,0 @@
/*
*
* include/Webserver/Page_system_HTML.h - system settings page HTML header file
*
*
*
*/
/* submenu SUBNAV */
const char Page_system_HTML_SUBNAV[] PROGMEM = R"(<ul class='subnav'>
<li><a class='%ACTIVE_SUBNAV_GENERAL%' href='/system/'>&#x1F6E0;&#xFE0F; General</a></li>
<li><a class='%ACTIVE_SUBNAV_SENSOR%' href='/system/sensor/'>&#x1F321;&#xFE0F; Sensor</a></li>
<li><a class='%ACTIVE_SUBNAV_OUTPUT%' href='/system/output/'>&#9889; Output</a></li>
<li><a class='%ACTIVE_SUBNAV_UPDATE%' href='/system/update'>&#x1F504; Firmware update</a></li>
<li><a class='%ACTIVE_SUBNAV_RESTART%' href='/system/restart' >&#x1F501; System restart</a></li>
<li><a class='%ACTIVE_SUBNAV_WIPE%' href='/system/wipe' >&#x1F4A3; Factory reset</a></li>
</ul>)";
/* /system main page */
const char Page_system_HTML[] PROGMEM = R"(%HEADER%
%SUBNAV%
%SAVE_MSG%
<p>here you can set which features and sensors you use<br></p><form method='post' action='/system/'>
<u>NTP offset</u>:<br>
<input class='inputShort' type='number' name='ntpOffset' min='-12' max='14' value='%NTPOFFSET%' required> Hours<br>
<u>Maintenance duration</u>:<br> <input class='inputShort' type='number' name='maintenanceDuration' min='0' max='900' value='%MAINTDUR%' required> Seconds<br>
<u>ESP32-Cam IP (optional)</u>:<br>
<input type='text' name='esp32cam' maxlength='16' value='%ESP32CAM%' ><br>
<u>HTTP log to serial</u>:<br>
<select name='httpLogSerial' required>
<option disabled value='' selected hidden>---</option>
%HTTPLOGSERIAL%
</select><br>
<u>I2C RTC</u>:<br>
%RTC_STATUS%<select name='rtc' required>
<option value='0' selected >---</option>
%RTC_AVAILABLE%
</select><br>
<u>Save time to LittleFS</u>:<br>
<select name='time2fs' required>
<option disabled value='' selected hidden>---</option>
%TIME2FS%
</select><br>
<u>PWM Frequency</u>:<br>
<input type='number' name='pwmFreq' min='0' max='65535' value='%PWMFREQ%'>
</select><br>
<br>
<input type='submit' value='&#x1F4BE; Save settings'>
</form>
%FOOTER% )";
/*******************************************************************************
* Subpage update
*/
const char Page_system_update_HTML[] PROGMEM = R"(%HEADER%
%SUBNAV%
Version: %CGVER% <br>
Build : %CGBUILD% <br>
<p>You find the latest CanGrow firmware version on the <a href='https://git.la10cy.net/DeltaLima/CanGrow/releases' target='_blank'>release page</a> of the git repository.</p>
<form method='POST' action='/system/update' enctype='multipart/form-data' onsubmit="document.getElementById('divUploading').style.display = '';">
<b>Select .bin file:</b><br>
<input type='file' accept='.bin,.bin.gz' name='firmware' required>
<input type='submit' value='Update Firmware'>
</form>
<div id='divUploading' style='display: none;' class='warnmsg'>&#x1F6DC; Uploading, please wait...</div>
%FOOTER% )";
const char Page_system_update_HTML_POST[] PROGMEM = R"(<html>
<head><meta http-equiv='refresh' content='20;url=/' /></head>
<body><h1>Successfully updated!
<br>Restarting...</h1><br>
Redirecting to "/" in 20 seconds
</body></html>)";
const char Page_system_update_HTML_POST_FAILED[] PROGMEM = R"(<html>
<head></head><body><h1>UPDATE FAILED</h1><br>
Please see messages on serial monitor for more information and go back to <a href='/system/update'>\"System settings > Firmware update\"</a> and try another file.
</body></html>)";
/*******************************************************************************
* Subpage restart
*/
const char Page_system_restart_HTML[] PROGMEM = R"(%HEADER%
%SUBNAV%
<div class='warnmsg'>
%RESTART_MSG%
</div>
%FOOTER% )";
const char Page_system_restart_HTML_RESTART_MSG[] PROGMEM = R"(Do you want to restart CanGrow?<br>Please confirm.
<form action='/system/restart' method='post'><input type='hidden' name='confirmed' value='true' />
<input type='submit' value='Confirm restart' />
</form>)";
const char Page_system_restart_HTML_RESTART_MSG_POST[] PROGMEM = R"(Restarting...<br><span style='helpbox'>Redirecting to root page in 20 seconds.</span>)";
/*******************************************************************************
* Subpage wipe
*/
const char Page_system_wipe_HTML[] PROGMEM = R"(%HEADER%
%SUBNAV%
<div class='warnmsg'>
%WIPE_MSG%
</div>
%FOOTER% )";
const char Page_system_wipe_HTML_WIPE_MSG[] PROGMEM = R"(All settings will be removed!!<br><br>
Please confirm wiping LittleFS
<form action='/system/wipe' method='post'><br>
Please confirm: <input type='checkbox' id='confirmed' name='confirmed' required /><br>
<input type='submit' value='Confirm wiping' />
</form>)";
const char Page_system_wipe_HTML_WIPE_MSG_POST[] PROGMEM = R"(Restarting...)";
/*******************************************************************************
* Subpage output
*/
const char Page_system_output_HTML[] PROGMEM = R"(%HEADER%
%SUBNAV%
%SAVE_MSG%
<a class='button %ADD_DISABLED%' href='/system/output/add'>&#10133; Add output</a>
<table class='centered'>
<tr>
<th>&nbsp;</th>
<th>ID</th>
<th>Name</th>
<th>Type</th>
<th>Device</th>
<th>&nbsp;</th>
<th>Action</th>
</tr>
%TR_TD%
</table>
%FOOTER% )";
/*******************************************************************************
* Subpage output add
*/
const char Page_system_output_add_HTML[] PROGMEM = R"(%HEADER%
%SUBNAV%
<h3>%ACTION% output ID %OUTPUT_ID%</h3>
%SAVE_MSG%
<p>Add/Edit CanGrow output.</p>
<form method='post' action='/system/output/add?%EDIT_MODE%'>
<input type='hidden' name='outputId' value='%OUTPUT_ID%' />
<u>Type</u>:<br>
<select id='type_sel' name='type' onchange="showSelect('type_sel', 'type_', 'hidden'); SystemOutputAddselectRequired('type_sel');" required>
<option disabled value='' selected hidden>---</option>
%OUTPUT_TYPE%
</select><br>
<u>Device</u>:<br>
<select name='device' required>
<option disabled value='' selected hidden>---</option>
%OUTPUT_DEVICE%
</select><br>
<u>Name</u>:<br>
<input type='text' name='name' maxlength='32' value='%OUTPUT_NAME%' required><br>
<u>Enable</u>:<br>
<select name='enabled' required>
<option disabled value='' selected hidden>---</option>
%OUTPUT_ENABLED%
</select><br>
<u>Invert</u>:<br>
<select name='invert' required>
<option disabled value='' selected hidden>---</option>
%INVERT%
</select><br>
<div class='hidden %CLASS_TYPE_1%' id='type_1'>
<u>GPIO</u>:<br>
<select id='gpio' name='gpio'>
<option disabled value='' selected hidden>---</option>
%GPIO_INDEX%
</select><br>
<u>GPIO PWM</u>:<br>
<select id='gpio_pwm' name='gpio_pwm' >
<option disabled value='' selected hidden>---</option>
%GPIO_PWM%
</select><br>
</div>
<div class='hidden ' id='type_2'>
<u>I2C type</u>:<br>
<select id='i2c_type' name='i2c_type' onchange="SystemOutputAdd_replaceI2cAddr('i2c_type', 'i2c_addr');">
<option disabled value='' selected hidden>---</option>
%I2C_TYPE%
</select><br>
<u>I2C address</u>:<br>
<select id='i2c_addr' name='i2c_addr' onchange="SystemOutputAdd_replaceI2cPort('i2c_type', 'i2c_addr', 'i2c_port');">
<option value='' selected >---</option>
</select><br>
<u>I2C module port</u>:<br>
<select id='i2c_port' name='i2c_port'>
<option value='' selected >---</option>
</select><br>
</div>
<div class='hidden %CLASS_TYPE_3%' id='type_3'>
<u>Webcall host</u>:<br>
<input id='webcall_host' type='text' name='webcall_host' maxlength='32' value='%WEBCALL_HOST%' ><br>
<u>Webcall path 'on'</u>:<br>
<input id='webcall_path_on' type='text' name='webcall_path_on' maxlength='32' value='%WEBCALL_PATH_ON%' ><br>
<u>Webcall path 'off'</u>:<br>
<input id='webcall_path_off' type='text' name='webcall_path_off' maxlength='32' value='%WEBCALL_PATH_OFF%' ><br>
</div>
<br>
<input type='submit' value='&#x1F4BE; Save settings'>
</form>
<script>
var addr = [ [] ,
%REPLACE_I2CADDR_JS% ];
%I2C_SAVED%
</script>
%FOOTER% )";
const char Page_system_output_add_HTML_NO_ID_AVAILABLE[] PROGMEM = R"(%HEADER%
%SUBNAV%
<h3>You cannot create more outputs, limit reached.</h3>
%FOOTER% )";
/*******************************************************************************
* Subpage sensor
*/
const char Page_system_sensor_HTML[] PROGMEM = R"(%HEADER%
%SUBNAV%
%SAVE_MSG%
<a class='button %ADD_DISABLED%' href='/system/sensor/add'>&#10133; Add sensor</a>
<table class='centered'>
<tr>
<th>&nbsp;</th>
<th>ID</th>
<th>Name</th>
<th>Type</th>
</tr>
%TR_TD%
</table>
%FOOTER% )";
/*******************************************************************************
* Subpage sensor add
*/
const char Page_system_sensor_add_HTML[] PROGMEM = R"(%HEADER%
%SUBNAV%
<h3>%ACTION% sensor ID %SENSOR_ID%</h3>
%SAVE_MSG%
<p>Add/Edit CanGrow sensor.</p>
<form method='post' action='/system/sensor/add'>
<input type='hidden' name='sensorId' value='%SENSOR_ID%' />
<u>Type</u>:<br>
<select id='type_sel' name='type' onchange="SystemSensorAddGpioI2cSel('type_sel'); SystemSensor_replaceAddr('type_sel', 'i2c_addr');" required>
<option disabled value='' selected hidden>---</option>
%SENSOR_TYPE%
</select><br>
<u>Name</u>:<br>
<input type='text' name='name' maxlength='32' value='%SENSOR_NAME%' required><br>
<div class='hidden %CLASS_TYPE_1%' id='type_1'>
<u>GPIO</u>:<br>
<select id='gpio' name='gpio'>
<option value='' selected >---</option>
%GPIO_INDEX%
</select><br>
</div>
<div class='hidden %CLASS_TYPE_2%' id='type_2'>
<u>I2C address</u>:<br>
<select id='i2c_addr' name='i2c_addr' required>
<option value='' selected >---</option>
</select><br>
</div>
<div class='hidden %CLASS_TYPE_3% %CLASS_TYPE_1%' id='type_3'>
<span>Special 3</span>
</div>
<!-- Sensor reading calibration -->
<br>
<input type='submit' value='&#x1F4BE; Save settings'>
</form>
<script>
var ESP = '%ESP_PLATFORM%';
// https://stackoverflow.com/a/67412019
function SystemSensor_replaceAddr(selectId, replaceId) {
var sel = document.querySelector('#' + replaceId);
let selVal = document.getElementById(selectId).value;
// Remove existing options
Array.from(sel).forEach((option) => {
sel.removeChild(option)
})
var addr = [ [] ,
%REPLACE_I2CADDR_JS%
]
addr[selVal].map((optionData) => {
let opt = document.createElement('option')
opt.appendChild(document.createTextNode(optionData[0]));
opt.value = optionData[1]
if(optionData[2] > 0) {
opt.disabled = true
}
sel.appendChild(opt);
})
}
%I2C_SAVED%
</script>
%FOOTER% )";
const char Page_system_sensor_add_HTML_NO_ID_AVAILABLE[] PROGMEM = R"(%HEADER%
%SUBNAV%
<h3>You cannot create more sensors, limit reached.</h3>
%FOOTER% )";
/*******************************************************************************
* Subpage sensor calibrate
*/
const char Page_system_sensor_calibrate_HTML[] PROGMEM = R"(%HEADER%
%SUBNAV%
<h3>&#x1F39B;&#xFE0F; Calibrate sensor ID %SENSOR_ID% (%SENSOR_NAME%)</h3>
%SAVE_MSG%
<p>Calibrate CanGrow sensor.</p>
%SENSOR_READING%
<script>
</script>
%FOOTER% )";

View file

@ -1,197 +0,0 @@
/*
*
* include/Webserver/Page_wifi.h - wifi page header file
*
*
*
*/
#include "Page_wifi_HTML.h"
String WebPage_wifi_ScanNetworks() {
const static char LogLoc[] PROGMEM= "[Webserver:wifi:ScanNetworks]";
String html;
#ifdef DEBUG
Log.verbose(F("%s scanning for available networks:" CR), LogLoc);
#endif
// https://github.com/mathieucarbou/ESPAsyncWebServer/blob/main/docs/index.md#scanning-for-available-wifi-networks
int n = WiFi.scanComplete();
if(n == -2){
WiFi.scanNetworks(true);
} else if(n){
for (int i = 0; i < n; ++i){
html += F("<option value='");
html += WiFi.SSID(i);
html += F("'>");
html += WiFi.SSID(i);
html += F("</option>");
/* dirty hack, arduino-log somehow destroys wifi names in output
/* so i have to print them oldschool with Serial.println
*/
#ifdef DEBUG
Log.verbose(F("%s - "), LogLoc);
Serial.println(WiFi.SSID(i));
#endif
}
WiFi.scanDelete();
if(WiFi.scanComplete() == -2){
WiFi.scanNetworks(true);
}
}
return html;
}
// https://techtutorialsx.com/2018/07/23/esp32-arduino-http-server-template-processing-with-multiple-placeholders/
String Proc_WebPage_wifi(const String& var) {
if(TestHeaderFooter(var)) {
return AddHeaderFooter(var, 3);
//CURRENT_SETTINGS
} else if(var == "CURRENT_SETTINGS") {
if(strlen(config.wifi.ssid) > 0) {
return String(Page_wifi_HTML_CURRENT_SETTINGS);
} else {
return String();
}
} else if(var == "CONFIGWIFI_SSID") {
return String(config.wifi.ssid);
} else if(var == "CONFIGWIFI_DHCP") {
return String(config.wifi.dhcp);
} else if(var == "CONFIGWIFI_IP") {
return String(WiFi.localIP().toString());
} else if(var == "CONFIGWIFI_NETMASK") {
return String(WiFi.subnetMask().toString());
} else if(var == "CONFIGWIFI_GATEWAY") {
return String(WiFi.gatewayIP().toString());
} else if(var == "CONFIGWIFI_DNS") {
return String(WiFi.dnsIP().toString());
} else if(var == "WIFI_LIST") {
return String(WebPage_wifi_ScanNetworks());
} else {
return String();
}
}
String Proc_WebPage_wifi_POST(const String& var) {
if(var == "SAVE_MSG") {
return String(Common_HTML_SAVE_MSG);
} else {
return Proc_WebPage_wifi(var);
}
}
String Proc_WebPage_wifi_POST_ERR(const String& var) {
if(var == "SAVE_MSG") {
return String(Common_HTML_SAVE_MSG_ERR);
} else {
return Proc_WebPage_wifi(var);
}
}
void WebPage_wifi(AsyncWebServerRequest *request) {
const static char LogLoc[] PROGMEM = "[Webserver:wifi]";
if(request->method() == HTTP_POST) {
if(request->hasParam("config.wifi.ssid", true)) {
const AsyncWebParameter* p_ssid = request->getParam("config.wifi.ssid", true);
strlcpy(config.wifi.ssid, p_ssid->value().c_str(), sizeof(config.wifi.ssid));
}
if(request->hasParam("config.wifi.password", true)) {
const AsyncWebParameter* p_password = request->getParam("config.wifi.password", true);
strlcpy(config.wifi.password, p_password->value().c_str(), sizeof(config.wifi.password));
}
if(
(request->hasParam("config.wifi.ip0", true)) &&
(request->hasParam("config.wifi.ip1", true)) &&
(request->hasParam("config.wifi.ip2", true)) &&
(request->hasParam("config.wifi.ip3", true))) {
const AsyncWebParameter* p_ip0 = request->getParam("config.wifi.ip0", true);
const AsyncWebParameter* p_ip1 = request->getParam("config.wifi.ip1", true);
const AsyncWebParameter* p_ip2 = request->getParam("config.wifi.ip2", true);
const AsyncWebParameter* p_ip3 = request->getParam("config.wifi.ip3", true);
config.wifi.ip[0] = p_ip0->value().toInt();
config.wifi.ip[1] = p_ip1->value().toInt();
config.wifi.ip[2] = p_ip2->value().toInt();
config.wifi.ip[3] = p_ip3->value().toInt();
}
if(
(request->hasParam("config.wifi.netmask0", true)) &&
(request->hasParam("config.wifi.netmask1", true)) &&
(request->hasParam("config.wifi.netmask2", true)) &&
(request->hasParam("config.wifi.netmask3", true))) {
const AsyncWebParameter* p_netmask0 = request->getParam("config.wifi.netmask0", true);
const AsyncWebParameter* p_netmask1 = request->getParam("config.wifi.netmask1", true);
const AsyncWebParameter* p_netmask2 = request->getParam("config.wifi.netmask2", true);
const AsyncWebParameter* p_netmask3 = request->getParam("config.wifi.netmask3", true);
config.wifi.netmask[0] = p_netmask0->value().toInt();
config.wifi.netmask[1] = p_netmask1->value().toInt();
config.wifi.netmask[2] = p_netmask2->value().toInt();
config.wifi.netmask[3] = p_netmask3->value().toInt();
}
if(
(request->hasParam("config.wifi.gateway0", true)) &&
(request->hasParam("config.wifi.gateway1", true)) &&
(request->hasParam("config.wifi.gateway2", true)) &&
(request->hasParam("config.wifi.gateway3", true))) {
const AsyncWebParameter* p_gateway0 = request->getParam("config.wifi.gateway0", true);
const AsyncWebParameter* p_gateway1 = request->getParam("config.wifi.gateway1", true);
const AsyncWebParameter* p_gateway2 = request->getParam("config.wifi.gateway2", true);
const AsyncWebParameter* p_gateway3 = request->getParam("config.wifi.gateway3", true);
config.wifi.gateway[0] = p_gateway0->value().toInt();
config.wifi.gateway[1] = p_gateway1->value().toInt();
config.wifi.gateway[2] = p_gateway2->value().toInt();
config.wifi.gateway[3] = p_gateway3->value().toInt();
}
if(
(request->hasParam("config.wifi.dns0", true)) &&
(request->hasParam("config.wifi.dns1", true)) &&
(request->hasParam("config.wifi.dns2", true)) &&
(request->hasParam("config.wifi.dns3", true))) {
const AsyncWebParameter* p_dns0 = request->getParam("config.wifi.dns0", true);
const AsyncWebParameter* p_dns1 = request->getParam("config.wifi.dns1", true);
const AsyncWebParameter* p_dns2 = request->getParam("config.wifi.dns2", true);
const AsyncWebParameter* p_dns3 = request->getParam("config.wifi.dns3", true);
config.wifi.dns[0] = p_dns0->value().toInt();
config.wifi.dns[1] = p_dns1->value().toInt();
config.wifi.dns[2] = p_dns2->value().toInt();
config.wifi.dns[3] = p_dns3->value().toInt();
}
if(request->hasParam("config.wifi.dhcp", true)) {
const AsyncWebParameter* p_dhcp = request->getParam("config.wifi.dhcp", true);
config.wifi.dhcp = p_dhcp->value().toInt();
}
if(SaveConfig()) {
// we need a restart to apply the new settings
needRestart = true;
Log.notice(F("%s config saved" CR), LogLoc);
request->send_P(200, TEXT_HTML, Page_wifi_HTML, Proc_WebPage_wifi_POST);
} else {
Log.error(F("%s ERROR while saving config" CR), LogLoc);
request->send_P(200, TEXT_HTML, Page_wifi_HTML, Proc_WebPage_wifi_POST_ERR);
}
} else {
request->send_P(200, TEXT_HTML, Page_wifi_HTML, Proc_WebPage_wifi);
}
}

View file

@ -1,72 +0,0 @@
/*
*
* include/Webserver/Page_wifi_HTML.h - wifi page HTML header file
*
*
*
*/
const char Page_wifi_HTML[] PROGMEM = R"(%HEADER%
%SAVE_MSG%
%CURRENT_SETTINGS%
<p>Select your wifi network from the SSID list.
<br>Reload the page, if your network is not listed.</p>
<form method='post' action='/wifi/'>
<u>SSID</u>:<br>
<select id='config.wifi.ssid' name='config.wifi.ssid' required>
<option disabled value='' selected hidden>-Select your network-</option>
%WIFI_LIST%
</select><br>
<u>Password</u>:<br>
<input type='password' name='config.wifi.password'><br>
<u>DHCP</u>:<br>
<select id='dhcp_sel' name='config.wifi.dhcp' onchange="showSelect('dhcp_sel', 'dhcp_', 'hidden');" required>
<option disabled value='' selected hidden>---</option>
<option value='1'>On</option>
<option value='0'>Off</option>
</select><br>
<div class='hidden' id='dhcp_0'>
<u>IP</u>:<br>
<input class='inputShort' type='number' min='0' max='255' name='config.wifi.ip0'> .
<input class='inputShort' type='number' min='0' max='255' name='config.wifi.ip1'> .
<input class='inputShort' type='number' min='0' max='255' name='config.wifi.ip2'> .
<input class='inputShort' type='number' min='0' max='255' name='config.wifi.ip3'><br>
<u>Netmask</u>:<br>
<input class='inputShort' type='number' min='0' max='255' name='config.wifi.netmask0'> .
<input class='inputShort' type='number' min='0' max='255' name='config.wifi.netmask1'> .
<input class='inputShort' type='number' min='0' max='255' name='config.wifi.netmask2'> .
<input class='inputShort' type='number' min='0' max='255' name='config.wifi.netmask3'><br>
<u>Gateway</u>:<br>
<input class='inputShort' type='number' min='0' max='255' name='config.wifi.gateway0'> .
<input class='inputShort' type='number' min='0' max='255' name='config.wifi.gateway1'> .
<input class='inputShort' type='number' min='0' max='255' name='config.wifi.gateway2'> .
<input class='inputShort' type='number' min='0' max='255' name='config.wifi.gateway3'><br>
<u>DNS</u>:<br>
<input class='inputShort' type='number' min='0' max='255' name='config.wifi.dns0'> .
<input class='inputShort' type='number' min='0' max='255' name='config.wifi.dns1'> .
<input class='inputShort' type='number' min='0' max='255' name='config.wifi.dns2'> .
<input class='inputShort' type='number' min='0' max='255' name='config.wifi.dns3'><br>
</div>
<br>
<input type='submit' value='&#x1F4BE; Save settings'>
</form>
%FOOTER% )";
const char Page_wifi_HTML_CURRENT_SETTINGS[] PROGMEM = R"(<b><u>Current Settings:</u></b><br>WiFi SSID: <b>%CONFIGWIFI_SSID%</b><br>
Use DHCP: <b>%CONFIGWIFI_DHCP%</b><br>
IP address: <b>%CONFIGWIFI_IP%</b><br>
Subnet mask: <b>%CONFIGWIFI_NETMASK%</b><br>
Gateway: <b>%CONFIGWIFI_GATEWAY%</b><br>
DNS: <b>%CONFIGWIFI_DNS%</b><br><br>)";

View file

@ -1,190 +0,0 @@
/*
*
* include/Webserver/Common.h - header file with common webserver functions
* HTML header or footer to a String()
*
*
*
*/
#include "Webserver_Common_HTML.h"
/*
* global char constants for various HTML tags and stuff
*
*/
/* return type */
const char TEXT_HTML[] PROGMEM = "text/html";
/*
* TestHeaderFooter - checks if the given var from the webserver processor
* is actual a template variable from header or footer.
*/
bool TestHeaderFooter(const String& var) {
const static char LogLoc[] PROGMEM = "[Webserver:Common:TestHeaderFooter]";
#ifdef DEBUG3
Log.verbose(F("%s var: %s" CR), LogLoc, var);
#endif
if(
(var == "HEADER") ||
(var == "FOOTER") ||
(var == "CGVER") ||
(var == "CGBUILD") ||
(var == "GROWNAME") ||
(var == "CANGROW_CSS") ||
(var == "TIME") ||
(var == "NEED_RESTART") ||
(var == "ACTIVE_NAV_GROW") ||
(var == "ACTIVE_NAV_SYSTEM") ||
(var == "ACTIVE_NAV_WIFI") ||
(var == "ACTIVE_NAV_HELP") ||
(var == "PLACEHOLDER")) {
return true;
} else {
return false;
}
}
/*
* AddHeaderFooter - processor for header and footer template variables
*
* String& var:
* the string we receive from the processor is the actual
* variable name we replace here.
* byte activeNav:
* contains the number representing which page is active
* 1 - grow settings
* 2 - system settings
* 3 - wifi settings
* 4 - help page
*/
String AddHeaderFooter(const String& var, byte activeNav = 0) {
String activeNav_ClassName = F("activeNav");
if(var == "HEADER") {
return String(Header_HTML);
} else if(var == "FOOTER") {
return String(Footer_HTML);
} else if(var == "CGVER") {
return String(CANGROW_VER);
} else if(var == "CGBUILD") {
return String(CANGROW_BUILD);
} else if(var == "GROWNAME") {
return String(config.grow.name);
} else if(var == "CANGROW_CSS") {
return String(File_cangrow_CSS);
} else if(var == "TIME") {
return Str_TimeNow();
} else if((var == "ACTIVE_NAV_GROW") && (activeNav == 1)) {
return activeNav_ClassName;
} else if((var == "ACTIVE_NAV_SYSTEM") && (activeNav == 2)) {
return activeNav_ClassName;
} else if((var == "ACTIVE_NAV_WIFI") && (activeNav == 3)) {
return activeNav_ClassName;
} else if((var == "ACTIVE_NAV_HELP") && (activeNav == 4)) {
return activeNav_ClassName;
} else if(var == "NEED_RESTART") {
if(needRestart == true) {
return String(Common_HTML_NEED_RESTART);
} else {
return String();
}
} else {
return String();
}
}
/*
* Html_SelectOpt_GPIOindex
*
* returns <option> list as string with available gpios
*/
String Html_SelectOpt_GPIOindex(byte selectId = 255, bool input = false) {
String gpioIndex_html;
// iterate through through all available GPIOs in index
for(byte i = 1; i <= GPIOindex_length; i++) {
bool gpioUsed = Check_GPIOindex_Used(i);
gpioIndex_html += F("<option value='");
gpioIndex_html += i;
gpioIndex_html += F("'");
// set disabled option for gpio which are already in use or incompatible // or only inputs when configuring sensor ADC
// || ( (input == true) && ((GPIOindex[i].note != INPUT_ONLY) || (GPIOindex[i].note != INT_ADC)) )
/* when GPIO is already in use AND not selected OR
* input is false AND GPIO is Input only OR
* input is true AND GPIO is not INT_ADC AND not INPUT_only*/
if( ((gpioUsed == true) && (i != selectId)) || ((input == false) && (GPIOindex[i].note == INPUT_ONLY))
#ifdef ESP32
/* If we are on ESP32, we check our input GPIOs - we dont need this on ESP8266, as it only has 1 ADC */
|| ((input == true) && ((GPIOindex[i].note != INT_ADC) && GPIOindex[i].note != INPUT_ONLY))
#endif
) {
//|| ((input == true) && ((GPIOindex[i].note != INPUT_ONLY) || (GPIOindex[i].note != INT_ADC) ))
gpioIndex_html += F(" disabled");
}
if(i == selectId) {
gpioIndex_html += F(" selected");
}
gpioIndex_html += F(">GPIO ");
gpioIndex_html += GPIOindex[i].gpio;
//add gpio note if there is some
//if(GPIOindex[i].note > 0) {
gpioIndex_html += F(" ");
gpioIndex_html += FPSTR(GPIO_Index_note_descr[GPIOindex[i].note]);
// disable output incompatible gpio
if((GPIOindex[i].note == INPUT_ONLY) && (input == false)) {
gpioIndex_html += F(" (N/A)");
// add USED if gpio is already in use
} else if((gpioUsed == true) && (i != selectId)) {
gpioIndex_html += F(" (used)");
}
gpioIndex_html += F("</option>");
}
return gpioIndex_html;
}
String Html_SelectOpt_bool(byte selectVal = 255, String trueStr = "Yes", String falseStr = "No") {
String html;
html += F("<option value='1'");
html += ((selectVal > 0) && (selectVal < 255)) ? F(" selected") : F("");
html += F(">");
html += trueStr;
html += F("</option>");
html += F("<option value='0'");
html += (selectVal == 0) ? F(" selected") : F("");
html += F(">");
html += falseStr;
html += F("</option>");
return html;
}
String Html_SelectOpt_array(byte total, const char * descr[] , byte selectVal = 255) {
const static char LogLoc[] PROGMEM= "[Webserver:Common:Html_Select_Opt_array]";
String html;
// go through all available array entries, skip 0 because it means unconfigured
for(byte i = 1; i <= total; i++) {
//Log.notice(F("%s i: %d selectVal: %d descr: %S" CR), LogLoc, i, selectVal, descr[i]);
html += F("<option value='");
html += i;
html += F("'");
if(i == selectVal) {
html += F(" selected");
}
html += F(">");
// use FPSTR because our descr is stored in PROGMEM
html += FPSTR(descr[i]);
html += F("</option>");
}
return html;
}

View file

@ -1,26 +0,0 @@
/*
*
* include/Webserver/Common_HTML.h - header file with common HTML snippets
* HTML header or footer to a String()
*
*
*
*/
// double div to force a linebreak. infomsg , warnmsg are inline-block
const char Common_HTML_SAVE_MSG[] PROGMEM = R"EOF(
<div><div class='infomsg'>&#x2705; Successfully saved!</div></div>
)EOF";
const char Common_HTML_SAVE_MSG_ERR[] PROGMEM = R"EOF(
<div><div class='infomsg'>!! ERROR saving!</div></div>
)EOF";
const char Common_HTML_NEED_RESTART[] PROGMEM = R"EOF(
<div><div class='warnmsg'>&#10071; Restart is required to apply new settings!
<form action='/system/restart' method='post'><input type='hidden' name='confirmed' value='true' />
<input type='submit' value='Restart now' />
</form>
</div></div>
)EOF";

File diff suppressed because it is too large Load diff

View file

@ -1,83 +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
},
"selection_filter": {
"dimensions": true,
"footprints": true,
"graphics": true,
"keepouts": true,
"lockedItems": false,
"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
},
"git": {
"repo_password": "",
"repo_type": "",
"repo_username": "",
"ssh_key": ""
},
"meta": {
"filename": "CanGrow.kicad_prl",
"version": 3
},
"project": {
"files": []
}
}

View file

@ -1,599 +0,0 @@
{
"board": {
"3dviewports": [],
"design_settings": {
"defaults": {
"apply_defaults_to_fp_fields": false,
"apply_defaults_to_fp_shapes": false,
"apply_defaults_to_fp_text": false,
"board_outline_line_width": 0.05,
"copper_line_width": 0.2,
"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.05,
"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.1,
"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.1,
"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.8,
"height": 1.6,
"width": 1.6
},
"silk_line_width": 0.1,
"silk_text_italic": false,
"silk_text_size_h": 1.0,
"silk_text_size_v": 1.0,
"silk_text_thickness": 0.1,
"silk_text_upright": false,
"zones": {
"min_clearance": 0.0
}
},
"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_symbol_mismatch": "warning",
"footprint_type_mismatch": "ignore",
"hole_clearance": "error",
"hole_near_hole": "error",
"holes_co_located": "warning",
"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": "warning",
"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": {
"max_error": 0.005,
"min_clearance": 0.0,
"min_connection": 0.0,
"min_copper_edge_clearance": 0.5,
"min_hole_clearance": 0.25,
"min_hole_to_hole": 0.25,
"min_microvia_diameter": 0.2,
"min_microvia_drill": 0.1,
"min_resolved_spokes": 2,
"min_silk_clearance": 0.0,
"min_text_height": 0.8,
"min_text_thickness": 0.08,
"min_through_hole_diameter": 0.3,
"min_track_width": 0.0,
"min_via_annular_width": 0.1,
"min_via_diameter": 0.5,
"solder_mask_to_copper_clearance": 0.0,
"use_height_for_length_calcs": true
},
"teardrop_options": [
{
"td_onpadsmd": true,
"td_onroundshapesonly": false,
"td_ontrackend": false,
"td_onviapad": true
}
],
"teardrop_parameters": [
{
"td_allow_use_two_tracks": true,
"td_curve_segcount": 0,
"td_height_ratio": 1.0,
"td_length_ratio": 0.5,
"td_maxheight": 2.0,
"td_maxlen": 1.0,
"td_on_pad_in_zone": false,
"td_target_name": "td_round_shape",
"td_width_to_size_filter_ratio": 0.9
},
{
"td_allow_use_two_tracks": true,
"td_curve_segcount": 0,
"td_height_ratio": 1.0,
"td_length_ratio": 0.5,
"td_maxheight": 2.0,
"td_maxlen": 1.0,
"td_on_pad_in_zone": false,
"td_target_name": "td_rect_shape",
"td_width_to_size_filter_ratio": 0.9
},
{
"td_allow_use_two_tracks": true,
"td_curve_segcount": 0,
"td_height_ratio": 1.0,
"td_length_ratio": 0.5,
"td_maxheight": 2.0,
"td_maxlen": 1.0,
"td_on_pad_in_zone": false,
"td_target_name": "td_track_end",
"td_width_to_size_filter_ratio": 0.9
}
],
"track_widths": [
0.0,
2.5,
6.0
],
"tuning_pattern_settings": {
"diff_pair_defaults": {
"corner_radius_percentage": 80,
"corner_style": 1,
"max_amplitude": 1.0,
"min_amplitude": 0.2,
"single_sided": false,
"spacing": 1.0
},
"diff_pair_skew_defaults": {
"corner_radius_percentage": 80,
"corner_style": 1,
"max_amplitude": 1.0,
"min_amplitude": 0.2,
"single_sided": false,
"spacing": 0.6
},
"single_track_defaults": {
"corner_radius_percentage": 80,
"corner_style": 1,
"max_amplitude": 1.0,
"min_amplitude": 0.2,
"single_sided": false,
"spacing": 0.6
}
},
"via_dimensions": [
{
"diameter": 0.0,
"drill": 0.0
}
],
"zones_allow_external_fillets": false
},
"ipc2581": {
"dist": "",
"distpn": "",
"internal_id": "",
"mfg": "",
"mpn": ""
},
"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_to_bus_conflict": "error",
"bus_to_net_conflict": "error",
"conflicting_netclasses": "error",
"different_unit_footprint": "error",
"different_unit_net": "error",
"duplicate_reference": "error",
"duplicate_sheet_names": "error",
"endpoint_off_grid": "warning",
"extra_units": "error",
"global_label_dangling": "warning",
"hier_label_mismatch": "error",
"label_dangling": "error",
"lib_symbol_issues": "warning",
"missing_bidi_pin": "warning",
"missing_input_pin": "warning",
"missing_power_pin": "error",
"missing_unit": "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",
"simulation_model_issue": "ignore",
"unannotated": "error",
"unit_value_mismatch": "error",
"unresolved_variable": "error",
"wire_dangling": "error"
}
},
"libraries": {
"pinned_footprint_libs": [],
"pinned_symbol_libs": []
},
"meta": {
"filename": "CanGrow.kicad_pro",
"version": 1
},
"net_settings": {
"classes": [
{
"bus_width": 12,
"clearance": 0.3,
"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.6,
"via_diameter": 0.6,
"via_drill": 0.3,
"wire_width": 6
}
],
"meta": {
"version": 3
},
"net_colors": null,
"netclass_assignments": null,
"netclass_patterns": []
},
"pcbnew": {
"last_paths": {
"gencad": "",
"idf": "",
"netlist": "",
"plot": "gerber/",
"pos_files": "",
"specctra_dsn": "",
"step": "",
"svg": "",
"vrml": ""
},
"page_layout_descr_file": ""
},
"schematic": {
"annotate_start_num": 0,
"bom_export_filename": "",
"bom_fmt_presets": [],
"bom_fmt_settings": {
"field_delimiter": ",",
"keep_line_breaks": false,
"keep_tabs": false,
"name": "CSV",
"ref_delimiter": ",",
"ref_range_delimiter": "",
"string_delimiter": "\""
},
"bom_presets": [],
"bom_settings": {
"exclude_dnp": false,
"fields_ordered": [
{
"group_by": false,
"label": "Reference",
"name": "Reference",
"show": true
},
{
"group_by": true,
"label": "Value",
"name": "Value",
"show": true
},
{
"group_by": false,
"label": "Datasheet",
"name": "Datasheet",
"show": true
},
{
"group_by": false,
"label": "Footprint",
"name": "Footprint",
"show": true
},
{
"group_by": false,
"label": "Qty",
"name": "${QUANTITY}",
"show": true
},
{
"group_by": true,
"label": "DNP",
"name": "${DNP}",
"show": true
}
],
"filter_string": "",
"group_symbols": true,
"name": "Grouped By Value",
"sort_asc": true,
"sort_field": "Referenz"
},
"connection_grid_size": 50.0,
"drawing": {
"dashed_lines_dash_length_ratio": 12.0,
"dashed_lines_gap_length_ratio": 3.0,
"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,
"operating_point_overlay_i_precision": 3,
"operating_point_overlay_i_range": "~A",
"operating_point_overlay_v_precision": 3,
"operating_point_overlay_v_range": "~V",
"overbar_offset_ratio": 1.23,
"pin_symbol_size": 25.0,
"text_offset_ratio": 0.15
},
"legacy_lib_dir": "",
"legacy_lib_list": [],
"meta": {
"version": 1
},
"net_format_name": "",
"page_layout_descr_file": "",
"plot_directory": "",
"spice_current_sheet_as_root": false,
"spice_external_command": "spice \"%I\"",
"spice_model_current_sheet_as_root": true,
"spice_save_all_currents": false,
"spice_save_all_dissipations": false,
"spice_save_all_voltages": false,
"subpart_first_id": 65,
"subpart_id_separator": 0
},
"sheets": [
[
"42428fce-ab8c-4a80-af19-dee4f0070d36",
"Root"
]
],
"text_variables": {}
}

File diff suppressed because it is too large Load diff

Binary file not shown.

Before

(image error) Size: 134 KiB

Binary file not shown.

Before

(image error) Size: 196 KiB

Binary file not shown.

Before

(image error) Size: 102 KiB

File diff suppressed because it is too large Load diff

View file

@ -1,595 +0,0 @@
{
"board": {
"3dviewports": [],
"design_settings": {
"defaults": {
"apply_defaults_to_fp_fields": false,
"apply_defaults_to_fp_shapes": false,
"apply_defaults_to_fp_text": false,
"board_outline_line_width": 0.05,
"copper_line_width": 0.2,
"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.05,
"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.1,
"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.1,
"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.1,
"silk_text_italic": false,
"silk_text_size_h": 1.0,
"silk_text_size_v": 1.0,
"silk_text_thickness": 0.1,
"silk_text_upright": false,
"zones": {
"min_clearance": 0.0
}
},
"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_symbol_mismatch": "warning",
"footprint_type_mismatch": "ignore",
"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": "warning",
"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": {
"max_error": 0.005,
"min_clearance": 0.0,
"min_connection": 0.0,
"min_copper_edge_clearance": 0.5,
"min_hole_clearance": 0.25,
"min_hole_to_hole": 0.25,
"min_microvia_diameter": 0.2,
"min_microvia_drill": 0.1,
"min_resolved_spokes": 2,
"min_silk_clearance": 0.0,
"min_text_height": 0.8,
"min_text_thickness": 0.08,
"min_through_hole_diameter": 0.3,
"min_track_width": 0.0,
"min_via_annular_width": 0.1,
"min_via_diameter": 0.5,
"solder_mask_to_copper_clearance": 0.0,
"use_height_for_length_calcs": true
},
"teardrop_options": [
{
"td_onpadsmd": true,
"td_onroundshapesonly": false,
"td_ontrackend": false,
"td_onviapad": true
}
],
"teardrop_parameters": [
{
"td_allow_use_two_tracks": true,
"td_curve_segcount": 0,
"td_height_ratio": 1.0,
"td_length_ratio": 0.5,
"td_maxheight": 2.0,
"td_maxlen": 1.0,
"td_on_pad_in_zone": false,
"td_target_name": "td_round_shape",
"td_width_to_size_filter_ratio": 0.9
},
{
"td_allow_use_two_tracks": true,
"td_curve_segcount": 0,
"td_height_ratio": 1.0,
"td_length_ratio": 0.5,
"td_maxheight": 2.0,
"td_maxlen": 1.0,
"td_on_pad_in_zone": false,
"td_target_name": "td_rect_shape",
"td_width_to_size_filter_ratio": 0.9
},
{
"td_allow_use_two_tracks": true,
"td_curve_segcount": 0,
"td_height_ratio": 1.0,
"td_length_ratio": 0.5,
"td_maxheight": 2.0,
"td_maxlen": 1.0,
"td_on_pad_in_zone": false,
"td_target_name": "td_track_end",
"td_width_to_size_filter_ratio": 0.9
}
],
"track_widths": [
0.0
],
"tuning_pattern_settings": {
"diff_pair_defaults": {
"corner_radius_percentage": 80,
"corner_style": 1,
"max_amplitude": 1.0,
"min_amplitude": 0.2,
"single_sided": false,
"spacing": 1.0
},
"diff_pair_skew_defaults": {
"corner_radius_percentage": 80,
"corner_style": 1,
"max_amplitude": 1.0,
"min_amplitude": 0.2,
"single_sided": false,
"spacing": 0.6
},
"single_track_defaults": {
"corner_radius_percentage": 80,
"corner_style": 1,
"max_amplitude": 1.0,
"min_amplitude": 0.2,
"single_sided": false,
"spacing": 0.6
}
},
"via_dimensions": [
{
"diameter": 0.0,
"drill": 0.0
}
],
"zones_allow_external_fillets": false
},
"ipc2581": {
"dist": "",
"distpn": "",
"internal_id": "",
"mfg": "",
"mpn": ""
},
"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_to_bus_conflict": "error",
"bus_to_net_conflict": "error",
"conflicting_netclasses": "error",
"different_unit_footprint": "error",
"different_unit_net": "error",
"duplicate_reference": "error",
"duplicate_sheet_names": "error",
"endpoint_off_grid": "warning",
"extra_units": "error",
"global_label_dangling": "warning",
"hier_label_mismatch": "error",
"label_dangling": "error",
"lib_symbol_issues": "warning",
"missing_bidi_pin": "warning",
"missing_input_pin": "warning",
"missing_power_pin": "error",
"missing_unit": "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",
"simulation_model_issue": "ignore",
"unannotated": "error",
"unit_value_mismatch": "error",
"unresolved_variable": "error",
"wire_dangling": "error"
}
},
"libraries": {
"pinned_footprint_libs": [],
"pinned_symbol_libs": []
},
"meta": {
"filename": "CanGrow.kicad_pro",
"version": 1
},
"net_settings": {
"classes": [
{
"bus_width": 12,
"clearance": 0.3,
"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.6,
"via_diameter": 0.6,
"via_drill": 0.3,
"wire_width": 6
}
],
"meta": {
"version": 3
},
"net_colors": null,
"netclass_assignments": null,
"netclass_patterns": []
},
"pcbnew": {
"last_paths": {
"gencad": "",
"idf": "",
"netlist": "",
"plot": "",
"pos_files": "",
"specctra_dsn": "CanGrow.dsn",
"step": "",
"svg": "",
"vrml": ""
},
"page_layout_descr_file": ""
},
"schematic": {
"annotate_start_num": 0,
"bom_fmt_presets": [],
"bom_fmt_settings": {
"field_delimiter": ",",
"keep_line_breaks": false,
"keep_tabs": false,
"name": "CSV",
"ref_delimiter": ",",
"ref_range_delimiter": "",
"string_delimiter": "\""
},
"bom_presets": [],
"bom_settings": {
"exclude_dnp": false,
"fields_ordered": [
{
"group_by": false,
"label": "Reference",
"name": "Reference",
"show": true
},
{
"group_by": true,
"label": "Value",
"name": "Value",
"show": true
},
{
"group_by": false,
"label": "Datasheet",
"name": "Datasheet",
"show": true
},
{
"group_by": false,
"label": "Footprint",
"name": "Footprint",
"show": true
},
{
"group_by": false,
"label": "Qty",
"name": "${QUANTITY}",
"show": true
},
{
"group_by": true,
"label": "DNP",
"name": "${DNP}",
"show": true
}
],
"filter_string": "",
"group_symbols": true,
"name": "Grouped By Value",
"sort_asc": true,
"sort_field": "Referenz"
},
"connection_grid_size": 50.0,
"drawing": {
"dashed_lines_dash_length_ratio": 12.0,
"dashed_lines_gap_length_ratio": 3.0,
"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,
"operating_point_overlay_i_precision": 3,
"operating_point_overlay_i_range": "~A",
"operating_point_overlay_v_precision": 3,
"operating_point_overlay_v_range": "~V",
"overbar_offset_ratio": 1.23,
"pin_symbol_size": 25.0,
"text_offset_ratio": 0.15
},
"legacy_lib_dir": "",
"legacy_lib_list": [],
"meta": {
"version": 1
},
"net_format_name": "",
"page_layout_descr_file": "",
"plot_directory": "",
"spice_current_sheet_as_root": false,
"spice_external_command": "spice \"%I\"",
"spice_model_current_sheet_as_root": true,
"spice_save_all_currents": false,
"spice_save_all_dissipations": false,
"spice_save_all_voltages": false,
"subpart_first_id": 65,
"subpart_id_separator": 0
},
"sheets": [
[
"42428fce-ab8c-4a80-af19-dee4f0070d36",
"Stammblatt"
]
],
"text_variables": {}
}

Binary file not shown.

View file

@ -1,152 +0,0 @@
(footprint "CanGrow-SolderWire-0.1sqmm_1x01_D0.4mm_OD1mm_0.8mm"
(version 20240108)
(generator "pcbnew")
(generator_version "8.0")
(layer "F.Cu")
(descr "Soldered wire connection, for a single 0.1 mm² wire, basic insulation, conductor diameter 0.4mm, outer diameter 1mm, size source Multi-Contact FLEXI-E 0.1 (https://ec.staubli.com/AcroFiles/Catalogues/TM_Cab-Main-11014119_(en)_hi.pdf), bend radius 3 times outer diameter, generated with kicad-footprint-generator")
(tags "connector wire 0.1sqmm")
(property "Reference" "VCC3.3"
(at 0 -2 0)
(layer "F.SilkS")
(uuid "3ed91354-25bd-4e84-bfb7-188fd65fc8e9")
(effects
(font
(size 0.8 0.8)
(thickness 0.1)
)
)
)
(property "Value" "SolderWire"
(at 0 2 0)
(layer "F.Fab")
(uuid "706134de-5a43-4b0f-a24a-fc10492bb447")
(effects
(font
(size 1 1)
(thickness 0.15)
)
)
)
(property "Footprint" "Connector_Wire:SolderWire-0.1sqmm_1x01_D0.4mm_OD1mm"
(at 0 0 0)
(unlocked yes)
(layer "F.Fab")
(hide yes)
(uuid "3ad71b9a-27a6-4a0e-bdb9-5be60a26b692")
(effects
(font
(size 1.27 1.27)
(thickness 0.15)
)
)
)
(property "Datasheet" ""
(at 0 0 0)
(unlocked yes)
(layer "F.Fab")
(hide yes)
(uuid "dddc3c2c-a4fc-463d-90ad-f465505d4a51")
(effects
(font
(size 1.27 1.27)
(thickness 0.15)
)
)
)
(property "Description" ""
(at 0 0 0)
(unlocked yes)
(layer "F.Fab")
(hide yes)
(uuid "ec7bec08-cf6f-41d0-9924-f484b7198917")
(effects
(font
(size 1.27 1.27)
(thickness 0.15)
)
)
)
(attr exclude_from_pos_files exclude_from_bom)
(fp_line
(start -1.3 -1.3)
(end -1.3 1.3)
(stroke
(width 0.05)
(type solid)
)
(layer "F.CrtYd")
(uuid "e690fe82-f99d-4c33-9eaa-df5fd9925bf4")
)
(fp_line
(start -1.3 1.3)
(end 1.3 1.3)
(stroke
(width 0.05)
(type solid)
)
(layer "F.CrtYd")
(uuid "de44eeda-4e8f-4e38-ab31-bc2514ca1c52")
)
(fp_line
(start 1.3 -1.3)
(end -1.3 -1.3)
(stroke
(width 0.05)
(type solid)
)
(layer "F.CrtYd")
(uuid "a58f350b-223a-4a2b-b385-7221f51f4b68")
)
(fp_line
(start 1.3 1.3)
(end 1.3 -1.3)
(stroke
(width 0.05)
(type solid)
)
(layer "F.CrtYd")
(uuid "3fa8882e-d3e5-49d9-8ba4-cec9106635a2")
)
(fp_circle
(center 0 0)
(end 0.5 0)
(stroke
(width 0.1)
(type solid)
)
(fill none)
(layer "F.Fab")
(uuid "fc8bd121-be08-4d5a-8538-8d1003ba0cd8")
)
(fp_text user "${REFERENCE}"
(at 0 0 0)
(layer "F.Fab")
(uuid "dbe65801-e081-4887-bdbd-f547de25cfc2")
(effects
(font
(size 0.25 0.25)
(thickness 0.04)
)
)
)
(pad "1" thru_hole roundrect
(at 0 0)
(size 1.6 1.6)
(drill 0.8)
(layers "*.Cu" "*.Mask")
(remove_unused_layers no)
(roundrect_rratio 0.15625)
(uuid "84abadc5-3aca-4364-bb7c-4e5aca89770a")
)
(model "${KICAD8_3DMODEL_DIR}/Connector_Wire.3dshapes/SolderWire-0.1sqmm_1x01_D0.4mm_OD1mm.wrl"
(offset
(xyz 0 0 0)
)
(scale
(xyz 1 1 1)
)
(rotate
(xyz 0 0 0)
)
)
)

View file

@ -1,247 +0,0 @@
(kicad_symbol_lib
(version 20231120)
(generator "kicad_symbol_editor")
(generator_version "8.0")
(symbol "I2C_Conn_01x04_Pin"
(pin_names
(offset 1.016) hide)
(exclude_from_sim no)
(in_bom yes)
(on_board yes)
(property "Reference" "J"
(at 0 5.08 0)
(effects
(font
(size 1.27 1.27)
)
)
)
(property "Value" "I2C_Conn_01x04_Pin"
(at 0 -7.62 0)
(effects
(font
(size 1.27 1.27)
)
)
)
(property "Footprint" ""
(at 0 0 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "Datasheet" "~"
(at 0 0 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "Description" "Generic connector, single row, 01x04, script generated"
(at 0 0 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "ki_locked" ""
(at 0 0 0)
(effects
(font
(size 1.27 1.27)
)
)
)
(property "ki_keywords" "connector"
(at 0 0 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "ki_fp_filters" "Connector*:*_1x??_*"
(at 0 0 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(symbol "I2C_Conn_01x04_Pin_1_1"
(polyline
(pts
(xy 1.27 -5.08) (xy 0.8636 -5.08)
)
(stroke
(width 0.1524)
(type default)
)
(fill
(type none)
)
)
(polyline
(pts
(xy 1.27 -2.54) (xy 0.8636 -2.54)
)
(stroke
(width 0.1524)
(type default)
)
(fill
(type none)
)
)
(polyline
(pts
(xy 1.27 0) (xy 0.8636 0)
)
(stroke
(width 0.1524)
(type default)
)
(fill
(type none)
)
)
(polyline
(pts
(xy 1.27 2.54) (xy 0.8636 2.54)
)
(stroke
(width 0.1524)
(type default)
)
(fill
(type none)
)
)
(rectangle
(start 0.8636 -4.953)
(end 0 -5.207)
(stroke
(width 0.1524)
(type default)
)
(fill
(type outline)
)
)
(rectangle
(start 0.8636 -2.413)
(end 0 -2.667)
(stroke
(width 0.1524)
(type default)
)
(fill
(type outline)
)
)
(rectangle
(start 0.8636 0.127)
(end 0 -0.127)
(stroke
(width 0.1524)
(type default)
)
(fill
(type outline)
)
)
(rectangle
(start 0.8636 2.667)
(end 0 2.413)
(stroke
(width 0.1524)
(type default)
)
(fill
(type outline)
)
)
(pin passive line
(at 5.08 2.54 180)
(length 3.81)
(name "GND"
(effects
(font
(size 1.27 1.27)
)
)
)
(number "1"
(effects
(font
(size 1.27 1.27)
)
)
)
)
(pin passive line
(at 5.08 0 180)
(length 3.81)
(name "VCC"
(effects
(font
(size 1.27 1.27)
)
)
)
(number "2"
(effects
(font
(size 1.27 1.27)
)
)
)
)
(pin passive line
(at 5.08 -2.54 180)
(length 3.81)
(name "SDL"
(effects
(font
(size 1.27 1.27)
)
)
)
(number "3"
(effects
(font
(size 1.27 1.27)
)
)
)
)
(pin passive line
(at 5.08 -5.08 180)
(length 3.81)
(name "SDA"
(effects
(font
(size 1.27 1.27)
)
)
)
(number "4"
(effects
(font
(size 1.27 1.27)
)
)
)
)
)
)
)

View file

@ -1,220 +0,0 @@
(kicad_symbol_lib
(version 20231120)
(generator "kicad_symbol_editor")
(generator_version "8.0")
(symbol "MP1584EN"
(exclude_from_sim no)
(in_bom yes)
(on_board yes)
(property "Reference" "A"
(at -0.508 0 0)
(effects
(font
(size 1.27 1.27)
)
)
)
(property "Value" ""
(at 0 0 0)
(effects
(font
(size 1.27 1.27)
)
)
)
(property "Footprint" "MP1584EN:MP1584EN_Module"
(at -8.636 -20.828 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "Datasheet" ""
(at 0 0 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(property "Description" ""
(at 0 0 0)
(effects
(font
(size 1.27 1.27)
)
(hide yes)
)
)
(symbol "MP1584EN_0_1"
(rectangle
(start -20.32 -1.27)
(end 0 -16.51)
(stroke
(width 0.254)
(type default)
)
(fill
(type background)
)
)
)
(symbol "MP1584EN_1_1"
(text "MP1584EN Module"
(at -9.906 -18.288 0)
(effects
(font
(size 1.27 1.27)
)
)
)
(pin power_in line
(at -22.86 -6.35 0)
(length 2.54)
(name "IN+"
(effects
(font
(size 1.27 1.27)
)
)
)
(number "1"
(effects
(font
(size 1.27 1.27)
)
)
)
)
(pin power_in line
(at -22.86 -3.81 0)
(length 2.54)
(name "IN+"
(effects
(font
(size 1.27 1.27)
)
)
)
(number "1"
(effects
(font
(size 1.27 1.27)
)
)
)
)
(pin power_in line
(at -22.86 -13.97 0)
(length 2.54)
(name "IN-"
(effects
(font
(size 1.27 1.27)
)
)
)
(number "2"
(effects
(font
(size 1.27 1.27)
)
)
)
)
(pin power_in line
(at -22.86 -11.43 0)
(length 2.54)
(name "IN-"
(effects
(font
(size 1.27 1.27)
)
)
)
(number "2"
(effects
(font
(size 1.27 1.27)
)
)
)
)
(pin power_out line
(at 2.54 -6.35 180)
(length 2.54)
(name "OUT+"
(effects
(font
(size 1.27 1.27)
)
)
)
(number "3"
(effects
(font
(size 1.27 1.27)
)
)
)
)
(pin power_out line
(at 2.54 -3.81 180)
(length 2.54)
(name "OUT+"
(effects
(font
(size 1.27 1.27)
)
)
)
(number "3"
(effects
(font
(size 1.27 1.27)
)
)
)
)
(pin power_out line
(at 2.54 -13.97 180)
(length 2.54)
(name "OUT-"
(effects
(font
(size 1.27 1.27)
)
)
)
(number "4"
(effects
(font
(size 1.27 1.27)
)
)
)
)
(pin power_out line
(at 2.54 -11.43 180)
(length 2.54)
(name "OUT-"
(effects
(font
(size 1.27 1.27)
)
)
)
(number "4"
(effects
(font
(size 1.27 1.27)
)
)
)
)
)
)
)

View file

@ -1,622 +0,0 @@
(footprint "MP1584EN_Module"
(version 20240108)
(generator "pcbnew")
(generator_version "8.0")
(layer "F.Cu")
(tags "Power 5V 3A")
(property "Reference" "U3"
(at 0 7.3152 180)
(layer "F.SilkS")
(hide yes)
(uuid "5401f2b9-08c4-433b-a536-95365a9d5b27")
(effects
(font
(size 1 1)
(thickness 0.15)
)
)
)
(property "Value" "SRT_Power_3A"
(at -0.3556 5.1816 0)
(layer "F.Fab")
(hide yes)
(uuid "3aca6114-af0d-478b-80ab-52d5719c58c0")
(effects
(font
(size 1 1)
(thickness 0.15)
)
)
)
(property "Footprint" ""
(at 0 0 0)
(unlocked yes)
(layer "F.Fab")
(hide yes)
(uuid "f5daedb9-8cff-461c-9d40-0b502c690d9b")
(effects
(font
(size 1.27 1.27)
(thickness 0.15)
)
)
)
(property "Datasheet" ""
(at 0 0 0)
(unlocked yes)
(layer "F.Fab")
(hide yes)
(uuid "598a64f4-0796-4fe9-b7be-c2493ea0b8fb")
(effects
(font
(size 1.27 1.27)
(thickness 0.15)
)
)
)
(property "Description" ""
(at 0 0 0)
(unlocked yes)
(layer "F.Fab")
(hide yes)
(uuid "8e81ccc2-3008-4749-80d7-e19277403287")
(effects
(font
(size 1.27 1.27)
(thickness 0.15)
)
)
)
(attr through_hole)
(fp_line
(start -10.2122 -6.0076)
(end -10.2122 10.1214)
(stroke
(width 0.12)
(type solid)
)
(layer "F.SilkS")
(uuid "6292f9f0-b20e-4f1e-990a-0b11f0bae5e2")
)
(fp_line
(start -10.2122 10.1214)
(end 11.1238 10.1214)
(stroke
(width 0.12)
(type solid)
)
(layer "F.SilkS")
(uuid "7b8db049-a75b-4c09-b1c9-de8a147005c4")
)
(fp_line
(start -6.453 -0.3942)
(end -0.357 -0.3942)
(stroke
(width 0.12)
(type solid)
)
(layer "F.SilkS")
(uuid "c4ed20b9-5f3d-4741-9180-be3de36b2638")
)
(fp_line
(start -6.453 4.0762)
(end -6.453 -0.3942)
(stroke
(width 0.12)
(type solid)
)
(layer "F.SilkS")
(uuid "d7e1ea82-a28a-4e4c-8051-86a68fea340c")
)
(fp_line
(start -5.3354 -4.2804)
(end -1.9064 -4.2804)
(stroke
(width 0.12)
(type solid)
)
(layer "F.SilkS")
(uuid "06328989-1b0f-46d7-883b-033109387c94")
)
(fp_line
(start -5.3354 -3.0104)
(end -5.3354 -4.2804)
(stroke
(width 0.12)
(type solid)
)
(layer "F.SilkS")
(uuid "8d77e0eb-1044-47c3-83fb-eeb0a96fae5d")
)
(fp_line
(start -1.9064 -4.2804)
(end -1.9064 -3.0104)
(stroke
(width 0.12)
(type solid)
)
(layer "F.SilkS")
(uuid "aacf448e-591d-467e-9b05-308beed279bf")
)
(fp_line
(start -1.9064 -3.0104)
(end -5.3354 -3.0104)
(stroke
(width 0.12)
(type solid)
)
(layer "F.SilkS")
(uuid "1a4d146d-43a7-4082-9120-8896a6302895")
)
(fp_line
(start -0.357 4.0762)
(end -6.453 4.0762)
(stroke
(width 0.12)
(type solid)
)
(layer "F.SilkS")
(uuid "28f1cc66-eb55-47b0-8a8f-d7e1518711f4")
)
(fp_line
(start -0.357 4.0762)
(end -0.357 -0.3942)
(stroke
(width 0.12)
(type solid)
)
(layer "F.SilkS")
(uuid "6820cfba-dad3-4b8f-888f-85dbe2540377")
)
(fp_line
(start 1.3194 -1.969)
(end 1.3194 4.6858)
(stroke
(width 0.12)
(type solid)
)
(layer "F.SilkS")
(uuid "6a862895-3bf4-4887-9dab-db5334a221bc")
)
(fp_line
(start 1.3194 -1.969)
(end 7.9742 -1.969)
(stroke
(width 0.12)
(type solid)
)
(layer "F.SilkS")
(uuid "79debc5c-9a35-496c-8f29-014030f144a4")
)
(fp_line
(start 2.8942 0.8885)
(end 5.5231 0.8885)
(stroke
(width 0.12)
(type solid)
)
(layer "F.SilkS")
(uuid "84cd7a4d-d683-4fbc-825f-665c5b0f0758")
)
(fp_line
(start 2.8942 1.5235)
(end 2.8942 0.8885)
(stroke
(width 0.12)
(type solid)
)
(layer "F.SilkS")
(uuid "4e52b40e-83c8-4d07-95ff-b90fa1783618")
)
(fp_line
(start 2.8942 1.5235)
(end 5.5231 1.5235)
(stroke
(width 0.12)
(type solid)
)
(layer "F.SilkS")
(uuid "a399ee1e-f3f5-4636-9f46-aa9b47e0db94")
)
(fp_line
(start 5.5231 0.19)
(end 6.7931 1.206)
(stroke
(width 0.12)
(type solid)
)
(layer "F.SilkS")
(uuid "974ab6af-0377-40af-977c-a13764cf3b42")
)
(fp_line
(start 5.5231 0.8885)
(end 5.5231 0.19)
(stroke
(width 0.12)
(type solid)
)
(layer "F.SilkS")
(uuid "a43ae26f-5b7d-4a6a-a076-714c678ef718")
)
(fp_line
(start 5.5231 2.2855)
(end 5.5231 1.5235)
(stroke
(width 0.12)
(type solid)
)
(layer "F.SilkS")
(uuid "cc8ed141-1479-47bc-b57c-ac3d7de64f29")
)
(fp_line
(start 6.7931 1.206)
(end 5.5231 2.2855)
(stroke
(width 0.12)
(type solid)
)
(layer "F.SilkS")
(uuid "59d9abe5-a32f-469b-9fd6-08593b95afde")
)
(fp_line
(start 7.9742 -1.969)
(end 7.9742 4.6858)
(stroke
(width 0.12)
(type solid)
)
(layer "F.SilkS")
(uuid "73f5a41f-61af-42a7-bacf-c58d7968f53a")
)
(fp_line
(start 7.9742 4.6858)
(end 1.3194 4.6858)
(stroke
(width 0.12)
(type solid)
)
(layer "F.SilkS")
(uuid "9ec91a9f-ceda-4f7c-b422-f6a162037a63")
)
(fp_line
(start 11.1238 -6.0076)
(end -10.2122 -6.0076)
(stroke
(width 0.12)
(type solid)
)
(layer "F.SilkS")
(uuid "ed6fa262-c61a-4bb7-90b5-f3b3ee971658")
)
(fp_line
(start 11.1238 10.1214)
(end 11.1238 -6.0076)
(stroke
(width 0.12)
(type solid)
)
(layer "F.SilkS")
(uuid "0251e69d-8ab4-482d-b741-3238b0fef811")
)
(fp_circle
(center -7.1896 -3.3787)
(end -6.6181 -2.8707)
(stroke
(width 0.12)
(type solid)
)
(fill none)
(layer "F.SilkS")
(uuid "55702fe3-b28e-4e80-86d3-f64c403681d1")
)
(fp_circle
(center -7.1388 7.302)
(end -6.5673 7.81)
(stroke
(width 0.12)
(type solid)
)
(fill none)
(layer "F.SilkS")
(uuid "0ffe4b1a-c314-4a3c-8456-b73925f03b28")
)
(fp_circle
(center 7.39 -3.3787)
(end 7.9615 -2.8707)
(stroke
(width 0.12)
(type solid)
)
(fill none)
(layer "F.SilkS")
(uuid "82347fd5-ab01-478d-bf06-2837edd3b9c5")
)
(fp_circle
(center 7.4916 7.2766)
(end 8.0631 7.7846)
(stroke
(width 0.12)
(type solid)
)
(fill none)
(layer "F.SilkS")
(uuid "f74d6220-edbd-4a39-a810-7cf19016933f")
)
(fp_poly
(pts
(xy -5.7418 -1.0546) (xy -5.3354 -1.0546) (xy -5.3354 -0.445) (xy -5.7418 -0.445)
)
(stroke
(width 0.1)
(type solid)
)
(fill solid)
(layer "F.SilkS")
(uuid "80ff133a-8874-4f0a-8c9e-250633a8855a")
)
(fp_poly
(pts
(xy -5.7418 4.127) (xy -5.3354 4.127) (xy -5.3354 4.7366) (xy -5.7418 4.7366)
)
(stroke
(width 0.1)
(type solid)
)
(fill solid)
(layer "F.SilkS")
(uuid "6e08c3a4-3092-400f-9a55-4e8081226741")
)
(fp_poly
(pts
(xy -4.2686 -1.0546) (xy -3.8622 -1.0546) (xy -3.8622 -0.445) (xy -4.2686 -0.445)
)
(stroke
(width 0.1)
(type solid)
)
(fill solid)
(layer "F.SilkS")
(uuid "6a96c3fa-8827-498f-9c6c-ffb212b62a93")
)
(fp_poly
(pts
(xy -4.2686 4.127) (xy -3.8622 4.127) (xy -3.8622 4.7366) (xy -4.2686 4.7366)
)
(stroke
(width 0.1)
(type solid)
)
(fill solid)
(layer "F.SilkS")
(uuid "0886a33b-f340-44bf-9de5-f44938efcb54")
)
(fp_poly
(pts
(xy -2.7954 -1.0546) (xy -2.389 -1.0546) (xy -2.389 -0.445) (xy -2.7954 -0.445)
)
(stroke
(width 0.1)
(type solid)
)
(fill solid)
(layer "F.SilkS")
(uuid "51e3d89a-8455-4f58-ad05-c15d024e9c54")
)
(fp_poly
(pts
(xy -2.7954 4.127) (xy -2.389 4.127) (xy -2.389 4.7366) (xy -2.7954 4.7366)
)
(stroke
(width 0.1)
(type solid)
)
(fill solid)
(layer "F.SilkS")
(uuid "5514ba93-c636-4e40-855a-d8123eaad1aa")
)
(fp_poly
(pts
(xy -1.2714 -1.0546) (xy -0.865 -1.0546) (xy -0.865 -0.445) (xy -1.2714 -0.445)
)
(stroke
(width 0.1)
(type solid)
)
(fill solid)
(layer "F.SilkS")
(uuid "d6b9bc27-cda7-4d8b-ad26-7b36a48ecfbd")
)
(fp_poly
(pts
(xy -1.2714 4.127) (xy -0.865 4.127) (xy -0.865 4.7366) (xy -1.2714 4.7366)
)
(stroke
(width 0.1)
(type solid)
)
(fill solid)
(layer "F.SilkS")
(uuid "3fef23e2-4bcd-4924-97f4-7575ce7994b1")
)
(fp_line
(start -10.39 -6.16)
(end -10.39 10.2992)
(stroke
(width 0.12)
(type solid)
)
(layer "F.Fab")
(uuid "bc1192d1-b770-4e65-bd6d-2beed31fb275")
)
(fp_line
(start -10.39 -6.16)
(end 11.3016 -6.1854)
(stroke
(width 0.12)
(type solid)
)
(layer "F.Fab")
(uuid "ac2ec45c-be62-4db0-88df-ef12d1f24714")
)
(fp_line
(start -10.39 10.2992)
(end 11.3016 10.2992)
(stroke
(width 0.12)
(type solid)
)
(layer "F.Fab")
(uuid "90a38e33-4119-4758-ac3b-323f8c73139c")
)
(fp_line
(start 11.3016 -6.1854)
(end 11.3016 10.2992)
(stroke
(width 0.12)
(type solid)
)
(layer "F.Fab")
(uuid "22193204-ae0b-4462-95d6-f226e27ae968")
)
(fp_text user "-"
(at 7.39 -3.4422 0)
(layer "F.SilkS")
(uuid "0bec24d9-5468-4c4b-96d3-ef96e25202cf")
(effects
(font
(size 1 1)
(thickness 0.15)
)
)
)
(fp_text user "-"
(at -7.1896 -3.4422 0)
(layer "F.SilkS")
(uuid "0c9a77a8-8422-401b-a5a1-b40da4e203c0")
(effects
(font
(size 1 1)
(thickness 0.15)
)
)
)
(fp_text user "VOUT"
(at 9.7776 1.5362 -90)
(layer "F.SilkS")
(uuid "3c0d7914-45a5-4bbb-8f5b-40ede46c7d70")
(effects
(font
(size 1 1)
(thickness 0.15)
)
)
)
(fp_text user "+"
(at -7.1388 7.2385 0)
(layer "F.SilkS")
(uuid "4145e214-17c8-4e57-9147-ea318329ca66")
(effects
(font
(size 1 1)
(thickness 0.15)
)
)
)
(fp_text user "4.5-28V"
(at -8.7898 1.7902 -90)
(layer "F.SilkS")
(uuid "9e9858d1-9320-4297-aaee-1e9bd601ead9")
(effects
(font
(size 1 1)
(thickness 0.15)
)
)
)
(fp_text user "+"
(at 7.4916 7.2131 0)
(layer "F.SilkS")
(uuid "c1d9d6ad-e0e9-47c6-989c-71feb1923647")
(effects
(font
(size 1 1)
(thickness 0.15)
)
)
)
(pad "1" thru_hole rect
(at -9.1962 6.1704 90)
(size 1.6 1.6)
(drill 0.8)
(layers "*.Cu" "*.Mask")
(remove_unused_layers no)
(uuid "7be89935-c19e-4baa-904a-69452ec23d23")
)
(pad "1" thru_hole rect
(at -9.1962 8.7104 90)
(size 1.6 1.6)
(drill 0.8)
(layers "*.Cu" "*.Mask")
(remove_unused_layers no)
(uuid "8f4284a4-3768-4b1e-9962-d551ccf57e63")
)
(pad "2" thru_hole oval
(at -9.2216 -2.1976 90)
(size 1.6 1.6)
(drill 0.8)
(layers "*.Cu" "*.Mask")
(remove_unused_layers no)
(uuid "1f87148a-c7c2-483f-b799-2ff5e01ff488")
)
(pad "2" thru_hole oval
(at -9.1962 -4.7376 90)
(size 1.6 1.6)
(drill 0.8)
(layers "*.Cu" "*.Mask")
(remove_unused_layers no)
(uuid "bf2458a2-a107-48c3-bb88-9e881f49434c")
)
(pad "3" thru_hole rect
(at 9.5998 6.1844 90)
(size 1.6 1.6)
(drill 0.8)
(layers "*.Cu" "*.Mask")
(remove_unused_layers no)
(uuid "69cb71b1-224d-4fe7-8b14-ee9b1da593fb")
)
(pad "3" thru_hole rect
(at 9.5998 8.7244 90)
(size 1.6 1.6)
(drill 0.8)
(layers "*.Cu" "*.Mask")
(remove_unused_layers no)
(uuid "39b44515-755a-4a18-9e94-5d0b3431f548")
)
(pad "4" thru_hole oval
(at 9.7014 -2.1976 90)
(size 1.6 1.6)
(drill 0.8)
(layers "*.Cu" "*.Mask")
(remove_unused_layers no)
(uuid "6af0a541-44c2-41dc-9c4e-37db310350d6")
)
(pad "4" thru_hole oval
(at 9.7268 -4.7376 90)
(size 1.6 1.6)
(drill 0.8)
(layers "*.Cu" "*.Mask")
(remove_unused_layers no)
(uuid "204e6602-d630-4983-8e4d-fb59a5c65572")
)
(model "${KISYS3DMOD}/Module.3dshapes/Arduino_UNO_R2.wrl"
(offset
(xyz 0 0 0)
)
(scale
(xyz 1 1 1)
)
(rotate
(xyz 0 0 0)
)
)
)

View file

@ -1,4 +0,0 @@
# MP1584EN Module KiCad Symbol and Footprint
This is a fork of https://github.com/kotakomputer/MP1584EN/ which I modified to my needs.
As i could not point out where the schematic Symbol for the MP1584EN module should be, I drew a very very simple by my own.

Binary file not shown.

Before

(image error) Size: 16 KiB

Binary file not shown.

Before

(image error) Size: 39 KiB

View file

@ -1,3 +0,0 @@
EESchema-DOCLIB Version 2.0
#
#End Doc Library

File diff suppressed because it is too large Load diff

View file

@ -1,388 +0,0 @@
G04 #@! TF.GenerationSoftware,KiCad,Pcbnew,8.0.5*
G04 #@! TF.CreationDate,2024-09-25T02:09:32+02:00*
G04 #@! TF.ProjectId,CanGrow,43616e47-726f-4772-9e6b-696361645f70,rev?*
G04 #@! TF.SameCoordinates,Original*
G04 #@! TF.FileFunction,Soldermask,Bot*
G04 #@! TF.FilePolarity,Negative*
%FSLAX46Y46*%
G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)*
G04 Created by KiCad (PCBNEW 8.0.5) date 2024-09-25 02:09:32*
%MOMM*%
%LPD*%
G01*
G04 APERTURE LIST*
G04 Aperture macros list*
%AMRoundRect*
0 Rectangle with rounded corners*
0 $1 Rounding radius*
0 $2 $3 $4 $5 $6 $7 $8 $9 X,Y pos of 4 corners*
0 Add a 4 corners polygon primitive as box body*
4,1,4,$2,$3,$4,$5,$6,$7,$8,$9,$2,$3,0*
0 Add four circle primitives for the rounded corners*
1,1,$1+$1,$2,$3*
1,1,$1+$1,$4,$5*
1,1,$1+$1,$6,$7*
1,1,$1+$1,$8,$9*
0 Add four rect primitives between the rounded corners*
20,1,$1+$1,$2,$3,$4,$5,0*
20,1,$1+$1,$4,$5,$6,$7,0*
20,1,$1+$1,$6,$7,$8,$9,0*
20,1,$1+$1,$8,$9,$2,$3,0*%
G04 Aperture macros list end*
%ADD10R,1.600000X1.600000*%
%ADD11C,1.600000*%
%ADD12R,1.800000X1.100000*%
%ADD13RoundRect,0.275000X-0.625000X0.275000X-0.625000X-0.275000X0.625000X-0.275000X0.625000X0.275000X0*%
%ADD14O,1.600000X1.600000*%
%ADD15R,2.000000X1.905000*%
%ADD16O,2.000000X1.905000*%
%ADD17R,1.800000X1.800000*%
%ADD18C,1.800000*%
%ADD19RoundRect,1.500000X1.500000X1.500000X-1.500000X1.500000X-1.500000X-1.500000X1.500000X-1.500000X0*%
%ADD20C,6.000000*%
%ADD21C,5.600000*%
%ADD22O,1.800000X1.800000*%
%ADD23O,3.800000X8.000000*%
%ADD24R,1.700000X1.700000*%
%ADD25O,1.700000X1.700000*%
%ADD26R,2.000000X2.000000*%
%ADD27O,2.000000X1.600000*%
%ADD28RoundRect,0.250000X-0.550000X-0.550000X0.550000X-0.550000X0.550000X0.550000X-0.550000X0.550000X0*%
%ADD29RoundRect,0.275000X0.625000X-0.275000X0.625000X0.275000X-0.625000X0.275000X-0.625000X-0.275000X0*%
%ADD30C,1.100000*%
%ADD31R,1.730000X2.030000*%
%ADD32O,1.730000X2.030000*%
%ADD33RoundRect,0.250000X0.550000X0.550000X-0.550000X0.550000X-0.550000X-0.550000X0.550000X-0.550000X0*%
G04 APERTURE END LIST*
D10*
X156600000Y-108750000D03*
D11*
X156600000Y-111250000D03*
D12*
X127400000Y-42690000D03*
D13*
X129470000Y-43960000D03*
X127400000Y-45230000D03*
D11*
X117000000Y-42420000D03*
D14*
X106840000Y-42420000D03*
D15*
X138430000Y-83750000D03*
D16*
X138430000Y-81210000D03*
X138430000Y-78670000D03*
D15*
X129800000Y-35150000D03*
D16*
X129800000Y-32610000D03*
X129800000Y-30070000D03*
D11*
X139900000Y-53320000D03*
D14*
X139900000Y-63480000D03*
D10*
X130748000Y-108846000D03*
X130748000Y-111386000D03*
D14*
X130722600Y-100478000D03*
X130748000Y-97938000D03*
D10*
X149544000Y-108860000D03*
X149544000Y-111400000D03*
D14*
X149645600Y-100478000D03*
X149671000Y-97938000D03*
D11*
X177750000Y-92250000D03*
D14*
X187910000Y-92250000D03*
D17*
X122231000Y-81220000D03*
D18*
X122231000Y-83760000D03*
D19*
X113930000Y-59700000D03*
D20*
X106730000Y-59700000D03*
D17*
X122250000Y-102750000D03*
D18*
X122250000Y-105290000D03*
D11*
X195500000Y-29200000D03*
D14*
X185340000Y-29200000D03*
D12*
X127430000Y-91400000D03*
D13*
X129500000Y-92670000D03*
X127430000Y-93940000D03*
D21*
X87000000Y-22750000D03*
D11*
X116855000Y-69500000D03*
D14*
X106695000Y-69500000D03*
D17*
X106770000Y-87720000D03*
D22*
X116930000Y-87720000D03*
D11*
X133000000Y-94000000D03*
D14*
X133000000Y-83840000D03*
D11*
X195500000Y-52900000D03*
D14*
X185340000Y-52900000D03*
D11*
X198180000Y-71050000D03*
D14*
X188020000Y-71050000D03*
D21*
X203000000Y-22750000D03*
D11*
X139920000Y-66500000D03*
D14*
X150080000Y-66500000D03*
D11*
X116910000Y-100250000D03*
D14*
X106750000Y-100250000D03*
D11*
X153100000Y-78010000D03*
D14*
X153100000Y-67850000D03*
D12*
X121500000Y-69860000D03*
D13*
X123570000Y-71130000D03*
X121500000Y-72400000D03*
D12*
X127400000Y-69860000D03*
D13*
X129470000Y-71130000D03*
X127400000Y-72400000D03*
D23*
X96500000Y-97650000D03*
X96500000Y-106950000D03*
D24*
X195485000Y-60100000D03*
D25*
X192945000Y-60100000D03*
D19*
X113930000Y-81220000D03*
D20*
X106730000Y-81220000D03*
D26*
X150100000Y-27460000D03*
D27*
X150100000Y-30000000D03*
X150100000Y-32540000D03*
X150100000Y-35080000D03*
X150100000Y-37620000D03*
X150100000Y-40160000D03*
X150100000Y-42700000D03*
X150100000Y-45240000D03*
X172960000Y-45240000D03*
X172960000Y-42700000D03*
X172960000Y-40160000D03*
X172960000Y-37620000D03*
X172960000Y-35080000D03*
X172960000Y-32540000D03*
X172960000Y-30000000D03*
X172960000Y-27460000D03*
D24*
X195400000Y-77000000D03*
D25*
X192860000Y-77000000D03*
X190320000Y-77000000D03*
D24*
X195485000Y-38630000D03*
D25*
X192945000Y-38630000D03*
X190405000Y-38630000D03*
X187865000Y-38630000D03*
D28*
X131800000Y-50800000D03*
D17*
X174830000Y-82250000D03*
D22*
X174830000Y-92410000D03*
D17*
X122300000Y-32620000D03*
D18*
X122300000Y-35160000D03*
D24*
X195600000Y-99250000D03*
D25*
X193060000Y-99250000D03*
X190520000Y-99250000D03*
D11*
X101455112Y-94200000D03*
X103955112Y-94200000D03*
D24*
X195490000Y-47890000D03*
D25*
X192950000Y-47890000D03*
X190410000Y-47890000D03*
X187870000Y-47890000D03*
D11*
X133000000Y-45310000D03*
D14*
X133000000Y-35150000D03*
D12*
X138400000Y-45170000D03*
D29*
X136330000Y-43900000D03*
X138400000Y-42630000D03*
D24*
X195485000Y-33980000D03*
D25*
X192945000Y-33980000D03*
X190405000Y-33980000D03*
X187865000Y-33980000D03*
D28*
X156500000Y-67860000D03*
D23*
X96500000Y-42450000D03*
X96500000Y-33150000D03*
X96500000Y-64000000D03*
X96500000Y-54700000D03*
D15*
X129680000Y-62250000D03*
D16*
X129680000Y-59710000D03*
X129680000Y-57170000D03*
D28*
X188000000Y-67900000D03*
D11*
X187670000Y-88250000D03*
D14*
X197830000Y-88250000D03*
D11*
X102500000Y-66200000D03*
X102500000Y-68700000D03*
X116900000Y-95000000D03*
D14*
X106740000Y-95000000D03*
D10*
X101500000Y-90100000D03*
D11*
X104000000Y-90100000D03*
X133000000Y-72460000D03*
D14*
X133000000Y-62300000D03*
D15*
X129680000Y-83750000D03*
D16*
X129680000Y-81210000D03*
X129680000Y-78670000D03*
D12*
X138370000Y-72370000D03*
D29*
X136300000Y-71100000D03*
X138370000Y-69830000D03*
D17*
X106760000Y-66240000D03*
D22*
X116920000Y-66240000D03*
D21*
X87000000Y-117250000D03*
D11*
X146480000Y-32600000D03*
D14*
X136320000Y-32600000D03*
D30*
X108900000Y-48620000D03*
D31*
X103820000Y-50780000D03*
D32*
X106360000Y-50780000D03*
X108900000Y-50780000D03*
X111440000Y-50780000D03*
D11*
X181830000Y-85750000D03*
X181830000Y-88250000D03*
D10*
X123600000Y-108750000D03*
D11*
X123600000Y-111250000D03*
D17*
X106870000Y-39160000D03*
D22*
X117030000Y-39160000D03*
D11*
X136325000Y-39600000D03*
D14*
X146485000Y-39600000D03*
D11*
X117000000Y-46000000D03*
D14*
X106840000Y-46000000D03*
D24*
X195485000Y-43250000D03*
D25*
X192945000Y-43250000D03*
X190405000Y-43250000D03*
X187865000Y-43250000D03*
D11*
X116860000Y-73500000D03*
D14*
X106700000Y-73500000D03*
D19*
X113990000Y-108095000D03*
D20*
X106790000Y-108095000D03*
D11*
X116930000Y-90980000D03*
D14*
X106770000Y-90980000D03*
D17*
X170830000Y-82250000D03*
D22*
X170830000Y-92410000D03*
D23*
X96500000Y-85450000D03*
X96500000Y-76150000D03*
D12*
X121500000Y-42690000D03*
D13*
X123570000Y-43960000D03*
X121500000Y-45230000D03*
D12*
X138400000Y-93900000D03*
D29*
X136330000Y-92630000D03*
X138400000Y-91360000D03*
D12*
X121490000Y-91400000D03*
D13*
X123560000Y-92670000D03*
X121490000Y-93940000D03*
D19*
X114000000Y-32610000D03*
D20*
X106800000Y-32610000D03*
D21*
X203000000Y-117250000D03*
D24*
X177250000Y-42790000D03*
D25*
X177250000Y-40250000D03*
D11*
X144200000Y-74300000D03*
D14*
X144200000Y-84460000D03*
D17*
X122230000Y-59710000D03*
D18*
X122230000Y-62250000D03*
D11*
X197830000Y-84250000D03*
D14*
X187670000Y-84250000D03*
D33*
X169750000Y-50800000D03*
M02*

View file

@ -1,15 +0,0 @@
G04 #@! TF.GenerationSoftware,KiCad,Pcbnew,8.0.5*
G04 #@! TF.CreationDate,2024-09-25T02:09:32+02:00*
G04 #@! TF.ProjectId,CanGrow,43616e47-726f-4772-9e6b-696361645f70,rev?*
G04 #@! TF.SameCoordinates,Original*
G04 #@! TF.FileFunction,Paste,Bot*
G04 #@! TF.FilePolarity,Positive*
%FSLAX46Y46*%
G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)*
G04 Created by KiCad (PCBNEW 8.0.5) date 2024-09-25 02:09:32*
%MOMM*%
%LPD*%
G01*
G04 APERTURE LIST*
G04 APERTURE END LIST*
M02*

View file

@ -1,472 +0,0 @@
G04 #@! TF.GenerationSoftware,KiCad,Pcbnew,8.0.5*
G04 #@! TF.CreationDate,2024-09-25T02:09:32+02:00*
G04 #@! TF.ProjectId,CanGrow,43616e47-726f-4772-9e6b-696361645f70,rev?*
G04 #@! TF.SameCoordinates,Original*
G04 #@! TF.FileFunction,Legend,Bot*
G04 #@! TF.FilePolarity,Positive*
%FSLAX46Y46*%
G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)*
G04 Created by KiCad (PCBNEW 8.0.5) date 2024-09-25 02:09:32*
%MOMM*%
%LPD*%
G01*
G04 APERTURE LIST*
G04 Aperture macros list*
%AMRoundRect*
0 Rectangle with rounded corners*
0 $1 Rounding radius*
0 $2 $3 $4 $5 $6 $7 $8 $9 X,Y pos of 4 corners*
0 Add a 4 corners polygon primitive as box body*
4,1,4,$2,$3,$4,$5,$6,$7,$8,$9,$2,$3,0*
0 Add four circle primitives for the rounded corners*
1,1,$1+$1,$2,$3*
1,1,$1+$1,$4,$5*
1,1,$1+$1,$6,$7*
1,1,$1+$1,$8,$9*
0 Add four rect primitives between the rounded corners*
20,1,$1+$1,$2,$3,$4,$5,0*
20,1,$1+$1,$4,$5,$6,$7,0*
20,1,$1+$1,$6,$7,$8,$9,0*
20,1,$1+$1,$8,$9,$2,$3,0*%
G04 Aperture macros list end*
%ADD10C,0.100000*%
%ADD11C,0.300000*%
%ADD12R,1.600000X1.600000*%
%ADD13C,1.600000*%
%ADD14R,1.800000X1.100000*%
%ADD15RoundRect,0.275000X-0.625000X0.275000X-0.625000X-0.275000X0.625000X-0.275000X0.625000X0.275000X0*%
%ADD16O,1.600000X1.600000*%
%ADD17R,2.000000X1.905000*%
%ADD18O,2.000000X1.905000*%
%ADD19R,1.800000X1.800000*%
%ADD20C,1.800000*%
%ADD21RoundRect,1.500000X1.500000X1.500000X-1.500000X1.500000X-1.500000X-1.500000X1.500000X-1.500000X0*%
%ADD22C,6.000000*%
%ADD23C,5.600000*%
%ADD24O,1.800000X1.800000*%
%ADD25O,3.800000X8.000000*%
%ADD26R,1.700000X1.700000*%
%ADD27O,1.700000X1.700000*%
%ADD28R,2.000000X2.000000*%
%ADD29O,2.000000X1.600000*%
%ADD30RoundRect,0.250000X-0.550000X-0.550000X0.550000X-0.550000X0.550000X0.550000X-0.550000X0.550000X0*%
%ADD31RoundRect,0.275000X0.625000X-0.275000X0.625000X0.275000X-0.625000X0.275000X-0.625000X-0.275000X0*%
%ADD32C,1.100000*%
%ADD33R,1.730000X2.030000*%
%ADD34O,1.730000X2.030000*%
%ADD35RoundRect,0.250000X0.550000X0.550000X-0.550000X0.550000X-0.550000X-0.550000X0.550000X-0.550000X0*%
G04 APERTURE END LIST*
D10*
X161150000Y-68950000D02*
X161550000Y-69750000D01*
X152300000Y-65000000D02*
X153300000Y-66000000D01*
X147800000Y-62500000D01*
X152300000Y-65000000D01*
G36*
X152300000Y-65000000D02*
G01*
X153300000Y-66000000D01*
X147800000Y-62500000D01*
X152300000Y-65000000D01*
G37*
X161640000Y-68925000D02*
X161840000Y-69325000D01*
X155800000Y-64500000D02*
X154800000Y-66000000D01*
X156800000Y-60500000D01*
X155800000Y-64500000D01*
G36*
X155800000Y-64500000D02*
G01*
X154800000Y-66000000D01*
X156800000Y-60500000D01*
X155800000Y-64500000D01*
G37*
X156800000Y-65000000D02*
X155800000Y-66000000D01*
X157800000Y-62000000D01*
X156800000Y-65000000D01*
G36*
X156800000Y-65000000D02*
G01*
X155800000Y-66000000D01*
X157800000Y-62000000D01*
X156800000Y-65000000D01*
G37*
X161350000Y-68850000D02*
X161750000Y-69650000D01*
X161300000Y-70000000D02*
X155800000Y-71500000D01*
X160800000Y-69000000D01*
X161300000Y-70000000D01*
G36*
X161300000Y-70000000D02*
G01*
X155800000Y-71500000D01*
X160800000Y-69000000D01*
X161300000Y-70000000D01*
G37*
X154300000Y-63500000D02*
X154300000Y-66000000D01*
X152800000Y-59000000D01*
X154300000Y-63500000D01*
G36*
X154300000Y-63500000D02*
G01*
X154300000Y-66000000D01*
X152800000Y-59000000D01*
X154300000Y-63500000D01*
G37*
X152300000Y-63500000D02*
X153800000Y-66000000D01*
X148800000Y-60500000D01*
X152300000Y-63500000D01*
G36*
X152300000Y-63500000D02*
G01*
X153800000Y-66000000D01*
X148800000Y-60500000D01*
X152300000Y-63500000D01*
G37*
D11*
X156300000Y-72000000D02*
X154800000Y-73000000D01*
X152800000Y-72000000D01*
X152300000Y-70000000D01*
X156800000Y-70000000D01*
X156300000Y-72000000D01*
%LPC*%
D12*
X156600000Y-108750000D03*
D13*
X156600000Y-111250000D03*
D14*
X127400000Y-42690000D03*
D15*
X129470000Y-43960000D03*
X127400000Y-45230000D03*
D13*
X117000000Y-42420000D03*
D16*
X106840000Y-42420000D03*
D17*
X138430000Y-83750000D03*
D18*
X138430000Y-81210000D03*
X138430000Y-78670000D03*
D17*
X129800000Y-35150000D03*
D18*
X129800000Y-32610000D03*
X129800000Y-30070000D03*
D13*
X139900000Y-53320000D03*
D16*
X139900000Y-63480000D03*
D12*
X130748000Y-108846000D03*
X130748000Y-111386000D03*
D16*
X130722600Y-100478000D03*
X130748000Y-97938000D03*
D12*
X149544000Y-108860000D03*
X149544000Y-111400000D03*
D16*
X149645600Y-100478000D03*
X149671000Y-97938000D03*
D13*
X177750000Y-92250000D03*
D16*
X187910000Y-92250000D03*
D19*
X122231000Y-81220000D03*
D20*
X122231000Y-83760000D03*
D21*
X113930000Y-59700000D03*
D22*
X106730000Y-59700000D03*
D19*
X122250000Y-102750000D03*
D20*
X122250000Y-105290000D03*
D13*
X195500000Y-29200000D03*
D16*
X185340000Y-29200000D03*
D14*
X127430000Y-91400000D03*
D15*
X129500000Y-92670000D03*
X127430000Y-93940000D03*
D23*
X87000000Y-22750000D03*
D13*
X116855000Y-69500000D03*
D16*
X106695000Y-69500000D03*
D19*
X106770000Y-87720000D03*
D24*
X116930000Y-87720000D03*
D13*
X133000000Y-94000000D03*
D16*
X133000000Y-83840000D03*
D13*
X195500000Y-52900000D03*
D16*
X185340000Y-52900000D03*
D13*
X198180000Y-71050000D03*
D16*
X188020000Y-71050000D03*
D23*
X203000000Y-22750000D03*
D13*
X139920000Y-66500000D03*
D16*
X150080000Y-66500000D03*
D13*
X116910000Y-100250000D03*
D16*
X106750000Y-100250000D03*
D13*
X153100000Y-78010000D03*
D16*
X153100000Y-67850000D03*
D14*
X121500000Y-69860000D03*
D15*
X123570000Y-71130000D03*
X121500000Y-72400000D03*
D14*
X127400000Y-69860000D03*
D15*
X129470000Y-71130000D03*
X127400000Y-72400000D03*
D25*
X96500000Y-97650000D03*
X96500000Y-106950000D03*
D26*
X195485000Y-60100000D03*
D27*
X192945000Y-60100000D03*
D21*
X113930000Y-81220000D03*
D22*
X106730000Y-81220000D03*
D28*
X150100000Y-27460000D03*
D29*
X150100000Y-30000000D03*
X150100000Y-32540000D03*
X150100000Y-35080000D03*
X150100000Y-37620000D03*
X150100000Y-40160000D03*
X150100000Y-42700000D03*
X150100000Y-45240000D03*
X172960000Y-45240000D03*
X172960000Y-42700000D03*
X172960000Y-40160000D03*
X172960000Y-37620000D03*
X172960000Y-35080000D03*
X172960000Y-32540000D03*
X172960000Y-30000000D03*
X172960000Y-27460000D03*
D26*
X195400000Y-77000000D03*
D27*
X192860000Y-77000000D03*
X190320000Y-77000000D03*
D26*
X195485000Y-38630000D03*
D27*
X192945000Y-38630000D03*
X190405000Y-38630000D03*
X187865000Y-38630000D03*
D30*
X131800000Y-50800000D03*
D19*
X174830000Y-82250000D03*
D24*
X174830000Y-92410000D03*
D19*
X122300000Y-32620000D03*
D20*
X122300000Y-35160000D03*
D26*
X195600000Y-99250000D03*
D27*
X193060000Y-99250000D03*
X190520000Y-99250000D03*
D13*
X101455112Y-94200000D03*
X103955112Y-94200000D03*
D26*
X195490000Y-47890000D03*
D27*
X192950000Y-47890000D03*
X190410000Y-47890000D03*
X187870000Y-47890000D03*
D13*
X133000000Y-45310000D03*
D16*
X133000000Y-35150000D03*
D14*
X138400000Y-45170000D03*
D31*
X136330000Y-43900000D03*
X138400000Y-42630000D03*
D26*
X195485000Y-33980000D03*
D27*
X192945000Y-33980000D03*
X190405000Y-33980000D03*
X187865000Y-33980000D03*
D30*
X156500000Y-67860000D03*
D25*
X96500000Y-42450000D03*
X96500000Y-33150000D03*
X96500000Y-64000000D03*
X96500000Y-54700000D03*
D17*
X129680000Y-62250000D03*
D18*
X129680000Y-59710000D03*
X129680000Y-57170000D03*
D30*
X188000000Y-67900000D03*
D13*
X187670000Y-88250000D03*
D16*
X197830000Y-88250000D03*
D13*
X102500000Y-66200000D03*
X102500000Y-68700000D03*
X116900000Y-95000000D03*
D16*
X106740000Y-95000000D03*
D12*
X101500000Y-90100000D03*
D13*
X104000000Y-90100000D03*
X133000000Y-72460000D03*
D16*
X133000000Y-62300000D03*
D17*
X129680000Y-83750000D03*
D18*
X129680000Y-81210000D03*
X129680000Y-78670000D03*
D14*
X138370000Y-72370000D03*
D31*
X136300000Y-71100000D03*
X138370000Y-69830000D03*
D19*
X106760000Y-66240000D03*
D24*
X116920000Y-66240000D03*
D23*
X87000000Y-117250000D03*
D13*
X146480000Y-32600000D03*
D16*
X136320000Y-32600000D03*
D32*
X108900000Y-48620000D03*
D33*
X103820000Y-50780000D03*
D34*
X106360000Y-50780000D03*
X108900000Y-50780000D03*
X111440000Y-50780000D03*
D13*
X181830000Y-85750000D03*
X181830000Y-88250000D03*
D12*
X123600000Y-108750000D03*
D13*
X123600000Y-111250000D03*
D19*
X106870000Y-39160000D03*
D24*
X117030000Y-39160000D03*
D13*
X136325000Y-39600000D03*
D16*
X146485000Y-39600000D03*
D13*
X117000000Y-46000000D03*
D16*
X106840000Y-46000000D03*
D26*
X195485000Y-43250000D03*
D27*
X192945000Y-43250000D03*
X190405000Y-43250000D03*
X187865000Y-43250000D03*
D13*
X116860000Y-73500000D03*
D16*
X106700000Y-73500000D03*
D21*
X113990000Y-108095000D03*
D22*
X106790000Y-108095000D03*
D13*
X116930000Y-90980000D03*
D16*
X106770000Y-90980000D03*
D19*
X170830000Y-82250000D03*
D24*
X170830000Y-92410000D03*
D25*
X96500000Y-85450000D03*
X96500000Y-76150000D03*
D14*
X121500000Y-42690000D03*
D15*
X123570000Y-43960000D03*
X121500000Y-45230000D03*
D14*
X138400000Y-93900000D03*
D31*
X136330000Y-92630000D03*
X138400000Y-91360000D03*
D14*
X121490000Y-91400000D03*
D15*
X123560000Y-92670000D03*
X121490000Y-93940000D03*
D21*
X114000000Y-32610000D03*
D22*
X106800000Y-32610000D03*
D23*
X203000000Y-117250000D03*
D26*
X177250000Y-42790000D03*
D27*
X177250000Y-40250000D03*
D13*
X144200000Y-74300000D03*
D16*
X144200000Y-84460000D03*
D19*
X122230000Y-59710000D03*
D20*
X122230000Y-62250000D03*
D13*
X197830000Y-84250000D03*
D16*
X187670000Y-84250000D03*
D35*
X169750000Y-50800000D03*
%LPD*%
M02*

View file

@ -1,23 +0,0 @@
G04 #@! TF.GenerationSoftware,KiCad,Pcbnew,8.0.5*
G04 #@! TF.CreationDate,2024-09-25T02:09:32+02:00*
G04 #@! TF.ProjectId,CanGrow,43616e47-726f-4772-9e6b-696361645f70,rev?*
G04 #@! TF.SameCoordinates,Original*
G04 #@! TF.FileFunction,Profile,NP*
%FSLAX46Y46*%
G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)*
G04 Created by KiCad (PCBNEW 8.0.5) date 2024-09-25 02:09:32*
%MOMM*%
%LPD*%
G01*
G04 APERTURE LIST*
G04 #@! TA.AperFunction,Profile*
%ADD10C,0.050000*%
G04 #@! TD*
G04 APERTURE END LIST*
D10*
X83000000Y-18750000D02*
X207000000Y-18750000D01*
X207000000Y-121250000D01*
X83000000Y-121250000D01*
X83000000Y-18750000D01*
M02*

File diff suppressed because it is too large Load diff

View file

@ -1,388 +0,0 @@
G04 #@! TF.GenerationSoftware,KiCad,Pcbnew,8.0.5*
G04 #@! TF.CreationDate,2024-09-25T02:09:32+02:00*
G04 #@! TF.ProjectId,CanGrow,43616e47-726f-4772-9e6b-696361645f70,rev?*
G04 #@! TF.SameCoordinates,Original*
G04 #@! TF.FileFunction,Soldermask,Top*
G04 #@! TF.FilePolarity,Negative*
%FSLAX46Y46*%
G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)*
G04 Created by KiCad (PCBNEW 8.0.5) date 2024-09-25 02:09:32*
%MOMM*%
%LPD*%
G01*
G04 APERTURE LIST*
G04 Aperture macros list*
%AMRoundRect*
0 Rectangle with rounded corners*
0 $1 Rounding radius*
0 $2 $3 $4 $5 $6 $7 $8 $9 X,Y pos of 4 corners*
0 Add a 4 corners polygon primitive as box body*
4,1,4,$2,$3,$4,$5,$6,$7,$8,$9,$2,$3,0*
0 Add four circle primitives for the rounded corners*
1,1,$1+$1,$2,$3*
1,1,$1+$1,$4,$5*
1,1,$1+$1,$6,$7*
1,1,$1+$1,$8,$9*
0 Add four rect primitives between the rounded corners*
20,1,$1+$1,$2,$3,$4,$5,0*
20,1,$1+$1,$4,$5,$6,$7,0*
20,1,$1+$1,$6,$7,$8,$9,0*
20,1,$1+$1,$8,$9,$2,$3,0*%
G04 Aperture macros list end*
%ADD10R,1.600000X1.600000*%
%ADD11C,1.600000*%
%ADD12R,1.800000X1.100000*%
%ADD13RoundRect,0.275000X-0.625000X0.275000X-0.625000X-0.275000X0.625000X-0.275000X0.625000X0.275000X0*%
%ADD14O,1.600000X1.600000*%
%ADD15R,2.000000X1.905000*%
%ADD16O,2.000000X1.905000*%
%ADD17R,1.800000X1.800000*%
%ADD18C,1.800000*%
%ADD19RoundRect,1.500000X1.500000X1.500000X-1.500000X1.500000X-1.500000X-1.500000X1.500000X-1.500000X0*%
%ADD20C,6.000000*%
%ADD21C,5.600000*%
%ADD22O,1.800000X1.800000*%
%ADD23O,3.800000X8.000000*%
%ADD24R,1.700000X1.700000*%
%ADD25O,1.700000X1.700000*%
%ADD26R,2.000000X2.000000*%
%ADD27O,2.000000X1.600000*%
%ADD28RoundRect,0.250000X-0.550000X-0.550000X0.550000X-0.550000X0.550000X0.550000X-0.550000X0.550000X0*%
%ADD29RoundRect,0.275000X0.625000X-0.275000X0.625000X0.275000X-0.625000X0.275000X-0.625000X-0.275000X0*%
%ADD30C,1.100000*%
%ADD31R,1.730000X2.030000*%
%ADD32O,1.730000X2.030000*%
%ADD33RoundRect,0.250000X0.550000X0.550000X-0.550000X0.550000X-0.550000X-0.550000X0.550000X-0.550000X0*%
G04 APERTURE END LIST*
D10*
X156600000Y-108750000D03*
D11*
X156600000Y-111250000D03*
D12*
X127400000Y-42690000D03*
D13*
X129470000Y-43960000D03*
X127400000Y-45230000D03*
D11*
X117000000Y-42420000D03*
D14*
X106840000Y-42420000D03*
D15*
X138430000Y-83750000D03*
D16*
X138430000Y-81210000D03*
X138430000Y-78670000D03*
D15*
X129800000Y-35150000D03*
D16*
X129800000Y-32610000D03*
X129800000Y-30070000D03*
D11*
X139900000Y-53320000D03*
D14*
X139900000Y-63480000D03*
D10*
X130748000Y-108846000D03*
X130748000Y-111386000D03*
D14*
X130722600Y-100478000D03*
X130748000Y-97938000D03*
D10*
X149544000Y-108860000D03*
X149544000Y-111400000D03*
D14*
X149645600Y-100478000D03*
X149671000Y-97938000D03*
D11*
X177750000Y-92250000D03*
D14*
X187910000Y-92250000D03*
D17*
X122231000Y-81220000D03*
D18*
X122231000Y-83760000D03*
D19*
X113930000Y-59700000D03*
D20*
X106730000Y-59700000D03*
D17*
X122250000Y-102750000D03*
D18*
X122250000Y-105290000D03*
D11*
X195500000Y-29200000D03*
D14*
X185340000Y-29200000D03*
D12*
X127430000Y-91400000D03*
D13*
X129500000Y-92670000D03*
X127430000Y-93940000D03*
D21*
X87000000Y-22750000D03*
D11*
X116855000Y-69500000D03*
D14*
X106695000Y-69500000D03*
D17*
X106770000Y-87720000D03*
D22*
X116930000Y-87720000D03*
D11*
X133000000Y-94000000D03*
D14*
X133000000Y-83840000D03*
D11*
X195500000Y-52900000D03*
D14*
X185340000Y-52900000D03*
D11*
X198180000Y-71050000D03*
D14*
X188020000Y-71050000D03*
D21*
X203000000Y-22750000D03*
D11*
X139920000Y-66500000D03*
D14*
X150080000Y-66500000D03*
D11*
X116910000Y-100250000D03*
D14*
X106750000Y-100250000D03*
D11*
X153100000Y-78010000D03*
D14*
X153100000Y-67850000D03*
D12*
X121500000Y-69860000D03*
D13*
X123570000Y-71130000D03*
X121500000Y-72400000D03*
D12*
X127400000Y-69860000D03*
D13*
X129470000Y-71130000D03*
X127400000Y-72400000D03*
D23*
X96500000Y-97650000D03*
X96500000Y-106950000D03*
D24*
X195485000Y-60100000D03*
D25*
X192945000Y-60100000D03*
D19*
X113930000Y-81220000D03*
D20*
X106730000Y-81220000D03*
D26*
X150100000Y-27460000D03*
D27*
X150100000Y-30000000D03*
X150100000Y-32540000D03*
X150100000Y-35080000D03*
X150100000Y-37620000D03*
X150100000Y-40160000D03*
X150100000Y-42700000D03*
X150100000Y-45240000D03*
X172960000Y-45240000D03*
X172960000Y-42700000D03*
X172960000Y-40160000D03*
X172960000Y-37620000D03*
X172960000Y-35080000D03*
X172960000Y-32540000D03*
X172960000Y-30000000D03*
X172960000Y-27460000D03*
D24*
X195400000Y-77000000D03*
D25*
X192860000Y-77000000D03*
X190320000Y-77000000D03*
D24*
X195485000Y-38630000D03*
D25*
X192945000Y-38630000D03*
X190405000Y-38630000D03*
X187865000Y-38630000D03*
D28*
X131800000Y-50800000D03*
D17*
X174830000Y-82250000D03*
D22*
X174830000Y-92410000D03*
D17*
X122300000Y-32620000D03*
D18*
X122300000Y-35160000D03*
D24*
X195600000Y-99250000D03*
D25*
X193060000Y-99250000D03*
X190520000Y-99250000D03*
D11*
X101455112Y-94200000D03*
X103955112Y-94200000D03*
D24*
X195490000Y-47890000D03*
D25*
X192950000Y-47890000D03*
X190410000Y-47890000D03*
X187870000Y-47890000D03*
D11*
X133000000Y-45310000D03*
D14*
X133000000Y-35150000D03*
D12*
X138400000Y-45170000D03*
D29*
X136330000Y-43900000D03*
X138400000Y-42630000D03*
D24*
X195485000Y-33980000D03*
D25*
X192945000Y-33980000D03*
X190405000Y-33980000D03*
X187865000Y-33980000D03*
D28*
X156500000Y-67860000D03*
D23*
X96500000Y-42450000D03*
X96500000Y-33150000D03*
X96500000Y-64000000D03*
X96500000Y-54700000D03*
D15*
X129680000Y-62250000D03*
D16*
X129680000Y-59710000D03*
X129680000Y-57170000D03*
D28*
X188000000Y-67900000D03*
D11*
X187670000Y-88250000D03*
D14*
X197830000Y-88250000D03*
D11*
X102500000Y-66200000D03*
X102500000Y-68700000D03*
X116900000Y-95000000D03*
D14*
X106740000Y-95000000D03*
D10*
X101500000Y-90100000D03*
D11*
X104000000Y-90100000D03*
X133000000Y-72460000D03*
D14*
X133000000Y-62300000D03*
D15*
X129680000Y-83750000D03*
D16*
X129680000Y-81210000D03*
X129680000Y-78670000D03*
D12*
X138370000Y-72370000D03*
D29*
X136300000Y-71100000D03*
X138370000Y-69830000D03*
D17*
X106760000Y-66240000D03*
D22*
X116920000Y-66240000D03*
D21*
X87000000Y-117250000D03*
D11*
X146480000Y-32600000D03*
D14*
X136320000Y-32600000D03*
D30*
X108900000Y-48620000D03*
D31*
X103820000Y-50780000D03*
D32*
X106360000Y-50780000D03*
X108900000Y-50780000D03*
X111440000Y-50780000D03*
D11*
X181830000Y-85750000D03*
X181830000Y-88250000D03*
D10*
X123600000Y-108750000D03*
D11*
X123600000Y-111250000D03*
D17*
X106870000Y-39160000D03*
D22*
X117030000Y-39160000D03*
D11*
X136325000Y-39600000D03*
D14*
X146485000Y-39600000D03*
D11*
X117000000Y-46000000D03*
D14*
X106840000Y-46000000D03*
D24*
X195485000Y-43250000D03*
D25*
X192945000Y-43250000D03*
X190405000Y-43250000D03*
X187865000Y-43250000D03*
D11*
X116860000Y-73500000D03*
D14*
X106700000Y-73500000D03*
D19*
X113990000Y-108095000D03*
D20*
X106790000Y-108095000D03*
D11*
X116930000Y-90980000D03*
D14*
X106770000Y-90980000D03*
D17*
X170830000Y-82250000D03*
D22*
X170830000Y-92410000D03*
D23*
X96500000Y-85450000D03*
X96500000Y-76150000D03*
D12*
X121500000Y-42690000D03*
D13*
X123570000Y-43960000D03*
X121500000Y-45230000D03*
D12*
X138400000Y-93900000D03*
D29*
X136330000Y-92630000D03*
X138400000Y-91360000D03*
D12*
X121490000Y-91400000D03*
D13*
X123560000Y-92670000D03*
X121490000Y-93940000D03*
D19*
X114000000Y-32610000D03*
D20*
X106800000Y-32610000D03*
D21*
X203000000Y-117250000D03*
D24*
X177250000Y-42790000D03*
D25*
X177250000Y-40250000D03*
D11*
X144200000Y-74300000D03*
D14*
X144200000Y-84460000D03*
D17*
X122230000Y-59710000D03*
D18*
X122230000Y-62250000D03*
D11*
X197830000Y-84250000D03*
D14*
X187670000Y-84250000D03*
D33*
X169750000Y-50800000D03*
M02*

View file

@ -1,15 +0,0 @@
G04 #@! TF.GenerationSoftware,KiCad,Pcbnew,8.0.5*
G04 #@! TF.CreationDate,2024-09-25T02:09:32+02:00*
G04 #@! TF.ProjectId,CanGrow,43616e47-726f-4772-9e6b-696361645f70,rev?*
G04 #@! TF.SameCoordinates,Original*
G04 #@! TF.FileFunction,Paste,Top*
G04 #@! TF.FilePolarity,Positive*
%FSLAX46Y46*%
G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)*
G04 Created by KiCad (PCBNEW 8.0.5) date 2024-09-25 02:09:32*
%MOMM*%
%LPD*%
G01*
G04 APERTURE LIST*
G04 APERTURE END LIST*
M02*

File diff suppressed because it is too large Load diff

View file

@ -1,837 +0,0 @@
%TF.GenerationSoftware,KiCad,Pcbnew,8.0.5*%
%TF.CreationDate,2024-09-25T02:09:35+02:00*%
%TF.ProjectId,CanGrow,43616e47-726f-4772-9e6b-696361645f70,rev?*%
%TF.SameCoordinates,Original*%
%TF.FileFunction,Drillmap*%
%TF.FilePolarity,Positive*%
%FSLAX45Y45*%
G04 Gerber Fmt 4.5, Leading zero omitted, Abs format (unit mm)*
G04 Created by KiCad (PCBNEW 8.0.5) date 2024-09-25 02:09:35*
%MOMM*%
%LPD*%
G01*
G04 APERTURE LIST*
%ADD10C,0.050000*%
%ADD11C,0.200000*%
%ADD12C,0.110000*%
G04 APERTURE END LIST*
D10*
X8300000Y-1875000D02*
X20700000Y-1875000D01*
X20700000Y-12125000D01*
X8300000Y-12125000D01*
X8300000Y-1875000D01*
D11*
D12*
X10835000Y-4807000D02*
X10945000Y-4917000D01*
X10945000Y-4807000D02*
X10835000Y-4917000D01*
D11*
X8558277Y-12438984D02*
X8558277Y-12238984D01*
X8558277Y-12238984D02*
X8605896Y-12238984D01*
X8605896Y-12238984D02*
X8634467Y-12248508D01*
X8634467Y-12248508D02*
X8653515Y-12267555D01*
X8653515Y-12267555D02*
X8663039Y-12286603D01*
X8663039Y-12286603D02*
X8672563Y-12324698D01*
X8672563Y-12324698D02*
X8672563Y-12353269D01*
X8672563Y-12353269D02*
X8663039Y-12391365D01*
X8663039Y-12391365D02*
X8653515Y-12410412D01*
X8653515Y-12410412D02*
X8634467Y-12429460D01*
X8634467Y-12429460D02*
X8605896Y-12438984D01*
X8605896Y-12438984D02*
X8558277Y-12438984D01*
X8758277Y-12438984D02*
X8758277Y-12305650D01*
X8758277Y-12343746D02*
X8767801Y-12324698D01*
X8767801Y-12324698D02*
X8777324Y-12315174D01*
X8777324Y-12315174D02*
X8796372Y-12305650D01*
X8796372Y-12305650D02*
X8815420Y-12305650D01*
X8882086Y-12438984D02*
X8882086Y-12305650D01*
X8882086Y-12238984D02*
X8872563Y-12248508D01*
X8872563Y-12248508D02*
X8882086Y-12258031D01*
X8882086Y-12258031D02*
X8891610Y-12248508D01*
X8891610Y-12248508D02*
X8882086Y-12238984D01*
X8882086Y-12238984D02*
X8882086Y-12258031D01*
X9005896Y-12438984D02*
X8986848Y-12429460D01*
X8986848Y-12429460D02*
X8977324Y-12410412D01*
X8977324Y-12410412D02*
X8977324Y-12238984D01*
X9110658Y-12438984D02*
X9091610Y-12429460D01*
X9091610Y-12429460D02*
X9082086Y-12410412D01*
X9082086Y-12410412D02*
X9082086Y-12238984D01*
X9339229Y-12438984D02*
X9339229Y-12238984D01*
X9339229Y-12238984D02*
X9405896Y-12381841D01*
X9405896Y-12381841D02*
X9472563Y-12238984D01*
X9472563Y-12238984D02*
X9472563Y-12438984D01*
X9653515Y-12438984D02*
X9653515Y-12334222D01*
X9653515Y-12334222D02*
X9643991Y-12315174D01*
X9643991Y-12315174D02*
X9624944Y-12305650D01*
X9624944Y-12305650D02*
X9586848Y-12305650D01*
X9586848Y-12305650D02*
X9567801Y-12315174D01*
X9653515Y-12429460D02*
X9634467Y-12438984D01*
X9634467Y-12438984D02*
X9586848Y-12438984D01*
X9586848Y-12438984D02*
X9567801Y-12429460D01*
X9567801Y-12429460D02*
X9558277Y-12410412D01*
X9558277Y-12410412D02*
X9558277Y-12391365D01*
X9558277Y-12391365D02*
X9567801Y-12372317D01*
X9567801Y-12372317D02*
X9586848Y-12362793D01*
X9586848Y-12362793D02*
X9634467Y-12362793D01*
X9634467Y-12362793D02*
X9653515Y-12353269D01*
X9748753Y-12305650D02*
X9748753Y-12505650D01*
X9748753Y-12315174D02*
X9767801Y-12305650D01*
X9767801Y-12305650D02*
X9805896Y-12305650D01*
X9805896Y-12305650D02*
X9824944Y-12315174D01*
X9824944Y-12315174D02*
X9834467Y-12324698D01*
X9834467Y-12324698D02*
X9843991Y-12343746D01*
X9843991Y-12343746D02*
X9843991Y-12400888D01*
X9843991Y-12400888D02*
X9834467Y-12419936D01*
X9834467Y-12419936D02*
X9824944Y-12429460D01*
X9824944Y-12429460D02*
X9805896Y-12438984D01*
X9805896Y-12438984D02*
X9767801Y-12438984D01*
X9767801Y-12438984D02*
X9748753Y-12429460D01*
X9929705Y-12419936D02*
X9939229Y-12429460D01*
X9939229Y-12429460D02*
X9929705Y-12438984D01*
X9929705Y-12438984D02*
X9920182Y-12429460D01*
X9920182Y-12429460D02*
X9929705Y-12419936D01*
X9929705Y-12419936D02*
X9929705Y-12438984D01*
X9929705Y-12315174D02*
X9939229Y-12324698D01*
X9939229Y-12324698D02*
X9929705Y-12334222D01*
X9929705Y-12334222D02*
X9920182Y-12324698D01*
X9920182Y-12324698D02*
X9929705Y-12315174D01*
X9929705Y-12315174D02*
X9929705Y-12334222D01*
D12*
X8187500Y-12712500D02*
X8297500Y-12822500D01*
X8297500Y-12712500D02*
X8187500Y-12822500D01*
D11*
X8663039Y-12858984D02*
X8548753Y-12858984D01*
X8605896Y-12858984D02*
X8605896Y-12658984D01*
X8605896Y-12658984D02*
X8586848Y-12687555D01*
X8586848Y-12687555D02*
X8567801Y-12706603D01*
X8567801Y-12706603D02*
X8548753Y-12716127D01*
X8748753Y-12839936D02*
X8758277Y-12849460D01*
X8758277Y-12849460D02*
X8748753Y-12858984D01*
X8748753Y-12858984D02*
X8739229Y-12849460D01*
X8739229Y-12849460D02*
X8748753Y-12839936D01*
X8748753Y-12839936D02*
X8748753Y-12858984D01*
X8948753Y-12858984D02*
X8834467Y-12858984D01*
X8891610Y-12858984D02*
X8891610Y-12658984D01*
X8891610Y-12658984D02*
X8872563Y-12687555D01*
X8872563Y-12687555D02*
X8853515Y-12706603D01*
X8853515Y-12706603D02*
X8834467Y-12716127D01*
X9072563Y-12658984D02*
X9091610Y-12658984D01*
X9091610Y-12658984D02*
X9110658Y-12668508D01*
X9110658Y-12668508D02*
X9120182Y-12678031D01*
X9120182Y-12678031D02*
X9129705Y-12697079D01*
X9129705Y-12697079D02*
X9139229Y-12735174D01*
X9139229Y-12735174D02*
X9139229Y-12782793D01*
X9139229Y-12782793D02*
X9129705Y-12820888D01*
X9129705Y-12820888D02*
X9120182Y-12839936D01*
X9120182Y-12839936D02*
X9110658Y-12849460D01*
X9110658Y-12849460D02*
X9091610Y-12858984D01*
X9091610Y-12858984D02*
X9072563Y-12858984D01*
X9072563Y-12858984D02*
X9053515Y-12849460D01*
X9053515Y-12849460D02*
X9043991Y-12839936D01*
X9043991Y-12839936D02*
X9034467Y-12820888D01*
X9034467Y-12820888D02*
X9024944Y-12782793D01*
X9024944Y-12782793D02*
X9024944Y-12735174D01*
X9024944Y-12735174D02*
X9034467Y-12697079D01*
X9034467Y-12697079D02*
X9043991Y-12678031D01*
X9043991Y-12678031D02*
X9053515Y-12668508D01*
X9053515Y-12668508D02*
X9072563Y-12658984D01*
X9263039Y-12658984D02*
X9282086Y-12658984D01*
X9282086Y-12658984D02*
X9301134Y-12668508D01*
X9301134Y-12668508D02*
X9310658Y-12678031D01*
X9310658Y-12678031D02*
X9320182Y-12697079D01*
X9320182Y-12697079D02*
X9329705Y-12735174D01*
X9329705Y-12735174D02*
X9329705Y-12782793D01*
X9329705Y-12782793D02*
X9320182Y-12820888D01*
X9320182Y-12820888D02*
X9310658Y-12839936D01*
X9310658Y-12839936D02*
X9301134Y-12849460D01*
X9301134Y-12849460D02*
X9282086Y-12858984D01*
X9282086Y-12858984D02*
X9263039Y-12858984D01*
X9263039Y-12858984D02*
X9243991Y-12849460D01*
X9243991Y-12849460D02*
X9234467Y-12839936D01*
X9234467Y-12839936D02*
X9224944Y-12820888D01*
X9224944Y-12820888D02*
X9215420Y-12782793D01*
X9215420Y-12782793D02*
X9215420Y-12735174D01*
X9215420Y-12735174D02*
X9224944Y-12697079D01*
X9224944Y-12697079D02*
X9234467Y-12678031D01*
X9234467Y-12678031D02*
X9243991Y-12668508D01*
X9243991Y-12668508D02*
X9263039Y-12658984D01*
X9415420Y-12858984D02*
X9415420Y-12725650D01*
X9415420Y-12744698D02*
X9424944Y-12735174D01*
X9424944Y-12735174D02*
X9443991Y-12725650D01*
X9443991Y-12725650D02*
X9472563Y-12725650D01*
X9472563Y-12725650D02*
X9491610Y-12735174D01*
X9491610Y-12735174D02*
X9501134Y-12754222D01*
X9501134Y-12754222D02*
X9501134Y-12858984D01*
X9501134Y-12754222D02*
X9510658Y-12735174D01*
X9510658Y-12735174D02*
X9529705Y-12725650D01*
X9529705Y-12725650D02*
X9558277Y-12725650D01*
X9558277Y-12725650D02*
X9577325Y-12735174D01*
X9577325Y-12735174D02*
X9586848Y-12754222D01*
X9586848Y-12754222D02*
X9586848Y-12858984D01*
X9682086Y-12858984D02*
X9682086Y-12725650D01*
X9682086Y-12744698D02*
X9691610Y-12735174D01*
X9691610Y-12735174D02*
X9710658Y-12725650D01*
X9710658Y-12725650D02*
X9739229Y-12725650D01*
X9739229Y-12725650D02*
X9758277Y-12735174D01*
X9758277Y-12735174D02*
X9767801Y-12754222D01*
X9767801Y-12754222D02*
X9767801Y-12858984D01*
X9767801Y-12754222D02*
X9777325Y-12735174D01*
X9777325Y-12735174D02*
X9796372Y-12725650D01*
X9796372Y-12725650D02*
X9824944Y-12725650D01*
X9824944Y-12725650D02*
X9843991Y-12735174D01*
X9843991Y-12735174D02*
X9853515Y-12754222D01*
X9853515Y-12754222D02*
X9853515Y-12858984D01*
X10243991Y-12649460D02*
X10072563Y-12906603D01*
X10501134Y-12658984D02*
X10520182Y-12658984D01*
X10520182Y-12658984D02*
X10539229Y-12668508D01*
X10539229Y-12668508D02*
X10548753Y-12678031D01*
X10548753Y-12678031D02*
X10558277Y-12697079D01*
X10558277Y-12697079D02*
X10567801Y-12735174D01*
X10567801Y-12735174D02*
X10567801Y-12782793D01*
X10567801Y-12782793D02*
X10558277Y-12820888D01*
X10558277Y-12820888D02*
X10548753Y-12839936D01*
X10548753Y-12839936D02*
X10539229Y-12849460D01*
X10539229Y-12849460D02*
X10520182Y-12858984D01*
X10520182Y-12858984D02*
X10501134Y-12858984D01*
X10501134Y-12858984D02*
X10482087Y-12849460D01*
X10482087Y-12849460D02*
X10472563Y-12839936D01*
X10472563Y-12839936D02*
X10463039Y-12820888D01*
X10463039Y-12820888D02*
X10453515Y-12782793D01*
X10453515Y-12782793D02*
X10453515Y-12735174D01*
X10453515Y-12735174D02*
X10463039Y-12697079D01*
X10463039Y-12697079D02*
X10472563Y-12678031D01*
X10472563Y-12678031D02*
X10482087Y-12668508D01*
X10482087Y-12668508D02*
X10501134Y-12658984D01*
X10653515Y-12839936D02*
X10663039Y-12849460D01*
X10663039Y-12849460D02*
X10653515Y-12858984D01*
X10653515Y-12858984D02*
X10643991Y-12849460D01*
X10643991Y-12849460D02*
X10653515Y-12839936D01*
X10653515Y-12839936D02*
X10653515Y-12858984D01*
X10786848Y-12658984D02*
X10805896Y-12658984D01*
X10805896Y-12658984D02*
X10824944Y-12668508D01*
X10824944Y-12668508D02*
X10834468Y-12678031D01*
X10834468Y-12678031D02*
X10843991Y-12697079D01*
X10843991Y-12697079D02*
X10853515Y-12735174D01*
X10853515Y-12735174D02*
X10853515Y-12782793D01*
X10853515Y-12782793D02*
X10843991Y-12820888D01*
X10843991Y-12820888D02*
X10834468Y-12839936D01*
X10834468Y-12839936D02*
X10824944Y-12849460D01*
X10824944Y-12849460D02*
X10805896Y-12858984D01*
X10805896Y-12858984D02*
X10786848Y-12858984D01*
X10786848Y-12858984D02*
X10767801Y-12849460D01*
X10767801Y-12849460D02*
X10758277Y-12839936D01*
X10758277Y-12839936D02*
X10748753Y-12820888D01*
X10748753Y-12820888D02*
X10739229Y-12782793D01*
X10739229Y-12782793D02*
X10739229Y-12735174D01*
X10739229Y-12735174D02*
X10748753Y-12697079D01*
X10748753Y-12697079D02*
X10758277Y-12678031D01*
X10758277Y-12678031D02*
X10767801Y-12668508D01*
X10767801Y-12668508D02*
X10786848Y-12658984D01*
X11024944Y-12725650D02*
X11024944Y-12858984D01*
X10977325Y-12649460D02*
X10929706Y-12792317D01*
X10929706Y-12792317D02*
X11053515Y-12792317D01*
X11110658Y-12658984D02*
X11234467Y-12658984D01*
X11234467Y-12658984D02*
X11167801Y-12735174D01*
X11167801Y-12735174D02*
X11196372Y-12735174D01*
X11196372Y-12735174D02*
X11215420Y-12744698D01*
X11215420Y-12744698D02*
X11224944Y-12754222D01*
X11224944Y-12754222D02*
X11234467Y-12773269D01*
X11234467Y-12773269D02*
X11234467Y-12820888D01*
X11234467Y-12820888D02*
X11224944Y-12839936D01*
X11224944Y-12839936D02*
X11215420Y-12849460D01*
X11215420Y-12849460D02*
X11196372Y-12858984D01*
X11196372Y-12858984D02*
X11139229Y-12858984D01*
X11139229Y-12858984D02*
X11120182Y-12849460D01*
X11120182Y-12849460D02*
X11110658Y-12839936D01*
X11301134Y-12658984D02*
X11424944Y-12658984D01*
X11424944Y-12658984D02*
X11358277Y-12735174D01*
X11358277Y-12735174D02*
X11386848Y-12735174D01*
X11386848Y-12735174D02*
X11405896Y-12744698D01*
X11405896Y-12744698D02*
X11415420Y-12754222D01*
X11415420Y-12754222D02*
X11424944Y-12773269D01*
X11424944Y-12773269D02*
X11424944Y-12820888D01*
X11424944Y-12820888D02*
X11415420Y-12839936D01*
X11415420Y-12839936D02*
X11405896Y-12849460D01*
X11405896Y-12849460D02*
X11386848Y-12858984D01*
X11386848Y-12858984D02*
X11329706Y-12858984D01*
X11329706Y-12858984D02*
X11310658Y-12849460D01*
X11310658Y-12849460D02*
X11301134Y-12839936D01*
X11501134Y-12658984D02*
X11501134Y-12697079D01*
X11577325Y-12658984D02*
X11577325Y-12697079D01*
X11872563Y-12935174D02*
X11863039Y-12925650D01*
X11863039Y-12925650D02*
X11843991Y-12897079D01*
X11843991Y-12897079D02*
X11834468Y-12878031D01*
X11834468Y-12878031D02*
X11824944Y-12849460D01*
X11824944Y-12849460D02*
X11815420Y-12801841D01*
X11815420Y-12801841D02*
X11815420Y-12763746D01*
X11815420Y-12763746D02*
X11824944Y-12716127D01*
X11824944Y-12716127D02*
X11834468Y-12687555D01*
X11834468Y-12687555D02*
X11843991Y-12668508D01*
X11843991Y-12668508D02*
X11863039Y-12639936D01*
X11863039Y-12639936D02*
X11872563Y-12630412D01*
X12053515Y-12858984D02*
X11939229Y-12858984D01*
X11996372Y-12858984D02*
X11996372Y-12658984D01*
X11996372Y-12658984D02*
X11977325Y-12687555D01*
X11977325Y-12687555D02*
X11958277Y-12706603D01*
X11958277Y-12706603D02*
X11939229Y-12716127D01*
X12291610Y-12858984D02*
X12291610Y-12658984D01*
X12377325Y-12858984D02*
X12377325Y-12754222D01*
X12377325Y-12754222D02*
X12367801Y-12735174D01*
X12367801Y-12735174D02*
X12348753Y-12725650D01*
X12348753Y-12725650D02*
X12320182Y-12725650D01*
X12320182Y-12725650D02*
X12301134Y-12735174D01*
X12301134Y-12735174D02*
X12291610Y-12744698D01*
X12501134Y-12858984D02*
X12482087Y-12849460D01*
X12482087Y-12849460D02*
X12472563Y-12839936D01*
X12472563Y-12839936D02*
X12463039Y-12820888D01*
X12463039Y-12820888D02*
X12463039Y-12763746D01*
X12463039Y-12763746D02*
X12472563Y-12744698D01*
X12472563Y-12744698D02*
X12482087Y-12735174D01*
X12482087Y-12735174D02*
X12501134Y-12725650D01*
X12501134Y-12725650D02*
X12529706Y-12725650D01*
X12529706Y-12725650D02*
X12548753Y-12735174D01*
X12548753Y-12735174D02*
X12558277Y-12744698D01*
X12558277Y-12744698D02*
X12567801Y-12763746D01*
X12567801Y-12763746D02*
X12567801Y-12820888D01*
X12567801Y-12820888D02*
X12558277Y-12839936D01*
X12558277Y-12839936D02*
X12548753Y-12849460D01*
X12548753Y-12849460D02*
X12529706Y-12858984D01*
X12529706Y-12858984D02*
X12501134Y-12858984D01*
X12682087Y-12858984D02*
X12663039Y-12849460D01*
X12663039Y-12849460D02*
X12653515Y-12830412D01*
X12653515Y-12830412D02*
X12653515Y-12658984D01*
X12834468Y-12849460D02*
X12815420Y-12858984D01*
X12815420Y-12858984D02*
X12777325Y-12858984D01*
X12777325Y-12858984D02*
X12758277Y-12849460D01*
X12758277Y-12849460D02*
X12748753Y-12830412D01*
X12748753Y-12830412D02*
X12748753Y-12754222D01*
X12748753Y-12754222D02*
X12758277Y-12735174D01*
X12758277Y-12735174D02*
X12777325Y-12725650D01*
X12777325Y-12725650D02*
X12815420Y-12725650D01*
X12815420Y-12725650D02*
X12834468Y-12735174D01*
X12834468Y-12735174D02*
X12843991Y-12754222D01*
X12843991Y-12754222D02*
X12843991Y-12773269D01*
X12843991Y-12773269D02*
X12748753Y-12792317D01*
X12910658Y-12935174D02*
X12920182Y-12925650D01*
X12920182Y-12925650D02*
X12939230Y-12897079D01*
X12939230Y-12897079D02*
X12948753Y-12878031D01*
X12948753Y-12878031D02*
X12958277Y-12849460D01*
X12958277Y-12849460D02*
X12967801Y-12801841D01*
X12967801Y-12801841D02*
X12967801Y-12763746D01*
X12967801Y-12763746D02*
X12958277Y-12716127D01*
X12958277Y-12716127D02*
X12948753Y-12687555D01*
X12948753Y-12687555D02*
X12939230Y-12668508D01*
X12939230Y-12668508D02*
X12920182Y-12639936D01*
X12920182Y-12639936D02*
X12910658Y-12630412D01*
X13272563Y-12935174D02*
X13263039Y-12925650D01*
X13263039Y-12925650D02*
X13243991Y-12897079D01*
X13243991Y-12897079D02*
X13234468Y-12878031D01*
X13234468Y-12878031D02*
X13224944Y-12849460D01*
X13224944Y-12849460D02*
X13215420Y-12801841D01*
X13215420Y-12801841D02*
X13215420Y-12763746D01*
X13215420Y-12763746D02*
X13224944Y-12716127D01*
X13224944Y-12716127D02*
X13234468Y-12687555D01*
X13234468Y-12687555D02*
X13243991Y-12668508D01*
X13243991Y-12668508D02*
X13263039Y-12639936D01*
X13263039Y-12639936D02*
X13272563Y-12630412D01*
X13348753Y-12725650D02*
X13348753Y-12858984D01*
X13348753Y-12744698D02*
X13358277Y-12735174D01*
X13358277Y-12735174D02*
X13377325Y-12725650D01*
X13377325Y-12725650D02*
X13405896Y-12725650D01*
X13405896Y-12725650D02*
X13424944Y-12735174D01*
X13424944Y-12735174D02*
X13434468Y-12754222D01*
X13434468Y-12754222D02*
X13434468Y-12858984D01*
X13558277Y-12858984D02*
X13539230Y-12849460D01*
X13539230Y-12849460D02*
X13529706Y-12839936D01*
X13529706Y-12839936D02*
X13520182Y-12820888D01*
X13520182Y-12820888D02*
X13520182Y-12763746D01*
X13520182Y-12763746D02*
X13529706Y-12744698D01*
X13529706Y-12744698D02*
X13539230Y-12735174D01*
X13539230Y-12735174D02*
X13558277Y-12725650D01*
X13558277Y-12725650D02*
X13586849Y-12725650D01*
X13586849Y-12725650D02*
X13605896Y-12735174D01*
X13605896Y-12735174D02*
X13615420Y-12744698D01*
X13615420Y-12744698D02*
X13624944Y-12763746D01*
X13624944Y-12763746D02*
X13624944Y-12820888D01*
X13624944Y-12820888D02*
X13615420Y-12839936D01*
X13615420Y-12839936D02*
X13605896Y-12849460D01*
X13605896Y-12849460D02*
X13586849Y-12858984D01*
X13586849Y-12858984D02*
X13558277Y-12858984D01*
X13682087Y-12725650D02*
X13758277Y-12725650D01*
X13710658Y-12658984D02*
X13710658Y-12830412D01*
X13710658Y-12830412D02*
X13720182Y-12849460D01*
X13720182Y-12849460D02*
X13739230Y-12858984D01*
X13739230Y-12858984D02*
X13758277Y-12858984D01*
X13977325Y-12725650D02*
X13977325Y-12925650D01*
X13977325Y-12735174D02*
X13996372Y-12725650D01*
X13996372Y-12725650D02*
X14034468Y-12725650D01*
X14034468Y-12725650D02*
X14053515Y-12735174D01*
X14053515Y-12735174D02*
X14063039Y-12744698D01*
X14063039Y-12744698D02*
X14072563Y-12763746D01*
X14072563Y-12763746D02*
X14072563Y-12820888D01*
X14072563Y-12820888D02*
X14063039Y-12839936D01*
X14063039Y-12839936D02*
X14053515Y-12849460D01*
X14053515Y-12849460D02*
X14034468Y-12858984D01*
X14034468Y-12858984D02*
X13996372Y-12858984D01*
X13996372Y-12858984D02*
X13977325Y-12849460D01*
X14186849Y-12858984D02*
X14167801Y-12849460D01*
X14167801Y-12849460D02*
X14158277Y-12830412D01*
X14158277Y-12830412D02*
X14158277Y-12658984D01*
X14348753Y-12858984D02*
X14348753Y-12754222D01*
X14348753Y-12754222D02*
X14339230Y-12735174D01*
X14339230Y-12735174D02*
X14320182Y-12725650D01*
X14320182Y-12725650D02*
X14282087Y-12725650D01*
X14282087Y-12725650D02*
X14263039Y-12735174D01*
X14348753Y-12849460D02*
X14329706Y-12858984D01*
X14329706Y-12858984D02*
X14282087Y-12858984D01*
X14282087Y-12858984D02*
X14263039Y-12849460D01*
X14263039Y-12849460D02*
X14253515Y-12830412D01*
X14253515Y-12830412D02*
X14253515Y-12811365D01*
X14253515Y-12811365D02*
X14263039Y-12792317D01*
X14263039Y-12792317D02*
X14282087Y-12782793D01*
X14282087Y-12782793D02*
X14329706Y-12782793D01*
X14329706Y-12782793D02*
X14348753Y-12773269D01*
X14415420Y-12725650D02*
X14491611Y-12725650D01*
X14443992Y-12658984D02*
X14443992Y-12830412D01*
X14443992Y-12830412D02*
X14453515Y-12849460D01*
X14453515Y-12849460D02*
X14472563Y-12858984D01*
X14472563Y-12858984D02*
X14491611Y-12858984D01*
X14634468Y-12849460D02*
X14615420Y-12858984D01*
X14615420Y-12858984D02*
X14577325Y-12858984D01*
X14577325Y-12858984D02*
X14558277Y-12849460D01*
X14558277Y-12849460D02*
X14548753Y-12830412D01*
X14548753Y-12830412D02*
X14548753Y-12754222D01*
X14548753Y-12754222D02*
X14558277Y-12735174D01*
X14558277Y-12735174D02*
X14577325Y-12725650D01*
X14577325Y-12725650D02*
X14615420Y-12725650D01*
X14615420Y-12725650D02*
X14634468Y-12735174D01*
X14634468Y-12735174D02*
X14643992Y-12754222D01*
X14643992Y-12754222D02*
X14643992Y-12773269D01*
X14643992Y-12773269D02*
X14548753Y-12792317D01*
X14815420Y-12858984D02*
X14815420Y-12658984D01*
X14815420Y-12849460D02*
X14796373Y-12858984D01*
X14796373Y-12858984D02*
X14758277Y-12858984D01*
X14758277Y-12858984D02*
X14739230Y-12849460D01*
X14739230Y-12849460D02*
X14729706Y-12839936D01*
X14729706Y-12839936D02*
X14720182Y-12820888D01*
X14720182Y-12820888D02*
X14720182Y-12763746D01*
X14720182Y-12763746D02*
X14729706Y-12744698D01*
X14729706Y-12744698D02*
X14739230Y-12735174D01*
X14739230Y-12735174D02*
X14758277Y-12725650D01*
X14758277Y-12725650D02*
X14796373Y-12725650D01*
X14796373Y-12725650D02*
X14815420Y-12735174D01*
X14891611Y-12935174D02*
X14901134Y-12925650D01*
X14901134Y-12925650D02*
X14920182Y-12897079D01*
X14920182Y-12897079D02*
X14929706Y-12878031D01*
X14929706Y-12878031D02*
X14939230Y-12849460D01*
X14939230Y-12849460D02*
X14948753Y-12801841D01*
X14948753Y-12801841D02*
X14948753Y-12763746D01*
X14948753Y-12763746D02*
X14939230Y-12716127D01*
X14939230Y-12716127D02*
X14929706Y-12687555D01*
X14929706Y-12687555D02*
X14920182Y-12668508D01*
X14920182Y-12668508D02*
X14901134Y-12639936D01*
X14901134Y-12639936D02*
X14891611Y-12630412D01*
M02*

View file

@ -1,16 +0,0 @@
M48
; DRILL file {KiCad 8.0.5} date 2024-09-25T02:09:34+0200
; FORMAT={-:-/ absolute / metric / decimal}
; #@! TF.CreationDate,2024-09-25T02:09:34+02:00
; #@! TF.GenerationSoftware,Kicad,Pcbnew,8.0.5
; #@! TF.FileFunction,NonPlated,1,2,NPTH
FMAT,2
METRIC
; #@! TA.AperFunction,NonPlated,NPTH,ComponentDrill
T1C1.100
%
G90
G05
T1
X108.9Y-48.62
M30

File diff suppressed because it is too large Load diff

View file

@ -1,238 +0,0 @@
M48
; DRILL file {KiCad 8.0.5} date 2024-09-25T02:09:34+0200
; FORMAT={-:-/ absolute / metric / decimal}
; #@! TF.CreationDate,2024-09-25T02:09:34+02:00
; #@! TF.GenerationSoftware,Kicad,Pcbnew,8.0.5
; #@! TF.FileFunction,Plated,1,2,PTH
FMAT,2
METRIC
; #@! TA.AperFunction,Plated,PTH,ComponentDrill
T1C0.750
; #@! TA.AperFunction,Plated,PTH,ComponentDrill
T2C0.800
; #@! TA.AperFunction,Plated,PTH,ComponentDrill
T3C0.900
; #@! TA.AperFunction,Plated,PTH,ComponentDrill
T4C1.000
; #@! TA.AperFunction,Plated,PTH,ComponentDrill
T5C1.020
; #@! TA.AperFunction,Plated,PTH,ComponentDrill
T6C1.100
; #@! TA.AperFunction,Plated,PTH,ComponentDrill
T7C2.000
; #@! TA.AperFunction,Plated,PTH,ComponentDrill
T8C3.200
; #@! TA.AperFunction,Plated,PTH,ComponentDrill
T9C4.500
%
G90
G05
T1
X121.89Y-91.4
X121.89Y-93.94
X121.9Y-42.69
X121.9Y-45.23
X121.9Y-69.86
X121.9Y-72.4
X123.16Y-92.67
X123.17Y-43.96
X123.17Y-71.13
X127.8Y-42.69
X127.8Y-45.23
X127.8Y-69.86
X127.8Y-72.4
X127.83Y-91.4
X127.83Y-93.94
X129.07Y-43.96
X129.07Y-71.13
X129.1Y-92.67
X136.7Y-71.1
X136.73Y-43.9
X136.73Y-92.63
X137.97Y-69.83
X137.97Y-72.37
X138.0Y-42.63
X138.0Y-45.17
X138.0Y-91.36
X138.0Y-93.9
T2
X101.455Y-94.2
X101.5Y-90.1
X102.5Y-66.2
X102.5Y-68.7
X103.955Y-94.2
X104.0Y-90.1
X106.695Y-69.5
X106.7Y-73.5
X106.74Y-95.0
X106.75Y-100.25
X106.77Y-90.98
X106.84Y-42.42
X106.84Y-46.0
X116.855Y-69.5
X116.86Y-73.5
X116.9Y-95.0
X116.91Y-100.25
X116.93Y-90.98
X117.0Y-42.42
X117.0Y-46.0
X123.6Y-108.75
X123.6Y-111.25
X130.723Y-100.478
X130.748Y-97.938
X130.748Y-108.846
X130.748Y-111.386
X131.8Y-50.8
X133.0Y-35.15
X133.0Y-45.31
X133.0Y-62.3
X133.0Y-72.46
X133.0Y-83.84
X133.0Y-94.0
X136.32Y-32.6
X136.325Y-39.6
X139.9Y-53.32
X139.9Y-63.48
X139.92Y-66.5
X144.2Y-74.3
X144.2Y-84.46
X146.48Y-32.6
X146.485Y-39.6
X149.544Y-108.86
X149.544Y-111.4
X149.646Y-100.478
X149.671Y-97.938
X150.08Y-66.5
X153.1Y-67.85
X153.1Y-78.01
X156.5Y-67.86
X156.6Y-108.75
X156.6Y-111.25
X169.75Y-50.8
X177.75Y-92.25
X181.83Y-85.75
X181.83Y-88.25
X185.34Y-29.2
X185.34Y-52.9
X187.67Y-84.25
X187.67Y-88.25
X187.91Y-92.25
X188.0Y-67.9
X188.02Y-71.05
X195.5Y-29.2
X195.5Y-52.9
X197.83Y-84.25
X197.83Y-88.25
X198.18Y-71.05
T3
X106.76Y-66.24
X106.77Y-87.72
X106.87Y-39.16
X116.92Y-66.24
X116.93Y-87.72
X117.03Y-39.16
X122.23Y-59.71
X122.23Y-62.25
X122.231Y-81.22
X122.231Y-83.76
X122.25Y-102.75
X122.25Y-105.29
X122.3Y-32.62
X122.3Y-35.16
X170.83Y-82.25
X170.83Y-92.41
X174.83Y-82.25
X174.83Y-92.41
T4
X150.1Y-27.46
X150.1Y-30.0
X150.1Y-32.54
X150.1Y-35.08
X150.1Y-37.62
X150.1Y-40.16
X150.1Y-42.7
X150.1Y-45.24
X172.96Y-27.46
X172.96Y-30.0
X172.96Y-32.54
X172.96Y-35.08
X172.96Y-37.62
X172.96Y-40.16
X172.96Y-42.7
X172.96Y-45.24
X177.25Y-40.25
X177.25Y-42.79
X187.865Y-33.98
X187.865Y-38.63
X187.865Y-43.25
X187.87Y-47.89
X190.32Y-77.0
X190.405Y-33.98
X190.405Y-38.63
X190.405Y-43.25
X190.41Y-47.89
X190.52Y-99.25
X192.86Y-77.0
X192.945Y-33.98
X192.945Y-38.63
X192.945Y-43.25
X192.945Y-60.1
X192.95Y-47.89
X193.06Y-99.25
X195.4Y-77.0
X195.485Y-33.98
X195.485Y-38.63
X195.485Y-43.25
X195.485Y-60.1
X195.49Y-47.89
X195.6Y-99.25
T5
X103.82Y-50.78
X106.36Y-50.78
X108.9Y-50.78
X111.44Y-50.78
T6
X129.68Y-57.17
X129.68Y-59.71
X129.68Y-62.25
X129.68Y-78.67
X129.68Y-81.21
X129.68Y-83.75
X129.8Y-30.07
X129.8Y-32.61
X129.8Y-35.15
X138.43Y-78.67
X138.43Y-81.21
X138.43Y-83.75
T8
X87.0Y-22.75
X87.0Y-117.25
X203.0Y-22.75
X203.0Y-117.25
T9
X106.73Y-59.7
X106.73Y-81.22
X106.79Y-108.095
X106.8Y-32.61
X113.93Y-59.7
X113.93Y-81.22
X113.99Y-108.095
X114.0Y-32.61
T7
X96.5Y-35.15G85X96.5Y-31.15
G05
X96.5Y-44.45G85X96.5Y-40.45
G05
X96.5Y-56.7G85X96.5Y-52.7
G05
X96.5Y-66.0G85X96.5Y-62.0
G05
X96.5Y-78.15G85X96.5Y-74.15
G05
X96.5Y-87.45G85X96.5Y-83.45
G05
X96.5Y-95.65G85X96.5Y-99.65
G05
X96.5Y-104.95G85X96.5Y-108.95
G05
M30

BIN
Logo/CanGrow_leaf_big.png Normal file

Binary file not shown.

After

(image error) Size: 971 B

BIN
Logo/CanGrow_leaf_big.xcf Normal file

Binary file not shown.