commit 8f8c69c37ab56a8c58ef359f855eddf1e3ffa939 Author: Finn_Dane Date: Mon Oct 30 23:15:40 2023 +0100 initial version diff --git a/arduino/.gitignore b/arduino/.gitignore new file mode 100644 index 0000000..03f4a3c --- /dev/null +++ b/arduino/.gitignore @@ -0,0 +1 @@ +.pio diff --git a/arduino/platformio.ini b/arduino/platformio.ini new file mode 100644 index 0000000..92be0e4 --- /dev/null +++ b/arduino/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:uno] +platform = atmelavr +board = uno +framework = arduino +lib_deps = + bakercp/PacketSerial@^1.4.0 + paulstoffregen/OneWire@^2.3.7 + milesburton/DallasTemperature@^3.11.0 + ivanseidel/LinkedList@0.0.0-alpha+sha.dac3874d28 +monitor_encoding = hexlify diff --git a/arduino/src/Sensor.cpp b/arduino/src/Sensor.cpp new file mode 100644 index 0000000..362c936 --- /dev/null +++ b/arduino/src/Sensor.cpp @@ -0,0 +1,35 @@ +#include +#include + +template +inline size_t writeType(byte *buffer, T val) { + memcpy(buffer, &val, sizeof(T)); + + return sizeof(T); +} + +Sensor::Sensor(int id, float resolution, float accuracy, DallasTemperature &bus, DeviceAddress address) : id(id), resolution(resolution), accuracy(accuracy), bus(bus) { + memcpy(this->address, address, sizeof(DeviceAddress)); +}; + +float Sensor::read() const { + bus.requestTemperaturesByAddress(address); + return bus.getTempC(address); +} + +uint16_t Sensor::makeDataPacket(byte (&buffer)[dataPacketMaxSize]) const { + size_t written = 0; + + written += writeType(&buffer[written], id); + written += writeType(&buffer[written], read()); + + return written + 1; +} + +void Sensor::registerSensor(byte (&buffer)[serializedSize]) const { + size_t written = 0; + + written += writeType(&buffer[written], id); + written += writeType(&buffer[written], resolution); + written += writeType(&buffer[written], accuracy); +} \ No newline at end of file diff --git a/arduino/src/Sensor.hpp b/arduino/src/Sensor.hpp new file mode 100644 index 0000000..a7efec5 --- /dev/null +++ b/arduino/src/Sensor.hpp @@ -0,0 +1,32 @@ +#ifndef SENSOR_HPP +#define SENSOR_HPP + +#include + +#include + +class Sensor { + private: + uint16_t id; + uint8_t resolution; + float accuracy; + DallasTemperature &bus; + DeviceAddress address; + + public: + static constexpr uint16_t serializedSize = sizeof(id) + sizeof(resolution) + sizeof(accuracy); + static constexpr uint16_t dataPacketMaxSize = 256; + + + Sensor(int id, float resolution, float accuracy, DallasTemperature &bus, DeviceAddress address); + + float read() const; + + uint16_t getId() const {return id;}; + + uint16_t makeDataPacket(byte (&buffer)[dataPacketMaxSize]) const; + + void registerSensor(byte (&buffer)[serializedSize]) const; +}; + +#endif \ No newline at end of file diff --git a/arduino/src/main.cpp b/arduino/src/main.cpp new file mode 100644 index 0000000..18cd770 --- /dev/null +++ b/arduino/src/main.cpp @@ -0,0 +1,59 @@ +#include +#include +#include +#include +#include + +#include + +constexpr uint8_t oneWirePort = 4; + +enum class PacketType : byte { + registration = 0x01, + data = 0x02 +}; + +int main() { + init(); + + PacketSerial packetSerial; + packetSerial.begin(9600); + + OneWire oneWire(oneWirePort); + DallasTemperature sensorBus(&oneWire); + sensorBus.begin(); + + LinkedList sensors; + + DeviceAddress tempAddress; + Sensor *tempSensorAddress; + for(int i = 0; i < sensorBus.getDeviceCount(); ++i) { + sensorBus.getAddress(tempAddress, i); + tempSensorAddress = new Sensor(i, sensorBus.getResolution(tempAddress), 0.5, sensorBus, tempAddress); + sensors.add(tempSensorAddress); + } + + byte buff[Sensor::serializedSize + 1]; + buff[0] = (byte)PacketType::registration; + for(int i = 0; i < sensors.size(); ++i) { + sensors.get(i)->registerSensor((byte(&)[Sensor::serializedSize])buff[1]); + packetSerial.send(buff, sizeof(buff)); + } + + byte dataBuff[Sensor::dataPacketMaxSize + 1]; + dataBuff[0] = (byte)PacketType::data; + uint16_t written; + while(true) { + for(int i = 0; i < sensors.size(); ++i) { + written = sensors.get(i)->makeDataPacket((byte(&)[Sensor::dataPacketMaxSize])dataBuff[1]); + packetSerial.send(dataBuff, written); + } + delay(500); + } + + for(int i = 0; i < sensors.size(); ++i) { + delete sensors.get(i); + } + + return 0; +} diff --git a/raspberryPi/.gitignore b/raspberryPi/.gitignore new file mode 100644 index 0000000..19e0180 --- /dev/null +++ b/raspberryPi/.gitignore @@ -0,0 +1 @@ +db/ diff --git a/raspberryPi/Dockerfile b/raspberryPi/Dockerfile new file mode 100644 index 0000000..0896ef5 --- /dev/null +++ b/raspberryPi/Dockerfile @@ -0,0 +1,10 @@ +FROM python:3.9.18 + +WORKDIR /usr/src/app + +COPY requirements.txt ./ +RUN pip install --no-cache-dir -r requirements.txt + +COPY src . + +CMD ["python", "./main.py"] diff --git a/raspberryPi/requirements.txt b/raspberryPi/requirements.txt new file mode 100644 index 0000000..6b6389a --- /dev/null +++ b/raspberryPi/requirements.txt @@ -0,0 +1,3 @@ +pyserial==3.5 +cobs==1.2.1 +flask==3.0.0 diff --git a/raspberryPi/src/.gitignore b/raspberryPi/src/.gitignore new file mode 100644 index 0000000..c18dd8d --- /dev/null +++ b/raspberryPi/src/.gitignore @@ -0,0 +1 @@ +__pycache__/ diff --git a/raspberryPi/src/main.py b/raspberryPi/src/main.py new file mode 100644 index 0000000..1154a0a --- /dev/null +++ b/raspberryPi/src/main.py @@ -0,0 +1,8 @@ +import threading +import reader +import website + +readThread = threading.Thread(target=reader.read) +websiteThread = threading.Thread(target=website.runWebsite) +readThread.start() +websiteThread.start() diff --git a/raspberryPi/src/reader.py b/raspberryPi/src/reader.py new file mode 100644 index 0000000..8417b42 --- /dev/null +++ b/raspberryPi/src/reader.py @@ -0,0 +1,80 @@ +import serial +import sqlite3 +from cobs import cobs +import time +import struct +from dataclasses import dataclass +from datetime import datetime + +@dataclass +class Sensor: + id: int + accuracy: float + resolution: float + +@dataclass +class DataPoint: + sensorId: int + value: float + +def ensureDatabaseExists(con: sqlite3.Connection) -> None: + cur = con.cursor() + cur.execute(""" + CREATE TABLE IF NOT EXISTS sensor( + sensorId INTEGER NOT NULL PRIMARY KEY, + resolution REAL NOT NULL, + accuracy REAL NOT NULL + ); + """) + + cur.execute(""" + CREATE TABLE IF NOT EXISTS dataPoint( + dataPointId INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + sensorFK INTEGER NOT NULL, + timestamp INTEGER NOT NULL, + value REAL NOT NULL, + FOREIGN KEY(sensorFK) REFERENCES sensor(sensorId) + ); + """) + +def ensureSensorExists(con: sqlite3.Connection, sensor: Sensor) -> None: + ensureDatabaseExists(con) + + cur = con.cursor() + res = cur.execute("SELECT COUNT(sensorId) FROM sensor WHERE sensorId = ?;", (sensor.id,)) + if(res.fetchone()[0] == 0): + cur.execute("INSERT INTO sensor(sensorId, resolution, accuracy) VALUES(?, ?, ?);", (sensor.id, sensor.resolution, sensor.accuracy)) + +def addDataPoint(con: sqlite3.Connection, dataPoint: DataPoint) -> bool: + ensureDatabaseExists(con) + + cur = con.cursor() + res = cur.execute("SELECT COUNT(sensorId) FROM sensor WHERE sensorId = ?;", (dataPoint.sensorId,)) + if(res.fetchone() == 0): + return False + cur.execute("INSERT INTO dataPoint(sensorFK, timestamp, value) VALUES(?, ?, ?);", (dataPoint.sensorId, round((datetime.utcnow() - datetime(1970, 1, 1)).total_seconds()), dataPoint.value)) + + return True + +def read() -> None: + con = sqlite3.connect("/data/specialIO.db") + with serial.Serial("/dev/ttyACM0", 9600) as s: + time.sleep(0.1) + s.reset_input_buffer() + + while True: + packet = cobs.decode(s.read_until(expected=b'\x00')[:-1]) + if(packet[0] == 0x01): + parsed = struct.unpack_from(' + + + + Sensoren grafiek + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/raspberryPi/src/website.py b/raspberryPi/src/website.py new file mode 100644 index 0000000..0e4a5cb --- /dev/null +++ b/raspberryPi/src/website.py @@ -0,0 +1,24 @@ +from flask import Flask +import sqlite3 + +app = Flask(__name__, static_folder='static') + +@app.route("/api/data//") +def getData(start: int, end: int) -> dict: + dbCon = sqlite3.connect("/data/specialIO.db") + cur = dbCon.cursor() + + retVal = {} + for sensor in cur.execute("SELECT sensorId, resolution, accuracy FROM sensor;").fetchall(): + retVal[sensor[0]] = {"resolution": sensor[1], "accuracy": sensor[2], "data":[]} + results = cur.execute("SELECT timestamp, value FROM dataPoint WHERE dataPoint.sensorFK = ? AND timestamp >= ? AND timestamp <= ?;", (sensor[0], start, end)).fetchall() + retVal[sensor[0]]["data"] = list(map(lambda x: {"time": x[0], "value": x[1]}, results)) + + return retVal + +@app.route("/") +def index(): + return app.send_static_file("index.html") + +def runWebsite() -> None: + app.run(host='0.0.0.0')