Table of Contents
Arduino
Hier befindet sich das gesamte Arduino-Projekt, zum iTender Proxy.
Full-Code
/**
* Official proxy code for iTender GPIO Communication
**/
#include <ArduinoJson.h>
#include "HX711.h"
// Define the size of the JSON buffer
#define JSON_BUFFER_SIZE 256
// Create a JSON object for incoming messages
StaticJsonDocument<JSON_BUFFER_SIZE> incomingJson;
// Create a JSON object for outgoing messages
DynamicJsonDocument outgoingJson(JSON_BUFFER_SIZE);
void setup() {
// Initialize serial communication
Serial.begin(9600);
}
void (*resetFunc)(void) = 0; //declare reset function @ address 0
void loop() {
// Wait for a new line on the serial console
if (Serial.available()) {
// Read the incoming JSON message
DeserializationError error = deserializeJson(incomingJson, Serial);
if (error) {
// Handle error
} else {
// Extract the "type" and "data" fields from the JSON object
String id = incomingJson["id"];
int type = incomingJson["type"];
JsonVariant data = incomingJson["data"];
// Create a nested object in the root object
JsonObject outgoingData = outgoingJson.to<JsonObject>().createNestedObject("data");
outgoingData["success"] = true;
// Handle the message based on the "type" field
switch (type) {
case 1:
// Handle SET_PIN message
pinMode((int)data["pin"], OUTPUT);
if (data["mode"] == "DIGITAL") {
digitalWrite((int)data["pin"], data["value"]);
} else {
analogWrite((int)data["pin"], (data["value"] == 255) ? HIGH : LOW);
}
break;
case 2:
// Handle GET_VAL message
pinMode((int)data["pin"], INPUT);
int val;
if (data["mode"] == "DIGITAL") {
val = digitalRead((int)data["pin"]);
} else {
val = analogRead((int)data["pin"]);
}
break;
case 3:
// Handle GET_SENSOR message
/*
(WEIGHT_VAL - NO_WEIGHT_VAL) / Sensitivitätsfaktor = Gewicht in Gramm
(WEIGHT_VAL - NO_WEIGHT_VAL) / (100g_val /100) ist die Formel zur Berechnung des Gewichts in Gramm nach der Kalibrierung mit einem 100 Gramm Gewicht.
100g_val /100 gibt den Sensitivitätsfaktor an, der angibt, wie viel sich der Sensorwert ändert, wenn sich das Gewicht um ein Gramm ändert.
Durch die Division von 100g_val durch 100 wird der Sensitivitätsfaktor berechnet, und durch die Division von (WEIGHT_VAL - NO_WEIGHT_VAL) durch den Sensitivitätsfaktor wird das Gewicht in Gramm berechnet.
Beispiel:
(WEIGHT_VAL - NO_WEIGHT_VAL) / (100g_val /100) = Gewicht in Gramm
(2400 - 2000) / (2450 /100) = 80 Gramm
*/
// HX711 circuit wiring
const int LOADCELL_DOUT_PIN = (int) data["pin_dout"];
const int LOADCELL_SCK_PIN = (int) data["pin_sck"];
HX711 scale;
scale.begin(LOADCELL_DOUT_PIN, LOADCELL_SCK_PIN);
// Get the weight value from the scale
long weight_val = scale.get_units();
outgoingData["value"] = weight_val;
break;
case 4:
resetFunc(); //call reset
break;
default:
// Handle unknown message type
outgoingData[""] = 0;
break;
}
// Prepare the outgoing JSON message
outgoingJson["id"] = id;
outgoingJson["type"] = 0;
outgoingJson["data"] = outgoingData;
// Send the outgoing JSON message
serializeJson(outgoingJson, Serial);
}
}
}
Das hier ist erstmal der gesamte Code.
Einzelerklärung
#include <ArduinoJson.h>
#include "HX711.h"
Dieser Teil importiert erstmal die Bibliotheken ArduinoJson und HX711.
ArduinoJson wird für den Austausch von Anfragen zwischen dem Arduino und dem Raspberry Pi über die Serielle-Verbindung genutzt.
HX711 ist die Bibliothek für die Wägezelle, welche verwendet wird um das Gewicht in gramm auszurechnen.
// Define the size of the JSON buffer
#define JSON_BUFFER_SIZE 256
// Create a JSON object for incoming messages
StaticJsonDocument<JSON_BUFFER_SIZE> incomingJson;
// Create a JSON object for outgoing messages
DynamicJsonDocument outgoingJson(JSON_BUFFER_SIZE);
Hier wird die Buffer-Größe definiert, welche angibt wie groß das JsonDocument sein kann, welches zwischen Raspberry Pi kommuniziert.
Die größe liegt hier bei 256-Bytes, welche derzeit ausreicht um die Anfragen vom Pi zu verarbeiten und zu beantworten.
Danach erstellen wir ein StaticJsonDocument-Objekt incomingJson
, welches das eingehende JSON verarbeiten soll. Hier geben wir mit <> unsere Buffer-Größe an.
Zuletzt gibt es das DynamicJsonDocument outgoingJson
, welches das ausgehende Json enthält.
void setup() {
// Initialize serial communication
Serial.begin(9600);
}
void (*resetFunc)(void) = 0; //declare reset function @ address 0
Hier befinden sich zwei Funktionen. Zum einen die void setup()
-Methode, welche ausgeführt wird, sobald der Arduino startet, zum anderen die eher merkwürdig aussehende void (*resetFunc)...
-Methode.
In der setup()
Methode haben wir nur die initalisierung der Seriellen Verbindung auf 9600-Baud. 9600 Baud gibt die Gewschwindigkeit der Übertragung zwischen Pi und Arduino an. Wir testen erstmal mit 9600, können aber bis 115200 erweitern.
Die resetFunc
dient zum Neustart des Arduinos per Software. Es gibt leider keine restart Funktion oder ähnliches, weshalb wir uns hier das Prinzip eines Sektor-Schreibens zu nutze machen und eine Funktion auf Adresse 0 im Speicher des Arduinos liegen.
Er stürzt quasi kontrolliert ab und startet neu.
Danach beginnen wir den Loop, welcher automatisch ausgeführt wird, sobald der Arduino gestartet ist
void loop() {
if (Serial.available()) {
// Read the incoming JSON message
DeserializationError error = deserializeJson(incomingJson, Serial);
if (error) {
// Handle error
} else {
// Extract the "type" and "data" fields from the JSON object
String id = incomingJson["id"];
int type = incomingJson["type"];
JsonVariant data = incomingJson["data"];
// Create a nested object in the root object
JsonObject outgoingData = outgoingJson.to<JsonObject>().createNestedObject("data");
Hier wird zuerst geprüft, ob Serielle Daten vorliegen.
Sollten Sie vorliegen "Deserialisieren" wir die JSON-Daten, welche gerade über die Serielle Verbindung gesendet werden.
Sollte es dabei einen Fehler geben, können wir diesen abfangen.
Derzeit würde die Anfrage vom Raspberry Pi -> Arduino einfach nach einer Sekunde eine Zeitüberschreitung erhalten, was im Prinzip das selbe ist wie, Arduino hat nicht geantwortet oder etwas stimmt nicht.
Falls erfolgreich speichern wir uns die Anfrage-ID, den Anfrage-Typ sowie ein data-Objekt in Typ JsonVariant
.
Die ID ist die Anfrage-ID, welche für unsere Antwort verwendet werden muss. Ansonsten kann Antwort und Anfrage nicht zugeordnet werden.
Der Type ist der Typ der Anfrage. Es gibt GET_PIN, SET_PIN, GET_SENSOR und RESTART.
Danach wird ein outgoingData Objekt erzeigt, welches dazu dient, die ausgehenden Daten zu speichern und zu setzen.
// Handle the message based on the "type" field
switch (type) {
case 1:
// Handle SET_PIN message
pinMode((int)data["pin"], OUTPUT);
if (data["mode"] == "DIGITAL") {
digitalWrite((int)data["pin"], data["value"]);
} else {
analogWrite((int)data["pin"], (data["value"] == 255) ? HIGH : LOW);
}
break;
case 2:
// Handle GET_VAL message
pinMode((int)data["pin"], INPUT);
int val;
if (data["mode"] == "DIGITAL") {
val = digitalRead((int)data["pin"]);
} else {
val = analogRead((int)data["pin"]);
}
break;
case 3:
// Handle GET_SENSOR message
/*
(WEIGHT_VAL - NO_WEIGHT_VAL) / Sensitivitätsfaktor = Gewicht in Gramm
(WEIGHT_VAL - NO_WEIGHT_VAL) / (100g_val /100) ist die Formel zur Berechnung des Gewichts in Gramm nach der Kalibrierung mit einem 100 Gramm Gewicht.
100g_val /100 gibt den Sensitivitätsfaktor an, der angibt, wie viel sich der Sensorwert ändert, wenn sich das Gewicht um ein Gramm ändert.
Durch die Division von 100g_val durch 100 wird der Sensitivitätsfaktor berechnet, und durch die Division von (WEIGHT_VAL - NO_WEIGHT_VAL) durch den Sensitivitätsfaktor wird das Gewicht in Gramm berechnet.
Beispiel:
(WEIGHT_VAL - NO_WEIGHT_VAL) / (100g_val /100) = Gewicht in Gramm
(2400 - 2000) / (2450 /100) = 80 Gramm
*/
// HX711 circuit wiring
const int LOADCELL_DOUT_PIN = (int)data["pin_dout"];
const int LOADCELL_SCK_PIN = (int)data["pin_sck"];
HX711 scale;
scale.begin(LOADCELL_DOUT_PIN, LOADCELL_SCK_PIN);
// Get the weight value from the scale
long weight_val = scale.get_units();
outgoingData["value"] = weight_val;
break;
case 4:
resetFunc(); //call reset
break;
default:
// Handle unknown message type
outgoingData[""] = 0;
break;
}
Es wird gestartet mit einem Switch-Statement.
Hier finden sich die einzelnen Types wieder, welche anstatt beispielsweise ENUM's wie GET_VALUE in Arduino nur Integer sind, welche diese repräsentieren.
In dem case 1
(SET_PIN) befindet sich der Code um einen Arduino-Pin Digital und Analog auf einen Wert (data["value"] zu setzen.
In case 2
(GET_PIN) wird ein Arduino-Pin Digital oder Analog ausgelesen.
Bei case 3
(GET_SENSOR) wird ein HX711-Sensor abgefragt.
In die Anfrage-Daten kommen als Parameter pin_dout
sowie pin_sck
mit, danach wird ein Wagen-Objekt initalisiert und danach versucht auszulesen.
Bei case 4
(RESTART) soll ein Reset des Arduinos ausgeführt werden. Wie oben erklärt wird die resetFunc
ausgeführt.
Default sendet einfach leere Daten als Fehler zurück.
// Prepare the outgoing JSON message
outgoingJson["id"] = id;
outgoingJson["type"] = 0;
outgoingJson["data"] = outgoingData;
// Send the outgoing JSON message
serializeJson(outgoingJson, Serial);
}
Zuletzt wird die outgoingJson-ID, -Type sowie die Daten gesetzt.
Das JSON wird nun serialized, also in Text-Gewandelt und dann über das Serial-Objekt gesendet.
}
}