Drahtloser Sensor zum Öffnen und Schließen mit erweiterter Funktionalität

Ich begrüße alle Leser von Habr und insbesondere die Leser der Sektion "DIY or Do it yourself"! Und wenn mir so etwas nicht einfällt, bin ich ein Arduino, ich kann ... Ich rühre das Hauptthema der Verwaltung von Aufzugskabinen nicht an :). Nach einigem Überlegen wollte ich aus irgendeinem Grund einen Öffnungs- und Schließsensor herstellen. Dieser Sensor basiert wie der Rest meines Handwerks, den ich kürzlich gemacht habe, auf Chips von Nordic Semiconductor. Der Sensor entschied sich für zwei Versionen, eine auf dem nRF52840-Chip und die zweite auf dem nRF52811-Chip.



Für die Versionen nRF52840 wurde das Chipmodul E73_2G4M08S1C der Firma EBYTE verwendet , für das Verciya-Chipmodul nRF52811 MC50SFA der Firma MiNEW . Ehrlich gesagt war die Suche nach erschwinglichen nRF52811-Chips immer noch ein Abenteuer. Als Ergebnis dieses Abenteuers im Gerät sind das Modul auf dem nRF52811-Chip von MINEW und die Brötchen in Form mehrerer Versionen der an diese Module gelöteten Chips nRF52810 und nRF52832.



Die Hauptfunktionalität des Geräts ist die Erkennung des Öffnens und Schließens anhand eines Reed-Schalters. Die Schaltung des Reed-Schalters wird mit Anti-Bounce neu phasengesteuert.

Schaltplan:


Arduino Schema :)



Als ich darüber nachdachte, was angemessen wäre, um die Hauptfunktionalität dieses Sensors zum Öffnen und Schließen zu verwässern, beschloss ich zu sehen, was auf dem Markt darüber zu finden ist. Wie sich fast nichts herausstellte, ist der Öffnungs- und Schließsensor auch in Afrika der Öffnungs- und Schließsensor. Die „fortschrittlichste“ Lösung wurde bei REDMOND gefunden . In ihrem BLE-Sensor (übrigens auch auf einem Chip von Nordic) sind neben dem Reed-Schalter ein Temperatursensor und ein kapazitiver Knopf auf dem TTP223-Chip implementiert. Aber aus irgendeinem Grund schien mir dies keine gute Lösung zu sein, wie nützlich die Temperaturwerte in der Nähe der Tür oder des Fensters waren (und was verhinderte, dass sie mit einem Chip gemessen wurden) und in welchen Situationen es angebracht ist, den Knopf am Sensor zu verwenden, der am Fenster oder an der Tür hängt (naja, außer vielleicht der Eingabe :)). Aus diesem Grund habe ich beschlossen, die Sicherheitsfunktionen meines Sensors zu erweitern.



Das Hauptauswahlkriterium war der Verbrauch zusätzlicher Sensoren, da beschlossen wurde, in diesem Sensor eine CR2032-Batterie zu verwenden. Die Gewinner unter den Kandidaten waren zwei Sensoren, der Beschleunigungsmesser LIS2DW12 und der Magnetfeldsensor DRV5032FB.

LIS2DW12 ist derzeit wahrscheinlich der wirtschaftlichste Beschleunigungsmesser. Im verbrauchsarmen Modus verbraucht dieser Beschleunigungsmesser 1 μA ( Datenblatt ). Der Magnetfeldsensor DRV5032FB zeigte ebenfalls hervorragende Verbrauchseigenschaften. Sein Verbrauch liegt bei 500nA ( Datenblatt ).

Es wurde beschlossen, den Beschleunigungsmesser im Stoßsensormodus und den Magnetfeldsensor für den vorgesehenen Zweck zu verwenden. Wenn ich über die Funktionalität des Stoßsensors ruhig war, ist die Verwendung eines Magnetfeldsensors immer noch eine sehr experimentelle Lösung, aber besser als ein Temperatursensor.



Der Software-Teil des Projekts wurde erstellt, um den Sensor im Maysensors-Netzwerk zu betreiben. Zumindest für jetzt. Mysensoren in der Variante der Arbeit an nordischen Chips (nRF24 (+ atmega 328, stm32f1), nRF51 und nRF52) auf der unteren Ebene verwenden das nordische proprietäre Protokoll - Enhanced ShockBurst (ESB), wodurch die Kompatibilität der Geräte mit nRF24 und nRF51-52 sichergestellt wird. Maysensors ist ein offenes Arduino-Projekt, um das sich in vielen Ländern der Welt bereits eine ziemlich große Gemeinschaft gebildet hat. Die guten Lösungen für nRF52-Chips sind jedoch, dass keine Maysensoren (ESB) verwendet werden müssen. Es reicht aus, die auf dem ZigBee- oder BLE-Protokoll basierende Software einfach zu ersetzen, da die Chips Multiprotokoll sind. ... In Bezug auf BLE werde ich ein wenig abschweifen. Schauen Sie sich an, was für ein wunderbarer Arduino NANO 33 Ble aus dem E73_2G4M08S1C-Modul hergestellt werden kann.Die Kosten für meinen NANO 33 betragen 4 US-Dollar.



Die Skizze für den Sensor wurde in Arduino erstellt. IDE für zusätzliche Bibliotheken wurde verwendet. Die Bibliothek für den Beschleunigungsmesser LIS2DW12 wurde von mir in den Standardeinstellungen der Register ein wenig geändert. In meiner Version funktioniert sie sofort mit den Einstellungen der niedrigsten Version des Stromverbrauchs ( verfügbar auf meinem Git ).

Ich werde die Logik des Programms beschreiben. In der Hauptbetriebsart befindet sich der Sensor in einem Traum mit konfigurierten externen Interrupts, nur 4 Interrupts. Es gibt zwei Konfigurationen von Interrupts: Konfigurationen konfigurieren Interrupts während des Programmvorgangs neu, abhängig vom Status des Reed-Schalters. Wenn die Tür geöffnet ist, sind Interrupts für den Stoßsensor und den Magnetfeldsensor deaktiviert. Sobald sich die Tür schließt, werden Interrupts für zwei dieser Sensoren aktiviert. Ich bin auch auf die Tatsache gestoßen, dass es während des Öffnens Situationen gab, in denen der Stoßsensor vor dem Reedschalter ausgelöst wurde und durch Vibrationen beim Öffnen des Schlosses verursacht wurde. Dieses Problem wurde nur mit der konfigurierten hohen Empfindlichkeit des Beschleunigungsmessers aufgezeichnet.

Um dieses Problem zu beseitigen, wurde beim Auslösen des Beschleunigungsmessers eine Wartezeit von 2 Sekunden eingeführt, während der der Stift des Reed-Schalters überwacht wird. Wenn während des Wartens eine Pegeländerung am Pin des Reed-Schalters auftritt, stoppt die weitere Verarbeitung des Ereignisses durch Unterbrechung vom Beschleunigungsmesser und die Verarbeitung des Ereignisses vom Reed-Schalter beginnt.

Der Sensor verfügt über einen Konfigurationsmodus. Wenn die Servicetaste gedrückt wird, wacht der Sensor bei Unterbrechung auf, das Funkmodul wechselt in den Abhörmodus und wartet auf eingehende Befehle von der UD-Steuerung. Wenn ein Befehl empfangen wird, schreibt der Sensor einen neuen Wert in den Speicher und wechselt sofort in den Betriebsmodus. Um den nächsten Befehl zu senden, muss die Aktivierung des Konfigurationsmodus wiederholt werden. Wenn der Sensor im Konfigurationsmodus innerhalb von 30 Sekunden nichts empfängt, wechselt er nach dieser Zeit ebenfalls in den Betriebsmodus und geht in den Ruhezustand. Zusätzlich zum Konfigurationsmodus können Sie über die Servicetaste die Präsentation der Sensorsensoren und das Zurücksetzen auf die Werkseinstellungen starten (der Sensor vergisst das Netzwerk, zu dem er hinzugefügt wurde, die Registrierung des Sensors nach dem Zurücksetzen muss erneut erfolgen).











Um den Sensor in Arduino IDE zu programmieren, müssen Sie Unterstützung für die folgenden Karten hinzufügen:

Sandeepmistry / Arduino-nRF5-
Mysensoren / ArduinoBoards-
Bibliotheken:

Mysensor
LIS2DW12-

Programmierer: st-link, j-link.

Skizzenprogramm
bool configMode = 0;
int8_t int_status = 0;
bool door_status = 1;
bool check;
bool magnet_status = 1;
bool nosleep = 0;
bool button_flag = 0;
bool onoff = 1;
bool flag_update_transport_param;
bool flag_sendRoute_parent;
bool flag_no_present;
bool flag_nogateway_mode;
bool flag_find_parent_process;
bool flag_fcount;
bool Ack_TL;
bool Ack_FP;
bool PRESENT_ACK;
bool send_a;
bool batt_flag;
byte conf_vibro_set = 2;
byte err_delivery_beat;
byte problem_mode_count;
uint8_t  countbatt = 0;
uint8_t batt_cap;
uint8_t old_batt_cap = 100;
uint32_t BATT_TIME;
uint32_t SLEEP_TIME = 10800000;
uint32_t SLEEP_NOGW = 60000;
uint32_t oldmillis;
uint32_t newmillis;
uint32_t previousMillis;
uint32_t lightMillisR;
uint32_t configMillis;
uint32_t interrupt_time;
uint32_t SLEEP_TIME_W;
uint32_t axel_time;
uint32_t axel_time0;
int16_t myid;
int16_t mypar;
int16_t old_mypar = -1;
bool vibro = 1;
uint32_t PIN_BUTTON_MASK;
uint32_t AXEL_INT_MASK;
uint32_t GERKON_INT_MASK;
uint32_t MAGNET_INT_MASK;
float ODR_1Hz6_LP_ONLY = 1.6f;
float ODR_12Hz5 = 12.5f;
float ODR_25Hz = 25.0f;
float ODR_50Hz = 50.0f;
float ODR_100Hz = 100.0f;
float ODR_200Hz = 200.0f;
volatile byte axelIntStatus = 0;
volatile byte gerkIntStatus = 0;
volatile byte magIntStatus = 0;
volatile byte buttIntStatus = 0;
uint16_t batteryVoltage;
int16_t linkQuality;
int16_t old_linkQuality;

//#define MY_DEBUG
#ifndef MY_DEBUG
#define MY_DISABLED_SERIAL
#endif
#define MY_RADIO_NRF5_ESB
int16_t mtwr;
#define MY_TRANSPORT_WAIT_READY_MS (mtwr)
#define MY_NRF5_ESB_PA_LEVEL (NRF5_PA_MAX)

#include <MySensors.h>

extern "C" {
#include "app_gpiote.h"
#include "nrf_gpio.h"
}
#define APP_GPIOTE_MAX_USERS 1
static app_gpiote_user_id_t m_gpiote_user_id;

#include <LIS2DW12Sensor.h>
LIS2DW12Sensor *lis2;

#define DWS_CHILD_ID 0
#define V_SENS_CHILD_ID 1
#define M_CHILD_ID 2
#define LEVEL_SENSIV_V_SENS_CHILD_ID 230
#define SIGNAL_Q_ID 250

MyMessage dwsMsg(DWS_CHILD_ID, V_TRIPPED);
MyMessage mMsg(M_CHILD_ID, V_TRIPPED);
MyMessage vibroMsg(V_SENS_CHILD_ID, V_TRIPPED);
MyMessage conf_vsensMsg(LEVEL_SENSIV_V_SENS_CHILD_ID, V_VAR1);
#define SN "DOOR & WINDOW SENS"
#define SV "1.12"


void before() {
  board_Init();
  happy_init();
  delay(500);
  batteryVoltage = hwCPUVoltage();
  digitalWrite(BLUE_LED, LOW);
}


void presentation()
{
  NRF_POWER->DCDCEN = 0;
  wait(10);

  check = sendSketchInfo(SN, SV);
  wait(30);
  if (!check) {
    _transportSM.failedUplinkTransmissions = 0;
    wait(30);
    check = sendSketchInfo(SN, SV);
    wait(30);
    _transportSM.failedUplinkTransmissions = 0;
  }
  if (check) {
    blinky(1, 1, BLUE_LED);
  } else {
    blinky(1, 1, RED_LED);
  }

  check = present(DWS_CHILD_ID, S_DOOR, "STATUS RS SENS");
  wait(40);
  if (!check) {
    _transportSM.failedUplinkTransmissions = 0;
    wait(40);
    check = present(DWS_CHILD_ID, S_DOOR, "STATUS RS SENS");
    wait(40);
    _transportSM.failedUplinkTransmissions = 0;
  }
  if (check) {
    blinky(1, 1, BLUE_LED);
  } else {
    blinky(1, 1, RED_LED);
  }

  check = present(V_SENS_CHILD_ID, S_VIBRATION, "STATUS SHOCK SENS");
  wait(50);
  if (!check) {
    _transportSM.failedUplinkTransmissions = 0;
    wait(50);
    check = present(V_SENS_CHILD_ID, S_VIBRATION, "STATUS SHOCK SENS");
    wait(50);
    _transportSM.failedUplinkTransmissions = 0;
  }
  if (check) {
    blinky(1, 1, BLUE_LED);
  } else {
    blinky(1, 1, RED_LED);
  }

  check = present(M_CHILD_ID, S_DOOR, "ANTI-MAGNET ALARM");
  wait(60);
  if (!check) {
    _transportSM.failedUplinkTransmissions = 0;
    wait(60);
    check = present(M_CHILD_ID, S_DOOR, "ANTI-MAGNET ALARM");
    wait(60);
    _transportSM.failedUplinkTransmissions = 0;
  }
  if (check) {
    blinky(1, 1, BLUE_LED);
  } else {
    blinky(1, 1, RED_LED);
  }

  check = present(SIGNAL_Q_ID, S_CUSTOM, "SIGNAL %");
  wait(70);
  if (!check) {
    _transportSM.failedUplinkTransmissions = 0;
    wait(70);
    check = present(SIGNAL_Q_ID, S_CUSTOM, "SIGNAL %");
    wait(70);
    _transportSM.failedUplinkTransmissions = 0;
  }
  if (check) {
    blinky(1, 1, BLUE_LED);
  } else {
    blinky(1, 1, RED_LED);
  }

  check = present(LEVEL_SENSIV_V_SENS_CHILD_ID, S_CUSTOM, "SENS LEVEL VIBRO");
  wait(80);
  if (!check) {
    _transportSM.failedUplinkTransmissions = 0;
    wait(80);
    check = present(LEVEL_SENSIV_V_SENS_CHILD_ID, S_CUSTOM, "SENS LEVEL VIBRO");
    wait(80);
    _transportSM.failedUplinkTransmissions = 0;
  }
  if (check) {
    blinky(1, 1, BLUE_LED);
  } else {
    blinky(1, 1, RED_LED);
  }

  check = send(conf_vsensMsg.set(conf_vibro_set));
  wait(90);
  if (!check) {
    _transportSM.failedUplinkTransmissions = 0;
    wait(90);
    check = send(conf_vsensMsg.set(conf_vibro_set));
    wait(90);
    _transportSM.failedUplinkTransmissions = 0;
  }
  if (check) {
    blinky(1, 1, BLUE_LED);
  } else {
    blinky(1, 1, RED_LED);
  }
  NRF_POWER->DCDCEN = 0;
  wait(10);
}


void setup() {
  digitalWrite(BLUE_LED, HIGH);
  config_Happy_node();
  sensors_Init();
}


void loop() {
  if (flag_update_transport_param == 1) {
    update_Happy_transport();
  }
  if (flag_sendRoute_parent == 1) {
    present_only_parent();
  }
  if (isTransportReady() == true) {
    if (flag_nogateway_mode == 0) {
      if (flag_find_parent_process == 1) {
        find_parent_process();
      }
      if (configMode == 0) {
        if ((axelIntStatus == AXEL_INT) || (buttIntStatus == PIN_BUTTON) || (gerkIntStatus == GERKON_INT) || (magIntStatus == MAGNET_INT)) {
          nosleep = 1;
          newmillis = millis();
          interrupt_time = newmillis - oldmillis;
          BATT_TIME = BATT_TIME - interrupt_time;
          if (BATT_TIME < 60000) {
            BATT_TIME = SLEEP_TIME;
            batteryVoltage = hwCPUVoltage();
            batt_flag = 1;
          }

          if (gerkIntStatus == GERKON_INT) {
            send_Gerkon();
            axel_time = millis();
            nosleep = 0;
          }

          if (magIntStatus == MAGNET_INT) {
            send_Magnet();
            nosleep = 0;
          }

          if (axelIntStatus == AXEL_INT) {
            if (millis() - axel_time0 >= 2000) {
              send_Axel();
              nosleep = 0;
            } else {
              if (digitalRead(GERKON_INT) == LOW) {
                send_Gerkon();
                axel_time = millis();
                nosleep = 0;
              }
            }
          }

          if (buttIntStatus == PIN_BUTTON) {
            if (digitalRead(PIN_BUTTON) == 0 && button_flag == 0) {
              button_flag = 1;
              previousMillis = millis();
              ledsOff();
            }
            if (digitalRead(PIN_BUTTON) == 0 && button_flag == 1) {
              if ((millis() - previousMillis > 0) && (millis() - previousMillis <= 1750)) {
                if (millis() - lightMillisR > 70) {
                  lightMillisR = millis();
                  onoff = !onoff;
                  digitalWrite(BLUE_LED, onoff);
                }
              }
              if ((millis() - previousMillis > 1750) && (millis() - previousMillis <= 2000)) {
                ledsOff();
              }
              if ((millis() - previousMillis > 2000) && (millis() - previousMillis <= 3750)) {
                if (millis() - lightMillisR > 50) {
                  lightMillisR = millis();
                  onoff = !onoff;
                  digitalWrite(GREEN_LED, onoff);
                }
              }
              if ((millis() - previousMillis > 3750) && (millis() - previousMillis <= 4000)) {
                ledsOff();
              }
              if ((millis() - previousMillis > 4000) && (millis() - previousMillis <= 5750)) {
                if (millis() - lightMillisR > 30) {
                  lightMillisR = millis();
                  onoff = !onoff;
                  digitalWrite(RED_LED, onoff);
                }
              }
              if (millis() - previousMillis > 5750) {
                ledsOff();
              }
            }

            if (digitalRead(PIN_BUTTON) == 1 && button_flag == 1) {
              if ((millis() - previousMillis <= 1750) && (button_flag == 1))
              {
                ledsOff();
                blinky(2, 2, BLUE_LED);
                button_flag = 0;
                buttIntStatus = 0;
                presentation();
                nosleep = 0;
              }
              if ((millis() - previousMillis > 2000) && (millis() - previousMillis <= 3750) && (button_flag == 1))
              {
                ledsOff();
                blinky(2, 2, GREEN_LED);
                configMode = 1;
                button_flag = 0;
                configMillis = millis();
                interrupt_Init(1);
                NRF_POWER->DCDCEN = 0;
                buttIntStatus = 0;
                NRF5_ESB_startListening();
                wait(50);
              }

              if ((millis() - previousMillis > 4000) && (millis() - previousMillis <= 5750) && (button_flag == 1))
              {
                ledsOff();
                blinky(3, 3, RED_LED);
                //new_device();
              }

              if ((((millis() - previousMillis > 1750) && (millis() - previousMillis <= 2000)) || ((millis() - previousMillis > 3750) && (millis() - previousMillis <= 4000)) || ((millis() - previousMillis > 5750))) && (button_flag == 1))
              {
                ledsOff();
                nosleep = 0;
                button_flag = 0;
                buttIntStatus = 0;
              }
            }
          }
        } else {
          batteryVoltage = hwCPUVoltage();
          BATT_TIME = SLEEP_TIME;
          sendBatteryStatus(1);
          nosleep = 0;
        }
      } else {
        if (millis() - configMillis > 30000) {
          blinky(3, 3, GREEN_LED);
          configMode = 0;
          nosleep = 0;
          interrupt_Init(0);
          NRF_POWER->DCDCEN = 1;
          wait(50);
        }
      }
    } else {
      if (buttIntStatus == PIN_BUTTON) {
        if (digitalRead(PIN_BUTTON) == 0 && button_flag == 0) {
          button_flag = 1;
          nosleep = 1;
          previousMillis = millis();
          ledsOff();
        }
        if (digitalRead(PIN_BUTTON) == 0 && button_flag == 1) {
          if ((millis() - previousMillis > 0) && (millis() - previousMillis <= 1750)) {
            if (millis() - lightMillisR > 25) {
              lightMillisR = millis();
              onoff = !onoff;
              digitalWrite(GREEN_LED, onoff);
            }
          }
          if ((millis() - previousMillis > 1750) && (millis() - previousMillis <= 2000)) {
            ledsOff();
          }
          if ((millis() - previousMillis > 2000) && (millis() - previousMillis <= 4000)) {
            if (millis() - lightMillisR > 25) {
              lightMillisR = millis();
              onoff = !onoff;
              digitalWrite(RED_LED, onoff);
            }
          }
          if (millis() - previousMillis > 4000) {
            ledsOff();
          }
        }

        if (digitalRead(PIN_BUTTON) == 1 && button_flag == 1) {
          if ((millis() - previousMillis <= 1750) && (button_flag == 1))
          {
            ledsOff();
            blinky(2, 2, BLUE_LED);
            button_flag = 0;
            buttIntStatus = 0;
            check_parent();
            nosleep = 0;
          }
          if ((millis() - previousMillis > 2000) && (millis() - previousMillis <= 4000) && (button_flag == 1))
          {
            ledsOff();
            blinky(3, 3, RED_LED);
            //new_device();
          }

          if ((((millis() - previousMillis > 1750) && (millis() - previousMillis <= 2000)) || ((millis() - previousMillis > 4000))) && (button_flag == 1))
          {
            ledsOff();
            nosleep = 0;
            button_flag = 0;
            buttIntStatus = 0;
          }
        }
      } else {
        check_parent();
      }
    }
  }

  if (_transportSM.failureCounter > 0)
  {
    _transportConfig.parentNodeId = loadState(101);
    _transportConfig.nodeId = myid;
    _transportConfig.distanceGW = loadState(103);
    mypar = _transportConfig.parentNodeId;
    nosleep = 0;
    flag_fcount = 1;
    err_delivery_beat = 6;
    happy_node_mode();
    gateway_fail();
  }

  if (nosleep == 0) {
    oldmillis = millis();
    axelIntStatus = 0;
    buttIntStatus = 0;
    gerkIntStatus = 0;
    magIntStatus = 0;
    sleep(SLEEP_TIME_W, false);
    nosleep = 1;
  }
}


void blinky(uint8_t pulses, uint8_t repit, uint8_t ledColor) {
  for (int x = 0; x < repit; x++) {
    if (x > 0) {
      wait(150);
    }
    for (int i = 0; i < pulses; i++) {
      if (i > 0) {
        wait(40);
      }
      digitalWrite(ledColor, LOW);
      wait(10);
      digitalWrite(ledColor, HIGH);
    }
  }
}


void board_Init() {
  pinMode(PIN_BUTTON, INPUT_PULLUP);
  pinMode(MAGNET_INT, INPUT);
  pinMode(GERKON_INT, INPUT);
  pinMode(AXEL_INT, INPUT);
  pinMode(RED_LED, OUTPUT);
  pinMode(GREEN_LED, OUTPUT);
  pinMode(BLUE_LED, OUTPUT);
  ledsOff();
  NRF_POWER->DCDCEN = 1;
  wait(5);
#ifndef MY_DEBUG
  NRF_UART0->ENABLE = 0;
  wait(5);
#endif
  //NRF_NFCT->TASKS_DISABLE = 1;
  // NRF_NVMC->CONFIG = 1;
  // NRF_UICR->NFCPINS = 0;
  // NRF_NVMC->CONFIG = 0;
  // NRF_SAADC ->ENABLE = 0;
  // NRF_PWM0  ->ENABLE = 0;
  // NRF_PWM1  ->ENABLE = 0;
  // NRF_PWM2  ->ENABLE = 0;
  // NRF_TWIM1 ->ENABLE = 0;
  // NRF_TWIS1 ->ENABLE = 0;
  NRF_RADIO->TXPOWER = 8;
  wait(5);

  conf_vibro_set = loadState(230);
  if ((conf_vibro_set > 5) || (conf_vibro_set == 0)) {
    conf_vibro_set = 2;
    saveState(230, conf_vibro_set);
  }

  blinky(1, 1, BLUE_LED);
}


void ledsOff() {
  digitalWrite(RED_LED, HIGH);
  digitalWrite(GREEN_LED, HIGH);
  digitalWrite(BLUE_LED, HIGH);
}


void happy_init() {
  //hwWriteConfig(EEPROM_NODE_ID_ADDRESS, 255); // ******************** checking the node config reset *************************

  if (hwReadConfig(EEPROM_NODE_ID_ADDRESS) == 0) {
    hwWriteConfig(EEPROM_NODE_ID_ADDRESS, 255);
  }
  if (loadState(100) == 0) {
    saveState(100, 255);
  }
  CORE_DEBUG(PSTR("EEPROM NODE ID: %d\n"), hwReadConfig(EEPROM_NODE_ID_ADDRESS));
  CORE_DEBUG(PSTR("USER MEMORY SECTOR NODE ID: %d\n"), loadState(100));

  if (hwReadConfig(EEPROM_NODE_ID_ADDRESS) == 255) {
    mtwr = 0;
  } else {
    mtwr = 11000;
    no_present();
  }
  CORE_DEBUG(PSTR("MY_TRANSPORT_WAIT_MS: %d\n"), mtwr);
}

void no_present() {
  _coreConfig.presentationSent = true;
  _coreConfig.nodeRegistered = true;
}


void interrupt_Init(bool start) {
  //***
  //SET
  //NRF_GPIO_PIN_NOPULL
  //NRF_GPIO_PIN_PULLUP
  //NRF_GPIO_PIN_PULLDOWN
  //***
  nrf_gpio_cfg_input(PIN_BUTTON, NRF_GPIO_PIN_PULLUP);
  nrf_gpio_cfg_input(AXEL_INT, NRF_GPIO_PIN_NOPULL);
  nrf_gpio_cfg_input(GERKON_INT, NRF_GPIO_PIN_NOPULL);
  nrf_gpio_cfg_input(MAGNET_INT, NRF_GPIO_PIN_NOPULL);
  APP_GPIOTE_INIT(APP_GPIOTE_MAX_USERS);
  PIN_BUTTON_MASK = 1 << PIN_BUTTON;
  AXEL_INT_MASK = 1 << AXEL_INT;
  GERKON_INT_MASK = 1 << GERKON_INT;
  MAGNET_INT_MASK = 1 << MAGNET_INT;
  //  app_gpiote_user_register(p_user_id, pins_low_to_high_mask, pins_high_to_low_mask, event_handler)
  if (start == 0) {
    app_gpiote_user_register(&m_gpiote_user_id, AXEL_INT_MASK | GERKON_INT_MASK, GERKON_INT_MASK | MAGNET_INT_MASK | PIN_BUTTON_MASK, gpiote_event_handler);
    wait(5);
  } else if (start == 1) {
    app_gpiote_user_register(&m_gpiote_user_id, GERKON_INT_MASK, GERKON_INT_MASK | MAGNET_INT_MASK | PIN_BUTTON_MASK, gpiote_event_handler);
    wait(5);
  }
  app_gpiote_user_enable(m_gpiote_user_id);
  wait(5);
  axelIntStatus = 0;
  buttIntStatus = 0;
  gerkIntStatus = 0;
  magIntStatus = 0;
}


void gpiote_event_handler(uint32_t event_pins_low_to_high, uint32_t event_pins_high_to_low)
{
  MY_HW_RTC->CC[0] = (MY_HW_RTC->COUNTER + 2); // Taken from d0016 example code, ends the sleep delay

  if (PIN_BUTTON_MASK & event_pins_high_to_low) {
    if ((buttIntStatus == 0) && (axelIntStatus == 0) && (gerkIntStatus == 0) && (magIntStatus == 0)) {
      buttIntStatus = PIN_BUTTON;
    }
  }
  if (flag_nogateway_mode == 0) {
    if (AXEL_INT_MASK & event_pins_low_to_high) {
      if ((axelIntStatus == 0) && (buttIntStatus == 0) && (gerkIntStatus == 0) && (magIntStatus == 0) && (door_status == 1)) {
        axelIntStatus = AXEL_INT;
        axel_time0 = millis();
      }
    }
    if ((GERKON_INT_MASK & event_pins_low_to_high) || (GERKON_INT_MASK & event_pins_high_to_low)) {
      if ((axelIntStatus == 0) && (buttIntStatus == 0) && (gerkIntStatus == 0) && (magIntStatus == 0)) {
        gerkIntStatus = GERKON_INT;
      }
    }
    if (MAGNET_INT_MASK & event_pins_high_to_low) {
      if ((axelIntStatus == 0) && (buttIntStatus == 0) && (gerkIntStatus == 0) && (magIntStatus == 0) && (door_status == 1)) {
        magIntStatus = MAGNET_INT;
      }
    }
  }
}


void sensors_Init() {
  Wire.begin();
  wait(100);
  lis2 = new LIS2DW12Sensor (&Wire);
  vibro_Init();
  if (flag_nogateway_mode == 0) {
    if (digitalRead(GERKON_INT) == HIGH) {
      door_status = 1;
      interrupt_Init(0);
    } else {
      door_status = 0;
      interrupt_Init(1);
    }
    send(dwsMsg.set(door_status));
    wait(50);

    SLEEP_TIME_W = SLEEP_TIME;
    axelIntStatus = 0;
    buttIntStatus = 0;
    gerkIntStatus = 0;
    magIntStatus = 0;
    sendBatteryStatus(0);
    wait(100);
    blinky(2, 1, BLUE_LED);
    wait(100);
    blinky(2, 1, GREEN_LED);
    wait(100);
    blinky(2, 1, RED_LED);
    axel_time = millis();
  } else {
    interrupt_Init(0);
    blinky(5, 3, RED_LED);
  }
}


void config_Happy_node() {
  if (mtwr == 0) {
    myid = getNodeId();
    saveState(100, myid);
    mypar = _transportConfig.parentNodeId;
    old_mypar = mypar;
    saveState(101, mypar);
    saveState(102, _transportConfig.distanceGW);
  }
  if (mtwr != 0) {
    myid = getNodeId();
    if (myid != loadState(100)) {
      saveState(100, myid);
    }
    if (isTransportReady() == true) {
      mypar = _transportConfig.parentNodeId;
      if (mypar != loadState(101)) {
        saveState(101, mypar);
      }
      if (_transportConfig.distanceGW != loadState(102)) {
        saveState(102, _transportConfig.distanceGW);
      }
      present_only_parent();
    }
    if (isTransportReady() == false)
    {
      no_present();
      flag_fcount = 1;
      err_delivery_beat = 6;
      _transportConfig.nodeId = myid;
      _transportConfig.parentNodeId = loadState(101);
      _transportConfig.distanceGW = loadState(102);
      mypar = _transportConfig.parentNodeId;
      happy_node_mode();
      gateway_fail();
    }
  }
}


void send_Axel() {
  if (millis() - axel_time >= 5000) {
    blinky(2, 1, GREEN_LED);
    blinky(2, 1, RED_LED);
    blinky(2, 1, GREEN_LED);
    blinky(2, 1, RED_LED);
    blinky(2, 1, GREEN_LED);
    blinky(2, 1, RED_LED);

    send_a = send(vibroMsg.set(vibro));
    wait(50);
    if (send_a == false) {
      send_a = send(vibroMsg.set(vibro));
      wait(100);
    }
    if (send_a == true) {
      err_delivery_beat = 0;
      if (flag_nogateway_mode == 1) {
        flag_nogateway_mode = 0;
        CORE_DEBUG(PSTR("MyS: NORMAL GATEWAY MODE\n"));
        err_delivery_beat = 0;
      }
    } else {
      _transportSM.failedUplinkTransmissions = 0;
      if (err_delivery_beat < 6) {
        err_delivery_beat++;
      }
      if (err_delivery_beat == 5) {
        if (flag_nogateway_mode == 0) {
          gateway_fail();
          CORE_DEBUG(PSTR("MyS: LOST GATEWAY MODE\n"));
        }
      }
    }
    axel_time = millis();
    axelIntStatus = 0;
    nosleep = 0;
  } else {
    axelIntStatus = 0;
    nosleep = 0;
  }
}


void send_Gerkon() {
  if (digitalRead(GERKON_INT) == HIGH) {
    door_status = 1;
    interrupt_Init(0);
  } else {
    door_status = 0;
    interrupt_Init(1);
  }
  if (door_status == 1) {
    blinky(1, 1, GREEN_LED);
  } else {
    blinky(1, 1, RED_LED);
  }
  send_a = send(dwsMsg.set(door_status));
  wait(50);
  if (send_a == false) {
    send_a = send(dwsMsg.set(door_status));
    wait(100);
    if (send_a == false) {
      send_a = send(dwsMsg.set(door_status));
      wait(150);
    }
  }
  if (send_a == true) {
    err_delivery_beat = 0;
    if (flag_nogateway_mode == 1) {
      flag_nogateway_mode = 0;
      CORE_DEBUG(PSTR("MyS: NORMAL GATEWAY MODE\n"));
      err_delivery_beat = 0;
    }
  } else {
    _transportSM.failedUplinkTransmissions = 0;
    if (err_delivery_beat < 6) {
      err_delivery_beat++;
    }
    if (err_delivery_beat == 5) {
      if (flag_nogateway_mode == 0) {
        gateway_fail();
        CORE_DEBUG(PSTR("MyS: LOST GATEWAY MODE\n"));
      }
    }
  }
  gerkIntStatus = 0;
  nosleep = 0;
}


void send_Magnet() {
  blinky(2, 1, BLUE_LED);
  blinky(2, 1, RED_LED);
  blinky(2, 1, BLUE_LED);
  blinky(2, 1, RED_LED);
  blinky(2, 1, BLUE_LED);
  blinky(2, 1, RED_LED);
  send_a = send(mMsg.set(magnet_status));
  wait(50);
  if (send_a == false) {
    send_a = send(mMsg.set(magnet_status));
    wait(100);
  }
  if (send_a == true) {
    err_delivery_beat = 0;
    if (flag_nogateway_mode == 1) {
      flag_nogateway_mode = 0;
      CORE_DEBUG(PSTR("MyS: NORMAL GATEWAY MODE\n"));
      err_delivery_beat = 0;
    }
  } else {
    _transportSM.failedUplinkTransmissions = 0;
    if (err_delivery_beat < 6) {
      err_delivery_beat++;
    }
    if (err_delivery_beat == 5) {
      if (flag_nogateway_mode == 0) {
        gateway_fail();
        CORE_DEBUG(PSTR("MyS: LOST GATEWAY MODE\n"));
      }
    }
  }
  magIntStatus = 0;
  nosleep = 0;
}


void new_device() {
  hwWriteConfig(EEPROM_NODE_ID_ADDRESS, 255);
  saveState(100, 255);
  wdt_enable(WDTO_15MS);
}


void update_Happy_transport() {
  CORE_DEBUG(PSTR("MyS: UPDATE TRANSPORT CONFIGURATION\n"));
  mypar = _transportConfig.parentNodeId;
  if (mypar != loadState(101))
  {
    saveState(101, mypar);
  }
  if (_transportConfig.distanceGW != loadState(102))
  {
    saveState(102, _transportConfig.distanceGW);
  }
  present_only_parent();
  wait(50);
  nosleep = 0;
  flag_update_transport_param = 0;
}


void present_only_parent() {
  if (old_mypar != mypar) {
    CORE_DEBUG(PSTR("MyS: SEND LITTLE PRESENT:) WITH PARENT ID\n"));
    if (_sendRoute(build(_msgTmp, 0, NODE_SENSOR_ID, C_INTERNAL, 6).set(mypar))) {
      flag_sendRoute_parent = 0;
      old_mypar = mypar;
    } else {
      flag_sendRoute_parent = 1;
    }
  }
}


void happy_node_mode() {
  _transportSM.findingParentNode = false;
  _transportSM.transportActive = true;
  _transportSM.uplinkOk = true;
  _transportSM.pingActive = false;
  _transportSM.failureCounter = 0;
  _transportSM.uplinkOk = true;
  _transportSM.failureCounter = 0u;
  _transportSM.failedUplinkTransmissions = 0u;
  transportSwitchSM(stReady);
  CORE_DEBUG(PSTR("TRANSPORT: %d\n"), isTransportReady());
}


void gateway_fail() {
  flag_nogateway_mode = 1;
  flag_update_transport_param = 0;
  SLEEP_TIME_W = SLEEP_NOGW;
}


void check_parent() {
  _transportSM.findingParentNode = true;
  CORE_DEBUG(PSTR("MyS: SEND FIND PARENT REQUEST, WAIT RESPONSE\n"));
  _sendRoute(build(_msg, 255, NODE_SENSOR_ID, C_INTERNAL, 7).set(""));
  wait(1500, C_INTERNAL, 8);
  if (_msg.sensor == 255) {
    if (mGetCommand(_msg) == 3) {
      if (_msg.type == 8) {
        Ack_FP = 1;
        CORE_DEBUG(PSTR("MyS: PARENT RESPONSE FOUND\n"));
      }
    }
  }
  if (Ack_FP == 1) {
    CORE_DEBUG(PSTR("MyS: FIND PARENT PROCESS\n"));
    Ack_FP = 0;
    transportSwitchSM(stParent);
    flag_nogateway_mode = 0;
    flag_find_parent_process = 1;
    problem_mode_count = 0;
  } else {
    _transportSM.findingParentNode = false;
    CORE_DEBUG(PSTR("MyS: PARENT RESPONSE NOT FOUND\n"));
    _transportSM.failedUplinkTransmissions = 0;
    CORE_DEBUG(PSTR("TRANSPORT: %d\n"), isTransportReady());
    nosleep = 0;
    if (problem_mode_count < 9) {
      CORE_DEBUG(PSTR("PROBLEM MODE COUNTER: %d\n"), problem_mode_count);
      problem_mode_count++;
      SLEEP_TIME_W = SLEEP_TIME_W + SLEEP_TIME_W;
    }
  }
}


void find_parent_process() {
  flag_update_transport_param = 1;
  flag_find_parent_process = 0;
  CORE_DEBUG(PSTR("MyS: STANDART TRANSPORT MODE IS RESTORED\n"));
  err_delivery_beat = 0;
  SLEEP_TIME_W = SLEEP_TIME;
  nosleep = 0;
}


void sendBatteryStatus(bool start) {
  batt_cap = battery_level_in_percent(batteryVoltage);
  if (start == 1) {
    //if (batt_cap < old_batt_cap) {
    sendBatteryLevel(battery_level_in_percent(batteryVoltage), 1);
    wait(1500, C_INTERNAL, I_BATTERY_LEVEL);
    old_batt_cap = batt_cap;
    // }
  } else {
    sendBatteryLevel(battery_level_in_percent(batteryVoltage), 1);
    wait(1500, C_INTERNAL, I_BATTERY_LEVEL);
  }

  linkQuality = calculationRxQuality();
  if (linkQuality != old_linkQuality) {
    wait(10);
    sendSignalStrength(linkQuality);
    wait(50);
    old_linkQuality = linkQuality;
  }
}


bool sendSignalStrength(const int16_t level, const bool ack)
{
  return _sendRoute(build(_msgTmp, GATEWAY_ADDRESS, SIGNAL_Q_ID, C_SET, V_VAR1,
                          ack).set(level));
}
int16_t calculationRxQuality() {
  int16_t nRFRSSI_temp = transportGetReceivingRSSI();
  int16_t nRFRSSI = map(nRFRSSI_temp, -85, -40, 0, 100);
  if (nRFRSSI < 0) {
    nRFRSSI = 0;
  }
  if (nRFRSSI > 100) {
    nRFRSSI = 100;
  }
  return nRFRSSI;
}


void receive(const MyMessage & message)
{
  if (message.sensor == LEVEL_SENSIV_V_SENS_CHILD_ID) {
    if (message.type == V_VAR1) {
      conf_vibro_set = message.getByte();
      vibro_Init();
      saveState(230, conf_vibro_set);
      wait(200);
      send(conf_vsensMsg.set(conf_vibro_set));
      wait(200);
      blinky(3, 3, GREEN_LED);
      configMode = 0;
      nosleep = 0;
    }
  }
}


void vibro_Init() {
  if (conf_vibro_set == 1) {
    lis2->ODRTEMP = ODR_1Hz6_LP_ONLY;
  }
  if (conf_vibro_set == 2) {
    lis2->ODRTEMP = ODR_12Hz5;
  }
  if (conf_vibro_set == 3) {
    lis2->ODRTEMP = ODR_25Hz;
  }
  if (conf_vibro_set == 4) {
    lis2->ODRTEMP = ODR_100Hz;
  }
  if (conf_vibro_set == 5) {
    lis2->ODRTEMP = ODR_200Hz;
  }
  lis2->Enable_X();
  wait(100);
  lis2->Enable_Wake_Up_Detection();
  wait(100);
}


Eine vollständige Liste der Projektdateien ist auf dem Git verfügbar .

Als UD-System benutze ich Majordomo schon lange . In diesem Artikel werde ich ein Beispiel beschreiben, wie der Sensor im Maysensors-Netzwerk über den UD-Controller funktioniert. In dieser Ausführungsform werden Daten vom Sensor über das Maysensors-Gateway an das UD-System gesendet. Majordomo implementiert die Unterstützung für das Mysensors-Protokoll in einem separaten Modul . Das Modul zum Herunterladen und Installieren ist im Add-On-Markt des UD-Systems im Abschnitt "Ausrüstung" verfügbar.


Derzeit ist die Implementierung für die Majordomo UD am vollständigsten und wird unterstützt:

  • alle Mesensordatentypen,
  • Arbeit mit OTA,
  • arbeiten mit mehreren Netzwerken gleichzeitig in einem Modul (Multi-Gate),
  • Unterstützung für SmartSleep-Geräte,
  • Daten von Sensoren im Netzwerk anfordern, wenn das Modul startet,
  • Anfrage zur Bestätigung der Nachrichtenübermittlung,
  • Unterstützung für Serviceanfragen wie Datenerfassung, Heartbeat, Präsentation, Neustart,
  • arbeite mit NodeManager

Es gibt natürlich Mängel, die zuvor hinzugefügte Unterstützung für serielle Gateways, im Zuge der natürlichen Entwicklung des Systems hat Majordomo eine lange Lebensdauer angeordnet und wird derzeit nicht unterstützt. Ich hatte nicht einmal die Möglichkeit, diese Art von Toren im Majordomo zu testen, da diese Funktion nicht mehr verfügbar war, bevor ich von Mysensors erfuhr. Der Modulentwickler versprach, diese Funktion bis September 2019 erneut hinzuzufügen, aber der Herbst des 19. verging, und es gibt noch keine Unterstützung für die Reihe von Gateways :(.

Sie können Mysensors mqtt-Gateways auch mit Majordomo verwenden, jedoch nicht über das Mysensors-Modul, sondern über das MQTT-Modul.

In meinem Sensor senden Schock- und Magnetfeldsensoren eine Einheit nur, wenn sie ausgelöst werden, und dies stellte sich als kleines Problem heraus. Das Simple Devices-Modul unterstützt diese Sensortypen nicht. Natürlich gibt es einen gemeinsamen Sensor, aber die Anpassung der Einstellungen ist sehr begrenzt. Beim Hinzufügen eines Sensors war ein unangenehmes Problem, dass ich, als die nächste Einheit vom Sensor kam, einen Rückwärts-Timer starten musste, damit nach einem im Timer angegebenen Zeitintervall Null in die Objekteigenschaft geschrieben wurde. Da jedoch alles über die Methode „Statusaktualisierung“ funktioniert und dann die Null aufgezeichnet wird, hat das Meysensormodul, das einen neuen Status empfängt, eine Nachricht mit diesen Daten an das Netzwerk an mein Gerät gesendet, und der Punkt ist Null.Die einfachste Lösung schien mir, eine neue Methode hinzuzufügen, bei der der Status von Eigenschaft1 zu Eigenschaft2 übertragen wird und ein Timer gestartet wird, um Null in Eigenschaft2 zu schreiben. Ein in einfachen Geräten erstelltes Objekt funktioniert mit property2 und im Maysensors-Modul mit property1.



if($this->getProperty('value2') == '1'){
$this->setProperty('status','1');
}

Als Nächstes müssen Sie in der Statusaktualisierungsmethode des gewünschten Objekts einen Timer-Start hinzufügen:

if (gg("MysensorsSmoke03.status") == "1") {
SetTimeOut('AlarmShock','sg("MysensorsSmoke03.status","0");',10);
}

Video mit der Bedienung des Sensors im Majordomo-System und der Majordroid-Anwendung. Ich empfehle, dass Sie es sich ansehen, wo immer es möglich ist, um die Funktionsweise der Hauptfunktionalität zu zeigen. Natürlich sind Ihre Vorlieben und Abonnements für meinen kleinen Heimkanal von unschätzbarem Wert, aber wenn Sie auf die Glocke klicken, werden Sie kein Video mit meinen neuen Sensoren verpassen;).


Die Geräteplatine wurde mit dem DeepTrace-Programm erstellt. Die Entwicklung dieses Editors für die Entwicklung der Elektronik ermöglichte es mir einmal, meine Fähigkeiten erheblich zu erweitern. Ich stelle fest, dass ich kein professioneller Elektronikingenieur bin. Meine Erfahrung in der Heimentwicklung von Motherboards beträgt eineinhalb oder zwei Jahre. Allen, die ihre Geräte auf einem Steckbrett herstellen, empfehle ich, eine Art Editor zu erlernen. YouTube ist voller Videohandbücher.





Die Unterstützung für nRF5-Chips in Maysensors basiert auf der Sandeep Mistry-Bibliothek - arduino-nRF5. Diese Bibliothek unterstützt jedoch nicht die Chips nRF52840, nRF52810 und die völlig neuen Chips nRF52811. Ich musste diese Chips gabeln und unterstützen, eine Übertragung und Anpassung wurde vom Nordic SDK vorgenommen. Es gab keine Unterstützung für weiche Geräte, da keine besondere Notwendigkeit für die Verwendung von Mysensoren besteht, und es gab keine Unterstützung für Port1 für nRF52840-Chips. In jüngerer Zeit wurden meine Forschungen zu diesem Thema und die Forschungen eines anderen Mitglieds der Maysensors-Community kombiniert, und als Ergebnis wurde die Unterstützung für nRF52840 bereits mit Port1 erhalten, die Pins wurden nur zu einem Meer.

Das Gehäuse für den Sensor wurde im SolidWorks-Programm entwickelt und vor etwa einem Jahr unabhängig von den Lektionen auf YouTube gemeistert. Das Gehäuse wurde auf einem ANYCUBIC FOTON SLA-Drucker gedruckt. Mit der Qualität und Genauigkeit des Drucks war ich sehr zufrieden. Das einzig Negative ist eine eher schlechte Auswahl an UV-Harzen, mit denen solche Haushaltsdrucker arbeiten können. Abmessungen des Gerätes im Gehäuse: Lang 43 mm, Breite 26 mm, Höhe 12,5 mm. Gehäuseabmessungen mit Magnet: Länge 37 mm, Breite 11 mm, Höhe 12,5 mm.









Der Sensorverbrauch in einem Traum lag je nach ausgewähltem Chip zwischen 4 μA und 7 μA. Der Verbrauch im Datenübertragungsmodus betrug 8 mA.

Der Sensor verwendet eine CR2032-Batterie. Alle Messungen wurden vom chinesischen "Multifiler" durchgeführt :) angesichts des Fehlens eines Profilers aufgrund seiner ziemlich hohen Kosten :(.

Das Gerät kann wiederholt werden, die schriftliche Skizze verwenden oder eine eigene schreiben. Um den Sensor zu wiederholen, ist alles, was Sie brauchen, auf meinem Github angeordnet ( Gerbera, Code, Fallmodelle ).

Wenn jemand bereit ist, beim Schreiben von Software nach dem ZIGBEE-Protokoll zu helfen, werde ich gerne zusammenarbeiten.

Wenn Sie an diesem Projekt interessiert sind, wenden Sie sich an die Telegrammgruppe. Es wird immer Unterstützung geben, nicht nur das Maysensors- Protokoll , sondern auch Zigbee und BLE auf nRF5 zu beherrschen. Sie werden umgehend zu allen Fragen der Programmierung von nRF52 in Arduino IDE und nicht nur darin beraten.

Karren chatten dort, wo ich wohne und Leute wie ich - @MYSENSORS_RUS .

Gut zu allen!

All Articles