HB-UNI-Sen-CURRENT - Stromsensor

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

Moderator: Co-Administratoren

UwePv
Beiträge: 243
Registriert: 23.02.2018, 14:35
Wohnort: Salzwedel
Hat sich bedankt: 34 Mal
Danksagung erhalten: 10 Mal

Re: HB-UNI-Sen-CURRENT - Stromsensor

Beitrag von UwePv » 04.03.2022, 15:35

Habe mich zu früh gefreut.
Werte wurden nicht an die CCU übertragen.
mit 5Sensoren.jpg
Original von Jerom läuft
mit 3 sensoren.jpg
könnte mir einer von euch einen funktionierenden Sketch für 5 Sensoren zukommen lassen:?:

Hatte bei meinem Versuch, dass die Werte zur CCU und die Config von der CCU nicht übertragen wurden :(
Nach mehr als 4 Stunden auf den Sketch schauen konnte ich ihn fast Singen und keine Zahlen mehr unterscheiden. :?
Bin nicht mehr der Jüngste.
noch Anfänger

UwePv
Beiträge: 243
Registriert: 23.02.2018, 14:35
Wohnort: Salzwedel
Hat sich bedankt: 34 Mal
Danksagung erhalten: 10 Mal

Re: HB-UNI-Sen-CURRENT - Stromsensor

Beitrag von UwePv » 05.03.2022, 17:51

Ich muss wohl auf 2 Anzeigen verzichten da ich unfähig bin den Sketch umzuschreiben.

Anzeige OK. Aber die Werte werden nicht in der CCU angezeigt.
Anzeige.jpg
F1.jpg
Das ist der Sketch.

Kann sich da einer erbarmen und mal rüberschauen ? :( :( :(
Versuch5x4.zip
(4.78 KiB) 31-mal heruntergeladen
Mus mich sonst aus Kummer besaufen. :oops:
noch Anfänger

a_quadrat
Beiträge: 18
Registriert: 14.11.2019, 19:54
Danksagung erhalten: 2 Mal

Re: HB-UNI-Sen-CURRENT - Stromsensor

Beitrag von a_quadrat » 06.03.2022, 11:51

Hallo Uwe,

anbei mein Setch, du musst natürlich die I2C Adressen ggf. anpassen.

Code: Alles auswählen

//- -----------------------------------------------------------------------------------------------------------------------
// AskSin++
// 2016-10-31 papa Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/
// 2020-07-04 jp112sdl Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/
//- -----------------------------------------------------------------------------------------------------------------------
// ci-test=yes board=328p aes=no

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

#define LCD_ADDRESS 0x27

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

#include <AskSinPP.h>
#include <LowPower.h>

#include <Register.h>
#include <MultiChannelDevice.h>

#ifdef LCD_ADDRESS
#include <LiquidCrystal_I2C.h>
#define BACKLIGHT_BTN_PIN  6
#endif

#include "Sensors/Ads1x15.h"
#define ADS1115_ADDR_1 0x4B
#define ADS1115_ADDR_2 0x4A
#define ADS1115_ADDR_3 0x48
#define ADS_SENSOR_GAIN   adsGain_t::GAIN_TWO


#define CONFIG_BUTTON_PIN  8
#define LED_PIN            4
#define BUSY_LED_PIN       5

#define NUM_CHANNELS       5

#define PEERS_PER_CHANNEL  4

using namespace as;

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

enum conditionTypes { ct_none, ct_above, ct_below, ct_disabled };
typedef struct {
  uint32_t current;
  uint8_t conditionType = ct_none;
  bool ok;
} _currentSensor;

// define all device properties
const struct DeviceInfo PROGMEM devinfo = {
  {0xF3, 0x4E, 0x01},          // Device ID
  "JPCUR00001",                // Device Serial
  {0xF3, 0x4E},                // Device Model
  0x10,                        // Firmware Version
  0x53,                        // Device Type
  {0x01, 0x01}                 // Info Bytes
};

/**
   Configure the used hardware
*/
typedef AskSin<StatusLed<LED_PIN>, BatterySensor, Radio<AvrSPI<10, 11, 12, 13>, 2>> Hal;
typedef StatusLed<BUSY_LED_PIN> BusyLed;
Hal hal;

DEFREGISTER(DReg0, MASTERID_REGS, DREG_LOWBATLIMIT, 0x07, 0x1f, 0x20, 0x21, DREG_BACKONTIME, DREG_POWERSUPPLY)
class DevList0 : public RegList0<DReg0> {
  public:
    DevList0 (uint16_t addr) : RegList0<DReg0>(addr) {}

    bool Sendeintervall (uint8_t value) const {
      return this->writeRegister(0x21, value & 0xff);
    }
    uint8_t Sendeintervall () const {
      return this->readRegister(0x21, 0);
    }

    bool Messintervall (uint16_t value) const {
      return this->writeRegister(0x1f, (value >> 8) & 0xff) && this->writeRegister(0x20, value & 0xff);
    }
    uint16_t Messintervall () const {
      return (this->readRegister(0x1f, 0) << 8) + this->readRegister(0x20, 0);
    }

    bool conditionCheckAverage () const {
      return this->readRegister(0x07,0);
    }
    bool conditionCheckAverage (uint8_t value) const {
      return this->writeRegister(0x07,value);
    }

    void defaults () {
      clear();
      lowBatLimit(22);
      Sendeintervall(12);
      Messintervall(10);
      powerSupply(true); //true = battery mode
      backOnTime(10);
      conditionCheckAverage(false);
    }
};

DEFREGISTER(CReg1, CREG_AES_ACTIVE, CREG_COND_TX_THRESHOLD_HI, CREG_COND_TX_THRESHOLD_LO, CREG_COND_TX_DECISION_ABOVE, CREG_COND_TX_DECISION_BELOW, 0x01, 0x02, 0x03)
class DevList1 : public RegList1<CReg1> {
  public:
    DevList1 (uint16_t addr) : RegList1<CReg1>(addr) {}

    bool sampleTime (uint16_t value) const {
      return this->writeRegister(0x01, (value >> 8) & 0xff) && this->writeRegister(0x02, value & 0xff);
    }
    uint16_t sampleTime () const {
      return (this->readRegister(0x01, 0) << 8) + this->readRegister(0x02, 0);
    }

    bool sensorType (uint16_t value) const {
      return this->writeRegister(0x03, value & 0xff);
    }
    uint16_t sensorType () const {
      return this->readRegister(0x03, 0);
    }

    void defaults () {
      clear();
      condTxThresholdHi(1000);
      condTxDecisionAbove(200);
      condTxThresholdLo(100);
      condTxDecisionBelow(0);
      sensorType(0);
      sampleTime(500);
    }
};

class MeasureEventMsg : public Message {
  public:
    void init(uint8_t msgcnt, uint32_t *sensorValues, _currentSensor *cs, bool batlow) {
      Message::init(0xa + (NUM_CHANNELS * 3), msgcnt, 0x53, BCAST, batlow ? 0x80 : 0x00, cs[0].ok ? 0x41 : 0xC1);
      //DPRINT(F("+Current (#0) : ")); DDECLN(sensorValues[0]);
      pload[0] =  (sensorValues[0] >> 8) & 0xff;
      pload[1] = (sensorValues[0])   & 0xff;
      for (uint8_t s = 0; s < NUM_CHANNELS - 1; s++) {
        //DPRINT(F("+Current (#")); DDEC(s + 1); DPRINT(F(") : ")); DDECLN(sensorValues[s+1]);
        pload[2+(s * 3)] = (cs[s+1].ok ? 0x42 : 0xC2) + s;
        pload[3+(s * 3)] = (sensorValues[s+1] >> 8) & 0xff;
        pload[4+(s * 3)] =  sensorValues[s+1]       & 0xff;
      }
    }
};

#ifdef LCD_ADDRESS
class LcdType :  public Button {
public:
  class BacklightAlarm : public Alarm {
    LcdType& lcdDev;
  public:
    BacklightAlarm (LcdType& l) :  Alarm(0), lcdDev(l) {}
    virtual ~BacklightAlarm () {}
    void restartTimer(uint8_t sec) {
      sysclock.cancel(*this);
      set(seconds2ticks(sec));
      lcdDev.lcd.backlight();
      sysclock.add(*this);
    }

    virtual void trigger (__attribute__((unused)) AlarmClock& clock) {
      lcdDev.lcd.noBacklight();
    }
  }backlightalarm;
private:
  bool _present;
  bool first;
  uint8_t backlightOnTime;

  byte arrowUp[8] = { 0b00000, 0b00100, 0b01110, 0b10101, 0b00100, 0b00100, 0b00100, 0b00000 };
  byte arrowDown[8] = { 0b00000, 0b00100, 0b00100, 0b00100, 0b10101, 0b01110, 0b00100, 0b00000 };

public:
  LiquidCrystal_I2C lcd;
  LcdType () :  backlightalarm(*this), _present(false), first(true), backlightOnTime(10), lcd(LCD_ADDRESS, 16, 2){}
  virtual ~LcdType () {}

  void displayValues(_currentSensor *cs) {
    if (_present == true) {
      if (first) {
        first = false;
       lcd.setCursor(0, 0);
       lcd.print(" L1    L2    L3 ");
      }
     lcd.setCursor(0, 1);
     lcd.print("                ");

     lcd.setCursor(cs[0].current > 999 ? 0 : 1, 1);
     lcd.print(cs[0].current / 100.0, 1);

     lcd.setCursor(cs[1].current > 999 ? 6 : 7, 1);
     lcd.print(cs[1].current / 100.0, 1);

     lcd.setCursor(cs[2].current > 999 ? 12 : 13, 1);
     lcd.print(cs[2].current / 100.0, 1);
    }
  }

  void showCondition(uint8_t conditionType, uint8_t channel) {
    lcd.setCursor(channel * 6 + 3, 0);
    if (conditionType == ct_above) lcd.write(byte(0));
    if (conditionType == ct_below) lcd.write(byte(1));
    if (conditionType == ct_none) lcd.print("*");
    if (conditionType == ct_disabled) lcd.print(" ");
  }

  void initLCD(uint8_t *serial) {
    Wire.begin();
    Wire.beginTransmission(LCD_ADDRESS);
    if (Wire.endTransmission() == 0) {
      _present = true;
      lcd.init();
      lcd.createChar(0, arrowUp);
      // create a new character
      lcd.createChar(1, arrowDown);
      lcd.backlight();
      lcd.setCursor(0, 0);
      lcd.print(ASKSIN_PLUS_PLUS_IDENTIFIER);
      lcd.setCursor(3, 1);
      lcd.setContrast(200);
      lcd.print((char*)serial);

      if (backlightOnTime > 0) backlightalarm.restartTimer(backlightOnTime);

    } else {
      DPRINT("LCD Display not found at 0x");DHEXLN((uint8_t)LCD_ADDRESS);
    }
  }

  void setBackLightOnTime(uint8_t t) {
    backlightOnTime = t;
    if (backlightOnTime == 0)
      lcd.backlight();
    else
      lcd.noBacklight();
  }

  virtual void state(uint8_t s) {
    Button::state(s);
    if (s==released ) {
      if (backlightOnTime > 0) backlightalarm.restartTimer(backlightOnTime);
    }
  }
};
LcdType lcd;
#endif

class MeasureChannel : public Channel<Hal, DevList1, EmptyList, List4, PEERS_PER_CHANNEL, DevList0> {
  private:
    enum sensorTypes { SCT013015, SCT013020, SCT013030, SCT013050, SCT0130100, INA219, ACS712_or_other };
  public:
    MeasureChannel () : Channel() {}
    virtual ~MeasureChannel () {}

    virtual void configChanged() {
    /*  DPRINT("configChanged on Ch");DDECLN(number());
      DPRINT("condTxThresholdHi  : ");DDECLN(this->getList1().condTxThresholdHi());
      DPRINT("condTxDecisionAbove: ");DDECLN(this->getList1().condTxDecisionAbove());
      DPRINT("condTxThresholdLo  : ");DDECLN(this->getList1().condTxThresholdLo());
      DPRINT("condTxDecisionBelow: ");DDECLN(this->getList1().condTxDecisionBelow());
      DPRINT("sensorType         : ");DDECLN(this->getList1().sensorType());
      DPRINT("sampleTime         : ");DDECLN(this->getList1().sampleTime());*/
    }

    void checkConditions(_currentSensor *sensor) {
      uint8_t sIdx = number()-1;

      static uint8_t evcnt  = 0;
      uint8_t decisionValue = 0;
      bool sendConditionalSwitchCommand = false;

      uint32_t current     = sensor[sIdx].current;
      uint16_t thresholdHi = this->getList1().condTxThresholdHi();
      uint16_t thresholdLo = this->getList1().condTxThresholdLo();

      if (thresholdHi > 0) {
        SensorEventMsg& rmsg = (SensorEventMsg&)device().message();

        if (current > thresholdHi && sensor[sIdx].conditionType != ct_above) {
          sensor[sIdx].conditionType = ct_above;
          decisionValue = this->getList1().condTxDecisionAbove();
          sendConditionalSwitchCommand = true;
        }

        if (current < thresholdLo && sensor[sIdx].conditionType != ct_below) {
          sensor[sIdx].conditionType = ct_below;
          decisionValue = this->getList1().condTxDecisionBelow();
          sendConditionalSwitchCommand = true;
        }

        if (sendConditionalSwitchCommand == true) {
          DPRINTLN("sendConditionalSwitchCommand");
          rmsg.init(device().nextcount(), number(), evcnt++, decisionValue, false , false);
          device().sendPeerEvent(rmsg, *this);
        }
        lcd.showCondition(sensor[sIdx].conditionType, sIdx);
      } else {
        lcd.showCondition(ct_disabled, sIdx);
      }

    }

    uint8_t sctFactor() {
      uint8_t sensorType = this->getList1().sensorType();
      switch (sensorType) {
        case SCT013015:
          return 15;
        case SCT013020:
          return 20;
        case SCT013030:
          return 30;
        case SCT013050:
          return 50;
        case SCT0130100:
          return 100;
        default:
          return 0;
      }
    }

    uint16_t sampleTime() {
      return this->getList1().sampleTime();
    }

    uint8_t status () const { return 0; }
    uint8_t flags  () const { return device().battery().low() ? 0x80 : 00; }
};

class DevType : public MultiChannelDevice<Hal, MeasureChannel, NUM_CHANNELS, DevList0> {
private:
  bool powerModeBattery;
  uint16_t txInterval;
  uint16_t measureInterval;
  bool boot;
  bool lcd_present;
  BusyLed busyLed;
public:
  class CurrentSensors : public Alarm {
    DevType&   dev;
    Sens_Ads1x15<ADS1115_ADDR_1> ads1;
    Sens_Ads1x15<ADS1115_ADDR_2> ads2;
    Sens_Ads1x15<ADS1115_ADDR_3> ads3;
    _currentSensor cs[NUM_CHANNELS];
    uint32_t cumulatedCurrentValues[NUM_CHANNELS];
    uint8_t measureCount;
    public:
       CurrentSensors (DevType& d) : Alarm(0), dev(d), measureCount(0) {}
       virtual ~CurrentSensors () {}
       void measure() {
         //measurement here:
         cs[0].current = ads1.getCurrent_0_1(dev.channel(1).sampleTime(), dev.channel(1).sctFactor());cs[0].ok = ads1.checkSensor();
         cs[1].current = ads1.getCurrent_2_3(dev.channel(2).sampleTime(), dev.channel(2).sctFactor());cs[1].ok = ads1.checkSensor();
         cs[2].current = ads2.getCurrent_0_1(dev.channel(3).sampleTime(), dev.channel(3).sctFactor());cs[2].ok = ads2.checkSensor();
         cs[3].current = ads2.getCurrent_2_3(dev.channel(4).sampleTime(), dev.channel(4).sctFactor());cs[3].ok = ads2.checkSensor();
         cs[4].current = ads3.getCurrent_0_1(dev.channel(5).sampleTime(), dev.channel(5).sctFactor());cs[4].ok = ads3.checkSensor();

#ifdef LCD_ADDRESS
         lcd.displayValues(cs);
#endif
       }

       void init() {
         ads1.init(ADS_SENSOR_GAIN);
         ads2.init(ADS_SENSOR_GAIN);
         ads3.init(ADS_SENSOR_GAIN);
       }

       virtual void trigger (__attribute__ ((unused)) AlarmClock& clock) {
         dev.busyLed.ledOn();
         measure();
         dev.busyLed.ledOff();

         // add measured current to the cumulated values
         cumulatedCurrentValues[0] += cs[0].current;
         cumulatedCurrentValues[1] += cs[1].current;
         cumulatedCurrentValues[2] += cs[2].current;
         cumulatedCurrentValues[3] += cs[3].current;
         cumulatedCurrentValues[4] += cs[4].current;

         measureCount++;
         DPRINT("measure() #");DDEC(measureCount);DPRINT(" of ");DDECLN(dev.txInterval);

         // check if any of the sensors is above/below a threshold
         for (uint8_t ch = 0; ch < NUM_CHANNELS; ch++) {
           // check conditions on each measure() or only on txInterval
           if (dev.getList0().conditionCheckAverage() == false) {
             dev.channel(ch+1).checkConditions(cs);
           } else {
             if (measureCount >= dev.txInterval) {
               cs[ch].current = cumulatedCurrentValues[ch] / measureCount;
               dev.channel(ch+1).checkConditions(cs);
             }
           }
         }

         set(seconds2ticks(max(10, dev.measureInterval * SYSCLOCK_FACTOR)));

         // send the cyclic message 
         if (measureCount >= dev.txInterval) {
           bool batlow = dev.onBattery() ? dev.battery().low() : false;

           // divide cumulated values by measure count
           for (uint8_t ch = 0; ch < NUM_CHANNELS; ch++)
             cumulatedCurrentValues[ch] /= measureCount;
             
           MeasureEventMsg& msg = (MeasureEventMsg&)dev.message();             
           msg.init(dev.nextcount(), cumulatedCurrentValues, cs, batlow);
           dev.broadcastEvent(msg);

           // reset values
           measureCount = 0;
           for (uint8_t s = 0; s < NUM_CHANNELS; s++)
             cumulatedCurrentValues[s] = 0;
         }
         sysclock.add(*this);
       }
    } currentSensors;

  typedef MultiChannelDevice<Hal, MeasureChannel, NUM_CHANNELS, DevList0> TSDevice;
  DevType(const DeviceInfo& info, uint16_t addr) : TSDevice(info, addr), powerModeBattery(true), txInterval(0), measureInterval(0), boot(true), lcd_present(false), currentSensors(*this) {}
  virtual ~DevType () {}

  void init (Hal& hal) {
    TSDevice::init(hal);
    currentSensors.init();
    busyLed.init();
    busyLed.ledOn(seconds2ticks(2));
  }

  virtual void configChanged () {
    TSDevice::configChanged();
    uint8_t batlow = max(this->getList0().lowBatLimit(),19);
    // DPRINT(F("*LOWBAT Limit: "));DDECLN(batlow);
    this->battery().low(batlow);

    if (this->getList0().Messintervall() != measureInterval) {
      measureInterval = this->getList0().Messintervall();
      //DPRINT(F("*ME Intervall: ")); DDECLN(measureInterval);
      if (boot == false) {
        sysclock.cancel(currentSensors);
        currentSensors.set(seconds2ticks(max(10, measureInterval) * SYSCLOCK_FACTOR));
      } else {
        currentSensors.set(seconds2ticks(3));
      }
      sysclock.add(currentSensors);
    }

    txInterval = max(1, this->getList0().Sendeintervall());
    //DPRINT(F("*TX Intervall: ")); DDECLN(txInterval);

    onBattery(this->getList0().powerSupply());
    //DPRINT(F("*Batterymode : ")); DDECLN(onBattery());

    uint8_t bOn = this->getList0().backOnTime();
    //DPRINT(F("*LCD Backlight Ontime : ")); DDECLN(bOn);
    lcd.setBackLightOnTime(bOn);

    //bool cc = this->getList0().conditionCheckAverage();
    //DPRINT(F("*Condition Check on Average : ")); DDECLN(cc);

    boot = false;
  }

  void onBattery(bool bat) {
    powerModeBattery = bat;
  }

  bool onBattery() {
    return powerModeBattery;
  }
};

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


void setup () {
  DINIT(57600, ASKSIN_PLUS_PLUS_IDENTIFIER);
  sdev.init(hal);
  buttonISR(cfgBtn, CONFIG_BUTTON_PIN);
  hal.battery.init(seconds2ticks(60UL * 60), sysclock);
  hal.battery.critical(19);
  sdev.initDone();
#ifdef LCD_ADDRESS
  uint8_t serial[11];
  sdev.getDeviceSerial(serial);
  serial[10]=0;
  lcd.initLCD(serial);
  buttonISR(lcd, BACKLIGHT_BTN_PIN);
#endif
}

void loop() {
  bool worked = hal.runready();
  bool poll = sdev.pollRadio();
  if ( worked == false && poll == false ) {
    if (sdev.onBattery() == true)
      hal.activity.savePower<Sleep<>>(hal);
    else
      hal.activity.savePower<Idle<>>(hal);
  }
}
VG Andreas

UwePv
Beiträge: 243
Registriert: 23.02.2018, 14:35
Wohnort: Salzwedel
Hat sich bedankt: 34 Mal
Danksagung erhalten: 10 Mal

Re: HB-UNI-Sen-CURRENT - Stromsensor

Beitrag von UwePv » 06.03.2022, 13:59

Besten Dank :D

Werde heute Nachmittag Probieren und Berichten.
Würde am liebsten gleich, aber es kommt noch Besuch.
noch Anfänger

1techone
Beiträge: 213
Registriert: 19.01.2016, 10:23
System: Alternative CCU (auf Basis OCCU)
Hat sich bedankt: 49 Mal
Danksagung erhalten: 19 Mal

Re: HB-UNI-Sen-CURRENT - Stromsensor

Beitrag von 1techone » 06.03.2022, 14:59

Hallo Uwe,
hier meine Version, die schon seit langem bestens fuktioniert:
CUR.jpg
Wie man sieht, habe ich 5 Sensoren und ein 20x4 Display
1 = meine Solarstromerzeugung
2 - 5 = Verbraucher
6 = der Gesamtverbrauch addiert

Code: Alles auswählen

//- -----------------------------------------------------------------------------------------------------------------------
// AskSin++
// 2016-10-31 papa Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/
// 2020-07-04 jp112sdl Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/
//- -----------------------------------------------------------------------------------------------------------------------
// ci-test=yes board=328p aes=no

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

#define LCD_ADDRESS 0x3F

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

#include <AskSinPP.h>
#include <LowPower.h>

#include <Register.h>
#include <MultiChannelDevice.h>

#ifdef LCD_ADDRESS
#include <LiquidCrystal_I2C.h>
#define BACKLIGHT_BTN_PIN  6
#endif

#include "Sensors/Ads1x15.h"
#define ADS1115_ADDR_1 0x4A
#define ADS1115_ADDR_2 0x4B
#define ADS1115_ADDR_3 0x48
#define ADS_SENSOR_GAIN   adsGain_t::GAIN_TWO


#define CONFIG_BUTTON_PIN  8
#define LED_PIN            4
#define BUSY_LED_PIN       5

#define NUM_CHANNELS       5

#define PEERS_PER_CHANNEL  4

using namespace as;

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

enum conditionTypes { ct_none, ct_above, ct_below, ct_disabled };
typedef struct {
  uint32_t current;
  uint8_t conditionType = ct_none;
  bool ok;
} _currentSensor;

// define all device properties
const struct DeviceInfo PROGMEM devinfo = {
  {0xF3, 0x4E, 0x01},          // Device ID
  "HJN_CUR_01",                // Device Serial
  {0xF3, 0x4E},                // Device Model
  0x10,                        // Firmware Version
  0x53,                        // Device Type
  {0x01, 0x01}                 // Info Bytes
};

/**
   Configure the used hardware
*/
typedef AskSin<StatusLed<LED_PIN>, BatterySensor, Radio<AvrSPI<10, 11, 12, 13>, 2>> Hal;
typedef StatusLed<BUSY_LED_PIN> BusyLed;
Hal hal;

DEFREGISTER(DReg0, MASTERID_REGS, DREG_LOWBATLIMIT, 0x07, 0x1f, 0x20, 0x21, DREG_BACKONTIME, DREG_POWERSUPPLY)
class DevList0 : public RegList0<DReg0> {
  public:
    DevList0 (uint16_t addr) : RegList0<DReg0>(addr) {}

    bool Sendeintervall (uint8_t value) const {
      return this->writeRegister(0x21, value & 0xff);
    }
    uint8_t Sendeintervall () const {
      return this->readRegister(0x21, 0);
    }

    bool Messintervall (uint16_t value) const {
      return this->writeRegister(0x1f, (value >> 8) & 0xff) && this->writeRegister(0x20, value & 0xff);
    }
    uint16_t Messintervall () const {
      return (this->readRegister(0x1f, 0) << 8) + this->readRegister(0x20, 0);
    }

    bool conditionCheckAverage () const {
      return this->readRegister(0x07,0);
    }
    bool conditionCheckAverage (uint8_t value) const {
      return this->writeRegister(0x07,value);
    }

    void defaults () {
      clear();
      lowBatLimit(22);
      Sendeintervall(2);
      Messintervall(5);
      powerSupply(false); //true = battery mode
      backOnTime(60);
      conditionCheckAverage(false);
    }
};

DEFREGISTER(CReg1, CREG_AES_ACTIVE, CREG_COND_TX_THRESHOLD_HI, CREG_COND_TX_THRESHOLD_LO, CREG_COND_TX_DECISION_ABOVE, CREG_COND_TX_DECISION_BELOW, 0x01, 0x02, 0x03)
class DevList1 : public RegList1<CReg1> {
  public:
    DevList1 (uint16_t addr) : RegList1<CReg1>(addr) {}

    bool sampleTime (uint16_t value) const {
      return this->writeRegister(0x01, (value >> 8) & 0xff) && this->writeRegister(0x02, value & 0xff);
    }
    uint16_t sampleTime () const {
      return (this->readRegister(0x01, 0) << 8) + this->readRegister(0x02, 0);
    }

    bool sensorType (uint16_t value) const {
      return this->writeRegister(0x03, value & 0xff);
    }
    uint16_t sensorType () const {
      return this->readRegister(0x03, 0);
    }

    void defaults () {
      clear();
      condTxThresholdHi(1000);
      condTxDecisionAbove(200);
      condTxThresholdLo(100);
      condTxDecisionBelow(0);
      sensorType(2);
      sampleTime(500);
    }
};

class MeasureEventMsg : public Message {
  public:
    void init(uint8_t msgcnt, uint32_t *sensorValues, _currentSensor *cs, bool batlow) {
      Message::init(0xa + (NUM_CHANNELS * 3), msgcnt, 0x53, BCAST, batlow ? 0x80 : 0x00, cs[0].ok ? 0x41 : 0xC1);
      // DPRINT(F("+Current (#0) : ")); DDECLN(sensorValues[0]);
      pload[0] =  (sensorValues[0] >> 8) & 0xff;
      pload[1] = (sensorValues[0])   & 0xff;
      for (uint8_t s = 0; s < NUM_CHANNELS - 1; s++) {
        // DPRINT(F("+Current (#")); DDEC(s + 1); DPRINT(F(") : ")); DDECLN(sensorValues[s+1]);
        pload[2+(s * 3)] = (cs[s+1].ok ? 0x42 : 0xC2) + s;
        pload[3+(s * 3)] = (sensorValues[s+1] >> 8) & 0xff;
        pload[4+(s * 3)] =  sensorValues[s+1]       & 0xff;
      }
    }
};

#ifdef LCD_ADDRESS
class LcdType :  public Button {
public:
  class BacklightAlarm : public Alarm {
    LcdType& lcdDev;
  public:
    BacklightAlarm (LcdType& l) :  Alarm(0), lcdDev(l) {}
    virtual ~BacklightAlarm () {}
    void restartTimer(uint8_t sec) {
      sysclock.cancel(*this);
      set(seconds2ticks(sec));
      lcdDev.lcd.backlight();
      sysclock.add(*this);
    }

    virtual void trigger (__attribute__((unused)) AlarmClock& clock) {
      lcdDev.lcd.noBacklight();
    }
  }backlightalarm;
private:
  bool _present;
  bool first;
  uint8_t backlightOnTime;

  byte arrowUp[8] = { 0b00000, 0b00100, 0b01110, 0b10101, 0b00100, 0b00100, 0b00100, 0b00000 };
  byte arrowDown[8] = { 0b00000, 0b00100, 0b00100, 0b00100, 0b10101, 0b01110, 0b00100, 0b00000 };

public:
  LiquidCrystal_I2C lcd;
  LcdType () :  backlightalarm(*this), _present(false), first(true), backlightOnTime(10), lcd(LCD_ADDRESS, 16, 2){}
  virtual ~LcdType () {}

  void displayValues(_currentSensor *cs) {
    if (_present == true) {
      if (first) {
        first = false;
       lcd.setCursor(0, 0);
       //       ("123456_123456_123456123456_123456_123456")
       lcd.print("SolErz WWHeiz SpuelMWaschM TrockM VerbSu");
      }
     lcd.setCursor(0, 1);
     lcd.print("                ");

     lcd.setCursor(cs[0].current > 999 ? 0 : 1, 1);
     lcd.print(cs[0].current / 100.0, 2);

     lcd.setCursor(cs[1].current > 999 ? 7 : 8, 1);
     lcd.print(cs[1].current / 100.0, 2);

     lcd.setCursor(cs[2].current > 999 ? 14 : 15, 1);
     lcd.print(cs[2].current / 100.0, 2);

     lcd.setCursor(cs[3].current > 999 ? 20 : 21, 1);
     lcd.print(cs[3].current / 100.0, 2);

     lcd.setCursor(cs[4].current > 999 ? 27 : 28, 1);
     lcd.print(cs[4].current / 100.0, 2);

     lcd.setCursor(cs[5].current > 999 ? 34 : 35, 1);
     lcd.print((cs[1].current + cs[2].current + cs[3].current + cs[4].current) / 100.0, 2);
    }                            
  }

  void showCondition(uint8_t conditionType, uint8_t channel) {
    if (channel < 3) lcd.setCursor(channel * 7 + 5, 1);
    if (channel > 2) lcd.setCursor((channel-3) * 7 + 25, 1);
    if (conditionType == ct_above) lcd.write(byte(0));
    if (conditionType == ct_below) lcd.write(byte(1));
    if (conditionType == ct_none) lcd.print("*");
    if (conditionType == ct_disabled) lcd.print("A");
  }

  void initLCD(uint8_t *serial) {
    Wire.begin();
    Wire.beginTransmission(LCD_ADDRESS);
    if (Wire.endTransmission() == 0) {
      _present = true;
      lcd.init();
      lcd.createChar(0, arrowUp);
      // create a new character
      lcd.createChar(1, arrowDown);
      lcd.backlight();
      lcd.setCursor(0, 0);
      lcd.print(ASKSIN_PLUS_PLUS_IDENTIFIER);
      lcd.setCursor(3, 1);
      lcd.setContrast(200);
      lcd.print((char*)serial);

      if (backlightOnTime > 0) backlightalarm.restartTimer(backlightOnTime);

    } else {
      DPRINT("LCD Display not found at 0x");DHEXLN((uint8_t)LCD_ADDRESS);
    }
  }

  void setBackLightOnTime(uint8_t t) {
    backlightOnTime = t;
    if (backlightOnTime == 0)
      lcd.backlight();
    else
      lcd.noBacklight();
  }

  virtual void state(uint8_t s) {
    Button::state(s);
    if (s==released ) {
      if (backlightOnTime > 0) backlightalarm.restartTimer(backlightOnTime);
    }
  }
};
LcdType lcd;
#endif

class MeasureChannel : public Channel<Hal, DevList1, EmptyList, List4, PEERS_PER_CHANNEL, DevList0> {
  private:
    enum sensorTypes { SCT013015, SCT013020, SCT013030, SCT013050, SCT0130100, INA219, ACS712_or_other };
  public:
    MeasureChannel () : Channel() {}
    virtual ~MeasureChannel () {}

    virtual void configChanged() {
    /*  DPRINT("configChanged on Ch");DDECLN(number());
      DPRINT("condTxThresholdHi  : ");DDECLN(this->getList1().condTxThresholdHi());
      DPRINT("condTxDecisionAbove: ");DDECLN(this->getList1().condTxDecisionAbove());
      DPRINT("condTxThresholdLo  : ");DDECLN(this->getList1().condTxThresholdLo());
      DPRINT("condTxDecisionBelow: ");DDECLN(this->getList1().condTxDecisionBelow());
      DPRINT("sensorType         : ");DDECLN(this->getList1().sensorType());
      DPRINT("sampleTime         : ");DDECLN(this->getList1().sampleTime());*/
    }

    void checkConditions(_currentSensor *sensor) {
      uint8_t sIdx = number()-1;

      static uint8_t evcnt  = 0;
      uint8_t decisionValue = 0;
      bool sendConditionalSwitchCommand = false;

      uint32_t current     = sensor[sIdx].current;
      uint16_t thresholdHi = this->getList1().condTxThresholdHi();
      uint16_t thresholdLo = this->getList1().condTxThresholdLo();

      if (thresholdHi > 0) {
        SensorEventMsg& rmsg = (SensorEventMsg&)device().message();

        if (current > thresholdHi && sensor[sIdx].conditionType != ct_above) {
          sensor[sIdx].conditionType = ct_above;
          decisionValue = this->getList1().condTxDecisionAbove();
          sendConditionalSwitchCommand = true;
        }

        if (current < thresholdLo && sensor[sIdx].conditionType != ct_below) {
          sensor[sIdx].conditionType = ct_below;
          decisionValue = this->getList1().condTxDecisionBelow();
          sendConditionalSwitchCommand = true;
        }

        if (sendConditionalSwitchCommand == true) {
          DPRINTLN("sendConditionalSwitchCommand");
          rmsg.init(device().nextcount(), number(), evcnt++, decisionValue, false , false);
          device().sendPeerEvent(rmsg, *this);
        }
        lcd.showCondition(sensor[sIdx].conditionType, sIdx);
      } else {
        lcd.showCondition(ct_disabled, sIdx);
      }

    }

    uint8_t sctFactor() {
      uint8_t sensorType = this->getList1().sensorType();
      switch (sensorType) {
        case SCT013015:
          return 15;
        case SCT013020:
          return 20;
        case SCT013030:
          return 30;
        case SCT013050:
          return 50;
        case SCT0130100:
          return 100;
        default:
          return 0;
      }
    }

    uint16_t sampleTime() {
      return this->getList1().sampleTime();
    }

    uint8_t status () const { return 0; }
    uint8_t flags  () const { return device().battery().low() ? 0x80 : 00; }
};

class DevType : public MultiChannelDevice<Hal, MeasureChannel, NUM_CHANNELS, DevList0> {
private:
  bool powerModeBattery;
  uint16_t txInterval;
  uint16_t measureInterval;
  bool boot;
  bool lcd_present;
  BusyLed busyLed;
public:
  class CurrentSensors : public Alarm {
    DevType&   dev;
    Sens_Ads1x15<ADS1115_ADDR_1> ads1;
    Sens_Ads1x15<ADS1115_ADDR_2> ads2;
    Sens_Ads1x15<ADS1115_ADDR_3> ads3;
    _currentSensor cs[NUM_CHANNELS];
    uint32_t cumulatedCurrentValues[NUM_CHANNELS];
    uint8_t measureCount;
    public:
       CurrentSensors (DevType& d) : Alarm(0), dev(d), measureCount(0) {}
       virtual ~CurrentSensors () {}
       void measure() {
         //measurement here:
         cs[0].current = ads1.getCurrent_0_1(dev.channel(1).sampleTime(), dev.channel(1).sctFactor());cs[0].ok = ads1.checkSensor();
         cs[1].current = ads1.getCurrent_2_3(dev.channel(2).sampleTime(), dev.channel(2).sctFactor());cs[1].ok = ads1.checkSensor();
         cs[2].current = ads2.getCurrent_0_1(dev.channel(3).sampleTime(), dev.channel(3).sctFactor());cs[2].ok = ads2.checkSensor();
         cs[3].current = ads2.getCurrent_2_3(dev.channel(4).sampleTime(), dev.channel(4).sctFactor());cs[3].ok = ads2.checkSensor();
         cs[4].current = ads3.getCurrent_0_1(dev.channel(5).sampleTime(), dev.channel(5).sctFactor());cs[4].ok = ads3.checkSensor();
         // cs[5].current = ads3.getCurrent_2_3(dev.channel(6).sampleTime(), dev.channel(6).sctFactor());cs[5].ok = ads3.checkSensor();

#ifdef LCD_ADDRESS
         lcd.displayValues(cs);
#endif
       }

       void init() {
         ads1.init(ADS_SENSOR_GAIN);
         ads2.init(ADS_SENSOR_GAIN);
         ads3.init(ADS_SENSOR_GAIN);
       }

       virtual void trigger (__attribute__ ((unused)) AlarmClock& clock) {
         dev.busyLed.ledOn();
         measure();
         dev.busyLed.ledOff();

         // add measured current to the cumulated values
         cumulatedCurrentValues[0] += cs[0].current;
         cumulatedCurrentValues[1] += cs[1].current;
         cumulatedCurrentValues[2] += cs[2].current;
         cumulatedCurrentValues[3] += cs[3].current;
         cumulatedCurrentValues[4] += cs[4].current;
         // cumulatedCurrentValues[5] += cs[5].current;

         measureCount++;
         DPRINT("measure() #");DDEC(measureCount);DPRINT(" of ");DDECLN(dev.txInterval);

         // check if any of the sensors is above/below a threshold
         for (uint8_t ch = 0; ch < NUM_CHANNELS; ch++) {
           // check conditions on each measure() or only on txInterval
           if (dev.getList0().conditionCheckAverage() == false) {
             dev.channel(ch+1).checkConditions(cs);
           } else {
             if (measureCount >= dev.txInterval) {
               cs[ch].current = cumulatedCurrentValues[ch] / measureCount;
               dev.channel(ch+1).checkConditions(cs);
             }
           }
         }

         set(seconds2ticks(max(10, dev.measureInterval * SYSCLOCK_FACTOR)));

         // send the cyclic message 
         if (measureCount >= dev.txInterval) {
           bool batlow = dev.onBattery() ? dev.battery().low() : false;

           // divide cumulated values by measure count
           for (uint8_t ch = 0; ch < NUM_CHANNELS; ch++)
             cumulatedCurrentValues[ch] /= measureCount;
             
           MeasureEventMsg& msg = (MeasureEventMsg&)dev.message();             
           msg.init(dev.nextcount(), cumulatedCurrentValues, cs, batlow);
           dev.broadcastEvent(msg);

           // reset values
           measureCount = 0;
           for (uint8_t s = 0; s < NUM_CHANNELS; s++)
             cumulatedCurrentValues[s] = 0;
         }
         sysclock.add(*this);
       }
    } currentSensors;

  typedef MultiChannelDevice<Hal, MeasureChannel, NUM_CHANNELS, DevList0> TSDevice;
  DevType(const DeviceInfo& info, uint16_t addr) : TSDevice(info, addr), powerModeBattery(true), txInterval(0), measureInterval(0), boot(true), lcd_present(false), currentSensors(*this) {}
  virtual ~DevType () {}

  void init (Hal& hal) {
    TSDevice::init(hal);
    currentSensors.init();
    busyLed.init();
    busyLed.ledOn(seconds2ticks(2));
  }

  virtual void configChanged () {
    TSDevice::configChanged();
    uint8_t batlow = max(this->getList0().lowBatLimit(),19);
    // DPRINT(F("*LOWBAT Limit: "));DDECLN(batlow);
    this->battery().low(batlow);

    if (this->getList0().Messintervall() != measureInterval) {
      measureInterval = this->getList0().Messintervall();
      //DPRINT(F("*ME Intervall: ")); DDECLN(measureInterval);
      if (boot == false) {
        sysclock.cancel(currentSensors);
        currentSensors.set(seconds2ticks(max(10, measureInterval) * SYSCLOCK_FACTOR));
      } else {
        currentSensors.set(seconds2ticks(3));
      }
      sysclock.add(currentSensors);
    }

    txInterval = max(1, this->getList0().Sendeintervall());
    //DPRINT(F("*TX Intervall: ")); DDECLN(txInterval);

    onBattery(this->getList0().powerSupply());
    //DPRINT(F("*Batterymode : ")); DDECLN(onBattery());

    uint8_t bOn = this->getList0().backOnTime();
    //DPRINT(F("*LCD Backlight Ontime : ")); DDECLN(bOn);
    lcd.setBackLightOnTime(bOn);

    //bool cc = this->getList0().conditionCheckAverage();
    //DPRINT(F("*Condition Check on Average : ")); DDECLN(cc);

    boot = false;
  }

  void onBattery(bool bat) {
    powerModeBattery = bat;
  }

  bool onBattery() {
    return powerModeBattery;
  }
};

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


void setup () {
  DINIT(57600, ASKSIN_PLUS_PLUS_IDENTIFIER);
  sdev.init(hal);
  buttonISR(cfgBtn, CONFIG_BUTTON_PIN);
  hal.battery.init(seconds2ticks(60UL * 60), sysclock);
  hal.battery.critical(19);
  sdev.initDone();
#ifdef LCD_ADDRESS
  uint8_t serial[11];
  sdev.getDeviceSerial(serial);
  serial[10]=0;
  lcd.initLCD(serial);
  buttonISR(lcd, BACKLIGHT_BTN_PIN);
#endif
}

void loop() {
  bool worked = hal.runready();
  bool poll = sdev.pollRadio();
  if ( worked == false && poll == false ) {
    if (sdev.onBattery() == true)
      hal.activity.savePower<Sleep<>>(hal);
    else
      hal.activity.savePower<Idle<>>(hal);
  }
}
Ich hoffe damit kannst du etwas anfangen !

mit freundlichem Gruß
Jürgen

UwePv
Beiträge: 243
Registriert: 23.02.2018, 14:35
Wohnort: Salzwedel
Hat sich bedankt: 34 Mal
Danksagung erhalten: 10 Mal

Re: HB-UNI-Sen-CURRENT - Stromsensor

Beitrag von UwePv » 06.03.2022, 20:26

Jetzt lauft es, wenn ich auch die LED Anzeige hinbekommen habe, melde ich noch mal. :D
LED Felder sortieren muss ich auch noch lernen.
noch Anfänger

UwePv
Beiträge: 243
Registriert: 23.02.2018, 14:35
Wohnort: Salzwedel
Hat sich bedankt: 34 Mal
Danksagung erhalten: 10 Mal

Re: HB-UNI-Sen-CURRENT - Stromsensor

Beitrag von UwePv » 06.03.2022, 23:53

Hallo Jürgen

Der Sketch hat sofort funktioniert. :D

Um ihn an meine Aufgaben anzupassen, habe ich da rann herumgebastelt, und fast hinbekommen.
Anz1.jpg
Anz2.jpg
Anz3.jpg
Aber das einzige Problem ist noch die letzten 6 Zeichen.
Wie kann man die Ausgabe auf 2 Zeichen hinter dem Komma begrenzen oder was kann man noch machen?

erfolg1.PNG
Schönen Abend noch
noch Anfänger

UwePv
Beiträge: 243
Registriert: 23.02.2018, 14:35
Wohnort: Salzwedel
Hat sich bedankt: 34 Mal
Danksagung erhalten: 10 Mal

Re: HB-UNI-Sen-CURRENT - Stromsensor

Beitrag von UwePv » 07.03.2022, 00:38

Problem gelöst :D
Danke an alle.
Lösung.PNG
Lösung.PNG (22.52 KiB) 872 mal betrachtet
noch Anfänger

zahnheinrich
Beiträge: 62
Registriert: 15.06.2015, 09:14
Hat sich bedankt: 1 Mal
Danksagung erhalten: 3 Mal

Re: HB-UNI-Sen-CURRENT - Stromsensor

Beitrag von zahnheinrich » 08.03.2022, 15:41

Hallo,
ich habe eine Frage an Jerome @jp112sdl:

Ich möchte den Sensor nachbauen. Zur Sicherheit folgende Fragen:
Bild

1. Sind die pinken Leiterbahnen leitend verbunden oder ist das eine nichtleitende Überkreuzung?
2. Meine Sensoren SCT013-030 haben rot/schwarze Zuleitungen.
Spielt die Polarität eine Rolle, da im Schaltbild keine Unterscheidung sichtbar ist?

Vielen Dank!
Ulrich
Dateianhänge
Screenshot (17)_LI.jpg
Screenshot (17)_LI.jpg (134.86 KiB) 827 mal betrachtet

Benutzeravatar
stan23
Beiträge: 2030
Registriert: 13.12.2016, 21:14
System: Alternative CCU (auf Basis OCCU)
Wohnort: Altmühltal
Hat sich bedankt: 577 Mal
Danksagung erhalten: 335 Mal
Kontaktdaten:

Re: HB-UNI-Sen-CURRENT - Stromsensor

Beitrag von stan23 » 08.03.2022, 16:41

zahnheinrich hat geschrieben:
08.03.2022, 15:41
1. Sind die pinken Leiterbahnen leitend verbunden oder ist das eine nichtleitende Überkreuzung?
Sie sind leitend verbunden.
Die 4 Verbindungen VCC, GND, SCL, SDA gehen an beide ADC-Platinen parallel, und die I2C-Adresse wird durch die weißliche Brücke eingestellt.

Beim Anschluss der SCT würde ich es einfach ausprobieren, entweder messen sie andersherum nicht oder es ist egal ;)
Viele Grüße
Marco

RaspberryMatic als VM auf einem NUC mit Proxmox und USB-Funkmodul
~80 Geräte (HM, HmIP, HMW, HBW, AskSin)

Antworten

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