Sensor de fuga de agua inalámbrico en nRF52832, proyecto de bricolaje

¡Saludos a todos los lectores de la sección de bricolaje o bricolaje en Habr! Hoy quiero hablar sobre mi próximo proyecto, este artículo tratará sobre un detector de fugas de agua alimentado por batería. Como en proyectos anteriores, este dispositivo se ejecuta en el microcontrolador nRF52832. Hay tres versiones de este sensor, en las tres versiones se utilizan módulos prefabricados con nRF52832, en este artículo hablaremos sobre la versión intermedia que utiliza el módulo YJ-17103 de HOLYIOT.



El detector de fluido se implementa en el chip SN74LVC1G00 | Datashit Describa brevemente el diseño del circuito y el principio de funcionamiento. El electrodo del sensor n. ° 1 está conectado a tierra, el electrodo del sensor n. ° 2 está conectado a las patas A y B del microcircuito SN74LVC1G00 a través de una resistencia de 100 ohmios, 3.3V a través de una resistencia de 1M también está conectado a esta línea, también se agrega una capacitancia al circuito. Cuando no hay contacto con el líquido en las patas del microcircuito A y B, la unidad lógica, respectivamente, en la pata Y conectada a la pata MK (configurada programáticamente para detectar la interrupción a través del comparador incorporado) es el cero lógico. Tan pronto como se produce el contacto con el líquido y las patas A y B están bajas, la señal en la pata Y del microcircuito SN74LVC1G00 también se invierte, lo que provocará una interrupción, lo que a su vez eliminará la MK del sueño. En el futuro, el chip SN74LVC1G00 puede ser reemplazado por el chip SN74LVC1G14 | Datashit , y tal vez no :). La detección de fluido de las patas MK a través del comparador incorporado no está planificada.

Al igual que todos mis otros proyectos, este también es un proyecto Arduino y, como todos los proyectos durante el año pasado (aproximadamente), este también se ha realizado para el proyecto Mysensors. Como en mis otros artículos, tocaré un poco el tema de Mysensors en este artículo.

Mysensors es una comunidad de desarrolladores de código abierto. La comunidad desarrolla este protocolo para crear redes de radio y cableadas. El proyecto fue desarrollado originalmente para la plataforma Arduino. Una red Mysensors estándar consta de una puerta (puerta de enlace), retransformadores y dispositivos finales (nodos). En una red puede haber hasta 254 dispositivos, cada uno de los dispositivos puede estar equipado con hasta 254 sensores, sensores, unidades de accionamiento. La operación de la red, el procesamiento de datos, la ejecución del script y la interacción en otros dispositivos se llevan a cabo utilizando el controlador UD. Algunos de los controladores (Majordomo) admiten trabajar con varias redes y Mysensors (multi-gate), respectivamente, puede haber muchas más redes que una controlada por un controlador.

Plataformas de hardware compatibles : Linux / Raspberry Pi / Orange Pi | ATMega 328P | ESP8266 | ESP32 | nRF5x (Cortex M0, M4) | Atmel SAMD utilizado en Arduino Zero (Cortex M0) | Teensy3 (MK66FX1M0VMD18) | STM32F1.

Transmisores de radio compatibles : NRF24L01 | RFM69 | RFM95 (LoRa) | nRF5x

Tipo de comunicación por cable compatible : RS485

Comunicación soportada entre la puerta y el controlador : MQTT | USB serie | Wifi | Ethernet | GSM

De vuelta al sensor de fugas. El dispositivo funciona con baterías CR2430, CR2450 o CR2477. El consumo de sueño es inferior a 3 μA. Velocidad de transmisión: 250 Kbps, 10-15 ms. El consumo de energía en el momento de la transmisión no es más de 8 mA. Teóricamente, la duración de la batería en una sola batería es aproximadamente igual al tiempo de autodescarga de la batería. En la práctica, por supuesto, todo es menos optimista, ya que hay un registro, presentación, envío periódico del nivel de carga, de modo que la duración de la batería está más cerca del valor: tiempo de autodescarga / 2 :). La energía se suministra directamente desde la batería, el nivel de la batería se controla directamente desde el pin VDD. Se instala un LED RGB en el sensor para indicar el registro del sensor en la red, para indicar los modos de servicio y para indicar la detección de fugas. Naturalmente, el LED puede no usarse en absoluto o usarse parcialmente.

La placa del dispositivo se fabricó para su posterior fabricación de acuerdo con el método LUT. Por lo tanto, a partir de los matices de esta opción, es un mayor ancho de las pistas, mayores distancias entre las pistas, mayores áreas para transiciones entre capas (para una perforación más conveniente de los agujeros) y la ausencia de relleno en áreas vacías debido al área pequeña del tablero. Más tarde, se hizo una opción para un pedido en producción.



La carcasa del dispositivo fue diseñada en dos partes. La cubierta superior con lugares para montar el tablero y la parte inferior (baño) con 2 orificios para tornillos de contacto de acero (el sellado es posible con sellador de silicona para la cabeza del tornillo o no es necesario) y dos tubos para botones (restablecimiento y modos) en el tablero. La impresión se realizó en una impresora ANICUBIC PHOTON SLA 3D. Después de la impresión, se procesaron papel de lija 320 y 1000 para adaptarse a las juntas de la tapa y la parte inferior del cuerpo.





Fotos del sensor















Código de prueba
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 


El nRF52832 está configurado mediante programación para funcionar en modo de baja potencia (modo DC-DC). El MC se despierta del modo inactivo mediante una señal del microcircuito SN74LVC1G00 a través del comparador interno LPCOMP. El dispositivo también tiene un botón de reloj para implementar modos de servicio, como emparejamiento de dispositivos, restablecimiento del dispositivo, etc. El botón está enrollado en el mismo pie MK que el detector de fugas. Ambas líneas están separadas por diodos Schottky. El chip SN74LVC1G00 en modo de monitoreo no consume nada. La administración de energía del microcircuito se lleva a cabo desde las patas del MK.

Por el momento, el desarrollo de un controlador para fugas de agua con el que deberían funcionar estos sensores está casi terminado.

Video que muestra el sensor de fugas


Proyecto Github
(archivos gerber, software, modelos de casos, lista de componentes)

Un lugar donde siempre estará feliz de ayudar a todos los que quieran familiarizarse con MYSENSORS (instalar placas, trabajar con microcontroladores nRF5 en el entorno Arduino IDE, consejos para trabajar con el protocolo mysensors, discutir nuevos proyectos de derechos de autor - telegram chat @mysensors_rus .

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


All Articles