Panel de control centralizado para fuentes de iluminación Rotor TsPKIO-2D

imagen

Lo siento, no me he divertido mucho con los nombres durante mucho tiempo, así como con los periféricos para la automatización del hogar. Específicamente, esta cosa, el control remoto de la luz, resultó porque quería algo con una interfaz de pulsar, girar y hacer clic, y no con una dispersión habitual de botones. El efecto wow no se ha logrado: en casa no noto el control remoto a quemarropa, pero al menos cerré la gestalt.

Breve TOR:

1) Gestión de tres grupos de iluminación en la cocina.
2) Gestión de tres grupos de iluminación en la sala.
3) Gestión de todas las fuentes de luz al mismo tiempo.
4) Duración razonable de la batería (de una semana)
5) Compatible con la codificación Livolo, SC2260, EV1527

Por lo tanto, no necesita leer más si no le gustan los interruptores Arduino, Livolo y los enchufes de radio chinos. Porque el primero es la base del control remoto, y el segundo y el tercero son la periferia.

Concepto


La lógica de control me pareció la siguiente:

  1. Al presionar la “perilla” se cambian las zonas del grupo de iluminación en un anillo (cocina - habitación - todo).
  2. Al girar la perilla, dependiendo del sentido de rotación, se enciende o apaga la iluminación del grupo seleccionado.
  3. El modo de funcionamiento (grupo seleccionado) se muestra mediante una discreta indicación LED.

Como uso el control de radio de acuerdo con la opción más despreciada, sin protección contra interferencias y retroalimentación, al mismo tiempo se proporciona un pequeño truco en caso de una operación perdida.

Si girar la perilla no conduce al resultado deseado, la combinación de presionar y girar en la dirección opuesta le permite omitir el comando. Entonces el comando puede repetirse como de costumbre.

Es decir, si gire la perilla en el sentido de las agujas del reloj y la luz principal no se enciende, entonces puedo presionar la perilla, girarla en sentido antihorario y luego soltarla y girarla en el sentido de las agujas del reloj nuevamente para repetir el encendido.


¿Por qué tan difícil? Entonces, además de los protocolos incómodos, también tengo periféricos incómodos. Por ejemplo, los interruptores de luz controlados por radio Livolo y los relés de radio, que tienen el mismo comando para encender y apagar, junto con los enchufes de radio normales, en los que los comandos para encender y apagar están separados.

El truco de omisión del equipo le permite vencer creativamente la no inclusión (no exclusión) sin romper el esquema general de iluminación. Además, omitir un comando le permite saltar sobre fuentes de luz que no necesita encender o apagar.

Bueno, por supuesto, para comprender lo que está sucediendo con el control remoto, tiene un indicador separado que se ilumina al enviar un comando.

Si la consola no se toca durante algún tiempo (configurada en el código), el controlador se pone en suspensión. Sin embargo, no guarda el último estado, y cuando se despierta presionando la pluma, comienza la vida desde cero.

Esto no es un error. Repito, tengo interruptores sin retroalimentación, y la consola es físicamente incapaz de obtener información sobre el estado actual de cada dispositivo periférico controlado.

Por lo tanto, inmediatamente después de despertarse, el giro de la perilla enciende o apaga la luz desde cero.

Primer acercamiento


El concepto visual del tipo "caja con un giro" requería, como puede suponer, dos cosas: cajas y giros. En la primera versión, el papel de la caja lo desempeñaba un banco de energía delgado, cuyo uso resolvió dos problemas a la vez: tenía un estuche y un esquema de carga de la batería, además con un conector. La batería en sí, por supuesto, tuvo que ser reemplazada por una más compacta, de lo contrario el relleno ya no podría caber.

imagen

Resultó más complejo con el giro. Mientras buscaba, descubrí que cuanto más bonita es la perilla del potenciómetro y cuanto más grande es, más cercano es el costo de su gramo al costo de un gramo de oro. Por lo tanto, adquirí un bolígrafo, que me convenía mínimamente por sus propiedades estéticas.

imagen

La parte de control fue el resultado de un experimento con ATmega328P y una continuación lógica de la historia establecida por la automatización del hogar existente (en los mismos protocolos de radio Arduino y primitivos).

No compré muy caro una dispersión de los controladores mencionados y prototipos condicionalmente (de hecho, un adaptador de una caja pequeña a un escalón grande) con el objetivo de tratar de hacer una versión de bajo presupuesto de Arduino con un número mínimo (pero razonable) de elementos.

imagen

imagen

imagen

El experimento resultó exitoso, y el controlador configurado para el entorno Arduino mostró el LED con bastante éxito después de tragarse el clásico Blink. Bueno, entonces, en el principio de "terminar el búho", agregué un codificador (con un botón), tres LED y un transmisor normal con modulación de amplitud en una portadora de 433.92 MHz a la placa resultante.

Para colocar todos los elementos en un caso pequeño, tuve que sufrir un poco, pero el control remoto aún funcionaba. Y aunque, al parecer, el problema está resuelto, quería más: el caso original.

imagen

imagen

Segundo enfoque


En realidad, la primera versión (quejándose) en apariencia de un grupo de camaradas se hizo añicos, así que la pospuse indefinidamente. Pero no lo desarmó: es una pena.

Pero cuando apareció la impresora 3D, se prometió un día hacer el mismo estuche original y así cerrar la pregunta con el control remoto.

No sé si resultó bueno o malo. Realmente no sé cómo evaluar mis cosas. Pero en 3DToday el equipo es más acogedor que en MySKU (de lo que no me quejo, no es un regalo para mí), y calificaron el caso más alto que yo.

imagen

Pero teniendo total libertad de acción, abandoné las baterías chinas frágiles y arrastradas, y tomé el viejo 18650 como fuente de energía. Y, como puede ver fácilmente, son precisamente sus dimensiones las que determinan en gran medida las dimensiones de todo el caso.

Comencé a hacer la carcasa modular, que consta de muchas partes, lo que me permitió reimprimir solo elementos individuales (erróneos o no muy óptimos), y no todo el producto.

Otro punto es que realmente no me gusta hacer recortes para los conectores, lo que realmente no puedo hacer. Por lo tanto, en la cadena de suministro hay otro truco conocido por Evelyn the hedgehog : la carga inalámbrica.



En mi zashashnik yacía otro receptor, que inmediatamente puse en acción.


Finalmente, el último truco es bastante obvio, pero aún así: para que el control remoto no se arrastre alrededor de la mesa, pegué un pedazo de una alfombrilla antideslizante en la parte inferior. Y al final, esto es un monolito absoluto, aunque reorganizarlo a otro lugar tampoco es un problema.

Lo que se necesita para repetir


Pieza de hierro


1) Controlador ATmega328P - 1 pc. (en mi paquete TQFP, pero cualquiera puede)
2) resistencia de 10 kOhm - 5 piezas (4 para suprimir el rebote del codificador, 1 para el controlador)
3) Resistencia de 100 ohmios - 3 piezas
4) Condensadores de cerámica 0.1 microfaradios - 4 piezas. (al controlador y supresión del rebote del codificador)
5) Empuje codificador (valcoder) - 1 pc. (Tengo PEC12-4220F-S0024 )
6) LED - 3 piezas (diámetro 3 mm)
7) Tablero de carga de batería de litio - 1 pc. (desde el banco de energía que llegó a la mano, en teoría, cualquiera con encendido automático bajo carga lo hará)
8) Receptor de carga inalámbrico Qi - 1 pc.
9) Un transmisor con modulación de amplitud a 433 MHz - 1 pc. ( como este )
10) Alguna fibra de vidrio para la placa codificadora
11) impresora 3D
12) Plástico adecuado (imprimí PLA)
13) Tornillos M4x30 - 4 piezas

En general, se puede reducir el número de componentes. Por ejemplo, en una versión muy mínima, el controlador no requiere flejes, aunque decidí seguir el consejo de Nick Gammon y no perdoné un par de condensadores y una resistencia.

Del mismo modo, no puede molestarse con la supresión de hardware del rebote de contactos e intentar sobrevivir con el software. Luego puede tachar cuatro resistencias más y un par de condensadores.

Alternativamente, puede usar una placa Arduino preparada, como la Pro Mini, pero en este caso no puedo garantizar un bajo nivel de consumo de energía, y tendrá que conjurarlo usted mismo. Al mismo tiempo, el caso tendrá que ser corregido.

Esquema:



Como referencia, el pinout ATmega328p en el paquete TQFP-32 de Hobby Electronics :



Para mi codificador, dibujé un pequeño tablero:







Si es bueno, tendría que perforarlo para montar el codificador, o presionarlo con su "barriga a la placa (cuidando el aislamiento para que no haya cortocircuito) para que el codificador se monte a) más o menos uniformemente yb) no se tambalee. Históricamente, tengo una segunda opción.

Para el caso, es importante que la altura de la placa con piezas, excluyendo el codificador, no sea más (o no mucho más) de 5 mm.

Si la placa Arduino no está lista, entonces para que todo funcione, primero debe escribir el gestor de arranque Arduino en el controlador ATmega328P.
Para hacer esto, primero, agregue una descripción del controlador al entorno Arduino. Para hacer esto, vaya al sitio web oficial de Arduino y descargue el archivo de descripción adecuado para su versión del entorno ( para 1.6 , para 1.5 , para 1.0 ).

El contenido del archivo debe extraerse en la carpeta de hardware de la carpeta del entorno Arduino. En el futuro, describo lo que está sucediendo en el ejemplo del entorno 1.0.3, que todavía uso.

Cuando se copian las descripciones, debe iniciar Arduino y cargar el boceto del programador en Arduino, que se utilizará como este mismo programador. El boceto se encuentra en el menú Archivo - Ejemplos - ArduinoISP.

imagen

Por supuesto, debe elegir su placa y puerto. Elijo Mega, porque lo tengo:

imagen

Después de cargar el boceto del programador, debe cambiar al tablero de destino. Es decir en nuestro caso, ATmega328 con una frecuencia de 8 MHz y un oscilador maestro interno. Estará en la lista de tableros si las descripciones mencionadas anteriormente se copian correctamente:

imagen

Ahora necesita conectar las líneas MISO, MOSI y SCK de la placa del programador y la placa con el futuro Arduino, y también conectar RESET, GND y VCC. Además, el poder es el mejor en último lugar.

Basado en la infografía anterior y la descripción de Arduino Mega, emerge la siguiente imagen:

SPI - Arduino Mega - ATmega328p

MISO - 50 - 16
MOSI - 51 - 15
SCK - 52-17
SS (RESET) - 53 - 29

Conexión física a su gusto, utilicé un método exclusivamente bárbaro: cables de tablero ordinarios directamente en los orificios del tablero, sin soldadura ni aislamiento:

imagen

Si todo está listo, escriba el gestor de arranque. Primero, asegúrese de seleccionar el programador correcto (Servicio - Programador - Arduino como ISP):

imagen

Luego hacemos el Servicio - Grabar el gestor de arranque:

imagen

Después de eso, la salida es una placa Arduino minimalista, para cargar bocetos en los que puede usar el adaptador serie USB o una placa Arduino completa con dicho adaptador a bordo. En el primer caso, debe conectar RX y TX en forma transversal, y no olvide conectar una tierra común. En el segundo caso, también es necesario acortar el RESET Arduino, que se usa como adaptador, a tierra.

Si, como yo, no tiene un circuito para reiniciar automáticamente el controlador antes de descargar el boceto, entonces hay dos opciones: ya sea reiniciarlo o simplemente encenderlo cuando el entorno Arduino dice que la descarga ha comenzado.

Vivienda


El caso, como dije, es modular. Esto significa que en la parte interna oculta a los ojos, puede dejar entrar el plástico que tiene rancio y ya no es adecuado. Puedes ponerle electrónica:

imagen

Llamo la atención sobre el hecho de que el caso es específico y está diseñado para adaptarse a mi versión del relleno.

Propongo que el rotor sea transparente para que disperse la luz de los indicadores. Para mayor peso, se puede insertar una tuerca M16 dentro del rotor:

imagen

Todavía necesito una camisa de rotor y una cubierta. La tapa se inserta simplemente hacia adentro y descansa sobre la fricción. Y, por supuesto, no puede prescindir de las partes superior e inferior de la carcasa exterior.

Imprimí un rotor con un relleno del 10%, el resto de los elementos con un relleno del 5%. Plástico - PLA. La temperatura establecida de la boquilla en mi impresora es 200C en las primeras tres capas, 185C en la siguiente. Desafortunadamente, no puedo decir cuál es la temperatura real de la boquilla. La mesa está fría.

El montaje es simple.



Los tableros se colocan en las ranuras de la caja duradera, los LED - patas en las ranuras de la parte inferior de la caja duradera. La antena del transmisor se baja, el receptor de carga inalámbrico se baja de la misma manera, por lo que está más cerca de esta misma carga.



El relleno se fija mediante una placa intermedia, en cuya ranura pasa el arnés de cables del codificador.

El codificador se fija con la placa superior, todos juntos se aprietan con tornillos M4x30, que cortan las roscas en plástico.

Ahora la carcasa duradera se puede encerrar en mitades de la carcasa exterior. Se coloca un rotor en el eje del codificador y se coloca una camisa en el rotor. Opcionalmente, una alfombrilla antideslizante se pega al fondo del cuerpo. Otra opción es un inserto decorativo que oculta la costura entre las mitades del cuerpo.

Código
En el código, debe especificar comandos para encender y apagar sus dispositivos periféricos. Opcional: cambie el tiempo de espera de apagado automático.

Todo esto se encuentra en la sección variable.

//  : http://donalmorrissey.blogspot.ru/2010/04/sleeping-arduino-part-5-wake-up-via.html //  Livolo: http://forum.arduino.cc/index.php?action=dlattach;topic=153525.0;attach=108106 #include <avr/sleep.h> #include <avr/power.h> #include <livolo.h> #define adc_disable() (ADCSRA &= ~(1<<ADEN)) // disable ADC (before power-off) #define adc_enable() (ADCSRA |= (1<<ADEN)) // re-enable ADC #define txPin 7 //   Livolo livolo(txPin); #define PULSESHORT 450 #define PULSELONG 1350 #define PULSESYNC 13950 #define encA 5 #define encB 6 //   #define buttonPin 2 //   () #define roomLed 10 //    #define kitchenLed 9 //    #define switchLed 3 //     #define switchLedTimeOut 150 //      #define switchTreshold 4 //      #define offDelay 15000 //   #define rLev 3 //     #define kLev 3 //     #define glev 2 //     #define txPowerPin 8 //    #define kitchenBackLightOn1 12 #define kitchenBackLightOn2 34 #define kitchenMainLightOn 56 #define roomBackLightOn 12 #define roomMainLightOn1 34 #define roomMainLightOn2 56 #define mainLightOn 12 #define kitchenBackLightOff1 12 #define kitchenBackLightOff2 34 #define kitchenMainLightOff 56 #define roomBackLightOff 12 #define roomMainLighOtff1 34 #define roomMainLightOff2 56 #define mainLightOff 12 #define LivoloID 8500 volatile byte rotorMode = 0; //   byte currentMode = 0; //    int curEncA, prevEncA, curButton, prevButton; //   ,  byte encCountPlus = 0; //   byte encCountMinus = 0; //   unsigned long offTimeOut = 0; //    unsigned long modeTimeOut = 0; //        unsigned long switchLedTime = 0; //      unsigned long modeTime = 0; //   unsigned int modeTreshold = 500; //     (  ) unsigned int bounceTreshold = 200; //     byte rLevState = 0; byte kLevState = 0; byte gLevState = 0; //   k - , r - , h - , b -  boolean kBackState = false; boolean kBackState1 = false; boolean kMainState = false; boolean rBackState = false; boolean rMainState = false; boolean rMainState1 = false; boolean hMainState = false; boolean bMainState = false; boolean afterSleep = false; //      boolean modeTimeOutStart = false; boolean switchLedOn = false; boolean allOn = false; //  " "    boolean allOff = false; //  " "    static void ookPulse(int on, int off) { digitalWrite(txPin, HIGH); delayMicroseconds(on); digitalWrite(txPin, LOW); delayMicroseconds(off); } static void rcSend(long remoteCode) { for (byte reSend = 0; reSend < 8; reSend++) { for(byte repeat=0; repeat<4; repeat++){ for (byte i = 24; i>0; i--) { // transmit remoteID byte txPulse=bitRead(remoteCode, i-1); // read bits from remote ID // Serial.print(txPulse); switch (txPulse) { case 0: // 00 ookPulse(PULSESHORT,PULSELONG); //ookPulse(PULSESHORT,PULSELONG); break; case 1: // 11 ookPulse(PULSELONG,PULSESHORT); //ookPulse(PULSELONG,PULSESHORT); break; } // switch } // for loop ookPulse(PULSESHORT,PULSESYNC); // S(ync) // Serial.println(); } // repeat } delay(150); } void switchLedToggle() { digitalWrite(switchLed, HIGH); switchLedTime = millis(); switchLedOn = true; } void lightsUp(boolean lightsUpMode) { //       ""     //   "" -   if (afterSleep == true) { if (lightsUpMode == false) { gLevState = 1; rLevState = 3; kLevState = 3; } else {gLevState = 0; rLevState = 0; kLevState = 0; } afterSleep = false; //   " " } //   if (rotorMode == 2) { if (lightsUpMode == false){ if (allOff == false) { switchLedToggle(); if (digitalRead(buttonPin) == HIGH) { //   gLevState = 0; rLevState = 0; kLevState = 0; rcSend(kitchenBackLightOff1); rcSend(kitchenBackLightOff2); rcSend(roomBackLightOff); livolo.sendButton(LivoloID, mainLightOff); } allOff = true; allOn = false; } } if (lightsUpMode == true){ //  ,    if (allOn == false) { switchLedToggle(); if (digitalRead(buttonPin) == HIGH) { gLevState = 1; rLevState = 3; kLevState = 3; rcSend(kitchenBackLightOn1); rcSend(kitchenBackLightOn2); rcSend(roomBackLightOn); livolo.sendButton(LivoloID, mainLightOff); //    Livolo livolo.sendButton(LivoloID, kitchenMainLightOn); //#1   livolo.sendButton(LivoloID, roomMainLightOn1); // #2 livolo.sendButton(LivoloID, roomMainLightOn2); // #3 livolo.sendButton(LivoloID, mainLightOn); // #6 } allOn = true; allOff = false; } } } //  if (rotorMode == 1) { if (lightsUpMode == false && kLevState > 0) { switchLedToggle(); if (digitalRead(buttonPin) == HIGH) { if (kLevState == 3) { //   livolo.sendButton(LivoloID, kitchenMainLightOff); // #3 } if (kLevState == 2) { //   2 livolo.sendButton(LivoloID, kitchenBackLightOff2); // #6 } if (kLevState == 1) { //   1 rcSend(kitchenBackLightOff1); } } if (kLevState!=0) { kLevState--;} // Serial.println(kLevState); } if (lightsUpMode == true && kLevState < 3) { switchLedToggle(); kLevState++; if (digitalRead(buttonPin) == HIGH) { if (kLevState > 3) {kLevState = 3;} // Serial.println(kLevState); if (kLevState == 1) { //   1 rcSend(kitchenBackLightOn1); } if (kLevState == 2) { //   2 livolo.sendButton(LivoloID, kitchenBackLightOn2); // #6 } if (kLevState == 3) { //   livolo.sendButton(LivoloID, kitchenMainLightOn); // #3 } } } } //  if (rotorMode == 0) { if (lightsUpMode == false && rLevState > 0) { switchLedToggle(); if (digitalRead(buttonPin) == HIGH) { if (rLevState == 3) { //  1 livolo.sendButton(LivoloID, roomMainLighOtff1); //#1 } if (rLevState == 2) { //   livolo.sendButton(LivoloID, roomMainLightOff2); // #2 } if (rLevState == 1) { //   rcSend(roomBackLightOff); } } if (rLevState != 0) { rLevState--; } } if (lightsUpMode == true && rLevState < 3) { switchLedToggle(); rLevState++; if (digitalRead(buttonPin) == HIGH) { if (rLevState == 1) { //   rcSend(roomBackLightOn); } if (rLevState == 2) { //   livolo.sendButton(LivoloID, roomMainLightOn1); //#1 } if (rLevState == 3) { //   1 livolo.sendButton(LivoloID, roomMainLightOn2); // #2 } } } } } void wakeUp() { detachInterrupt(0); } void setMode() { if (rotorMode >= 2 ) { rotorMode = 0; } else { rotorMode++; } offTimeOut = millis(); } void ledBlink() { for (byte iLed = 0; iLed<3; iLed++) { digitalWrite(kitchenLed, HIGH); digitalWrite(roomLed, HIGH); delay(100); digitalWrite(kitchenLed, LOW); digitalWrite(roomLed, LOW); delay(100); } } void setLed() { if (rotorMode == 0) { //  digitalWrite(roomLed, HIGH); digitalWrite(kitchenLed, LOW); } if (rotorMode == 1) { //  digitalWrite(roomLed, LOW); digitalWrite(kitchenLed, HIGH); } if (rotorMode == 2) { //    digitalWrite(roomLed, HIGH); digitalWrite(kitchenLed, HIGH); } } void enterSleep() { // ledBlink(); afterSleep = true; digitalWrite(txPin, LOW); digitalWrite(txPowerPin, LOW); digitalWrite(roomLed, LOW); digitalWrite(kitchenLed, LOW); digitalWrite(switchLed, LOW); pinMode(txPin, INPUT); pinMode(txPowerPin, INPUT); pinMode(roomLed, INPUT); pinMode(kitchenLed, INPUT); pinMode(switchLed, INPUT); attachInterrupt(0, wakeUp, LOW); adc_disable(); set_sleep_mode(SLEEP_MODE_PWR_DOWN); sleep_enable(); sleep_mode(); sleep_disable(); power_all_enable(); pinMode(txPin, OUTPUT); pinMode(txPowerPin, OUTPUT); pinMode(roomLed, OUTPUT); pinMode(kitchenLed, OUTPUT); pinMode(switchLed, OUTPUT); digitalWrite(txPin, LOW); digitalWrite(txPowerPin, HIGH); // ledBlink(); setLed(); offTimeOut = millis(); allOn = false; allOff = false; } void setup() { // Serial.begin(115200); pinMode(txPin, OUTPUT); pinMode(txPowerPin, OUTPUT); pinMode(roomLed, OUTPUT); pinMode(kitchenLed, OUTPUT); pinMode(switchLed, OUTPUT); digitalWrite(txPin, LOW); digitalWrite(txPowerPin, HIGH); digitalWrite(roomLed, LOW); digitalWrite(kitchenLed, LOW); digitalWrite(switchLed, LOW); // pinMode(buttonPin, INPUT_PULLUP); pinMode(buttonPin, INPUT); pinMode(encA, INPUT); pinMode(encB, INPUT); prevEncA = digitalRead(encA); offTimeOut = millis(); rotorMode = 0; setLed(); prevButton = digitalRead(buttonPin); } void loop() { if ((millis() - offTimeOut) > offDelay) { enterSleep(); } else { //     if (switchLedOn == true) { if ((millis() - switchLedTime) > switchLedTimeOut) { digitalWrite(switchLed, LOW); switchLedOn = false; } } //       if (digitalRead(buttonPin) == LOW) { offTimeOut = millis(); } //   curButton = digitalRead(buttonPin); if ((prevButton == HIGH) && (curButton == LOW)) { if (modeTimeOutStart == false) { modeTimeOut = millis(); modeTimeOutStart = true; } } else { if (modeTimeOutStart == true) { modeTime = millis() - modeTimeOut; if ((modeTime < modeTreshold) && (modeTime > bounceTreshold)) { setMode(); modeTimeOutStart = false; prevButton = digitalRead(buttonPin); } else { modeTimeOutStart = false; prevButton = digitalRead(buttonPin); } } } //    if (currentMode != rotorMode) { //       ,     currentMode = rotorMode; //     setLed(); } //   curEncA = digitalRead(encA); if ((prevEncA == LOW) && (curEncA == HIGH)) { offTimeOut = millis(); if (digitalRead(encB) == LOW) { encCountMinus++; encCountPlus = 0; // Serial.println("Encoder Minus"); if (encCountMinus > switchTreshold) { encCountMinus = 0; lightsUp(false); } } else { encCountPlus++; encCountMinus = 0; // Serial.println("Encoder Plus"); if (encCountPlus > switchTreshold) { encCountPlus = 0; lightsUp(true); } } } prevEncA = curEncA; } } 


Modelo de caja por referencia .

Eso es todo.

PD: Traté de no olvidar nada, pero pude. Si es así, me disculpo y haré todo lo posible para responder correctamente las preguntas principales y corregir los errores.

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


All Articles