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/README b/lib/README new file mode 100644 index 0000000..2593a33 --- /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 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/platformio.ini b/platformio.ini new file mode 100644 index 0000000..9645421 --- /dev/null +++ b/platformio.ini @@ -0,0 +1,20 @@ +; 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:rpipicow] +platform = https://github.com/maxgerhardt/platform-raspberrypi.git +board = rpipicow +framework = arduino +board_build.core = earlephilhower +lib_deps = + adafruit/DHT sensor library@^1.4.6 + olikraus/U8g2@^2.35.19 + arduino-libraries/NTPClient@^3.2.1 + adafruit/Adafruit BME280 Library@^2.2.4 diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..7bf3370 --- /dev/null +++ b/readme.md @@ -0,0 +1,3 @@ +A digital clock and thermomter/hydrometer written in Arduino for the Raspberry Pi Pico using an SH1106 OLED dispaly and a BME280 environment sensor + +Pin definitions and display drivers (as long as the display is 128x64) can easily be changed in the code \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..8e2f133 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,338 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SCK 17 +#define SDA 16 +#define SUMMER 18 +#define BATT A1 +#define BUTTON 14 + +U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE); + +const char* ssid = "Limbo"; +const char* password = "95H2O862U235@#"; + +bool lpmode; + +WiFiUDP ntpUDP; +NTPClient timeClient(ntpUDP, "gr.pool.ntp.org", 0, 1296000000); +Adafruit_BME280 bme; + +int batteryReader() +{ + static int battery, readings, batterya; + int batteryv = analogRead(BATT); + batterya = batterya + batteryv; + readings++; + if (readings >= 10) + { + battery = batterya / 10; + batterya = 0; + readings = 0; + } + return battery; +} + +float getBatteryVoltage() +{ + float battv = (batteryReader() * 3.3 / 4095) * 2; + return battv; +} + +float getBatteryPrecentage() +{ + float battp = (getBatteryVoltage() - 2.5) / (4.2 - 2.5) * 100; + if (battp <= 0) + { + battp = 1; + } + if (battp > 99) + { + lpmode = false; + } + else + { + lpmode = true; + } + return battp; +} + +int getWifiRSSI() +{ + static int RSSI, RSSIavg, readings; + int rssir = WiFi.RSSI(); + RSSIavg = RSSIavg + rssir; + readings++; + if (readings >= 10) + { + RSSI = RSSIavg / 10; + RSSIavg = 0; + readings = 0; + } + return RSSI; +} + +int getWifiStatus(){ + int RSSI = getWifiRSSI(); + int wifiStatus; + if (RSSI >= -55 && WiFi.status() == WL_CONNECTED) + { + wifiStatus = 0; + } + else if (RSSI >= -67 && WiFi.status() == WL_CONNECTED) + { + wifiStatus = 1; + } + else if (RSSI >= -90 && WiFi.status() == WL_CONNECTED) + { + wifiStatus = 2; + } + else if (WiFi.status() != WL_CONNECTED && lpmode == true) + { + wifiStatus = 3; + } + else + { + wifiStatus = 4; + WiFi.begin(ssid, password); + } + return wifiStatus; +} + +const char* getDay() +{ + const char* day; + switch (timeClient.getDay()) + { + case 0: + day = "Sun"; + break; + case 1: + day = "Mon"; + break; + case 2: + day = "Tue"; + break; + case 3: + day = "Wed"; + break; + case 4: + day = "Thu"; + break; + case 5: + day = "Fri"; + break; + case 6: + day = "Sat"; + break; + } + return day; +} + +bool checkBrightness() +{ + static bool brightness = lpmode; + if (digitalRead(BUTTON) == true) + { + brightness = !brightness; + } + return brightness; +} + +float readTemperature() +{ + static int counter; + float temp = bme.readTemperature(); + static float tempAvg, returnTempAvg; + tempAvg = tempAvg + temp; + counter++; + if (counter >= 10) + { + returnTempAvg = tempAvg / 10; + tempAvg = 0; + counter = 0; + } + return returnTempAvg; +} + +float readHumidity() +{ + static int counter; + float hum = bme.readHumidity(); + static float humAvg, returnHumAvg; + humAvg = humAvg + hum; + counter++; + if (counter >= 10) + { + returnHumAvg = humAvg / 10; + humAvg = 0; + counter = 0; + } + return returnHumAvg; +} + +void displayClock(const char *day, float battp, int wifiStatus, int hour, int minute, bool brightness, float temp, float hum) +{ + static bool blink; + + u8g2.clearBuffer(); + + u8g2.setFont(u8g2_font_spleen16x32_mn); + if (hour < 10) + { + u8g2.setCursor(24, 41); + u8g2.print("0"); + u8g2.setCursor(40, 41); + u8g2.print(hour); + } + else + { + u8g2.setCursor(24, 41); + u8g2.print(hour); + } + u8g2.setCursor(56, 41); + switch (blink) + { + case true: + u8g2.print(":"); + break; + case false: + u8g2.print(""); + break; + } + if (minute < 10) + { + u8g2.setCursor(72, 41); + u8g2.print("0"); + u8g2.setCursor(88, 41); + u8g2.print(minute); + } + else + { + u8g2.setCursor(72, 41); + u8g2.print(minute); + } + + u8g2.setFont(u8g2_font_7x14_mf); + u8g2.setCursor(0, 60); + u8g2.print(day); + + u8g2.setFont(u8g2_font_siji_t_6x10); + switch (wifiStatus) + { + case 0: + u8g2.drawGlyph(0, 10, 57882); + break; + case 1: + u8g2.drawGlyph(0, 10, 57881); + break; + case 2: + u8g2.drawGlyph(0, 10, 57880); + break; + case 3: + u8g2.drawGlyph(0, 10, 57907); + break; + case 4: + u8g2.drawGlyph(0, 10, 57879); + break; + } + + if (battp < 99) + { + if (battp >= 70) + { + u8g2.drawGlyph(92, 10, 57487); + } + else if (battp >= 30) + { + u8g2.drawGlyph(92, 10, 57486); + } + else + { + u8g2.drawGlyph(92, 10, 57485); + } + u8g2.setCursor(104, 10); + u8g2.print(battp, 0); + u8g2.drawStr(116, 10, "%"); + } + else + { + u8g2.drawGlyph(116, 10, 57409); + } + + u8g2.drawGlyph(44, 10, 57550); + u8g2.setCursor(56, 10); + u8g2.print(temp, 1); + u8g2.drawGlyph(44, 60, 57549); + u8g2.setCursor(56, 60); + u8g2.print(hum, 1); + + switch (brightness) + { + case false: + u8g2.setContrast(255); + u8g2.drawGlyph(114, 60, 57794); + break; + case true: + u8g2.setContrast(50); + u8g2.drawGlyph(114, 60, 57795); + break; + } + + blink = !blink; + u8g2.sendBuffer(); +} + +void setup() +{ + pinMode(SUMMER, INPUT_PULLDOWN); + pinMode(BUTTON, INPUT_PULLDOWN); + analogReadResolution(12); + Wire.setSCL(SCK); + Wire.setSDA(SDA); + WiFi.mode(WIFI_STA); + WiFi.hostname("pico-clock"); + WiFi.begin(ssid, password); + while (WiFi.status() != WL_CONNECTED) + { + delay(500); + } + Wire.begin(); + u8g2.begin(); + timeClient.begin(); + bme.begin(0x76); + if (digitalRead(SUMMER) == 1) + { + timeClient.setTimeOffset(10800); //UTC+3 + } + else + { + timeClient.setTimeOffset(7200); //UTC+2 + } + for (int i = 0; i < 10; i++) + { + getBatteryPrecentage(); + getWifiRSSI(); + } +} + +void loop() +{ + bool disconnected; + timeClient.update(); + displayClock(getDay(), getBatteryPrecentage(), getWifiStatus(), timeClient.getHours(), timeClient.getMinutes(), checkBrightness(), readTemperature(), readHumidity()); + sleep_ms(1000); + if (lpmode == true && disconnected == false) + { + WiFi.disconnect(); + set_sys_clock_khz(20000, true); + disconnected = true; + } +} 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 diff --git a/updates.md b/updates.md new file mode 100644 index 0000000..a841bf9 --- /dev/null +++ b/updates.md @@ -0,0 +1,57 @@ +**CHANGELOG:** + +2.0: +* Removed DHT reading capabilities, second core is now unused +* Added BME280 sensor reading, displayClock() now requires the temperature and humidity to be provided instead of being global variables + +1.9: +* Removed the WiFi low power mode timer. The WiFi now disconnects permenantly when on battery after getting NTP time once +* Removed WiFi agressive low power mode when connecting +* Tweaked checks for displaying wifi symbols +* Changed NTP update time to 15 days + +1.8: +* Added a flag that gets set when the WiFi gets disconnected manually by low power mode so that we can differinciate from when a disconnection occurs for other reasons +* Removed delay time when trying to connect to WiFi on low power mode + +1.7: +* Added the use of dynamic frequency scaling. The processor will now go down to 20Mhz when the WiFi is turned off and 96Mhz when turned on. This results in a power draw of ~14mA when in low-power mode. Expected battery life on a standard 2000mAh 18650 Li-Ion should now be ~142 Hours (5 - 6 days) + +1.6: +* Tweaked some indentations +* Moved clock counter for WiFi turning on/off to the second core +* Moved WiFi status checking to it's own function. displayClock() now only checks a status integer that function returns +* Changed if statement to switch when checking blink + +1.5: +* Added a global flag that is set if the board is plugged into power (battery precentage over 99) that is false is plugged in and true if not. If true, WiFi disconnects as expected and while the WiFi is disconnected a moon symbol is shown, and the brightness is set to low by default. If false, the WiFi is constantly connected and if disconnected the x symbol is shown and reconnection is attempted, and brightness is high by default. +* Battery voltage/precentage and WiFi RSSI is now checked in setup + +1.4: +* Updated WiFi so that it now connects only every hour to update NTP and corrent any errors. It will remain disconnected for the majority of time. This reduces power consumption from ~50mA (on WiFi agressive low power mode) to ~27mA (while completely disconnected). Expected battery life on a standard 2000mAh 18650 Li-Ion should now be ~74 Hours (3 days) +* Updated NTP update time from 10 hours to 1 hour +* Low brightness mode is now the default, lowering power consumption by ~3mA. This can add ~8 hours of battery life. +* Cores now use sleep_ms instead of delay, which will attempt to put cores in a low-power state when waiting +* WiFi no longer automatically reconnects if disconnected (when x symbol is displayed), since it will do that automatically in the loop + +1.31: +* Tweaked readability of the code by moving curly braces (not my choise) +* Added a check setting battery precentage to 1 if it is less or equal to 0, since 0 isn't possible while the board is on + +1.3: +* Tweaked some checks for showing the appropriate battery symbols +* Removed functions for variables being called in displayClock() function, instead being passed as arguements from loop(). These include: getDay(), getBatteryPrecentage(), getWifiRSSI(), timeClient.getHours(), timeClient.getMinutes(), checkBrightness() + +1.2: +* Removed a check setting battery precentage to 99 if it was 100 or more since values over 99 will never be displayed anyways, instead being replaced with a plug symbol +* Added temperature and humidity value averaging and updated DHT sensor read time to 6 seconds. This means the display will be updated with new values for temperature and humidity every minute + +1.11: +* Fixed the RSSI averaging function being called 3 times per second instead of 1 + +1.1: +* Added WiFi RSSI averaging when display the WiFi singal + +1.0: +* Updated battery precentage calculator math to original (measurement - min) / (max - min) * 100 +* Created this updates.md file \ No newline at end of file