Ciclo de desarrollo completo del dispositivo IoT para el control de calefacción de piscinas en el ESP8266 en un entorno Arduino

En esta publicación, compartiré mi experiencia sobre la creación de un dispositivo IoT desde cero: desde el surgimiento de una idea y su implementación en hardware hasta la creación de firmware para un controlador y una interfaz web para administrar un dispositivo creado a través de Internet.


Antes de crear este dispositivo, yo:


  • Casi no entendía los circuitos. Solo a nivel de principios de trabajo
    resistencia / transistor ... No tenía experiencia en crear circuitos complicados.
  • Nunca diseñó placas de circuito.
  • Componente SMD nunca soldado. El nivel de soldador estaba en el nivel de cables de soldadura y algún tipo de relé.
  • Nunca he escrito programas tan complejos para un microcontrolador. Toda la experiencia fue en el nivel "enciende el LED en Arduino", y conocí por primera vez el controlador ESP8266.
  • Escribí bastante C ++ para el "hermano mayor", pero eso fue hace más de una docena de años y todo fue olvidado hace mucho tiempo.

Por supuesto, la experiencia de trabajar como programador (principalmente Microsoft .NET) y el pensamiento sistémico me ayudaron a comprender el tema. Creo que el lector podrá hacerlo. Enlaces y artículos útiles sobre el mar de Internet. Lo más, en mi opinión, interesante y ayudando a entender el tema, lo traigo con el artículo.


Declaración del problema.


Vivo en una casa privada cerca de Minsk, y mi propia piscina, aunque sea la más simple, es una parte integral del conjunto de "beneficios" que obtienen muchas personas que viven en una casa de campo. En nuestro clima inestable, resultó que nadar en la piscina es incómodo si está al aire libre: el agua se enfría por la noche y el clima ventoso durante el día no hace que nadar sea cómodo. El año pasado, con mis propias manos, construí una cúpula geodésica más llena sobre la piscina, puse una colina y colgué un bungee: los niños están felices.



Reportaje fotográfico de la construcción de la cúpula en Flickr.


Este año fui aún más lejos y decidí organizar un calentador de piscina desde una caldera de gas,
que sirve para calentar la casa en invierno y calentar agua caliente en verano.


En verano, el circuito de "calefacción" de la caldera con la ayuda de válvulas cambia a calefacción
piscina El agua de la piscina se calienta con la ayuda de un intercambiador de calor de titanio, cuyo circuito primario pasa el refrigerante (agua caliente sin impurezas) del circuito de calefacción y el agua secundaria de la piscina, bombeada por una bomba de recirculación del sistema de filtración. Como uso la piscina con un clorador ( en ForumHouse se describen muchos temas interesantes), el agua contiene un poco de sal y se necesita un intercambiador de calor de titanio. No puede simplemente tomar y dejar que el agua pase directamente por la caldera; de lo contrario, corroerá todas las tuberías con sal.



Al pasar por el intercambiador de calor, el portador de calor calentado por la caldera con una temperatura de aproximadamente 70-90 ° C emite calor al agua de la piscina, calentándola un par de grados. El refrigerante se enfría un par de decenas de grados y vuelve a la caldera para volver a estar
calentado La relación entre el enfriamiento del agua de la caldera y el calentamiento del agua de la piscina depende de muchos factores: la capacidad del intercambiador de calor y la velocidad de circulación del agua en los circuitos primario y secundario.


Las tuberías conectadas desde la piscina al intercambiador de calor son tuberías de polietileno ordinarias, aquellas que
Actualmente se utiliza para suministrar agua fría a hogares privados. Barato, la capacidad de soportar una presión decente, la ausencia de corrosión: estas son las principales ventajas de tales tuberías. Para todos, sin excepción, las tuberías de polietileno, la temperatura de funcionamiento está limitada a 40 grados centígrados. En principio, esto es más que suficiente para la piscina.


Sin embargo, existe una alta probabilidad de emergencia en caso de que la bomba
La recirculación de agua del agua de la piscina se detendrá por alguna razón, y la caldera continuará calentando el intercambiador de calor: en este caso, el agua en el circuito secundario del intercambiador de calor se elevará rápidamente a la temperatura del circuito primario, lo que significa que las secciones de tuberías de polietileno adyacentes al intercambiador de calor se derretirán y el agua de la piscina se inundará. Todo el espacio alrededor.


Debe ser posible proteger el sobrecalentamiento del intercambiador de calor.


Arreglo rápido


Para resolver este problema, se incluyó un sensor de flujo que funciona según el principio del efecto Hall en el circuito del circuito de recirculación de agua de la piscina. Además, sensores de temperatura ubicados en el circuito secundario
intercambiador de calor, proporciona un segundo nivel de defensa, rastreando el posible sobrecalentamiento.


Es imposible controlar el sobrecalentamiento solo con sensores de temperatura: el sistema tiene una gran inercia: después de una parada repentina de agua en el circuito de la piscina, a
apagando la caldera, la temperatura sigue subiendo por algún tiempo, ya que la caldera aún impulsa el agua calentada a lo largo del circuito por inercia, evitando el sobrecalentamiento de "yo, mi amado".


Por lo tanto, es importante responder lo antes posible: es decir, detener el flujo de agua en el circuito
piscina


Se usó un sensor de flujo tal . La carcasa de plástico y la falta de contacto del sensor con el agua permite su uso en agua salada.


Sensores de temperatura, se decidió utilizar el Dallas DS18B20, son fáciles de conectar varias piezas a la vez en un bus de 1 cable .



Se decidió colgar un par de sensores en la entrada y salida tanto de la secundaria como de la primaria.
circuito: total 4 sensores. Una ventaja adicional de este enfoque es
la capacidad de monitorear los parámetros del sistema: puede monitorear cuánto se enfría el refrigerante en el circuito primario y cuánta agua de la piscina se calienta en el circuito secundario. Entonces, para monitorear la optimización de la calefacción y predecir el tiempo de calefacción.


Ubicaciones de sensores en intercambiadores de calor y tuberías de entrada

Parámetros del dispositivo


El primer prototipo del dispositivo se construyó sobre la base de Arduino Uno y se lanzó con éxito.



Pero luego quedó claro que me gustaría más. Calentó 16 metros cúbicos de agua, incluso solo
unos pocos grados no es rápido. Y me gustaría monitorear directamente los parámetros de calefacción desde el trabajo, encenderlo / apagarlo. Pero al mismo tiempo, sería interesante tomar horarios de calefacción, por ejemplo, por día.


Bueno, dado que ya estamos obteniendo un dispositivo IoT, ¿por qué no controlamos al mismo tiempo la activación remota del clorador de la piscina y la bomba para ello?


Términos de referencia


Entonces, se decidió desarrollar un dispositivo: un controlador de piscina multifuncional. Debe ser capaz de:


  • Para controlar la calefacción de la piscina a través del intercambiador de calor, enciende / apaga la caldera de gas para calentar el agua.
  • Evite el sobrecalentamiento del intercambiador de calor controlando la presencia de un flujo de agua de la piscina en el circuito secundario y el exceso de temperatura del circuito secundario.
  • Muestra estadísticas de calefacción en tiempo real (temperatura en la entrada y salida de ambos circuitos).
  • Registre (registre) valores de temperatura en la memoria flash. Mostrar datos para
    cierto período en forma de gráfico.
  • Con un relé, puede encender / apagar las bombas de la piscina y el clorador.
  • Administre todos los parámetros del dispositivo de forma remota a través del servidor micro-web incorporado.

También existía la tentación de joder Blink, MQTT. Pero de estas "campanas y silbatos" en la primera etapa
Se decidió rechazar. Y aún más, no quisiera aprovechar la posibilidad de control en algún lugar externo. El servidor web incorporado para mis propósitos es suficiente. Y la seguridad está garantizada por el hecho de que puede ingresar a la red doméstica desde el mundo exterior solo a través de una VPN.


Hardware


Como controlador, se decidió utilizar el ESP8266 barato y popular. Fue perfecto para mis propósitos, excepto por una cosa: hacer coincidir los niveles de señal de los sensores de 5 voltios con la lógica del controlador de 3.3 voltios. En principio, los sensores de Dallas parecen funcionar a 3 voltios, pero tengo una línea bastante larga desde el controlador hasta los sensores, unos 7 metros. Por lo tanto, es mejor aumentar el voltaje.


Se determinó que es necesario tener el hardware:


  • Controlador ESP8266 o su hermano mayor ESP32 (como módulo DevKit ).
  • Alineación de niveles de señal para sensores.
  • El regulador de potencia es una parte de 5 voltios del circuito.
  • Módulo de control de relé.
  • Reloj RTC + memoria flash para el registro.
  • La pantalla LCD de 2 líneas más simple para mostrar los valores actuales de los sensores y el estado del dispositivo y el relé.
  • Varios botones físicos para controlar el estado del dispositivo sin acceso a través de la web.

Muchos componentes de la lista se venden como módulos para Arduino y muchos módulos son compatibles con la lógica 3.3v. Sin embargo, no quería "atascar" todo esto en la placa con paquetes de cables, porque quiero tener un "dispositivo" bonito y bonito. Sí, y por el dinero otorgado a los chinos por los módulos, puede dibujar y ordenar completamente su placa de circuito impreso individual, y la expectativa de su llegada será compensada por una instalación relativamente rápida y confiable.


Una vez más, noto que esta es mi primera experiencia en circuitos y en el diseño del hardware de tales cosas. Tuve que estudiar mucho. De hecho, en mi especialidad, soy un poco distante de los microcontroladores. Pero hacer todo "de rodillas" no permitió el espíritu de perfeccionismo que vive en mí.


Diagrama de circuito


Hay una gran cantidad de programas en el mercado que le permiten dibujar un circuito y una placa de circuito impreso. Sin experiencia en esta área, inmediatamente me gustó EasyEDA , un editor en línea gratuito que le permite pintar bellamente un diagrama de circuito, verificar que no se haya olvidado nada y que todos los componentes tengan conexiones, dibujar una placa de circuito impreso y luego ordenar inmediatamente su producción.


La primera dificultad que encontré: hay muchas opciones para el controlador DevKit ESP8266 o ESP32, algunas de ellas difieren en la ubicación de los pines y su propósito, y algunas incluso en ancho. Se decidió dibujar el circuito para que fuera posible colocar DevKit de cualquier ancho y con cualquier ubicación de los terminales, y a los lados del mismo: 2 filas de pares de agujeros de puente y, posteriormente, cableado para conectar los terminales necesarios, con respecto al controlador adquirido específicamente.


Coloque debajo del controlador y 2 filas de puentes emparejados: JH1 y JH2 en el diagrama:



La ubicación de los pines de entrada 5v y salida 3.3v de la fuente de alimentación del estabilizador incorporado, así como GND, me pareció lo mismo para diferentes DevKit, pero aún así decidí ir a lo seguro y también hacerlos puentes: JP1, JP2, JP3 en el diagrama.


Decidí firmar los puentes conectándolos a los componentes del circuito con funciones que probablemente realizarán.


Y así es como se ve con el DevKit ESP8266, que finalmente compré e instalé

Aquí D1 (GPIO5) y D2 (GPIO4) son responsables del bus I2C, D5 (GPIO14) para 1-Wire, D6 (GPIO12) - para recibir pulsos del sensor de flujo.


Diagrama del circuito:



(imagen en la que se puede hacer clic)


A pesar de la presencia a bordo del ESP8266 de un regulador de potencia incorporado para 3.3v, todavía necesitamos 5 voltios para alimentar los sensores y la pantalla LCD, y 12 voltios para alimentar el relé. Se decidió hacer que la placa tuviera una potencia de 12 voltios y colocar el regulador de voltaje AMS1117-5.0 en la entrada, dando los 5 voltios deseados en la salida.


Para igualar los niveles de señal en el bus de 1 cable, utilicé un transistor de efecto de campo BSS138 c con "pull-ups" de voltaje en ambos lados.



Muy bueno acerca de la coincidencia de niveles está escrito en el artículo Coincidencia de niveles lógicos de dispositivos 5V y 3.3V .


Para igualar los niveles de señal del sensor de flujo, simplemente utilicé un divisor de voltaje a través de las resistencias. El sensor de flujo es simplemente un dispositivo colector abierto . Algunos sensores ya pueden tener una resistencia pull-up incorporada, esto debe considerarse:



Azul en el diagrama es una designación esquemática del conjunto del sensor de flujo. A la derecha del conector hay divisores de voltaje seleccionados por mí para tener un nivel máximo de 3.3 voltios en la salida.


En el bus I2C, colgué un reloj DS3231SN en tiempo real y una memoria flash AT24C256C para almacenar registros. La memoria flash integrada en el ESP8266 no es adecuada, ya que tiene una pequeña cantidad de ciclos de reescritura (10 mil versus 1 millón para AT24Cxxx, según las hojas de datos).


El control de relé está organizado en un montón de chips PCF8574AT y ULN2803A.



El primer chip es un expansor de puerto de microcontrolador I2C. El estado de la salida o entrada activa PCF8574AT se selecciona seleccionando una dirección en el bus I2C.
El chip tiene algunas características interesantes, bien descritas en el artículo expansor de puertos I2C PCF8574 .


El chip no puede controlar directamente la carga (relé). Para esto, se utiliza una matriz de transistores ULN2803A. Hay una característica: la matriz puede extraer fácilmente sus salidas con una carga al suelo, lo que significa que si se aplica un voltaje de suministro al segundo polo del relé, la corriente fluirá a través del devanado del relé y los contactos del relé se cerrarán. Desafortunadamente, con esta inclusión, obtenemos un efecto secundario: el valor de la señal del controlador se invierte y todos los relés "hacen clic" cuando se enciende el circuito. Todavía no he descubierto cómo eliminar esta función.


Aquí se describe más información sobre el chip.


El expansor de puerto PCF8574AT también se puede usar como entrada: los botones de hardware se pueden colgar en algunas de las entradas, leyendo sus valores en el bus I2C. En el diagrama, los pines 4-7 se pueden usar para leer el estado de los botones. Lo principal es no olvidar habilitar mediante programación el ajuste incorporado de las patas correspondientes a la nutrición.


Al mismo tiempo, dejé el cableado a la matriz del transistor, en caso de que de repente desee conectar relés adicionales. Para posibles conexiones, traje todos los cables a los conectores (más precisamente, a los agujeros debajo de ellos donde se pueden soldar los cables o se puede soldar el conector DIP estándar de 2.54 mm).


El pin del expansor de puerto INT se puede usar para responder rápidamente a la presión de un botón. Se puede conectar a un puerto libre en el controlador y configurar el disparador de interrupción para cambiar el estado de este pin.


La pantalla LCD de dos líneas también se controla a través del expansor PCF8574AT. El punto principal: la pantalla está alimentada por 5 voltios, mientras que la pantalla está controlada por lógica de 3 voltios. Por cierto, los adaptadores Arduino estándar para I2C no están diseñados para doble voltaje. Encontré la idea de tal conexión en algún lugar de Internet, desafortunadamente, perdí el enlace, así que no cito la fuente.


Placa de circuito


Al diseñar el tablero, resultó que las partes comunes con patas ocupan demasiado espacio, y muchas fichas en el diseño DIP no son fáciles de encontrar. Después de leer en Internet que la instalación de SMD no es tan complicada, y con la habilidad adecuada, es aún menos lenta, decidí diseñar la placa para piezas SMD. Y no me equivoqué. Resultó una placa base compacta y hermosa, donde puse fácilmente todo lo que necesitaba. Las piezas SMD, con un buen soldador, fundente y soldadura, resultaron realmente muy fáciles de montar.


En el tablero, agregué algunos márgenes cuadrados de agujeros para la creación de prototipos, si de repente quiero soldar otra cosa.


Hice una placa de circuito impreso de 97x97 mm. Se adapta fácilmente a una caja eléctrica de corte estándar. Además, las tablas con tamaños inferiores a 100x100 son baratas de fabricar. La producción de un lote mínimo de 5 tableros de acuerdo con el diseño desarrollado costó 5 USD, su entrega a Bielorrusia costó otros 9 USD.



El diseño de la placa se encuentra en el sitio web de EasyEDA y está disponible para todos.


Observo que en la foto del controlador a continuación aparece la primera muestra de la placa, en la que "torcí" muchas cosas innecesarias e innecesarias (con la esperanza de usar este lote mínimo de 5 placas en otros proyectos). Aquí y en EasyEDA publiqué una versión "limpia" de todas estas cosas innecesarias.



Fotos de ambos lados del tablero

Lado delantero:



Reverso:



Parte de software


Para programar el microcontrolador, dada la acumulación en forma de prototipo en Arduino Uno, se decidió utilizar el entorno Arduino con el ESP8266 Arduino Core instalado. Sí, puedes usar Lua en el ESP8266, pero dicen que hay bloqueos. Yo, dada la función crítica realizada, no quisiera en absoluto.
El entorno Arduino en sí mismo me parece un poco anticuado, pero, afortunadamente, hay una extensión para Visual Studio de Visual Micro. El entorno le permite usar sugerencias de código IntelliSence, saltar rápidamente a las declaraciones de funciones, refactorizar el código: en general, todo lo que el entorno para las computadoras "adultas" se permite. La versión paga de Visual Micro también le permite depurar convenientemente el código, pero estaba contento con la opción gratuita.

Estructura del proyecto


El proyecto consta de los siguientes archivos:
Estructura del proyecto en Visual Studio

ArchivoCita
WaterpoolManager.ino
Declaración de variables básicas y constantes. Inicializacion. Bucle principal
HeaterMainLogic.ino
La lógica básica de controlar el relé de la caldera (según la temperatura) y los relés auxiliares.
Sensors.ino
Leer datos del sensor
Settings.ino
Configuración del dispositivo, guardándolos en la memoria flash del controlador
LCD.ino
Salida de información en LCD
ClockTimer.ino
Lectura de reloj RTC o simulación de reloj
Relés.ino
Control de encendido / apagado de relé
ButtonLogic.ino
Lógica de reacción al estado de los botones de hardware.
ReadButtonStates.ino
Leer estados del botón de hardware
EEPROM_Logging.ino
Registro de datos del sensor en EEPROM
WebServer.ino
Servidor web incorporado para administración de dispositivos y visualización de estado
Paginas web
Las páginas del servidor web se almacenan en esta carpeta.
index.h
La página principal para mostrar el estado del dispositivo. Lectura del estado actual con llamada ajax. Actualiza cada 5 segundos.
loggraph.h
Muestra un registro de datos del sensor y estados de relé en un gráfico. Se utiliza la biblioteca jqPlot: toda la construcción se realiza en el lado del cliente. La solicitud al controlador va solo a un archivo binario: copias de datos de la EEPROM.
logtable.h
también, pero en forma de tabla
ajustes.h
Gestión de la configuración del dispositivo: configuración de límites de temperatura, flujo de agua, frecuencia de registro de datos
hora.h
Ajuste de hora actual

Bibliotecas
EepromLogger.cpp
Biblioteca de registro de Flash
EepromLogger.h
crc8.cpp
8- CRC
crc8.h
TimeSpan.cpp

TimeSpan.h


OneWire tempSensAddr. . ( 4 ):


while (ds.search(tempSensAddr[lastSensorIndex]) && lastSensorIndex < 4) { Serial.print("ROM ="); for (byte i = 0; i < 8; i++) { Serial.print(' '); Serial.print(tempSensAddr[lastSensorIndex][i], HEX); } if (OneWire::crc8(tempSensAddr[lastSensorIndex], 7) != tempSensAddr[lastSensorIndex][7]) { Serial.print(" CRC is not valid!"); } else lastSensorIndex++; Serial.println(); } ds.reset_search(); lastSensorIndex--; Serial.print("\r\nTemperature sensor count: "); Serial.print(lastSensorIndex + 1, DEC);  ,       ().       Serial   LCD  : // Read sensor values and print temperatures ds.reset(); ds.write(0xCC, TEMP_SENSOR_POWER_MODE); // Request all sensors at the one time ds.write(0x44, TEMP_SENSOR_POWER_MODE); // Acquire temperatures delay(1000); // Delay is required by temp. sensors char tempString[10]; for (byte addr = 0; addr <= lastSensorIndex; addr++) { ds.reset(); ds.select(tempSensAddr[addr]); ds.write(0xBE, TEMP_SENSOR_POWER_MODE); // Read Scratchpad tempData[addr] = ds.read() | (ds.read() << 8); // Read first 2 bytes which carry temperature data int tempInCelsius = (tempData[addr] + 8) >> 4; // In celsius, with math rounding Serial.print(tempInCelsius, DEC); // Print temperature Serial.println(" C"); } 

Según la hoja de datos, los sensores requieren un retraso de al menos 750 ms entre solicitar un valor de temperatura y recibir una respuesta del sensor. Por lo tanto, el código introdujo un retraso con un pequeño margen.


Sin embargo, esta demora, cuando todo el dispositivo está esperando una respuesta, es aceptable al comienzo, pero es absolutamente inapropiado esperar cada vez con sondeos regulares de los sensores. Por lo tanto, se escribió el siguiente código complicado, llamado cada 50 ms por temporizador:


 #define TEMP_MEASURE_PERIOD 20 // Time of measuring, * TEMP_TIMER_PERIODICITY ms #define TEMP_TIMER_PERIODICITY 50 // Periodicity of timer calling, ms timer.attach_ms(TEMP_TIMER_PERIODICITY, tempReadTimer); int tempMeasureCycleCount = 0; void tempReadTimer() // Called many times in second, perform only one small operation per call { tempMeasureCycleCount++; if (tempMeasureCycleCount >= TEMP_MEASURE_PERIOD) { tempMeasureCycleCount = 0; // Start cycle again } if (tempMeasureCycleCount == 0) { ds.reset(); ds.write(0xCC, TEMP_SENSOR_POWER_MODE); // Request all sensors at the one time ds.write(0x44, TEMP_SENSOR_POWER_MODE); // Acquire temperatures } // Between phases above and below should be > 750 ms int addr = TEMP_MEASURE_PERIOD - tempMeasureCycleCount - 1; if (addr >= 0 && addr <= lastSensorIndex) { ds.reset(); ds.select(tempSensAddr[addr]); ds.write(0xBE, TEMP_SENSOR_POWER_MODE); // Read Scratchpad tempData[addr] = ds.read() | (ds.read() << 8); // Read first 2 bytes which carry temperature data } } 

Al comienzo de cada ciclo tempMeasureCycleCount, se solicita a los sensores que lean sus valores. Después de que pasan aproximadamente 50 ciclos de este tipo (y en total es 50 * 20 = 1000 ms = 1 segundo), se lee el valor de cada sensor, uno a la vez. Todo el trabajo se divide en pedazos para que el código que se ejecuta en la interrupción del temporizador no tome mucho tiempo del controlador.


El valor del sensor de flujo se calcula de la siguiente manera. Al interrumpir el pin en el que se cuelga el sensor, aumentamos el valor del contador de tics que provino del sensor de flujo:


 pinMode(FLOW_SENSOR_PIN, INPUT); attachInterrupt(digitalPinToInterrupt(FLOW_SENSOR_PIN), flow, RISING); // Setup Interrupt volatile int flow_frequency; // Flow sensor pulses int flowMeasureCycleCount = 0; void flow() // Flow sensor interrupt function { flow_frequency++; } 

En el mismo temporizador donde se sondean los sensores de temperatura, una vez por segundo tomamos este valor de marca y lo traducimos a litros usando la constante FLOW_SENSOR_CONST, cuyo valor se puede encontrar en las características del sensor:


 flowMeasureCycleCount++; if (flowMeasureCycleCount >= 1000 / TEMP_TIMER_PERIODICITY) { flowMeasureCycleCount = 0; litersInMinute = (flow_frequency / FLOW_SENSOR_CONST); // Pulse frequency (Hz) = FLOW_SENSOR_CONST*Q, Q is flow rate in L/min. flow_frequency = 0; // Reset Counter } 

Registro de datos de sensores y estado del dispositivo


Al desarrollar el mecanismo de registro, el hecho de que el dispositivo puede apagarse repentinamente, es decir, en casi cualquier momento Cuando deje de grabar, debemos poder restaurar todo lo grabado hasta el último momento. Al mismo tiempo, no podemos reescribir constantemente la misma área de memoria flash (por ejemplo, un título determinado en un lugar determinado, recordando la dirección donde se realizó la grabación por última vez), a fin de evitar el "borrado" acelerado de la unidad flash en este lugar.


Después de alguna "acumulación", se inventó e implementó el siguiente modelo de grabación:



Cada registro es un registro que contiene información sobre el valor actual del flujo de agua, las temperaturas del sensor, así como el estado del dispositivo codificado en el byte (los bits individuales indican si el relé está encendido o no, si el calentamiento está habilitado o no):


 struct LogEvent { unsigned char litersInMinute = 0; unsigned char tempCelsius[4]{ 0, 0, 0, 0 }; unsigned char deviceStatus = 0; } 

Después de cada registro, hay un byte de suma de verificación CRC , que indica si el registro se escribió correctamente y, en general, si al menos algo se escribió en esta ubicación de memoria.


Dado que sería demasiado costoso registrar datos en la hora actual ( marca de tiempo ) para cada registro en términos de volumen, los datos se organizan en bloques grandes, con N registros en cada uno. La marca de tiempo para cada bloque se registra solo una vez, para el resto, se calcula en función de la información sobre la frecuencia de registro.


 unsigned int logRecordsInBlock = 60 * 60 / loggingPeriodSeconds; // 1 block for hour unsigned int block_size = sizeof(Block_Header) + logRecordsInBlock * (record_size + crcSize); unsigned int block_count = total_storage_size / block_size; 

Por ejemplo, con una frecuencia de registro de una vez cada 30 segundos, tendremos 120 entradas en un bloque, y el tamaño del bloque será de aproximadamente 840 bytes. En total, podemos colocar 39 bloques en la memoria de una unidad flash de 32 kilobytes de tamaño. Con tal organización, resulta que cada bloque comienza en una dirección estrictamente definida en la memoria, y "atravesar" todos los bloques no es un problema.


En consecuencia, con un salto repentino en el registro durante el último apagado del dispositivo, tendremos un bloqueo inacabado (es decir, en el que faltan algunos de los registros). Cuando se enciende el dispositivo, el algoritmo busca el último encabezado de bloque válido (marca de tiempo + crc). Y continúa grabando, comenzando con el siguiente bloque. La grabación se realiza cíclicamente: el último bloque sobrescribe los datos del bloque más antiguo.


Al leer, todos los bloques se leen secuencialmente. Los bloques no válidos (aquellos que no pasan CRC para la marca de tiempo) se ignoran por completo. Los registros en cada bloque se leen hasta la reunión del primer registro no válido (es decir, aquel en el que se cortó la grabación la última vez si el bloque no se grabó por completo). El resto son ignorados.
Para cada registro, el tiempo actual se calcula en función de la marca de tiempo del bloque y el número de serie del registro en el bloque.


LCD


El dispositivo utiliza una pantalla QC1602A, capaz de mostrar 2 líneas de 16 caracteres. La primera línea muestra la información actual sobre los valores actuales de los sensores: flujo y temperaturas. Si se excede el límite especificado, aparece un signo de exclamación cerca del valor. La segunda línea muestra el estado del relé de calefacción y la bomba, así como el tiempo transcurrido desde que se encendió o apagó la calefacción. Cada 5 segundos, la pantalla en la segunda línea muestra brevemente los límites actuales. Las fotos de la pantalla en varios modos se muestran al final de la publicación.


Gráficos


Cuando se solicita a través del servidor web incorporado, los datos de registro se leen en forma binaria usando JavaScript:


 var xhttp = new XMLHttpRequest(); xhttp.open("GET", "logs.bin", true); xhttp.responseType = "arraybuffer"; xhttp.onprogress = updateProgress; xhttp.onload = function (oEvent) { var arrayBuffer = xhttp.response; if (arrayBuffer) { var byteArray = new Uint8Array(arrayBuffer); … }}; xhttp.send(null); 

Leerlos en algún formato no binario popular, como ajax, sería un lujo inadmisible para el controlador, principalmente debido a la gran cantidad que tendría que devolver el servidor http incorporado.


Por la misma razón, la biblioteca JavaScript jqPlot se usa para construir gráficos, y los archivos de la biblioteca JS se cargan desde CDN populares.


Un ejemplo de la programación del dispositivo:



Se ve claramente que aproximadamente a las 9:35 el dispositivo se encendió para calentar, la caldera comenzó a calentar gradualmente el circuito de calefacción (sensores T3, T4), después de lo cual la temperatura del circuito de la piscina comenzó a aumentar (sensores T1, T2). Alrededor de las 10:20, la caldera pasó a calentar agua caliente en la casa, la temperatura del circuito de calefacción bajó. Luego, después de otros 10 minutos, la caldera volvió a calentar el agua de la piscina. A las 10:50 ocurrió un accidente: la bomba de circulación de agua en la piscina se apagó de repente. El flujo de agua cayó bruscamente a cero, el relé de calentamiento se apagó (línea roja punteada en la segunda tabla), evitando el sobrecalentamiento. Pero el dispositivo aún permaneció en estado de calentamiento (línea roja en el segundo gráfico). Es decir Si la bomba se volviera a encender y las temperaturas fueran normales, el dispositivo volvería a calentarse. Observo que después de un apagado de emergencia de la bomba, las temperaturas en el circuito de agua de la piscina (T1, T2) comenzaron a aumentar bruscamente debido al sobrecalentamiento del intercambiador de calor. Y si no fuera por un apagado brusco de la caldera, habría problemas.


Servidor web incorporado


Para comunicarse con el mundo exterior, se utiliza la clase estándar ESP8266WebServer . Cuando se inicia el dispositivo, se inicializa como un punto de acceso con la contraseña predeterminada especificada en #define AP_PASS. Se abre automáticamente una página web para seleccionar una red wi-fi disponible e ingresar una contraseña. Después de ingresar la contraseña, el dispositivo se reinicia y se conecta al punto de acceso especificado.


Dispositivo terminado


El dispositivo terminado se colocó en una caja de corte estándar para el cableado. Se recortó un agujero para la pantalla LCD y agujeros para los conectores.



Fotos de la fachada del dispositivo en diferentes modos.

Con la visualización del tiempo transcurrido después de encender:



Con los límites mostrados:



Conclusión


En conclusión, quiero decir que, al desarrollar dicho dispositivo, obtuve una gran experiencia con circuitos, diseño de PCB, habilidades de instalación para componentes SMD, en la arquitectura y programación de microcontroladores, recordé C ++ casi olvidado y un manejo cuidadoso de la memoria y otros recursos limitados del controlador. El conocimiento de HTML5, JavaScript y las habilidades de depuración de los scripts en el navegador también fueron útiles hasta cierto punto.


Estas habilidades y el placer recibido durante el desarrollo del dispositivo son los principales beneficios obtenidos. Y los códigos fuente del dispositivo, el diagrama del circuito, las placas de circuito impreso, por favor use, modifique. Todos los códigos fuente del proyecto están en GitHab. Hardware en un proyecto público en EasyEDA. Recopilé datos en los chips utilizados en el proyecto en una unidad de red .

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


All Articles