Bodenfeuchtesensor HB-UNI-Sen-CAP-MOIST(-T)

Entwicklung und Bau von Hardware aller Art, die im HM-Umfeld eingesetzt werden kann

Moderator: Co-Administratoren

jp112sdl
Beiträge: 5094
Registriert: 20.11.2016, 20:01
Hat sich bedankt: 188 Mal
Danksagung erhalten: 401 Mal
Kontaktdaten:

Re: Bodenfeuchtesensor HB-UNI-Sen-CAP-MOIST(-T)

Beitrag von jp112sdl » 27.02.2020, 17:22

Klar 8)
Musst nur das Multichanneldevice durch ein Channeldevice mit Virtchannels ersetzen.
Einen VirtChannel machst du als WeatherChannel, den anderen als SwitchChannel.
Wenn das ganze weiterhin mit Batterien laufen soll, nicht vergessen WOR zu aktivieren.

Auf der Seite der CCU musst du ein neues Gerät implementieren.
Beispiele (Skripte zur Modifikation der WebUI Files und Gerätebeschreibungsdateien (XML)) findest du im JP-HB-Devices-Addon genügend.

VG,
Jérôme

b-tronic
Beiträge: 11
Registriert: 15.02.2020, 08:32
System: Alternative CCU (RaspberryMatic etc.)
Danksagung erhalten: 3 Mal

Re: Bodenfeuchtesensor HB-UNI-Sen-CAP-MOIST(-T)

Beitrag von b-tronic » 28.02.2020, 21:27

Stimmt der Arduino Code so ? :

Code: Alles auswählen


//- -----------------------------------------------------------------------------------------------------------------------
// AskSin++
// 2016-10-31 papa Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/
// 2019-05-03 jp112sdl Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/
// 2019-05-04 stan23 Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/
//- -----------------------------------------------------------------------------------------------------------------------

//Sensor:
//https://www.dfrobot.com/wiki/index.php/Capacitive_Soil_Moisture_Sensor_SKU:SEN0193

// define this to read the device id, serial and device type from bootloader section
// #define USE_OTA_BOOTLOADER

#define NO_DS18B20 //use model without temperature sensor

#define EI_NOTEXTERNAL
#include <EnableInterrupt.h>
#define SENSOR_ONLY

// Arduino Pro mini 8 Mhz
// Arduino pin for the config button
#define CONFIG_BUTTON_PIN      8
#define LED_PIN                4
#define BATT_EN_PIN            5
#define BATT_SENS_PIN          14  // A0
#define RELAY_PIN_1            15
#define CC1101_GDO0_PIN        7
#define CC1101_CS_PIN          10
#define CC1101_MOSI_PIN        11
#define CC1101_MISO_PIN        12
#define CC1101_SCK_PIN         13
const uint8_t SENSOR_PINS[]    {17}; //AOut Pins der Sensoren (hier A1, A2 und A3)
//bei Verwendung von > 3 Sensoren sollten die Vcc der Sensoren auf 2 Enable Pins verteilt werden (max. Last pro AVR-Pin beachten!)
const uint8_t SENSOR_EN_PINS[] {6};

#define DS18B20_PIN            3

#define CYCLETIME seconds2ticks(60UL * 3 * 0.88)  // every 3 minutes

#define DEVICE_CHANNEL_COUNT sizeof(SENSOR_PINS)
#include <AskSinPP.h>
#include <LowPower.h>

#include <Register.h>
#include <MultiChannelDevice.h>
#include <Switch.h>
#ifndef NO_DS18B20
#include <OneWire.h>
#include <sensors/Ds18b20.h>
OneWire oneWire(DS18B20_PIN);
#endif

// number of available peers per channel
#define PEERS_PER_CHANNEL 4
#define PEERS_PER_SwitchChannel  6
// all library classes are placed in the namespace 'as'
using namespace as;

//Korrekturfaktor der Clock-Ungenauigkeit, wenn keine RTC verwendet wird
#define SYSCLOCK_FACTOR    0.88

#ifdef NO_DS18B20
#define DEVICE_MODEL  0x11
#else
#define DEVICE_MODEL  0x12
#endif

// define all device properties
const struct DeviceInfo PROGMEM devinfo = {
  {0xF3, DEVICE_MODEL, 0x10},  // Device ID
  "JPCAPMR000",                // Device Serial
  {0xF3, DEVICE_MODEL},        // Device Model
  0x10,                        // Firmware Version
  as::DeviceType::THSensor,    // Device Type
  {0x01, 0x01}                 // Info Bytes
};

/**
   Configure the used hardware
*/
typedef AvrSPI<CC1101_CS_PIN, CC1101_MOSI_PIN, CC1101_MISO_PIN, CC1101_SCK_PIN> SPIType;
typedef Radio<SPIType, CC1101_GDO0_PIN> RadioType;
typedef StatusLed<LED_PIN> LedType;
typedef AskSin<LedType, BatterySensorUni<BATT_SENS_PIN, BATT_EN_PIN, 0>, RadioType> BaseHal;
class Hal : public BaseHal {
  public:
    void init (const HMID& id) {
      BaseHal::init(id);
      battery.init(seconds2ticks(60UL * 60) * SYSCLOCK_FACTOR, sysclock); //battery measure once an hour
      battery.low(22);
      battery.critical(19);
    }

    bool runready () {
      return sysclock.runready() || BaseHal::runready();
    }
} hal;

DEFREGISTER(Reg0, MASTERID_REGS, DREG_INTKEY, DREG_CYCLICINFOMSG, DREG_SABOTAGEMSG)
class SwList0 : public RegList0<Reg0> {
  public:
    SwList0(uint16_t addr) : RegList0<Reg0>(addr) {}
    void defaults() {
      clear();
      intKeyVisible(true);
      sabotageMsg(true);
      cycleInfoMsg(true);
    }
};

typedef SwitchChannel<Hal, PEERS_PER_SwitchChannel, SwList0>  SwChannel;
//typedef TwoStateChannel<Hal, SwList0, SensList1, DefList4, PEERS_PER_SENSCHANNEL> SensChannel;

class MixDevice : public ChannelDevice<Hal, VirtBaseChannel<Hal, SwList0>, 8, SwList0> {
    class CycleInfoAlarm : public Alarm {
        MixDevice& dev;
      public:
        CycleInfoAlarm (MixDevice& d) : Alarm (CYCLETIME), dev(d) {}
        virtual ~CycleInfoAlarm () {}

        void trigger (AlarmClock& clock)  {
          set(CYCLETIME);
          clock.add(*this);
          dev.switchChannel(1).changed(true);
        }
    } cycle;

  public:
    VirtChannel<Hal, SwChannel, SwList0>   swChannel1,   swChannel2,   swChannel3,   swChannel4;

  public:
    typedef ChannelDevice<Hal, VirtBaseChannel<Hal, SwList0>, 8, SwList0> DeviceType;
    MixDevice (const DeviceInfo& info, uint16_t addr) : DeviceType(info, addr), cycle(*this) {
      DeviceType::registerChannel(swChannel1, 1);
      DeviceType::registerChannel(swChannel2, 2);
      DeviceType::registerChannel(swChannel3, 3);
      DeviceType::registerChannel(swChannel4, 4);
     }
    virtual ~MixDevice () {}


    SwChannel& switchChannel (uint8_t num)  {
      switch (num) {
        case 1:
          return swChannel1;
          break;
        case 2:
          return swChannel2;
          break;
        case 3:
          return swChannel3;
          break;
        case 4:
          return swChannel4;
          break;
      }
    }
};
DEFREGISTER(UReg0, MASTERID_REGS, DREG_LOWBATLIMIT, 0x21, 0x22)
class UList0 : public RegList0<UReg0> {
  public:
    UList0 (uint16_t addr) : RegList0<UReg0>(addr) {}

    bool Sendeintervall (uint16_t value) const {
      return this->writeRegister(0x21, (value >> 8) & 0xff) && this->writeRegister(0x22, value & 0xff);
    }
    uint16_t Sendeintervall () const {
      return (this->readRegister(0x21, 0) << 8) + this->readRegister(0x22, 0);
    }

    void defaults () {
      clear();
      lowBatLimit(22);
      Sendeintervall(30);
    }
};

DEFREGISTER(UReg1, 0x01, 0x02, 0x03, 0x04, 0x23, 0x24, 0x25, 0x26)
class UList1 : public RegList1<UReg1> {
  public:
    UList1 (uint16_t addr) : RegList1<UReg1>(addr) {}
    bool HIGHValue (uint16_t value) const {
      return this->writeRegister(0x23, (value >> 8) & 0xff) && this->writeRegister(0x24, value & 0xff);
    }
    uint16_t HIGHValue () const {
      return (this->readRegister(0x23, 0) << 8) + this->readRegister(0x24, 0);
    }

    bool LOWValue (uint16_t value) const {
      return this->writeRegister(0x25, (value >> 8) & 0xff) && this->writeRegister(0x26, value & 0xff);
    }
    uint16_t LOWValue () const {
      return (this->readRegister(0x25, 0) << 8) + this->readRegister(0x26, 0);
    }

#ifndef NO_DS18B20
    bool Offset (int32_t value) const {
      return
          this->writeRegister(0x01, (value >> 24) & 0xff) &&
          this->writeRegister(0x02, (value >> 16) & 0xff) &&
          this->writeRegister(0x03, (value >> 8)  & 0xff) &&
          this->writeRegister(0x04, (value)       & 0xff)
          ;
    }

    int32_t Offset () const {
      return
          ((int32_t)(this->readRegister(0x01, 0)) << 24) +
          ((int32_t)(this->readRegister(0x02, 0)) << 16) +
          ((int32_t)(this->readRegister(0x03, 0)) << 8 ) +
          ((int32_t)(this->readRegister(0x04, 0))      )
          ;
    }
#endif

    void defaults () {
      clear();
      HIGHValue(830);
      LOWValue(420);
#ifndef NO_DS18B20
      Offset(0);
#endif
    }
};

class WeatherEventMsg : public Message {
  public:
  void init(uint8_t msgcnt, uint8_t *h, bool batlow, uint8_t volt, __attribute__ ((unused))  int16_t temperature, __attribute__ ((unused))  int8_t offset) {

#ifndef NO_DS18B20
    int16_t t = temperature + offset;
    DPRINT(F("+Temp         C : ")); DDECLN(t);
#endif
    DPRINT(F("+Battery      V : ")); DDECLN(volt);
#ifdef NO_DS18B20
#define PAYLOAD_OFFSET 0
#else
#define PAYLOAD_OFFSET 2
#endif

    Message::init(0xc + PAYLOAD_OFFSET + (DEVICE_CHANNEL_COUNT * 2), msgcnt, 0x53, (msgcnt % 20 == 1) ? (BIDI | WKMEUP) : BCAST, batlow ? 0x80 : 0x00, 0x41);

#ifndef NO_DS18B20
    pload[0] = (t >> 8) & 0xff;
    pload[1] = (t)      & 0xff;
#endif

    pload[PAYLOAD_OFFSET] = (volt)   & 0xff;
    for (uint8_t s = 0; s < DEVICE_CHANNEL_COUNT; s++) {
      DPRINT(F("+Humidity (#")); DDEC(s + 1); DPRINT(F(") %: ")); DDECLN(h[s]);
      pload[1+PAYLOAD_OFFSET+(s * 2)] = 0x42 + s;
      pload[2+PAYLOAD_OFFSET+(s * 2)] = h[s] & 0xff;
    }
  }
  void init(uint8_t msgcnt, uint8_t *h, bool batlow, uint8_t volt) {
    init(msgcnt, h, batlow, volt, 0, 0);
  }
};

class WeatherChannel : public Channel<Hal, UList1, EmptyList, List4, PEERS_PER_CHANNEL, UList0> {
  public:
    WeatherChannel () : Channel() {}
    virtual ~WeatherChannel () {}

    void configChanged() {
      DPRINT(F("Config changed List1 (CH "));DDEC(number());DPRINTLN(F(")"));
#ifndef NO_DS18B20
      if (number() == 1) { DPRINT(F("*Offset    : ")); DDECLN(this->getList1().Offset()); }
#endif
      if (number() > 1)  { DPRINT(F("*HIGHValue : ")); DDECLN(this->getList1().HIGHValue()); }
      if (number() > 1)  { DPRINT(F("*LOWValue  : ")); DDECLN(this->getList1().LOWValue()); }
    }

    uint8_t status () const {
      return 0;
    }

    uint8_t flags () const {
      return 0;
    }
};

class UType : public MultiChannelDevice<Hal, WeatherChannel, DEVICE_CHANNEL_COUNT + 1, UList0> {
public:
#ifndef NO_DS18B20
  Ds18b20      sensor[1];
#endif
  class SensorArray : public Alarm {
       UType& dev;

       public:
         uint8_t       humidity[DEVICE_CHANNEL_COUNT];
         uint8_t       sensorcount;
         SensorArray (UType& d) : Alarm(0), dev(d), sensorcount(0) {}

         void measure() {
           //enable all moisture sensors
           for (uint8_t s = 0; s < sizeof(SENSOR_EN_PINS); s++) {
             digitalWrite(SENSOR_EN_PINS[s], HIGH);
             _delay_ms(5);
           }

           //wait a moment to settle
           _delay_ms(500);
           //now measure all sensors
           for (uint8_t s = 0; s < DEVICE_CHANNEL_COUNT; s++) {
             uint16_t sens_val = 0;

             //measure 8 times and calculate average
             for (uint8_t i = 0; i < 8; i++) {
               sens_val += analogRead(SENSOR_PINS[s]);
               _delay_ms(10);
             }
             sens_val /= 8;

             DPRINT(F("+Analog     (#")); DDEC(s + 1); DPRINT(F("): ")); DDEC(sens_val);
             uint16_t upper_limit = dev.channel(s + 2).getList1().HIGHValue();
             uint16_t lower_limit = dev.channel(s + 2).getList1().LOWValue();
             if (sens_val > upper_limit) {
               humidity[s] = 0;
               DPRINTLN(F(" higher than limit!"));
             }
             else if (sens_val < lower_limit) {
               humidity[s] = 100;
               DPRINTLN(F(" lower than limit!"));
             }
             else {
               uint16_t range = upper_limit - lower_limit;
               uint16_t base = sens_val - lower_limit;
               uint8_t pct_inv = (base * 100) / range;
               humidity[s] = 100 - pct_inv;
               DPRINTLN("");
             }

             //humidity[s] = random(0,100);

           }
           //disable all moisture sensors
           for (uint8_t s = 0; s < sizeof(SENSOR_EN_PINS); s++)
             digitalWrite(SENSOR_EN_PINS[s], LOW);

#ifndef NO_DS18B20
           Ds18b20::measure(dev.sensor, 1);
#endif
         }

         virtual void trigger (__attribute__ ((unused)) AlarmClock& clock) {
           measure();
           tick = delay();
           WeatherEventMsg& msg = (WeatherEventMsg&)dev.message();
#ifndef NO_DS18B20
           msg.init(dev.nextcount(), humidity, dev.battery().low(), dev.battery().current(), dev.sensor[0].temperature(), dev.channel(1).getList1().Offset());
#else
           msg.init(dev.nextcount(), humidity, dev.battery().low(), dev.battery().current());
#endif
           dev.send(msg, dev.getMasterID());
           sysclock.add(*this);
         }

         uint32_t delay () {
           //Sendeintervall festlegen
           uint16_t _txDelay = max(dev.getList0().Sendeintervall(), 1);
           return seconds2ticks(_txDelay * 60 * SYSCLOCK_FACTOR);
         }

      } sensarray;


    typedef MultiChannelDevice<Hal, WeatherChannel, DEVICE_CHANNEL_COUNT + 1, UList0> TSDevice;
    UType(const DeviceInfo& info, uint16_t addr) : TSDevice(info, addr), sensarray(*this) {}
    virtual ~UType () {}

    void init (Hal& hal) {
      TSDevice::init(hal);
      for (uint8_t s = 0; s < DEVICE_CHANNEL_COUNT; s++)
        pinMode(SENSOR_PINS[ s ], INPUT);

      for (uint8_t s = 0; s < sizeof(SENSOR_EN_PINS); s++)
      pinMode(SENSOR_EN_PINS[s], OUTPUT);

#ifndef NO_DS18B20
      uint8_t sensorcount = Ds18b20::init(oneWire, sensor, 1);
      DPRINT(F("DS18B20 Sensor "));DPRINTLN((sensorcount > 0) ? F("OK"):F("ERROR"));
#endif
      sensarray.set(seconds2ticks(5));
      sysclock.add(sensarray);
    }

    virtual void configChanged () {
      TSDevice::configChanged();
      DPRINT(F("*LOW BAT Limit: "));
      DDECLN(this->getList0().lowBatLimit());
      this->battery().low(this->getList0().lowBatLimit());
      DPRINT(F("*Sendeintervall: ")); DDECLN(this->getList0().Sendeintervall());

    }
};

UType sdev(devinfo, 0x20);
ConfigButton<UType> cfgBtn(sdev);

void setup () {
  DINIT(57600, ASKSIN_PLUS_PLUS_IDENTIFIER);
  sdev.init(hal);
  DDEVINFO(sdev);
  buttonISR(cfgBtn, CONFIG_BUTTON_PIN);
  sdev.initDone();
}

void loop() {
  bool worked = hal.runready();
  bool poll = sdev.pollRadio();

  if ( worked == false && poll == false ) {
    if ( hal.battery.critical() ) {
      DPRINT(F("Battery critical! "));DDECLN(hal.battery.current());
      Serial.flush();
      hal.activity.sleepForever(hal);
    }
    hal.activity.savePower<Sleep<>>(hal);
  }
}

Beim JP-HB-Devices-Addon hab ich allerdings keinen Plan.....
PiVCCU
Openhab
Siemens S7
Tasmota

jp112sdl
Beiträge: 5094
Registriert: 20.11.2016, 20:01
Hat sich bedankt: 188 Mal
Danksagung erhalten: 401 Mal
Kontaktdaten:

Re: Bodenfeuchtesensor HB-UNI-Sen-CAP-MOIST(-T)

Beitrag von jp112sdl » 28.02.2020, 22:15

b-tronic hat geschrieben:
28.02.2020, 21:27
Stimmt der Arduino Code so ? :
Nein, du hast jetzt nur irgendwas von irgendwoher rein kopiert, das nun zwar im Sketch rumliegt, aber nicht verwendet wird.

Das
jp112sdl hat geschrieben:
27.02.2020, 17:22
Klar
war auch mit ein wenig Ironie gemeint, denn wie du hier schon feststellst:
b-tronic hat geschrieben:
28.02.2020, 21:27
Beim JP-HB-Devices-Addon hab ich allerdings keinen Plan.....
...kommst du schneller zum Ziel, wenn du dir mit ein paar Teilen einfach noch einen separaten Aktor baust.

VG,
Jérôme

wolwin
Beiträge: 104
Registriert: 06.06.2018, 12:27
Danksagung erhalten: 8 Mal

Re: Bodenfeuchtesensor HB-UNI-Sen-CAP-MOIST(-T)

Beitrag von wolwin » 06.03.2020, 18:07

Hi Jérôme,
bin bei der Übernahme von HB-UNI-Sen-CAP-MOIST-T nach ioBroker über einen Fehler gestolpert, der schon einmal hier besprochen wurde:
viewtopic.php?f=76&t=46658&start=170#p513705

Könnte es sein, dass der Fehler im AddOn liegt (Raspberrymatic 3.51.6 + AddOn 2.18)? Ich habe beide Versionen (mit und ohne T) mal eingespielt und gecheckt - es fehlt grundsätzlich der HUMIDITY Eintrag …
Zwischenablage00.jpg
WebUI - HB-UNI-Sen-CAP-MOIST-T - alles ok:
Zwischenablage01.jpg
WebUI - HB-UNI-Sen-CAP-MOIST - Fehler ??:
Zwischenablage02.jpg
Beide Versionen können nicht in der WebUI abgelernt werden
Zwischenablage03.jpg
Ist jetzt nicht so dringend, da meine vier Sensoren laufen - aber vielleicht kannst Du ja mal einen Blick drauf werfen … wäre schön die Werte direkt im ioBroker zu haben.

VG
Wolfram

jp112sdl
Beiträge: 5094
Registriert: 20.11.2016, 20:01
Hat sich bedankt: 188 Mal
Danksagung erhalten: 401 Mal
Kontaktdaten:

Re: Bodenfeuchtesensor HB-UNI-Sen-CAP-MOIST(-T)

Beitrag von jp112sdl » 06.03.2020, 18:16

Hi.
Mit ioBroker kenne ich mich nicht aus.
Hab nur mal irgendwo gelesen, dass bei geschildertem Problem helfen soll, einen hm-rpc.meta-Ordner zu löschen

VG,
Jérôme

jp112sdl
Beiträge: 5094
Registriert: 20.11.2016, 20:01
Hat sich bedankt: 188 Mal
Danksagung erhalten: 401 Mal
Kontaktdaten:

Re: Bodenfeuchtesensor HB-UNI-Sen-CAP-MOIST(-T)

Beitrag von jp112sdl » 06.03.2020, 18:17

Ansonsten im ioBroker Forum mal nachfragen.

VG,
Jérôme

Alveran
Beiträge: 214
Registriert: 07.08.2018, 20:17
Hat sich bedankt: 39 Mal
Danksagung erhalten: 20 Mal

Re: Bodenfeuchtesensor HB-UNI-Sen-CAP-MOIST(-T)

Beitrag von Alveran » 06.03.2020, 21:03

Läuft bei mir ohne Probleme mit der sync zu IOBroker.
Lösch mal den Zweig mit dem Sensor und lasse alle Geräte neu synchronisieren im Adapter.

jp112sdl
Beiträge: 5094
Registriert: 20.11.2016, 20:01
Hat sich bedankt: 188 Mal
Danksagung erhalten: 401 Mal
Kontaktdaten:

Re: Bodenfeuchtesensor HB-UNI-Sen-CAP-MOIST(-T)

Beitrag von jp112sdl » 06.03.2020, 21:23

wolwin hat geschrieben:
06.03.2020, 18:07
Beide Versionen können nicht in der WebUI abgelernt werden
Den Config-Taster am Gerät hast du kurz gedrückt und anschließend sofort auf "Erneut löschen" geklickt?

VG,
Jérôme

wolwin
Beiträge: 104
Registriert: 06.06.2018, 12:27
Danksagung erhalten: 8 Mal

Re: Bodenfeuchtesensor HB-UNI-Sen-CAP-MOIST(-T)

Beitrag von wolwin » 07.03.2020, 11:17

Den Config-Taster am Gerät hast du kurz gedrückt und anschließend sofort auf "Erneut löschen" geklickt?
:o :o :o Den Hint kannte ich bis dato nicht … es funktioniert genau so !! :D

jp112sdl
Beiträge: 5094
Registriert: 20.11.2016, 20:01
Hat sich bedankt: 188 Mal
Danksagung erhalten: 401 Mal
Kontaktdaten:

Re: Bodenfeuchtesensor HB-UNI-Sen-CAP-MOIST(-T)

Beitrag von jp112sdl » 07.03.2020, 11:59

Das ist der handelsübliche Vorgang auch bei Originalsensoren ;)

Du kannst auch erst kurz den Config Taster drücken und dann auf Löschen. Dann gehts auch beim ersten Versuch.

VG,
Jérôme

Antworten

Zurück zu „Hardwareentwicklung und Selbstbau von Aktoren und Sensoren“