From bfaf0e4e7db51de0eb28b35c7d9c915d1aa7c598 Mon Sep 17 00:00:00 2001 From: Gurkengewuerz Date: Mon, 8 Aug 2022 15:43:30 +0200 Subject: [PATCH] wer das liest ist doof --- .gitignore | 5 + .gitkeep | 0 .vscode/extensions.json | 10 + .vscode/settings.json | 3 + data/css/font.css | 200 +++++++++++++ data/css/style.css | 378 ++++++++++++++++++++++++ data/index.html | 60 ++++ data/js/index.js | 71 +++++ data/static/svg/graphic.svg | 20 ++ data/static/svg/logo.svg | 11 + include/README | 39 +++ lib/Display/display.cpp | 98 ++++++ lib/Display/display.h | 44 +++ lib/Input/input_controller.cpp | 47 +++ lib/Input/input_controller.h | 19 ++ lib/README | 46 +++ lib/Utils/file_handler.cpp | 84 ++++++ lib/Utils/file_handler.h | 20 ++ lib/View/Controller/view_controller.cpp | 16 + lib/View/Model/view.h | 8 + lib/View/Model/view_controller.h | 13 + lib/View/View/home_view.cpp | 25 ++ lib/View/View/home_view.h | 14 + lib/WiFi_Manager/wifi_manager.cpp | 207 +++++++++++++ lib/WiFi_Manager/wifi_manager.h | 44 +++ platformio.ini | 23 ++ project.json | 27 ++ src/main.cpp | 96 ++++++ test/README | 11 + 29 files changed, 1639 insertions(+) create mode 100644 .gitignore create mode 100644 .gitkeep create mode 100644 .vscode/extensions.json create mode 100644 .vscode/settings.json create mode 100644 data/css/font.css create mode 100644 data/css/style.css create mode 100644 data/index.html create mode 100644 data/js/index.js create mode 100644 data/static/svg/graphic.svg create mode 100644 data/static/svg/logo.svg create mode 100644 include/README create mode 100644 lib/Display/display.cpp create mode 100644 lib/Display/display.h create mode 100644 lib/Input/input_controller.cpp create mode 100644 lib/Input/input_controller.h create mode 100644 lib/README create mode 100644 lib/Utils/file_handler.cpp create mode 100644 lib/Utils/file_handler.h create mode 100644 lib/View/Controller/view_controller.cpp create mode 100644 lib/View/Model/view.h create mode 100644 lib/View/Model/view_controller.h create mode 100644 lib/View/View/home_view.cpp create mode 100644 lib/View/View/home_view.h create mode 100644 lib/WiFi_Manager/wifi_manager.cpp create mode 100644 lib/WiFi_Manager/wifi_manager.h create mode 100644 platformio.ini create mode 100644 project.json create mode 100644 src/main.cpp create mode 100644 test/README diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..89cc49c --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.pio +.vscode/.browse.c_cpp.db* +.vscode/c_cpp_properties.json +.vscode/launch.json +.vscode/ipch diff --git a/.gitkeep b/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..080e70d --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,10 @@ +{ + // See http://go.microsoft.com/fwlink/?LinkId=827846 + // for the documentation about the extensions.json format + "recommendations": [ + "platformio.platformio-ide" + ], + "unwantedRecommendations": [ + "ms-vscode.cpptools-extension-pack" + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..691a8f6 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "C_Cpp.errorSquiggles": "Disabled" +} \ No newline at end of file diff --git a/data/css/font.css b/data/css/font.css new file mode 100644 index 0000000..3e0070c --- /dev/null +++ b/data/css/font.css @@ -0,0 +1,200 @@ +/* cyrillic-ext */ +@font-face { + font-family: 'Montserrat'; + font-style: normal; + font-weight: 300; + src: url(https://fonts.gstatic.com/s/montserrat/v25/JTUSjIg1_i6t8kCHKm459WRhyzbi.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Montserrat'; + font-style: normal; + font-weight: 300; + src: url(https://fonts.gstatic.com/s/montserrat/v25/JTUSjIg1_i6t8kCHKm459W1hyzbi.woff2) format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* vietnamese */ +@font-face { + font-family: 'Montserrat'; + font-style: normal; + font-weight: 300; + src: url(https://fonts.gstatic.com/s/montserrat/v25/JTUSjIg1_i6t8kCHKm459WZhyzbi.woff2) format('woff2'); + unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; +} +/* latin-ext */ +@font-face { + font-family: 'Montserrat'; + font-style: normal; + font-weight: 300; + src: url(https://fonts.gstatic.com/s/montserrat/v25/JTUSjIg1_i6t8kCHKm459Wdhyzbi.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Montserrat'; + font-style: normal; + font-weight: 300; + src: url(https://fonts.gstatic.com/s/montserrat/v25/JTUSjIg1_i6t8kCHKm459Wlhyw.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Montserrat'; + font-style: normal; + font-weight: 400; + src: url(https://fonts.gstatic.com/s/montserrat/v25/JTUSjIg1_i6t8kCHKm459WRhyzbi.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Montserrat'; + font-style: normal; + font-weight: 400; + src: url(https://fonts.gstatic.com/s/montserrat/v25/JTUSjIg1_i6t8kCHKm459W1hyzbi.woff2) format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* vietnamese */ +@font-face { + font-family: 'Montserrat'; + font-style: normal; + font-weight: 400; + src: url(https://fonts.gstatic.com/s/montserrat/v25/JTUSjIg1_i6t8kCHKm459WZhyzbi.woff2) format('woff2'); + unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; +} +/* latin-ext */ +@font-face { + font-family: 'Montserrat'; + font-style: normal; + font-weight: 400; + src: url(https://fonts.gstatic.com/s/montserrat/v25/JTUSjIg1_i6t8kCHKm459Wdhyzbi.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Montserrat'; + font-style: normal; + font-weight: 400; + src: url(https://fonts.gstatic.com/s/montserrat/v25/JTUSjIg1_i6t8kCHKm459Wlhyw.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Montserrat'; + font-style: normal; + font-weight: 500; + src: url(https://fonts.gstatic.com/s/montserrat/v25/JTUSjIg1_i6t8kCHKm459WRhyzbi.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Montserrat'; + font-style: normal; + font-weight: 500; + src: url(https://fonts.gstatic.com/s/montserrat/v25/JTUSjIg1_i6t8kCHKm459W1hyzbi.woff2) format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* vietnamese */ +@font-face { + font-family: 'Montserrat'; + font-style: normal; + font-weight: 500; + src: url(https://fonts.gstatic.com/s/montserrat/v25/JTUSjIg1_i6t8kCHKm459WZhyzbi.woff2) format('woff2'); + unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; +} +/* latin-ext */ +@font-face { + font-family: 'Montserrat'; + font-style: normal; + font-weight: 500; + src: url(https://fonts.gstatic.com/s/montserrat/v25/JTUSjIg1_i6t8kCHKm459Wdhyzbi.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Montserrat'; + font-style: normal; + font-weight: 500; + src: url(https://fonts.gstatic.com/s/montserrat/v25/JTUSjIg1_i6t8kCHKm459Wlhyw.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Montserrat'; + font-style: normal; + font-weight: 600; + src: url(https://fonts.gstatic.com/s/montserrat/v25/JTUSjIg1_i6t8kCHKm459WRhyzbi.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Montserrat'; + font-style: normal; + font-weight: 600; + src: url(https://fonts.gstatic.com/s/montserrat/v25/JTUSjIg1_i6t8kCHKm459W1hyzbi.woff2) format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* vietnamese */ +@font-face { + font-family: 'Montserrat'; + font-style: normal; + font-weight: 600; + src: url(https://fonts.gstatic.com/s/montserrat/v25/JTUSjIg1_i6t8kCHKm459WZhyzbi.woff2) format('woff2'); + unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; +} +/* latin-ext */ +@font-face { + font-family: 'Montserrat'; + font-style: normal; + font-weight: 600; + src: url(https://fonts.gstatic.com/s/montserrat/v25/JTUSjIg1_i6t8kCHKm459Wdhyzbi.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Montserrat'; + font-style: normal; + font-weight: 600; + src: url(https://fonts.gstatic.com/s/montserrat/v25/JTUSjIg1_i6t8kCHKm459Wlhyw.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Montserrat'; + font-style: normal; + font-weight: 700; + src: url(https://fonts.gstatic.com/s/montserrat/v25/JTUSjIg1_i6t8kCHKm459WRhyzbi.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Montserrat'; + font-style: normal; + font-weight: 700; + src: url(https://fonts.gstatic.com/s/montserrat/v25/JTUSjIg1_i6t8kCHKm459W1hyzbi.woff2) format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* vietnamese */ +@font-face { + font-family: 'Montserrat'; + font-style: normal; + font-weight: 700; + src: url(https://fonts.gstatic.com/s/montserrat/v25/JTUSjIg1_i6t8kCHKm459WZhyzbi.woff2) format('woff2'); + unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; +} +/* latin-ext */ +@font-face { + font-family: 'Montserrat'; + font-style: normal; + font-weight: 700; + src: url(https://fonts.gstatic.com/s/montserrat/v25/JTUSjIg1_i6t8kCHKm459Wdhyzbi.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Montserrat'; + font-style: normal; + font-weight: 700; + src: url(https://fonts.gstatic.com/s/montserrat/v25/JTUSjIg1_i6t8kCHKm459Wlhyw.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} \ No newline at end of file diff --git a/data/css/style.css b/data/css/style.css new file mode 100644 index 0000000..323e69a --- /dev/null +++ b/data/css/style.css @@ -0,0 +1,378 @@ +:root{ + --clr-accent-200: hsl(144, 75%, 65%); + --clr-accent-400: hsl(144, 75%, 55%); + --clr-accent-400-05: hsla(144, 75%, 61%, 0.5); + --clr-accent-600: hsl(144, 75%, 40%); + + --clr-primary-400: hsl(0, 0%, 20%); + --clr-primary-200: hsl(0, 0%, 36%); + + --ff-primary: 'Montserrat', sans-serif; + + --ff-body: var(--ff-primary); + + --fw-light: 300; + --fw-regular: 400; + --fw-medium: 500; + --fw-semi-bold: 600; + --fw-bold: 700; + + --fs-100: 0.5rem; + --fs-200: 0.5625rem; + --fs-300: 0.625; + --fs-400: 0.6875rem; + --fs-500: 0.75rem; + --fs-600: 0.8125rem; + --fs-700: 0.875rem; + --fs-800: 0.9375rem; + --fs-900: 1rem; + --fs-large-100: 1.25rem; + --fs-large-200: 2.25rem; + + --fs-body: var(--fs-900); + --fs-button: var(--fs-500); +} + +.ff-priamry {font-family: var(--ff-primary);} + +.bg-primary{background-color: white;} + +.fc-primary-400{color: var(--clr-primary-400);} +.fc-primary-200{color: var(--clr-primary-200);} + +.fc-accent{color: var(--clr-accent-400);} + +.fw-light {font-weight: var(--fw-light);} +.fw-regular {font-weight: var(--fw-regular);} +.fw-medium {font-weight: var(--fw-medium);} +.fw-semi-bold {font-weight: var(--fw-semi-bold);} +.fw-bold {font-weight: var(--fw-bold);} + +.fs-100 {font-size: var(--fs-100);} +.fs-200 {font-size: var(--fs-200);} +.fs-300 {font-size: var(--fs-300);} +.fs-400 {font-size: var(--fs-400);} +.fs-500 {font-size: var(--fs-500);} +.fs-600 {font-size: var(--fs-600);} +.fs-700 {font-size: var(--fs-700);} +.fs-800 {font-size: var(--fs-800);} +.fs-900 {font-size: var(--fs-900);} +.fs-large-100 {font-size: var(--fs-large-100);} + + +@media (min-width: 50em){ + .fs-700{font-size: var(--fs-800);} + .fs-800{font-size: var(--fs-900);} + + .wifi-form{ + width: 800px; + margin: 0 auto; + } + + #bottom-graphic{ + max-width: 800px; + } +} + +/* Box sizing rules */ +*, +*::before, +*::after { + box-sizing: border-box; +} + +/* Remove default margin */ +body, +h1, +h2, +h3, +h4, +p, +figure, +blockquote, +dl, +dd { + margin: 0; +} + +/* Remove list styles on ul, ol elements with a list role, which suggests default styling will be removed */ +ul[role='list'], +ol[role='list'] { + list-style: none; +} + +/* Set core root defaults */ +html:focus-within { + scroll-behavior: smooth; +} + +/* Set core body defaults */ +body { + min-height: 100vh; + text-rendering: optimizeSpeed; + line-height: 1.5; +} + +/* Make images easier to work with */ +img, +picture { + max-width: 100%; + display: block; +} + +/* Inherit fonts for inputs and buttons */ +input, +button, +textarea, +select { + font: inherit; +} + +/* Remove all animations, transitions and smooth scroll for people that prefer not to see them */ +@media (prefers-reduced-motion: reduce) { + html:focus-within { + scroll-behavior: auto; + } + + *, + *::before, + *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + scroll-behavior: auto !important; + } +} + +/* Utility classes */ +.text-primary-400 { + color: var(--clr-primary-400); + +} + +body { + margin: auto; +} + + +body > header > .container { + display: flex; + justify-content: center; +} + +.wifi-form > .button { + margin: 0 auto; +} + +body > main > .container > .form-wrapper > .wifi-form > .input-wrapper > .label { + /* font-size: var(--fs-800); */ + font-weight: var(--fw-semi-bold); + color: var(--clr-primary-400); +} + +body > main > .container > .form-wrapper { + display: flex; + justify-content: center; + flex-direction: column; +} + +body > main > .container > .form-wrapper > .wifi-form { + display: flex; + flex-direction: column; + justify-content: center; + gap: 20px; + padding: 40px; + z-index: 2; +} + +body > main > .container > .form-wrapper > .wifi-form > .input-wrapper { + display: flex; + flex-direction: column; +} + +.option-wrapper{ + display:flex; + flex-direction: column; + gap: 10px; +} + +.option-wrapper .option-child { + display: flex; + justify-content: space-between; + align-items: center; +} + +.option-wrapper .option-child > .text { + font-weight: var(--fw-regular); + text-align: center; + display: inline-block; + white-space: nowrap; + overflow: hidden !important; + text-overflow: ellipsis; +} + +.option-wrapper > #div-static-ip { + visibility: hidden; +} + +#static-ip{ + width: 160px; +} + +#save-btn{ + margin-top: 20px; +} + +.button:disabled{ + background-color: rgb(177, 177, 177); + box-shadow: none; +} + +.button { + cursor: pointer; + font-size: var(--fs-800); + background-color: var(--clr-accent-400); + width: min-content; + border:none; + border-radius: 50px; + color: white; + font-weight: var(--fw-medium); + padding: 10px 75px; + text-transform: uppercase; + letter-spacing: 1px; + box-shadow: 2px 2px 10px var(--clr-accent-400-05); +} + +.button:hover:enabled{ + background-color: var(--clr-accent-200); + outline: none; + border: none; + -webkit-tap-highlight-color: transparent; +} + +input { + border: #E6E6E6 1px solid; + background-color: #FBFBFB; + border-radius: 10px; + padding: 5px 20px; + font-weight: var(--fw-regular); + font-size: var(--fs-800); + color: var(--clr-primary-400); + box-shadow: 1px 1px 3px hsla(0, 0%, 0%, 0.2); +} + +input:focus { + outline: var(--clr-accent-400) solid 1px; + border-color: var(--clr-accent-400); +} + +input[type="checkbox"] { + outline: none; +} + +.switch { + position: relative; + display: inline-block; + width: 50px; + height: 24px; +} + +/* Hide default HTML checkbox */ +.switch input { + opacity: 0; + width: 0; + height: 0; +} + +/* The slider */ +.slider { + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: #ccc; + -webkit-transition: .4s; + transition: .4s; +} + +.slider:before { + position: absolute; + content: ""; + height: 16px; + width: 16px; + left: 4px; + bottom: 4px; + background-color: white; + -webkit-transition: .4s; + transition: .4s; +} + +input:checked + .slider { + background-color: var(--clr-accent-400); +} + +input:focus + .slider { + box-shadow: 0 0 1px var(--clr-accent-400); +} + +input:checked + .slider:before { + -webkit-transform: translateX(26px); + -ms-transform: translateX(26px); + transform: translateX(26px); +} + +/* Rounded sliders */ +.slider.round { + border-radius: 34px; +} + +.slider.round:before { + border-radius: 50%; +} + +#bottom-graphic{ + position: fixed; + bottom: 0; + width : 100%; + height: auto; + z-index: 1; + opacity: 0.8; +} + +h1 { + font-size: var(--fs-large-100); + font-weight: var(--fw-semi-bold); + color: var(--clr-primary-400); +} + +#logo { + width : 250px; + height: auto; + +} + +#uid { + text-align: center; + font-weight: var(--fw-medium); + font-size: var(--fs-900); + color: var(--clr-accent-400); +} + +.text-wrapper{ + margin-top: 50px; + display: flex; + flex-direction: column; + align-items: center; +} + +h1, h2, p{ + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + +} diff --git a/data/index.html b/data/index.html new file mode 100644 index 0000000..5eef42e --- /dev/null +++ b/data/index.html @@ -0,0 +1,60 @@ + + + + OnTop - WiFi Manager + + + + + + +
+
+ +
+
+
+
+
+
+

WiFi-Konfiguration

+

+
+
+
+

SSID

+ +
+
+

Passwort

+ +
+
+

Gateway

+ +
+
+

IP-Adresse

+
+
+

Dynamische IP-Adresse

+ +
+
+

Statische IP-Adresse

+ +
+
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/data/js/index.js b/data/js/index.js new file mode 100644 index 0000000..05e00de --- /dev/null +++ b/data/js/index.js @@ -0,0 +1,71 @@ +let sessionValid = {ssid: false, password: false, gateway: false, "static-ip": true}; +const isStaticIPEnabled = document.getElementById("cb-static-ip"); + +function getUID() { + const queryString = window.location.search; + const urlParams = new URLSearchParams(queryString); + + const uid = document.getElementById("uid"); + const urlUID = urlParams.get('uid') + + uid.innerHTML = urlUID || ""; +} + +function changeVisibility(e) { + document.getElementById("div-static-ip").style.visibility = "visible"; + if (e.checked) { + document.getElementById("div-static-ip").style.visibility = "hidden"; + } + + validate(e); +} + +function validate(e){ + let rgx; + const element = document.getElementById(e.id); + const newValue = element.value; + const oldValue = newValue.slice(0, newValue.length - 1); + const lastValue = newValue[newValue.length - 1]; + + switch(e.id){ + case "gateway": + rgx = /^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)(\.(?!$)|$)){4}$/; + break; + case "static-ip": + rgx = /^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)(\.(?!$)|$)){4}$/; + break; + case "ssid": + rgx = /^[^!#;+\]\/"\t][^+\]\/"\t]{0,30}[^ +\]\/"\t]$|^[^ !#;+\]\/"\t]$[ \t]+$/; + break; + case "password": + sessionValid.password = false; + if (newValue.length >= 5){ + sessionValid.password = true; + } + case "cb-static-ip": + sessionValid["static-ip"] = false; + if (isStaticIPEnabled.checked) { + sessionValid["static-ip"] = true; + } + break; + default: + break; + } + + if(rgx) { + sessionValid[e.id] = false; + if(newValue.match(rgx)){ + sessionValid[e.id] = true; + } + } + + updateSession(); +} + +function updateSession(){ + document.getElementById("save-btn").disabled = true; + if (!Object.keys(sessionValid).some(key => sessionValid[key] === false)) { + document.getElementById("save-btn").disabled = false; + } + console.log(sessionValid); +} diff --git a/data/static/svg/graphic.svg b/data/static/svg/graphic.svg new file mode 100644 index 0000000..b80f570 --- /dev/null +++ b/data/static/svg/graphic.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/data/static/svg/logo.svg b/data/static/svg/logo.svg new file mode 100644 index 0000000..6efaac7 --- /dev/null +++ b/data/static/svg/logo.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/include/README b/include/README new file mode 100644 index 0000000..194dcd4 --- /dev/null +++ b/include/README @@ -0,0 +1,39 @@ + +This directory is intended for project header files. + +A header file is a file containing C declarations and macro definitions +to be shared between several project source files. You request the use of a +header file in your project source file (C, C++, etc) located in `src` folder +by including it, with the C preprocessing directive `#include'. + +```src/main.c + +#include "header.h" + +int main (void) +{ + ... +} +``` + +Including a header file produces the same results as copying the header file +into each source file that needs it. Such copying would be time-consuming +and error-prone. With a header file, the related declarations appear +in only one place. If they need to be changed, they can be changed in one +place, and programs that include the header file will automatically use the +new version when next recompiled. The header file eliminates the labor of +finding and changing all the copies as well as the risk that a failure to +find one copy will result in inconsistencies within a program. + +In C, the usual convention is to give header files names that end with `.h'. +It is most portable to use only letters, digits, dashes, and underscores in +header file names, and at most one dot. + +Read more about using header files in official GCC documentation: + +* Include Syntax +* Include Operation +* Once-Only Headers +* Computed Includes + +https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html diff --git a/lib/Display/display.cpp b/lib/Display/display.cpp new file mode 100644 index 0000000..cdde636 --- /dev/null +++ b/lib/Display/display.cpp @@ -0,0 +1,98 @@ +#include "display.h" + +#define LED_BUILTIN GPIO_NUM_25 +#define PIN_35 GPIO_NUM_35 + +#define SCREEN_WIDTH 128 // OLED display width, in pixels +#define SCREEN_HEIGHT 64 // OLED display height, in pixels +#define SCREEN_H_CENTER SCREEN_WIDTH / 2 +#define SCREEN_V_CENTER SCREEN_HEIGHT / 2 + +#define SCREEN_PADDING 7 +#define SCREEN_TOP_PADDING SCREEN_PADDING +#define SCREEN_BOTTOM_PADDING SCREEN_HEIGHT - SCREEN_PADDING +#define SCREEN_TOP_RIGHT_CURSOR_POSITION SCREEN_WIDTH +#define SCREEN_BOTTOM_RIGHT_CURSOR_POSITION SCREEN_WIDTH +#define SCREEN_TOP_LEFT_CURSOR_POSITION 0 +#define SCREEN_BOTTOM_LEFT_CURSOR_POSITION 0 + +#define TEXT_START_LINE 2 +#define TEXT_END_LINE 7 + +#define VIEW_TYPE_MENU 0 +#define VIEW_TYPE_CUSTOM 1 + +#define BTN_SHORT_PRESS 1 +#define BTN_LONG_PRESS 2 + +#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin) +#define SCREEN_ADDRESS 0x3D ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32 + +Adafruit_SSD1306 display(SCREEN_HEIGHT, SCREEN_WIDTH, &Wire, OLED_RESET); +Display *Display::instance = nullptr; + +Display *Display::getInstance() +{ + if (instance == nullptr) + { + instance = new Display(); + } + return instance; +} + +Display::Display() +{ + pinMode(LED_BUILTIN, OUTPUT); + pinMode(PIN_35, INPUT); + + if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) + { // Address 0x3D for 128x64 + Serial.println(F("SSD1306 allocation failed")); + for (;;) + ; + } + + delay(2000); + loop(); +} + +void Display::viewController() +{ + Serial.println(">viewController()"); + int inputType = BTN_SHORT_PRESS; + if (btnPressTime > 800) + { + inputType = BTN_LONG_PRESS; + } + + switch (inputType) + { + case BTN_SHORT_PRESS: + { + break; + } + case BTN_LONG_PRESS: + { + break; + } + } + + reqUpdate = 1; + btnPressTime = 0; +} + +uint8_t Display::checkScreenState() +{ + if (reqUpdate == 0) + { + return -1; + } + + display.stopscroll(); + display.clearDisplay(); + display.setTextSize(0); + display.setTextColor(WHITE); + reqUpdate = 0; + + return 0; +} \ No newline at end of file diff --git a/lib/Display/display.h b/lib/Display/display.h new file mode 100644 index 0000000..be3be24 --- /dev/null +++ b/lib/Display/display.h @@ -0,0 +1,44 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include + +class Display +{ + +public: + static Display *getInstance(); + +protected: + int time = 0; + int menuCursor = 0; + + int btnConfirm = 0; + int isBtnConfirmPressed = 0; + int btnPressStartTime; + int btnPressTime = 0; + +private: + void renderView(); + void viewController(); + void renderHomeScreen(); + void renderMenuScreen(); + Display(); + uint8_t checkScreenState(); + + static Display *instance; + + Adafruit_SSD1306 display; + // Utils *util = Utils::getInstance(); + + uint8_t SCREEN_TEXT_LINE[8] = {0, 8, 16, 24, 32, 40, 48, 56}; + + int16_t text_x_position, text_y_position; + uint16_t text_width, text_height; + int8_t reqUpdate = 1; +}; \ No newline at end of file diff --git a/lib/Input/input_controller.cpp b/lib/Input/input_controller.cpp new file mode 100644 index 0000000..a057092 --- /dev/null +++ b/lib/Input/input_controller.cpp @@ -0,0 +1,47 @@ +#include + +#define LED_BUILTIN GPIO_NUM_25 +#define PIN_35 GPIO_NUM_35 + +InputController *InputController::instance = nullptr; + +InputController::InputController() +{ +} + +InputController *InputController::getInstance() +{ + if (instance == nullptr) + { + instance = new InputController(); + } + + return instance; +} + +int InputController::setup() +{ + btnConfirm = digitalRead(PIN_35); + + if (btnConfirm == HIGH && isBtnConfirmPressed == 0) + { + btnPressStartTime = millis(); + isBtnConfirmPressed = 1; + Serial.println("Button 1: HIGHT"); + digitalWrite(LED_BUILTIN, HIGH); + } + if (btnConfirm == LOW && isBtnConfirmPressed == 1) + { + btnPressTime = millis() - btnPressStartTime; + isBtnConfirmPressed = 0; + Serial.println("Button 1: LOW"); + digitalWrite(LED_BUILTIN, LOW); + } + + if (btnPressTime > 0) + { + // viewController(); + } + + return 0; +} diff --git a/lib/Input/input_controller.h b/lib/Input/input_controller.h new file mode 100644 index 0000000..04d416a --- /dev/null +++ b/lib/Input/input_controller.h @@ -0,0 +1,19 @@ +#pragma once +#include + +class InputController +{ +public: + static InputController *getInstance(); + +protected: + int btnConfirm = 0; + int isBtnConfirmPressed = 0; + int btnPressStartTime; + int btnPressTime = 0; + +private: + static InputController *instance; + InputController(); + int setup(); +}; diff --git a/lib/README b/lib/README new file mode 100644 index 0000000..6debab1 --- /dev/null +++ b/lib/README @@ -0,0 +1,46 @@ + +This directory is intended for project specific (private) libraries. +PlatformIO will compile them to static libraries and link into executable file. + +The source code of each library should be placed in a an own separate directory +("lib/your_library_name/[here are source files]"). + +For example, see a structure of the following two libraries `Foo` and `Bar`: + +|--lib +| | +| |--Bar +| | |--docs +| | |--examples +| | |--src +| | |- Bar.c +| | |- Bar.h +| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html +| | +| |--Foo +| | |- Foo.c +| | |- Foo.h +| | +| |- README --> THIS FILE +| +|- platformio.ini +|--src + |- main.c + +and a contents of `src/main.c`: +``` +#include +#include + +int main (void) +{ + ... +} + +``` + +PlatformIO Library Dependency Finder will find automatically dependent +libraries scanning project source files. + +More information about PlatformIO Library Dependency Finder +- https://docs.platformio.org/page/librarymanager/ldf.html diff --git a/lib/Utils/file_handler.cpp b/lib/Utils/file_handler.cpp new file mode 100644 index 0000000..c4fbdaa --- /dev/null +++ b/lib/Utils/file_handler.cpp @@ -0,0 +1,84 @@ +#include "file_handler.h" + +FileHandler *FileHandler::instance = nullptr; + +FileHandler *FileHandler::getInstance() +{ + if (instance == nullptr) + { + instance = new FileHandler(); + } + return instance; +} + +FileHandler::FileHandler() +{ + initSPIFFS(); +} + +int FileHandler::read(char *filePath, byte *buffer, size_t size) +{ + if (exists(filePath)) + { + File file = SPIFFS.open(filePath, FILE_READ); + Serial.println(file.size()); + if (file) + { + file.read(buffer, size); + file.close(); + } + else + { + Serial.println("[FileHandler::read - Failed to open file."); + return 1; + } + return 0; + } + return 1; +} + +int FileHandler::remove(char *filePath) +{ + if (exists(filePath)) + { + SPIFFS.remove(filePath); + return 0; + } + return 1; +} + +int FileHandler::exists(char *filePath) +{ + return SPIFFS.exists(filePath); +} + +int FileHandler::write(char *filePath, byte *data, size_t size) +{ + File file = SPIFFS.open(filePath, FILE_WRITE); + + /* for (int i = 0; i < size; i++) + { + Serial.print(data[i], HEX); + } */ + + if (file) + { + file.write(data, size); + file.close(); + return 0; + } + else + { + Serial.println("FileHandler::write - Failed to open file."); + return 1; + } +} + +void FileHandler::initSPIFFS() +{ + if (!SPIFFS.begin(true)) + { + Serial.println("An error has occurred while mounting SPIFFS"); + } + Serial.println("SPIFFS mounted successfully"); +} \ No newline at end of file diff --git a/lib/Utils/file_handler.h b/lib/Utils/file_handler.h new file mode 100644 index 0000000..f0d8596 --- /dev/null +++ b/lib/Utils/file_handler.h @@ -0,0 +1,20 @@ +#pragma once +#include "SPIFFS.h" +#include + +class FileHandler +{ +public: + static FileHandler *getInstance(); + int read(char *filePath, byte *buffer, size_t size); + int write(char *filePath, byte *data, size_t size); + int exists(char *filePath); + int remove(char *filePath); + +private: + static FileHandler *instance; + FileHandler(); + void initSPIFFS(); + +protected: +}; \ No newline at end of file diff --git a/lib/View/Controller/view_controller.cpp b/lib/View/Controller/view_controller.cpp new file mode 100644 index 0000000..4d134ed --- /dev/null +++ b/lib/View/Controller/view_controller.cpp @@ -0,0 +1,16 @@ +#include "../Model/view_controller.h" + +ViewController *ViewController::instance = nullptr; + +ViewController *ViewController::getInstance() +{ + if (instance == 0) + { + instance = new ViewController(); + } + return instance; +} + +ViewController::ViewController() +{ +} \ No newline at end of file diff --git a/lib/View/Model/view.h b/lib/View/Model/view.h new file mode 100644 index 0000000..b55935f --- /dev/null +++ b/lib/View/Model/view.h @@ -0,0 +1,8 @@ +class View +{ +public: + virtual void render() = 0; + +protected: +private: +}; \ No newline at end of file diff --git a/lib/View/Model/view_controller.h b/lib/View/Model/view_controller.h new file mode 100644 index 0000000..c41f4ce --- /dev/null +++ b/lib/View/Model/view_controller.h @@ -0,0 +1,13 @@ +#pragma once +#include "../Display/display.h" + +class ViewController +{ +public: + static ViewController *getInstance(); + +protected: +private: + static ViewController *instance; + ViewController(); +}; diff --git a/lib/View/View/home_view.cpp b/lib/View/View/home_view.cpp new file mode 100644 index 0000000..4dd8c8e --- /dev/null +++ b/lib/View/View/home_view.cpp @@ -0,0 +1,25 @@ +#include "home_view.h" + +Display *display = Display::getInstance(); + +void HomeView::render() +{ + /* + Serial.println(">renderHomeScreen()"); + + // Draw Header + display.getTextBounds("00:00", 0, SCREEN_TEXT_LINE[0], &text_x_position, &text_y_position, &text_width, &text_height); + display.setCursor(0, SCREEN_TEXT_LINE[0]); + display.println("00:00"); + + display.drawRect(SCREEN_WIDTH - 16, SCREEN_TEXT_LINE[0], 16, 8, WHITE); + display.drawFastHLine(0, SCREEN_TEXT_LINE[1] + 2, SCREEN_WIDTH, WHITE); + + // Draw Footer + display.getTextBounds("v0.1-2a43c", SCREEN_WIDTH, SCREEN_TEXT_LINE[7], &text_x_position, &text_y_position, &text_width, &text_height); + display.setCursor(SCREEN_WIDTH - text_width, SCREEN_TEXT_LINE[7]); + display.println("v0.1-2a43c"); + + display.display(); + */ +} \ No newline at end of file diff --git a/lib/View/View/home_view.h b/lib/View/View/home_view.h new file mode 100644 index 0000000..ff943c2 --- /dev/null +++ b/lib/View/View/home_view.h @@ -0,0 +1,14 @@ +#pragma once +#include "../Model/view.h" +#include +#include + +class HomeView : public View +{ +public: + void render(); + +private: +protected: + Display *display; +}; \ No newline at end of file diff --git a/lib/WiFi_Manager/wifi_manager.cpp b/lib/WiFi_Manager/wifi_manager.cpp new file mode 100644 index 0000000..fbb52e9 --- /dev/null +++ b/lib/WiFi_Manager/wifi_manager.cpp @@ -0,0 +1,207 @@ +#include "wifi_manager.h" + +AsyncWebServer server(80); + +IPAddress localIP; +IPAddress localGateway; +IPAddress subnet(255, 255, 0, 0); + +unsigned long previousMillis = 0; +const long interval = TIMEOUT_MS; + +const char *WEB_INPUT_SSID = "ssid"; +const char *WEB_INPUT_PASSWORD = "password"; +const char *WEB_INPUT_GATEWAY = "gateway"; +const char *WEB_INPUT_STATIC_IP = "static-ip"; +const char *WEB_CHECKBOX_STATIC_IP = "cb-static-ip"; + +char const *CONFIG_PATH = NETWORK_CONFIG; +WifiManager *WifiManager::instance = nullptr; + +WifiManager *WifiManager::getInstance() +{ + if (instance == nullptr) + { + instance = new WifiManager(); + } + return instance; +} + +WifiManager::WifiManager() +{ + this->fh = FileHandler::getInstance(); + connectionStatus = 0; + + setup(); +} + +int WifiManager::initWifi() +{ + if (networkConfig.ssid == "" || networkConfig.staticAddress == "") + { + Serial.println("Undefined SSID or IP address."); + return 1; + } + + WiFi.mode(WIFI_STA); + // localIP.fromString(networkConfig.); + localGateway.fromString(networkConfig.gateway); + + if (!WiFi.config(localIP, localGateway, subnet)) + { + Serial.println("STA Failed to configure"); + return 1; + } + + WiFi.begin("231313", "123"); + Serial.println("Connecting to WiFi..."); + + unsigned long currentMillis = millis(); + previousMillis = currentMillis; + + while (WiFi.status() != WL_CONNECTED) + { + currentMillis = millis(); + if (currentMillis - previousMillis >= TIMEOUT_MS) + { + Serial.println("Failed to connect."); + return 1; + } + } + + Serial.println(WiFi.localIP()); + return 0; +} + +void WifiManager::initSPIFFS() +{ + if (!SPIFFS.begin(true)) + { + Serial.println("An error has occurred while mounting SPIFFS"); + } + Serial.println("SPIFFS mounted successfully"); +} + +void WifiManager::httpListener() +{ + server.onNotFound([](AsyncWebServerRequest *request) + { request->send(404); }); + server.on("/test", HTTP_GET, [](AsyncWebServerRequest *request) + { request->send(SPIFFS, "/index.html", "text/html"); }); + + server.serveStatic("/", SPIFFS, "/"); + Serial.println(SPIFFS.exists("/index.html")); + + server.on("/", HTTP_POST, [](AsyncWebServerRequest *request) + { + int params = request->params(); + Serial.println(params); + for(int i=0;igetParam(i); + if(p->isPost()){ + // HTTP POST ssid value + if (p->name() == WEB_INPUT_SSID) { + Serial.print("SSID set to: "); + Serial.println(p->value().c_str()); + // Write file to save value + //writeFile(SPIFFS, ssidPath, ssid.c_str()); + } + // HTTP POST pass value + if (p->name() == WEB_INPUT_PASSWORD) { + Serial.print("Password set to: "); + Serial.println(p->value().c_str()); + // Write file to save value + //writeFile(SPIFFS, passPath, pass.c_str()); + } + // HTTP POST ip value + if (p->name() == WEB_INPUT_STATIC_IP) { + Serial.print("IP Address set to: "); + Serial.println(p->value().c_str()); + // Write file to save value + //writeFile(SPIFFS, ipPath, ip.c_str()); + } + if (p->name() == WEB_CHECKBOX_STATIC_IP) { + Serial.print("CBset to: "); + Serial.println(p->value().c_str()); + // Write file to save value + //writeFile(SPIFFS, ipPath, ip.c_str()); + } + // HTTP POST gateway value + if (p->name() == WEB_INPUT_GATEWAY) { + Serial.print("Gateway set to: "); + Serial.println(p->value().c_str()); + // Write file to save value + //writeFile(SPIFFS, gatewayPath, gateway.c_str()); + } + //Serial.printf("POST[%s]: %s\n", p->name().c_str(), p->value().c_str()); + } + } + request->send(200, "text/plain", "Done. ESP will restart, connect to your router and go to IP address: "); + delay(3000); + ESP.restart(); }); +} + +void WifiManager::setup() +{ + initSPIFFS(); + + IPAddress localIP; + IPAddress localGateway; + IPAddress subnet(255, 255, 0, 0); + + // Load values saved in SPIFFS + + if (true) //! initWifi() + { + // Connect to Wi-Fi network with SSID and password + Serial.println("Setting AP (Access Point)"); + + WiFi.softAP("ESP-WIFI-MANAGER", "12345"); + IPAddress IP = WiFi.softAPIP(); + Serial.print("AP IP address: "); + Serial.println(IP); + + // Web Server Root URL + + server.on("/hello", HTTP_GET, [](AsyncWebServerRequest *request) + { request->send(200, "text/plain", "Hello World"); }); + + httpListener(); + server.begin(); + } +} + +int WifiManager::writeNetworkConfig() +{ + return true; +} + +int WifiManager::removeNetworkConfig() +{ + return fh->remove((char *)CONFIG_PATH); +} + +int WifiManager::checkNetworkConfig() +{ + strcpy(networkConfig.ssid, "PROGRAM 1"); + strcpy(networkConfig.gateway, "123.123.123.123"); + strcpy(networkConfig.password, "Test123"); + strcpy(networkConfig.staticAddress, "124.124.124.124"); + networkConfig.isDynamicAddress = 0; + // Serial.println(fh->write(CONFIG_PATH, (byte *)&config, sizeof(config))); + + /* NetworkConfig readConfig = {}; + fh->read(CONFIG_PATH, (byte *)&readConfig, sizeof(readConfig)); + Serial.println(readConfig.ssid); + Serial.println(readConfig.gateway); + Serial.println(readConfig.password); + Serial.println(readConfig.staticAddress); + Serial.println(readConfig.isDynamicAddress); + Serial.println(fh->exists(CONFIG_PATH)); */ + Serial.println(fh->exists((char *)CONFIG_PATH)); + Serial.println(fh->remove((char *)CONFIG_PATH)); + Serial.println(fh->exists((char *)CONFIG_PATH)); + // Serial.println(fh->remove(CONFIG_PATH)); + return 0; +} \ No newline at end of file diff --git a/lib/WiFi_Manager/wifi_manager.h b/lib/WiFi_Manager/wifi_manager.h new file mode 100644 index 0000000..1a59693 --- /dev/null +++ b/lib/WiFi_Manager/wifi_manager.h @@ -0,0 +1,44 @@ +#pragma once +#include +#include +#include +#include +#include +#include "SPIFFS.h" +#include "../Utils/file_handler.h" + +#define NETWORK_CONFIG "/config/network.txt" +#define TIMEOUT_MS 10000 + +struct NetworkConfig +{ + char ssid[33]; + char password[33]; + char gateway[16]; + bool isDynamicAddress; + char staticAddress[16]; +}; + +class WifiManager +{ +public: + static WifiManager *getInstance(); + +private: + static WifiManager *instance; + int connectionStatus; + FileHandler *fh; + NetworkConfig networkConfig; + + WifiManager(); + void setup(); + unsigned long previousMillis; + void initSPIFFS(); + int initWifi(); + void httpListener(); + int checkNetworkConfig(); + int removeNetworkConfig(); + int writeNetworkConfig(); + +protected: +}; \ No newline at end of file diff --git a/platformio.ini b/platformio.ini new file mode 100644 index 0000000..55825fb --- /dev/null +++ b/platformio.ini @@ -0,0 +1,23 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + +[env:esp32doit-devkit-v1] +platform = espressif32 +board = esp32dev +framework = arduino +monitor_speed = 115200 +lib_ldf_mode=deep +lib_deps = + Wire + SPI + adafruit/Adafruit NeoPixel@^1.10.5 + adafruit/Adafruit SSD1306@^2.5.6 + +monitor_filters = esp32_exception_decoder diff --git a/project.json b/project.json new file mode 100644 index 0000000..0d49325 --- /dev/null +++ b/project.json @@ -0,0 +1,27 @@ +{ + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "apps/datacollector/src", + "projectType": "application", + "targets": { + "build": { + "executor": "nx:run-commands", + "options": { + "command": "" + } + }, + "serve": { + "executor": "nx:run-commands", + "options": { + "command": "pio run -t exec" + } + }, + "version": { + "executor": "@jscutlery/semver:version", + "options": { + "preset": "conventional", + "commitMessageFormat": "chore(${projectName}): release version ${version}" + } + } + }, + "tags": [] +} diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..0a56fb6 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,96 @@ +#include +#include +#include +#include +#include + +#define BUTTON_IN_1 GPIO_NUM_27 +#define RGB_PIN GPIO_NUM_15 +#define LED_COUNT 1 + +Adafruit_NeoPixel pixels(1, RGB_PIN, NEO_GRB + NEO_KHZ800); +Adafruit_SSD1306 display(128, 64, &Wire, -1); + +bool i2c_scanner() { + uint8_t error, address; + int nDevices; + + Serial.println("Scanning sensors on I2C Bus..."); + + for (address = 1; address < 127; 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.println(address, HEX); + } else if (error == 4) { + Serial.print("Unknown error at address 0x"); + if (address < 16) + Serial.print("0"); + Serial.println(address, HEX); + } + } + + return true; +} + +void setup() { + Serial.begin(115200); + Serial.println("Main.setup"); + + Wire.begin(); + + pixels.begin(); + pixels.clear(); + pixels.show(); + pixels.setBrightness(10); + + i2c_scanner(); + + if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { + Serial.println(F("SSD1306 allocation failed")); + for (;;) + ; // Don't proceed, loop forever + } + + display.clearDisplay(); + + display.setTextSize(2); + display.setTextColor(SSD1306_WHITE); + display.setCursor(10, 0); + display.println(F("Hallo Welt")); + display.display(); +} + +uint16_t hsv = 0; +uint32_t hsvUpdate = 0; +uint32_t btnPressStartTime = 0; +uint8_t isBtnPressed = 0; + +void loop() { + uint32_t now = millis(); + + int btnIn1 = digitalRead(BUTTON_IN_1); + + if (btnIn1 == HIGH && isBtnPressed == 0) { + btnPressStartTime = now; + isBtnPressed = 1; + Serial.println("BTN pressed"); + } + if (btnIn1 == LOW && isBtnPressed == 1) { + isBtnPressed = 0; + } + + if (now - hsvUpdate >= 100) { + hsv += 100; + if (hsv >= 65535) hsv = 0; + + pixels.setPixelColor(0, pixels.ColorHSV(hsv)); + pixels.show(); + + hsvUpdate = now; + } +} \ No newline at end of file diff --git a/test/README b/test/README new file mode 100644 index 0000000..9b1e87b --- /dev/null +++ b/test/README @@ -0,0 +1,11 @@ + +This directory is intended for PlatformIO Test Runner and project tests. + +Unit Testing is a software testing method by which individual units of +source code, sets of one or more MCU program modules together with associated +control data, usage procedures, and operating procedures, are tested to +determine whether they are fit for use. Unit testing finds problems early +in the development cycle. + +More information about PlatformIO Unit Testing: +- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html