Hier ein weiteres Projekt von mir auf Basis eines Arduino Nano/Uno. Es können bis zu 8 Drucksensoren ausgelesen werden, vorerst mal mit den 0,5 MPa und 1,2 MPa Flüssigkeitsdrucksensoren. Später möchte ich noch Gasdrucksensoren hinzufügen um die drücke meiner Wärmepumpe auslesen zu können. Die Messung funktioniert soweit mal.
Screenshot 2024-04-02 143857.png
20240402_144038.jpg
Nun folgt noch die Einbindung in Homematic Wired. Finde es total spannend da ich absolut keine Programmier-Skills habe und alles bisher aus anderen Projekten zusammenkopiert und mit ChatGPT verwirklicht habe.
#include <Arduino.h>
// Pin-Arrays für die Drucksensoren
const byte SENSOR_EN_PINS[] = {5, 6, 7, 8, 9, 10, 11, 12}; // VCC-Pins der Sensoren
const byte SENSOR_PINS[] = {A0, A1, A2, A3, A4, A5, A6, A7}; // Analog-Pins der Sensoren
const byte NUM_SENSORS = sizeof(SENSOR_EN_PINS) / sizeof(SENSOR_EN_PINS[0]); // Anzahl der Sensoren
// Analogwert, der dem analogen Steckplatz entspricht
#define ANALOG_SOCKET_VALUE 110
// Drucksensor-Typen
enum PressureSensorTypes {
MPA_1_2,
MPA_0_5
};
// Minimale Spannung, die darauf hinweist, dass kein Sensor angeschlossen ist
#define NO_SENSOR_VOLTAGE 0.1 // Beispielwert, anpassen je nach Anwendung
// Funktion zur Durchführung einer Druckmessung
float measurePressure(uint8_t sensorIndex, uint8_t sensorType) {
digitalWrite(SENSOR_EN_PINS[sensorIndex], HIGH);
delay(500);
uint16_t sensorValue = 0;
for (uint8_t i = 0; i < 10; i++) {
sensorValue += analogRead(SENSOR_PINS[sensorIndex]);
delay(5);
}
sensorValue = sensorValue / 10;
digitalWrite(SENSOR_EN_PINS[sensorIndex], LOW);
float sensorFactor = 0.75; // Standardfaktor
switch (sensorType) {
case MPA_1_2:
sensorFactor = 0.75;
break;
case MPA_0_5:
sensorFactor = 1.6;
break;
default:
break;
}
float pressure = (((sensorValue / 1024.0) - (ANALOG_SOCKET_VALUE / 1000.0)) / sensorFactor) * 1000;
return pressure > 0 ? pressure : 0;
}
// Funktion zur Überprüfung, ob ein Sensor angeschlossen ist
bool isSensorConnected(byte sensorPin) {
float voltage = analogRead(sensorPin) * (5.0 / 1023.0); // 5.0 ist die Referenzspannung
return voltage > NO_SENSOR_VOLTAGE;
}
void setup() {
Serial.begin(9600);
// Setze alle VCC-Pins als Ausgänge
for (uint8_t i = 0; i < NUM_SENSORS; i++) {
pinMode(SENSOR_EN_PINS[i], OUTPUT);
}
}
void loop() {
// Überprüfung für jeden Sensor durchführen und ausgeben
for (uint8_t i = 0; i < NUM_SENSORS; i++) {
if (isSensorConnected(SENSOR_PINS[i])) {
// Wenn ein Sensor angeschlossen ist, den Druck messen und ausgeben
float pressure = measurePressure(i, MPA_1_2); // Beispiel: MPA_1_2 als Drucksensortyp
Serial.print("Sensor ");
Serial.print(i + 1);
Serial.print(" - Druck (mBar): ");
Serial.println(pressure * 10); // Ausgabe des Drucks in mBar
} else {
// Wenn kein Sensor angeschlossen ist, entsprechende Meldung ausgeben
Serial.print("Sensor ");
Serial.print(i + 1);
Serial.println(" - nicht erkannt");
}
delay(1000); // Verzögerung zwischen den Messungen
}
}
Hier mal der aktuelle Sketch. Ist noch nicht ganz final.
//*******************************************************************
//
// HBW-Sen-PRESS
//
// Homematic Wired Hombrew Hardware
// Arduino NANO als Homematic-Device
//
// nach einer Vorlage von
// Thorsten Pferdekaemper (thorsten@pferdekaemper.com), Dirk Hoffmann (hoffmann@vmd-jena.de)
//
// 2024-04-02 maxx3105 Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/at/
//
//*******************************************************************
// Changes
// v0.01
// - initial version
//
#define HARDWARE_VERSION 0x01
#define FIRMWARE_VERSION 0x0001
#define HMW_DEVICETYPE 0xAB //device ID (make sure to import hbw_sen_press.xml into FHEM or install Addon into CCU3/RaspberryMatic)
#define NUMBER_OF_SEN_CHAN 8 // input channels
//#define USE_HARDWARE_SERIAL // use hardware serial (USART) for final device - this disables debug output
/* Undefine "HBW_DEBUG" in 'HBWired.h' to remove code not needed. "HBW_DEBUG" also works as master switch,
* as hbwdebug() or hbwdebughex() used in channels will point to empty functions. */
#include <Arduino.h>
#include <HBWired.h>
#include <HBWAnalogIn.h>
// Pins
#ifdef USE_HARDWARE_SERIAL
#define RS485_TXEN 2 // Transmit-Enable
#define BUTTON 8 // Button fuer Factory-Reset etc.
#else
#define RS485_RXD 4
#define RS485_TXD 2
#define RS485_TXEN 3 // Transmit-Enable
#define BUTTON 8 // Button fuer Factory-Reset etc.
#include "FreeRam.h"
#include <HBWSoftwareSerial.h>
HBWSoftwareSerial rs485(RS485_RXD, RS485_TXD); // RX, TX
#endif //USE_HARDWARE_SERIAL
#define LED LED_BUILTIN // Signal-LED
#define NUMBER_OF_CHAN NUMBER_OF_SEN_CHAN
struct hbw_config {
uint8_t logging_time; // 0x01
uint32_t central_address; // 0x02 - 0x05
uint8_t direct_link_deactivate:1; // 0x06:0
uint8_t :7; // 0x06:1-7
// hbw_config_sensor SensorCfg[NUMBER_OF_SEN_CHAN]; // 0x07 - 0x..(address step 9)} hbwconfig;
} hbwconfig;
// Pin-Arrays für die Drucksensoren
byte SENSOR_EN_PINS[] = {5, 6, 7, 9, 10, 11, 12, 13}; // VCC-Pins der Sensoren
byte SENSOR_PINS[] = {A0, A1, A2, A3, A4, A5, A6, A7}; // Analog-Pins der Sensoren
#define ANALOG_SOCKET_VALUE 100 // Hier 100 durch den entsprechenden Wert ersetzen
// Konfiguration für die HBWAnalogIn-Kanäle
hbw_config_analog_in analogInConfigs[NUMBER_OF_CHAN];
// Drucksensor-Typen für jeden Kanal festlegen
enum PressureSensorTypes {
MPA_1_2,
MPA_0_5
};
// Konfigurationsklasse für jeden Kanal
class ChannelConfig {
private:
PressureSensorTypes sensorType;
public:
void setSensorType(PressureSensorTypes type) {
sensorType = type;
}
PressureSensorTypes getSensorType() const {
return sensorType;
}
};
// Array für die Konfiguration jedes Kanals
ChannelConfig channelConfigs[NUMBER_OF_CHAN];
// Minimale Spannung, die darauf hinweist, dass kein Sensor angeschlossen ist
#define NO_SENSOR_VOLTAGE 0.1 // Beispielwert, anpassen je nach Anwendung
// HBWDevice-Objekt erstellen
HBWDevice* device = NULL;
// Initialisierung der Konfiguration für jeden Kanal
void setupChannelConfigs() {
for (uint8_t i = 0; i < NUMBER_OF_CHAN; i++) {
channelConfigs[i].setSensorType(MPA_1_2); // Beispiel: Standardmäßig MPA_1_2
}
}
// Drucksensor-Typen für jeden Kanal festlegen
void setChannelSensorTypes() {
// Hier können Sie die Drucksensortypen für jeden Kanal festlegen
// Zum Beispiel: channelConfigs[0].setSensorType(MPA_1_2);
}
void setup() {
#ifdef USE_HARDWARE_SERIAL
Serial.begin(19200, SERIAL_8E1);
device = new HBWDevice(HMW_DEVICETYPE, HARDWARE_VERSION, FIRMWARE_VERSION,
&Serial, RS485_TXEN, sizeof(analogInConfigs) / sizeof(hbw_config_analog_in),
NUMBER_OF_CHAN, NULL, NULL, NULL);
#else
Serial.begin(115200); // Serial->USB for debug
rs485.begin(19200); // RS485 via SoftwareSerial, must use 19200 baud!
device = new HBWDevice(HMW_DEVICETYPE, HARDWARE_VERSION, FIRMWARE_VERSION,
&rs485, RS485_TXEN, sizeof(analogInConfigs) / sizeof(hbw_config_analog_in),
NUMBER_OF_CHAN, &Serial, NULL, NULL);
device->setConfigPins(BUTTON, LED); // 8 (button) and 13 (led) is the default
hbwdebug(F("B: 2A "));
hbwdebug(freeRam());
hbwdebug(F("\n"));
#endif
}
void loop() {
for (uint8_t i = 0; i < NUMBER_OF_CHAN; i++) {
if (isSensorConnected(SENSOR_PINS[i])) {
float pressure = measurePressure(i, channelConfigs[i].getSensorType());
// Konvertiere den Druck in eine Ganzzahl, wie von Homematic Wired erwartet
int pressureValue = pressure * 10; // Multipliziere mit 10, um eine Ganzzahl zu erhalten
// Stellen Sie sicher, dass das device-Objekt initialisiert wurde, bevor Sie darauf zugreifen
if (device != NULL) {
uint8_t channel = i + 1;
uint8_t data[] = {pressureValue};
device->sendInfoMessage(channel, sizeof(data), data);
} else {
#ifndef USE_HARDWARE_SERIAL
Serial.println("Device nicht initialisiert!");
#endif
}
#ifndef USE_HARDWARE_SERIAL
Serial.print("Sensor ");
Serial.print(i + 1);
Serial.print(" - Druck (mBar): ");
Serial.println(pressure * 10);
#endif
} else {
#ifndef USE_HARDWARE_SERIAL
Serial.print("Sensor ");
Serial.print(i + 1);
Serial.println(" - nicht erkannt");
#endif
}
delay(1000);
}
}
float measurePressure(uint8_t sensorIndex, PressureSensorTypes sensorType) {
digitalWrite(SENSOR_EN_PINS[sensorIndex], HIGH);
delay(500);
uint16_t sensorValue = 0;
for (uint8_t i = 0; i < 10; i++) {
sensorValue += analogRead(SENSOR_PINS[sensorIndex]);
delay(5);
}
sensorValue = sensorValue / 10;
digitalWrite(SENSOR_EN_PINS[sensorIndex], LOW);
float sensorFactor = 0.75; // Standardfaktor
switch (sensorType) {
case MPA_1_2:
sensorFactor = 0.75;
break;
case MPA_0_5:
sensorFactor = 1.6;
break;
default:
break;
}
float pressure = (((sensorValue / 1024.0) - (ANALOG_SOCKET_VALUE / 1000.0)) / sensorFactor) * 1000;
return pressure > 0 ? pressure : 0;
}
bool isSensorConnected(byte sensorPin) {
float voltage = analogRead(sensorPin) * (5.0 / 1023.0); // 5.0 ist die Referenzspannung
return voltage > NO_SENSOR_VOLTAGE;
}
Soll die Messwertübertragung bei Änderung des Drucks oder im Intervall erfolgen?
Lg Markus
Hi Markus,
da diese Drucksensoren eigentlich nichts anderes als analoge Eingänge sind, würde ich das Verhalten / Konfigurationsmöglichkeiten wie in HBWAnalogIn.h implementieren, d.h.
uint8_t send_delta_value; // Differenz des Messwerts, ab dem gesendet wird
uint16_t send_max_interval; // Maximum-Sendeintervall
uint8_t send_min_interval; // Minimum-Sendeintervall (factor 10, range 10 to 2540 seconds)..... oder uint16_t wie bei send_max_interval
HBWAnalogIn gibt dir auch eine gute Struktur für eine HBWAnalogPRESS... Klasse vor. HBWAnalogIn::loop() müsste dann angepasst werden, um den Drucksensor einzuschalten und den analogRead() Wert enspr. umzurechnen.
Das setzen von Standardwerten z.b. PressureSensorTypes käme in afterReadConfig(). Wenn du uint8_t PressureSensorTypes:2 dem config "struct hbw_config_analog_..." hinzufügst und den max. Wert als Standardwert nimmst, brauchst du auch keine extra Funktion.
delay() solltest du gar nicht verwenden, das würde die Buskommunikation unterbrechen
Gruß,
Thomas
So hier die Anpassung
//*******************************************************************
//
// HBW-Sen-PRESS
//
// Homematic Wired Hombrew Hardware
// Arduino NANO als Homematic-Device
//
// nach einer Vorlage von
// Thorsten Pferdekaemper (thorsten@pferdekaemper.com), Dirk Hoffmann (hoffmann@vmd-jena.de)
//
// 2024-04-02 maxx3105 Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/at/
//
//*******************************************************************
// Changes
// v0.01
// - initial version
//
#define HARDWARE_VERSION 0x01
#define FIRMWARE_VERSION 0x0001
#define HMW_DEVICETYPE 0xAB //device ID (make sure to import hbw_sen_press.xml into FHEM or install Addon into CCU3/RaspberryMatic)
#define NUMBER_OF_CHAN 4 // input channels
//#define USE_HARDWARE_SERIAL // use hardware serial (USART) for final device - this disables debug output
/* Undefine "HBW_DEBUG" in 'HBWired.h' to remove code not needed. "HBW_DEBUG" also works as master switch,
* as hbwdebug() or hbwdebughex() used in channels will point to empty functions. */
#include <Arduino.h>
#include <HBWired.h>
#include "HBWAnalogPRESS.h"
// Pins
#ifdef USE_HARDWARE_SERIAL
#define RS485_TXEN 2 // Transmit-Enable
#define BUTTON 8 // Button fuer Factory-Reset etc.
#else
#define RS485_RXD 4
#define RS485_TXD 2
#define RS485_TXEN 3 // Transmit-Enable
#define BUTTON 8 // Button fuer Factory-Reset etc.
#define DEBUG_OUTPUT
#include "FreeRam.h"
#include <HBWSoftwareSerial.h>
HBWSoftwareSerial rs485(RS485_RXD, RS485_TXD); // RX, TX
#endif //USE_HARDWARE_SERIAL
#define LED LED_BUILTIN // Signal-LED
#define ANALOG_SOCKET_VALUE 110
struct hbw_config {
uint8_t logging_time; // 0x01
uint32_t central_address; // 0x02 - 0x05
uint8_t direct_link_deactivate:1; // 0x06:0
uint8_t :7; // 0x06:1-7
hbw_config_analog_press analogPressConfigs[NUMBER_OF_CHAN]; // 0x07 - 0x..(address step 9)} hbwconfig;
} hbwconfig;
// Pin-Arrays für die Drucksensoren
byte SENSOR_EN_PINS[] = {5, 6, 7, 9}; // VCC-Pins der Sensoren
byte SENSOR_PINS[] = {A0, A1, A2, A4}; // Analog-Pins der Sensoren
HBWDevice* device = NULL;
// Konfiguration für die HBWAnalogPRESS-Kanäle
HBWAnalogPRESS* channels[NUMBER_OF_CHAN];
void setup() {
pinMode(SENSOR_EN_PINS[0], OUTPUT);
digitalWrite(SENSOR_EN_PINS[0], LOW);
// Konfigurationen für die Drucksensorkanäle
for (int i = 0; i < NUMBER_OF_CHAN; i++) {
hbwconfig.analogPressConfigs[i].send_delta_value = 10;
hbwconfig.analogPressConfigs[i].send_max_interval = 600;
hbwconfig.analogPressConfigs[i].send_min_interval = 60;
hbwconfig.analogPressConfigs[i].PressureSensorType = 0;
}
#ifdef USE_HARDWARE_SERIAL // RS485 via UART Serial, no debug (_debugstream is NULL)
Serial.begin(19200, SERIAL_8E1);
// HBWDevice-Objekt erstellen
device = new HBWDevice(HMW_DEVICETYPE, HARDWARE_VERSION, FIRMWARE_VERSION,
&Serial, RS485_TXEN,sizeof(hbwconfig),&hbwconfig,
NUMBER_OF_CHAN,(HBWChannel**)channels,
NULL,
NULL, NULL);
device->setConfigPins(BUTTON, LED); // 8 (button) and 13 (led) is the default
device->setStatusLEDPins(LED, LED); // Tx, Rx LEDs using config LED
#else
Serial.begin(115200); // Serial->USB for debug
rs485.begin(19200); // RS485 via SoftwareSerial, must use 19200 baud!
device = new HBWDevice(HMW_DEVICETYPE, HARDWARE_VERSION, FIRMWARE_VERSION,
&rs485, RS485_TXEN,sizeof(hbwconfig),&hbwconfig,
NUMBER_OF_CHAN,(HBWChannel**)channels,
&Serial,
NULL, NULL);
device->setConfigPins(BUTTON, LED); // 8 (button) and 13 (led) is the default
// device->setStatusLEDPins(LED, LED); // Tx, Rx LEDs
hbwdebug(F("B: 2A "));
hbwdebug(freeRam());
hbwdebug(F("\n"));
#endif
// HBWAnalogPRESS-Objekte erstellen und dem Array hinzufügen
for (int i = 0; i < NUMBER_OF_CHAN; i++) {
channels[i] = new HBWAnalogPRESS(SENSOR_PINS[i], &hbwconfig.analogPressConfigs[i]);
}
}
void loop()
{
device->loop();
// POWERSAVE(); // go sleep a bit
};
hier die HBWAnalogPRESS.h
// HBWAnalogPRESS.h
#ifndef HBWAnalogPRESS_h
#define HBWAnalogPRESS_h
#define SAMPLE_INTERVAL 3 // seconds * 3 samples
#define DEFAULT_UPDATE_INTERVAL 300 // seconds
/* Supported sensors */
//MPa 0,5 Gerätecode 0x10
//MPa 1,2 Gerätecode 0x28
//
#include <inttypes.h>
#include "HBWired.h"
struct hbw_config_analog_press {
uint8_t send_delta_value; // Differenz des Messwerts, ab dem gesendet wird
uint8_t offset; // offset in mBar ()
uint16_t send_max_interval; // Maximum-Sendeintervall
uint16_t send_min_interval; // Minimum-Sendeintervall (factor 10, range 10 to 2540 seconds)
uint8_t update_interval; // factor 10, range 10 to 2540 seconds //TODO: 10 seconds stepping is ok?
uint8_t PressureSensorType:2; // Drucksensortyp
};
class HBWAnalogPRESS : public HBWChannel {
public:
HBWAnalogPRESS(uint8_t pin, hbw_config_analog_press* config);
virtual uint8_t get(uint8_t* data);
virtual void loop(HBWDevice*, uint8_t channel);
virtual void afterReadConfig();
private:
uint8_t pin; // Pin
hbw_config_analog_press* config;
uint32_t lastActionTime;
uint16_t nextActionDelay;
uint16_t currentValue; // store last result (average)
uint16_t lastSendValue; // result that has been send (average)
uint32_t lastSentTime; // last time of successful send
// Drucksensor-Typen und Faktoren
static const uint8_t MPA_0_5_ID = 0x10;
static const uint8_t MPA_1_2_ID = 0x22;
static const float sensorFactor_MPA_0_5;
static const float sensorFactor_MPA_1_2;
};
#endif
und die HBWAnalogPRESS.cpp
/*
* HBWAnalogPRESS.cpp
*
*
*
*/
#include "HBWAnalogPRESS.h"
#include <EEPROM.h>
// Class HBWAnalogPRESS
HBWAnalogPRESS::HBWAnalogPRESS(uint8_t _pin, hbw_config_analog_press* _config) {
pin = _pin;
config = _config;
lastActionTime = 0;
nextActionDelay = SAMPLE_INTERVAL * 2; // initial delay
currentValue = 0;
lastSendValue = 0;
lastSentTime = 0;
analogRead(pin);
}
// channel specific settings or defaults
void HBWAnalogPRESS::afterReadConfig() {
if (config->update_interval == 0xFF) config->update_interval = DEFAULT_UPDATE_INTERVAL / 10;
if (config->send_delta_value == 0xFF) config->send_delta_value = 100; // delta as raw ADC value (consider factor in XML!)
if (config->send_max_interval == 0xFFFF) config->send_max_interval = DEFAULT_UPDATE_INTERVAL * 2; // not faster than update interval
if (config->send_min_interval == 0xFF) config->send_min_interval = 30 / 10; // 30 seconds (send_min_interval has 10 seconds stepping)
// Set default values for PressureSensorType and offset if not provided
if (config->PressureSensorType == 0xFF) {
config->PressureSensorType = MPA_1_2_ID; // Default to MPA 1.2 sensor
}
if (config->offset == 0xFF) {
config->offset = 0; // Default offset to 0 mBar
}
}
/* standard public function - returns length of data array. Data array contains current channel reading */
uint8_t HBWAnalogPRESS::get(uint8_t* data) {
// MSB first
*data++ = (currentValue >> 8);
*data = currentValue & 0xFF;
return 2;
}
/* standard public function - called by main loop for every channel in sequential order */
void HBWAnalogPRESS::loop(HBWDevice* device, uint8_t channel) {
if (config->update_interval == 0) { // skip disabled channels
currentValue = 0;
lastSendValue = 0;
return;
}
unsigned long now = millis();
// check if some values have to be sent - do not send before min interval
if (config->send_min_interval && now - lastSentTime <= (uint32_t)config->send_min_interval * 10000) return;
if ((config->send_max_interval && now - lastSentTime >= (uint32_t)config->send_max_interval * 1000) ||
(config->send_delta_value && abs(currentValue - lastSendValue) >= (unsigned int)config->send_delta_value)) {
// send new value
static uint8_t level[2];
get(level);
if (device->sendInfoMessage(channel, sizeof(level), level) != HBWDevice::BUS_BUSY) {
lastSendValue = currentValue; // store last value only on success
}
lastSentTime = now; // if send failed, next try will be on send_max_interval or send_min_interval in case the value is still different (send_delta_value)
#ifdef DEBUG_OUTPUT
hbwdebug(F("adc-ch: ")); hbwdebug(channel);
hbwdebug(F(" sent: ")); hbwdebug(lastSendValue); lastSendValue == currentValue ? hbwdebug(F(" SUCCESS!\n")) : hbwdebug(F(" FAILED!\n"));
#endif
}
if (now - lastActionTime < ((uint32_t)nextActionDelay * 1000)) return; // quit if wait time not yet passed
nextActionDelay = SAMPLE_INTERVAL;
#define MAX_SAMPLES 3 // update "buffer" array definition, when changing this
static uint16_t buffer[MAX_SAMPLES] = {0, 0, 0};
static uint8_t nextIndex = 0;
buffer[nextIndex++] = analogRead(pin);
lastActionTime = now;
if (nextIndex >= MAX_SAMPLES) {
nextIndex = 0;
uint32_t sum = 0;
uint8_t i = MAX_SAMPLES;
do {
sum += buffer[--i];
}
while (i);
currentValue = sum / MAX_SAMPLES;
// "sleep" until next update
nextActionDelay = config->update_interval * 10;
#ifdef DEBUG_OUTPUT
hbwdebug(F("adc-ch:")); hbwdebug(channel); hbwdebug(F(" measured:")); hbwdebug(currentValue); hbwdebug(F("\n"));
#endif
}
}
die ausgabe des Seriellen Monitor ->
12:02:54.842 -> B: 2A 1418
12:02:55.821 -> T: FD:FF:FF:FF:FF:F8:41:FF:FF:FF:12:41:00:AB:01:00:01:48:42:57:37:32:39:36:32:35:35:A1:AA
LG Markus