Interruptor táctil inalámbrico con luz de fondo fluorescente adicional

¡Saludos a todos los lectores de la sección de bricolaje o bricolaje en Habr! El artículo de hoy tratará sobre el interruptor táctil en el chip TTP223 | hoja de datos El interruptor funciona en el microcontrolador nRF52832 | hoja de datos , se utilizó el módulo YJ-17103 con una antena impresa y un conector para una antena externa MHF4. El interruptor táctil funciona con baterías CR2430 o CR2450. El consumo en modo de transmisión no es más de 8 mA, en modo de suspensión no es más de 6 mA.



Como todos los proyectos anteriores, este también es un proyecto Arduino, el programa está escrito en el IDE de Arduino. La implementación del software del dispositivo se basa en el protocolo Mysensors | Bibliotecas GitHub , soporte GitHub para placas nRF5 en Mysensors. Foro de la comunidad en idioma inglés: http://forum.mysensors.org , foro de la comunidad en idioma ruso: http://mysensors.ru/forum/
(Para aquellos que deseen estudiar - Documentación , Protocolo de serie , API , Protocolo , Analizador | para aquellos que deseen ayudar en el desarrollo del proyecto - Documentación )

La placa del interruptor táctil se desarrolló en el programa Diptrace, teniendo en cuenta la fabricación posterior de acuerdo con el método de Tecnología de Hierro Láser (LUT). El tablero se desarrolló en tamaños de 60x60 mm (el panel de vidrio estándar tiene dimensiones de 80x80 mm). El circuito se imprimió en las páginas de la revista Antenna y se transfirió con una plancha Bosch con el ajuste "Len" (potencia máxima) a una placa de fibra de vidrio recubierta de aluminio de doble cara de 1,5 mm, 35 µm (por falta de otra).


El grabado se realizó con una solución de cloruro férrico, previamente preparada en las proporciones de 1,5 partes de una cuchara por 250 ml de agua tibia. El proceso tomó 15 minutos.
Los orificios para las transiciones entre capas y para montar el soporte de la batería se perforaron con un mini taladro DREMEL 3000 montado en un soporte de taladro DREMEL 220. Los orificios para las transiciones entre capas se taladraron con un taladro de 0,4 mm, los orificios para el soporte de la batería con un taladro de 1,1 mm. El recorte a lo largo de los bordes del tablero se realizó con el mismo mini taladro con una boquilla DREMEL 540 (rueda de corte d = 32,0 mm). El cultivo se realizó en un respirador.
El estañado del tablero grabado se realizó usando una aleación Rose en una solución acuosa (1 cucharadita de ácido cítrico cristalizado en 300 ml de agua).

El proceso de soldadura tomó aproximadamente una hora, la mayor parte del tiempo se dedicó a soldar el alambre (estañado, de 0,4 mm de diámetro) en los agujeros para las transiciones entre capas.

La placa se lavó con el limpiador en aerosol FLUX OFF.




El desarrollo de la carcasa del dispositivo se realizó en un editor de diseño tridimensional asistido por computadora. Dimensiones de la caja 78.5 mm X 78.5 mm X 12 mm.


El modelo terminado de la carcasa y la tapa de la batería se guardaron en formato STL, luego fue necesario preparar estos modelos para imprimir en una impresora SLA (agregar soportes, orientación). En este punto, surgió un pequeño problema, ya que el área de impresión de las impresoras domésticas SLA es pequeña. El modelo del cuerpo del dispositivo en la posición más óptima en relación con el tiempo de impresión no se ajustaba al tamaño del área de impresión. Al colocar el modelo a 45 grados, también dio un resultado decepcionante, el peso de soporte fue igual al peso del modelo de caja. Se decidió imprimir el modelo verticalmente, apoyando en uno de los lados frontales, previamente de acuerdo con el hecho del procesamiento posterior. Tomó 5 horas sellar la caja con una configuración de capa de 50 micras. A continuación, el procesamiento se realizó con papel de lija de grano muy fino (no escribiré el número, porque no sé :)). La tapa de la batería se imprimió durante 40 minutos.


Los paneles de vidrio con Aliexpress se venden con un marco de plástico ya pegado, no hubo problemas para quitar el marco. Me quité el precalentamiento del panel de vidrio con un secador de pelo normal.




El difusor para la retroiluminación LED estaba hecho de cinta de doble cara con adhesivo acrílico 3M 9088-200. Para la iluminación fluorescente, había varios materiales para elegir, cinta adhesiva china y papel adhesivo cortado en cintas de la compañía nacional Luminophore. La elección se hizo a favor de un fabricante nacional; según mis sentimientos, brillaba más y más. Se pegó un cuadrado de papel con pigmento fluorescente encima de cinta adhesiva de doble cara 3M 9088-200.

El vidrio se pegó a la caja del disyuntor utilizando cinta de doble cara con adhesivo acrílico 3M VHB 4910.


La cubierta se fijó con un tornillo M 1.4 X 5 mm.

El costo del dispositivo fue de 890 rublos.

Luego vino la parte del software. No hay problema Resulta que los microcircuitos del sensor TTP223 funcionan perfectamente con energía estabilizada a 3.3V y no son muy buenos cuando se alimentan directamente de una batería bien descargada. Al comienzo del dispositivo con alimentación en la región de 2.5v, más después de una "reducción" adicional al realizar la presentación de Mysensors, el chip TTP223 (inmediatamente después de la calibración) hizo que el MC se interrumpiera porque estaba con el activador activo.

Se cambió el circuito para suministrar energía al microcircuito (administración de energía TTP223 con gpio MK), se suministró tierra adicional, en las líneas de led rgb (que pasan al otro lado de la placa del sensor capacitivo), se reemplazaron las resistencias con una mayor resistencia. También se agregó al software: activación de energía para un microcircuito capacitivo después de iniciar el marco Mysensors y resolver la presentación. La demora para la calibración automática del chip TTP223 se duplica cuando se le aplica energía. Todos estos cambios arreglaron completamente este problema.

Antes de ver el código del programa, le recomiendo que se familiarice con la estructura básica de los bocetos en Mysensors.
void before()
{
// , , before() setup(), Mysensors, SPI
}

void setup()
{

}

void presentation()
{
//
sendSketchInfo("Name of my sensor node", "1.0"); // ,
present(CHILD_ID, S_WHATEVER, "Description"); // ,
}

void loop()
{

}



Código de prueba para el programa de interruptor táctil:
test_sens.ino
/**
NRF_LPCOMP
*/
bool button_flag;
bool sens_flag;
bool send_flag;
bool detection;
bool nosleep;
byte timer;
unsigned long SLEEP_TIME = 21600000; //6 hours
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 = 2400;
uint16_t battery_vcc_max = 3000;

#define MY_RADIO_NRF5_ESB
//#define MY_PASSIVE_NODE
#define MY_NODE_ID 30
#define MY_PARENT_NODE_ID 0
#define MY_PARENT_NODE_IS_STATIC
#define MY_TRANSPORT_UPLINK_CHECK_DISABLED
#define IRT_PIN 3 //(PORT0, gpio 5)
#include <MySensors.h>
// see https://www.mysensors.org/download/serial_api_20
#define SENS_CHILD_ID 0
#define CHILD_ID_VOLT 254
MyMessage sensMsg(SENS_CHILD_ID, V_VAR1);
//MyMessage voltMsg(CHILD_ID_VOLT, V_VOLTAGE);

void preHwInit() {
sleep(2000);
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(MODE_PIN, INPUT);
pinMode(SENS_PIN, INPUT);
}

void before()
{
NRF_POWER->DCDCEN = 1;
NRF_UART0->ENABLE = 0;
sleep(1000);
digitalWrite(BLUE_LED, LOW);
sleep(150);
digitalWrite(BLUE_LED, HIGH);
}

void presentation() {
sendSketchInfo("EFEKTA Sens 1CH Sensor", "1.1");
present(SENS_CHILD_ID, S_CUSTOM, "SWITCH STATUS");
//present(CHILD_ID_VOLT, S_MULTIMETER, "Battery");
}

void setup() {
digitalWrite(BLUE_LED, LOW);
sleep(100);
digitalWrite(BLUE_LED, HIGH);
sleep(200);
digitalWrite(BLUE_LED, LOW);
sleep(100);
digitalWrite(BLUE_LED, HIGH);
lpComp();
detection = false;
SLEEP_TIME_W = SLEEP_TIME;
pinMode(31, OUTPUT);
digitalWrite(31, HIGH);
/*
while (timer < 10) {
timer++;
digitalWrite(GREEN_LED, LOW);
wait(5);
digitalWrite(GREEN_LED, HIGH);
wait(500);
}
timer = 0;
*/
sleep(7000);
while (timer < 3) {
timer++;
digitalWrite(GREEN_LED, LOW);
sleep(15);
digitalWrite(GREEN_LED, HIGH);
sleep(85);
}
timer = 0;
sleep(1000);
}

void loop() {

if (detection) {
if (digitalRead(MODE_PIN) == 1 && button_flag == 0 && digitalRead(SENS_PIN) == 0) {
//back side button detection
button_flag = 1;
nosleep = 1;
}
if (digitalRead(MODE_PIN) == 1 && button_flag == 1 && digitalRead(SENS_PIN) == 0) {
digitalWrite(RED_LED, LOW);
wait(10);
digitalWrite(RED_LED, HIGH);
wait(50);
}
if (digitalRead(MODE_PIN) == 0 && button_flag == 1 && digitalRead(SENS_PIN) == 0) {
nosleep = 0;
button_flag = 0;
digitalWrite(RED_LED, HIGH);
lpComp_reset();
}

if (digitalRead(SENS_PIN) == 1 && sens_flag == 0 && digitalRead(MODE_PIN) == 0) {
//sens detection
sens_flag = 1;
nosleep = 1;
newmillis = millis();
interrupt_time = newmillis - oldmillis;
SLEEP_TIME_W = SLEEP_TIME_W - interrupt_time;
if (send(sensMsg.set(detection))) {
send_flag = 1;
}
}
if (digitalRead(SENS_PIN) == 1 && sens_flag == 1 && digitalRead(MODE_PIN) == 0) {
if (send_flag == 1) {
while (timer < 10) {
timer++;
digitalWrite(GREEN_LED, LOW);
wait(20);
digitalWrite(GREEN_LED, HIGH);
wait(30);
}
timer = 0;
} else {
while (timer < 10) {
timer++;
digitalWrite(RED_LED, LOW);
wait(20);
digitalWrite(RED_LED, HIGH);
wait(30);
}
timer = 0;
}
}
if (digitalRead(SENS_PIN) == 0 && sens_flag == 1 && digitalRead(MODE_PIN) == 0) {
sens_flag = 0;
nosleep = 0;
send_flag = 0;
digitalWrite(GREEN_LED, HIGH);
sleep(500);
lpComp_reset();
}
if (SLEEP_TIME_W < 60000) {
SLEEP_TIME_W = SLEEP_TIME;
sendBatteryStatus();
}
}
else {
//if (detection == -1) {
SLEEP_TIME_W = SLEEP_TIME;
sendBatteryStatus();
}
if (nosleep == 0) {
oldmillis = millis();
sleep(SLEEP_TIME_W);
}
}

void sendBatteryStatus() {
wait(20);
batteryVoltage = hwCPUVoltage();
wait(2);

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(2000, C_INTERNAL, I_BATTERY_LEVEL);
//send(powerMsg.set(batteryVoltage), 1);
//wait(2000, 1, V_VAR1);
}

void lpComp() {
NRF_LPCOMP->PSEL = IRT_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

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);
}
}

void lpComp_reset () {
s_lpComp();
detection = false;
NRF_LPCOMP->EVENTS_UP = 0;
r_lpComp();
}


MyBoardNRF5.cpp
#ifdef MYBOARDNRF5
#include <variant.h>

/*
* Pins descriptions. Attributes are ignored by arduino-nrf5 variant.
* Definition taken from Arduino Primo Core with ordered ports
*/
const PinDescription g_APinDescription[]=
{
{ NOT_A_PORT, 0, PIO_DIGITAL, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER}, // LFCLK
{ NOT_A_PORT, 1, PIO_DIGITAL, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER}, // LFCLK
{ PORT0, 2, PIO_DIGITAL, (PIN_ATTR_DIGITAL|PIN_ATTR_PWM), ADC_A0, PWM4, NOT_ON_TIMER},
{ PORT0, 3, PIO_DIGITAL, (PIN_ATTR_DIGITAL|PIN_ATTR_PWM), ADC_A1, PWM5, NOT_ON_TIMER},
{ PORT0, 4, PIO_DIGITAL, (PIN_ATTR_DIGITAL|PIN_ATTR_PWM), ADC_A2, PWM6, NOT_ON_TIMER},
{ PORT0, 5, PIO_DIGITAL, (PIN_ATTR_DIGITAL|PIN_ATTR_PWM), ADC_A3, PWM7, NOT_ON_TIMER},
{ PORT0, 6, PIO_DIGITAL, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER}, // INT3
{ PORT0, 7, PIO_DIGITAL, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER}, // INT4
{ PORT0, 8, PIO_DIGITAL, (PIN_ATTR_DIGITAL|PIN_ATTR_PWM), No_ADC_Channel, PWM10, NOT_ON_TIMER}, //USER_LED
{ PORT0, 9, PIO_DIGITAL, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER}, // NFC1
{ PORT0, 10, PIO_DIGITAL, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER}, // NFC2
{ PORT0, 11, PIO_DIGITAL, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER}, // TX
{ PORT0, 12, PIO_DIGITAL, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER}, // RX
{ PORT0, 13, PIO_DIGITAL, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER}, // SDA
{ PORT0, 14, PIO_DIGITAL, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER}, // SCL
{ PORT0, 15, PIO_DIGITAL, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER}, // SDA1
{ PORT0, 16, PIO_DIGITAL, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER}, // SCL1
{ PORT0, 17, PIO_DIGITAL, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER}, // TP4
{ PORT0, 18, PIO_DIGITAL, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER}, // TP5
{ PORT0, 19, PIO_DIGITAL, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER}, // INT2
{ PORT0, 20, PIO_DIGITAL, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER}, // INT1
{ PORT0, 21, PIO_DIGITAL, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER}, // INT1
{ PORT0, 22, PIO_DIGITAL, (PIN_ATTR_DIGITAL|PIN_ATTR_PWM), No_ADC_Channel, PWM9, NOT_ON_TIMER},
{ PORT0, 23, PIO_DIGITAL, (PIN_ATTR_DIGITAL|PIN_ATTR_PWM), No_ADC_Channel, PWM8, NOT_ON_TIMER},
{ PORT0, 24, PIO_DIGITAL, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER}, // INT
{ PORT0, 25, PIO_DIGITAL, (PIN_ATTR_DIGITAL|PIN_ATTR_PWM), No_ADC_Channel, PWM11, NOT_ON_TIMER}, //RED_LED
{ PORT0, 26, PIO_DIGITAL, (PIN_ATTR_DIGITAL|PIN_ATTR_PWM), No_ADC_Channel, PWM11, NOT_ON_TIMER}, //GREEN_LED
{ PORT0, 27, PIO_DIGITAL, (PIN_ATTR_DIGITAL|PIN_ATTR_PWM), No_ADC_Channel, PWM11, NOT_ON_TIMER}, //BLUE_LED
{ PORT0, 28, PIO_DIGITAL, (PIN_ATTR_DIGITAL|PIN_ATTR_PWM), ADC_A4, PWM3, NOT_ON_TIMER},
{ PORT0, 29, PIO_DIGITAL, (PIN_ATTR_DIGITAL|PIN_ATTR_PWM), ADC_A5, PWM2, NOT_ON_TIMER},
{ PORT0, 30, PIO_DIGITAL, (PIN_ATTR_DIGITAL|PIN_ATTR_PWM), ADC_A6, PWM1, NOT_ON_TIMER},
{ PORT0, 31, PIO_DIGITAL, (PIN_ATTR_DIGITAL|PIN_ATTR_PWM), ADC_A7, PWM0, NOT_ON_TIMER}
};

// Don't remove this line
#include <compat_pin_mapping.h>

#endif


MyBoardNRF5.h
#ifndef _MYBOARDNRF5_H_
#define _MYBOARDNRF5_H_

#ifdef __cplusplus
extern "C"
{
#endif // __cplusplus

// Number of pins defined in PinDescription array
#define PINS_COUNT (32u)
#define NUM_DIGITAL_PINS (32u)
#define NUM_ANALOG_INPUTS (8u)
#define NUM_ANALOG_OUTPUTS (8u)

/*
* LEDs
*
* This is optional
*
* With My Sensors, you can use
* hwPinMode() instead of pinMode()
* hwPinMode() allows to use advanced modes like OUTPUT_H0H1 to drive LEDs.
* https://github.com/mysensors/MySensors/blob/development/drivers/NRF5/nrf5_wiring_constants.h
*
*/
#define PIN_LED1 (16)
#define PIN_LED2 (15)
#define PIN_LED3 (17)
#define RED_LED (PIN_LED1)
#define GREEN_LED (PIN_LED2)
#define BLUE_LED (PIN_LED3)
#define INTERRUPT_PIN (5)
#define MODE_PIN (25)
#define SENS_PIN (27)

/*
* Analog ports
*
* If you change g_APinDescription, replace PIN_AIN0 with
* port numbers mapped by the g_APinDescription Array.
* You can add PIN_AIN0 to the g_APinDescription Array if
* you want provide analog ports MCU independed, you can add
* PIN_AIN0..PIN_AIN7 to your custom g_APinDescription Array
* defined in MyBoardNRF5.cpp
*/
static const uint8_t A0 = ADC_A0;
static const uint8_t A1 = ADC_A1;
static const uint8_t A2 = ADC_A2;
static const uint8_t A3 = ADC_A3;
static const uint8_t A4 = ADC_A4;
static const uint8_t A5 = ADC_A5;
static const uint8_t A6 = ADC_A6;
static const uint8_t A7 = ADC_A7;

/*
* Serial interfaces
*
* RX and TX are required.
* If you have no serial port, use unused pins
* CTS and RTS are optional.
*/
#define PIN_SERIAL_RX (11)
#define PIN_SERIAL_TX (12)

#ifdef __cplusplus
}
#endif

#endif



El interruptor tiene un botón táctil y un botón de reloj en la parte posterior del dispositivo. Este botón del reloj se usará para los modos de servicio, modo de captura de aire, puesta a cero del dispositivo. El botón implementa un hierro anti rebote. La línea del sensor capacitivo y la línea del botón del reloj a través de los diodos Schottky están conectadas y conectadas al pin analógico p0.05, las líneas a los pines p0.25 y p0.27 MK van al sensor capacitivo y al botón del reloj para leer los estados después de activar la interrupción en el pin p0. 05. En el pin p0.05, se activa una interrupción a través del comparador (NRF_LPCOMP) por EVENTS_UP. Obtuve inspiración para resolver el problema aquí y aquí .

El conmutador se agregó a la red Mysensors, administrado por el controlador de la casa inteligente, Magordomo ( sitio del proyecto )

Código PHP para agregar al método de cambio statusUpdate
 if (getGlobal("MysensorsButton01.status")==1) { if (getGlobal('MysensorsRelay04.status') == 0) { setGlobal('MysensorsRelay04.status', '1'); } else if (getGlobal('MysensorsRelay04.status') == 1) { setGlobal('MysensorsRelay04.status', '0'); } } 



Mira el resultado en el video





Más tarde, se hizo una opción con un convertidor de refuerzo, pero esto no está relacionado con el funcionamiento del chip capacitivo TTP223, existe un mayor deseo de una iluminación buena y uniforme al trabajar con los grifos durante toda la vida útil de la batería.

Vista





Proyecto Github : github.com/smartboxchannel/EFEKTA_WIRELESS_TOUCH_SWITCH

Sitio de la comunidad en ruso Mysensors

Telegram chat Mysensors : solución rápida a problemas con Mysensors, consejos, trucos, instalación de tableros, trabajo con microcontroladores atmega 328, stm32, nRF5 en un IDE Arduino - @mysensors_rus

Pocas fotos









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


All Articles