mirror of
https://github.com/SasaKaranovic/DIY-Filament-Dryer-v2.git
synced 2026-07-08 18:02:31 +02:00
Initial commit
This commit is contained in:
+95
@@ -0,0 +1,95 @@
|
||||
# Prerequisites
|
||||
*.d
|
||||
|
||||
# Object files
|
||||
*.o
|
||||
*.ko
|
||||
*.obj
|
||||
*.elf
|
||||
|
||||
# Linker output
|
||||
*.ilk
|
||||
*.map
|
||||
*.exp
|
||||
|
||||
# Precompiled Headers
|
||||
*.gch
|
||||
*.pch
|
||||
|
||||
# Libraries
|
||||
*.lib
|
||||
*.a
|
||||
*.la
|
||||
*.lo
|
||||
|
||||
# Shared objects (inc. Windows DLLs)
|
||||
*.dll
|
||||
*.so
|
||||
*.so.*
|
||||
*.dylib
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
*.i*86
|
||||
*.x86_64
|
||||
*.hex
|
||||
|
||||
# Debug files
|
||||
*.dSYM/
|
||||
*.su
|
||||
*.idb
|
||||
*.pdb
|
||||
|
||||
# Kernel Module Compile Results
|
||||
*.mod*
|
||||
*.cmd
|
||||
.tmp_versions/
|
||||
modules.order
|
||||
Module.symvers
|
||||
Mkfile.old
|
||||
dkms.conf
|
||||
|
||||
### PlatformIO ###
|
||||
.pioenvs
|
||||
.piolibdeps
|
||||
.clang_complete
|
||||
.gcc-flags.json
|
||||
.pio
|
||||
|
||||
# Various Altium files
|
||||
*.PrjPCBStructure
|
||||
*.SchDocPreview
|
||||
*.PcbDocPreview
|
||||
__Previews
|
||||
*.PrjPcbStructure
|
||||
*.Dat
|
||||
*.REP
|
||||
*.TLT
|
||||
*.LOG
|
||||
*.log
|
||||
*.htm
|
||||
*.$$$
|
||||
|
||||
#useless dirs
|
||||
Project\ Outputs
|
||||
Project\ Logs
|
||||
Project Outputs for*
|
||||
|
||||
#History
|
||||
History
|
||||
|
||||
#Exports, but not manual ones
|
||||
exports/*
|
||||
!exports/manual
|
||||
|
||||
#Python stuff
|
||||
scripts/__pycache__/
|
||||
*.bak
|
||||
*.dwl
|
||||
*.dwl2
|
||||
|
||||
Firmware/data/
|
||||
Firmware/src/version.h
|
||||
Firmware/src/binary_data.ino
|
||||
@@ -0,0 +1,44 @@
|
||||
# How to build firmware
|
||||
|
||||
For our firmware development, we are going to use Arduino. Mainly because it's user friendly, easy to use and
|
||||
most hobbyists are familiar with it or have already used it. While we could build the same or even more efficient
|
||||
firmware using C and ESP-IDF, we are going to stick with Arduino and hope that this allows the project to be
|
||||
more friendly and easier to use/modify by wider DIY/hacker community.
|
||||
|
||||
## How can I build/compile firmware?
|
||||
|
||||
Arduino has many third party libraries that you can use and also build firmware for many different boards.
|
||||
However, depending for which board you are building firmware and also which libraries your project (sketch) uses,
|
||||
you might need to manually install them or download them trough board/library manager.
|
||||
|
||||
While above mentioned solution works fine, it can lead to issues like installing the "wrong/different" version
|
||||
of the library, different board files, "wrong/different" build/upload tools and so on. On top of that, since board files
|
||||
and libraries are shared, we could install a library or a board file while working on another project that breaks our current project.
|
||||
Hopefully you can see why this is not so great for open-source open-hardware project where we want everyone to be able to easily
|
||||
checkout a repository and hit the ground running without spending hours setting up and debugging what library or board files are missing.
|
||||
|
||||
Luckily we can automate/avoid this by using a build system like PlatformIO. So below we will have two ways of building
|
||||
the firmware, one will be "easy" by leveraging PlatformiIO build system, and the other one will be a manual one by using
|
||||
Arduino IDE and manually searching and downloading all the board/library files.
|
||||
|
||||
## - The super easy way - Compile and upload using PlatformIO build system -
|
||||
|
||||
### 1. Install PlatformIO
|
||||
|
||||
Installing PlatformIO CLI is pretty straight-forward and also well documented for Windows, Linux and MacOS.
|
||||
You will need to follow few steps and get PlatformIO CLI installed, detailed tutorial can be found at https://platformio.org/install/cli
|
||||
Make sure to install [PlatformIO Core](https://docs.platformio.org/en/latest//core/installation.html#installation-methods 'https://docs.platformio.org/en/latest//core/installation.html#installation-methods') and also that it is available trough [shell](https://docs.platformio.org/en/latest//core/installation.html#piocore-install-shell-commands 'PlatformIO Core - Install Shell Commands¶').
|
||||
|
||||
### 2. Build firmware
|
||||
|
||||
Open shell/command-prompt and navigate to 'DIY-Filament-Dryer_v2/Firmware' folder.
|
||||
|
||||
1. Compile the firmware by typing `pio run`, PlatformIO will download all the required board files and libraries and finally compile the firmware.
|
||||
2. Upload the firmware with `pio run --target upload --upload-port <COM-PORT>`. Make sure to replace `<COM-PORT>` with your ESP32's COM port (ie COM1 or /dev/ttyACM0)
|
||||
|
||||
|
||||
Every time you make a firmware change or change the web page/UI, you need to re-compile and re-upload the firmware.
|
||||
|
||||
|
||||
|
||||
[<- Go back to repository root](../README.md)
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,8 @@
|
||||
/*!
|
||||
* Bootstrap Reboot v4.0.0 (https://getbootstrap.com)
|
||||
* Copyright 2011-2018 The Bootstrap Authors
|
||||
* Copyright 2011-2018 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
|
||||
*/*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;-ms-overflow-style:scrollbar;-webkit-tap-highlight-color:transparent}@-ms-viewport{width:device-width}article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus{outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}dfn{font-style:italic}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent;-webkit-text-decoration-skip:objects}a:hover{color:#0056b3;text-decoration:underline}a:not([href]):not([tabindex]){color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus,a:not([href]):not([tabindex]):hover{color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus{outline:0}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto;-ms-overflow-style:scrollbar}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg:not(:root){overflow:hidden}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}[type=reset],[type=submit],button,html [type=button]{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=date],input[type=datetime-local],input[type=month],input[type=time]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important}
|
||||
/*# sourceMappingURL=bootstrap-reboot.min.css.map */
|
||||
+12
File diff suppressed because one or more lines are too long
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
/*!
|
||||
* Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
*/
|
||||
@font-face{font-family:"Font Awesome 5 Brands";font-style:normal;font-weight:400;font-display:block;src:url(../webfonts/fa-brands-400.eot);src:url(../webfonts/fa-brands-400.eot?#iefix) format("embedded-opentype"),url(../webfonts/fa-brands-400.woff2) format("woff2"),url(../webfonts/fa-brands-400.woff) format("woff"),url(../webfonts/fa-brands-400.ttf) format("truetype"),url(../webfonts/fa-brands-400.svg#fontawesome) format("svg")}.fab{font-family:"Font Awesome 5 Brands";font-weight:400}
|
||||
Vendored
+273
@@ -0,0 +1,273 @@
|
||||
body {
|
||||
padding-top: 120px;
|
||||
background-color: #212529;
|
||||
}
|
||||
pre {
|
||||
background: #f7f7f9;
|
||||
}
|
||||
iframe {
|
||||
overflow: hidden;
|
||||
border: none;
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
body > .navbar-transparent {
|
||||
box-shadow: none;
|
||||
}
|
||||
body > .navbar-transparent .navbar-nav > .open > a {
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
.dark-bg {
|
||||
background-color: #212529;
|
||||
}
|
||||
.light-bg {
|
||||
background-color: #212529;
|
||||
}
|
||||
#home,
|
||||
#help {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
#home .navbar,
|
||||
#help .navbar {
|
||||
background: #349aed;
|
||||
background: linear-gradient(145deg, #349aed 50%, #34d8ed 100%);
|
||||
transition: box-shadow 200ms ease-in;
|
||||
}
|
||||
#home .navbar-transparent,
|
||||
#help .navbar-transparent {
|
||||
background: none !important;
|
||||
box-shadow: none;
|
||||
}
|
||||
#home .navbar-brand .nav-link,
|
||||
#help .navbar-brand .nav-link {
|
||||
display: inline-block;
|
||||
margin-right: -30px;
|
||||
}
|
||||
#home .nav-link,
|
||||
#help .nav-link {
|
||||
text-transform: uppercase;
|
||||
font-weight: 500;
|
||||
color: #fff;
|
||||
}
|
||||
#home {
|
||||
padding-top: 0;
|
||||
}
|
||||
#home .btn {
|
||||
padding: 0.6rem 0.55rem 0.5rem;
|
||||
box-shadow: none;
|
||||
font-size: 0.7rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
.bs-docs-section {
|
||||
margin-top: 4em;
|
||||
}
|
||||
.bs-docs-section .page-header h1 {
|
||||
padding: 2rem 0;
|
||||
font-size: 3rem;
|
||||
}
|
||||
.dropdown-menu.show[aria-labelledby="themes"] {
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
width: 420px;
|
||||
-ms-flex-wrap: wrap;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.dropdown-menu.show[aria-labelledby="themes"] .dropdown-item {
|
||||
width: 33.333%;
|
||||
}
|
||||
.dropdown-menu.show[aria-labelledby="themes"] .dropdown-item:first-child {
|
||||
width: 100%;
|
||||
}
|
||||
.bs-component {
|
||||
position: relative;
|
||||
}
|
||||
.bs-component + .bs-component {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
.bs-component .card {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.bs-component .modal {
|
||||
position: relative;
|
||||
top: auto;
|
||||
right: auto;
|
||||
left: auto;
|
||||
bottom: auto;
|
||||
z-index: 1;
|
||||
display: block;
|
||||
}
|
||||
.bs-component .modal-dialog {
|
||||
width: 90%;
|
||||
}
|
||||
.bs-component .popover {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 220px;
|
||||
margin: 20px;
|
||||
}
|
||||
.source-button {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: 100;
|
||||
font-weight: 700;
|
||||
}
|
||||
.source-button:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
.bs-component:hover .source-button {
|
||||
display: block;
|
||||
}
|
||||
#source-modal pre {
|
||||
max-height: calc(100vh - 11rem);
|
||||
background-color: rgba(0, 0, 0, 0.7);
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
.nav-tabs {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.progress {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
#footer {
|
||||
margin: 5em 0;
|
||||
}
|
||||
#footer li {
|
||||
float: left;
|
||||
margin-right: 1.5em;
|
||||
margin-bottom: 1.5em;
|
||||
}
|
||||
#footer p {
|
||||
clear: left;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.splash {
|
||||
padding: 12em 0 6em;
|
||||
background: #349aed;
|
||||
background: linear-gradient(145deg, #349aed 50%, #34d8ed 100%);
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
}
|
||||
.splash .logo {
|
||||
width: 160px;
|
||||
}
|
||||
.splash h1 {
|
||||
font-size: 3em;
|
||||
color: #fff;
|
||||
}
|
||||
.splash #social {
|
||||
margin: 2em 0 3em;
|
||||
}
|
||||
.splash .alert {
|
||||
margin: 2em 0;
|
||||
border: none;
|
||||
}
|
||||
.splash .sponsor a {
|
||||
color: #fff;
|
||||
}
|
||||
.section-tout {
|
||||
padding: 6em 0 1em;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
|
||||
background-color: #eaf1f1;
|
||||
text-align: center;
|
||||
}
|
||||
.section-tout .icon {
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-ms-flex-pack: center;
|
||||
justify-content: center;
|
||||
-ms-flex-align: center;
|
||||
align-items: center;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
margin: 0 auto 1rem;
|
||||
background: #349aed;
|
||||
background: linear-gradient(145deg, #3b9cea 50%, #3db8eb 100%);
|
||||
border-radius: 50%;
|
||||
font-size: 2rem;
|
||||
color: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
.section-tout p {
|
||||
margin-bottom: 5em;
|
||||
}
|
||||
.section-preview {
|
||||
padding: 4em 0;
|
||||
}
|
||||
.section-preview .preview {
|
||||
margin-bottom: 4em;
|
||||
background-color: #eaf1f1;
|
||||
}
|
||||
.section-preview .preview .image {
|
||||
position: relative;
|
||||
}
|
||||
.section-preview .preview .image::before {
|
||||
box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1);
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
content: "";
|
||||
pointer-events: none;
|
||||
}
|
||||
.section-preview .preview .options {
|
||||
padding: 2em;
|
||||
border: 1px solid rgba(0, 0, 0, 0.05);
|
||||
border-top: none;
|
||||
text-align: center;
|
||||
}
|
||||
.section-preview .preview .options p {
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
.section-preview .dropdown-menu {
|
||||
text-align: left;
|
||||
}
|
||||
.section-preview .lead {
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
.sponsor #carbonads {
|
||||
max-width: 240px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.sponsor .carbon-text {
|
||||
display: block;
|
||||
margin-top: 1em;
|
||||
font-size: 12px;
|
||||
}
|
||||
.sponsor .carbon-poweredby {
|
||||
float: right;
|
||||
margin-top: 1em;
|
||||
font-size: 10px;
|
||||
}
|
||||
@media (max-width: 767px) {
|
||||
.splash {
|
||||
padding-top: 8em;
|
||||
}
|
||||
.splash .logo {
|
||||
width: 100px;
|
||||
}
|
||||
.splash h1 {
|
||||
font-size: 2em;
|
||||
}
|
||||
#banner {
|
||||
margin-bottom: 2em;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-group {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
display: none;
|
||||
position: absolute;
|
||||
padding: 10px;
|
||||
color: #777;
|
||||
background-color: #fff;
|
||||
border: 1px solid #777;
|
||||
box-shadow: 0 1px 3px 1px rgba(0, 0, 0, 0.5);
|
||||
border-radius: 3px;
|
||||
}
|
||||
+5
File diff suppressed because one or more lines are too long
@@ -0,0 +1,54 @@
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Lato';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
font-display: swap;
|
||||
src: url(../webfonts/lato-1.woff2) format('woff2');
|
||||
unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Lato';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
font-display: swap;
|
||||
src: url(../webfonts/lato-2.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Lato';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(../webfonts/lato-3.woff2) format('woff2');
|
||||
unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Lato';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(../webfonts/lato-4.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
/* latin-ext */
|
||||
@font-face {
|
||||
font-family: 'Lato';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src: url(../webfonts/lato-5.woff2) format('woff2');
|
||||
unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
/* latin */
|
||||
@font-face {
|
||||
font-family: 'Lato';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
src: url(../webfonts/lato-6.woff2) format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
/*!
|
||||
* Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
*/
|
||||
@font-face{font-family:"Font Awesome 5 Free";font-style:normal;font-weight:900;font-display:block;src:url(../webfonts/fa-solid-900.eot);src:url(../webfonts/fa-solid-900.eot?#iefix) format("embedded-opentype"),url(../webfonts/fa-solid-900.woff2) format("woff2"),url(../webfonts/fa-solid-900.woff) format("woff"),url(../webfonts/fa-solid-900.ttf) format("truetype"),url(../webfonts/fa-solid-900.svg#fontawesome) format("svg")}.fa,.fas{font-family:"Font Awesome 5 Free";font-weight:900}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.2 KiB |
@@ -0,0 +1,209 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Filament Dry Box v2 - Sasa Karanovic</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="css/bootstrap.min.css" />
|
||||
<link rel="stylesheet" href="css/custom.min.css" />
|
||||
<link href="css/fontawesome.min.css" rel="stylesheet">
|
||||
<link href="css/brands.min.css" rel="stylesheet">
|
||||
<link href="css/solid.min.css" rel="stylesheet">
|
||||
<link rel="shortcut icon" type="image/jpg" href="img/favicon.png" />
|
||||
</head>
|
||||
<body class="d-flex flex-column min-vh-100">
|
||||
<div class="navbar navbar-expand-lg fixed-top navbar-dark dark-bg">
|
||||
<div class="container">
|
||||
<a href="index.html" class="navbar-brand">Filament Dry Box v2</a>
|
||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarResponsive">
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="settings.html">Settings</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="https://sasakaranovic.com/projects/diy-filament-dry-box-v2/">Project Home</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="https://github.com/SasaKaranovic/DIY-Filament-Dryer-v2" target="_blank">Source Code</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Container -->
|
||||
<div class="container">
|
||||
<!-- Content - First row -->
|
||||
<div class="row">
|
||||
<!-- Left column -->
|
||||
<div class="col-lg-5 col-md-5 col-sm-5">
|
||||
<!-- DryBox status title -->
|
||||
<div class="page-header">
|
||||
<h2>DryBox Status</h2>
|
||||
</div>
|
||||
<!-- DryBox status title end -->
|
||||
|
||||
<!-- DryBox status -->
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm table-borderless">
|
||||
<tr>
|
||||
<td colspan="4"><span id="status-box" class="badge badge-pill badge-warning">Dry box is $STATUS$</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="col"></th>
|
||||
<th scope="col">Inside</th>
|
||||
<th scope="col">Outside</th>
|
||||
<th scope="col"></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Temperature</td>
|
||||
<td><span id="status-temp-inside">$TEMP_IN$</span>°C</td>
|
||||
<td><span id="status-temp-outside">$TEMP_OUT$</span>°C</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Humidity</td>
|
||||
<td><span id="status-humidity-inside">$HUMIDITY_IN$</span>%</td>
|
||||
<td><span id="status-humidity-outside">$HUMIDITY_OUT$</span>%</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="4"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="4"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="4"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="col"></th>
|
||||
<th scope="col">Current</th>
|
||||
<th scope="col">Limit</th>
|
||||
<th scope="col"></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Heater Temperature</td>
|
||||
<td><span id="status-temp-heater">$HEATER_TEMPERATURE$</span>°C</td>
|
||||
<td><span id="status-temp-heater-max">$TARGET_HEATER_TEMPERATURE$</span>°C</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Temperature Inside</td>
|
||||
<td><span id="status-temp-inside2">$TEMP_IN$</span>°C</td>
|
||||
<td><span id="status-temp-inside-target">$TARGET_INSIDE_TEMPERATURE$</span>°C</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">Heater Power</td>
|
||||
<td colspan="3"><span id="status-heater-power">$HEATER_POWER$</span>%</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1">Fan speed</td>
|
||||
<td colspan="3"><span id="status-fan-speed">$FAN_POWER$</span>%</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- DryBox status END -->
|
||||
</div>
|
||||
<!-- Left column END -->
|
||||
|
||||
<!-- Right column -->
|
||||
<div class="col-lg-5 col-md-5 col-sm-5 offset-lg-1">
|
||||
<div class="row">
|
||||
<!-- DryBox control -->
|
||||
<div class="col-lg-12">
|
||||
<h2>Control</h2>
|
||||
</div>
|
||||
<!-- DryBox control END -->
|
||||
|
||||
<div class="col-lg-12">
|
||||
<legend>Temperature</legend>
|
||||
|
||||
<fieldset class="form-group">
|
||||
<label for="labelSetTemperature">Target temperature: $TARGET_INSIDE_TEMPERATURE$°C</label>
|
||||
<div class="float-right">
|
||||
<a href="#" id="pop-temp"><img src="img/help.png" /></a>
|
||||
</div>
|
||||
<input type="range" class="custom-range" id="sliderTemperature" min="15" max="80" step="1" value="$TARGET_INSIDE_TEMPERATURE$" />
|
||||
</fieldset>
|
||||
|
||||
<legend>Fan speed</legend>
|
||||
<fieldset class="form-group">
|
||||
<label for="labelSetFanSpeed">Fan speed: $FAN_POWER$%</label>
|
||||
<div class="float-right">
|
||||
<a href="#" id="pop-fan"><img src="img/help.png" /></a>
|
||||
</div>
|
||||
<input type="range" class="custom-range" id="sliderFanSpeed" min="0" max="100" step="5" value="$FAN_POWER$" />
|
||||
</fieldset>
|
||||
|
||||
<legend>Max Heater Temperature</legend>
|
||||
<fieldset class="form-group">
|
||||
<label for="labelSetHeaterTemperature">Max heater temperature: $TARGET_HEATER_TEMPERATURE$°C</label>
|
||||
<div class="float-right">
|
||||
<a href="#" id="pop-heater"><img src="img/help.png" /></a>
|
||||
</div>
|
||||
<input type="range" class="custom-range" id="sliderHeaterTemperature" min="30" max="80" step="1" value="$TARGET_HEATER_TEMPERATURE$" />
|
||||
</fieldset>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-12">
|
||||
<div class="btn-group btn-group-md mr-2" role="group">
|
||||
<button type="button" id="btnSet" class="btn btn-primary mr-2"><i class="fas fa-fire"></i> Turn ON</button>
|
||||
<button type="button" id="btnTurnOff" class="btn btn-primary"><i class="fas fa-power-off"></i> Turn OFF</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Right column END -->
|
||||
</div>
|
||||
<!-- Content - First row END -->
|
||||
|
||||
<!-- Content - Second row -->
|
||||
<div class="clearfix"></div>
|
||||
<div style="padding-top: 3em;">
|
||||
<div id="chart" style="margin-top: 3em;">
|
||||
<canvas id="myChart" width="600" height="200"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Content - Second row END -->
|
||||
|
||||
</div>
|
||||
<!-- Container END -->
|
||||
|
||||
<!-- FOOTER -->
|
||||
<footer class="w-100 py-2 flex-shrink-0 mt-auto dark-bg">
|
||||
<div class="container py-2">
|
||||
<div class="row gy-4 gx-5">
|
||||
<div class="col-lg-6 col-md-6">
|
||||
<p class="small text-muted">
|
||||
Dry box controller by <a href="https://SasaKaranovic.com/" target="_blank">Saša Karanović</a><br>
|
||||
Released as open-source and open-hardware for you to hack and enjoy! :)<br>
|
||||
Firmware version $VERSION_MAJOR$$VERSION_MINOR$$VERSION_PATCH$
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-lg-6 col-md-6">
|
||||
<a class="m-2" href="https://sasakaranovic.com/" target="_blank"><i class="fas fa-home"></i> Blog</a>
|
||||
<a class="m-2" href="https://youtube.com/c/sasakaranovic" target="_blank"><i class="fab fa-youtube"></i> YouTube</a>
|
||||
<a class="m-2" href="https://twitter.com/_sasakaranovic_" target="_blank"><i class="fab fa-twitter"></i> Twitter</a>
|
||||
<a class="m-2" href="https://github.com/sasakaranovic" target="_blank"><i class="fab fa-github"></i> GitHub</a>
|
||||
<a class="m-2" href="https://github.com/sponsors/SasaKaranovic" target="_blank"><i class="fas fa-heart"></i> Sponsor on GitHub</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<script src="js/jquery-3.6.0.min.js"></script>
|
||||
<script src="js/bootstrap.bundle.min.js"></script>
|
||||
<script src='js/chart.js'></script>
|
||||
<script src="js/drybox.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Vendored
+1
File diff suppressed because one or more lines are too long
@@ -0,0 +1,214 @@
|
||||
let measurements_chart;
|
||||
|
||||
// Shorthand for $( document ).ready()
|
||||
$(function() {
|
||||
|
||||
$('#sliderTemperature').on('input change', function(){
|
||||
$("label[for='labelSetTemperature']").text("Target temperature: " + parseInt(this.value) +"°C");
|
||||
});
|
||||
|
||||
$('#sliderHeaterTemperature').on('input change', function(){
|
||||
$("label[for='labelSetHeaterTemperature']").text("Max heater temperature: " + parseInt(this.value) +"°C");
|
||||
});
|
||||
|
||||
$('#sliderFanSpeed').on('input change', function(){
|
||||
$("label[for='labelSetFanSpeed']").text("Fan speed: " + parseInt(this.value) +"%");
|
||||
});
|
||||
|
||||
$('#btnSet').on('click', function(){
|
||||
var temperature = $("#sliderTemperature").val();
|
||||
var heater = $("#sliderHeaterTemperature").val();
|
||||
var fanspeed = $("#sliderFanSpeed").val();
|
||||
|
||||
// Send
|
||||
$.ajax({
|
||||
url: "/api/v0/turn_on",
|
||||
type: "get", //send it through get method
|
||||
data: {
|
||||
temperature: temperature,
|
||||
heater: heater,
|
||||
fanspeed: fanspeed,
|
||||
},
|
||||
timeout: 2000,
|
||||
success: function(response) {
|
||||
//Do Something
|
||||
console.log(response);
|
||||
update_status(false);
|
||||
},
|
||||
error: function(xhr) {
|
||||
//Do Something to handle error
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
$('#btnTurnOff').on('click', function(){
|
||||
// Send
|
||||
$.ajax({
|
||||
url: "/api/v0/turn_off",
|
||||
type: "get", //send it through get method
|
||||
timeout: 2000,
|
||||
success: function(response) {
|
||||
console.log(response);
|
||||
update_status(false);
|
||||
},
|
||||
error: function(xhr) {
|
||||
//Do Something to handle error
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$('a#pop-temp').popover({
|
||||
trigger : 'click',
|
||||
placement : 'top',
|
||||
html: true,
|
||||
title : 'Target Temperature',
|
||||
content : 'This is the maximum/desired temperature we want inside the box.<br>\
|
||||
The system will try to reach and then maintain this temperature.',
|
||||
});
|
||||
|
||||
|
||||
$('a#pop-fan').popover({
|
||||
trigger : 'click',
|
||||
placement : 'top',
|
||||
html: true,
|
||||
title : 'Fan speed',
|
||||
content : 'Set the speed of the internal fan used to circulate the air inside the dry box.<br><br>\
|
||||
0% = Off<br>\
|
||||
50% = 50% of fan\'s maximum RPM<br>\
|
||||
100% = Full speed',
|
||||
});
|
||||
|
||||
|
||||
$('a#pop-heater').popover({
|
||||
trigger : 'click',
|
||||
placement : 'top',
|
||||
html: true,
|
||||
title : 'Max heater temperature',
|
||||
content : 'Maximum allowed temerature heater can reach.<br>This limit should be set to avoid damage to your filament or the box/heater itself.<br>\
|
||||
For example; Let\'s say that your heater could reach max temperature of 150°C. However this could start to melt your filament, melt the dry box or start a fire.<br>\
|
||||
In order to avoid this, we set a heater temperature limit to make sure heater never passes this set temperature during normal operation.<br>\
|
||||
This limit will also affect how long it takes to reach target temperature.',
|
||||
});
|
||||
|
||||
const ctx = document.getElementById('myChart');
|
||||
measurements_chart = new Chart(ctx, {
|
||||
type: 'line',
|
||||
data: {
|
||||
datasets: [
|
||||
{
|
||||
label: 'Heater',
|
||||
data: []
|
||||
},
|
||||
{
|
||||
label: 'Enclosure',
|
||||
data: []
|
||||
}
|
||||
]
|
||||
},
|
||||
options: {
|
||||
animation: false, // disables all animations
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: false,
|
||||
suggestedMin: 25,
|
||||
// suggestedMax: 2.25,
|
||||
ticks: {
|
||||
// forces step size to be 1.0 units
|
||||
stepSize: 1
|
||||
}
|
||||
},
|
||||
x: {
|
||||
beginAtZero: false,
|
||||
reverse: true
|
||||
}
|
||||
},
|
||||
plugins: {
|
||||
legend: {
|
||||
display: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Finally update status
|
||||
update_status(true);
|
||||
});
|
||||
|
||||
|
||||
function update_status(rearm)
|
||||
{
|
||||
var ajaxTime= new Date().getTime();
|
||||
|
||||
$.ajax({
|
||||
url: "/api/v0/status",
|
||||
type: "get", //send it through get method
|
||||
timeout: 2000,
|
||||
success: function(response) {
|
||||
console.log(response);
|
||||
|
||||
var res = jQuery.parseJSON(response);
|
||||
|
||||
$('#status-temp-target').text(res.target_temp_in);
|
||||
$('#status-fan-speed').text(res.fan_speed);
|
||||
$('#status-temp-inside').text(res.temp_in.toFixed(1));
|
||||
$('#status-temp-inside2').text(res.temp_in.toFixed(1));
|
||||
$('#status-temp-inside-target').text(res.target_temp_in.toFixed(1));
|
||||
$('#status-temp-heater').text(res.temp_heater.toFixed(1));
|
||||
$('#status-temp-heater-max').text(res.max_temp_heater.toFixed(0));
|
||||
$('#status-humidity-inside').text(res.humid_in.toFixed(0));
|
||||
$('#status-temp-outside').text(res.temp_out.toFixed(0));
|
||||
$('#status-humidity-outside').text(res.humid_out.toFixed(0));
|
||||
$('#status-heater-power').text(res.heater_power.toFixed(0));
|
||||
|
||||
chart_add_point(0, {x: get_timestamp(), y: res.temp_heater.toFixed(1)});
|
||||
chart_add_point(1, {x: get_timestamp(), y: res.temp_in.toFixed(1)});
|
||||
|
||||
// Homed status
|
||||
if(res.status == 1) {
|
||||
$('#status-box').text('Dry box is on').addClass('badge-success').removeClass('badge-warning').removeClass('badge-danger');
|
||||
}
|
||||
else {
|
||||
$('#status-box').text('Dry box is off').addClass('badge-warning').removeClass('badge-success').removeClass('badge-danger');
|
||||
}
|
||||
|
||||
},
|
||||
error: function(xhr) {
|
||||
$('#status-box').text('Dry box is unreachable!').addClass('badge-danger').removeClass('badge-success').removeClass('badge-warning');
|
||||
}
|
||||
}).done(function () {
|
||||
var totalTime = new Date().getTime()-ajaxTime;
|
||||
// Here I want to get the how long it took to load some.php and use it further
|
||||
});;
|
||||
|
||||
if(rearm) {
|
||||
rearm_status();
|
||||
}
|
||||
}
|
||||
|
||||
function rearm_status()
|
||||
{
|
||||
setTimeout(function () {
|
||||
update_status(true);
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
function chart_add_point(chartIndex, newData) {
|
||||
measurements_chart.data.datasets[chartIndex].data.unshift(newData);
|
||||
if (measurements_chart.data.datasets[chartIndex].data.length >= 900)
|
||||
{
|
||||
measurements_chart.data.datasets[chartIndex].data.shift();
|
||||
}
|
||||
|
||||
measurements_chart.update('none');
|
||||
}
|
||||
|
||||
function get_timestamp()
|
||||
{
|
||||
let date = new Date();
|
||||
return twoDigitPad(date.getHours()) +':'+ twoDigitPad(date.getMinutes()) +':'+ twoDigitPad(date.getSeconds());
|
||||
}
|
||||
|
||||
function twoDigitPad(num) {
|
||||
return num < 10 ? "0" + num : num;
|
||||
}
|
||||
+2
File diff suppressed because one or more lines are too long
@@ -0,0 +1,74 @@
|
||||
// Shorthand for $( document ).ready()
|
||||
$(function() {
|
||||
|
||||
$('#btn_SavePIDSettings').on('click', function(){
|
||||
var kp = $("#input_kp").val();
|
||||
var ki = $("#input_ki").val();
|
||||
var kd = $("#input_kd").val();
|
||||
|
||||
// Send
|
||||
$.ajax({
|
||||
url: "/api/v0/pid/set",
|
||||
type: "get", //send it through get method
|
||||
data: {
|
||||
p: kp,
|
||||
i: ki,
|
||||
d: kd,
|
||||
},
|
||||
timeout: 2000,
|
||||
success: function(response) {
|
||||
console.log(response);
|
||||
alert("PID values updated and applied!");
|
||||
},
|
||||
error: function(xhr) {
|
||||
console.log(xhr);
|
||||
alert("Error updating values. Please check console.");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$('a#pop-pid-calc').popover({
|
||||
trigger : 'click',
|
||||
placement : 'top',
|
||||
html: true,
|
||||
title : 'PID Calculation',
|
||||
content : 'We output PWM signal to the heater that can have duty between 0 and PWM_MAX_VALUE.<br>\
|
||||
`Target temperature` - Either desired inside temperature, or maximum allowed heater temperature (whichever is lower). <br>\
|
||||
T_current - Current measured temperature. <br>\
|
||||
T_previous - Previously measured temperature.<br>\
|
||||
T_delta - T_current - T_previous.<br>\
|
||||
T_target - Current target temperature. <br>\
|
||||
T_avg - Average temperature, sampled over last $ADC_HEATER_SAMPLES$ cycles.<br>\<br>\
|
||||
PWM value is calculated as follows:<br>\
|
||||
PWM_P = (T_target - T_current) * KP<br>\
|
||||
PWM_I = (T_target - T_avg) * KI<br>\
|
||||
PWM_D = (T_target - T_delta) * KD<br>\
|
||||
PWM = PWM_P + PWM_I + PWM_D\
|
||||
'
|
||||
});
|
||||
|
||||
$('#btn_SaveTemperatureSettings').on('click', function(){
|
||||
var compensateIn = $("#input_in_temperature").val();
|
||||
var compensateOut = $("#input_out_temperature").val();
|
||||
|
||||
// Send
|
||||
$.ajax({
|
||||
url: "/api/v0/compensation/temperature/set",
|
||||
type: "get", //send it through get method
|
||||
data: {
|
||||
temperatureIn: compensateIn,
|
||||
temperatureOut: compensateOut,
|
||||
},
|
||||
timeout: 2000,
|
||||
success: function(response) {
|
||||
console.log(response);
|
||||
alert("Temperature compensation values updated and applied!");
|
||||
},
|
||||
error: function(xhr) {
|
||||
console.log(xhr);
|
||||
alert("Error updating values. Please check console.");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
@@ -0,0 +1,368 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Filament Dry Box v2 - Sasa Karanovic</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="css/bootstrap.min.css" />
|
||||
<link rel="stylesheet" href="css/custom.min.css" />
|
||||
<link href="css/fontawesome.min.css" rel="stylesheet">
|
||||
<link href="css/brands.min.css" rel="stylesheet">
|
||||
<link href="css/solid.min.css" rel="stylesheet">
|
||||
<link rel="shortcut icon" type="image/jpg" href="img/favicon.png" />
|
||||
</head>
|
||||
<body class="d-flex flex-column min-vh-100">
|
||||
<div class="navbar navbar-expand-lg fixed-top navbar-dark dark-bg">
|
||||
<div class="container">
|
||||
<a href="index.html" class="navbar-brand">Filament Dry Box v2</a>
|
||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarResponsive">
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="settings.html">Settings</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="https://sasakaranovic.com/projects/diy-filament-dry-box-v2/">Project Home</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="https://github.com/SasaKaranovic/DIY-Filament-Dryer-v2" target="_blank">Source Code</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Page header - Camera slider -->
|
||||
<div class="container">
|
||||
|
||||
|
||||
<!-- Main content -->
|
||||
<div class="row">
|
||||
|
||||
|
||||
<!-- Left column -->
|
||||
<div class="col-lg-5 col-md-5 col-sm-5">
|
||||
|
||||
<div class="row mt-4">
|
||||
<div class="col-lg-12 mr-1">
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col-lg-12 mr-1">
|
||||
<legend>PID Values</legend>
|
||||
|
||||
<div class="alert alert-danger" role="alert">
|
||||
<p><i class="fas fa-exclamation"></i> WARNING <i class="fas fa-exclamation"></i></p>
|
||||
<p>Do _NOT_ modify below values unless you are absolutely sure that you know what you are doing!</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Left column -->
|
||||
<div class="col-lg-7 mr-2">
|
||||
<div class="float-right">
|
||||
<a href="#" id="pop-pid-calc"><img src="img/help.png" /></a>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="control-label">KP coefficient</label>
|
||||
<div class="input-group mb-1">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">P</span>
|
||||
</div>
|
||||
<input type="text" id="input_kp" class="form-control" size="5" maxlength="3" value="$PID_KP$">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="control-label">KI coefficient</label>
|
||||
<div class="input-group mb-1">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">I</span>
|
||||
</div>
|
||||
<input type="text" id="input_ki" class="form-control" size="5" maxlength="3" value="$PID_KI$">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="control-label">KD coefficient</label>
|
||||
<div class="input-group mb-1">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">D</span>
|
||||
</div>
|
||||
<input type="text" id="input_kd" class="form-control" size="5" maxlength="3" value="$PID_KD$">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mr-4">
|
||||
<div class="col-lg-12 mr-2">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<button type="button" id="btn_SavePIDSettings" class="btn btn-primary">Save settings</button>
|
||||
</div>
|
||||
|
||||
<div class="row mr-4">
|
||||
<div class="col-lg-12 mr-2">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<button type="button" id="btn_ResetSettings" class="btn btn-primary">Reset to default settings</button>
|
||||
</div>
|
||||
|
||||
</div> <!-- End left speed column -->
|
||||
|
||||
</div >
|
||||
|
||||
<div class="clearfix"></div>
|
||||
<div style="padding-top: 3em;"></div>
|
||||
<div class="row">
|
||||
|
||||
<div class="col-lg-12 mr-1">
|
||||
<legend>Temperature compensation</legend>
|
||||
</div>
|
||||
|
||||
<!-- Left column -->
|
||||
<div class="col-lg-7 mr-2">
|
||||
<div class="float-right">
|
||||
<a href="#" id="pop-pid-calc"><img src="img/help.png" /></a>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="control-label">Inside temperature</label>
|
||||
<div class="input-group mb-1">
|
||||
<input type="text" id="input_in_temperature" class="form-control" size="5" maxlength="6" value="$COMPENSATE_TEMP_IN$">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="control-label">Outside temperature</label>
|
||||
<div class="input-group mb-1">
|
||||
<input type="text" id="input_out_temperature" class="form-control" size="5" maxlength="6" value="$COMPENSATE_TEMP_OUT$">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mr-4">
|
||||
<div class="col-lg-12 mr-2">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<button type="button" id="btn_SaveTemperatureSettings" class="btn btn-primary">Save settings</button>
|
||||
</div>
|
||||
|
||||
<div class="row mr-4">
|
||||
<div class="col-lg-12 mr-2">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div> <!-- End left speed column -->
|
||||
|
||||
</div >
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div> <!-- End left column -->
|
||||
|
||||
<!-- Right column -->
|
||||
<div class="col-lg-5 col-md-5 col-sm-5 offset-2">
|
||||
<div class="row">
|
||||
|
||||
</div>
|
||||
</div> <!-- End right column -->
|
||||
|
||||
<div class="container">
|
||||
<div class="p-5 text-center bg-body-tertiary">
|
||||
<h1 class="text-body-emphasis">Firmware information</h1>
|
||||
|
||||
<table class="table table-sm table-striped text-left">
|
||||
<thead>
|
||||
<tr class="d-flex">
|
||||
<th class="col-4 text-break">Parameter</th>
|
||||
<th class="col-2">Value</th>
|
||||
<th class="col-6">Note</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="d-flex">
|
||||
<td class="col-4 text-break">PWM_HEATER_INVERT_VALUES</td>
|
||||
<td class="col-2 text-break">$PWM_HEATER_INVERT_VALUES$</td>
|
||||
<td class="col-6">Maximum number of calibration points supported by firmware.</td>
|
||||
</tr>
|
||||
<tr class="d-flex">
|
||||
<td class="col-4 text-break">PWM_FREQ_HEATER</td>
|
||||
<td class="col-2 text-break">$PWM_FREQ_HEATER$</td>
|
||||
<td class="col-6">PWM frequency used for heater signal</td>
|
||||
</tr>
|
||||
<tr class="d-flex">
|
||||
<td class="col-4 text-break">PWM_FREQ_FAN</td>
|
||||
<td class="col-2 text-break">$PWM_FREQ_FAN$</td>
|
||||
<td class="col-6">PWM frequency used for fan PWM signal</td>
|
||||
</tr>
|
||||
<tr class="d-flex">
|
||||
<td class="col-4 text-break">PWM_MAX_VALUE</td>
|
||||
<td class="col-2 text-break">$PWM_MAX_VALUE$</td>
|
||||
<td class="col-6">Maximum value of the PWM channel</td>
|
||||
</tr>
|
||||
<tr class="d-flex">
|
||||
<td class="col-4 text-break">PWM_HEATER_INVERT_VALUES</td>
|
||||
<td class="col-2 text-break">$PWM_HEATER_INVERT_VALUES$</td>
|
||||
<td class="col-6">Heater driver requires inverted (1) or non-inverted (0) signal</td>
|
||||
</tr>
|
||||
<tr class="d-flex">
|
||||
<td class="col-4 text-break">PWM_CH_HEATER</td>
|
||||
<td class="col-2 text-break">GPIO$PWM_CH_HEATER$</td>
|
||||
<td class="col-6">GPIO controlling the heater</td>
|
||||
</tr>
|
||||
<tr class="d-flex">
|
||||
<td class="col-4 text-break">PWM_CH_FAN</td>
|
||||
<td class="col-2 text-break">GPIO$PWM_CH_FAN$</td>
|
||||
<td class="col-6">GPIO controlling the fan</td>
|
||||
</tr>
|
||||
<tr class="d-flex">
|
||||
<td class="col-4 text-break">LIMIT_TEMP_IN_MAX</td>
|
||||
<td class="col-2 text-break">$LIMIT_TEMP_IN_MAX$</td>
|
||||
<td class="col-6">Maximum INSIDE temperature</td>
|
||||
</tr>
|
||||
<tr class="d-flex">
|
||||
<td class="col-4 text-break">LIMIT_TEMP_HEATER_MAX</td>
|
||||
<td class="col-2 text-break">$LIMIT_TEMP_HEATER_MAX$</td>
|
||||
<td class="col-6">Maximum HEATER temperature</td>
|
||||
</tr>
|
||||
<tr class="d-flex">
|
||||
<td class="col-4 text-break">NTC_BETA</td>
|
||||
<td class="col-2 text-break">$NTC_BETA$</td>
|
||||
<td class="col-6">Beta value of NTC thermistor</td>
|
||||
</tr>
|
||||
<tr class="d-flex">
|
||||
<td class="col-4 text-break">NTC_RESISTANCE</td>
|
||||
<td class="col-2 text-break">$NTC_RESISTANCE$</td>
|
||||
<td class="col-6">NTC resistance</td>
|
||||
</tr>
|
||||
<tr class="d-flex">
|
||||
<td class="col-4 text-break">NTC_R1_VALUE</td>
|
||||
<td class="col-2 text-break">$NTC_R1_VALUE$</td>
|
||||
<td class="col-6">Value of R1 in the NTC resistor divider</td>
|
||||
</tr>
|
||||
<tr class="d-flex">
|
||||
<td class="col-4 text-break">ADC_HEATER_SAMPLES</td>
|
||||
<td class="col-2 text-break">$ADC_HEATER_SAMPLES$</td>
|
||||
<td class="col-6">Number of ADC samples for NTC conversion</td>
|
||||
</tr>
|
||||
<tr class="d-flex">
|
||||
<td class="col-4 text-break">ADC_MAX_VAUE</td>
|
||||
<td class="col-2 text-break">$ADC_MAX_VAUE$</td>
|
||||
<td class="col-6">Maximum ADC value</td>
|
||||
</tr>
|
||||
<tr class="d-flex">
|
||||
<td class="col-4 text-break">COMPENSATE_TEMP_IN</td>
|
||||
<td class="col-2 text-break">$COMPENSATE_TEMP_IN$</td>
|
||||
<td class="col-6">Inside temperature compensation</td>
|
||||
</tr>
|
||||
<tr class="d-flex">
|
||||
<td class="col-4 text-break">COMPENSATE_TEMP_OUT</td>
|
||||
<td class="col-2 text-break">$COMPENSATE_TEMP_OUT$</td>
|
||||
<td class="col-6">Outside temperature compensation</td>
|
||||
</tr>
|
||||
<tr class="d-flex">
|
||||
<td class="col-4 text-break">WIFI_CHECK_CONNECTION_MS</td>
|
||||
<td class="col-2 text-break">$WIFI_CHECK_CONNECTION_MS$</td>
|
||||
<td class="col-6">How often (in miliseconds) to check for WiFi connectivity</td>
|
||||
</tr>
|
||||
<tr class="d-flex">
|
||||
<td class="col-4 text-break">BUILD_TIME</td>
|
||||
<td class="col-2 text-break">$BUILD_TIME$</td>
|
||||
<td class="col-6">Time when firmware was compiled</td>
|
||||
</tr>
|
||||
<tr class="d-flex">
|
||||
<td class="col-4 text-break">BUILD_DATE</td>
|
||||
<td class="col-2 text-break">$BUILD_DATE$</td>
|
||||
<td class="col-6">Date when firmware was compiled</td>
|
||||
</tr>
|
||||
<tr class="d-flex">
|
||||
<td class="col-4 text-break">BUILD_HASH</td>
|
||||
<td class="col-2 text-break">$BUILD_HASH$</td>
|
||||
<td class="col-6">Build Git hash (short)</td>
|
||||
</tr>
|
||||
<tr class="d-flex">
|
||||
<td class="col-4 text-break">ADC_CHIP</td>
|
||||
<td class="col-2 text-break">$ADC_CHIP$</td>
|
||||
<td class="col-6">ADC chip populated on the board</td>
|
||||
</tr>
|
||||
<tr class="d-flex">
|
||||
<td class="col-4 text-break">PID_MIN_VALUE</td>
|
||||
<td class="col-2 text-break">$PID_MIN_VALUE$</td>
|
||||
<td class="col-6">Minimum accepted P/I/D coefficient</td>
|
||||
</tr>
|
||||
<tr class="d-flex">
|
||||
<td class="col-4 text-break">PID_MAX_VALUE</td>
|
||||
<td class="col-2 text-break">$PID_MAX_VALUE$</td>
|
||||
<td class="col-6">Maximum accepted P/I/D coefficient</td>
|
||||
</tr>
|
||||
<tr class="d-flex">
|
||||
<td class="col-4 text-break">DEF_DEBUG_PID</td>
|
||||
<td class="col-2 text-break">$DEF_DEBUG_PID$</td>
|
||||
<td class="col-6">PID debug prinout over serial (1=Enabled)</td>
|
||||
</tr>
|
||||
<tr class="d-flex">
|
||||
<td class="col-4 text-break">I2C_DEVICE_LIST</td>
|
||||
<td class="col-2 text-break">$I2C_DEVICE_LIST$</td>
|
||||
<td class="col-6">List of I2C devices detected</td>
|
||||
</tr>
|
||||
<tr class="d-flex">
|
||||
<td class="col-4 text-break">FW_INT_TEMP_LIB_STYPE</td>
|
||||
<td class="col-2 text-break">$FW_INT_TEMP_LIB_STYPE$</td>
|
||||
<td class="col-6">FW SHT library sensor type</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="">
|
||||
<p class="col-lg-8 mx-auto fs-5 text-muted mb-4">
|
||||
Values in this table are provided for development/debugging purposes (and just for fun).<br>
|
||||
You should not have to worry about them during devices normal operation.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- FOOTER -->
|
||||
<footer class="w-100 py-2 flex-shrink-0 mt-auto dark-bg">
|
||||
<div class="container py-2">
|
||||
<div class="row gy-4 gx-5">
|
||||
<div class="col-lg-6 col-md-6">
|
||||
<p class="small text-muted">
|
||||
Dry box controller by <a href="https://SasaKaranovic.com/" target="_blank">Saša Karanović</a><br>
|
||||
Released as open-source and open-hardware for you to hack and enjoy! :)<br>
|
||||
Firmware version $VERSION_MAJOR$$VERSION_MINOR$$VERSION_PATCH$
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-lg-6 col-md-6">
|
||||
<a class="m-2" href="https://sasakaranovic.com/" target="_blank"><i class="fas fa-home"></i> Blog</a>
|
||||
<a class="m-2" href="https://youtube.com/c/sasakaranovic" target="_blank"><i class="fab fa-youtube"></i> YouTube</a>
|
||||
<a class="m-2" href="https://twitter.com/_sasakaranovic_" target="_blank"><i class="fab fa-twitter"></i> Twitter</a>
|
||||
<a class="m-2" href="https://github.com/sasakaranovic" target="_blank"><i class="fab fa-github"></i> GitHub</a>
|
||||
<a class="m-2" href="https://github.com/sponsors/SasaKaranovic" target="_blank"><i class="fas fa-heart"></i> Sponsor on GitHub</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<script src="js/jquery-3.6.0.min.js"></script>
|
||||
<script src="js/bootstrap.bundle.min.js"></script>
|
||||
<script src="js/settings.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,236 @@
|
||||
import os
|
||||
import io
|
||||
import shutil
|
||||
import gzip
|
||||
from datetime import datetime
|
||||
from itertools import islice
|
||||
|
||||
# Todo: Add option to chose between generating h+c files or .ino file
|
||||
# Todo: Add option to configure custom server handlers
|
||||
|
||||
class espembed:
|
||||
def __init__(self, data_directory, output_directory):
|
||||
self.data_directory = data_directory
|
||||
self.output_directory = output_directory
|
||||
self.files = {}
|
||||
self.max_line_length = 60
|
||||
self.supported_extensions = {
|
||||
".htm": { "gzip": False, "template_processor": None, "handler_generator": self.gen_handler_default, "encoding": "text/html", "ctype": "uint8_t"},
|
||||
".html":{ "gzip": False, "template_processor": None, "handler_generator": self.gen_handler_default, "encoding": "text/html", "ctype": "uint8_t"},
|
||||
".js": { "gzip": True, "template_processor": None, "handler_generator": self.gen_handler_default, "encoding": "text/javascript", "ctype": "uint8_t"},
|
||||
".css": { "gzip": True, "template_processor": None, "handler_generator": self.gen_handler_default, "encoding": "text/css", "ctype": "uint8_t"},
|
||||
".ico": { "gzip": True, "template_processor": None, "handler_generator": self.gen_handler_image, "encoding": "image/x-icon", "ctype": "uint8_t"},
|
||||
".png": { "gzip": True, "template_processor": None, "handler_generator": self.gen_handler_image, "encoding": "image/png", "ctype": "uint8_t"},
|
||||
".jpg": { "gzip": True, "template_processor": None, "handler_generator": self.gen_handler_image, "encoding": "image/jpeg", "ctype": "uint8_t"},
|
||||
".jpeg":{ "gzip": True, "template_processor": None, "handler_generator": self.gen_handler_image, "encoding": "image/jpeg", "ctype": "uint8_t"},
|
||||
".gif": { "gzip": True, "template_processor": None, "handler_generator": self.gen_handler_image, "encoding": "image/gif", "ctype": "uint8_t"},
|
||||
".woff": { "gzip": True, "template_processor": None, "handler_generator": self.gen_handler_default, "encoding": "font/woff", "ctype": "uint8_t"},
|
||||
".woff2": { "gzip": True, "template_processor": None, "handler_generator": self.gen_handler_default, "encoding": "font/woff2", "ctype": "uint8_t"},
|
||||
}
|
||||
self.web_handler_links = []
|
||||
self.web_data_header = ""
|
||||
self.web_data_content = ""
|
||||
# self.output_path = output_directory
|
||||
self.output_filename = "binary_data.ino"
|
||||
|
||||
def add_file(self, filepath, handler, encoding=None, template_processor=None, handler_generator=None):
|
||||
extension = os.path.splitext(filepath)[1]
|
||||
if extension not in self.supported_extensions.keys():
|
||||
print(f"File `{filepath}` is not in the supported extensions list. Skipping.")
|
||||
return None
|
||||
|
||||
# Option to override default encoding
|
||||
if encoding is None:
|
||||
encoding = self.supported_extensions[extension]['encoding']
|
||||
|
||||
# Option to override default template_processor
|
||||
if template_processor is None:
|
||||
template_processor = self.supported_extensions[extension]['template_processor']
|
||||
|
||||
# Option to override default handler_generator
|
||||
if handler_generator is None:
|
||||
if self.supported_extensions[extension]['handler_generator'] is None:
|
||||
handler_generator = self.gen_handler_default
|
||||
else:
|
||||
handler_generator = self.supported_extensions[extension]['handler_generator']
|
||||
|
||||
|
||||
# Read file contents
|
||||
with open(filepath, mode="rb") as f:
|
||||
content = f.read()
|
||||
|
||||
# If gzip is enabled
|
||||
if self.supported_extensions[extension]['gzip']:
|
||||
print(len(content))
|
||||
content = gzip.compress(content)
|
||||
print(len(content))
|
||||
gzipped = True
|
||||
else:
|
||||
gzipped = False
|
||||
|
||||
item = {
|
||||
'binary_data' : content,
|
||||
"gzipped": gzipped,
|
||||
'size': len(content),
|
||||
'handler': handler ,
|
||||
'encoding': encoding,
|
||||
'ctype': self.supported_extensions[extension]['ctype'],
|
||||
'template_processor': template_processor,
|
||||
'handler_generator': handler_generator
|
||||
}
|
||||
print(f"\t- File content length {len(content)} (gzipped: {gzipped}))")
|
||||
self.files[handler] = item
|
||||
|
||||
def add_directory(self, directory, base_start):
|
||||
for root, dirs, files in os.walk(directory):
|
||||
for file in files:
|
||||
extension = os.path.splitext(file)[1]
|
||||
if extension in self.supported_extensions.keys():
|
||||
handler_base = root.replace(base_start, '').replace('\\', '/')
|
||||
handler = f"{handler_base}/{file}"
|
||||
filepath = os.path.join(root, file)
|
||||
print(f"-- Adding handler for `{handler}` located at `{filepath}`")
|
||||
self.add_file(filepath, handler)
|
||||
|
||||
def binary_data_to_hex_string(self, data):
|
||||
# Providing compatibility for pre 3.12
|
||||
def _batched(iterable, n):
|
||||
if n < 1:
|
||||
raise ValueError('n must be at least one')
|
||||
it = iter(iterable)
|
||||
while batch := tuple(islice(it, n)):
|
||||
yield batch
|
||||
|
||||
ret = ','.join('0x{:02X}'.format(x) for x in data)
|
||||
ret = ",\n".join(", ".join(l) for l in _batched(ret.split(","), 16))
|
||||
return ret
|
||||
|
||||
def generate_c_const_name(self, handler):
|
||||
ret = f"web_data_{handler}"
|
||||
ret = ret.replace('/', '_')
|
||||
ret = ret.replace('\\', '_')
|
||||
ret = ret.replace('-', '_')
|
||||
ret = ret.replace('.', '_')
|
||||
ret = ret.replace('__', '_')
|
||||
return ret
|
||||
|
||||
def write_file_data(self, handler, data):
|
||||
dec_name = self.generate_c_const_name(handler)
|
||||
data_arr_str = self.binary_data_to_hex_string(data)
|
||||
|
||||
header = f""
|
||||
content = f""
|
||||
item = { 'handler': handler, 'header':header, 'content':content }
|
||||
self.web_handler_links.append(item)
|
||||
|
||||
def gen_handler_default(self, data_const_name, item):
|
||||
gzip = ""
|
||||
if item['gzipped']:
|
||||
gzip = 'response->addHeader("Content-Encoding", "gzip");\n\t\t'
|
||||
|
||||
if item['template_processor'] is None:
|
||||
template_processor = ""
|
||||
else:
|
||||
template_processor = f", {item['template_processor']}"
|
||||
|
||||
# request->send_P(200, "{encoding}", {variable_name});
|
||||
ret = f'''server.on("{item['handler']}", HTTP_GET, [](AsyncWebServerRequest *request){{
|
||||
AsyncWebServerResponse *response = request->beginResponse_P(200, "{item['encoding']}", {data_const_name}, {item['size']}{template_processor});
|
||||
{gzip}request->send(response);
|
||||
}});'''
|
||||
return ret
|
||||
|
||||
def gen_handler_image(self, data_const_name, item):
|
||||
gzip = ""
|
||||
if item['gzipped']:
|
||||
gzip = 'response->addHeader("Content-Encoding", "gzip");\n\t\t'
|
||||
|
||||
ret = f'''server.on("{item['handler']}", HTTP_GET, [](AsyncWebServerRequest *request){{
|
||||
AsyncWebServerResponse *response = request->beginResponse_P(200, "{item['encoding']}", {data_const_name}, {item['size']});
|
||||
{gzip}request->send(response);
|
||||
}});'''
|
||||
return ret
|
||||
|
||||
def set_template_processor(self, extension, function_name):
|
||||
try:
|
||||
extension = extension.lower()
|
||||
self.supported_extensions[extension]['template_processor'] = function_name
|
||||
except KeyError as e:
|
||||
print(f"Extensions `{extension}` is not supported.")
|
||||
return
|
||||
|
||||
def generate_output_file(self, filepath):
|
||||
forward_declarations = io.StringIO()
|
||||
data_section = io.StringIO()
|
||||
request_handlers = io.StringIO()
|
||||
|
||||
data_section.write("\n\n//---- BEGIN DATA SECTION ----\n\n")
|
||||
request_handlers.write("\n\n//---- Request handler ----\n")
|
||||
request_handlers.write("void server_init_handlers(void)\n{\n")
|
||||
|
||||
|
||||
# Iterate over all items and generate content for output file
|
||||
for handler, item in self.files.items():
|
||||
'''
|
||||
# Write forward declarations
|
||||
forward_declarations.write('\n\n//---- Forward delcarations ----\n\n')
|
||||
for _, item in self.files.items():
|
||||
forward_declarations.write(f"// Data for handler `{item['handler']}`\n")
|
||||
forward_declarations.write(f"extern const char {item['c_data_const_name']}[{item['size']}];\n")
|
||||
'''
|
||||
|
||||
# --- Write item to data section
|
||||
# todo: Optionally we could gzip/compress data here before embedding
|
||||
c_data_const_name = self.generate_c_const_name(handler)
|
||||
c_hex_str_data = self.binary_data_to_hex_string(item['binary_data'])
|
||||
c_server_handler = item['handler_generator'](c_data_const_name, self.files[handler])
|
||||
|
||||
data_section.write(f"// Content of `{handler}`\n")
|
||||
data_section.write(f"const {item['ctype']} {c_data_const_name}[{item['size']}] PROGMEM = {{\n{c_hex_str_data}\n}};\n")
|
||||
# f.write(f"const uint8_t {item['c_data_const_name']}[] PROGMEM = {{\n{item['c_hex_str_data']}\n}};\n")
|
||||
|
||||
# --- Create request handler for item
|
||||
# Write server request handlers
|
||||
request_handlers.write(f"\t{c_server_handler}\n")
|
||||
|
||||
# -- Finalize data and handler sections
|
||||
data_section.write("\n\n//---- END DATA SECTION ----\n\n")
|
||||
request_handlers.write("}\n")
|
||||
|
||||
# Merge content
|
||||
content = data_section.getvalue() + '\n\n' + request_handlers.getvalue()
|
||||
self.write_ino_file(content);
|
||||
data_section.close()
|
||||
request_handlers.close()
|
||||
forward_declarations.close()
|
||||
|
||||
def write_ino_file(self, content):
|
||||
filename = os.path.join(self.output_directory, self.output_filename)
|
||||
with open(filename, mode='w', encoding='utf-8') as f:
|
||||
f.write("// This file was auto generated using ESP32 Data Embed script\n")
|
||||
f.write("// Please do not manually modify this file\n")
|
||||
f.write("// since any changes to this file will be overwritten on next build.\n")
|
||||
f.write(f"// Generated on {datetime.today().strftime('%Y-%m-%d %H:%M:%S')}\n\n")
|
||||
f.write(content)
|
||||
f.write("\n// ---- End of auto generated file\n")
|
||||
|
||||
def run(self):
|
||||
for handler, file_data in self.files.items():
|
||||
self.write_file_data(handler, file_data['binary_data'])
|
||||
|
||||
self.generate_output_file(self.output_directory)
|
||||
|
||||
def main(data_dir, src_dir):
|
||||
embed = espembed(data_dir, src_dir)
|
||||
embed.set_template_processor('.htm', 'template_const_processor')
|
||||
embed.set_template_processor('.html', 'template_const_processor')
|
||||
embed.add_directory('data', 'data')
|
||||
embed.run()
|
||||
|
||||
Import("env")
|
||||
print("\n--- Embedding binary data...")
|
||||
path = os.getcwd()
|
||||
data_dir = os.path.join(path, 'data')
|
||||
src_dir = os.path.join(path, 'src')
|
||||
main(data_dir, src_dir)
|
||||
print("--- Embedding binary data: complete")
|
||||
@@ -0,0 +1,46 @@
|
||||
# Sasa Karanovic's Simple script to make `version.h` file from current date-time and git commit
|
||||
|
||||
import datetime
|
||||
import subprocess
|
||||
|
||||
def get_git_revision_hash() -> str:
|
||||
return subprocess.check_output(['git', 'rev-parse', 'HEAD']).decode('ascii').strip()
|
||||
|
||||
def get_git_revision_short_hash() -> str:
|
||||
return subprocess.check_output(['git', 'rev-parse', '--short', 'HEAD']).decode('ascii').strip()
|
||||
|
||||
def write_version_to_file(filepath, contents):
|
||||
with open(filepath, "w", encoding="utf-8") as f:
|
||||
f.write(contents)
|
||||
|
||||
def make_version_string():
|
||||
now = datetime.datetime.now()
|
||||
year = now.strftime('%Y')
|
||||
month = now.strftime('%m')
|
||||
day = now.strftime('%d')
|
||||
git_long_hash = get_git_revision_hash()
|
||||
git_short_hash = get_git_revision_short_hash()
|
||||
|
||||
version_header = \
|
||||
f"""#ifndef __DIY_DRYBOX_V2_FW_VERSION_H_INC__
|
||||
#define __DIY_DRYBOX_V2_FW_VERSION_H_INC__
|
||||
|
||||
#define VERSION_MAJOR "{year}"
|
||||
#define VERSION_MINOR "{month}"
|
||||
#define VERSION_PATCH "{day}"
|
||||
#define GITH_LONG_HASH "{git_long_hash}"
|
||||
#define GITH_SHORT_HASH "{git_short_hash}"
|
||||
|
||||
#endif
|
||||
"""
|
||||
|
||||
print(f"New version {year}-{month}-{day}")
|
||||
return version_header
|
||||
|
||||
def make_version():
|
||||
print("--- Cutting version ---")
|
||||
version = make_version_string()
|
||||
write_version_to_file('src/version.h', version)
|
||||
|
||||
Import("env")
|
||||
make_version()
|
||||
@@ -0,0 +1,72 @@
|
||||
# Source https://github.com/letscontrolit/ESPEasy/pull/3845#issuecomment-1005864664
|
||||
|
||||
Import("env") # noqa
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
|
||||
if os.environ.get("ESPHOME_USE_SUBPROCESS") is None:
|
||||
try:
|
||||
import esptool
|
||||
except ImportError:
|
||||
env.Execute("$PYTHONEXE -m pip install esptool")
|
||||
else:
|
||||
import subprocess
|
||||
from SCons.Script import ARGUMENTS
|
||||
|
||||
# Copy over the default sdkconfig.
|
||||
from os import path
|
||||
if path.exists("./sdkconfig.defaults"):
|
||||
os.makedirs(".temp", exist_ok=True)
|
||||
shutil.copy("./sdkconfig.defaults", "./.temp/sdkconfig-esp32-idf")
|
||||
|
||||
def esp32_create_combined_bin(source, target, env):
|
||||
verbose = bool(int(ARGUMENTS.get("PIOVERBOSE", "0")))
|
||||
print("-------------------------------------------------------")
|
||||
# if verbose:
|
||||
if True:
|
||||
print("Generating combined binary for serial flashing")
|
||||
app_offset = 0x10000
|
||||
|
||||
new_file_name = env.subst("$BUILD_DIR/merged-firmware.bin")
|
||||
sections = env.subst(env.get("FLASH_EXTRA_IMAGES"))
|
||||
firmware_name = env.subst("$BUILD_DIR/${PROGNAME}.bin")
|
||||
chip = env.get("BOARD_MCU")
|
||||
flash_size = env.BoardConfig().get("upload.flash_size")
|
||||
cmd = [
|
||||
"--chip",
|
||||
chip,
|
||||
"merge-bin",
|
||||
"-o",
|
||||
new_file_name,
|
||||
"--flash-size",
|
||||
flash_size,
|
||||
]
|
||||
|
||||
# if verbose:
|
||||
print(f"Sections: {sections}")
|
||||
if True:
|
||||
print(" Offset | File")
|
||||
for section in sections:
|
||||
sect_adr, sect_file = section.split(" ", 1)
|
||||
if verbose:
|
||||
print(f" - {sect_adr} | {sect_file}")
|
||||
cmd += [sect_adr, sect_file]
|
||||
|
||||
cmd += [hex(app_offset), firmware_name]
|
||||
|
||||
# if verbose:
|
||||
if True:
|
||||
print(f" - {hex(app_offset)} | {firmware_name}")
|
||||
print()
|
||||
print(f"Using esptool arguments: {' '.join(cmd)}")
|
||||
print()
|
||||
|
||||
# if os.environ.get("ESPHOME_USE_SUBPROCESS") is None:
|
||||
# esptool.main(cmd)
|
||||
# else:
|
||||
subprocess.run(["esptool", *cmd])
|
||||
|
||||
# pylint: disable=E0602
|
||||
env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", esp32_create_combined_bin) # noqa
|
||||
@@ -0,0 +1,116 @@
|
||||
# Sasa Karanovic's Simple minify script to minify html, css and js data
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import minify_html
|
||||
from rjsmin import jsmin
|
||||
|
||||
class Minify:
|
||||
def __init__(self, work_dir, raw_directory, min_directory):
|
||||
self.work_dir = work_dir
|
||||
self.raw_directory = raw_directory
|
||||
self.min_directory = min_directory
|
||||
self._bytes_saved = { 'html': 0, 'js': 0, 'css': 0 }
|
||||
|
||||
print(f"Working directory: {self.work_dir}")
|
||||
print(f"Source directory: {self.raw_directory}")
|
||||
print(f"Data directory: {self.min_directory}")
|
||||
|
||||
def run(self):
|
||||
print("--- Running minifier ---")
|
||||
self._copy_source()
|
||||
|
||||
minify_files = self._find_minify_candidates(self.min_directory)
|
||||
|
||||
for file in minify_files:
|
||||
self._minify(file)
|
||||
print("---")
|
||||
|
||||
def show_stats(self, key=None):
|
||||
if key is not None:
|
||||
print(f"Saved {self._bytes_saved[key]} bytes by minifying `.{key}` files")
|
||||
else:
|
||||
total = 0
|
||||
for key in self._bytes_saved:
|
||||
print(f"Saved {self._bytes_saved[key]} bytes by minifying `.{key}` files")
|
||||
total = total + self._bytes_saved[key]
|
||||
print(f"Total {total} bytes saved")
|
||||
|
||||
def _find_minify_candidates(self, folder):
|
||||
minify_candidates = []
|
||||
for root, dirs, files in os.walk(folder):
|
||||
for file in files:
|
||||
if file.lower().endswith(('.htm', '.html', '.js', '.css')):
|
||||
minify_candidates.append(os.path.join(root, file))
|
||||
|
||||
return minify_candidates
|
||||
|
||||
def _update_saved_count(self, category, amount):
|
||||
self._bytes_saved[category] = self._bytes_saved[category] + amount
|
||||
|
||||
def _minify(self, file):
|
||||
if file.lower().endswith(('.html', '.htm')):
|
||||
content = self._read_file(file)
|
||||
content_minified = self._min_html(content)
|
||||
print(f" -> Minified {file} [saved {len(content)-len(content_minified)} bytes]")
|
||||
self._write_file(file, content_minified)
|
||||
elif file.lower().endswith('.js'):
|
||||
content = self._read_file(file)
|
||||
content_minified = self._min_js(content)
|
||||
self._write_file(file, content_minified)
|
||||
print(f" -> Minified {file} [saved {len(content)-len(content_minified)} bytes]")
|
||||
else:
|
||||
print(f" -> No minifier available for file {file}")
|
||||
|
||||
def _min_html(self, content):
|
||||
size_pre = len(content)
|
||||
content = minify_html.minify(content, minify_js=True, remove_processing_instructions=True)
|
||||
size_after = len(content)
|
||||
self._update_saved_count('html', size_pre-size_after)
|
||||
return content
|
||||
|
||||
def _min_js(self, content):
|
||||
size_pre = len(content)
|
||||
content = jsmin(content)
|
||||
size_after = len(content)
|
||||
self._update_saved_count('js', size_pre-size_after)
|
||||
return content
|
||||
|
||||
def _copy_source(self):
|
||||
shutil.copytree(self.raw_directory, self.min_directory, dirs_exist_ok=True)
|
||||
|
||||
def _read_file(self, file):
|
||||
with open(file, mode="r", encoding="utf-8") as f:
|
||||
content = f.read()
|
||||
return content
|
||||
|
||||
def _write_file(self, file, content):
|
||||
with open(file, mode="w", encoding="utf-8") as f:
|
||||
f.write(content)
|
||||
|
||||
def _list_dir(self, filetype):
|
||||
filtered = []
|
||||
files = os.listdir(self.min_directory)
|
||||
for file in files:
|
||||
if file.endswith(f'.{filetype}'):
|
||||
filepath = os.path.join(self.min_directory, file)
|
||||
filtered.append(filepath)
|
||||
return filtered
|
||||
|
||||
def minify(source, target, env):
|
||||
# work_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
work_dir = os.getcwd()
|
||||
raw_directory = os.path.join(work_dir, 'data_pre')
|
||||
min_directory = os.path.join(work_dir, 'data')
|
||||
|
||||
print("-- Removing previous data directory")
|
||||
shutil.rmtree(min_directory, ignore_errors=True)
|
||||
|
||||
print("-- Running data minify script")
|
||||
|
||||
mc = Minify(work_dir, raw_directory, min_directory)
|
||||
mc.run()
|
||||
mc.show_stats()
|
||||
|
||||
Import("env")
|
||||
minify(None, None, None)
|
||||
@@ -0,0 +1,52 @@
|
||||
; PlatformIO Project Configuration File
|
||||
;
|
||||
; Build options: build flags, source filter
|
||||
; Upload options: custom upload port, speed and extra flags
|
||||
; Library options: dependencies, extra library storages
|
||||
; Advanced options: extra scripting
|
||||
;
|
||||
; Please visit documentation for the other options and examples
|
||||
; https://docs.platformio.org/page/projectconf.html
|
||||
|
||||
[platformio]
|
||||
default_envs = esp32c3
|
||||
|
||||
[env:esp32_ota]
|
||||
extends = env:esp32c3
|
||||
upload_protocol = espota
|
||||
|
||||
[env:esp32c3]
|
||||
board_build.f_cpu = 160000000L
|
||||
board_build.f_flash = 80000000L
|
||||
board_build.flash_mode = qio
|
||||
board_build.partitions = huge_app.csv
|
||||
|
||||
framework = arduino
|
||||
platform = https://github.com/platformio/platform-espressif32.git
|
||||
board = esp32-c3-devkitm-1
|
||||
upload_protocol = esptool
|
||||
Monitor_speed = 115200
|
||||
build_flags =
|
||||
-DARDUINO_USB_MODE=1
|
||||
-DARDUINO_USB_CDC_ON_BOOT=1
|
||||
-DUSE_ESP32
|
||||
-DUSE_ESP32_FRAMEWORK_ARDUINO
|
||||
-DAUDIO_NO_SD_FS ; i2s_audio
|
||||
-DTEMPLATE_PLACEHOLDER=36 ; '$'
|
||||
|
||||
extra_scripts =
|
||||
pre:make_version.py
|
||||
pre:minify_data.py
|
||||
pre:embed_data.py
|
||||
post:merge_firmware.py
|
||||
|
||||
lib_deps=
|
||||
SPI
|
||||
WiFi
|
||||
Wire
|
||||
https://github.com/esphome/ESPAsyncWebServer @ ^3.2.2
|
||||
https://github.com/esphome/AsyncTCP @ ^2.1.3
|
||||
ESPmDNS @ 2.0.0
|
||||
https://github.com/tzapu/WiFiManager.git
|
||||
https://github.com/Sensirion/arduino-sht @ ^1.2.5
|
||||
https://github.com/RobTillaart/CRC @ ^1.0.2
|
||||
@@ -0,0 +1,2 @@
|
||||
minify-html>=0.15.0
|
||||
rjsmin>=1.2.3
|
||||
@@ -0,0 +1,130 @@
|
||||
#include <Wire.h>
|
||||
|
||||
uint32_t adc_samples[ADC_MAX_SAMPLES] = {0};
|
||||
uint8_t rxBuff[2] = {0};
|
||||
uint8_t rxPos = 0;
|
||||
uint32_t nADCTimeoutTick = 0;
|
||||
|
||||
void adc_init(void)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
float readHeaterTemperature(void)
|
||||
{
|
||||
uint32_t heater_adc = adc_sample_data(ADC_HEATER_SAMPLES);
|
||||
float temperature;
|
||||
float v_out;
|
||||
float resistance;
|
||||
|
||||
// Output voltage
|
||||
v_out = ( (ADC_VDD/(ADC_MAX_VAUE-1) ) * heater_adc);
|
||||
|
||||
// Resistance
|
||||
resistance = (NTC_R1_VALUE*(ADC_VDD-v_out))/v_out;
|
||||
|
||||
temperature = resistance / NTC_RESISTANCE; // (R/Ro)
|
||||
temperature = log(temperature); // ln(R/Ro)
|
||||
temperature /= NTC_BETA; // 1/B * ln(R/Ro)
|
||||
temperature += 1.0 / (25 + 273.15); // + (1/To)
|
||||
temperature = 1.0 / temperature; // Invert
|
||||
temperature -= 273.15;
|
||||
|
||||
return temperature;
|
||||
}
|
||||
|
||||
|
||||
// Helper function to indicate (in the UI) which
|
||||
// algo is used for calculating final ADC value
|
||||
String adc_get_algo(void)
|
||||
{
|
||||
#ifdef ADC_FINAL_ADC_VALUE_USING_MEAN
|
||||
return String("MEAN");
|
||||
#else
|
||||
return String("AVERAGE");
|
||||
#endif
|
||||
}
|
||||
|
||||
String adc_get_chip(void)
|
||||
{
|
||||
#ifdef ADC_CHIP_IS_MCP3221
|
||||
return String("MCP3221");
|
||||
#else
|
||||
return String("MCP3021");
|
||||
#endif
|
||||
}
|
||||
|
||||
// This function will take `N` samples and store them into an array
|
||||
// After that we should use one of the available functions to calculate value
|
||||
// ie. calculate average, mean, trimmed_mean etc.
|
||||
uint32_t adc_sample_data(uint8_t nSamples)
|
||||
{
|
||||
if(nSamples > ADC_MAX_SAMPLES)
|
||||
{
|
||||
Serial.print("Limiting number of samples to ");
|
||||
Serial.println(ADC_MAX_SAMPLES);
|
||||
nSamples = ADC_MAX_SAMPLES;
|
||||
}
|
||||
else if (nSamples <= 0)
|
||||
{
|
||||
Serial.println("Requested invalid number of samples. Aborting.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t accumulator = 0;
|
||||
uint32_t sample = 0;
|
||||
rxPos = 0;
|
||||
|
||||
while(rxPos++ < nSamples)
|
||||
{
|
||||
// Request two bytes
|
||||
Wire.requestFrom(I2C_ADDR_ADC, 2);
|
||||
// // Wait for data to become available
|
||||
nADCTimeoutTick = millis() + 10;
|
||||
while( (Wire.available() < 2) && (millis()<=nADCTimeoutTick) );
|
||||
|
||||
// Read two bytes
|
||||
rxBuff[0] = Wire.read();
|
||||
rxBuff[1] = Wire.read();
|
||||
|
||||
// Accumulate value
|
||||
#ifdef ADC_CHIP_IS_MCP3221
|
||||
// Using MCP3221
|
||||
sample = ( (rxBuff[0] << 8 | (rxBuff[1])));
|
||||
#else
|
||||
// Using MCP3021
|
||||
sample = ( (rxBuff[0] << 6 | (rxBuff[1]) >> 2));
|
||||
#endif
|
||||
|
||||
adc_samples[rxPos] = sample;
|
||||
accumulator += sample;
|
||||
}
|
||||
|
||||
// Divide value with number of samples
|
||||
return (accumulator/nSamples);
|
||||
}
|
||||
|
||||
int qsort_compare( const void* a, const void* b)
|
||||
{
|
||||
uint32_t int_a = * ( (uint32_t*) a );
|
||||
uint32_t int_b = * ( (uint32_t*) b );
|
||||
|
||||
// an easy expression for comparing
|
||||
return (int_a > int_b) - (int_a < int_b);
|
||||
}
|
||||
|
||||
uint32_t adc_get_mean(void)
|
||||
{
|
||||
// We want to have 3 or more samples
|
||||
if (rxPos <= 2)
|
||||
{
|
||||
Serial.println("Insufficient number of samples. Returing the first value");
|
||||
return adc_samples[0];
|
||||
}
|
||||
// Sort samples
|
||||
qsort(adc_samples, rxPos, sizeof(uint32_t), qsort_compare);
|
||||
|
||||
// Get the "middle" element
|
||||
// (we will round up/down on even number of samples, but we can live with that for now)
|
||||
return adc_samples[rxPos/2];
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
#ifndef _DIY_DRYBOX_CFG_H_
|
||||
#define _DIY_DRYBOX_CFG_H_
|
||||
// clang-format off
|
||||
#define MDNS_NAME "sk-drybox"
|
||||
#define PIN_HEATER_CTL 4
|
||||
#define PIN_FAN_PWM 5
|
||||
#define PIN_FAN_TACHO
|
||||
#define PIN_LED_RED 10
|
||||
#define PIN_LED_GREEN 7
|
||||
#define PIN_LED_BLUE 6
|
||||
#define PIN_I2C_SDA 0
|
||||
#define PIN_I2C_SCL 3
|
||||
#define PIN_ENCL_I2C_CLK 0
|
||||
#define PIN_ENCL_I2C_SDA 1
|
||||
#define I2C_ADDR_SENSOR_EXTERNAL 0x70
|
||||
#define I2C_ADDR_SENSOR_INTERNAL 0x40
|
||||
#define I2C_ADDR_ADC 0x4D
|
||||
#define I2C_READ_MAX_TRIES 2
|
||||
#define ADC_HEATER_SAMPLES 10
|
||||
#define ADC_MAX_SAMPLES 64
|
||||
#define ADC_VDD 3.3
|
||||
#define NTC_RESISTANCE 10000
|
||||
#define NTC_BETA 4150
|
||||
#define NTC_ROOM_TEMPERATURE 25
|
||||
#define NTC_R1_VALUE 100000
|
||||
#define SHT_SAMPLING_PERIOD 3000
|
||||
|
||||
#ifdef ADC_CHIP_IS_MCP3221
|
||||
#define ADC_MAX_VAUE 2048
|
||||
#else
|
||||
#define ADC_MAX_VAUE 1024
|
||||
#endif
|
||||
|
||||
#define LED_ON(x) do{digitalWrite(x, LOW);}while(0)
|
||||
#define LED_OFF(x) do{digitalWrite(x, HIGH);}while(0)
|
||||
|
||||
#define LED_ALL_OFF() do{digitalWrite(PIN_LED_RED, HIGH);\
|
||||
digitalWrite(PIN_LED_GREEN, HIGH);\
|
||||
digitalWrite(PIN_LED_BLUE, HIGH); }while(0)
|
||||
#define LED_ALL_ON() do{digitalWrite(PIN_LED_RED, LOW);\
|
||||
digitalWrite(PIN_LED_GREEN, LOW);\
|
||||
digitalWrite(PIN_LED_BLUE, LOW); }while(0)
|
||||
#define LED_STATUS_WIFI PIN_LED_GREEN
|
||||
#define LED_STATUS_HEATER PIN_LED_RED
|
||||
#define PID_SAMPLES 100
|
||||
#define PID_TEMP_PROXIMITY 5
|
||||
#define PID_DEFAULT_KP 92
|
||||
#define PID_DEFAULT_KI 81
|
||||
#define PID_DEFAULT_KD 50
|
||||
#define PID_MIN_VALUE 0
|
||||
#define PID_MAX_VALUE 1000
|
||||
|
||||
#define PWM_HEATER_INVERT_VALUES 1
|
||||
#define PWM_FREQ_HEATER 500
|
||||
#define PWM_FREQ_FAN 25000
|
||||
#define PWM_FREQ_LED 25000
|
||||
#define PWM_RESOLUTION 10
|
||||
#define PWM_MAX_VALUE 1024L
|
||||
#define HEATER_PWM_OFF 0
|
||||
#define PWM_CH_HEATER 0
|
||||
#define PWM_CH_FAN 2
|
||||
#define PWM_CH_LED_RED 3
|
||||
#define PWM_CH_LED_GREEN 4
|
||||
#define PWM_CH_LED_BLUE 5
|
||||
#define TEMPERATURE_PRECISION 10
|
||||
#define LIMIT_TEMP_IN_MIN 20
|
||||
#define LIMIT_TEMP_IN_MAX 70
|
||||
#define LIMIT_TEMP_HEATER_MIN 20
|
||||
#define LIMIT_TEMP_HEATER_MAX 75
|
||||
#define LIMIT_FAN_SPEED_MIN 0
|
||||
#define LIMIT_FAN_SPEED_MAX 100
|
||||
#define WDT_TIMEOUT 5
|
||||
#define WIFI_CHECK_CONNECTION_MS 10000
|
||||
|
||||
#define DEF_DEBUG_CALIBRATE_ADC 0
|
||||
#define DEF_DEBUG_PID 1
|
||||
#define DEF_DEBUG_HETER_SAMPLES 0
|
||||
#define DEF_DEBUG_SENSOR_SAMPLES 0
|
||||
#define DEF_DEBUG_PWM_VALUES 0
|
||||
|
||||
typedef struct eeprom_data
|
||||
{
|
||||
uint32_t pid_kp;
|
||||
uint32_t pid_ki;
|
||||
uint32_t pid_kd;
|
||||
float out_temp_comp;
|
||||
float in_temp_comp;
|
||||
uint32_t checksum;
|
||||
} eeprom_data_t;
|
||||
|
||||
|
||||
// --- Safety features ---
|
||||
// Do not modify unless you understand what impact your changes will have
|
||||
#define SAFETY_HEATER_REACH_MAX_TEMP_PERIOD 1800000 // 30 minutes
|
||||
#define SAFETY_HEATER_REACH_MAX_TEMP_TOLERANCE 5
|
||||
#define SAFETY_HEATER_MIN_TEMP_INCREASE_PERIOD 60000 // 1 minute
|
||||
#define SAFETY_HEATER_MIN_TEMP_INCREASE 2
|
||||
#define SAFETY_HEATER_OVERSHOOT_LIMIT 10
|
||||
#define SAFETY_INSIDE_TEMPERATURE_REACHED_TOLERANCE 5
|
||||
#define SAFETY_HEATER_HIGH_PWM_THRESHOLD 922 // ~90% of 1024 (PWM_MAX_VALUE)
|
||||
#define SAFETY_HEATER_HIGH_PWM_PERIOD 90000 // 1 minute
|
||||
#define SAFETY_HEATER_HIGH_PWM_TEMP_INCREASE 3
|
||||
#define SAFETY_HEATER_HIGH_PWM_TEMP_INCREASE_THRESHOLD 35
|
||||
#define SAFETY_HEATER_MIN_START_TEMPERATURE 5
|
||||
#define SAFETY_HEATER_DISCONNECTED_TEMPERATURE -5
|
||||
|
||||
// clang-format on
|
||||
#endif
|
||||
@@ -0,0 +1,461 @@
|
||||
#include <Arduino.h>
|
||||
#include <esp_task_wdt.h>
|
||||
#include "config.h"
|
||||
#include "drybox_pid.hpp"
|
||||
|
||||
SKDryBox_PID::SKDryBox_PID(int HeaterCH, int HeaterLED)
|
||||
{
|
||||
_PWM_CH_Heater = HeaterCH;
|
||||
_HeaterLED = HeaterLED;
|
||||
PID_VAR_KP = PID_DEFAULT_KP;
|
||||
PID_VAR_KI = PID_DEFAULT_KI;
|
||||
PID_VAR_KD = PID_DEFAULT_KD;
|
||||
}
|
||||
|
||||
|
||||
void SKDryBox_PID::_safety_tick(void)
|
||||
{
|
||||
/*
|
||||
This function will perform various 'safety checks' in firmware.
|
||||
Here we will only check if we have a reason to believe something is 'wrong' and
|
||||
we should disable the drybox.
|
||||
To be on the safe side we will act even on the occasional potential 'false positive' event
|
||||
if it means we don't have a 'false negative'.
|
||||
|
||||
Mainly we are focused on when to turn the heater off and disable dry-box to:
|
||||
- Avoid overheating/fire
|
||||
- Avoid damage
|
||||
- Flag misconfiguration or faulty equipment
|
||||
|
||||
## 1: DryBox has been ON for X minute(s) but the heater has not yet reached max temperature.
|
||||
Inside target temperature has _never_ been reached AND
|
||||
SAFETY_HEATER_REACH_MAX_TEMP_PERIOD milliseconds has passed since drybox has been turned on
|
||||
and the heater has not yet reached the heater (max temperature - SAFETY_HEATER_REACH_MAX_TEMP_TOLERANCE).
|
||||
- We assume temperature probe might be faulty, disconnected or not attached to the heater
|
||||
|
||||
## 2: Heater temperature has not increased by a minimum expected amount after x minutes has passed.
|
||||
Inside target temperature has _never_ been reached AND
|
||||
SAFETY_HEATER_MIN_TEMP_INCREASE_PERIOD milliseconds has passed since drybox has been turned on
|
||||
and the heater temperature has not increased by SAFETY_HEATER_MIN_TEMP_INCREASE
|
||||
|
||||
## 3: Heater temperature has over-shot the target maximum/limit temperature by x degrees.
|
||||
If heater temperature has over-shot by SAFETY_HEATER_OVERSHOOT_LIMIT or more
|
||||
|
||||
## 4: Heater has been running with high PWM or more for X minute(s) but the heater temperature has not increased.
|
||||
If the heater PWM value has been _continuously_ over SAFETY_HEATER_HIGH_PWM_THRESHOLD for more than
|
||||
a SAFETY_HEATER_HIGH_PWM_PERIOD milliseconds, and the heater temperature is
|
||||
below SAFETY_HEATER_HIGH_PWM_TEMP_INCREASE_THRESHOLD and the heater temperature has not increased by
|
||||
at least SAFETY_HEATER_HIGH_PWM_TEMP_INCREASE degrees.
|
||||
|
||||
*/
|
||||
|
||||
// ##0: General one-off housekeeping
|
||||
// If ever heater temperature is less than 0 degrees, we can assume something is wrong
|
||||
// This is only if dry box heater is turned on
|
||||
if(temperature_heater < SAFETY_HEATER_DISCONNECTED_TEMPERATURE)
|
||||
{
|
||||
Serial.println("SAFETY PANIC: #0 Heater temperature is abnormal!");
|
||||
this->_panic_disable_drybox();
|
||||
}
|
||||
|
||||
// Track if we ever reached target IN temperature
|
||||
if ( (!sf_TargetInTemperatureWasReached) &&
|
||||
(temperature_in >= (target_temperature_in-SAFETY_INSIDE_TEMPERATURE_REACHED_TOLERANCE)))
|
||||
{
|
||||
sf_TargetInTemperatureWasReached = true;
|
||||
Serial.println("NOTE: Target IN temperature has been approached.");
|
||||
}
|
||||
|
||||
// Track if we ever reached max heater temperature limit
|
||||
if ( (!sf_HeaterMaxTemperatureReached) &&
|
||||
(temperature_heater >= (max_temperature_heater-SAFETY_HEATER_REACH_MAX_TEMP_TOLERANCE)))
|
||||
{
|
||||
sf_HeaterMaxTemperatureReached = true;
|
||||
Serial.println("NOTE: Heater max temperature has been approached.");
|
||||
}
|
||||
|
||||
// Heater temperature has increased since start
|
||||
if( (!sf_HeaterHasWarmedUp) &&
|
||||
(temperature_heater >= (sf_HeaterStartTemperature+SAFETY_HEATER_MIN_TEMP_INCREASE)))
|
||||
{
|
||||
sf_HeaterHasWarmedUp = true;
|
||||
Serial.println("NOTE: Heater temperature has increased since start.");
|
||||
}
|
||||
|
||||
// ##1: DryBox has been ON for X minute(s) but the heater has not yet reached max temperature.
|
||||
// (Except for a case when target IN temperature has already been reached because)
|
||||
if(!sf_TargetInTemperatureWasReached && !sf_HeaterMaxTemperatureReached)
|
||||
{
|
||||
if (millis() >= (sf_StartTime + SAFETY_HEATER_REACH_MAX_TEMP_PERIOD))
|
||||
{
|
||||
Serial.println("SAFETY PANIC: #1 Heater failed to reach max temperature!");
|
||||
this->_panic_disable_drybox();
|
||||
}
|
||||
}
|
||||
|
||||
// ## 2: Heater temperature has not increased by a minimum amount after x minutes has passed.
|
||||
if(!sf_HeaterHasWarmedUp)
|
||||
{
|
||||
if (millis() >= (sf_StartTime+SAFETY_HEATER_MIN_TEMP_INCREASE_PERIOD))
|
||||
{
|
||||
Serial.println("SAFETY PANIC: #2 Heater failed to increase temperature!");
|
||||
this->_panic_disable_drybox();
|
||||
}
|
||||
}
|
||||
|
||||
// ## 3: Heater temperature has over-shot the target maximum/limit temperature by x degrees.
|
||||
if (temperature_heater >= (max_temperature_heater + SAFETY_HEATER_OVERSHOOT_LIMIT))
|
||||
{
|
||||
Serial.println("SAFETY PANIC: #3 Temperature over-shot!");
|
||||
this->_panic_disable_drybox();
|
||||
}
|
||||
|
||||
// ## 4: Heater has been running with high PWM for more for X minute(s) but the heater temperature has not increased.
|
||||
if ((sf_HighPWMIsActive) && (millis() >= (sf_HighPWMStartTime+SAFETY_HEATER_HIGH_PWM_PERIOD)))
|
||||
{
|
||||
// Only expect heater temperature to rise quickly at 100% PWM if we are below SAFETY_HEATER_HIGH_PWM_TEMP_INCREASE_THRESHOLD temperature
|
||||
if(temperature_heater < SAFETY_HEATER_HIGH_PWM_TEMP_INCREASE_THRESHOLD)
|
||||
{
|
||||
if(temperature_heater < (sf_HighPWMHeaterStartTemp + SAFETY_HEATER_HIGH_PWM_TEMP_INCREASE))
|
||||
{
|
||||
Serial.println("SAFETY PANIC: #4 Heater running at high PWM but temperature not increasing!");
|
||||
this->_panic_disable_drybox();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SKDryBox_PID::TurnOn()
|
||||
{
|
||||
// We don't start if heater temperature is below SAFETY_HEATER_MIN_START_TEMPERATURE
|
||||
if(temperature_heater <= SAFETY_HEATER_MIN_START_TEMPERATURE)
|
||||
{
|
||||
Serial.println("SAFETY: Heater temperature is abnormal!!!");
|
||||
Serial.println("SAFETY: DryBox will NOT turn on!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Update safety values if we just started
|
||||
if(!_drybox_is_on)
|
||||
{
|
||||
sf_StartTime = millis();
|
||||
sf_HeaterStartTemperature = temperature_heater;
|
||||
_drybox_is_on = true;
|
||||
LED_ON(_HeaterLED);
|
||||
Serial.println("DryBox heater on");
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.println("DryBox updated");
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
void SKDryBox_PID::TurnOff()
|
||||
{
|
||||
this->_set_heater_pwm(HEATER_PWM_OFF);
|
||||
this->SetTargetInTemperature(0);
|
||||
this->SetMaxHeaterTemperature(0);
|
||||
this->_reset_safety_variables();
|
||||
_drybox_is_on = false;
|
||||
LED_OFF(_HeaterLED);
|
||||
Serial.println("DryBox heater off");
|
||||
};
|
||||
|
||||
void SKDryBox_PID::_reset_safety_variables(void)
|
||||
{
|
||||
uint32_t sf_StartTime = 0;
|
||||
bool sf_HeaterMaxTemperatureReached = false;
|
||||
bool sf_TargetInTemperatureWasReached = false;
|
||||
bool sf_HeaterHasWarmedUp = false;
|
||||
float sf_HeaterStartTemperature = 0;
|
||||
bool sf_HighPWMIsActive = false;
|
||||
uint32_t sf_HighPWMStartTime = 0;
|
||||
float sf_HighPWMHeaterStartTemp = 0;
|
||||
}
|
||||
|
||||
void SKDryBox_PID::_panic_disable_drybox(void)
|
||||
{
|
||||
this->_set_heater_pwm(HEATER_PWM_OFF);
|
||||
this->SetTargetInTemperature(0);
|
||||
this->SetMaxHeaterTemperature(0);
|
||||
_drybox_is_on = false;
|
||||
|
||||
LED_ALL_OFF();
|
||||
while(1)
|
||||
{
|
||||
esp_task_wdt_reset();
|
||||
digitalWrite(_HeaterLED, LOW);
|
||||
delay(100);
|
||||
digitalWrite(_HeaterLED, HIGH);
|
||||
delay(100);
|
||||
}
|
||||
}
|
||||
|
||||
bool SKDryBox_PID::RecordTemperatures(float in, float heater)
|
||||
{
|
||||
// Debug
|
||||
#if DEF_DEBUG_HETER_SAMPLES
|
||||
Serial.println((String)in + "\t" + heater + "\t" +
|
||||
target_temperature_in);
|
||||
#endif
|
||||
|
||||
if (_ready_for_recalc)
|
||||
{
|
||||
Serial.println("Record temp was called while recalc was pending! Ignoring record temp request.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Update 'current' values
|
||||
temperature_in = in;
|
||||
temperature_heater = heater;
|
||||
|
||||
// Log 'historic' values
|
||||
temperature_samples_in[ts_pos] = in;
|
||||
temperature_samples_heater[ts_pos] = heater;
|
||||
|
||||
if (ts_pos == 0)
|
||||
{
|
||||
// Record milliseconds of our first sample
|
||||
pid_first_millis = millis();
|
||||
}
|
||||
|
||||
ts_pos++;
|
||||
|
||||
if (ts_pos >= PID_SAMPLES)
|
||||
{
|
||||
pid_last_millis = millis();
|
||||
ts_pos = 0;
|
||||
_ready_for_recalc = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SKDryBox_PID::SetTargetInTemperature(float val)
|
||||
{
|
||||
target_temperature_in = val;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SKDryBox_PID::SetMaxHeaterTemperature(float val)
|
||||
{
|
||||
max_temperature_heater = val;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SKDryBox_PID::SetPID(uint32_t kp, uint32_t ki, uint32_t kd)
|
||||
{
|
||||
if( (kp<PID_MIN_VALUE) || (kp>PID_MAX_VALUE) ||
|
||||
(ki<PID_MIN_VALUE) || (ki>PID_MAX_VALUE) ||
|
||||
(kd<PID_MIN_VALUE) || (kd>PID_MAX_VALUE) )
|
||||
{
|
||||
Serial.println("Invalid new PID values. Aborting.");
|
||||
return false;
|
||||
}
|
||||
|
||||
PID_VAR_KP = kp;
|
||||
PID_VAR_KI = ki;
|
||||
PID_VAR_KD = kd;
|
||||
|
||||
#if DEF_DEBUG_PID
|
||||
Serial.println((String) "PID P:" + PID_VAR_KP);
|
||||
Serial.println((String) "PID I:" + PID_VAR_KI);
|
||||
Serial.println((String) "PID D:" + PID_VAR_KD);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SKDryBox_PID::PeriodicTick(void)
|
||||
{
|
||||
// If DryBox is off.. just abort, we don't have anything to do.
|
||||
if (!_drybox_is_on)
|
||||
{
|
||||
_ready_for_recalc = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// First run safety check
|
||||
this->_safety_tick();
|
||||
|
||||
// If we have enough samples for PWM recalc
|
||||
if(_ready_for_recalc)
|
||||
{
|
||||
this->_heater_recalc_pwm();
|
||||
_ready_for_recalc = false;
|
||||
}
|
||||
}
|
||||
|
||||
// --- Private functions
|
||||
void SKDryBox_PID::_set_heater_pwm(uint32_t pwm)
|
||||
{
|
||||
if (pwm > PWM_MAX_VALUE)
|
||||
{
|
||||
pwm = PWM_MAX_VALUE;
|
||||
}
|
||||
|
||||
// Mark HighPWM event
|
||||
if (pwm >= SAFETY_HEATER_HIGH_PWM_THRESHOLD)
|
||||
{
|
||||
// Don't re-activate if already active
|
||||
if (!sf_HighPWMIsActive)
|
||||
{
|
||||
sf_HighPWMIsActive = true;
|
||||
sf_HighPWMStartTime = millis();
|
||||
sf_HighPWMHeaterStartTemp = temperature_heater;
|
||||
// Serial.println("HighPWM ON");
|
||||
}
|
||||
}
|
||||
// Otherwise clear HighPWM event
|
||||
else
|
||||
{
|
||||
// Serial.println("HighPWM OFF");
|
||||
sf_HighPWMIsActive = false;
|
||||
}
|
||||
|
||||
#if PWM_HEATER_INVERT_VALUES
|
||||
pwm = PWM_MAX_VALUE - pwm; // Driver signal is inverted
|
||||
#endif
|
||||
|
||||
heater_power = 100 - ((float)pwm / (float)PWM_MAX_VALUE) * 100.0;
|
||||
|
||||
#if DEF_DEBUG_PWM_VALUES
|
||||
Serial.println((String) "set_heater_pwm: " + pwm + "/" + PWM_MAX_VALUE);
|
||||
#endif
|
||||
ledcWrite(_PWM_CH_Heater, pwm);
|
||||
}
|
||||
|
||||
void SKDryBox_PID::_heater_recalc_pwm(void)
|
||||
{
|
||||
// We are using a simple PID-like control loop to calculate PWM value for a
|
||||
// heater Heater is heating up our box to a target temperature and keeping
|
||||
// it steady after/if that temperature is reached. At the same time we have
|
||||
// to ensure heater does not go beyond our "safe" temperature and start
|
||||
// damaging itself/fillament/cables/enclosure etc.
|
||||
//
|
||||
// As a simple solution, we will use the following flow
|
||||
// 1. Check if heater temperature is higher or equal to the maximum set
|
||||
// heater temperature
|
||||
// - if True, use heater max temperature as the target for our PID
|
||||
// controller
|
||||
// - if False, use enclosure maximum temperature as the target for
|
||||
// our PID controller
|
||||
|
||||
float average = 0;
|
||||
float pid_del_p;
|
||||
float pid_del_i;
|
||||
float pid_del_d;
|
||||
int32_t pid_val_p;
|
||||
int32_t pid_val_i;
|
||||
int32_t pid_val_d;
|
||||
int32_t calc_pwm;
|
||||
int32_t pwm_val;
|
||||
float pid_target_temperature = 0.0;
|
||||
float pid_temperature = 0.0;
|
||||
float pid_temperature_previous = 0.0;
|
||||
unsigned long pid_time_diff;
|
||||
|
||||
// Make sure values are valid
|
||||
if (isnan(temperature_heater))
|
||||
{
|
||||
Serial.println("Invalid temperature values");
|
||||
Serial.println((String) "temp_heater:" + temperature_heater);
|
||||
Serial.println((String) "target_temperature_in:" +
|
||||
target_temperature_in);
|
||||
this->_set_heater_pwm(HEATER_PWM_OFF);
|
||||
return;
|
||||
}
|
||||
|
||||
// Inside temperature has NOT reached the target temperature
|
||||
// However, heater temperature is approaching it's maximum allowed
|
||||
// temperature. Then, regulate heater max temperature
|
||||
if ((temperature_heater >= (max_temperature_heater - PID_TEMP_PROXIMITY)) &&
|
||||
(temperature_in < target_temperature_in))
|
||||
{
|
||||
// Serial.println("Using HEATER temperature as the limit.");
|
||||
for (uint8_t i = 0; i < PID_SAMPLES; i++)
|
||||
{
|
||||
average += temperature_samples_heater[i];
|
||||
}
|
||||
average = average / PID_SAMPLES;
|
||||
|
||||
pid_target_temperature = max_temperature_heater;
|
||||
pid_temperature = temperature_heater;
|
||||
// pid_temperature_previous = temperature_samples_heater[PID_SAMPLES - 1]; // This should be index 0??
|
||||
pid_temperature_previous = temperature_samples_heater[0];
|
||||
}
|
||||
// Heater is not close to it's maximum allowed temperature
|
||||
// Or, inside temperature has already reached the target value
|
||||
else
|
||||
{
|
||||
// Serial.println("Using IN temperature as the limit.");
|
||||
for (uint8_t i = 0; i < PID_SAMPLES; i++)
|
||||
{
|
||||
average += temperature_samples_in[i];
|
||||
}
|
||||
average = average / PID_SAMPLES;
|
||||
|
||||
pid_target_temperature = target_temperature_in;
|
||||
pid_temperature = temperature_in;
|
||||
// pid_temperature_previous = temperature_samples_in[PID_SAMPLES - 1]; // This should be index 0??
|
||||
pid_temperature_previous = temperature_samples_in[0];
|
||||
}
|
||||
|
||||
// P
|
||||
pid_del_p = pid_target_temperature - pid_temperature;
|
||||
pid_val_p = (int32_t)((float)(pid_del_p)*PID_VAR_KP);
|
||||
|
||||
// I
|
||||
pid_del_i = pid_target_temperature - average;
|
||||
pid_val_i = (int32_t)((float)(pid_del_i)*PID_VAR_KI);
|
||||
|
||||
// D
|
||||
pid_del_d = pid_temperature_previous - pid_temperature;
|
||||
pid_time_diff = pid_last_millis - pid_first_millis;
|
||||
if (pid_time_diff > 0)
|
||||
{
|
||||
float time_elapsed = pid_time_diff / 1000.f;
|
||||
float fval = 0.0;
|
||||
fval = (pid_del_d / time_elapsed) * (float)PID_VAR_KD;
|
||||
pid_val_d = (int32_t)fval;
|
||||
}
|
||||
else
|
||||
{
|
||||
pid_val_d = 0;
|
||||
}
|
||||
|
||||
// P + I + D
|
||||
pwm_val = pid_val_p + pid_val_i + pid_val_d;
|
||||
calc_pwm = pwm_val;
|
||||
|
||||
if (pwm_val > PWM_MAX_VALUE)
|
||||
{
|
||||
pwm_val = PWM_MAX_VALUE;
|
||||
}
|
||||
else if (pwm_val < 0)
|
||||
{
|
||||
pwm_val = 0;
|
||||
}
|
||||
|
||||
|
||||
// Serial debug
|
||||
#if DEF_DEBUG_PID
|
||||
if (millis() >= pid_debug_next)
|
||||
{
|
||||
Serial.printf("Delta P: %4.4f I: %4.4f D: %4.4f T:%dms\r\n",
|
||||
pid_del_p, pid_del_i, pid_del_d, pid_last_millis - pid_first_millis);
|
||||
|
||||
Serial.printf("PWM Val P: %4ld I: %4ld D:%4ld\r\n",
|
||||
pid_val_p, pid_val_i, pid_val_d);
|
||||
|
||||
Serial.printf("PWM : %4d\r\n\r\n",
|
||||
pwm_val);
|
||||
pid_debug_next = millis() + 500;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Finally update the heater
|
||||
this->_set_heater_pwm(pwm_val);
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
#ifndef __SK_DRYBOX_INC_HPP__
|
||||
#define __SK_DRYBOX_INC_HPP__
|
||||
|
||||
/*
|
||||
Sasa Karanovic's DryBox Class
|
||||
Simple abstraction for DryBox functions.
|
||||
This class allows us to keep main PID logic and rest of the code separated.
|
||||
Hopefully this makes editing and reviewing the 'main' portion of the code easier.
|
||||
|
||||
Before running the class we need to (unless we are OK with default values):
|
||||
-> Set PID values
|
||||
-> Set target inside temperature
|
||||
-> Set maximum heater temperature
|
||||
|
||||
This class expects to periodically:
|
||||
-> Get called to receive heater and 'inside' temperature
|
||||
-> RecordInTemperature and RecordHeaterTemperature
|
||||
|
||||
-> Run the PID and update the heater PWM output
|
||||
-> PeriodicTick
|
||||
*/
|
||||
class SKDryBox_PID {
|
||||
public:
|
||||
SKDryBox_PID(int HeaterCH, int HeaterLED);
|
||||
bool RecordTemperatures(float in, float heater);
|
||||
void TurnOn(void);
|
||||
void TurnOff(void);
|
||||
bool SetTargetInTemperature(float val);
|
||||
bool SetMaxHeaterTemperature(float val);
|
||||
bool SetPID(uint32_t kp, uint32_t ki, uint32_t kd);
|
||||
void PeriodicTick(void);
|
||||
bool isDryBoxOn(void) { return _drybox_is_on; }
|
||||
uint8_t GetHeaterPower(void) { return heater_power; };
|
||||
float GetInTemperature(void) { return temperature_in; };
|
||||
float GetTargetTemperatureIn(void) { return target_temperature_in; };
|
||||
float GetHeaterTemperature(void) { return temperature_heater; };
|
||||
float GetHeaterMaxTemperature(void) { return max_temperature_heater; };
|
||||
uint32_t GetPID_KP(void) { return PID_VAR_KP; }
|
||||
uint32_t GetPID_KI(void) { return PID_VAR_KI; }
|
||||
uint32_t GetPID_KD(void) { return PID_VAR_KD; }
|
||||
|
||||
private:
|
||||
int _PWM_CH_Heater;
|
||||
int _HeaterLED;
|
||||
bool _drybox_is_on = false;
|
||||
bool _ready_for_recalc = false;
|
||||
float temperature_samples_in[PID_SAMPLES] = {0};
|
||||
float temperature_samples_heater[PID_SAMPLES] = {0};
|
||||
uint8_t ts_pos = 0;
|
||||
float target_temperature_in = 25.0;
|
||||
float max_temperature_heater = 55.0;
|
||||
float temperature_in = 0;
|
||||
float temperature_heater = 0;
|
||||
uint8_t heater_power = 0;
|
||||
unsigned long pid_first_millis = 0;
|
||||
unsigned long pid_last_millis = 0;
|
||||
unsigned long pid_debug_next = 0;
|
||||
uint32_t PID_VAR_KP = PID_DEFAULT_KP;
|
||||
uint32_t PID_VAR_KI = PID_DEFAULT_KI;
|
||||
uint32_t PID_VAR_KD = PID_DEFAULT_KD;
|
||||
|
||||
uint32_t sf_StartTime = 0;
|
||||
bool sf_HeaterMaxTemperatureReached = false;
|
||||
bool sf_TargetInTemperatureWasReached = false;
|
||||
bool sf_HeaterHasWarmedUp = false;
|
||||
float sf_HeaterStartTemperature = 0;
|
||||
bool sf_HighPWMIsActive = false;
|
||||
uint32_t sf_HighPWMStartTime = 0;
|
||||
float sf_HighPWMHeaterStartTemp = 0;
|
||||
|
||||
void _reset_safety_variables(void);
|
||||
void _panic_disable_drybox(void);
|
||||
void _set_heater_pwm(uint32_t pwm);
|
||||
void _safety_tick(void);
|
||||
void _heater_recalc_pwm(void);
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,53 @@
|
||||
|
||||
void eeprom_init(void)
|
||||
{
|
||||
EEPROM.begin(sizeof(eeprom_data_t));
|
||||
}
|
||||
|
||||
bool eeprom_load(void)
|
||||
{
|
||||
bool ret;
|
||||
uint32_t calc_crc;
|
||||
|
||||
// Read EEPROM data
|
||||
EEPROM.readBytes(0, (void *)&eepromData, sizeof(eeprom_data_t));
|
||||
|
||||
// Calculate CRC
|
||||
calc_crc = calcCRC32((uint8_t*)&eepromData, (sizeof(eeprom_data_t)-4) );
|
||||
|
||||
if (eepromData.checksum == calc_crc)
|
||||
{
|
||||
Serial.println("EEPROM CRC is valid");
|
||||
ret = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.println("Calibration in EEPROM is invalid. Resetting!");
|
||||
eeprom_reset();
|
||||
ret = false;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void eeprom_reset(void)
|
||||
{
|
||||
eepromData.pid_kp = PID_DEFAULT_KP;
|
||||
eepromData.pid_ki = PID_DEFAULT_KI;
|
||||
eepromData.pid_kd = PID_DEFAULT_KD;
|
||||
eepromData.in_temp_comp = 0;
|
||||
eepromData.out_temp_comp = 0;
|
||||
|
||||
eeprom_save();
|
||||
}
|
||||
|
||||
void eeprom_save(void)
|
||||
{
|
||||
uint32_t calc_crc;
|
||||
|
||||
calc_crc = calcCRC32((uint8_t*)&eepromData, (sizeof(eeprom_data_t)-4) );
|
||||
eepromData.checksum = calc_crc;
|
||||
|
||||
EEPROM.writeBytes(0, (void *)&eepromData, sizeof(eeprom_data_t));
|
||||
EEPROM.commit();
|
||||
}
|
||||
@@ -0,0 +1,304 @@
|
||||
#include <Arduino.h>
|
||||
#include <esp_task_wdt.h>
|
||||
#include <ESPmDNS.h>
|
||||
#include <EEPROM.h>
|
||||
#include <WiFiManager.h>
|
||||
#include <AsyncTCP.h>
|
||||
#include "ESPAsyncWebServer.h"
|
||||
#include "WiFi.h"
|
||||
#include <Wire.h>
|
||||
#include "SHTSensor.h"
|
||||
#include "config.h"
|
||||
#include "CRC.h"
|
||||
#include "version.h"
|
||||
#include "drybox_pid.hpp"
|
||||
|
||||
WiFiManager wifiManager;
|
||||
AsyncWebServer server(80);
|
||||
int WiFi_status = WL_IDLE_STATUS;
|
||||
// SHTSensor sensor_in(SHTSensor::SHT3X_ALT); // SK
|
||||
SHTSensor sensor_in(SHTSensor::SHT3X); // DryBox
|
||||
// SHTSensor sensor_in(SHTSensor::SHT2X); // Generic
|
||||
SHTSensor sensor_out(SHTSensor::SHTC3);
|
||||
uint32_t sht_next_sample = 0;
|
||||
float temperature_in = 0;
|
||||
float temperature_out = 0;
|
||||
float temperature_heater = 0;
|
||||
uint8_t box_status = 0;
|
||||
unsigned long wifi_tick_previous = 0;
|
||||
eeprom_data_t eepromData = {0};
|
||||
uint8_t i2c_device_list[10] = {0};
|
||||
float humidity_in = 0;
|
||||
uint8_t humidity_out = 0;
|
||||
uint8_t fan_duty = 0;
|
||||
|
||||
SKDryBox_PID SKDryBox(PWM_CH_HEATER, LED_STATUS_HEATER);
|
||||
|
||||
void setup()
|
||||
{
|
||||
// Immediately setup and turn off heater
|
||||
pinMode(PIN_HEATER_CTL, OUTPUT);
|
||||
pinMode(PIN_FAN_PWM, OUTPUT);
|
||||
// Heater PWM -- Make sure heater is initially OFF
|
||||
ledcSetup(PWM_CH_HEATER, PWM_FREQ_HEATER, PWM_RESOLUTION);
|
||||
ledcAttachPin(PIN_HEATER_CTL, PWM_CH_HEATER);
|
||||
// ledcWrite(PWM_CH_HEATER, HEATER_PWM_OFF);
|
||||
SKDryBox.TurnOff();
|
||||
|
||||
// Setup LEDs
|
||||
pinMode(PIN_LED_RED, OUTPUT);
|
||||
pinMode(PIN_LED_GREEN, OUTPUT);
|
||||
pinMode(PIN_LED_BLUE, OUTPUT);
|
||||
|
||||
LED_OFF(PIN_LED_RED);
|
||||
LED_OFF(PIN_LED_GREEN);
|
||||
LED_ON(PIN_LED_BLUE);
|
||||
|
||||
// Configure Serial communication
|
||||
Serial.begin(115200);
|
||||
delay(2000);
|
||||
Serial.setDebugOutput(true);
|
||||
Serial.println("Sasa Karanovic's DIY DryBox v2");
|
||||
Serial.println("https://sasakaranovic.com");
|
||||
Serial.println("Version: " + String(VERSION_MAJOR) + "-" + String(VERSION_MINOR) + "-" + String(VERSION_PATCH) );
|
||||
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.setHostname(MDNS_NAME);
|
||||
WiFi.setTxPower(WIFI_POWER_8_5dBm);
|
||||
int txPower = WiFi.getTxPower();
|
||||
Serial.print("TX power: ");
|
||||
Serial.println(txPower);
|
||||
|
||||
delay(1000);
|
||||
// FAN PW -- Make sure FAN PWM is set to maximum (fan OFF)
|
||||
ledcSetup(PWM_CH_FAN, PWM_FREQ_FAN, PWM_RESOLUTION);
|
||||
ledcAttachPin(PIN_FAN_PWM, PWM_CH_FAN);
|
||||
ledcWrite(PWM_CH_FAN, PWM_MAX_VALUE);
|
||||
|
||||
eeprom_init();
|
||||
if (eeprom_load())
|
||||
{
|
||||
Serial.println("PID values loaded from EEPROM.");
|
||||
SKDryBox.SetPID(eepromData.pid_kp, eepromData.pid_ki, eepromData.pid_kd);
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.println("FAILED to load PID values from EEPROM! Using defaults.");
|
||||
}
|
||||
|
||||
// Setup WiFi
|
||||
bool res;
|
||||
res = wifiManager.autoConnect("SK-DryBox");
|
||||
|
||||
wifiManager.setHostname(MDNS_NAME);
|
||||
wifiManager.setTitle("DryBox v2 WiFi Setup");
|
||||
wifiManager.setConnectRetries(4);
|
||||
|
||||
if(!res) {
|
||||
Serial.println("Failed to connect or hit timeout");
|
||||
ESP.restart();
|
||||
}
|
||||
else {
|
||||
//if you get here you have connected to the WiFi
|
||||
Serial.println("Connected.");
|
||||
}
|
||||
|
||||
// Setup WDT
|
||||
esp_task_wdt_init(WDT_TIMEOUT, true); //enable panic so ESP32 restarts
|
||||
esp_task_wdt_add(NULL); //add current thread to WDT watch
|
||||
|
||||
// Setup temperature and humidity oneWireSensors
|
||||
Wire.begin(PIN_I2C_SDA, PIN_I2C_SCL);
|
||||
i2c_scan();
|
||||
adc_init();
|
||||
sensor_in.init();
|
||||
sensor_out.init();
|
||||
sensor_in.setAccuracy(SHTSensor::SHT_ACCURACY_MEDIUM);
|
||||
sensor_out.setAccuracy(SHTSensor::SHT_ACCURACY_MEDIUM);
|
||||
|
||||
Serial.println("");
|
||||
|
||||
if (!MDNS.begin(MDNS_NAME))
|
||||
{
|
||||
Serial.println("Error starting mDNS");
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.println((String) "mDNS http://" + MDNS_NAME + ".local");
|
||||
MDNS.addService("http", "tcp", 80);
|
||||
}
|
||||
|
||||
Serial.print("WiFi IP: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
|
||||
setupWebServer();
|
||||
server.begin();
|
||||
|
||||
Serial.println("Ready to go.");
|
||||
// LED_OFF(PIN_LED_BLUE);
|
||||
}
|
||||
|
||||
//
|
||||
// ** Main loop **
|
||||
//
|
||||
void loop()
|
||||
{
|
||||
esp_task_wdt_reset();
|
||||
|
||||
// Sample temperature and humidity from all available sensors
|
||||
temperature_heater = readHeaterTemperature();
|
||||
|
||||
// Sampling SHT temperature takes long time so we will do it every SHT_SAMPLING_PERIOD miliseconds
|
||||
// Also SHT measures IN and OUT ambient temperature which should not be able to make huge jumps
|
||||
if(millis() >= sht_next_sample)
|
||||
{
|
||||
sample_sens_in_and_out();
|
||||
sht_next_sample = millis() + SHT_SAMPLING_PERIOD;
|
||||
}
|
||||
|
||||
// Run DryBox
|
||||
SKDryBox.RecordTemperatures(temperature_in, temperature_heater);
|
||||
SKDryBox.PeriodicTick();
|
||||
|
||||
// Check if we have WiFi connection, if not try to reconnect
|
||||
check_wifi_connection();
|
||||
}
|
||||
|
||||
void sample_sens_in_and_out(void)
|
||||
{
|
||||
uint8_t tries = I2C_READ_MAX_TRIES;
|
||||
|
||||
// Read internal sensor
|
||||
while(tries--)
|
||||
{
|
||||
if(sensor_in.readSample())
|
||||
{
|
||||
temperature_in = sensor_in.getTemperature() + eepromData.in_temp_comp;
|
||||
humidity_in = sensor_in.getHumidity();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (tries == 0)
|
||||
{
|
||||
Serial.println("Error reading internal sensor!");
|
||||
}
|
||||
|
||||
// Read external sensor
|
||||
tries=I2C_READ_MAX_TRIES;
|
||||
while(tries--)
|
||||
{
|
||||
if(sensor_out.readSample())
|
||||
{
|
||||
temperature_out = sensor_out.getTemperature() + eepromData.out_temp_comp;
|
||||
humidity_out = sensor_out.getHumidity();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (tries == 0)
|
||||
{
|
||||
Serial.println("Error reading external sensor!");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void set_fan_duty(uint8_t duty)
|
||||
{
|
||||
if (duty > 100)
|
||||
{
|
||||
duty = 100;
|
||||
}
|
||||
|
||||
fan_duty = duty;
|
||||
uint32_t pwm_raw_fan = ((PWM_MAX_VALUE * (100 - duty)) / 100.0);
|
||||
#if DEF_DEBUG_PWM_VALUES
|
||||
Serial.println((String) "Setting fan to " + pwm_raw_fan);
|
||||
#endif
|
||||
ledcWrite(PWM_CH_FAN, pwm_raw_fan);
|
||||
}
|
||||
|
||||
void check_wifi_connection(void)
|
||||
{
|
||||
unsigned long currentMillis = millis();
|
||||
|
||||
// Check WiFi status every WIFI_CHECK_CONNECTION_MS miliseconds
|
||||
if ((currentMillis - wifi_tick_previous) >= WIFI_CHECK_CONNECTION_MS)
|
||||
{
|
||||
if (WiFi.status() == WL_CONNECTED)
|
||||
{
|
||||
LED_ON(LED_STATUS_WIFI);
|
||||
LED_OFF(PIN_LED_BLUE);
|
||||
}
|
||||
else
|
||||
{
|
||||
LED_OFF(LED_STATUS_WIFI);
|
||||
LED_ON(PIN_LED_BLUE);
|
||||
Serial.println("Reconnecting to WiFi...");
|
||||
WiFi.reconnect();
|
||||
}
|
||||
|
||||
wifi_tick_previous = currentMillis;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void i2c_scan(void)
|
||||
{
|
||||
byte error, address;
|
||||
int nDevices;
|
||||
|
||||
Serial.println("Scanning...");
|
||||
|
||||
nDevices = 0;
|
||||
for(address = 1; address < 127; address++ )
|
||||
{
|
||||
// The i2c_scanner uses the return value of
|
||||
// the Write.endTransmisstion to see if
|
||||
// a device did acknowledge to the address.
|
||||
Wire.beginTransmission(address);
|
||||
error = Wire.endTransmission();
|
||||
|
||||
if (error == 0)
|
||||
{
|
||||
Serial.print("I2C device found at address 0x");
|
||||
if (address<16)
|
||||
{
|
||||
Serial.print("0");
|
||||
}
|
||||
Serial.print(address,HEX);
|
||||
Serial.println(" !");
|
||||
|
||||
i2c_device_list[nDevices++] = address;
|
||||
}
|
||||
else if (error==4)
|
||||
{
|
||||
Serial.print("Unknown error at address 0x");
|
||||
if (address<16)
|
||||
{
|
||||
Serial.print("0");
|
||||
}
|
||||
Serial.println(address,HEX);
|
||||
}
|
||||
}
|
||||
if (nDevices == 0)
|
||||
{
|
||||
Serial.println("No I2C devices found\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.println("done\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool update_pid_coefficients(uint32_t p, uint32_t i, uint32_t d)
|
||||
{
|
||||
if(SKDryBox.SetPID(p, i, d))
|
||||
{
|
||||
eepromData.pid_kp = p;
|
||||
eepromData.pid_ki = i;
|
||||
eepromData.pid_kd = d;
|
||||
eeprom_save();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,452 @@
|
||||
// Helper function that allows us to replace template variable in .html file
|
||||
// with a value from our code.
|
||||
const String template_const_processor(const String& var)
|
||||
{
|
||||
if (var == "BUILD_DATE")
|
||||
{
|
||||
return String(__DATE__);
|
||||
|
||||
}
|
||||
else if (var == "BUILD_TIME")
|
||||
{
|
||||
return String(__TIME__);
|
||||
|
||||
}
|
||||
else if (var == "DEVICE_NAME")
|
||||
{
|
||||
return String(MDNS_NAME);
|
||||
|
||||
}
|
||||
else if (var == "STATUS")
|
||||
{
|
||||
return String(box_status);
|
||||
}
|
||||
else if (var == "TEMP_IN")
|
||||
{
|
||||
return String((int)SKDryBox.GetInTemperature());
|
||||
}
|
||||
else if (var == "TEMP_OUT")
|
||||
{
|
||||
return String((int)temperature_out);
|
||||
}
|
||||
else if (var == "HUMIDITY_IN")
|
||||
{
|
||||
return String((int)humidity_in);
|
||||
}
|
||||
else if (var == "HUMIDITY_OUT")
|
||||
{
|
||||
return String((int)humidity_out);
|
||||
}
|
||||
else if (var == "HEATER_TEMPERATURE")
|
||||
{
|
||||
return String((int)SKDryBox.GetHeaterTemperature());
|
||||
}
|
||||
else if (var == "TARGET_HEATER_TEMPERATURE")
|
||||
{
|
||||
return String((int)SKDryBox.GetHeaterMaxTemperature());
|
||||
}
|
||||
else if (var == "TARGET_INSIDE_TEMPERATURE")
|
||||
{
|
||||
return String((int)SKDryBox.GetTargetTemperatureIn());
|
||||
}
|
||||
else if (var == "FAN_POWER")
|
||||
{
|
||||
return String(fan_duty);
|
||||
}
|
||||
else if (var == "HEATER_POWER")
|
||||
{
|
||||
return String(SKDryBox.GetHeaterPower());
|
||||
}
|
||||
// PID values
|
||||
else if (var == "PID_KP")
|
||||
{
|
||||
return String(SKDryBox.GetPID_KP());
|
||||
}
|
||||
else if (var == "PID_KI")
|
||||
{
|
||||
return String(SKDryBox.GetPID_KI());
|
||||
}
|
||||
else if (var == "PID_KD")
|
||||
{
|
||||
return String(SKDryBox.GetPID_KD());
|
||||
}
|
||||
else if (var == "PWM_HEATER_INVERT_VALUES")
|
||||
{
|
||||
return String(PWM_HEATER_INVERT_VALUES);
|
||||
}
|
||||
else if (var == "PWM_FREQ_HEATER")
|
||||
{
|
||||
return String(PWM_FREQ_HEATER);
|
||||
}
|
||||
else if (var == "PWM_FREQ_FAN")
|
||||
{
|
||||
return String(PWM_FREQ_FAN);
|
||||
}
|
||||
else if (var == "PWM_MAX_VALUE")
|
||||
{
|
||||
return String(PWM_MAX_VALUE);
|
||||
}
|
||||
else if (var == "ADC_HEATER_SAMPLES")
|
||||
{
|
||||
return String(ADC_HEATER_SAMPLES);
|
||||
}
|
||||
else if (var == "PWM_CH_HEATER")
|
||||
{
|
||||
return String(PWM_CH_HEATER);
|
||||
}
|
||||
else if (var == "PWM_CH_FAN")
|
||||
{
|
||||
return String(PWM_CH_FAN);
|
||||
}
|
||||
else if (var == "LIMIT_TEMP_IN_MAX")
|
||||
{
|
||||
return String(LIMIT_TEMP_IN_MAX);
|
||||
}
|
||||
else if (var == "LIMIT_TEMP_HEATER_MAX")
|
||||
{
|
||||
return String(LIMIT_TEMP_HEATER_MAX);
|
||||
}
|
||||
else if (var == "ADC_MAX_VAUE")
|
||||
{
|
||||
return String(ADC_MAX_VAUE);
|
||||
}
|
||||
else if (var == "WIFI_CHECK_CONNECTION_MS")
|
||||
{
|
||||
return String(WIFI_CHECK_CONNECTION_MS);
|
||||
}
|
||||
else if (var == "ADC_CHIP")
|
||||
{
|
||||
return adc_get_chip();
|
||||
}
|
||||
else if (var == "NTC_RESISTANCE")
|
||||
{
|
||||
return String(NTC_RESISTANCE);
|
||||
}
|
||||
else if (var == "NTC_BETA")
|
||||
{
|
||||
return String(NTC_BETA);
|
||||
}
|
||||
else if (var == "NTC_R1_VALUE")
|
||||
{
|
||||
return String(NTC_R1_VALUE);
|
||||
}
|
||||
else if (var == "ADC_MAX_SAMPLES")
|
||||
{
|
||||
return String(ADC_MAX_SAMPLES);
|
||||
}
|
||||
else if (var == "PID_MIN_VALUE")
|
||||
{
|
||||
return String(PID_MIN_VALUE);
|
||||
}
|
||||
else if (var == "PID_MAX_VALUE")
|
||||
{
|
||||
return String(PID_MAX_VALUE);
|
||||
}
|
||||
else if (var == "DEF_DEBUG_PID")
|
||||
{
|
||||
return String(DEF_DEBUG_PID);
|
||||
}
|
||||
else if (var == "BUILD_TIME")
|
||||
{
|
||||
return String(__TIME__);
|
||||
}
|
||||
else if (var == "BUILD_DATE")
|
||||
{
|
||||
return String(__DATE__);
|
||||
}
|
||||
else if (var == "VERSION_MAJOR")
|
||||
{
|
||||
return String(VERSION_MAJOR);
|
||||
}
|
||||
else if (var == "VERSION_MINOR")
|
||||
{
|
||||
return String(VERSION_MINOR);
|
||||
}
|
||||
else if (var == "VERSION_PATCH")
|
||||
{
|
||||
return String(VERSION_PATCH);
|
||||
}
|
||||
else if (var == "COMPENSATE_TEMP_IN")
|
||||
{
|
||||
return String(eepromData.in_temp_comp);
|
||||
}
|
||||
else if (var == "COMPENSATE_TEMP_OUT")
|
||||
{
|
||||
return String(eepromData.out_temp_comp);
|
||||
}
|
||||
else if(var == "I2C_DEVICE_LIST")
|
||||
{
|
||||
char buff[50] = {0};
|
||||
int len;
|
||||
len = snprintf(buff, 400,
|
||||
"0x%02X 0x%02X 0x%02X 0x%02X 0x%02X",
|
||||
i2c_device_list[0],
|
||||
i2c_device_list[1],
|
||||
i2c_device_list[2],
|
||||
i2c_device_list[3],
|
||||
i2c_device_list[4]);
|
||||
return String(buff);
|
||||
}
|
||||
else if(var == "FW_INT_TEMP_LIB_STYPE")
|
||||
{
|
||||
uint8_t sht_type = sensor_in.getSensorType();
|
||||
switch(sht_type)
|
||||
{
|
||||
case 0:
|
||||
return String("AUTO_DETECT");
|
||||
break;
|
||||
|
||||
case 1:
|
||||
return String("SHT3X");
|
||||
break;
|
||||
|
||||
case 2:
|
||||
return String("SHT85");
|
||||
break;
|
||||
|
||||
case 3:
|
||||
return String("SHT3X_ALT");
|
||||
break;
|
||||
|
||||
case 4:
|
||||
return String("SHTC1");
|
||||
break;
|
||||
|
||||
case 5:
|
||||
return String("SHTC3");
|
||||
break;
|
||||
|
||||
case 6:
|
||||
return String("SHTW1");
|
||||
break;
|
||||
|
||||
case 7:
|
||||
return String("SHTW2");
|
||||
break;
|
||||
|
||||
case 8:
|
||||
return String("SHT4X");
|
||||
break;
|
||||
|
||||
case 9:
|
||||
return String("SHT2X");
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if(var == "BUILD_HASH")
|
||||
{
|
||||
return String(GITH_SHORT_HASH);
|
||||
}
|
||||
|
||||
|
||||
Serial.print("Unknown template variable: ");
|
||||
Serial.println(var);
|
||||
return String("__MISSING_VAR__");
|
||||
}
|
||||
|
||||
void setupWebServer(void)
|
||||
{
|
||||
server.onNotFound([](AsyncWebServerRequest *request) {
|
||||
Serial.print("404: ");
|
||||
Serial.println(request->url());
|
||||
request->send(404);
|
||||
});
|
||||
|
||||
server_init_handlers();
|
||||
|
||||
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request)
|
||||
{
|
||||
request->redirect("/index.html");
|
||||
});
|
||||
|
||||
// Get dry box status
|
||||
server.on("/api/v0/status", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
char buff[400] = {0};
|
||||
int len;
|
||||
len =
|
||||
snprintf(buff, 400,
|
||||
"{\"status\":%d,\"target_temp_in\":%4.2f,"
|
||||
"\"max_temp_heater\":%4.2f,"
|
||||
"\"temp_in\":%4.2f,\"temp_heater\":%4.2f,\"humid_in\":%4.2f,"
|
||||
"\"fan_speed\":%d,\"temp_out\":%4.2f,\"humid_out\":%d,\"heater_power\":%d}",
|
||||
SKDryBox.isDryBoxOn(),
|
||||
SKDryBox.GetTargetTemperatureIn(), SKDryBox.GetHeaterMaxTemperature(),
|
||||
SKDryBox.GetInTemperature(), SKDryBox.GetHeaterTemperature(), humidity_in, fan_duty,
|
||||
temperature_out, humidity_out, SKDryBox.GetHeaterPower());
|
||||
|
||||
if (len)
|
||||
{
|
||||
request->send(200, "text/plain", buff);
|
||||
}
|
||||
else
|
||||
{
|
||||
request->send(500, "text/plain",
|
||||
"{\"status\": \"Internal server error\"}");
|
||||
}
|
||||
});
|
||||
|
||||
// Get dry box status
|
||||
server.on("/api/v0/wifi_info", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
char buff[400] = {0};
|
||||
int len;
|
||||
len = snprintf(buff, 400, "{\"status\":\"ok\", \"SSID\":\"%s\", \"RSSI\":\"%4d\", \"CH\":\"%2d\" }", WiFi.SSID().c_str(), WiFi.RSSI(), WiFi.channel());
|
||||
});
|
||||
|
||||
// Turn OFF dry box
|
||||
server.on("/api/v0/turn_off", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
box_status = 0;
|
||||
SKDryBox.TurnOff();
|
||||
set_fan_duty(0);
|
||||
request->send(200, "text/plain", "{\"status\": \"OK\"}");
|
||||
});
|
||||
|
||||
// Turn ON dry box and set target temperature, max heater temperature and fan speed
|
||||
server.on("/api/v0/turn_on", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
// Check if temperature, heater temperature and fan speed arguments are present
|
||||
if (request->hasParam("temperature") && request->hasParam("heater") &&
|
||||
request->hasParam("fanspeed"))
|
||||
{
|
||||
String str_temperature;
|
||||
String str_heater;
|
||||
String str_fanspeed;
|
||||
int32_t temperature = 0;
|
||||
int32_t heater = 0;
|
||||
int32_t fanspeed = 0;
|
||||
|
||||
str_temperature = request->getParam("temperature")->value();
|
||||
temperature = str_temperature.toInt();
|
||||
|
||||
str_heater = request->getParam("heater")->value();
|
||||
heater = str_heater.toInt();
|
||||
|
||||
str_fanspeed = request->getParam("fanspeed")->value();
|
||||
fanspeed = str_fanspeed.toInt();
|
||||
|
||||
#if DEF_DEUG_WEB_API
|
||||
Serial.println((String) "Target temp: " + temperature +
|
||||
"C Heater: " + heater + "C Fan Speed: " + fanspeed);
|
||||
#endif
|
||||
|
||||
// Check drybox temperature and heater temperature limit
|
||||
if ( (temperature <= LIMIT_TEMP_IN_MAX) && (heater <= LIMIT_TEMP_HEATER_MAX) )
|
||||
{
|
||||
SKDryBox.SetTargetInTemperature(temperature);
|
||||
SKDryBox.SetMaxHeaterTemperature(heater);
|
||||
SKDryBox.TurnOn();
|
||||
set_fan_duty(fanspeed);
|
||||
box_status = 1;
|
||||
request->send(200, "text/plain", "{\"status\": \"OK\"}");
|
||||
return;
|
||||
}
|
||||
// If either of them are out of range, return bad request status
|
||||
else
|
||||
{
|
||||
request->send(400, "text/plain", "{\"status\": \"Bad request. Limit error!\"}");
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
// Not all arguments are present in the request
|
||||
else
|
||||
{
|
||||
request->send(400, "text/plain", "{\"status\": \"Bad request\"}");
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// Set PID parameters
|
||||
server.on("/api/v0/pid/set", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
// Check if temperature, heater temperature and fan speed arguments are present
|
||||
if (request->hasParam("p") && request->hasParam("i") &&
|
||||
request->hasParam("d"))
|
||||
{
|
||||
String str_kp;
|
||||
String str_ki;
|
||||
String str_kd;
|
||||
int32_t kp = 0;
|
||||
int32_t ki = 0;
|
||||
int32_t kd = 0;
|
||||
|
||||
str_kp = request->getParam("p")->value();
|
||||
kp = str_kp.toInt();
|
||||
|
||||
str_ki = request->getParam("i")->value();
|
||||
ki = str_ki.toInt();
|
||||
|
||||
str_kd = request->getParam("d")->value();
|
||||
kd = str_kd.toInt();
|
||||
|
||||
// Check drybox temperature and heater temperature limit
|
||||
if ( int_in_range(kp, PID_MIN_VALUE, PID_MAX_VALUE) &&
|
||||
int_in_range(ki, PID_MIN_VALUE, PID_MAX_VALUE) &&
|
||||
int_in_range(kd, PID_MIN_VALUE, PID_MAX_VALUE)
|
||||
)
|
||||
{
|
||||
SKDryBox.SetPID(kp, ki, kd);
|
||||
eepromData.pid_kp = kp;
|
||||
eepromData.pid_ki = ki;
|
||||
eepromData.pid_kd = kd;
|
||||
eeprom_save();
|
||||
|
||||
request->send(200, "text/plain", "{\"status\": \"OK\"}");
|
||||
return;
|
||||
}
|
||||
// If either of them are out of range, return bad request status
|
||||
else
|
||||
{
|
||||
request->send(400, "text/plain", "{\"status\": \"Bad request. Limit error!\"}");
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
// Not all arguments are present in the request
|
||||
else
|
||||
{
|
||||
request->send(400, "text/plain", "{\"status\": \"Bad request\"}");
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
// Set Temperature compensation values
|
||||
server.on("/api/v0/compensation/temperature/set", HTTP_GET, [](AsyncWebServerRequest *request) {
|
||||
// Check if temperature, heater temperature and fan speed arguments are present
|
||||
if (request->hasParam("temperatureIn") && request->hasParam("temperatureOut"))
|
||||
{
|
||||
|
||||
String str_temp_in;
|
||||
String str_temp_out;
|
||||
float comp_in = 0;
|
||||
float comp_out = 0;
|
||||
|
||||
str_temp_in = request->getParam("temperatureIn")->value();
|
||||
comp_in = str_temp_in.toFloat();
|
||||
|
||||
str_temp_out = request->getParam("temperatureOut")->value();
|
||||
comp_out = str_temp_out.toFloat();
|
||||
|
||||
// Check drybox temperature and heater temperature limit
|
||||
eepromData.in_temp_comp = comp_in;
|
||||
eepromData.out_temp_comp = comp_out;
|
||||
eeprom_save();
|
||||
|
||||
Serial.println(str_temp_in);
|
||||
Serial.println(str_temp_out);
|
||||
|
||||
request->send(200, "text/plain", "{\"status\": \"OK\"}");
|
||||
return;
|
||||
|
||||
}
|
||||
// Not all arguments are present in the request
|
||||
else
|
||||
{
|
||||
request->send(400, "text/plain", "{\"status\": \"Bad request\"}");
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
bool int_in_range (int value, int min, int max)
|
||||
{
|
||||
return ( (min<=value) && (value<max) );
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,8 @@
|
||||
Board Thickness: 1.6mm
|
||||
Material: FR4 (or similar)
|
||||
Copper: 1oz
|
||||
Layers: 2
|
||||
Soldermask: Black
|
||||
Silkscreen: White
|
||||
Surface finish: HASL (Lead Free)
|
||||
Tented Vias: Yes
|
||||
@@ -0,0 +1,130 @@
|
||||
M48
|
||||
;Layer_Color=9474304
|
||||
;FILE_FORMAT=4:4
|
||||
METRIC,LZ
|
||||
;TYPE=PLATED
|
||||
T01F00S00C0.4000
|
||||
T03F00S00C0.9000
|
||||
T04F00S00C1.1000
|
||||
T05F00S00C1.3000
|
||||
T06F00S00C1.4000
|
||||
T07F00S00C1.6000
|
||||
T08F00S00C1.7000
|
||||
T09F00S00C3.1000
|
||||
;TYPE=NON_PLATED
|
||||
T10F00S00C0.6500
|
||||
T11F00S00C3.1000
|
||||
%
|
||||
T01
|
||||
X00184Y00049
|
||||
X0021Y00026
|
||||
X00219Y00085
|
||||
X00164Y00145
|
||||
X-0015Y00066
|
||||
X-0016
|
||||
Y00055
|
||||
X-0015
|
||||
Y00044
|
||||
X-0016
|
||||
X-00159Y00226
|
||||
X-001447Y00245
|
||||
X-0013175Y002129
|
||||
X-0008367Y0019667
|
||||
X-00035Y00168
|
||||
X-00008Y00203
|
||||
X00002
|
||||
X00012
|
||||
X00022
|
||||
X00032
|
||||
Y00214
|
||||
X00022
|
||||
X00012
|
||||
X00002
|
||||
X-00008
|
||||
Y00225
|
||||
X00002
|
||||
X00012
|
||||
X00022
|
||||
X00032
|
||||
X-00003Y00269
|
||||
X-00017Y00274
|
||||
X-00033Y00269
|
||||
X-00047Y00265
|
||||
X-0005Y00305
|
||||
X-00061193Y00295039
|
||||
X-00072Y00317
|
||||
X-00094Y0036525
|
||||
Y00394
|
||||
X-00076Y00385
|
||||
X-00057Y00403
|
||||
X-00047Y00414
|
||||
X-00037Y00418
|
||||
X-00017Y00412
|
||||
X-00009Y00425
|
||||
X-0001Y00442
|
||||
X-00012Y00459
|
||||
X-0001Y00477
|
||||
X-000246Y004699
|
||||
X-00052Y00465
|
||||
X000159Y004699
|
||||
Y00457
|
||||
Y004426
|
||||
X0001Y00421
|
||||
Y00404
|
||||
X-00006Y003885
|
||||
Y003775
|
||||
Y003665
|
||||
X-00017
|
||||
X-00028
|
||||
Y003775
|
||||
Y003885
|
||||
X-00017
|
||||
X0006779Y00379454
|
||||
X000509Y0042
|
||||
X0000869Y00315536
|
||||
X-00108Y00279
|
||||
X-00136Y00289
|
||||
X-00169Y00267
|
||||
X-00149Y00392
|
||||
X-00137Y00416
|
||||
X-00128Y004595
|
||||
X-00098Y00433
|
||||
X00151Y00203
|
||||
T03
|
||||
X-0021Y002046
|
||||
Y0023
|
||||
Y002554
|
||||
T04
|
||||
X-00205Y00314401
|
||||
Y00339801
|
||||
Y00365201
|
||||
Y00390601
|
||||
T05
|
||||
X-001834Y00339801
|
||||
T06
|
||||
X00203Y001083
|
||||
Y001337
|
||||
Y001822
|
||||
Y002076
|
||||
Y00233
|
||||
Y002584
|
||||
T07
|
||||
X-0009335Y00099
|
||||
Y00133
|
||||
X0004135
|
||||
Y00099
|
||||
T08
|
||||
X-000904Y00055
|
||||
X-000396
|
||||
X000402
|
||||
X00091
|
||||
T09
|
||||
X00215Y-0001703
|
||||
X-00215
|
||||
T10
|
||||
X001782Y003311
|
||||
Y003889
|
||||
T11
|
||||
X-00215Y0054797
|
||||
X00215
|
||||
M30
|
||||
@@ -0,0 +1,29 @@
|
||||
M48
|
||||
;Layer_Color=9474304
|
||||
;FILE_FORMAT=4:4
|
||||
METRIC,LZ
|
||||
;TYPE=PLATED
|
||||
T02F00S00C0.6000
|
||||
;TYPE=NON_PLATED
|
||||
%
|
||||
G90
|
||||
G05
|
||||
T02
|
||||
G00X001787Y003168
|
||||
M15
|
||||
G01X001677
|
||||
M16
|
||||
G00X00219Y003168
|
||||
M15
|
||||
G01X00211
|
||||
M16
|
||||
G00X00219Y004032
|
||||
M15
|
||||
G01X00211
|
||||
M16
|
||||
G00X001787Y004032
|
||||
M15
|
||||
G01X001677
|
||||
M16
|
||||
M17
|
||||
M30
|
||||
@@ -0,0 +1,54 @@
|
||||
G04:AMPARAMS|DCode=20|XSize=0.75mm|YSize=0.35mm|CornerRadius=0.0438mm|HoleSize=0mm|Usage=FLASHONLY|Rotation=90.000|XOffset=0mm|YOffset=0mm|HoleType=Round|Shape=RoundedRectangle|*
|
||||
%AMROUNDEDRECTD20*
|
||||
21,1,0.7500,0.2625,0,0,90.0*
|
||||
21,1,0.6625,0.3500,0,0,90.0*
|
||||
1,1,0.0875,0.1313,0.3313*
|
||||
1,1,0.0875,0.1313,-0.3313*
|
||||
1,1,0.0875,-0.1313,-0.3313*
|
||||
1,1,0.0875,-0.1313,0.3313*
|
||||
%
|
||||
G04:AMPARAMS|DCode=24|XSize=0.25mm|YSize=0.75mm|CornerRadius=0.0313mm|HoleSize=0mm|Usage=FLASHONLY|Rotation=0.000|XOffset=0mm|YOffset=0mm|HoleType=Round|Shape=RoundedRectangle|*
|
||||
%AMROUNDEDRECTD24*
|
||||
21,1,0.2500,0.6875,0,0,0.0*
|
||||
21,1,0.1875,0.7500,0,0,0.0*
|
||||
1,1,0.0625,0.0938,-0.3438*
|
||||
1,1,0.0625,-0.0938,-0.3438*
|
||||
1,1,0.0625,-0.0938,0.3438*
|
||||
1,1,0.0625,0.0938,0.3438*
|
||||
%
|
||||
G04:AMPARAMS|DCode=25|XSize=0.25mm|YSize=0.75mm|CornerRadius=0.0313mm|HoleSize=0mm|Usage=FLASHONLY|Rotation=270.000|XOffset=0mm|YOffset=0mm|HoleType=Round|Shape=RoundedRectangle|*
|
||||
%AMROUNDEDRECTD25*
|
||||
21,1,0.2500,0.6875,0,0,270.0*
|
||||
21,1,0.1875,0.7500,0,0,270.0*
|
||||
1,1,0.0625,-0.3438,-0.0938*
|
||||
1,1,0.0625,-0.3438,0.0938*
|
||||
1,1,0.0625,0.3438,0.0938*
|
||||
1,1,0.0625,0.3438,-0.0938*
|
||||
%
|
||||
G04:AMPARAMS|DCode=47|XSize=0.9532mm|YSize=0.5532mm|CornerRadius=0.1454mm|HoleSize=0mm|Usage=FLASHONLY|Rotation=90.000|XOffset=0mm|YOffset=0mm|HoleType=Round|Shape=RoundedRectangle|*
|
||||
%AMROUNDEDRECTD47*
|
||||
21,1,0.9532,0.2625,0,0,90.0*
|
||||
21,1,0.6625,0.5532,0,0,90.0*
|
||||
1,1,0.2907,0.1313,0.3313*
|
||||
1,1,0.2907,0.1313,-0.3313*
|
||||
1,1,0.2907,-0.1313,-0.3313*
|
||||
1,1,0.2907,-0.1313,0.3313*
|
||||
%
|
||||
G04:AMPARAMS|DCode=51|XSize=0.41mm|YSize=0.91mm|CornerRadius=0.1113mm|HoleSize=0mm|Usage=FLASHONLY|Rotation=0.000|XOffset=0mm|YOffset=0mm|HoleType=Round|Shape=RoundedRectangle|*
|
||||
%AMROUNDEDRECTD51*
|
||||
21,1,0.4100,0.6875,0,0,0.0*
|
||||
21,1,0.1875,0.9100,0,0,0.0*
|
||||
1,1,0.2225,0.0938,-0.3438*
|
||||
1,1,0.2225,-0.0938,-0.3438*
|
||||
1,1,0.2225,-0.0938,0.3438*
|
||||
1,1,0.2225,0.0938,0.3438*
|
||||
%
|
||||
G04:AMPARAMS|DCode=52|XSize=0.41mm|YSize=0.91mm|CornerRadius=0.1113mm|HoleSize=0mm|Usage=FLASHONLY|Rotation=270.000|XOffset=0mm|YOffset=0mm|HoleType=Round|Shape=RoundedRectangle|*
|
||||
%AMROUNDEDRECTD52*
|
||||
21,1,0.4100,0.6875,0,0,270.0*
|
||||
21,1,0.1875,0.9100,0,0,270.0*
|
||||
1,1,0.2225,-0.3438,-0.0938*
|
||||
1,1,0.2225,-0.3438,0.0938*
|
||||
1,1,0.2225,0.3438,0.0938*
|
||||
1,1,0.2225,0.3438,-0.0938*
|
||||
%
|
||||
@@ -0,0 +1,25 @@
|
||||
----------------------------------------------------------------------------------------------------------------------------------
|
||||
NCDrill File Report For: DryBox.PcbDoc 2024-10-09 22:39:16
|
||||
----------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
Layer Pair : Top Layer to Bottom Layer
|
||||
ASCII RoundHoles File : DryBox-RoundHoles.TXT
|
||||
ASCII SlotHoles File : DryBox-SlotHoles.TXT
|
||||
|
||||
Tool Hole Size Hole Tolerance Hole Type Hole Count Plated Tool Travel
|
||||
----------------------------------------------------------------------------------------------------------------------------------
|
||||
T1 0.4mm (16mil) Round 74 PTH 279.80mm (11.02inch)
|
||||
T2 0.6mm (24mil) Slot 4 PTH 38.14mm (1.50inch)
|
||||
T3 0.9mm (35mil) Round 3 PTH 5.08mm (0.20inch)
|
||||
T4 1.1mm (43mil) Round 4 PTH 7.62mm (0.30inch)
|
||||
T5 1.3mm (51mil) Round 1 PTH 0.00mm (0.00inch)
|
||||
T6 1.4mm (55mil) Round 6 PTH 15.01mm (0.59inch)
|
||||
T7 1.6mm (63mil) Round 4 PTH 20.27mm (0.80inch)
|
||||
T8 1.7mm (67mil) Round 4 PTH 18.14mm (0.71inch)
|
||||
T9 3.1mm (122mil) Round 2 PTH 43.00mm (1.69inch)
|
||||
T10 0.65mm (26mil) Round 2 NPTH 5.78mm (0.23inch)
|
||||
T11 3.1mm (122mil) Round 2 NPTH 43.00mm (1.69inch)
|
||||
----------------------------------------------------------------------------------------------------------------------------------
|
||||
Totals 106
|
||||
|
||||
Total Processing Time (hh:mm:ss) : 00:00:00
|
||||
@@ -0,0 +1,21 @@
|
||||
------------------------------------------------------------------------------------------
|
||||
Gerber File Extension Report For: DryBox.GBR 2024-10-09 22:39:14
|
||||
------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
------------------------------------------------------------------------------------------
|
||||
Layer Extension Layer Description
|
||||
------------------------------------------------------------------------------------------
|
||||
.GTO Top Overlay
|
||||
.GTP Top Paste
|
||||
.GTS Top Solder
|
||||
.GTL Top Layer
|
||||
.GBL Bottom Layer
|
||||
.GBS Bottom Solder
|
||||
.GBP Bottom Paste
|
||||
.GBO Bottom Overlay
|
||||
.GM8 Route Tool Path
|
||||
.GKO Keep-Out Layer
|
||||
.GD1 Drill Drawing
|
||||
.GG1 Drill Guide
|
||||
------------------------------------------------------------------------------------------
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,16 @@
|
||||
G04*
|
||||
G04 #@! TF.GenerationSoftware,Altium Limited,Altium Designer,24.8.2 (39)*
|
||||
G04*
|
||||
G04 Layer_Color=128*
|
||||
%FSLAX44Y44*%
|
||||
%MOMM*%
|
||||
G71*
|
||||
G04*
|
||||
G04 #@! TF.SameCoordinates,74074FDB-1FAA-448C-9F72-8EE957A70AFD*
|
||||
G04*
|
||||
G04*
|
||||
G04 #@! TF.FilePolarity,Positive*
|
||||
G04*
|
||||
G01*
|
||||
G75*
|
||||
M02*
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,442 @@
|
||||
G04*
|
||||
G04 #@! TF.GenerationSoftware,Altium Limited,Altium Designer,24.8.2 (39)*
|
||||
G04*
|
||||
G04 Layer_Color=128*
|
||||
%FSLAX44Y44*%
|
||||
%MOMM*%
|
||||
G71*
|
||||
G04*
|
||||
G04 #@! TF.SameCoordinates,74074FDB-1FAA-448C-9F72-8EE957A70AFD*
|
||||
G04*
|
||||
G04*
|
||||
G04 #@! TF.FilePolarity,Positive*
|
||||
G04*
|
||||
G01*
|
||||
G75*
|
||||
%ADD104C,0.0508*%
|
||||
D104*
|
||||
X33350Y99000D02*
|
||||
X49350D01*
|
||||
X41350Y91000D02*
|
||||
Y107000D01*
|
||||
X33350Y133000D02*
|
||||
X49350D01*
|
||||
X41350Y125000D02*
|
||||
Y141000D01*
|
||||
X-101350Y99000D02*
|
||||
X-85350D01*
|
||||
X-93350Y91000D02*
|
||||
Y107000D01*
|
||||
X-101350Y133000D02*
|
||||
X-85350D01*
|
||||
X-93350Y125000D02*
|
||||
Y141000D01*
|
||||
X196000Y233000D02*
|
||||
X210000D01*
|
||||
X203000Y226000D02*
|
||||
Y240000D01*
|
||||
X196000Y207600D02*
|
||||
X210000D01*
|
||||
X203000Y200600D02*
|
||||
Y214600D01*
|
||||
X196000Y182200D02*
|
||||
X210000D01*
|
||||
X203000Y175200D02*
|
||||
Y189200D01*
|
||||
X196000Y258400D02*
|
||||
X210000D01*
|
||||
X203000Y251400D02*
|
||||
Y265400D01*
|
||||
X196000Y133700D02*
|
||||
X210000D01*
|
||||
X203000Y126700D02*
|
||||
Y140700D01*
|
||||
X196000Y108300D02*
|
||||
X210000D01*
|
||||
X203000Y101300D02*
|
||||
Y115300D01*
|
||||
X-214500Y204600D02*
|
||||
X-205500D01*
|
||||
X-210000Y200100D02*
|
||||
Y209100D01*
|
||||
X-214500Y230000D02*
|
||||
X-205500D01*
|
||||
X-210000Y225500D02*
|
||||
Y234500D01*
|
||||
X-214500Y255400D02*
|
||||
X-205500D01*
|
||||
X-210000Y250900D02*
|
||||
Y259900D01*
|
||||
X-48100Y55000D02*
|
||||
X-31100D01*
|
||||
X-39600Y46500D02*
|
||||
Y63500D01*
|
||||
X-98900Y55000D02*
|
||||
X-81900D01*
|
||||
X-90400Y46500D02*
|
||||
Y63500D01*
|
||||
X82500Y55000D02*
|
||||
X99500D01*
|
||||
X91000Y46500D02*
|
||||
Y63500D01*
|
||||
X31700Y55000D02*
|
||||
X48700D01*
|
||||
X40200Y46500D02*
|
||||
Y63500D01*
|
||||
X174950Y331100D02*
|
||||
X181450D01*
|
||||
X178200Y327850D02*
|
||||
Y334350D01*
|
||||
X174950Y388900D02*
|
||||
X181450D01*
|
||||
X178200Y385650D02*
|
||||
Y392150D01*
|
||||
X170200Y403200D02*
|
||||
X176200D01*
|
||||
X173200Y400200D02*
|
||||
Y406200D01*
|
||||
X170200Y316800D02*
|
||||
X176200D01*
|
||||
X173200Y313800D02*
|
||||
Y319800D01*
|
||||
X212000Y403200D02*
|
||||
X218000D01*
|
||||
X215000Y400200D02*
|
||||
Y406200D01*
|
||||
X212000Y316800D02*
|
||||
X218000D01*
|
||||
X215000Y313800D02*
|
||||
Y319800D01*
|
||||
X199500Y-17030D02*
|
||||
X230500D01*
|
||||
X215000Y-32530D02*
|
||||
Y-1530D01*
|
||||
X-230500Y-17030D02*
|
||||
X-199500D01*
|
||||
X-215000Y-32530D02*
|
||||
Y-1530D01*
|
||||
X-230500Y547970D02*
|
||||
X-199500D01*
|
||||
X-215000Y532470D02*
|
||||
Y563470D01*
|
||||
X199500Y547970D02*
|
||||
X230500D01*
|
||||
X215000Y532470D02*
|
||||
Y563470D01*
|
||||
X-189900Y339801D02*
|
||||
X-176900D01*
|
||||
X-183400Y333301D02*
|
||||
Y346301D01*
|
||||
X-210500Y314401D02*
|
||||
X-199500D01*
|
||||
X-205000Y308901D02*
|
||||
Y319901D01*
|
||||
X-210500Y339801D02*
|
||||
X-199500D01*
|
||||
X-205000Y334301D02*
|
||||
Y345301D01*
|
||||
X-210500Y365201D02*
|
||||
X-199500D01*
|
||||
X-205000Y359701D02*
|
||||
Y370701D01*
|
||||
X-210500Y390601D02*
|
||||
X-199500D01*
|
||||
X-205000Y385101D02*
|
||||
Y396101D01*
|
||||
X65790Y379454D02*
|
||||
X69790D01*
|
||||
X67790Y377454D02*
|
||||
Y381454D01*
|
||||
X-138000Y289000D02*
|
||||
X-134000D01*
|
||||
X-136000Y287000D02*
|
||||
Y291000D01*
|
||||
X-110000Y279000D02*
|
||||
X-106000D01*
|
||||
X-108000Y277000D02*
|
||||
Y281000D01*
|
||||
X-12000Y477000D02*
|
||||
X-8000D01*
|
||||
X-10000Y475000D02*
|
||||
Y479000D01*
|
||||
X-139000Y416000D02*
|
||||
X-135000D01*
|
||||
X-137000Y414000D02*
|
||||
Y418000D01*
|
||||
X6690Y315536D02*
|
||||
X10690D01*
|
||||
X8690Y313536D02*
|
||||
Y317536D01*
|
||||
X-8000Y388500D02*
|
||||
X-4000D01*
|
||||
X-6000Y386500D02*
|
||||
Y390500D01*
|
||||
X-8000Y377500D02*
|
||||
X-4000D01*
|
||||
X-6000Y375500D02*
|
||||
Y379500D01*
|
||||
X-8000Y366500D02*
|
||||
X-4000D01*
|
||||
X-6000Y364500D02*
|
||||
Y368500D01*
|
||||
X-19000Y388500D02*
|
||||
X-15000D01*
|
||||
X-17000Y386500D02*
|
||||
Y390500D01*
|
||||
X-19000Y366500D02*
|
||||
X-15000D01*
|
||||
X-17000Y364500D02*
|
||||
Y368500D01*
|
||||
X-30000Y388500D02*
|
||||
X-26000D01*
|
||||
X-28000Y386500D02*
|
||||
Y390500D01*
|
||||
X-30000Y377500D02*
|
||||
X-26000D01*
|
||||
X-28000Y375500D02*
|
||||
Y379500D01*
|
||||
X-30000Y366500D02*
|
||||
X-26000D01*
|
||||
X-28000Y364500D02*
|
||||
Y368500D01*
|
||||
X-49000Y414000D02*
|
||||
X-45000D01*
|
||||
X-47000Y412000D02*
|
||||
Y416000D01*
|
||||
X-54000Y465000D02*
|
||||
X-50000D01*
|
||||
X-52000Y463000D02*
|
||||
Y467000D01*
|
||||
X-74000Y317000D02*
|
||||
X-70000D01*
|
||||
X-72000Y315000D02*
|
||||
Y319000D01*
|
||||
X-26600Y469900D02*
|
||||
X-22600D01*
|
||||
X-24600Y467900D02*
|
||||
Y471900D01*
|
||||
X-59000Y403000D02*
|
||||
X-55000D01*
|
||||
X-57000Y401000D02*
|
||||
Y405000D01*
|
||||
X-5000Y269000D02*
|
||||
X-1000D01*
|
||||
X-3000Y267000D02*
|
||||
Y271000D01*
|
||||
X-19000Y274000D02*
|
||||
X-15000D01*
|
||||
X-17000Y272000D02*
|
||||
Y276000D01*
|
||||
X-35000Y269000D02*
|
||||
X-31000D01*
|
||||
X-33000Y267000D02*
|
||||
Y271000D01*
|
||||
X-52000Y305000D02*
|
||||
X-48000D01*
|
||||
X-50000Y303000D02*
|
||||
Y307000D01*
|
||||
X-49000Y265000D02*
|
||||
X-45000D01*
|
||||
X-47000Y263000D02*
|
||||
Y267000D01*
|
||||
X-63193Y295039D02*
|
||||
X-59193D01*
|
||||
X-61193Y293039D02*
|
||||
Y297039D01*
|
||||
X-130000Y459500D02*
|
||||
X-126000D01*
|
||||
X-128000Y457500D02*
|
||||
Y461500D01*
|
||||
X-151000Y392000D02*
|
||||
X-147000D01*
|
||||
X-149000Y390000D02*
|
||||
Y394000D01*
|
||||
X-146700Y245000D02*
|
||||
X-142700D01*
|
||||
X-144700Y243000D02*
|
||||
Y247000D01*
|
||||
X-161000Y226000D02*
|
||||
X-157000D01*
|
||||
X-159000Y224000D02*
|
||||
Y228000D01*
|
||||
X-171000Y267000D02*
|
||||
X-167000D01*
|
||||
X-169000Y265000D02*
|
||||
Y269000D01*
|
||||
X-96000Y394000D02*
|
||||
X-92000D01*
|
||||
X-94000Y392000D02*
|
||||
Y396000D01*
|
||||
X-100000Y433000D02*
|
||||
X-96000D01*
|
||||
X-98000Y431000D02*
|
||||
Y435000D01*
|
||||
X162000Y145000D02*
|
||||
X166000D01*
|
||||
X164000Y143000D02*
|
||||
Y147000D01*
|
||||
X-39000Y418000D02*
|
||||
X-35000D01*
|
||||
X-37000Y416000D02*
|
||||
Y420000D01*
|
||||
X-19000Y412000D02*
|
||||
X-15000D01*
|
||||
X-17000Y410000D02*
|
||||
Y414000D01*
|
||||
X-96000Y365250D02*
|
||||
X-92000D01*
|
||||
X-94000Y363250D02*
|
||||
Y367250D01*
|
||||
X149000Y203000D02*
|
||||
X153000D01*
|
||||
X151000Y201000D02*
|
||||
Y205000D01*
|
||||
X208000Y26000D02*
|
||||
X212000D01*
|
||||
X210000Y24000D02*
|
||||
Y28000D01*
|
||||
X182000Y49000D02*
|
||||
X186000D01*
|
||||
X184000Y47000D02*
|
||||
Y51000D01*
|
||||
X217000Y85000D02*
|
||||
X221000D01*
|
||||
X219000Y83000D02*
|
||||
Y87000D01*
|
||||
X-37000Y168000D02*
|
||||
X-33000D01*
|
||||
X-35000Y166000D02*
|
||||
Y170000D01*
|
||||
X-162000Y66000D02*
|
||||
X-158000D01*
|
||||
X-160000Y64000D02*
|
||||
Y68000D01*
|
||||
X-162000Y44000D02*
|
||||
X-158000D01*
|
||||
X-160000Y42000D02*
|
||||
Y46000D01*
|
||||
X-162000Y55000D02*
|
||||
X-158000D01*
|
||||
X-160000Y53000D02*
|
||||
Y57000D01*
|
||||
X-152000Y55000D02*
|
||||
X-148000D01*
|
||||
X-150000Y53000D02*
|
||||
Y57000D01*
|
||||
X-152000Y44000D02*
|
||||
X-148000D01*
|
||||
X-150000Y42000D02*
|
||||
Y46000D01*
|
||||
X-152000Y66000D02*
|
||||
X-148000D01*
|
||||
X-150000Y64000D02*
|
||||
Y68000D01*
|
||||
X0Y225000D02*
|
||||
X4000D01*
|
||||
X2000Y223000D02*
|
||||
Y227000D01*
|
||||
X0Y203000D02*
|
||||
X4000D01*
|
||||
X2000Y201000D02*
|
||||
Y205000D01*
|
||||
X0Y214000D02*
|
||||
X4000D01*
|
||||
X2000Y212000D02*
|
||||
Y216000D01*
|
||||
X-10000Y214000D02*
|
||||
X-6000D01*
|
||||
X-8000Y212000D02*
|
||||
Y216000D01*
|
||||
X-10000Y203000D02*
|
||||
X-6000D01*
|
||||
X-8000Y201000D02*
|
||||
Y205000D01*
|
||||
X-10000Y225000D02*
|
||||
X-6000D01*
|
||||
X-8000Y223000D02*
|
||||
Y227000D01*
|
||||
X10000Y225000D02*
|
||||
X14000D01*
|
||||
X12000Y223000D02*
|
||||
Y227000D01*
|
||||
X10000Y203000D02*
|
||||
X14000D01*
|
||||
X12000Y201000D02*
|
||||
Y205000D01*
|
||||
X10000Y214000D02*
|
||||
X14000D01*
|
||||
X12000Y212000D02*
|
||||
Y216000D01*
|
||||
X20000Y214000D02*
|
||||
X24000D01*
|
||||
X22000Y212000D02*
|
||||
Y216000D01*
|
||||
X20000Y203000D02*
|
||||
X24000D01*
|
||||
X22000Y201000D02*
|
||||
Y205000D01*
|
||||
X20000Y225000D02*
|
||||
X24000D01*
|
||||
X22000Y223000D02*
|
||||
Y227000D01*
|
||||
X13900Y457000D02*
|
||||
X17900D01*
|
||||
X15900Y455000D02*
|
||||
Y459000D01*
|
||||
X8000Y421000D02*
|
||||
X12000D01*
|
||||
X10000Y419000D02*
|
||||
Y423000D01*
|
||||
X8000Y404000D02*
|
||||
X12000D01*
|
||||
X10000Y402000D02*
|
||||
Y406000D01*
|
||||
X-12000Y442000D02*
|
||||
X-8000D01*
|
||||
X-10000Y440000D02*
|
||||
Y444000D01*
|
||||
X-14000Y459000D02*
|
||||
X-10000D01*
|
||||
X-12000Y457000D02*
|
||||
Y461000D01*
|
||||
X-11000Y425000D02*
|
||||
X-7000D01*
|
||||
X-9000Y423000D02*
|
||||
Y427000D01*
|
||||
X30000Y225000D02*
|
||||
X34000D01*
|
||||
X32000Y223000D02*
|
||||
Y227000D01*
|
||||
X30000Y203000D02*
|
||||
X34000D01*
|
||||
X32000Y201000D02*
|
||||
Y205000D01*
|
||||
X30000Y214000D02*
|
||||
X34000D01*
|
||||
X32000Y212000D02*
|
||||
Y216000D01*
|
||||
X-85670Y196670D02*
|
||||
X-81670D01*
|
||||
X-83670Y194670D02*
|
||||
Y198670D01*
|
||||
X-133750Y212900D02*
|
||||
X-129750D01*
|
||||
X-131750Y210900D02*
|
||||
Y214900D01*
|
||||
X48900Y420000D02*
|
||||
X52900D01*
|
||||
X50900Y418000D02*
|
||||
Y422000D01*
|
||||
X-78000Y385000D02*
|
||||
X-74000D01*
|
||||
X-76000Y383000D02*
|
||||
Y387000D01*
|
||||
X13900Y469900D02*
|
||||
X17900D01*
|
||||
X15900Y467900D02*
|
||||
Y471900D01*
|
||||
X13900Y442600D02*
|
||||
X17900D01*
|
||||
X15900Y440600D02*
|
||||
Y444600D01*
|
||||
M02*
|
||||
@@ -0,0 +1,86 @@
|
||||
G04*
|
||||
G04 #@! TF.GenerationSoftware,Altium Limited,Altium Designer,24.8.2 (39)*
|
||||
G04*
|
||||
G04 Layer_Color=16711935*
|
||||
%FSLAX44Y44*%
|
||||
%MOMM*%
|
||||
G71*
|
||||
G04*
|
||||
G04 #@! TF.SameCoordinates,74074FDB-1FAA-448C-9F72-8EE957A70AFD*
|
||||
G04*
|
||||
G04*
|
||||
G04 #@! TF.FilePolarity,Positive*
|
||||
G04*
|
||||
G01*
|
||||
G75*
|
||||
%ADD15C,0.1000*%
|
||||
D15*
|
||||
X-222000Y582000D02*
|
||||
G03*
|
||||
X-242000Y562000I0J-20000D01*
|
||||
G01*
|
||||
Y490215D02*
|
||||
G03*
|
||||
X-231785Y480000I10215J0D01*
|
||||
G01*
|
||||
X-159285Y419895D02*
|
||||
G03*
|
||||
X-149071Y430109I0J10215D01*
|
||||
G01*
|
||||
X-149285Y469785D02*
|
||||
G03*
|
||||
X-159500Y480000I-10215J0D01*
|
||||
G01*
|
||||
X-231785D02*
|
||||
G03*
|
||||
X-242000Y469785I0J-10215D01*
|
||||
G01*
|
||||
X-231785Y420105D02*
|
||||
G03*
|
||||
X-242000Y409891I0J-10215D01*
|
||||
G01*
|
||||
Y430320D02*
|
||||
G03*
|
||||
X-231785Y420105I10215J0D01*
|
||||
G01*
|
||||
X-242000Y-25000D02*
|
||||
G03*
|
||||
X-222000Y-45000I20000J0D01*
|
||||
G01*
|
||||
X222000Y-45000D02*
|
||||
G03*
|
||||
X242000Y-25000I0J20000D01*
|
||||
G01*
|
||||
Y562000D02*
|
||||
G03*
|
||||
X222000Y582000I-20000J0D01*
|
||||
G01*
|
||||
X-231785Y480000D02*
|
||||
X-159500D01*
|
||||
X-242000Y435210D02*
|
||||
Y464895D01*
|
||||
Y410000D02*
|
||||
X-242000Y-25000D01*
|
||||
X-242000Y490215D02*
|
||||
Y562000D01*
|
||||
X-149071Y430109D02*
|
||||
Y440000D01*
|
||||
X-149285Y460000D02*
|
||||
Y469785D01*
|
||||
X-231571Y419895D02*
|
||||
X-159285D01*
|
||||
X-242000Y464895D02*
|
||||
Y469785D01*
|
||||
Y430320D02*
|
||||
Y435210D01*
|
||||
X-222000Y-45000D02*
|
||||
X222000Y-45000D01*
|
||||
X-222000Y-45000D02*
|
||||
X222000Y-45000D01*
|
||||
X-197000D02*
|
||||
X197000Y-45000D01*
|
||||
X-222000Y582000D02*
|
||||
X222000D01*
|
||||
X242000Y562000D02*
|
||||
X242000Y-25000D01*
|
||||
M02*
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,515 @@
|
||||
G04*
|
||||
G04 #@! TF.GenerationSoftware,Altium Limited,Altium Designer,24.8.2 (39)*
|
||||
G04*
|
||||
G04 Layer_Color=8421504*
|
||||
%FSLAX44Y44*%
|
||||
%MOMM*%
|
||||
G71*
|
||||
G04*
|
||||
G04 #@! TF.SameCoordinates,74074FDB-1FAA-448C-9F72-8EE957A70AFD*
|
||||
G04*
|
||||
G04*
|
||||
G04 #@! TF.FilePolarity,Positive*
|
||||
G04*
|
||||
G01*
|
||||
G75*
|
||||
G04:AMPARAMS|DCode=20|XSize=0.75mm|YSize=0.35mm|CornerRadius=0.0438mm|HoleSize=0mm|Usage=FLASHONLY|Rotation=90.000|XOffset=0mm|YOffset=0mm|HoleType=Round|Shape=RoundedRectangle|*
|
||||
%AMROUNDEDRECTD20*
|
||||
21,1,0.7500,0.2625,0,0,90.0*
|
||||
21,1,0.6625,0.3500,0,0,90.0*
|
||||
1,1,0.0875,0.1313,0.3313*
|
||||
1,1,0.0875,0.1313,-0.3313*
|
||||
1,1,0.0875,-0.1313,-0.3313*
|
||||
1,1,0.0875,-0.1313,0.3313*
|
||||
%
|
||||
%ADD20ROUNDEDRECTD20*%
|
||||
%ADD21R,1.6000X0.7000*%
|
||||
%ADD22R,1.1000X0.6000*%
|
||||
%ADD23R,0.6000X1.1000*%
|
||||
G04:AMPARAMS|DCode=24|XSize=0.25mm|YSize=0.75mm|CornerRadius=0.0313mm|HoleSize=0mm|Usage=FLASHONLY|Rotation=0.000|XOffset=0mm|YOffset=0mm|HoleType=Round|Shape=RoundedRectangle|*
|
||||
%AMROUNDEDRECTD24*
|
||||
21,1,0.2500,0.6875,0,0,0.0*
|
||||
21,1,0.1875,0.7500,0,0,0.0*
|
||||
1,1,0.0625,0.0938,-0.3438*
|
||||
1,1,0.0625,-0.0938,-0.3438*
|
||||
1,1,0.0625,-0.0938,0.3438*
|
||||
1,1,0.0625,0.0938,0.3438*
|
||||
%
|
||||
%ADD24ROUNDEDRECTD24*%
|
||||
G04:AMPARAMS|DCode=25|XSize=0.25mm|YSize=0.75mm|CornerRadius=0.0313mm|HoleSize=0mm|Usage=FLASHONLY|Rotation=270.000|XOffset=0mm|YOffset=0mm|HoleType=Round|Shape=RoundedRectangle|*
|
||||
%AMROUNDEDRECTD25*
|
||||
21,1,0.2500,0.6875,0,0,270.0*
|
||||
21,1,0.1875,0.7500,0,0,270.0*
|
||||
1,1,0.0625,-0.3438,-0.0938*
|
||||
1,1,0.0625,-0.3438,0.0938*
|
||||
1,1,0.0625,0.3438,0.0938*
|
||||
1,1,0.0625,0.3438,-0.0938*
|
||||
%
|
||||
%ADD25ROUNDEDRECTD25*%
|
||||
%ADD26R,3.5000X3.5000*%
|
||||
%ADD27R,0.9000X0.7000*%
|
||||
%ADD28R,0.7000X0.9000*%
|
||||
%ADD29R,0.9000X0.9650*%
|
||||
%ADD30R,0.8000X0.9000*%
|
||||
%ADD31C,1.0000*%
|
||||
%ADD32R,1.0500X0.6000*%
|
||||
%ADD33R,0.8500X0.7500*%
|
||||
%ADD34R,2.2000X4.2000*%
|
||||
%ADD35R,1.1500X0.3000*%
|
||||
%ADD36R,1.1500X0.6000*%
|
||||
%ADD37R,1.3400X1.8000*%
|
||||
%ADD38R,1.3000X0.6000*%
|
||||
%ADD39R,4.1000X4.4100*%
|
||||
%ADD40R,0.9650X0.9000*%
|
||||
%ADD41R,0.8000X0.5500*%
|
||||
%ADD42R,1.2300X2.0200*%
|
||||
%ADD43R,1.0000X1.3000*%
|
||||
%ADD44R,0.9000X1.6000*%
|
||||
%ADD45R,1.8000X0.4000*%
|
||||
%ADD46O,2.3000X1.3000*%
|
||||
D20*
|
||||
X-200000Y440300D02*
|
||||
D03*
|
||||
X-190000D02*
|
||||
D03*
|
||||
Y459700D02*
|
||||
D03*
|
||||
X-200000D02*
|
||||
D03*
|
||||
D21*
|
||||
X-195000Y450000D02*
|
||||
D03*
|
||||
D22*
|
||||
X167750Y39500D02*
|
||||
D03*
|
||||
Y58500D02*
|
||||
D03*
|
||||
X196250D02*
|
||||
D03*
|
||||
Y49000D02*
|
||||
D03*
|
||||
Y39500D02*
|
||||
D03*
|
||||
D23*
|
||||
X-145500Y278750D02*
|
||||
D03*
|
||||
X-136000D02*
|
||||
D03*
|
||||
X-126500D02*
|
||||
D03*
|
||||
Y307250D02*
|
||||
D03*
|
||||
X-145500D02*
|
||||
D03*
|
||||
D24*
|
||||
X500Y402000D02*
|
||||
D03*
|
||||
X-4500D02*
|
||||
D03*
|
||||
X-9500D02*
|
||||
D03*
|
||||
X-14500D02*
|
||||
D03*
|
||||
X-19500D02*
|
||||
D03*
|
||||
X-24500D02*
|
||||
D03*
|
||||
X-29500D02*
|
||||
D03*
|
||||
X-34500D02*
|
||||
D03*
|
||||
Y353000D02*
|
||||
D03*
|
||||
X-29500D02*
|
||||
D03*
|
||||
X-24500D02*
|
||||
D03*
|
||||
X-19500D02*
|
||||
D03*
|
||||
X-14500D02*
|
||||
D03*
|
||||
X-9500D02*
|
||||
D03*
|
||||
X-4500D02*
|
||||
D03*
|
||||
X500D02*
|
||||
D03*
|
||||
D25*
|
||||
X-41500Y395000D02*
|
||||
D03*
|
||||
Y390000D02*
|
||||
D03*
|
||||
Y385000D02*
|
||||
D03*
|
||||
Y380000D02*
|
||||
D03*
|
||||
Y375000D02*
|
||||
D03*
|
||||
Y370000D02*
|
||||
D03*
|
||||
Y365000D02*
|
||||
D03*
|
||||
Y360000D02*
|
||||
D03*
|
||||
X7500D02*
|
||||
D03*
|
||||
Y365000D02*
|
||||
D03*
|
||||
Y370000D02*
|
||||
D03*
|
||||
Y375000D02*
|
||||
D03*
|
||||
Y380000D02*
|
||||
D03*
|
||||
Y385000D02*
|
||||
D03*
|
||||
Y390000D02*
|
||||
D03*
|
||||
Y395000D02*
|
||||
D03*
|
||||
D26*
|
||||
X-17000Y377500D02*
|
||||
D03*
|
||||
D27*
|
||||
X-39000Y331400D02*
|
||||
D03*
|
||||
Y315600D02*
|
||||
D03*
|
||||
X-9000Y331400D02*
|
||||
D03*
|
||||
Y315600D02*
|
||||
D03*
|
||||
X-24000Y331400D02*
|
||||
D03*
|
||||
Y315600D02*
|
||||
D03*
|
||||
X-84000Y279100D02*
|
||||
D03*
|
||||
Y294900D02*
|
||||
D03*
|
||||
X-99000Y279100D02*
|
||||
D03*
|
||||
Y294900D02*
|
||||
D03*
|
||||
X-113000Y456400D02*
|
||||
D03*
|
||||
Y440600D02*
|
||||
D03*
|
||||
X-38000D02*
|
||||
D03*
|
||||
Y456400D02*
|
||||
D03*
|
||||
X-22000Y440600D02*
|
||||
D03*
|
||||
Y456400D02*
|
||||
D03*
|
||||
X149000Y252100D02*
|
||||
D03*
|
||||
Y267900D02*
|
||||
D03*
|
||||
X214000Y41100D02*
|
||||
D03*
|
||||
Y56900D02*
|
||||
D03*
|
||||
X-222500Y457900D02*
|
||||
D03*
|
||||
Y442100D02*
|
||||
D03*
|
||||
X500Y465400D02*
|
||||
D03*
|
||||
Y449600D02*
|
||||
D03*
|
||||
X-68000Y456400D02*
|
||||
D03*
|
||||
Y440600D02*
|
||||
D03*
|
||||
X-83000Y456400D02*
|
||||
D03*
|
||||
Y440600D02*
|
||||
D03*
|
||||
X-52000Y456400D02*
|
||||
D03*
|
||||
Y440600D02*
|
||||
D03*
|
||||
X-98000D02*
|
||||
D03*
|
||||
Y456400D02*
|
||||
D03*
|
||||
X-139250Y212900D02*
|
||||
D03*
|
||||
Y197100D02*
|
||||
D03*
|
||||
X134000Y267900D02*
|
||||
D03*
|
||||
Y252100D02*
|
||||
D03*
|
||||
X68000Y385400D02*
|
||||
D03*
|
||||
Y369600D02*
|
||||
D03*
|
||||
X-124250Y212900D02*
|
||||
D03*
|
||||
Y197100D02*
|
||||
D03*
|
||||
X-53000Y279100D02*
|
||||
D03*
|
||||
Y294900D02*
|
||||
D03*
|
||||
X-69000Y279100D02*
|
||||
D03*
|
||||
Y294900D02*
|
||||
D03*
|
||||
D28*
|
||||
X-95400Y232500D02*
|
||||
D03*
|
||||
X-79600D02*
|
||||
D03*
|
||||
X189900Y22000D02*
|
||||
D03*
|
||||
X174100D02*
|
||||
D03*
|
||||
X190900Y76000D02*
|
||||
D03*
|
||||
X175100D02*
|
||||
D03*
|
||||
X218900D02*
|
||||
D03*
|
||||
X203100D02*
|
||||
D03*
|
||||
X-82400Y385000D02*
|
||||
D03*
|
||||
X-66600D02*
|
||||
D03*
|
||||
X35100Y397500D02*
|
||||
D03*
|
||||
X50900D02*
|
||||
D03*
|
||||
X35100Y351000D02*
|
||||
D03*
|
||||
X50900D02*
|
||||
D03*
|
||||
X50900Y412500D02*
|
||||
D03*
|
||||
X35100D02*
|
||||
D03*
|
||||
X-82400Y365250D02*
|
||||
D03*
|
||||
X-66600D02*
|
||||
D03*
|
||||
X35100Y336000D02*
|
||||
D03*
|
||||
X50900D02*
|
||||
D03*
|
||||
X-82400Y350250D02*
|
||||
D03*
|
||||
X-66600D02*
|
||||
D03*
|
||||
X15900Y477500D02*
|
||||
D03*
|
||||
X100D02*
|
||||
D03*
|
||||
X50900Y427500D02*
|
||||
D03*
|
||||
X35100D02*
|
||||
D03*
|
||||
X15900Y435000D02*
|
||||
D03*
|
||||
X100D02*
|
||||
D03*
|
||||
X137100Y328000D02*
|
||||
D03*
|
||||
X152900D02*
|
||||
D03*
|
||||
X137100Y403000D02*
|
||||
D03*
|
||||
X152900D02*
|
||||
D03*
|
||||
D29*
|
||||
X151000Y191000D02*
|
||||
D03*
|
||||
Y175000D02*
|
||||
D03*
|
||||
X-47000Y237000D02*
|
||||
D03*
|
||||
Y221000D02*
|
||||
D03*
|
||||
X-32000Y237000D02*
|
||||
D03*
|
||||
Y221000D02*
|
||||
D03*
|
||||
X151000Y161000D02*
|
||||
D03*
|
||||
Y145000D02*
|
||||
D03*
|
||||
D30*
|
||||
X-31000Y203000D02*
|
||||
D03*
|
||||
X-49000D02*
|
||||
D03*
|
||||
X-40000Y183000D02*
|
||||
D03*
|
||||
X-186500Y257500D02*
|
||||
D03*
|
||||
X-168500D02*
|
||||
D03*
|
||||
X-177500Y277500D02*
|
||||
D03*
|
||||
D31*
|
||||
X-224000Y154000D02*
|
||||
D03*
|
||||
X214000Y464000D02*
|
||||
D03*
|
||||
D32*
|
||||
X-101939Y215670D02*
|
||||
D03*
|
||||
Y206170D02*
|
||||
D03*
|
||||
Y196670D02*
|
||||
D03*
|
||||
X-73439D02*
|
||||
D03*
|
||||
Y206170D02*
|
||||
D03*
|
||||
Y215670D02*
|
||||
D03*
|
||||
D33*
|
||||
X36250Y382750D02*
|
||||
D03*
|
||||
Y372250D02*
|
||||
D03*
|
||||
X49750Y382750D02*
|
||||
D03*
|
||||
Y372250D02*
|
||||
D03*
|
||||
D34*
|
||||
X-182000Y55500D02*
|
||||
D03*
|
||||
Y128500D02*
|
||||
D03*
|
||||
D35*
|
||||
X167450Y342500D02*
|
||||
D03*
|
||||
Y347500D02*
|
||||
D03*
|
||||
Y352500D02*
|
||||
D03*
|
||||
Y377500D02*
|
||||
D03*
|
||||
Y372500D02*
|
||||
D03*
|
||||
Y367500D02*
|
||||
D03*
|
||||
Y357500D02*
|
||||
D03*
|
||||
Y362500D02*
|
||||
D03*
|
||||
D36*
|
||||
Y384000D02*
|
||||
D03*
|
||||
Y336000D02*
|
||||
D03*
|
||||
Y392000D02*
|
||||
D03*
|
||||
Y328000D02*
|
||||
D03*
|
||||
D37*
|
||||
X45700Y166000D02*
|
||||
D03*
|
||||
X74300D02*
|
||||
D03*
|
||||
X79700Y281000D02*
|
||||
D03*
|
||||
X108300D02*
|
||||
D03*
|
||||
D38*
|
||||
X47066Y188098D02*
|
||||
D03*
|
||||
Y200798D02*
|
||||
D03*
|
||||
Y213498D02*
|
||||
D03*
|
||||
Y226198D02*
|
||||
D03*
|
||||
D39*
|
||||
X86944Y207148D02*
|
||||
D03*
|
||||
D40*
|
||||
X-9000Y168000D02*
|
||||
D03*
|
||||
X-25000D02*
|
||||
D03*
|
||||
X5000Y183000D02*
|
||||
D03*
|
||||
X21000D02*
|
||||
D03*
|
||||
Y168000D02*
|
||||
D03*
|
||||
X5000D02*
|
||||
D03*
|
||||
X-116000Y329000D02*
|
||||
D03*
|
||||
X-132000D02*
|
||||
D03*
|
||||
X-117000Y374000D02*
|
||||
D03*
|
||||
X-133000D02*
|
||||
D03*
|
||||
X-117000Y389000D02*
|
||||
D03*
|
||||
X-133000D02*
|
||||
D03*
|
||||
X-205250Y287500D02*
|
||||
D03*
|
||||
X-221250D02*
|
||||
D03*
|
||||
X-185500Y225000D02*
|
||||
D03*
|
||||
X-169500D02*
|
||||
D03*
|
||||
X-185500Y240000D02*
|
||||
D03*
|
||||
X-169500D02*
|
||||
D03*
|
||||
D41*
|
||||
X-132500Y345500D02*
|
||||
D03*
|
||||
Y354500D02*
|
||||
D03*
|
||||
X-116500D02*
|
||||
D03*
|
||||
Y345500D02*
|
||||
D03*
|
||||
D42*
|
||||
X-101850Y255000D02*
|
||||
D03*
|
||||
X-73150D02*
|
||||
D03*
|
||||
D43*
|
||||
X-125300Y235000D02*
|
||||
D03*
|
||||
X-144700D02*
|
||||
D03*
|
||||
X-125300Y255000D02*
|
||||
D03*
|
||||
X-144700D02*
|
||||
D03*
|
||||
D44*
|
||||
X-59500Y322000D02*
|
||||
D03*
|
||||
X-100500D02*
|
||||
D03*
|
||||
D45*
|
||||
X0Y535500D02*
|
||||
D03*
|
||||
Y504500D02*
|
||||
D03*
|
||||
D46*
|
||||
X173200Y403200D02*
|
||||
D03*
|
||||
Y316800D02*
|
||||
D03*
|
||||
X215000Y403200D02*
|
||||
D03*
|
||||
Y316800D02*
|
||||
D03*
|
||||
M02*
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,3 @@
|
||||
Layer Pairs Export File for PCB: C:\Users\Sasa\Desktop\git\_shop\DryBox\Hardware\DryBox.PcbDoc
|
||||
LayersSetName=Top_Bot_Thru_Holes|DrillFile=drybox-roundholes.txt|DrillLayers=gtl,gbl
|
||||
LayersSetName=Top_Bot_Slot_Holes|DrillFile=drybox-slotholes.txt|DrillLayers=gtl,gbl
|
||||
@@ -0,0 +1,8 @@
|
||||
DRC Rules Export File for PCB: C:\Users\Sasa\Desktop\git\_shop\DryBox\Hardware\DryBox.PcbDoc
|
||||
RuleKind=Clearance|RuleName=Clearance_3|Scope=Board|Minimum=7.87
|
||||
RuleKind=Clearance|RuleName=Clearance_2|Scope=Board|Minimum=5.91
|
||||
RuleKind=Clearance|RuleName=Clearance_1|Scope=Board|Minimum=11.81
|
||||
RuleKind=SolderMaskExpansion|RuleName=SolderMaskExpansion|Scope=Board|Minimum=4.00
|
||||
RuleKind=Width|RuleName=Width|Scope=Board|Minimum=5.91
|
||||
RuleKind=Clearance|RuleName=Clearance|Scope=Board|Minimum=5.91
|
||||
RuleKind=ShortCircuit|RuleName=ShortCircuit|Scope=Board|Allowed=0
|
||||
@@ -0,0 +1,95 @@
|
||||
D10 ROUNDED 7.874 7.874 0.000 LINE 0.000
|
||||
D11 ROUNDED 9.842 9.842 0.000 LINE 0.000
|
||||
D12 ROUNDED 11.811 11.811 0.000 LINE 0.000
|
||||
D13 ROUNDED 5.000 5.000 0.000 LINE 0.000
|
||||
D14 ROUNDED 8.000 8.000 0.000 LINE 0.000
|
||||
D15 ROUNDED 3.937 3.937 0.000 LINE 0.000
|
||||
D16 ROUNDED 5.906 5.906 0.000 LINE 0.000
|
||||
D17 ROUNDED 5.020 5.020 0.000 LINE 0.000
|
||||
D18 ROUNDED 10.000 10.000 0.000 LINE 0.000
|
||||
D19 ROUNDED 7.000 7.000 0.000 LINE 0.000
|
||||
D21 RECTANGULAR 27.559 62.992 0.000 FLASH 90.000
|
||||
D22 RECTANGULAR 23.622 43.307 0.000 FLASH 270.000
|
||||
D23 RECTANGULAR 23.622 43.307 0.000 FLASH 180.000
|
||||
D26 RECTANGULAR 137.795 137.795 0.000 FLASH 270.000
|
||||
D27 RECTANGULAR 27.559 35.433 0.000 FLASH 90.000
|
||||
D28 RECTANGULAR 27.559 35.433 0.000 FLASH 180.000
|
||||
D29 RECTANGULAR 37.992 35.433 0.000 FLASH 90.000
|
||||
D30 RECTANGULAR 31.496 35.433 0.000 FLASH 0.000
|
||||
D31 ROUNDED 39.370 39.370 0.000 FLASH 0.000
|
||||
D32 RECTANGULAR 23.622 41.339 0.000 FLASH 90.000
|
||||
D33 RECTANGULAR 33.465 29.528 0.000 FLASH 180.000
|
||||
D34 RECTANGULAR 165.354 86.614 0.000 FLASH 270.000
|
||||
D35 RECTANGULAR 11.811 45.276 0.000 FLASH 90.000
|
||||
D36 RECTANGULAR 23.622 45.276 0.000 FLASH 90.000
|
||||
D37 RECTANGULAR 52.756 70.866 0.000 FLASH 180.000
|
||||
D38 RECTANGULAR 51.181 23.622 0.000 FLASH 0.000
|
||||
D39 RECTANGULAR 161.417 173.622 0.000 FLASH 0.000
|
||||
D40 RECTANGULAR 37.992 35.433 0.000 FLASH 180.000
|
||||
D41 RECTANGULAR 21.654 31.496 0.000 FLASH 270.000
|
||||
D42 RECTANGULAR 79.528 48.425 0.000 FLASH 270.000
|
||||
D43 RECTANGULAR 39.370 51.181 0.000 FLASH 0.000
|
||||
D44 RECTANGULAR 35.433 62.992 0.000 FLASH 180.000
|
||||
D45 RECTANGULAR 70.866 15.748 0.000 FLASH 0.000
|
||||
D46 ROUNDED 90.551 51.181 0.000 FLASH 180.000
|
||||
D48 RECTANGULAR 35.559 70.992 0.000 FLASH 90.000
|
||||
D49 RECTANGULAR 31.622 51.307 0.000 FLASH 270.000
|
||||
D50 RECTANGULAR 31.622 51.307 0.000 FLASH 180.000
|
||||
D53 RECTANGULAR 145.795 145.795 0.000 FLASH 270.000
|
||||
D54 RECTANGULAR 35.559 43.433 0.000 FLASH 90.000
|
||||
D55 RECTANGULAR 35.559 43.433 0.000 FLASH 180.000
|
||||
D56 RECTANGULAR 45.992 43.433 0.000 FLASH 90.000
|
||||
D57 RECTANGULAR 39.496 43.433 0.000 FLASH 0.000
|
||||
D58 ROUNDED 74.803 74.803 0.000 FLASH 0.000
|
||||
D59 RECTANGULAR 31.622 49.339 0.000 FLASH 90.000
|
||||
D60 RECTANGULAR 41.465 37.528 0.000 FLASH 180.000
|
||||
D61 RECTANGULAR 173.354 94.614 0.000 FLASH 270.000
|
||||
D62 RECTANGULAR 19.811 53.276 0.000 FLASH 90.000
|
||||
D63 RECTANGULAR 31.622 53.276 0.000 FLASH 90.000
|
||||
D64 RECTANGULAR 60.756 78.866 0.000 FLASH 180.000
|
||||
D65 RECTANGULAR 59.181 31.622 0.000 FLASH 0.000
|
||||
D66 RECTANGULAR 169.417 181.622 0.000 FLASH 0.000
|
||||
D67 RECTANGULAR 45.992 43.433 0.000 FLASH 180.000
|
||||
D68 RECTANGULAR 29.654 39.496 0.000 FLASH 270.000
|
||||
D69 RECTANGULAR 87.528 56.425 0.000 FLASH 270.000
|
||||
D70 RECTANGULAR 47.370 59.181 0.000 FLASH 0.000
|
||||
D71 RECTANGULAR 43.433 70.992 0.000 FLASH 180.000
|
||||
D72 RECTANGULAR 78.866 23.748 0.000 FLASH 0.000
|
||||
D73 ROUNDED 145.795 118.236 0.000 FLASH 0.000
|
||||
D74 ROUNDED 86.740 126.110 0.000 FLASH 90.000
|
||||
D75 ROUNDED 67.055 67.055 0.000 FLASH 0.000
|
||||
D76 RECTANGULAR 67.055 67.055 0.000 FLASH 270.000
|
||||
D77 ROUNDED 165.480 165.480 0.000 FLASH 0.000
|
||||
D78 ROUNDED 33.591 33.591 0.000 FLASH 0.000
|
||||
D79 ROUNDED 98.551 59.181 0.000 FLASH 180.000
|
||||
D80 ROUNDED 145.795 145.795 0.000 FLASH 0.000
|
||||
D81 ROUNDED 8.000 8.000 0.000 FLASH 0.000
|
||||
D82 ROUNDED 63.118 63.118 0.000 FLASH 0.000
|
||||
D83 ROUNDED 86.740 86.740 0.000 FLASH 0.000
|
||||
D84 ROUNDED 39.496 39.496 0.000 FLASH 0.000
|
||||
D85 ROUNDED 39.370 39.370 0.000 LINE 0.000
|
||||
D86 ROUNDED 19.685 19.685 0.000 LINE 0.000
|
||||
D87 ROUNDED 15.748 15.748 0.000 LINE 0.000
|
||||
D88 ROUNDED 35.433 35.433 0.000 LINE 0.000
|
||||
D89 ROUNDED 27.559 27.559 0.000 LINE 0.000
|
||||
D90 ROUNDED 23.622 23.622 0.000 LINE 0.000
|
||||
D91 ROUNDED 31.496 31.496 0.000 LINE 0.000
|
||||
D92 ROUNDED 78.740 78.740 0.000 LINE 0.000
|
||||
D93 ROUNDED 52.756 52.756 0.000 LINE 0.000
|
||||
D94 ROUNDED 137.795 110.236 0.000 FLASH 0.000
|
||||
D95 ROUNDED 78.740 118.110 0.000 FLASH 90.000
|
||||
D96 ROUNDED 59.055 59.055 0.000 FLASH 0.000
|
||||
D97 RECTANGULAR 59.055 59.055 0.000 FLASH 270.000
|
||||
D98 ROUNDED 157.480 157.480 0.000 FLASH 0.000
|
||||
D99 ROUNDED 25.591 25.591 0.000 FLASH 0.000
|
||||
D100 ROUNDED 137.795 137.795 0.000 FLASH 0.000
|
||||
D101 ROUNDED 55.118 55.118 0.000 FLASH 0.000
|
||||
D102 ROUNDED 78.740 78.740 0.000 FLASH 0.000
|
||||
D103 ROUNDED 31.496 31.496 0.000 FLASH 0.000
|
||||
D104 ROUNDED 2.000 2.000 0.000 LINE 0.000
|
||||
D105 ROUNDED 7.874 7.874 0.000 FLASH 0.000
|
||||
D106 ROUNDED 15.000 15.000 0.000 LINE 0.000
|
||||
D107 ROUNDED 1.000 1.000 0.000 LINE 0.000
|
||||
D108 ROUNDED 16.000 16.000 0.000 LINE 0.000
|
||||
D109 ROUNDED 14.000 14.000 0.000 LINE 0.000
|
||||
D110 ROUNDED 6.000 6.000 0.000 LINE 0.000
|
||||
@@ -0,0 +1,96 @@
|
||||
Altium Designer Pick and Place Locations
|
||||
C:\Users\Sasa\Desktop\git\_shop\DryBox\Hardware\Project Outputs for DryBox\Pick Place\Pick Place for DryBox.txt
|
||||
|
||||
========================================================================================================================
|
||||
File Design Information:
|
||||
|
||||
Date: 09-10-24
|
||||
Time: 22:39
|
||||
Revision: 1c9de3defc70aa6f167d8c94982d31005a57ed81
|
||||
Variant: No variations
|
||||
Units used: mm
|
||||
|
||||
Designator Comment Layer Center-X(mm) Center-Y(mm) Rotation Description
|
||||
U3 NCP161ASN330T1G TopLayer -13.6000mm 29.3000mm 90 "Linear Voltage Regulator IC Positive Fixed 1 Output 450mA SOT-23-5"
|
||||
C26 Cap TopLayer -8.4000mm 28.7000mm 90 Capacitor
|
||||
C25 Cap TopLayer -9.9000mm 28.7000mm 90 Capacitor
|
||||
L2 Inductor TopLayer -3.8000mm 44.8500mm 90 "1.1A 2.2nH 100mO 0603 Inductors (SMD) ROHS"
|
||||
U1 SHTC3-TR-10KS TopLayer -19.5000mm 45.0000mm 90 "DFN-4-EP(2x2) Temperature and Humidity Sensor ROHS"
|
||||
R1 Res TopLayer 18.3000mm 7.6000mm 180 "100mW Thick Film Resistors 75V ±100ppm/? ±1% 100kO 0603 Chip Resistor - Surface Mount ROHS"
|
||||
U5 MCP3021A5T-E/OT TopLayer 18.2000mm 4.9000mm 180 "10 Bit Analog to Digital Converter 1 Input 1 SAR SOT-23-5"
|
||||
C2 Cap TopLayer 21.4000mm 4.9000mm 90 Capacitor
|
||||
C1 Cap TopLayer 18.2000mm 2.2000mm 180 Capacitor
|
||||
P1 DB2ERC-2.54-3P-GN TopLayer -21.0000mm 23.0000mm 270 "150V 5A 2 Bend -40?~+105? Copper Alloy 1 2.54mm 1x3P Green Board Edge/Receptacle-Close Tin Push-Pull,P=2.54mm Pluggable System Terminal Block ROHS"
|
||||
P7 DB2EVC-2.54-4P-GN TopLayer 20.3000mm 22.0300mm 90 "150V 5A 3 Straight pin -40?~+105? Copper Alloy 1 2.54mm 1x3P Green Board Edge/Receptacle-Close Tin Plugin,P=2.54mm Pluggable System Terminal Block ROHS"
|
||||
P5 DB2EVC-2.54-2P-GN TopLayer 20.3000mm 12.1000mm 90 "2 Position Terminal Block Header, Male Pins, Shrouded (4 Side) 0.098" (2.50mm) Vertical Through Hole"
|
||||
MH4 "Mounting Hole" TopLayer 21.5000mm -1.7030mm 0 "Board Mounting Hole"
|
||||
MH3 "Mounting Hole" TopLayer 21.5000mm 54.7970mm 0 "Board Mounting Hole"
|
||||
MH2 "Mounting Hole" TopLayer -21.5000mm -1.7030mm 0 "Board Mounting Hole"
|
||||
MH1 "Mounting Hole" TopLayer -21.5000mm 54.7970mm 0 "Board Mounting Hole"
|
||||
FD2 "Fiducial 1mm" TopLayer -22.4000mm 15.4000mm 0 ""
|
||||
FD1 "Fiducial 1mm" TopLayer 21.4000mm 46.4000mm 0 ""
|
||||
D4 "RGB LED" TopLayer -12.4500mm 35.0000mm 180 "Red, Green, Blue (RGB) - LED Indication - Discrete 1.9V Red, 2.8V Green, 2.8V Blue 0606 (1616 Metric)"
|
||||
L1 Inductor TopLayer -8.7500mm 25.5000mm 180 Inductor
|
||||
X1 40MHz TopLayer 4.3000mm 37.7500mm 180 "40MHz 9pF ±10ppm SMD2016-4P Crystals ROHS"
|
||||
U4 AP63205WU-7 TopLayer -8.7690mm 20.6170mm 0 "Step-down type Fixed 2A 5V 3.8V~32V TSOT-23-6 DC-DC Converters ROHS"
|
||||
SW3 Switch TopLayer -8.0000mm 32.2000mm 0 "50mA Surface Mount SPST 100MO 100,000 Times 12V 160gf@±50gf SMD Tactile Switches ROHS"
|
||||
R30 Res TopLayer 14.9000mm 26.0000mm 270 Resistor
|
||||
R29 Res TopLayer 4.3000mm 35.1000mm 0 Resistor
|
||||
R28 Res TopLayer 14.5000mm 40.3000mm 0 Resistor
|
||||
R27 Res TopLayer 14.5000mm 32.8000mm 0 Resistor
|
||||
R26 Res TopLayer -12.4000mm 32.9000mm 180 Resistor
|
||||
R25 Res TopLayer -12.5000mm 37.4000mm 180 Resistor
|
||||
R24 Res TopLayer -12.5000mm 38.9000mm 180 Resistor
|
||||
R21 Res TopLayer -7.4500mm 36.5250mm 0 Resistor
|
||||
R16 Res TopLayer -9.8000mm 44.8500mm 90 Resistor
|
||||
R15 Res TopLayer 13.4000mm 26.0000mm 270 Resistor
|
||||
R14 Res TopLayer 0.0500mm 45.7500mm 270 Resistor
|
||||
P9 "USB-C Recepticle" TopLayer 19.1225mm 36.0000mm 90 "3A 1 Surface Mount 16P Female -25?~+85? Type-C SMD USB Connectors ROHS"
|
||||
C35 Cap TopLayer 6.8000mm 37.7500mm 270 Capacitor
|
||||
C34 Cap TopLayer -5.2000mm 44.8500mm 270 Capacitor
|
||||
C33 Cap TopLayer -2.2000mm 44.8500mm 270 Capacitor
|
||||
C32 Cap TopLayer -3.9000mm 32.3500mm 90 Capacitor
|
||||
C31 Cap TopLayer -7.4500mm 38.5000mm 0 Capacitor
|
||||
C30 Cap TopLayer -0.9000mm 32.3500mm 90 Capacitor
|
||||
C29 Cap TopLayer 15.1000mm 18.3000mm 90 Capacitor
|
||||
C28 Cap TopLayer -22.2500mm 45.0000mm 270 Capacitor
|
||||
C23 Cap TopLayer -8.7500mm 23.2500mm 180 Capacitor
|
||||
C19 Cap TopLayer 4.3000mm 39.7500mm 0 Capacitor
|
||||
C18 Cap TopLayer 4.3000mm 42.7500mm 180 Capacitor
|
||||
C17 Cap TopLayer 4.3000mm 41.2500mm 180 Capacitor
|
||||
C16 Cap TopLayer -6.8000mm 44.8500mm 270 Capacitor
|
||||
C6 Cap TopLayer -13.5000mm 23.5000mm 0 Capacitor
|
||||
C5 Cap TopLayer -13.5000mm 25.5000mm 0 Capacitor
|
||||
C4 Cap TopLayer -13.9250mm 20.5000mm 270 Capacitor
|
||||
A1 ANT3216LL00R2400A TopLayer 0.0000mm 52.0000mm 90 "RF ANT 2.45GHZ CHIP SOLDER SMD"
|
||||
C27 Cap TopLayer 15.1000mm 15.3000mm 270 Capacitor
|
||||
C3 Cap TopLayer -12.4250mm 20.5000mm 270 Capacitor
|
||||
R9 Res TopLayer 21.1000mm 7.6000mm 180 "100mW Thick Film Resistors 75V ±100ppm/? ±1% 100kO 0603 Chip Resistor - Surface Mount ROHS"
|
||||
R23 Res TopLayer -1.7000mm 16.8000mm 180 Resistor
|
||||
R19 Res TopLayer 1.3000mm 16.8000mm 180 Resistor
|
||||
R17 Res TopLayer 1.3000mm 18.3000mm 0 Resistor
|
||||
R13 Res TopLayer -17.7500mm 24.0000mm 0 Resistor
|
||||
R5 Res TopLayer -17.7500mm 22.5000mm 0 Resistor
|
||||
R12 Res TopLayer -21.3250mm 28.7500mm 180 Resistor
|
||||
R8 Res TopLayer -7.4500mm 35.0250mm 0 Resistor
|
||||
Q1 DMN62D0U-13 TopLayer -17.7500mm 26.7500mm 90 "MOSFET N-CH 60V 0.38A"
|
||||
D2 MBR1020VL TopLayer 9.4000mm 28.1000mm 180 "Diode Schottky 20V 1A Surface Mount SOD-123F"
|
||||
U2 ESP32-C3-FH4 TopLayer -1.7000mm 37.7500mm 270 "Ultra-Low-Power SoC with RISC-V Single-Core CPU 2.4 GHz Wi-Fi (802.11 b/g/n) and Bluetooth® 5 (LE)"
|
||||
R20 Res TopLayer -8.3000mm 44.8500mm 270 Resistor
|
||||
R4 Res TopLayer 4.3000mm 33.6000mm 0 Resistor
|
||||
C13 Cap TopLayer -5.3000mm 28.7000mm 270 Capacitor
|
||||
C12 Cap TopLayer -6.9000mm 28.7000mm 270 Capacitor
|
||||
C11 Cap TopLayer -2.4000mm 32.3500mm 90 Capacitor
|
||||
C10 "Cap - 220uF 35V" TopLayer -18.2000mm 9.2000mm 270 "220 µF 35V Aluminum - Polymer Capacitors Radial, Can - SMD 29mOhm 2000 Hrs @ 125°C"
|
||||
C9 Cap TopLayer 0.8000mm 43.5000mm 180 Capacitor
|
||||
C8 Cap TopLayer 0.8000mm 47.7500mm 180 Capacitor
|
||||
C7 Cap TopLayer -11.3000mm 44.8500mm 90 Capacitor
|
||||
Q3 DMN62D0U-13 TopLayer -4.0000mm 19.3000mm 270 "MOSFET N-CH 60V 0.38A"
|
||||
P3 DB2ERC-2.54-2P-GN TopLayer -6.5000mm 5.5000mm 0 "150V 5A 2 Bend -40?~+105? Copper Alloy 1 2.54mm 1x2P Green Board Edge/Receptacle-Close Tin Push-Pull,P=2.54mm Pluggable System Terminal Block ROHS"
|
||||
P11 WM4330-ND TopLayer -19.4200mm 35.2501mm 90 "4 Positions Header Connector 0.100" (2.54mm) Through Hole Tin"
|
||||
F1 3557-2 TopLayer -2.6000mm 11.6000mm 0 "Fuse Block 30 A 500V 1 Circuit Blade Through Hole"
|
||||
R18 Res TopLayer -4.7000mm 22.9000mm 270 Resistor
|
||||
R22 Res TopLayer -3.2000mm 22.9000mm 270 Resistor
|
||||
Q2 PSMN9R1-30YL TopLayer 6.7005mm 20.7148mm 0 "MOSFET N-CH 30V 37A LFPAK"
|
||||
P8 DB2ERC-2.54-2P-GN TopLayer 6.5600mm 5.5000mm 0 "150V 5A 2 Bend -40?~+105? Copper Alloy 1 2.54mm 1x2P Green Board Edge/Receptacle-Close Tin Push-Pull,P=2.54mm Pluggable System Terminal Block ROHS"
|
||||
D3 MBR230S1F-7 TopLayer 6.0000mm 16.6000mm 180 "Diode Schottky 20V 1A Surface Mount SOD-123F"
|
||||
@@ -0,0 +1,44 @@
|
||||
# DIY WiFi Enabled 3D Printing Filament Dry Box v2
|
||||
|
||||
|
||||
## What is this project about?
|
||||
|
||||
[](http://www.youtube.com/watch?v=1QqDBpliQO0 "DIY WiFi Enabled Filament Dry Box v2")
|
||||
|
||||
Above video shows the finished DryBox v2, how it works and motivation behind some of the design decisions.
|
||||
|
||||
## Can I build my own?
|
||||
|
||||
Yes! All hardware and firmware source files are available in their respective folders.
|
||||
|
||||
The above video explains the idea and how hardware and firmware works. It's recommended that you watch that first.
|
||||
|
||||
There is [documentation page](https://docs.sasakaranovic.com/drybox/) that can help you with all the parts and tools you might need.
|
||||
|
||||
Also the original [DIY Filament Dryer v1 video](https://www.youtube.com/watch?v=1QqDBpliQO0) can fill in some gaps.
|
||||
|
||||
|
||||
## Can I buy a kit?
|
||||
|
||||
Yes! If you don't want to make your own electronics, you can buy the kit from the [web shop](https://shop.sasakaranovic.com/products/drybox-v2-diy-dry-box). You will still have to source some of the off-the-shelf parts yourself.
|
||||
|
||||
---
|
||||
|
||||
Blog page: https://sasakaranovic.com/projects/diy-filament-dry-box-v2
|
||||
|
||||
YouTube video: http://www.youtube.com/watch?v=1QqDBpliQO0
|
||||
|
||||
|
||||
---
|
||||
|
||||
<br/><br/>
|
||||
|
||||
#### Sasa Karanovic
|
||||
|
||||
<a href="https://sasakaranovic.com/" target="_blank" title="Sasa Karanovic Home Page"><img src="https://raw.githubusercontent.com/SasaKaranovic/common/master/assets/img_home.png" width="16"> Home Page</a> ·
|
||||
<a href="https://shop.sasakaranovic.com/" target="_blank" title="Sasa Karanovic Web Shop"><img src="https://raw.githubusercontent.com/SasaKaranovic/common/master/assets/img_home.png" width="16"> Web Shop</a> ·
|
||||
<a href="https://youtube.com/c/sasakaranovic" target="_blank" title="Sasa Karanovic on YouTube"><img src="https://raw.githubusercontent.com/SasaKaranovic/common/master/assets/img_youtube.png" width="16"> YouTube</a> ·
|
||||
<a href="https://github.com/sasakaranovic" target="_blank" title="Sasa Karanovic on GitHub"><img src="https://raw.githubusercontent.com/SasaKaranovic/common/master/assets/img_github.png" width="16"> GitHub</a> ·
|
||||
<a href="https://twitter.com/_sasakaranovic_" target="_blank" title="Sasa Karanovic on Twitter"><img src="https://raw.githubusercontent.com/SasaKaranovic/common/master/assets/img_twitter.png" width="16"> Twitter</a> ·
|
||||
<a href="https://instagram.com/_sasakaranovic_" target="_blank" title="Sasa Karanovic on Instagram"><img src="https://raw.githubusercontent.com/SasaKaranovic/common/master/assets/img_instagram.png" width="16"> Instagram</a> ·
|
||||
<a href="https://github.com/sponsors/SasaKaranovic" target="_blank" title="Sponsor on GitHub"><img src="https://raw.githubusercontent.com/SasaKaranovic/common/master/assets/img_github.png" width="16"> Sponsor on GitHub</a>
|
||||
Reference in New Issue
Block a user