From e554d52668bb1eddb4df0900536301c99541dbaa Mon Sep 17 00:00:00 2001 From: Owen Yang Date: Fri, 15 Oct 2021 21:01:24 -0700 Subject: [PATCH] add platformio example --- waste_watcher_code_platformio_demo/.gitignore | 5 + .../.vscode/extensions.json | 7 + .../include/README | 39 ++ .../lib/NTPClient/.travis.yml | 10 + .../lib/NTPClient/CHANGELOG | 15 + .../lib/NTPClient/LICENSE | 9 + .../lib/NTPClient/NTPClient.cpp | 234 ++++++++++++ .../lib/NTPClient/NTPClient.h | 105 +++++ .../lib/NTPClient/README.md | 47 +++ .../NTPClient/examples/Advanced/Advanced.ino | 37 ++ .../lib/NTPClient/examples/Basic/Basic.ino | 33 ++ .../lib/NTPClient/keywords.txt | 20 + .../lib/NTPClient/library.json | 24 ++ .../lib/NTPClient/library.properties | 9 + waste_watcher_code_platformio_demo/lib/README | 46 +++ .../platformio.ini | 15 + .../src/main.cpp | 360 ++++++++++++++++++ .../test/README | 11 + 18 files changed, 1026 insertions(+) create mode 100644 waste_watcher_code_platformio_demo/.gitignore create mode 100644 waste_watcher_code_platformio_demo/.vscode/extensions.json create mode 100644 waste_watcher_code_platformio_demo/include/README create mode 100644 waste_watcher_code_platformio_demo/lib/NTPClient/.travis.yml create mode 100644 waste_watcher_code_platformio_demo/lib/NTPClient/CHANGELOG create mode 100644 waste_watcher_code_platformio_demo/lib/NTPClient/LICENSE create mode 100644 waste_watcher_code_platformio_demo/lib/NTPClient/NTPClient.cpp create mode 100644 waste_watcher_code_platformio_demo/lib/NTPClient/NTPClient.h create mode 100644 waste_watcher_code_platformio_demo/lib/NTPClient/README.md create mode 100644 waste_watcher_code_platformio_demo/lib/NTPClient/examples/Advanced/Advanced.ino create mode 100644 waste_watcher_code_platformio_demo/lib/NTPClient/examples/Basic/Basic.ino create mode 100644 waste_watcher_code_platformio_demo/lib/NTPClient/keywords.txt create mode 100644 waste_watcher_code_platformio_demo/lib/NTPClient/library.json create mode 100644 waste_watcher_code_platformio_demo/lib/NTPClient/library.properties create mode 100644 waste_watcher_code_platformio_demo/lib/README create mode 100644 waste_watcher_code_platformio_demo/platformio.ini create mode 100644 waste_watcher_code_platformio_demo/src/main.cpp create mode 100644 waste_watcher_code_platformio_demo/test/README diff --git a/waste_watcher_code_platformio_demo/.gitignore b/waste_watcher_code_platformio_demo/.gitignore new file mode 100644 index 0000000..89cc49c --- /dev/null +++ b/waste_watcher_code_platformio_demo/.gitignore @@ -0,0 +1,5 @@ +.pio +.vscode/.browse.c_cpp.db* +.vscode/c_cpp_properties.json +.vscode/launch.json +.vscode/ipch diff --git a/waste_watcher_code_platformio_demo/.vscode/extensions.json b/waste_watcher_code_platformio_demo/.vscode/extensions.json new file mode 100644 index 0000000..e80666b --- /dev/null +++ b/waste_watcher_code_platformio_demo/.vscode/extensions.json @@ -0,0 +1,7 @@ +{ + // See http://go.microsoft.com/fwlink/?LinkId=827846 + // for the documentation about the extensions.json format + "recommendations": [ + "platformio.platformio-ide" + ] +} diff --git a/waste_watcher_code_platformio_demo/include/README b/waste_watcher_code_platformio_demo/include/README new file mode 100644 index 0000000..194dcd4 --- /dev/null +++ b/waste_watcher_code_platformio_demo/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/waste_watcher_code_platformio_demo/lib/NTPClient/.travis.yml b/waste_watcher_code_platformio_demo/lib/NTPClient/.travis.yml new file mode 100644 index 0000000..fa09d0f --- /dev/null +++ b/waste_watcher_code_platformio_demo/lib/NTPClient/.travis.yml @@ -0,0 +1,10 @@ +language: c +sudo: false +before_install: + - source <(curl -SLs https://raw.githubusercontent.com/adafruit/travis-ci-arduino/master/install.sh) +script: + - build_platform esp8266 +notifications: + email: + on_success: change + on_failure: change diff --git a/waste_watcher_code_platformio_demo/lib/NTPClient/CHANGELOG b/waste_watcher_code_platformio_demo/lib/NTPClient/CHANGELOG new file mode 100644 index 0000000..6a082d5 --- /dev/null +++ b/waste_watcher_code_platformio_demo/lib/NTPClient/CHANGELOG @@ -0,0 +1,15 @@ +NTPClient 3.1.0 - 2016.05.31 + +* Added functions for changing the timeOffset and updateInterval later. Thanks @SirUli + +NTPClient 3.0.0 - 2016.04.19 + +* Constructors now require UDP instance argument, to add support for non-ESP8266 boards +* Added optional begin API to override default local port +* Added end API to close UDP socket +* Changed return type of update and forceUpdate APIs to bool, and return success or failure +* Change return type of getDay, getHours, getMinutes, and getSeconds to int + +Older + +* Changes not recorded diff --git a/waste_watcher_code_platformio_demo/lib/NTPClient/LICENSE b/waste_watcher_code_platformio_demo/lib/NTPClient/LICENSE new file mode 100644 index 0000000..3e11fd9 --- /dev/null +++ b/waste_watcher_code_platformio_demo/lib/NTPClient/LICENSE @@ -0,0 +1,9 @@ +MIT License + +Copyright © taranais (https://github.com/taranais) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/waste_watcher_code_platformio_demo/lib/NTPClient/NTPClient.cpp b/waste_watcher_code_platformio_demo/lib/NTPClient/NTPClient.cpp new file mode 100644 index 0000000..6f6a4cd --- /dev/null +++ b/waste_watcher_code_platformio_demo/lib/NTPClient/NTPClient.cpp @@ -0,0 +1,234 @@ +/** + * The MIT License (MIT) + * Copyright (c) 2015 by Fabrice Weinberg + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "NTPClient.h" + +NTPClient::NTPClient(UDP& udp) { + this->_udp = &udp; +} + +NTPClient::NTPClient(UDP& udp, int timeOffset) { + this->_udp = &udp; + this->_timeOffset = timeOffset; +} + +NTPClient::NTPClient(UDP& udp, const char* poolServerName) { + this->_udp = &udp; + this->_poolServerName = poolServerName; +} + +NTPClient::NTPClient(UDP& udp, const char* poolServerName, int timeOffset) { + this->_udp = &udp; + this->_timeOffset = timeOffset; + this->_poolServerName = poolServerName; +} + +NTPClient::NTPClient(UDP& udp, const char* poolServerName, int timeOffset, unsigned long updateInterval) { + this->_udp = &udp; + this->_timeOffset = timeOffset; + this->_poolServerName = poolServerName; + this->_updateInterval = updateInterval; +} + +void NTPClient::begin() { + this->begin(NTP_DEFAULT_LOCAL_PORT); +} + +void NTPClient::begin(int port) { + this->_port = port; + + this->_udp->begin(this->_port); + + this->_udpSetup = true; +} + +bool NTPClient::isValid(byte * ntpPacket) +{ + //Perform a few validity checks on the packet + if((ntpPacket[0] & 0b11000000) == 0b11000000) //Check for LI=UNSYNC + return false; + + if((ntpPacket[0] & 0b00111000) >> 3 < 0b100) //Check for Version >= 4 + return false; + + if((ntpPacket[0] & 0b00000111) != 0b100) //Check for Mode == Server + return false; + + if((ntpPacket[1] < 1) || (ntpPacket[1] > 15)) //Check for valid Stratum + return false; + + if( ntpPacket[16] == 0 && ntpPacket[17] == 0 && + ntpPacket[18] == 0 && ntpPacket[19] == 0 && + ntpPacket[20] == 0 && ntpPacket[21] == 0 && + ntpPacket[22] == 0 && ntpPacket[22] == 0) //Check for ReferenceTimestamp != 0 + return false; + + return true; +} + +bool NTPClient::forceUpdate() { + #ifdef DEBUG_NTPClient + Serial.println("Update from NTP Server"); + #endif + // flush any existing packets + while(this->_udp->parsePacket() != 0) + this->_udp->flush(); + this->sendNTPPacket(); + + // Wait till data is there or timeout... + byte timeout = 0; + int cb = 0; + do { + delay ( 10 ); + cb = this->_udp->parsePacket(); + + if(cb > 0) + { + this->_udp->read(this->_packetBuffer, NTP_PACKET_SIZE); + if(!this->isValid(this->_packetBuffer)) + cb = 0; + } + + if (timeout > 100) return false; // timeout after 1000 ms + timeout++; + } while (cb == 0); + + this->_lastUpdate = millis() - (10 * (timeout + 1)); // Account for delay in reading the time + + unsigned long highWord = word(this->_packetBuffer[40], this->_packetBuffer[41]); + unsigned long lowWord = word(this->_packetBuffer[42], this->_packetBuffer[43]); + // combine the four bytes (two words) into a long integer + // this is NTP time (seconds since Jan 1 1900): + unsigned long secsSince1900 = highWord << 16 | lowWord; + + this->_currentEpoc = secsSince1900 - SEVENZYYEARS; + + return true; +} + +bool NTPClient::update() { + if ((millis() - this->_lastUpdate >= this->_updateInterval) // Update after _updateInterval + || this->_lastUpdate == 0) { // Update if there was no update yet. + if (!this->_udpSetup) this->begin(); // setup the UDP client if needed + return this->forceUpdate(); + } + return true; +} + +unsigned long NTPClient::getEpochTime() { + return this->_timeOffset + // User offset + this->_currentEpoc + // Epoc returned by the NTP server + ((millis() - this->_lastUpdate) / 1000); // Time since last update +} + +int NTPClient::getDay() { + return (((this->getEpochTime() / 86400L) + 4 ) % 7); //0 is Sunday +} +int NTPClient::getHours() { + return ((this->getEpochTime() % 86400L) / 3600); +} +int NTPClient::getMinutes() { + return ((this->getEpochTime() % 3600) / 60); +} +int NTPClient::getSeconds() { + return (this->getEpochTime() % 60); +} + +String NTPClient::getFormattedTime(unsigned long secs) { + unsigned long rawTime = secs ? secs : this->getEpochTime(); + unsigned long hours = (rawTime % 86400L) / 3600; + String hoursStr = hours < 10 ? "0" + String(hours) : String(hours); + + unsigned long minutes = (rawTime % 3600) / 60; + String minuteStr = minutes < 10 ? "0" + String(minutes) : String(minutes); + + unsigned long seconds = rawTime % 60; + String secondStr = seconds < 10 ? "0" + String(seconds) : String(seconds); + + return hoursStr + ":" + minuteStr + ":" + secondStr; +} + +// Based on https://github.com/PaulStoffregen/Time/blob/master/Time.cpp +// currently assumes UTC timezone, instead of using this->_timeOffset +String NTPClient::getFormattedDate(unsigned long secs) { + unsigned long rawTime = (secs ? secs : this->getEpochTime()) / 86400L; // in days + unsigned long days = 0, year = 1970; + uint8_t month; + static const uint8_t monthDays[]={31,28,31,30,31,30,31,31,30,31,30,31}; + + while((days += (LEAP_YEAR(year) ? 366 : 365)) <= rawTime) + year++; + rawTime -= days - (LEAP_YEAR(year) ? 366 : 365); // now it is days in this year, starting at 0 + days=0; + for (month=0; month<12; month++) { + uint8_t monthLength; + if (month==1) { // february + monthLength = LEAP_YEAR(year) ? 29 : 28; + } else { + monthLength = monthDays[month]; + } + if (rawTime < monthLength) break; + rawTime -= monthLength; + } + String monthStr = ++month < 10 ? "0" + String(month) : String(month); // jan is month 1 + String dayStr = ++rawTime < 10 ? "0" + String(rawTime) : String(rawTime); // day of month + return String(year) + "-" + monthStr + "-" + dayStr + "T" + this->getFormattedTime(secs ? secs : 0) + "Z"; +} + +void NTPClient::end() { + this->_udp->stop(); + + this->_udpSetup = false; +} + +void NTPClient::setTimeOffset(int timeOffset) { + this->_timeOffset = timeOffset; +} + +void NTPClient::setUpdateInterval(unsigned long updateInterval) { + this->_updateInterval = updateInterval; +} + +void NTPClient::sendNTPPacket() { + // set all bytes in the buffer to 0 + memset(this->_packetBuffer, 0, NTP_PACKET_SIZE); + // Initialize values needed to form NTP request + // (see URL above for details on the packets) + this->_packetBuffer[0] = 0b11100011; // LI, Version, Mode + this->_packetBuffer[1] = 0; // Stratum, or type of clock + this->_packetBuffer[2] = 6; // Polling Interval + this->_packetBuffer[3] = 0xEC; // Peer Clock Precision + // 8 bytes of zero for Root Delay & Root Dispersion + this->_packetBuffer[12] = 0x49; + this->_packetBuffer[13] = 0x4E; + this->_packetBuffer[14] = 0x49; + this->_packetBuffer[15] = 0x52; + + // all NTP fields have been given values, now + // you can send a packet requesting a timestamp: + this->_udp->beginPacket(this->_poolServerName, 123); //NTP requests are to port 123 + this->_udp->write(this->_packetBuffer, NTP_PACKET_SIZE); + this->_udp->endPacket(); +} + +void NTPClient::setEpochTime(unsigned long secs) { + this->_currentEpoc = secs; +} diff --git a/waste_watcher_code_platformio_demo/lib/NTPClient/NTPClient.h b/waste_watcher_code_platformio_demo/lib/NTPClient/NTPClient.h new file mode 100644 index 0000000..ad45070 --- /dev/null +++ b/waste_watcher_code_platformio_demo/lib/NTPClient/NTPClient.h @@ -0,0 +1,105 @@ +#pragma once + +#include "Arduino.h" + +#include + +#define SEVENZYYEARS 2208988800UL +#define NTP_PACKET_SIZE 48 +#define NTP_DEFAULT_LOCAL_PORT 1337 +#define LEAP_YEAR(Y) ( (Y>0) && !(Y%4) && ( (Y%100) || !(Y%400) ) ) + + +class NTPClient { + private: + UDP* _udp; + bool _udpSetup = false; + + const char* _poolServerName = "pool.ntp.org"; // Default time server + int _port = NTP_DEFAULT_LOCAL_PORT; + int _timeOffset = 0; + + unsigned long _updateInterval = 60000; // In ms + + unsigned long _currentEpoc = 0; // In s + unsigned long _lastUpdate = 0; // In ms + + byte _packetBuffer[NTP_PACKET_SIZE]; + + void sendNTPPacket(); + bool isValid(byte * ntpPacket); + + public: + NTPClient(UDP& udp); + NTPClient(UDP& udp, int timeOffset); + NTPClient(UDP& udp, const char* poolServerName); + NTPClient(UDP& udp, const char* poolServerName, int timeOffset); + NTPClient(UDP& udp, const char* poolServerName, int timeOffset, unsigned long updateInterval); + + /** + * Starts the underlying UDP client with the default local port + */ + void begin(); + + /** + * Starts the underlying UDP client with the specified local port + */ + void begin(int port); + + /** + * This should be called in the main loop of your application. By default an update from the NTP Server is only + * made every 60 seconds. This can be configured in the NTPClient constructor. + * + * @return true on success, false on failure + */ + bool update(); + + /** + * This will force the update from the NTP Server. + * + * @return true on success, false on failure + */ + bool forceUpdate(); + + int getDay(); + int getHours(); + int getMinutes(); + int getSeconds(); + + /** + * Changes the time offset. Useful for changing timezones dynamically + */ + void setTimeOffset(int timeOffset); + + /** + * Set the update interval to another frequency. E.g. useful when the + * timeOffset should not be set in the constructor + */ + void setUpdateInterval(unsigned long updateInterval); + + /** + * @return secs argument (or 0 for current time) formatted like `hh:mm:ss` + */ + String getFormattedTime(unsigned long secs = 0); + + /** + * @return time in seconds since Jan. 1, 1970 + */ + unsigned long getEpochTime(); + + /** + * @return secs argument (or 0 for current date) formatted to ISO 8601 + * like `2004-02-12T15:19:21+00:00` + */ + String getFormattedDate(unsigned long secs = 0); + + /** + * Stops the underlying UDP client + */ + void end(); + + /** + * Replace the NTP-fetched time with seconds since Jan. 1, 1970 + */ + void setEpochTime(unsigned long secs); +}; diff --git a/waste_watcher_code_platformio_demo/lib/NTPClient/README.md b/waste_watcher_code_platformio_demo/lib/NTPClient/README.md new file mode 100644 index 0000000..6c8c07a --- /dev/null +++ b/waste_watcher_code_platformio_demo/lib/NTPClient/README.md @@ -0,0 +1,47 @@ +# NTPClient + +[![Build Status](https://travis-ci.org/arduino-libraries/NTPClient.svg?branch=master)](https://travis-ci.org/arduino-libraries/NTPClient) + +Connect to a NTP server, here is how: + +```cpp +#include +// change next line to use with another board/shield +#include +//#include // for WiFi shield +//#include // for WiFi 101 shield or MKR1000 +#include + +const char *ssid = ""; +const char *password = ""; + +WiFiUDP ntpUDP; + +// By default 'pool.ntp.org' is used with 60 seconds update interval and +// no offset +NTPClient timeClient(ntpUDP); + +// You can specify the time server pool and the offset, (in seconds) +// additionaly you can specify the update interval (in milliseconds). +// NTPClient timeClient(ntpUDP, "europe.pool.ntp.org", 3600, 60000); + +void setup(){ + Serial.begin(115200); + WiFi.begin(ssid, password); + + while ( WiFi.status() != WL_CONNECTED ) { + delay ( 500 ); + Serial.print ( "." ); + } + + timeClient.begin(); +} + +void loop() { + timeClient.update(); + + Serial.println(timeClient.getFormattedTime()); + + delay(1000); +} +``` diff --git a/waste_watcher_code_platformio_demo/lib/NTPClient/examples/Advanced/Advanced.ino b/waste_watcher_code_platformio_demo/lib/NTPClient/examples/Advanced/Advanced.ino new file mode 100644 index 0000000..2559508 --- /dev/null +++ b/waste_watcher_code_platformio_demo/lib/NTPClient/examples/Advanced/Advanced.ino @@ -0,0 +1,37 @@ +#include +// change next line to use with another board/shield +#include +//#include // for WiFi shield +//#include // for WiFi 101 shield or MKR1000 +#include + +const char *ssid = ""; +const char *password = ""; + +WiFiUDP ntpUDP; + +// You can specify the time server pool and the offset (in seconds, can be +// changed later with setTimeOffset() ). Additionaly you can specify the +// update interval (in milliseconds, can be changed using setUpdateInterval() ). +NTPClient timeClient(ntpUDP, "europe.pool.ntp.org", 3600, 60000); + +void setup(){ + Serial.begin(115200); + + WiFi.begin(ssid, password); + + while ( WiFi.status() != WL_CONNECTED ) { + delay ( 500 ); + Serial.print ( "." ); + } + + timeClient.begin(); +} + +void loop() { + timeClient.update(); + + Serial.println(timeClient.getFormattedTime()); + + delay(1000); +} diff --git a/waste_watcher_code_platformio_demo/lib/NTPClient/examples/Basic/Basic.ino b/waste_watcher_code_platformio_demo/lib/NTPClient/examples/Basic/Basic.ino new file mode 100644 index 0000000..f0a2a7c --- /dev/null +++ b/waste_watcher_code_platformio_demo/lib/NTPClient/examples/Basic/Basic.ino @@ -0,0 +1,33 @@ +#include +// change next line to use with another board/shield +#include +//#include // for WiFi shield +//#include // for WiFi 101 shield or MKR1000 +#include + +const char *ssid = ""; +const char *password = ""; + +WiFiUDP ntpUDP; +NTPClient timeClient(ntpUDP); + +void setup(){ + Serial.begin(115200); + + WiFi.begin(ssid, password); + + while ( WiFi.status() != WL_CONNECTED ) { + delay ( 500 ); + Serial.print ( "." ); + } + + timeClient.begin(); +} + +void loop() { + timeClient.update(); + + Serial.println(timeClient.getFormattedTime()); + + delay(1000); +} diff --git a/waste_watcher_code_platformio_demo/lib/NTPClient/keywords.txt b/waste_watcher_code_platformio_demo/lib/NTPClient/keywords.txt new file mode 100644 index 0000000..afb03c9 --- /dev/null +++ b/waste_watcher_code_platformio_demo/lib/NTPClient/keywords.txt @@ -0,0 +1,20 @@ +####################################### +# Datatypes (KEYWORD1) +####################################### + +NTPClient KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +begin KEYWORD2 +end KEYWORD2 +update KEYWORD2 +forceUpdate KEYWORD2 +getDay KEYWORD2 +getHours KEYWORD2 +getMinutes KEYWORD2 +getSeconds KEYWORD2 +getFormattedTime KEYWORD2 +getEpochTime KEYWORD2 diff --git a/waste_watcher_code_platformio_demo/lib/NTPClient/library.json b/waste_watcher_code_platformio_demo/lib/NTPClient/library.json new file mode 100644 index 0000000..d6249c1 --- /dev/null +++ b/waste_watcher_code_platformio_demo/lib/NTPClient/library.json @@ -0,0 +1,24 @@ +{ + "name": "NTPClient", + "keywords": "ntp, client, time", + "description": "A NTPClient to connect to a time server", + "authors": + [ + { + "name": "Fabrice Weinberg", + "email": "fabrice@weinberg.me" + }, + { + "name": "Sandeep Mistry", + "email": "s.mistry@arduino.cc" + } + ], + "repository": + { + "type": "git", + "url": "https://github.com/arduino-libraries/NTPClient.git" + }, + "version": "3.1.0", + "frameworks": "arduino", + "platforms": "espressif" +} diff --git a/waste_watcher_code_platformio_demo/lib/NTPClient/library.properties b/waste_watcher_code_platformio_demo/lib/NTPClient/library.properties new file mode 100644 index 0000000..d4908ca --- /dev/null +++ b/waste_watcher_code_platformio_demo/lib/NTPClient/library.properties @@ -0,0 +1,9 @@ +name=NTPClient +version=3.1.0 +author=Fabrice Weinberg +maintainer=Fabrice Weinberg +sentence=An NTPClient to connect to a time server +paragraph=Get time from a NTP server and keep it in sync. +category=Timing +url=https://github.com/arduino-libraries/NTPClient +architectures=* diff --git a/waste_watcher_code_platformio_demo/lib/README b/waste_watcher_code_platformio_demo/lib/README new file mode 100644 index 0000000..6debab1 --- /dev/null +++ b/waste_watcher_code_platformio_demo/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/waste_watcher_code_platformio_demo/platformio.ini b/waste_watcher_code_platformio_demo/platformio.ini new file mode 100644 index 0000000..18d1071 --- /dev/null +++ b/waste_watcher_code_platformio_demo/platformio.ini @@ -0,0 +1,15 @@ +; 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:esp32cam] +platform = espressif32 +board = esp32cam +framework = arduino +monitor_speed = 115200 diff --git a/waste_watcher_code_platformio_demo/src/main.cpp b/waste_watcher_code_platformio_demo/src/main.cpp new file mode 100644 index 0000000..c14115a --- /dev/null +++ b/waste_watcher_code_platformio_demo/src/main.cpp @@ -0,0 +1,360 @@ + + +// *Author*: Owen Yang +// *Description*: A simple waste auditing data logger script for the ESP32 CAM. +// *Acknowledgements*: Random Nerd Tutorials +// *License*: Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files. +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// *Resources*: +// - https://randomnerdtutorials.com/esp32-ntp-client-date-time-arduino-ide/ +// - https://randomnerdtutorials.com/telegram-esp32-cam-photo-arduino/ +// - https://randomnerdtutorials.com/esp32-cam-take-photo-save-microsd-card/ +// - https://techtutorialsx.com/2020/09/06/esp32-writing-file-to-sd-card/ +// - https://www.instructables.com/Select-SD-Interface-for-ESP32/ +// - https://randomnerdtutorials.com/esp32-cam-ai-thinker-pinout/ +// - https://randomnerdtutorials.com/esp32-deep-sleep-arduino-ide-wake-up-sources/ +// - https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/gpio.html + +#include +#include "esp_camera.h" +#include "Arduino.h" +#include "FS.h" // SD Card ESP32 +#include "SD_MMC.h" // SD Card ESP32 +#include // SD Card ESP32 +#include "soc/soc.h" // Disable brownout problems +#include "soc/rtc_cntl_reg.h" // Disable brownout problems +#include "driver/rtc_io.h" +#include +#include +#include + + +// === wifi credentials === +// replace with your network credentials +const char* WIFI_SSID = "REPLACE_WITH_YOUR_WIFI_SSID"; +const char* WIFI_PASS = "REPLACE_WITH_YOUR_WIFI_PASSWORD"; + +// === peripherals pin assignment === +const int ultrasonicTrigPin = 13; +const int ultrasonicEchoPin = 12; + +// === deep sleep === +#define uS_TO_S_FACTOR 1000000 /* conversion factor for usec to sec */ +#define TIME_TO_SLEEP 1800 /* TIME ESP32 will go to sleep in sec */ + +// === other parameters === +const int binHeight = 50; // units: cm, used to calculate fullness +const int ultraMinRange = 2; //units: cm, min. limit of accurate HC-SR04 reading +const int ultraMaxRange = 400; //units: cm, max. limit of accurate HC-SR04 reading +const char* datalogFile = "/data.csv"; + +// === system variables === +long duration; // units: microseconds, for HC-SR04 +int distance; // units:cm, for HC-SR04 +int fullness; //units:cm, the bin fullness +String datetimeStamp; +String path; +String dataMessage; + +// Define NTP Client to get time +WiFiUDP ntpUDP; +NTPClient timeClient(ntpUDP); + +// === camera pin definitions === +#define FLASH_LED_PIN 4 + +//CAMERA_MODEL_AI_THINKER +#define PWDN_GPIO_NUM 32 +#define RESET_GPIO_NUM -1 +#define XCLK_GPIO_NUM 0 +#define SIOD_GPIO_NUM 26 +#define SIOC_GPIO_NUM 27 + +#define Y9_GPIO_NUM 35 +#define Y8_GPIO_NUM 34 +#define Y7_GPIO_NUM 39 +#define Y6_GPIO_NUM 36 +#define Y5_GPIO_NUM 21 +#define Y4_GPIO_NUM 19 +#define Y3_GPIO_NUM 18 +#define Y2_GPIO_NUM 5 +#define VSYNC_GPIO_NUM 25 +#define HREF_GPIO_NUM 23 +#define PCLK_GPIO_NUM 22 + +void configInitCamera(){ + camera_config_t config; + config.ledc_channel = LEDC_CHANNEL_0; + config.ledc_timer = LEDC_TIMER_0; + config.pin_d0 = Y2_GPIO_NUM; + config.pin_d1 = Y3_GPIO_NUM; + config.pin_d2 = Y4_GPIO_NUM; + config.pin_d3 = Y5_GPIO_NUM; + config.pin_d4 = Y6_GPIO_NUM; + config.pin_d5 = Y7_GPIO_NUM; + config.pin_d6 = Y8_GPIO_NUM; + config.pin_d7 = Y9_GPIO_NUM; + config.pin_xclk = XCLK_GPIO_NUM; + config.pin_pclk = PCLK_GPIO_NUM; + config.pin_vsync = VSYNC_GPIO_NUM; + config.pin_href = HREF_GPIO_NUM; + config.pin_sscb_sda = SIOD_GPIO_NUM; + config.pin_sscb_scl = SIOC_GPIO_NUM; + config.pin_pwdn = PWDN_GPIO_NUM; + config.pin_reset = RESET_GPIO_NUM; + config.xclk_freq_hz = 20000000; + config.pixel_format = PIXFORMAT_JPEG; + + //=== init with high specs to pre-allocate larger buffers === + if(psramFound()){ + Serial.println("PSRAM found"); + config.frame_size = FRAMESIZE_UXGA; + config.jpeg_quality = 20; //0-63 lower number means higher quality + config.fb_count = 2; + } else { + Serial.println("PSRAM not found"); + config.frame_size = FRAMESIZE_SVGA; + config.jpeg_quality = 15; //0-63 lower number means higher quality + config.fb_count = 1; + } + + // === Init Camera === + esp_err_t err = esp_camera_init(&config); + if (err != ESP_OK) { + Serial.printf("Camera init failed with error 0x%x", err); + return; + } + + // Drop down frame size for higher initial frame rate + sensor_t * s = esp_camera_sensor_get(); + s->set_framesize(s, FRAMESIZE_UXGA); // UXGA|SXGA|XGA|SVGA|VGA|CIF|QVGA|HQVGA|QQVGA + +} + +void getDateTime() { + timeClient.begin(); + while(!timeClient.update()) { + timeClient.forceUpdate(); + } + datetimeStamp = timeClient.getFormattedDate(); //format: 2018-04-30T16:00:13Z + datetimeStamp.replace(":","-"); // get rid of colons + + //=== end the client === + timeClient.end(); +} + +void takePhoto() { + // === determine datetimestamp === + getDateTime(); + + // === camera initialize buffer === + camera_fb_t *fb = NULL; + + // === take picture with camera === + fb = esp_camera_fb_get(); + if (!fb) { + Serial.println("Camera capture failed"); + delay(1000); + ESP.restart(); + return; + } + Serial.println("photo taken!"); + + // === determine image name === + path = "/picture" + datetimeStamp + ".jpg"; + fs::FS &fs = SD_MMC; + Serial.printf("Picture file name: %s\n", path.c_str()); + + // === save photo to SD-card === + File file = fs.open(path.c_str(), FILE_WRITE); + if(!file){ + Serial.println("Failed to open file in writing mode"); + } + else { + file.write(fb->buf, fb->len); // payload (image), payload length + Serial.printf("Saved file to path: %s\n", path.c_str()); + } + file.close(); + + //=== return the frame buffer back to be reused === + esp_camera_fb_return(fb); +} + +void fullnessRead(){ + rtc_gpio_hold_dis(GPIO_NUM_13); + rtc_gpio_hold_dis(GPIO_NUM_12); + for (int i = 0; i < 5; ++i ) + { + // clear trigger pin + digitalWrite(ultrasonicTrigPin,LOW); + delayMicroseconds(2); //units: us + + // Toggle trigPin HIGH then LOW + digitalWrite(ultrasonicTrigPin,HIGH); + delayMicroseconds(10); //units: us + digitalWrite(ultrasonicTrigPin,LOW); + + // Reads echoPin and returns sound wave travel time + duration = pulseIn(ultrasonicEchoPin, HIGH); + distance = duration*0.034/2; + Serial.print("Raw Distance: "); + Serial.print(distance); + Serial.println(); + + // Filters out out of range values + if (distanceultraMaxRange) { + distance = -1; + fullness = -1; + } + else { + // calculate fullness + fullness = binHeight - distance; + return; + } + } + + // Turn off signal pins + digitalWrite(ultrasonicTrigPin,LOW); + digitalWrite(ultrasonicTrigPin,LOW); + rtc_gpio_hold_en(GPIO_NUM_13); + rtc_gpio_hold_en(GPIO_NUM_12); +} + +void writeFile(fs::FS &fs, const char * path, const char * message){ + Serial.printf("Writing file: %s\n", path); + + File file = fs.open(path, FILE_WRITE); + if(!file){ + Serial.println("Failed to open file for writing"); + return; + } + if(file.print(message)){ + Serial.println("File written"); + } else { + Serial.println("Write failed"); + } +} + +void appendFile(fs::FS &fs, const char * path, const char * message){ + Serial.printf("Appending to file: %s\n", path); + + File file = fs.open(path, FILE_APPEND); + if(!file){ + Serial.println("Failed to open file for appending"); + return; + } + if(file.print(message)){ + Serial.println("Message appended"); + } else { + Serial.println("Append failed"); + } +} + +void sdSetup() { + // === sd card setup === + // Card Mounting + Serial.println("Starting SD Card"); + if(!SD_MMC.begin("/sdcard", true)){ + Serial.println("SD Card Mount Failed"); + return; + } + + //Detect Card + uint8_t cardType = SD_MMC.cardType(); + if(cardType == CARD_NONE){ + Serial.println("No SD Card attached"); + return; + } + + // If the data.txt file doesn't exist + // Create a file on the SD card and write the data labels + File file = SD_MMC.open(datalogFile); + if(!file) { + file.close(); + Serial.println("File doesn't exist"); + writeFile(SD_MMC, datalogFile, "Datetimestamp,fullness(cm),count\n"); + return; + } + else { + Serial.println("File already exists"); + file.close(); + } +} + +void setup() { + Serial.begin(115200); + Serial.println("Setup has started..."); + + // === deep sleep setup === + esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR); + Serial.println("Setup ESP32 to sleep for every " + String(TIME_TO_SLEEP) + " Seconds"); + + // === sd card setup === + sdSetup(); + + // === ultrasonic setup === + pinMode(ultrasonicTrigPin, OUTPUT); + pinMode(ultrasonicEchoPin, INPUT); + + // === wifi setup === + Serial.print("Connecting to "); + Serial.println(WIFI_SSID); + WiFi.begin(WIFI_SSID, WIFI_PASS); + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + // Print local IP address and start web server + Serial.println(""); + Serial.println("WiFi connected."); + Serial.println("IP address: "); + Serial.println(WiFi.localIP()); + + // === camera setup === + pinMode( FLASH_LED_PIN, OUTPUT); // Setup flash + configInitCamera(); // Setup camera + + Serial.println("Setup finished"); + Serial.println(); + + // === take a photo === + rtc_gpio_hold_dis(GPIO_NUM_4); + digitalWrite(FLASH_LED_PIN,HIGH); + takePhoto(); // will also automatically update the datetimeStamp + digitalWrite(FLASH_LED_PIN,LOW); + rtc_gpio_hold_en(GPIO_NUM_4); + + // === get distance reading === + fullnessRead(); + + // === write data to sd card === + //update the datetime stamp + getDateTime(); + //create data message + dataMessage = datetimeStamp + "," + String(fullness) + "\n"; + Serial.print("Writing "); + Serial.print(dataMessage); + Serial.println(); + + // convert String to char* + int str_len = dataMessage.length()+1; + char charBuf[str_len]; + dataMessage.toCharArray(charBuf,str_len); + + // write to sd card + appendFile(SD_MMC, datalogFile, charBuf); + + // === sleep timer === + Serial.println("Going to sleep now"); + delay(1000); + Serial.flush(); + esp_deep_sleep_start(); + +} + +void loop() { + +} \ No newline at end of file diff --git a/waste_watcher_code_platformio_demo/test/README b/waste_watcher_code_platformio_demo/test/README new file mode 100644 index 0000000..b94d089 --- /dev/null +++ b/waste_watcher_code_platformio_demo/test/README @@ -0,0 +1,11 @@ + +This directory is intended for PlatformIO Unit Testing and project tests. + +Unit Testing is a software testing method by which individual units of +source code, sets of one or more MCU program modules together with associated +control data, usage procedures, and operating procedures, are tested to +determine whether they are fit for use. Unit testing finds problems early +in the development cycle. + +More information about PlatformIO Unit Testing: +- https://docs.platformio.org/page/plus/unit-testing.html