Capteur de fuite d'eau sans fil sur nRF52832, projet de bricolage

Salutations à tous les lecteurs de la section DIY ou Do-it-yourself sur Habr! Aujourd'hui, je veux parler de mon prochain projet, cet article portera sur un détecteur de fuite d'eau alimenté par batterie. Comme dans les projets précédents, cet appareil fonctionne sur le microcontrôleur nRF52832. Il existe trois versions de ce capteur, dans les trois versions, des modules prêts à l'emploi avec nRF52832 sont utilisés, dans cet article, nous parlerons de la version intermédiaire qui utilise le module YJ-17103 de HOLYIOT.



Le détecteur de fluide est implémenté sur la puce SN74LVC1G00 | Datashit . Je décrirai brièvement la conception du circuit et le principe de fonctionnement. L'électrode de capteur n ° 1 est connectée à la masse, l'électrode de capteur n ° 2 est connectée aux jambes A et B de la puce SN74LVC1G00 via une résistance de 100 Ohms, 3,3 V via une résistance de 1 M est également connectée à cette ligne, une capacité est également ajoutée au circuit. Lorsqu'il n'y a pas de contact avec le liquide sur les jambes du microcircuit A et B, l'unité logique, respectivement, sur la jambe Y connectée à la jambe MK (configurée par programme pour détecter une interruption via le comparateur intégré) est un zéro logique. Dès que le contact avec le liquide se produit et que les jambes A et B sont faibles, le signal sur la jambe Y du microcircuit SN74LVC1G00 est également inversé, ce qui provoquera une interruption, ce qui à son tour retirera le MK du sommeil. À l'avenir, la puce SN74LVC1G00 pourrait être remplacée par la puce SN74LVC1G14 | Datashit , et peut-être pas :). La détection de fluide des jambes MK à travers le comparateur intégré n'est pas prévue.

Comme tous mes autres projets, celui-ci est également un projet Arduino et, comme tous les projets de l'année dernière (environ), celui-ci a également été réalisé pour le projet Mysensors. Comme dans mes autres articles, je vais aborder un peu le sujet des Mysensors dans cet article.

Mysensors est une communauté de développeurs open source. Ce protocole est développé par la communauté pour créer des réseaux radio et filaires. Le projet a été initialement développé pour la plate-forme Arduino. Un réseau Mysensors standard se compose d'une porte (passerelle), de retransformateurs et de terminaux (nœuds). Dans un réseau, il peut y avoir jusqu'à 254 appareils, chacun des appareils peut être équipé de jusqu'à 254 capteurs, capteurs, unités d'actionnement. Le fonctionnement du réseau, le traitement des données, l'exécution de scripts et l'interaction dans d'autres appareils sont effectués à l'aide du contrôleur UD. Certains contrôleurs (Majordomo) prennent en charge le travail avec plusieurs réseaux et Mysensors (multi-gate), il peut donc y avoir beaucoup plus de réseaux qu'un seul contrôlé par un contrôleur.

Plates - formes matérielles prises en charge : Linux / Raspberry Pi / Orange Pi | ATMega 328P | ESP8266 | ESP32 | nRF5x (Cortex M0, M4) | Atmel SAMD utilisé dans Arduino Zero (Cortex M0) | Teensy3 (MK66FX1M0VMD18) | STM32F1.

Émetteurs radio pris en charge : NRF24L01 | RFM69 | RFM95 (LoRa) | nRF5x

Type de communication filaire pris en charge : RS485

Communication prise en charge entre le portail et le contrôleur : MQTT | USB série | Wifi | Ethernet | GSM

Revenons au capteur de fuite. L'appareil est alimenté par des piles CR2430, CR2450 ou CR2477. La consommation de sommeil est inférieure à 3 μA. Vitesse de transmission - 250 Kbps, 10-15 ms. La consommation d'énergie au moment de la transmission ne dépasse pas 8 mA. Théoriquement, la durée de vie d'une batterie sur une seule batterie est approximativement égale au temps d'autodécharge de la batterie. Dans la pratique, bien sûr, tout est moins rose, car il y a un enregistrement, une présentation, un envoi périodique du niveau de charge, afin que la durée de vie de la batterie soit plus proche de la valeur - temps d'auto-décharge / 2 :). L'alimentation est fournie directement par la batterie, le niveau de la batterie est contrôlé directement à partir de la broche VDD. Une LED RGB est installée dans le capteur pour indiquer l'enregistrement du capteur dans le réseau, pour indiquer les modes de service et pour indiquer la détection des fuites. Naturellement, la LED ne peut pas être utilisée du tout ou partiellement.

La carte d'appareil a été fabriquée pour sa fabrication ultérieure selon la méthode LUT. Par conséquent, à partir des nuances de cette option, c'est une largeur accrue des pistes, des distances accrues entre les pistes, des zones accrues pour les transitions intercouches (pour un perçage plus pratique des trous) et l'absence de remplissage dans les zones vides en raison de la petite zone de la planche. Plus tard, une option a été faite pour une commande en production.



Le boîtier de l'appareil a été conçu en deux parties. Le capot supérieur avec des emplacements pour le montage de la carte et la partie inférieure (bain) avec 2 trous pour les vis de contact en acier (l'étanchéité est possible avec du mastic silicone pour la tête de vis ou non requis) et deux tubes pour les boutons (réinitialisation et modes) sur la carte. L'impression a été effectuée sur une imprimante 3D ANICUBIC PHOTON SLA. Après l'impression, le papier de verre 320 et 1000 a été traité pour s'adapter aux joints du couvercle et du fond du corps.





Photos du capteur















Code de test
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 


Le nRF52832 est configuré par programme pour fonctionner en mode basse consommation (mode DC-DC). Le MC est réveillé du sommeil par un signal provenant du microcircuit SN74LVC1G00 via le comparateur LPCOMP interne. L'appareil dispose également d'un bouton d'horloge pour implémenter les modes de service, tels que l'appariement de l'appareil, la réinitialisation de l'appareil, etc. Le bouton est enroulé sur le même pied MK que le détecteur de fuite. Les deux lignes sont séparées par des diodes Schottky. La puce SN74LVC1G00 en mode surveillance ne consomme rien. La gestion de l'alimentation du microcircuit s'effectue à partir des jambes du MK.

À l'heure actuelle, le développement d'un contrôleur de fuite d'eau avec lequel ces capteurs devraient fonctionner est presque terminé.

Vidéo montrant le capteur de fuite


Projet Github
(fichiers gerber, logiciels, modèles de boîtiers, liste des composants)

Un endroit où vous êtes toujours heureux d'aider tous ceux qui veulent se familiariser avec MYSENSORS (installation de cartes, travail avec des microcontrôleurs nRF5 dans l'environnement Arduino IDE, conseils pour travailler avec le protocole mysensors, discussion de nouveaux projets de copyright - télégramme chat @mysensors_rus .

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


All Articles