diff --git a/.gitignore b/.gitignore index 0d53eb50b05fe0fe8db7234c0ccf66db882d119a..19971839421182f449dfa156a87ad6f5fa8aa30b 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ arduino_secrets.h erikoistyö_raportti.pdf Työtunnit - Taulukko.pdf .DS_Store +*.png diff --git a/README.md b/README.md index 458afb316ad7706242484718dd49323ba916ea14..d5a9600f5ef90c8d5785aa679aa29538e3fdc8b4 100644 --- a/README.md +++ b/README.md @@ -21,8 +21,6 @@ which it wants data. The user table has a array in it which contains the ID's of the subscribed nodes. The node table has info on the node, but there will be a subtable which contains nodeID, timestamp and attributes. -## Technical info - ## Future implementations Watcher should have the possibility to add friends to users friends list and possibly share their data. diff --git a/WatcherGui/src/main/java/com/joelhelkala/watcherGui/Nodes/Node/Node.java b/WatcherGui/src/main/java/com/joelhelkala/watcherGui/Nodes/Node/Node.java index e9e3a4cfb19547bd7792c4d2ed924a642b6ce7ad..92f9668906ec03af85de998505f3125b44e3e806 100644 --- a/WatcherGui/src/main/java/com/joelhelkala/watcherGui/Nodes/Node/Node.java +++ b/WatcherGui/src/main/java/com/joelhelkala/watcherGui/Nodes/Node/Node.java @@ -69,8 +69,7 @@ public class Node { NodeData data = findRecentData(); if(data == null) return 0; return data.getHumidity(); - } - + } public Integer getRecentLuminosity() { NodeData data = findRecentData(); diff --git a/WatcherGui/src/main/java/com/joelhelkala/watcherGui/frames/WelcomePage.java b/WatcherGui/src/main/java/com/joelhelkala/watcherGui/frames/WelcomePage.java index 137d527f5937e6794413453e4dea9f98d372e3c4..0d7b1645c910c18f73d9adfcaead8c2602e23f4d 100644 --- a/WatcherGui/src/main/java/com/joelhelkala/watcherGui/frames/WelcomePage.java +++ b/WatcherGui/src/main/java/com/joelhelkala/watcherGui/frames/WelcomePage.java @@ -28,7 +28,7 @@ import com.joelhelkala.watcherGui.User.User; import com.joelhelkala.watcherGui.User.Roles.Role; import com.joelhelkala.watcherGui.frames.dialogs.Dialog; import com.joelhelkala.watcherGui.frames.subframes.AdminFrame; -import com.joelhelkala.watcherGui.frames.subframes.FriendsFrame; +import com.joelhelkala.watcherGui.frames.subframes.Histograms; import com.joelhelkala.watcherGui.frames.subframes.NodeDataFrame; import com.joelhelkala.watcherGui.frames.subframes.NodeSettingsFrame; import com.joelhelkala.watcherGui.frames.subframes.SettingsFrame; @@ -57,7 +57,7 @@ public class WelcomePage implements MouseListener, ActionListener { private JPanel stagePanel; private final JLabel nodeDataLabel = new JLabel("Node data"); private final JLabel nodeSettingsLabel = new JLabel("Node settings"); - private final JLabel friendsLabel = new JLabel("Friends"); + private final JLabel friendsLabel = new JLabel("Histograms"); private final JLabel adminLabel = new JLabel("Admin"); private final JLabel helpLabel = new JLabel("Help"); private final JLabel settingsLabel = new JLabel("Settings"); @@ -69,6 +69,7 @@ public class WelcomePage implements MouseListener, ActionListener { private static NodeDataFrame nodeDataFrame = new NodeDataFrame(leftPanelWidth, topPanelHeight, width-leftPanelWidth, height-topPanelHeight-bottomPanelHeight); private static NodeSettingsFrame nodeSettingFrame = new NodeSettingsFrame(width-leftPanelWidth, height-topPanelHeight-bottomPanelHeight); + private static Histograms histogramFrame = new Histograms(); public WelcomePage(){ frame = new JFrame(); @@ -187,12 +188,13 @@ public class WelcomePage implements MouseListener, ActionListener { stagePanel.setBounds(leftPanelWidth, topPanelHeight, width-leftPanelWidth, height-topPanelHeight-bottomPanelHeight); stagePanel.setBackground(Color.red); - JPanel friendsFrame = new FriendsFrame(leftPanelWidth, topPanelHeight, width-leftPanelWidth, height-topPanelHeight-bottomPanelHeight); + //JPanel friendsFrame = new FriendsFrame(leftPanelWidth, topPanelHeight, width-leftPanelWidth, height-topPanelHeight-bottomPanelHeight); JPanel settingsFrame = new SettingsFrame(); + stagePanel.setLayout(cl); stagePanel.add(nodeDataFrame, "data"); stagePanel.add(nodeSettingFrame, "setting"); - stagePanel.add(friendsFrame, "friends"); + stagePanel.add(histogramFrame, "friends"); stagePanel.add(settingsFrame, "settings"); if(User.getRole() == Role.ADMIN) { @@ -385,6 +387,7 @@ public class WelcomePage implements MouseListener, ActionListener { node.updateData(HttpRequests.getNodeData(node.getId())); nodeDataFrame.updateData(node); nodeSettingFrame.updateInformation(node); + histogramFrame.updateData(node); } /* diff --git a/WatcherGui/src/main/java/com/joelhelkala/watcherGui/frames/panels/LineChartPanel.java b/WatcherGui/src/main/java/com/joelhelkala/watcherGui/frames/panels/LineChartPanel.java index fcfc9fb94cd360568979659a42fbc4820fd3796a..2b27d20d466404d3f9614c08b2c40e441c14b301 100644 --- a/WatcherGui/src/main/java/com/joelhelkala/watcherGui/frames/panels/LineChartPanel.java +++ b/WatcherGui/src/main/java/com/joelhelkala/watcherGui/frames/panels/LineChartPanel.java @@ -1,8 +1,8 @@ package com.joelhelkala.watcherGui.frames.panels; +import java.awt.BorderLayout; import java.awt.Dimension; -import java.time.format.DateTimeFormatter; -import java.time.format.FormatStyle; +import java.util.List; import javax.swing.JPanel; @@ -15,9 +15,6 @@ import org.jfree.data.category.DefaultCategoryDataset; import com.joelhelkala.watcherGui.Nodes.Node.NodeData.NodeData; -import java.awt.BorderLayout; -import java.util.List; - public class LineChartPanel extends JPanel { private JFreeChart lineChart; diff --git a/WatcherGui/src/main/java/com/joelhelkala/watcherGui/frames/subframes/AdminFrame.java b/WatcherGui/src/main/java/com/joelhelkala/watcherGui/frames/subframes/AdminFrame.java index f53ad4c13a6af97ee520cee703baa35cad674990..23a8143b5b7bac713cf3968f3941120eba67bdf5 100644 --- a/WatcherGui/src/main/java/com/joelhelkala/watcherGui/frames/subframes/AdminFrame.java +++ b/WatcherGui/src/main/java/com/joelhelkala/watcherGui/frames/subframes/AdminFrame.java @@ -2,18 +2,17 @@ package com.joelhelkala.watcherGui.frames.subframes; import java.awt.BorderLayout; import java.awt.Color; -import java.util.List; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.util.List; import javax.swing.Box; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JLabel; -import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JTextField; import javax.swing.SwingConstants; diff --git a/WatcherGui/src/main/java/com/joelhelkala/watcherGui/frames/subframes/Histograms.java b/WatcherGui/src/main/java/com/joelhelkala/watcherGui/frames/subframes/Histograms.java new file mode 100644 index 0000000000000000000000000000000000000000..7097b4c50e0116a2b737dca0722cdb437a6dd4e8 --- /dev/null +++ b/WatcherGui/src/main/java/com/joelhelkala/watcherGui/frames/subframes/Histograms.java @@ -0,0 +1,90 @@ +package com.joelhelkala.watcherGui.frames.subframes; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.util.ArrayList; + +import javax.swing.JButton; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.SpringLayout; + +import com.joelhelkala.watcherGui.Colors.Colors; +import com.joelhelkala.watcherGui.Nodes.Node.Node; +import com.joelhelkala.watcherGui.frames.panels.LineChartPanel; + +public class Histograms extends JPanel { + private LineChartPanel lumiChart; + private LineChartPanel moistChart; + private LineChartPanel tempChart; + + public Histograms() { + JScrollPane scroll = new JScrollPane(); + + JButton next = new JButton("Next"); + JPanel buttonPanel = new JPanel(); + buttonPanel.add(next); + SpringLayout layout = new SpringLayout(); + JPanel mainPanel = new JPanel(); + mainPanel.setLayout(null); + mainPanel.setBackground(Colors.gray); + setLayout(new BorderLayout()); + + mainPanel.add(addTempChart(30, 0)); + mainPanel.add(addLumiChart(30, 350)); + mainPanel.add(addMoistChart(30, 700)); + + + mainPanel.setPreferredSize(new Dimension(mainPanel.getWidth(), 1500)); + scroll.setPreferredSize(new Dimension(500,500)); + scroll.setViewportView(mainPanel); + scroll.setBackground(Colors.gray); + add(scroll); + add(buttonPanel,BorderLayout.SOUTH); + + setSize(500, 600); + setVisible(true); + } + + private JPanel addMoistChart(int x, int y) { + JPanel chartPanel = new JPanel(); + chartPanel.setBackground(Colors.errorText); + chartPanel.setBounds(x,y,800,300); + chartPanel.setLayout(new BorderLayout()); + + moistChart = new LineChartPanel("Moisture", new ArrayList<>()); + moistChart.setBackground(Colors.buttonColor); + chartPanel.add(moistChart, BorderLayout.CENTER); + return chartPanel; + } + + private JPanel addLumiChart(int x, int y) { + JPanel chartPanel = new JPanel(); + chartPanel.setBackground(Colors.errorText); + chartPanel.setBounds(x,y,800,300); + chartPanel.setLayout(new BorderLayout()); + + lumiChart = new LineChartPanel("Luminosity", new ArrayList<>()); + lumiChart.setBackground(Colors.buttonColor); + chartPanel.add(lumiChart, BorderLayout.CENTER); + return chartPanel; + } + + private JPanel addTempChart(int x, int y) { + JPanel chartPanel = new JPanel(); + chartPanel.setBackground(Colors.errorText); + chartPanel.setBounds(x,y,800,300); + chartPanel.setLayout(new BorderLayout()); + + tempChart = new LineChartPanel("Temperature", new ArrayList<>()); + tempChart.setBackground(Colors.buttonColor); + chartPanel.add(tempChart, BorderLayout.CENTER); + return chartPanel; + } + + public void updateData(Node node) { + tempChart.updateData(node.getData()); + lumiChart.updateData(node.getData()); + moistChart.updateData(node.getData()); + } +} diff --git a/WatcherGui/src/main/java/com/joelhelkala/watcherGui/frames/subframes/NodeDataFrame.java b/WatcherGui/src/main/java/com/joelhelkala/watcherGui/frames/subframes/NodeDataFrame.java index 476f076023e9b9b84c88d50f4a319a2279d4ce40..bac2b5a446ddf9da8a939b6ccc76e422a49e644a 100644 --- a/WatcherGui/src/main/java/com/joelhelkala/watcherGui/frames/subframes/NodeDataFrame.java +++ b/WatcherGui/src/main/java/com/joelhelkala/watcherGui/frames/subframes/NodeDataFrame.java @@ -9,9 +9,7 @@ import com.joelhelkala.watcherGui.Nodes.Node.Node; import com.joelhelkala.watcherGui.frames.panels.HumidityPanel; import com.joelhelkala.watcherGui.frames.panels.LineChartPanel; import com.joelhelkala.watcherGui.frames.panels.LuminosityPanel; -import com.joelhelkala.watcherGui.frames.panels.ProgressBarCirclePanel; import com.joelhelkala.watcherGui.frames.panels.TemperaturePanel; -import com.joelhelkala.watcherGui.httpRequests.HttpRequests; public class NodeDataFrame extends JPanel { diff --git a/WatcherGui/src/main/java/com/joelhelkala/watcherGui/frames/subframes/NodeSettingsFrame.java b/WatcherGui/src/main/java/com/joelhelkala/watcherGui/frames/subframes/NodeSettingsFrame.java index 15f2b45d7bbc02ebf541d55f9ed04388a1308489..ae91cf4ac64089354087cdabad2f8b5c9cd8cfbb 100644 --- a/WatcherGui/src/main/java/com/joelhelkala/watcherGui/frames/subframes/NodeSettingsFrame.java +++ b/WatcherGui/src/main/java/com/joelhelkala/watcherGui/frames/subframes/NodeSettingsFrame.java @@ -36,35 +36,34 @@ public class NodeSettingsFrame extends JPanel implements ActionListener{ // Costructor public NodeSettingsFrame(int width, int height) { setLayout(null); - setBounds(0,0,width,height); setOpaque(true); setBackground(Colors.gray); JLabel descLabel = new JLabel("Description"); descLabel.setForeground(Color.white); - descLabel.setBounds(10,10, 100, 30); - descField.setBounds(120, 10, 200, 30); + descLabel.setBounds(width/2-200,40, 100, 30); + descField.setBounds(width/2-100, 40, 200, 30); add(descLabel); add(descField); JLabel locationLabel = new JLabel("Location"); locationLabel.setForeground(Color.white); - locationLabel.setBounds(10,50, 100, 30); - locationField.setBounds(120, 50, 200, 30); + locationLabel.setBounds(width/2-200,100, 100, 30); + locationField.setBounds(width/2-100, 100, 200, 30); add(locationLabel); add(locationField); JLabel latLabel = new JLabel("Latitude"); latLabel.setForeground(Color.white); - latLabel.setBounds(10,90, 100, 30); - latField.setBounds(120, 90, 200, 30); + latLabel.setBounds(width/2-200,160, 100, 30); + latField.setBounds(width/2-100, 160, 200, 30); add(latLabel); add(latField); JLabel lonLabel = new JLabel("Longitude"); lonLabel.setForeground(Color.white); - lonLabel.setBounds(10,130, 100, 30); - lonField.setBounds(120, 130, 200, 30); + lonLabel.setBounds(width/2-200,220, 100, 30); + lonField.setBounds(width/2-100, 220, 200, 30); add(lonLabel); add(lonField); @@ -73,8 +72,8 @@ public class NodeSettingsFrame extends JPanel implements ActionListener{ reset_btn.addActionListener(this); save_btn.addActionListener(this); - reset_btn.setBounds(width/4, height-200, 50, 20); - save_btn.setBounds(width/2, height-200, 50, 20); + reset_btn.setBounds(width/2-300, 300, 50, 20); + save_btn.setBounds(width/2+200, 300, 50, 20); add(reset_btn); add(save_btn); } diff --git a/arduinoSensor/arduinoSensor.ino b/arduinoSensor/arduinoSensor.ino index 7238f6c4ff4a9c93e2abce499a4fd8583f35becd..42ff76d42c95337f8a9edbbebf7de47996b68b40 100644 --- a/arduinoSensor/arduinoSensor.ino +++ b/arduinoSensor/arduinoSensor.ino @@ -1,19 +1,10 @@ /* - Repeating WiFi Web Client - - This sketch connects to a a web server and makes a request - using a WiFi equipped Arduino board. - - created 23 April 2012 - modified 31 May 2012 - by Tom Igoe - modified 13 Jan 2014 - by Federico Vanzati - - http://www.arduino.cc/en/Tutorial/WifiWebClientRepeating - This code is in the public domain. + * This is a arduino program which measures environmental data + * and sends a measured average of these measurements to server. + * + * Joel Helkala + * */ - #include <SPI.h> #include <WiFiNINA.h> #include <ArduinoHttpClient.h> @@ -25,7 +16,10 @@ #include <Adafruit_BME280.h> #include "Adafruit_TSL2591.h" +#include "measurements.h" +#include <iostream> #include <vector> +#include <chrono> // Initialize sensors Adafruit_BME280 bme; // BME sensor which has pressure, humidity and temperature @@ -35,54 +29,69 @@ Adafruit_TSL2591 tsl = Adafruit_TSL2591(2591); // Luminosity sensor char ssid[] = SECRET_SSID; // your network SSID (name) char pass[] = SECRET_PASS; // your network password (use for WPA, or use as key for WEP) -int keyIndex = 0; // your network key index number (needed only for WEP) - int status = WL_IDLE_STATUS; boolean api_success = false; // server address: -char server[] = "192.168.0.2"; -IPAddress server_ip(192,168,0,2); +char server[] = "84.249.42.194"; +IPAddress server_ip(84,249,42,194); int port = 8080; // Initialize the WiFi client library WiFiClient client; HttpClient http = HttpClient(client, server, port); -unsigned long lastConnectionTime = 0, lastMeasurement = 0; // last time you connected to the server, in milliseconds -const unsigned long postingInterval = 60L * 10L * 1000L; // delay (60 * 10 * 1000 = 10 minutes) between updates, in milliseconds -const unsigned long measurementInterval = 5L * 1000L; // measure every 10 seconds - -struct Data { - int temp, humid, lumi; -}; +//using namespace std::literals::chrono_literals; +typedef std::chrono::high_resolution_clock Time; -struct Measurements { - std::vector<Data> datas; +struct Timer { + std::chrono::time_point<std::chrono::high_resolution_clock> lastConnectionTime, lastMeasurement; + const std::chrono::milliseconds postingInterval = std::chrono::milliseconds(1000 * 60 * 10); // 10 minutes + const std::chrono::milliseconds measurementInterval = std::chrono::milliseconds(1000 * 10); + int counter = 0; - void AddMeasurements(Data values) { - datas.push_back(values); + Timer() { + lastConnectionTime = std::chrono::high_resolution_clock::now(); + lastMeasurement = std::chrono::high_resolution_clock::now(); } - - Data AverageAndClear() { - int temps = 0, humids = 0, lumis = 0; - - for(Data d : datas) { - temps += d.temp; - humids += d.humid; - lumis += d.lumi; - } + bool timeToPost() { + /*std::chrono::duration<float> duration = std::chrono::high_resolution_clock::now() - lastConnectionTime; + float durationMs = duration.count(); + Serial.println(durationMs); + if (durationMs > postingInterval.count()) return true; + return false;*/ + if (counter >= 10) return true; + return false; + } + bool timeToMeasure() { + std::chrono::duration<float> duration = Time::now() - lastMeasurement; - int amount = datas.size(); - int av_temp = temps/amount; - int av_humid = humids/amount; - int av_lumi = lumis/amount; - datas.clear(); - return Data{av_temp, av_humid, av_lumi}; + float durationMs = duration.count() * 1000.0f; + if (durationMs > measurementInterval.count()) return true; + return false; + } + void resetConnectionTime() { + //lastConnectionTime = std::chrono::high_resolution_clock::now(); + counter = 0; + } + void resetMeasurementTime() { + lastMeasurement = std::chrono::high_resolution_clock::now(); } }; -Measurements measurements; +struct Data { + int temp, humid, lumi; +}; + +void initPins() { + bme.begin(0x76); + tsl.begin(); + tsl.setGain(TSL2591_GAIN_MED); + tsl.setTiming(TSL2591_INTEGRATIONTIME_300MS); +} + +Watcher::Measurements measurements; +Timer timer; void setup() { //Initialize serial and wait for port to open: @@ -91,12 +100,7 @@ void setup() { ; // wait for serial port to connect. Needed for native USB port only } - // Initialize pins - bme.begin(0x76); - tsl.begin(); - tsl.setGain(TSL2591_GAIN_MED); - tsl.setTiming(TSL2591_INTEGRATIONTIME_300MS); - + initPins(); // check for the WiFi module: if (WiFi.status() == WL_NO_MODULE) { Serial.println("Communication with WiFi module failed!"); @@ -116,8 +120,11 @@ void setup() { } // you're connected now, so print out the status: printWifiStatus(); + timer.resetMeasurementTime(); + timer.resetConnectionTime(); } + // MAIN LOOP void loop() { // if there's incoming data from the net connection. @@ -128,31 +135,39 @@ void loop() { Serial.write(c); } - // Take measurements and add them to measurements - if(millis() - lastMeasurement > measurementInterval) { - int temp = bme.readTemperature(); - int humidity = bme.readHumidity(); - int luminosity = tsl.getLuminosity(TSL2591_VISIBLE); - measurements.AddMeasurements({temp, humidity, luminosity}); - lastMeasurement = millis(); - } - if (millis() - lastConnectionTime > postingInterval) { + // Take measurements and add them to list + double temp = bme.readTemperature(); + double humidity = bme.readHumidity(); + double luminosity = tsl.getLuminosity(TSL2591_VISIBLE); + Watcher::measurement_t meas = {temp, humidity, luminosity}; + Serial.print("Temperature: "); + Serial.println(meas.temperature); + + measurements.addMeasurement(meas); + timer.counter++; + + + if (timer.timeToPost()) { httpRequest(); } + + delay(10000); // sleep for 10s to save power } void httpRequest() { // close any connection before send a new request. // This will free the socket on the NINA module Serial.println("making POST request"); - Data average = measurements.AverageAndClear(); + Watcher::measurement_t average = measurements.getAverage(); + measurements.clear(); + String contentType = "application/x-www-form-urlencoded"; String postData ="temperature="; - postData += average.temp; + postData += average.temperature; postData += "&humidity="; - postData += average.humid; + postData += average.humidity; postData += "&luminosity="; - postData += average.lumi; + postData += average.luminosity; postData += "&parent_id="; postData += 1; @@ -168,7 +183,7 @@ void httpRequest() { Serial.println(response); // note the time that the connection was made: - lastConnectionTime = millis(); + timer.resetConnectionTime(); } // Tulostetaan wifin tiedot diff --git a/arduinoSensor/measurements.cpp b/arduinoSensor/measurements.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ce4273141462cd27782a740c7bf8793f012c247f --- /dev/null +++ b/arduinoSensor/measurements.cpp @@ -0,0 +1,39 @@ +#include "measurements.h" +#include <iostream> +#include <vector> + +namespace Watcher { + +Measurements::Measurements() : Measurements(3) {} +Measurements::Measurements(size_t size) { +} + +Measurements::~Measurements() { + // +} + +measurement_t Measurements::getAverage() const { + measurement_t average; + + for (auto measurement : _measurements_list) { + average.temperature += measurement.temperature; + average.humidity += measurement.humidity; + average.luminosity += measurement.luminosity; + } + + size_t measurements_amount = _measurements_list.size(); + average.temperature = average.temperature / measurements_amount; + average.humidity = average.humidity / measurements_amount; + average.luminosity = average.luminosity / measurements_amount; + + return average; +} + +void Measurements::clear() { + _measurements_list.clear(); +} + +void Measurements::addMeasurement(measurement_t &measurement) { + _measurements_list.emplace_back(measurement); +} +} diff --git a/arduinoSensor/measurements.h b/arduinoSensor/measurements.h new file mode 100644 index 0000000000000000000000000000000000000000..46bd495958b35de24fa55dca6485c43738194fd9 --- /dev/null +++ b/arduinoSensor/measurements.h @@ -0,0 +1,28 @@ +#ifndef Measurements_h +#define Measurements_h + +#include <iostream> +#include <chrono> +#include <vector> + +namespace Watcher { + +struct measurement_t { + double temperature, humidity, luminosity; +}; + +class Measurements { +public: + Measurements(); + Measurements(size_t size); + ~Measurements(); + + measurement_t getAverage() const; + void clear(); + void addMeasurement(measurement_t &measurement); + +private: + std::vector<measurement_t> _measurements_list; +}; +} +#endif