Update many stuff, see toDo

Took 9 seconds
This commit is contained in:
Tobias Hopp 2023-01-11 18:34:17 +01:00
parent 05fa75f63c
commit cb72920611
16 changed files with 2540 additions and 2103 deletions

26
ToDo.md
View File

@ -2,10 +2,10 @@
- [ ] Behälter unten rechts anzeigen
- [ ] Status für Netzwerk oben neben Uhrzeit
- [ ] Fix Fehler, wenn keine Getränke hinzugefügt worden sind
- [ ] Schriftarten Lokal machen
- [ ] ~~Fix Fehler, wenn keine Getränke hinzugefügt worden sind~~
- [x] Schriftarten Lokal machen
- [x] Probleme beim Laden der Container im Setup
- [ ] Container option "Auto tare" hinzufügen
- [ ] ~~Container option "Auto tare" hinzufügen~~
- Heißt, sobald ein Inhalt in einen Container eingestellt wird und das Volumen angegeben ist, werden die Sensoren auf den aktuellen Füllstand als 100% gesetzt
- [ ] Nach Speichern des Setups und bei Einmessen auf Schließen, Setup verlassen
@ -13,11 +13,17 @@
------
# New
- Gesamtvolumen des Containers komplett entfernen
- Bei Behälter aktualisieren wird das Volumen des neu zu stellendes Behälters eingestellt, welches dann als 100% interpretiert wird
- Im Setup kommt ein neues Feature, tarieren von Wägezelle (also der 0-Wert der Wägezelle) nach Speicherung, anstatt aktueller 2-Wege-Tarierung
- Ultraschallsensor wird entfernt
- Wenn Sensorik für Behälter vorhanden ist, nutze Wägezelle des Containers und messe anhand dessen Inhaltsmenge
- [X] Gesamtvolumen des Containers in fill status entfernen
- [X] Bei Behälter aktualisieren wird das Volumen des neu zu stellendes Behälters eingestellt, welches dann als 100% interpretiert wird
- [X] Im Setup kommt ein neues Feature, tarieren von Wägezelle (also der 0-Wert der Wägezelle) nach Speicherung, anstatt aktueller 2-Wege-Tarierung
- [X] Ultraschallsensor wird entfernt
- [X] Wenn Sensorik für Behälter vorhanden ist, nutze Wägezelle des Containers und messe anhand dessen Inhaltsmenge
- 1G=1ML
- Bei Einstellung neues Inhalts wird das Gewicht als ml übersetzt, danach wird Gewicht-Eingestellte Millitier gerechnet, das Ergebnis ist das Gewicht des Behälters
- Da nun generell eine Fehlermeldung erscheint, sobald eine Wägezelle inkorrekt läuft, muss vor dem Tarieren (also beim Drücken von Speichern) erst eine CHECK request gesendet werden, danach folgt dann bei erfolgreich die Tarierung
- [X] Bei Einstellung neues Inhalts wird das Gewicht als ml übersetzt, danach wird Gewicht-Eingestellte Millitier gerechnet, das Ergebnis ist das Gewicht des Behälters
- [ ] Da nun generell eine Fehlermeldung erscheint, sobald eine Wägezelle inkorrekt läuft, muss vor dem Tarieren (also beim Drücken von Speichern) erst eine CHECK request gesendet werden, danach folgt dann bei erfolgreich die Tarierung
- [ ] Unterstützung von Arduino
- [X] Programmierung der Schnittstelle für Arduino (Proxy)
- [ ] Programmierung des Arduino Codes
- [ ] Automatischer Flash
- [ ] Bei Container erstellung kann optional "Arduino Proxy" angewählt werden
- Danach werden die Daten dieses Containers über den Arduino bezogen

View File

@ -6,7 +6,7 @@ if [ "$EUID" -ne 0 ]; then
fi
echo "Creating user if not exists"
adduser itender || true
useradd -p $(openssl passwd -1 iTender2022) itender || true
echo "Updating indexes"
apt update
@ -52,6 +52,13 @@ echo "Installing mongodb and yarn..."
apt install nodejs yarn mongodb-org -y
apt upgrade -y
# V2: Arduino CLI
echo "Installing arduino-cli..."
sudo -u itender mkdir -p /home/itender/bin
sudo -u itender sh -c 'curl -fsSL https://raw.githubusercontent.com/arduino/arduino-cli/master/install.sh | BINDIR=/home/itender/bin/ sh'
sudo -u itender /home/itender/bin/arduino-cli config init
sudo -u itender /home/itender/bin/arduino-cli core update-index || true
echo "Installing autostart..."
# Autostart
cat <<EOT >/etc/xdg/openbox/autostart
@ -98,7 +105,8 @@ if [ -d "$DIR" ]; then
git pull
else
echo "Cloning..."
sudo git config --global credential.helper store || exit
sudo -u itender git config --global credential.helper store
git config --global credential.helper store
git clone "https://tobiash:!IwedwrimmVeudiweN!@git.gaminggeneration.de/tobiash/itender.git" --quiet
fi
cd "$DIR" || exit
@ -139,6 +147,7 @@ WantedBy=multi-user.target
EOT
#sh -c "git pull --quiet || true"
echo "Activating systemctl daemons..."
systemctl daemon-reload
systemctl enable mongod
@ -178,6 +187,7 @@ if ! grep -w "gpu_freq=700" /boot/config.txt; then
echo "gpu_freq=700" >>/boot/config.txt
fi
echo "Setting no-logo..."
systemctl disable getty@tty1.service
@ -191,7 +201,7 @@ if ! grep -w "logo.nologo" /boot/cmdline.txt; then
sed -i 's/console=tty0/console=tty3/' /boot/cmdline.txt
fi
echo "iTender 2022
echo "iTender© 2022-2023
Programmed by Tobias Hopp" >/etc/motd
echo "[Service]
@ -201,4 +211,6 @@ chown itender:itender -R /home/itender/
adduser itender gpio
adduser itender sudo
echo "Installation finished!"
reboot now

81
getSerialList.js Normal file
View File

@ -0,0 +1,81 @@
const { SerialPort } = require('serialport');
const { ReadlineParser } = require('@serialport/parser-readline');
( async( ) => {
console.log( await SerialPort.list() );
let port = new SerialPort( { path: '/dev/ttyS11', baudRate: 9600 } );
/*port.on('data', function (data) {
console.log('Data:', data.toString())
})*/
port.close()
let callbacks = {};
function request()
{
return new Promise((resolve, reject) => {
let id = makeid(8);
console.log(id);
let req = {
id: id,
type: "REQUEST",
data: "1,2"
}
callbacks[req.id] = resolve;
let done = false;
setTimeout( () => {
if( !done )
reject("Request with id " + id + " timed out");
}, 15000 );
port.write(JSON.stringify(req) + "\r", "utf-8");
});
}
const parser = port.pipe(new ReadlineParser({ delimiter: '\r' }))
parser.on('data', (data) => {
/*let req = {
id: id,
type: "ACK",
data: "1,2"
}*/
data = data.toString().trim();
let json = JSON.parse(data);
console.log("GOT: " + data );
callbacks[json.id]();
});
try {
await request();
console.log("FUNCTION RESOLVED YAY!");
} catch( e )
{
console.error(e);
}
} )();
function makeid(length) {
var result = '';
var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
var charactersLength = characters.length;
for ( var i = 0; i < length; i++ ) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return result;
}

View File

@ -13,6 +13,7 @@
"watchWP": "webpack --watch"
},
"dependencies": {
"@serialport/parser-readline": "^10.5.0",
"@types/cookie-parser": "^1.4.3",
"@types/debug": "^4.1.7",
"@types/express": "^4.17.14",
@ -22,6 +23,7 @@
"@types/node": "^18.11.9",
"@types/rpi-gpio": "^2.1.1",
"@types/rpi-ws281x-native": "^1.0.0",
"@types/serialport": "^8.0.2",
"axios": "^1.2.0",
"buffer": "^6.0.3",
"cookie-parser": "^1.4.6",
@ -37,7 +39,8 @@
"onoff": "^6.0.3",
"pug": "2.0.0-beta11",
"rpi-gpio": "^2.1.7",
"rpi-ws281x-native": "^1.0.4"
"rpi-ws281x-native": "^1.0.4",
"serialport": "^10.5.0"
},
"devDependencies": {
"nodemon": "^2.0.20",

93
src/ArduinoProxy.ts Normal file
View File

@ -0,0 +1,93 @@
import SerialPort from "serialport";
import debug from "debug";
import {ArduinoProxyPayload} from "./ArduinoProxyPayload";
import {Utils} from "./Utils";
import {ReadlineParser} from "@serialport/parser-readline";
const log = debug("itender:arduinoProxy");
export class ArduinoProxy {
private static serialPort;
private static callbacks: Record<string, { resolve: Function, reject: Function }> = {};
private static encoding: string = "utf-8";
private static onData(data) {
data = data.toString().trim();
try {
let json = JSON.parse(data) as ArduinoProxyPayload;
if (this.callbacks[json.id]) {
this.callbacks[json.id].resolve(json);
delete this.callbacks[json.id];
log("Answered request " + json.id);
} else {
log("ERROR - Got an response from arduino but we are not waiting for it?");
}
} catch (e) {
log("ERROR - Got an invalid response from arduino?");
}
}
public static connect() {
return new Promise<void>(async (resolve, reject) => {
// @ts-ignore
let list = await SerialPort.list()
let arduino = list.find((ele) => {
return ele.manufacturer == "Arduino";
});
if (!arduino) {
return reject("No arduino found");
}
// @ts-ignore
this.serialPort = new SerialPort({
path: arduino.path,
baudRate: 9600,
autoOpen: false,
});
this.serialPort.open((err: Error | null | undefined) => {
if (err) {
log("Error whilst connecting to proxy (open serial-connection)");
log(err.name + "\n" + err.message + "\n" + err.stack);
return reject(err.name);
}
// @ts-ignore
const parser = this.serialPort.pipe(new ReadlineParser({delimiter: '\r'}));
parser.on('data', (data) => this.onData(data));
resolve();
});
});
}
public static disconnect() {
if (this.serialPort.isOpen) {
this.serialPort.close((err) => {
this.serialPort = null;
});
}
}
public static sendRequest(request: ArduinoProxyPayload, timeout: number = 1000) {
return new Promise<ArduinoProxyPayload>((resolve, reject) => {
let id = Utils.generateRandomString(8);
request.id = id;
this.callbacks[id] = {resolve: resolve, reject: reject};
this.serialPort.write(JSON.stringify(request), ArduinoProxy.encoding, (err: Error | null | undefined) => {
if (err) {
reject("I/O error on request " + id);
}
});
setTimeout(() => {
reject("Timeout on request " + id);
delete this.callbacks[id];
}, timeout);
});
}
}

View File

@ -0,0 +1,35 @@
import {ArduinoProxyPayloadType} from "./ArduinoProxyPayloadType";
import {ArduinoProxy} from "./ArduinoProxy";
export class ArduinoProxyPayload {
set id(value: string) {
this._id = value;
}
private readonly _type: ArduinoProxyPayloadType;
private readonly _data: any;
private _id: string ="";
constructor(type: ArduinoProxyPayloadType, data: any) {
this._type = type;
this._data = data;
}
get type(): ArduinoProxyPayloadType {
return this._type;
}
get data(): any {
return this._data;
}
get id(): string {
return this._id;
}
public send() {
return ArduinoProxy.sendRequest(this);
}
}

View File

@ -0,0 +1,7 @@
export enum ArduinoProxyPayloadType {
ACK="ACK",
SET_PIN="SET_PIN",
GET_VAL="GET_VAL",
GET_SENSOR="GET_SENSOR",
RESTART="RESTART",
}

View File

View File

@ -5,5 +5,7 @@ export enum RequestType {
JOB = "JOB",
STARTFILL = "STARTFILL",
STOPFILL = "STOPFILL",
DOWNLOAD_DRINKS = "DOWNLOAD_DRINKS"
DOWNLOAD_DRINKS = "DOWNLOAD_DRINKS",
TARE = "TARE",
CHECK = "CHECK",
}

View File

@ -84,4 +84,14 @@ export class Utils {
}
static generateRandomString(number: number) {
let result = '';
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
const charactersLength = characters.length;
for (let i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return result;
}
}

View File

@ -13,6 +13,7 @@ export const ContainerSchema = new Mongoose.Schema<IContainer>({
content: {type: mongoose.Types.ObjectId, ref: "Ingredient"},
sensorDelta: Number, // V2: Now sensorDelta - Differenz, welche beim Einstellen der Zutat aus Gewicht(Sensor) - Volumen errechnet wird
sensorTare: Number, // V2: Now sensorTare
sensorProxy: {type: Boolean, default: false},
filled: Number,
enabled: {type: Boolean, default: false},
});

View File

@ -12,6 +12,7 @@ export interface IContainer extends mongoose.Document {
sensorType: SensorType;
sensorPin1: number;
sensorPin2: number;
sensorProxy: boolean
rawData: number;
pumpPin: number;
filled: number;

View File

@ -470,13 +470,31 @@ export class iTender {
});
}
public static clearAllRawMeasurements()
{
return new Promise<void>(async (resolve, reject) => {
for (let c of (await Container.find({}))) {
if (c.sensorType != SensorType.NONE) {
c.rawData = -1;
await c.save();
}
}
resolve();
})
}
public static measureAllRaw() {
return new Promise<void>(async (resolve, reject) => {
for (let c of (await Container.find({}))) {
if (c.sensorType != SensorType.NONE) {
let weight = SensorHelper.measure(c);
if (weight == null) {
let weight : number | null = c.rawData;
if( !c.sensorProxy )
{
// Check values
weight = SensorHelper.measure(c);
}
if (weight == null || weight > 1000 || weight < 0 ) { //fixme werte
// Problem erkannt!
return reject("Fehler Sensor (" + c.sensorPin1 + ", " + c.sensorPin2 + ") - Container " + c.slot + 1);
}

View File

@ -157,6 +157,37 @@ router.ws('/', async (ws, req, next) => {
WebSocketHandler.answerRequest(msg.data["type"] as RequestType, "ok");
break;
}
case RequestType.CHECK: {
await iTender.clearAllRawMeasurements();
let content : {error: boolean, msg: string} = {
error: false,
msg: ""
};
// Check config
/// Check Proxy
if( Settings.get("arduino_proxy_enabled") == true )
{
}
// Check measurements
await iTender.measureAllRaw();
for( let c of await Container.find() )
{
if( c.sensorType != SensorType.NONE && c.rawData == -1 )
{
content.error = true;
content.msg = "Container " + (c.slot+1) + " weist Fehler im Sensor auf.<br>Überprüfe die Einstellungen der Sensoren-Pins.";
return WebSocketHandler.answerRequest(msg.data["type"] as RequestType, content);
}
}
break;
}
case RequestType.TARE: {
let type = msg.data["type"];
// Start TARE

View File

@ -21,6 +21,9 @@ block setup
div.inputGroup
label(onclick="document.getElementById('hotspotCheckbox').checked = !document.getElementById('hotspotCheckbox').checked;") Ohne WiFi Hotspot aktivieren
input#hotspotCheckbox.input(type="checkbox")
div.inputGroup
label(onclick="document.getElementById('proxyCheckbox').checked = !document.getElementById('proxyCheckbox').checked;") Arduino Mega als Proxy erlauben
input#proxyCheckbox.input(type="checkbox")
div#setupContainersDiv
@ -52,6 +55,7 @@ block menu
block settings
// Settings
button.btn.btn-primary#settings_refreshDrinks Getränke herunterladen
button.btn.btn-primary#settings_update System aktualisieren

4305
yarn.lock

File diff suppressed because it is too large Load Diff