Drahtloser Wasserlecksensor auf nRF52832, DIY-Projekt

Grüße an alle Leser des DIY- oder Do-it-yourself-Bereichs auf Habr! Heute möchte ich über mein nächstes Projekt sprechen. In diesem Artikel geht es um einen batteriebetriebenen Wasserleckdetektor. Wie in früheren Projekten läuft dieses Gerät auf dem Mikrocontroller nRF52832. Es gibt drei Versionen dieses Sensors. In allen drei Versionen werden vorgefertigte Module mit nRF52832 verwendet. In diesem Artikel wird auf die mittlere Version eingegangen, die das YJ-17103-Modul von HOLYIOT verwendet.



Der Flüssigkeitsdetektor ist auf dem Chip SN74LVC1G00 | implementiert Datashit . Ich werde kurz das Schaltungsdesign und das Funktionsprinzip beschreiben. Die Sensorelektrode Nr. 1 ist mit Masse verbunden, die Sensorelektrode Nr. 2 ist über einen 100-Ohm-Widerstand mit den Beinen A und B des SN74LVC1G00-Chips verbunden, 3,3 V über einen 1-M-Widerstand sind ebenfalls mit dieser Leitung verbunden, und der Schaltung wird ebenfalls eine Kapazität hinzugefügt. Wenn kein Kontakt mit der Flüssigkeit auf den Schenkeln der Mikroschaltung A und B besteht, ist die logische Einheit auf dem Schenkel Y, der mit dem MK-Schenkel verbunden ist (programmgesteuert konfiguriert, um eine Unterbrechung über den eingebauten Komparator zu erkennen), logisch Null. Sobald Kontakt mit der Flüssigkeit auftritt und die Beine A und B niedrig sind, wird auch das Signal am Bein Y des Mikroschaltkreises SN74LVC1G00 invertiert, was zu einer Unterbrechung führt, die wiederum den MK aus dem Schlaf entfernt. In Zukunft kann der Chip SN74LVC1G00 durch den Chip SN74LVC1G14 | ersetzt werden Datashit und vielleicht auch nicht :). Der Nachweis von Flüssigkeit aus den MK-Beinen durch den eingebauten Komparator ist nicht geplant.

Wie alle meine anderen Projekte ist auch dieses ein Arduino-Projekt und wie alle Projekte des letzten Jahres (ungefähr) wurde dieses auch für das Mysensors-Projekt erstellt. Wie in meinen anderen Artikeln werde ich in diesem Artikel ein wenig auf das Thema Mysensoren eingehen.

Mysensors ist eine Open-Source-Community von Entwicklern. Dieses Protokoll wurde von der Community entwickelt, um Funk- und Kabelnetzwerke zu erstellen. Das Projekt wurde ursprünglich für die Arduino-Plattform entwickelt. Ein Standard-Mysensors-Netzwerk besteht aus einem Gate (Gateway), Retransformatoren und Endgeräten (Knoten). In einem Netzwerk können bis zu 254 Geräte vorhanden sein. Jedes der Geräte kann mit bis zu 254 Sensoren, Sensoren und Betätigungseinheiten ausgestattet sein. Der Netzwerkbetrieb, die Datenverarbeitung, die Skriptausführung und die Interaktion in anderen Geräten werden mit dem UD-Controller ausgeführt. Einige der Controller (Majordomo) unterstützen die Arbeit mit mehreren Netzwerken und Mysensoren (Multi-Gate), sodass es viel mehr Netzwerke geben kann als eines, das von einem Controller gesteuert wird.

Unterstützte Hardwareplattformen : Linux / Raspberry Pi / Orange Pi | ATMega 328P | ESP8266 | ESP32 | nRF5x (Cortex M0, M4) | Atmel SAMD verwendet in Arduino Zero (Cortex M0) | Teensy3 (MK66FX1M0VMD18) | STM32F1.

Unterstützte Funksender: NRF24L01 | RFM69 | RFM95 (LoRa) | nRF5x

Unterstützte drahtgebundene Kommunikationsart : RS485

Unterstützte Kommunikation zwischen Gate und Controller : MQTT | Serielle USB | Wifi | Ethernet | GSM

Zurück zum Lecksensor. Das Gerät wird mit CR2430-, CR2450- oder CR2477-Batterien betrieben. Der Schlafverbrauch beträgt weniger als 3μA. Übertragungsgeschwindigkeit - 250 Kbit / s, 10-15 ms. Der Stromverbrauch zum Zeitpunkt der Übertragung beträgt nicht mehr als 8 mA. Theoretisch entspricht die Batterielebensdauer einer einzelnen Batterie ungefähr der Selbstentladungszeit der Batterie. In der Praxis ist natürlich alles weniger rosig, da der Ladezustand registriert, präsentiert und regelmäßig gesendet wird, so dass die Batterielebensdauer eher dem Wert entspricht - Selbstentladungszeit / 2 :). Die Stromversorgung erfolgt direkt über die Batterie, der Batteriestand wird direkt über den VDD-Pin gesteuert. Im Sensor ist eine RGB-LED installiert, um die Registrierung des Sensors im Netzwerk anzuzeigen, Servicemodi anzuzeigen und die Lecksuche anzuzeigen. Natürlich darf die LED überhaupt nicht oder nur teilweise verwendet werden.

Die Geräteplatine wurde für die weitere Herstellung nach der LUT-Methode hergestellt. Aufgrund der Nuancen dieser Option ist es daher eine größere Breite der Gleise, größere Abstände zwischen den Gleisen, größere Flächen für Zwischenschichtübergänge (zum bequemeren Bohren von Löchern), mangelndes Ausfüllen leerer Flächen aufgrund der kleinen Fläche der Platte. Später wurde eine Option für einen Auftrag in der Produktion gemacht.



Das Gerätegehäuse besteht aus zwei Teilen. Die obere Abdeckung mit Platz für die Montage der Platine und der untere Teil (Bad) mit 2 Löchern für Stahlkontaktschrauben (Abdichtung mit Silikondichtmittel für den Schraubenkopf möglich oder nicht erforderlich) und zwei Rohren für Knöpfe (Reset und Modi) auf der Platine. Der Druck wurde auf einem ANICUBIC PHOTON SLA 3D-Drucker durchgeführt. Nach dem Drucken wurden die Schleifpapiere 320 und 1000 so verarbeitet, dass sie in die Fugen des Deckels und der Unterseite des Körpers passten.





Sensorfotos















Testcode
wl_standart_test.ino

bool button_flag; bool send_flag; bool detection; bool nosleep; byte timer; bool AckG; bool AckB; bool AckL; bool PRESENT_ACK; bool flag_lq; unsigned long SLEEP_TIME = 172800000; //48 hours //unsigned long SLEEP_TIME = 3600000; //1 hour unsigned long oldmillis; unsigned long newmillis; unsigned long interrupt_time; unsigned long SLEEP_TIME_W; uint16_t currentBatteryPercent; uint16_t batteryVoltage = 0; uint16_t battery_vcc_min = 2300; uint16_t battery_vcc_max = 3000; int16_t linkQuality; #define MY_DISABLED_SERIAL #define MY_RADIO_NRF5_ESB #define MY_RF24_PA_LEVEL (NRF5_PA_MAX) //#define MY_PASSIVE_NODE #define MY_NODE_ID 86 #define MY_PARENT_NODE_ID 0 #define MY_PARENT_NODE_IS_STATIC #define MY_TRANSPORT_UPLINK_CHECK_DISABLED #define INTR_PIN 3 //(PORT0, gpio 5) #include <MySensors.h> // see https://www.mysensors.org/download/serial_api_20 #define W_L_SENS_CHILD_ID 0 #define LINK_QUALITY_CHILD_ID 253 MyMessage sensMsg(W_L_SENS_CHILD_ID, V_VAR1); //MyMessage voltMsg(CHILD_ID_VOLT, V_VOLTAGE); void preHwInit() { pinMode(POWER_PIN, OUTPUT); digitalWrite(POWER_PIN, HIGH); wait(3000); pinMode(RED_LED, OUTPUT); digitalWrite(RED_LED, HIGH); pinMode(GREEN_LED, OUTPUT); digitalWrite(GREEN_LED, HIGH); pinMode(BLUE_LED, OUTPUT); digitalWrite(BLUE_LED, HIGH); pinMode(PIN_BUTTON, INPUT); pinMode(W_L_SENS, INPUT); //pinMode(24, OUTPUT); //pinMode(20, OUTPUT); } void before() { NRF_POWER->DCDCEN = 1; NRF_UART0->ENABLE = 0; digitalWrite(BLUE_LED, LOW); sleep(50); digitalWrite(BLUE_LED, HIGH); } void presentation() { sendSketchInfo("EFEKTA ST WL Sensor", "1.1"); present(W_L_SENS_CHILD_ID, S_CUSTOM, "SWITCH STATUS"); present(LINK_QUALITY_CHILD_ID, S_CUSTOM, "LINK_QUALITY"); } void setup() { digitalWrite(BLUE_LED, LOW); wait(100); digitalWrite(BLUE_LED, HIGH); wait(200); digitalWrite(BLUE_LED, LOW); wait(100); digitalWrite(BLUE_LED, HIGH); lpComp(); detection = false; SLEEP_TIME_W = SLEEP_TIME; wait(100); sendBatteryStatus(); wait(100); send(sensMsg.set(detection), 1); wait(2000, 1, V_VAR1); } void loop() { if (nosleep == 0) { oldmillis = millis(); sleep(SLEEP_TIME_W); } if (detection) { if (digitalRead(PIN_BUTTON) == 1 && button_flag == 0 && digitalRead(W_L_SENS) == 0) { //back side button detection button_flag = 1; nosleep = 1; } if (digitalRead(PIN_BUTTON) == 1 && button_flag == 1 && digitalRead(W_L_SENS) == 0) { digitalWrite(GREEN_LED, LOW); wait(10); digitalWrite(GREEN_LED, HIGH); wait(50); } if (digitalRead(PIN_BUTTON) == 0 && button_flag == 1 && digitalRead(W_L_SENS) == 0) { nosleep = 0; button_flag = 0; digitalWrite(GREEN_LED, HIGH); lpComp_reset(); } if (digitalRead(W_L_SENS) == 1 && digitalRead(PIN_BUTTON) == 0) { //sens detection newmillis = millis(); interrupt_time = newmillis - oldmillis; SLEEP_TIME_W = SLEEP_TIME_W - interrupt_time; send(sensMsg.set(detection), 1); wait(3000, 1, V_VAR1); if (AckG == 1) { while (timer < 10) { timer++; digitalWrite(BLUE_LED, LOW); wait(20); digitalWrite(BLUE_LED, HIGH); wait(30); } timer = 0; AckG = 0; wait(200); } else { while (timer < 10) { timer++; digitalWrite(RED_LED, LOW); wait(20); digitalWrite(RED_LED, HIGH); wait(30); } timer = 0; send(sensMsg.set(detection), 1); wait(3000, 1, V_VAR1); if (AckG == 1) { while (timer < 10) { timer++; digitalWrite(BLUE_LED, LOW); wait(20); digitalWrite(BLUE_LED, HIGH); wait(30); } timer = 0; AckG = 0; } else { while (timer < 10) { timer++; digitalWrite(RED_LED, LOW); wait(20); digitalWrite(RED_LED, HIGH); wait(30); } timer = 0; } lpComp_reset(); } } if (SLEEP_TIME_W < 60000) { SLEEP_TIME_W = SLEEP_TIME; sendBatteryStatus(); } } else { //if (detection == -1) { SLEEP_TIME_W = SLEEP_TIME; sendBatteryStatus(); } } void receive(const MyMessage & message) { if (message.type == V_VAR1) { if (message.sensor == W_L_SENS_CHILD_ID) { if (mGetCommand(message) == 1) { if (message.isAck()) { AckG = 1; } else { } } } } if (message.type == I_BATTERY_LEVEL) { if (message.sensor == 255) { if (mGetCommand(message) == 3) { if (message.isAck()) { AckB = 1; } else { } } } } if (message.type == V_VAR1) { if (message.sensor == 255) { if (mGetCommand(message) == 1) { if (message.isAck()) { AckL = 1; } else { } } } } } void sendBatteryStatus() { wait(100); batteryVoltage = hwCPUVoltage(); wait(20); if (batteryVoltage > battery_vcc_max) { currentBatteryPercent = 100; } else if (batteryVoltage < battery_vcc_min) { currentBatteryPercent = 0; } else { currentBatteryPercent = (100 * (batteryVoltage - battery_vcc_min)) / (battery_vcc_max - battery_vcc_min); } sendBatteryLevel(currentBatteryPercent, 1); wait(3000, C_INTERNAL, I_BATTERY_LEVEL); if (AckB == 1) { AckB = 0; flag_lq = 1; } else { sendBatteryLevel(currentBatteryPercent, 1); wait(3000, C_INTERNAL, I_BATTERY_LEVEL); if (AckB == 1) { AckB = 0; flag_lq = 1; } } //send(powerMsg.set(batteryVoltage), 1); //wait(2000, 1, V_VAR1); //sleep(10000); // if (flag_lq == 1) { linkQuality = calculationRxQuality(); wait(50); sendSignalStrength(linkQuality, 1); wait(2000, 1, V_VAR1); if (AckL == 1) { AckL = 0; } else { sendSignalStrength(linkQuality, 1); wait(2000, 1, V_VAR1); if (AckL == 1) { AckG = 0; } } flag_lq = 0; } } void lpComp() { NRF_LPCOMP->PSEL = INTR_PIN; NRF_LPCOMP->ANADETECT = 1; NRF_LPCOMP->INTENSET = B0100; NRF_LPCOMP->ENABLE = 1; NRF_LPCOMP->TASKS_START = 1; NVIC_SetPriority(LPCOMP_IRQn, 15); NVIC_ClearPendingIRQ(LPCOMP_IRQn); NVIC_EnableIRQ(LPCOMP_IRQn); } void s_lpComp() { if ((NRF_LPCOMP->ENABLE) && (NRF_LPCOMP->EVENTS_READY)) { NRF_LPCOMP->INTENCLR = B0100; } } void r_lpComp() { NRF_LPCOMP->INTENSET = B0100; } #if __CORTEX_M == 0x04 #define NRF5_RESET_EVENT(event) \ event = 0; \ (void)event #else #define NRF5_RESET_EVENT(event) event = 0 #endif void lpComp_reset () { s_lpComp(); detection = false; NRF_LPCOMP->EVENTS_UP = 0; r_lpComp(); } //****************************** very experimental ******************************* bool sendSignalStrength(const int16_t level, const bool ack) { return _sendRoute(build(_msgTmp, GATEWAY_ADDRESS, NODE_SENSOR_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; } //****************************** very experimental ******************************* extern "C" { void LPCOMP_IRQHandler(void) { detection = true; NRF5_RESET_EVENT(NRF_LPCOMP->EVENTS_UP); NRF_LPCOMP->EVENTS_UP = 0; MY_HW_RTC->CC[0] = (MY_HW_RTC->COUNTER + 2); } } 

MyBoardNRF5.h

 #ifndef _MYBOARDNRF5_H_ #define _MYBOARDNRF5_H_ #ifdef __cplusplus extern "C" { #endif // __cplusplus #define PINS_COUNT (32u) #define NUM_DIGITAL_PINS (32u) #define NUM_ANALOG_INPUTS (8u) #define NUM_ANALOG_OUTPUTS (8u) #define PIN_LED1 (27) #define PIN_LED2 (25) #define PIN_LED3 (26) #define RED_LED (PIN_LED1) #define GREEN_LED (PIN_LED2) #define BLUE_LED (PIN_LED3) #define PIN_BUTTON (14) #define W_L_SENS (8) #define POWER_PIN (7) #define PIN_SERIAL_RX (12) #define PIN_SERIAL_TX (11) #ifdef __cplusplus } #endif #endif 


Der nRF52832 ist programmgesteuert für den Betrieb im Energiesparmodus (DC-DC-Modus) konfiguriert. Der MC wird durch ein Signal vom Mikrokreis SN74LVC1G00 über den internen LPCOMP-Komparator aus dem Ruhezustand geweckt. Das Gerät verfügt außerdem über eine Uhrentaste zum Implementieren von Servicemodi, z. B. Gerätekopplung, Zurücksetzen des Geräts usw. Der Knopf ist auf den gleichen MK-Fuß wie der Lecksucher gewickelt. Beide Leitungen sind durch Schottky-Dioden getrennt. Der Chip SN74LVC1G00 im Überwachungsmodus verbraucht nichts. Die Energieverwaltung der Mikroschaltung erfolgt über die Beine des MK.

Derzeit ist die Entwicklung einer Steuerung für Wasserleckagen, mit der diese Sensoren arbeiten sollen, fast abgeschlossen.

Video zeigt den Leckagesensor


Github-Projekt
(Gerber-Dateien, Software, Fallmodelle, Komponentenliste)

Ein Ort, an dem Sie immer gerne allen helfen, die sich mit MYSENSORS vertraut machen möchten (Installation von Boards, Arbeit mit nRF5-Mikrocontrollern in der Arduino IDE-Umgebung, Tipps zur Arbeit mit dem mysensors-Protokoll, Erörterung neuer Copyright-Projekte - Telegramm-Chat @mysensors_rus .

Source: https://habr.com/ru/post/de460177/


All Articles