Advertisement
jp112

HB-UNI-Sensor1 with BH1750 support

Jun 12th, 2018
859
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 12.71 KB | None | 0 0
  1. //- -----------------------------------------------------------------------------------------------------------------------
  2. // AskSin++
  3. // 2016-10-31 papa Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/
  4. // HB-UNI-Sensor1
  5. // 2018-05-11 Tom Major (Creative Commons)
  6. // 2018-06-12 jp112sdl (Creative Commons) - added BH1750 support
  7. //- -----------------------------------------------------------------------------------------------------------------------
  8.  
  9. //----------------------------------------------
  10. // !! NDEBUG should be defined when the sensor development and testing ist done and the device moves to serious operation mode
  11. // With BME280 and TSL2561 activated, this saves 2k Flash and 560 Bytes RAM (especially the RAM savings are important for stability / dynamic memory allocation etc.)
  12. // This will get rid of the Arduino warning "Low memory available, stability problems may occur."
  13. //#define NDEBUG
  14.  
  15. //----------------------------------------------
  16. // define this to read the device id, serial and device type from bootloader section
  17. // #define USE_OTA_BOOTLOADER
  18.  
  19. #define  EI_NOTEXTERNAL
  20. #include <EnableInterrupt.h>
  21. #include <AskSinPP.h>
  22. #include <LowPower.h>
  23. #include <Register.h>
  24. #include <MultiChannelDevice.h>
  25.  
  26. //----------------------------------------------
  27. // SENSOR_ definitions for real sensors, if undefined dummy sensor values are used (for testing HM/RM BidCoS device communication without the real sensors)
  28. //#define SENSOR_DS18X20
  29. //#define SENSOR_BME280
  30. //#define SENSOR_TSL2561
  31. //#define SENSOR_BH1750
  32.  
  33. //----------------------------------------------
  34. // Battery definitions
  35. #define BAT_VOLT_LOW        22       // 2.2V
  36. #define BAT_VOLT_CRITICAL   19       // 1.9V
  37.  
  38. //----------------------------------------------
  39. // Pin definitions
  40. #define CONFIG_BUTTON_PIN   8
  41. #define LED_PIN             6
  42. #define ONEWIRE_PIN         3
  43.  
  44. // number of available peers per channel
  45. #define PEERS_PER_CHANNEL   6
  46.  
  47. #ifdef SENSOR_DS18X20
  48. #include "Sensors/Sens_Ds18x20.h"
  49. #endif
  50.  
  51. #ifdef SENSOR_BME280
  52. #include "Sensors/Sens_Bme280.h"
  53. #endif
  54.  
  55. #ifdef SENSOR_TSL2561
  56. #include "Sensors/Sens_Tsl2561.h"
  57. #endif
  58.  
  59. #ifdef SENSOR_BH1750
  60. #include <sensors/Bh1750.h>
  61. #endif
  62. #define BH1750_BRIGHTNESS_FACTOR   1.2
  63.  
  64.  
  65. // all library classes are placed in the namespace 'as'
  66. using namespace as;
  67.  
  68. // define all device properties
  69. const struct DeviceInfo PROGMEM devinfo = {
  70.   {0xf1, 0x03, 0xA3},        // Device ID
  71.   "UNISENS001",              // Device Serial
  72.   {0xF1, 0x03},              // Device Model
  73.   0x10,                      // Firmware Version
  74.   as::DeviceType::THSensor,  // Device Type
  75.   {0x01, 0x01}               // Info Bytes
  76. };
  77.  
  78. // Configure the used hardware
  79. typedef AvrSPI<10, 11, 12, 13> SPIType;
  80. typedef Radio<SPIType, 2> RadioType;
  81. typedef StatusLed<LED_PIN> LedType;
  82. typedef AskSin<LedType, BatterySensor, RadioType> BaseHal;
  83.  
  84. class Hal : public BaseHal {
  85.   public:
  86.     void init (const HMID& id) {
  87.       BaseHal::init(id);
  88.       // init real time clock - 1 tick per second
  89.       //rtc.init();
  90.       // measure battery every 12h
  91.       battery.init(seconds2ticks(12UL * 60 * 60), sysclock);
  92.       battery.low(BAT_VOLT_LOW);
  93.       battery.critical(BAT_VOLT_CRITICAL);
  94.     }
  95.  
  96.     bool runready () {
  97.       return sysclock.runready() || BaseHal::runready();
  98.     }
  99. } hal;
  100.  
  101. class WeatherEventMsg : public Message {
  102.   public:
  103.     void init(uint8_t msgcnt, int16_t temp, uint16_t airPressure, uint8_t humidity, uint32_t brightness, uint16_t batteryVoltage, bool batLow) {
  104.  
  105.       uint8_t t1 = (temp >> 8) & 0x7f;
  106.       uint8_t t2 = temp & 0xff;
  107.       if ( batLow == true ) {
  108.         t1 |= 0x80; // set bat low bit
  109.       }
  110.       Message::init(0x14, msgcnt, 0x70, BCAST, t1, t2); // first byte determines message length; pload[0] starts at byte 13
  111.  
  112.       // 1 Byte payload -> length 0x0C
  113.       // 6 Byte payload -> length 0x11
  114.       // 9 Byte payload -> length 0x14
  115.       // max. msg length 0x19 ?
  116.  
  117.       // BIDI|WKMEUP: erwartet ACK vom Empfänger, ohne ACK wird das Senden wiederholt
  118.       //       LazyConfig funktioniert, d.h. eine anstehende Conf.Änderung von der CCU wird nach dem nächsten Senden übernommen
  119.       //       Aber erhöhter Funkverkehr wegen ACK
  120.       // BCAST: ohne ACK zu Erwarten, Standard für HM Sensoren
  121.       //       LazyConfig funktioniert nicht, d.h. eine anstehende Conf.Änderung von der CCU muss durch den Config Button am Sensor übernommen werden!!
  122.  
  123.       // papa:
  124.       // BIDI - fordert den Empfänger auf ein Ack zu schicken. Das wird auch zwingend für AES-Handling gebraucht.
  125.       // BCAST - signalisiert eine Broadcast-Message. Das wird z.B. verwendet, wenn mehrere Peers vor einen Sensor existieren.
  126.       //   Es wird dann an einen Peer gesndet und zusätzlich das BCAST-Flag gesetzt. So dass sich alle die Nachrricht ansehen.
  127.       //   Ein Ack macht dann natürlich keinen Sinn - es ist ja nicht klar, wer das senden soll.
  128.       // WKMEUP - wird für LazyConfig verwendet. Ist es in einer Message gesetzt, so weiss die Zentrale, dass das Geräte noch kurz auf weitere Nachrichten wartet.
  129.       //   Die Lib setzt diese Flag für die StatusInfo-Message automatisch. Außerdem bleibt nach einer Kommunikation der Empfang grundsätzlich für 500ms angeschalten.
  130.  
  131.       // airPressure
  132.       pload[0] = (airPressure >> 8) & 0xff;
  133.       pload[1] = airPressure & 0xff;
  134.  
  135.       // humidity
  136.       pload[2] = humidity;
  137.  
  138.       // brightness (Lux)
  139.       pload[3] = (brightness >> 24) & 0xff;
  140.       pload[4] = (brightness >> 16) & 0xff;
  141.       pload[5] = (brightness >>  8) & 0xff;
  142.       pload[6] = (brightness >>  0) & 0xff;
  143.  
  144.       // batteryVoltage
  145.       pload[7] = (batteryVoltage >> 8) & 0xff;
  146.       pload[8] = batteryVoltage & 0xff;
  147.     }
  148. };
  149.  
  150. // die "freien" Register 0x20/21 werden hier als 16bit memory für das Update Intervall in Sek. benutzt
  151. // siehe auch hb_uni_sensor1.xml, <parameter id="Update Intervall"> ..
  152. // ausserdem werden die Register 0x22/0x23 für den konf. Parameter Höhe benutzt
  153. DEFREGISTER(Reg0, MASTERID_REGS, DREG_TRANSMITTRYMAX, DREG_LOWBATLIMIT, 0x20, 0x21, 0x22, 0x23)
  154. class SensorList0 : public RegList0<Reg0> {
  155.   public:
  156.     SensorList0(uint16_t addr) : RegList0<Reg0>(addr) {}
  157.  
  158.     bool updIntervall (uint16_t value) const {
  159.       return this->writeRegister(0x20, (value >> 8) & 0xff) && this->writeRegister(0x21, value & 0xff);
  160.     }
  161.     uint16_t updIntervall () const {
  162.       return (this->readRegister(0x20, 0) << 8) + this->readRegister(0x21, 0);
  163.     }
  164.  
  165.     bool height (uint16_t value) const {
  166.       return this->writeRegister(0x22, (value >> 8) & 0xff) && this->writeRegister(0x23, value & 0xff);
  167.     }
  168.     uint16_t height () const {
  169.       return (this->readRegister(0x22, 0) << 8) + this->readRegister(0x23, 0);
  170.     }
  171.  
  172.     void defaults () {
  173.       clear();
  174.       transmitDevTryMax(6);
  175.       lowBatLimit(BAT_VOLT_LOW);
  176.       updIntervall(300);
  177.       height(0);
  178.     }
  179. };
  180.  
  181. class WeatherChannel : public Channel<Hal, List1, EmptyList, List4, PEERS_PER_CHANNEL, SensorList0>, public Alarm {
  182.  
  183.     WeatherEventMsg msg;
  184.  
  185.     int16_t       temperature;
  186.     uint16_t      airPressure;
  187.     uint8_t       humidity;
  188.     uint32_t      brightness;
  189.     uint16_t      batteryVoltage;
  190.     bool          sensorSetupDone;
  191.  
  192. #ifdef SENSOR_DS18X20
  193.     Sens_Ds18x20 ds18x20;
  194. #endif
  195.  
  196. #ifdef SENSOR_BME280
  197.     Sens_Bme280  bme280;
  198. #endif
  199.  
  200. #ifdef SENSOR_TSL2561
  201.     Sens_Tsl2561 tsl2561;
  202. #endif
  203.  
  204. #ifdef SENSOR_BH1750
  205.     Bh1750<>     bh1750;
  206. #endif
  207.  
  208.   public:
  209.     WeatherChannel () : Channel(), Alarm(seconds2ticks(60)), sensorSetupDone(false) {}
  210.     virtual ~WeatherChannel () {}
  211.  
  212.     virtual void trigger (__attribute__ ((unused)) AlarmClock& clock) {
  213.       // delayed sensor setup
  214.       if (!sensorSetupDone) {
  215. #ifdef SENSOR_DS18X20
  216.         ds18x20.init(ONEWIRE_PIN);
  217. #endif
  218. #ifdef SENSOR_BME280
  219.         bme280.init();
  220. #endif
  221. #ifdef SENSOR_TSL2561
  222.         tsl2561.init();
  223. #endif
  224. #ifdef SENSOR_BH1750
  225.         bh1750.init();
  226. #endif
  227.         sensorSetupDone = true;
  228.         DPRINTLN("Sensor setup done");
  229.       }
  230.       uint8_t msgcnt = device().nextcount();
  231.       measure();
  232.       msg.init(msgcnt, temperature, airPressure, humidity, brightness, batteryVoltage, device().battery().low());
  233.       device().sendPeerEvent(msg, *this);
  234.       // reactivate for next measure
  235.       uint16_t updCycle = this->device().getList0().updIntervall();
  236.       tick = seconds2ticks(updCycle);
  237.       clock.add(*this);
  238.     }
  239.  
  240.     // here we do the measurement
  241.     void measure () {
  242.  
  243. #ifdef SENSOR_DS18X20
  244.       ds18x20.measure();
  245.       temperature = ds18x20.temperature();
  246. #else
  247.       temperature = 150 + random(50);   // 15C +x
  248. #endif
  249.  
  250. #ifdef SENSOR_BME280
  251.       uint16_t height = this->device().getList0().height();
  252.       bme280.measure(height);
  253.       temperature = bme280.temperature();
  254.       airPressure = bme280.pressureNN();
  255.       humidity    = bme280.humidity();
  256. #else
  257.       airPressure = 1024 + random(9);   // 1024 hPa +x
  258.       humidity    = 66 + random(7);     // 66% +x
  259. #endif
  260.  
  261.       brightness = 67000 + random(1000);   // 67000 Lux +x
  262. #ifdef SENSOR_TSL2561
  263.       tsl2561.measure();
  264.       brightness = tsl2561.brightnessLux(); // also available: brightnessVis(), brightnessIR(), brightnessFull(), but these are dependent on integration time setting
  265. #endif
  266.  
  267. #ifdef SENSOR_BH1750
  268.       bh1750.measure();
  269.       brightness = bh1750.brightness() * BH1750_BRIGHTNESS_FACTOR;
  270.       DPRINT(F("BRIGHTNESS (BH1750)    : ")  ); DDECLN(brightness);
  271. #endif
  272.  
  273.       // convert default AskSinPP battery() resolution of 100mV to 1mV, last 2 digits will be 00
  274.       // for higher resolution, override battery() with modified voltage() calculation
  275.       // see my HB-SEC-WDS-2 for an example with higher resolution
  276.       batteryVoltage = 100UL * device().battery().current();
  277.     }
  278.  
  279.     void setup(Device<Hal, SensorList0>* dev, uint8_t number, uint16_t addr) {
  280.       Channel::setup(dev, number, addr);
  281.       tick = seconds2ticks(5);          // first message in 5 sec.
  282.       sysclock.add(*this);
  283.     }
  284.  
  285.     void configChanged() {
  286.       //DPRINTLN("Config changed: List1");
  287.     }
  288.  
  289.     uint8_t status () const {
  290.       return 0;
  291.     }
  292.  
  293.     uint8_t flags () const {
  294.       return 0;
  295.     }
  296. };
  297.  
  298. class SensChannelDevice : public MultiChannelDevice<Hal, WeatherChannel, 1, SensorList0> {
  299.   public:
  300.     typedef MultiChannelDevice<Hal, WeatherChannel, 1, SensorList0> TSDevice;
  301.     SensChannelDevice(const DeviceInfo& info, uint16_t addr) : TSDevice(info, addr) {}
  302.     virtual ~SensChannelDevice () {}
  303.  
  304.     virtual void configChanged () {
  305.       TSDevice::configChanged();
  306.       DPRINTLN("Config Changed: List0");
  307.  
  308.       uint8_t lowBatLimit = this->getList0().lowBatLimit();
  309.       DPRINT("lowBatLimit: "); DDECLN(lowBatLimit);
  310.       battery().low(lowBatLimit);
  311.  
  312.       uint8_t txDevTryMax = this->getList0().transmitDevTryMax();
  313.       DPRINT("transmitDevTryMax: "); DDECLN(txDevTryMax);
  314.  
  315.       uint16_t updCycle = this->getList0().updIntervall();
  316.       DPRINT("updCycle: "); DDECLN(updCycle);
  317.  
  318.       uint16_t height = this->getList0().height();
  319.       DPRINT("height: "); DDECLN(height);
  320.     }
  321. };
  322.  
  323. SensChannelDevice sdev(devinfo, 0x20);
  324. ConfigButton<SensChannelDevice> cfgBtn(sdev);
  325.  
  326. void setup () {
  327.   DINIT(57600, ASKSIN_PLUS_PLUS_IDENTIFIER);
  328.   sdev.init(hal);
  329.   buttonISR(cfgBtn, CONFIG_BUTTON_PIN);
  330.   sdev.initDone();
  331. }
  332.  
  333. void loop() {
  334.   bool worked = hal.runready();
  335.   bool poll = sdev.pollRadio();
  336.   if ( worked == false && poll == false ) {
  337.     // deep discharge protection
  338.     // if we drop below critical battery level - switch off all and sleep forever
  339.     if ( hal.battery.critical() ) {
  340.       // this call will never return
  341.       hal.activity.sleepForever(hal);
  342.     }
  343.     // if nothing to do - go sleep
  344.     hal.activity.savePower<Sleep<>>(hal);
  345.   }
  346. }
  347.  
  348.  
  349. /*
  350.   ----------------------------------------------
  351.   Die Registerklassen (Listen) eines Homematic-Gerätes
  352.  
  353.   Gerätebezogene Register
  354.   Gerätebezogene Register existieren für jedes HomeMatic-Gerät nur einmal und werden in der sogenannten List0 gespeichert.
  355.  
  356.   Kanalbezogene Register
  357.   Kanalbezogene Register existieren für jeden Kanal eines Gerätes einmal und werden in der sogenannten List1 gespeichert.
  358.  
  359.   Verknüpfungsbezogene Register
  360.   Diese Register sind am umfangreichsten und werden für jeden Verknüpfungspartner (peer) einzeln separat angelegt in der List3 (RegL_03.<peer>). Die grundsätzlichen Funktionen und ihre Zusammenhänge sind auch ausführlich in der Einsteigerdokumentation erklärt, inklusive Skizzen für die sogenannte state machine.
  361.  
  362.   https://wiki.fhem.de/wiki/Homematic-Register_von_A-Z_(Namen,_Erkl%C3%A4rung)
  363.   https://wiki.fhem.de/wiki/HomeMatic_Register_programmieren
  364.   ----------------------------------------------
  365. */
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement