Hacemos un controlador para un hogar inteligente y no solo.
En un artículo anterior, describí el desarrollo del sistema como un todo. En esto, describiré el desarrollo de un controlador que es responsable de sondear sensores y módulos de E / S. "¿Por qué reinventar la rueda?" - usted pregunta En primer lugar, es interesante, y en segundo lugar, curiosamente, no existe una solución OpenSource para dicho controlador que cubra tanto el software como el hardware. El artículo está dirigido a personas que están un poco familiarizadas con la electrónica y el desarrollo integrado de Linux.
Hacer un controlador, dices, es muy complicado: necesitas hacer una pizarra, escribir software, imprimir el estuche. Pero en realidad, todo es un poco más complicado, eso es lo que me ha servido, pero en principio tienes razón:
1. hardware del controlador
- elección de la placa de la CPU para el controlador
- elección del controlador IO
- elección de la fuente de alimentación
- diagrama de bloques del controlador
- desarrollo de una tabla cruzada para el controlador
- desarrollo de placas para módulos RS-485
- producción de tableros
2. software para el controlador
- elección del sistema de compilación para Linux kernel y rootfs
- estructura de partición de la tarjeta SD
- elección del gestor de arranque y carga de los rootfs necesarios
- cambios en el árbol de dispositivos
- la elección de un sistema para cobrar débitos negociados
- escribir un sistema de construcción
- escribir un núcleo de comunicación
- escritura de puerta de enlace mqtt (puntos de controlador discreto / analógico -> temas mqtt)
- escribir un analizador de Google y construir un archivo de configuración json para la puerta de enlace
- escribir un monitor de puntos para acceder a los puntos del controlador
- montar el sistema de archivos de solo lectura
3. caja del controlador
- lo que debería ser, conectores, refrigeración, asientos para una tabla, hipotecas para clips para soportes en un dinrake.
- diseño e impresión
Algunas palabras sobre el hardware.
Probablemente solo los más desesperados ahora tomen un procesador, memoria, flash, controlador de energía, un par de cientos de componentes y comiencen a esculpirlo todo junto. El resto usa los frutos del trabajo de otras personas, es más rápido y más fácil. Solo necesita abrir un navegador y escribir "computadora de placa única" y pasar el resto del día eligiendo la correcta. Necesitaba muchos puertos serie y es deseable que la placa soporte de -40 ° C a + 85 ° C, por lo que la elección recayó en BeagleBone Black (BBB). También en BBB, todos los periféricos están conectados a dos conectores PBD de 46 pines en incrementos de 2.54, lo cual es conveniente para la creación de prototipos y el desarrollo de una placa cruzada. Se necesita una placa cruzada para combinar todos los componentes en una placa, para mí es una placa de CPU, fuente de alimentación, controlador IO y placas de canal RS485. Además, es la tabla cruzada la que debe fijarse a la carcasa y tiene conectores para alimentación y cable RS485.

Entonces, descubrimos la placa de la CPU, lo siguiente que debemos decidir es si es necesario colocar un controlador de Entrada / Salida (IO) en la placa cruzada o no. Lo puse en el tablero y todavía no lo he usado con éxito. Lo único que hace es posponer el inicio del BBB por 1s después de aplicar la energía y sirve el botón de reinicio.
La fuente de alimentación para el controlador, tomé el MeanWell NSD10-12S5 ya hecho, desarrollarlo para un solo dispositivo es una tarea sin sentido, simplemente lo recogí para el consumo y eso es todo. No preste atención a la pantalla LCD, está en el tablero, pero no implementé el soporte.


Algunas palabras sobre las tarjetas de canal RS485.
Hay 4 interfaces serie BBB en la placa cruzada. Así que allí puedes poner cualquier tipo de canal que necesites, RS485, CAN, módulo Zigbee ...
Necesitaba canales RS485, así que solo los hice, son con control automático de transmisión / recepción y con aislamiento galvánico. ¿Por qué no utilizar el control del transceptor con BBB, porque TI dejó de admitir oficialmente la luz estroboscópica para RS485 en el controlador del dispositivo en serie? Puede encontrar un parche para el controlador, puede agregarlo usted mismo, pero ¿por qué? Una vez que el canal se ha autobloqueado, puede colocarlo en cualquier placa, por ejemplo, en RaspberyPi, donde nunca ha habido tal soporte, si lo hubo, entonces corríjame. La luz estroboscópica para el controlador rs485 está configurada en attiny10, barata y alegre.
Regresamos al software.
Elegir un sistema de compilación para el kernel de Linux y rootfs.
Existen varios sistemas de este tipo, los más populares son Yocto y BuildRoot. Si necesita desarrollar un proyecto grande, si tiene mucho tiempo y desea escribir recetas, entonces Yocto es su elección. Con la ayuda de BuildRoot, puede recopilar todo lo que necesita para iniciar fácilmente el tablero es muy, muy simple, porque Estoy haciendo un sistema en Beaglebone Black (en adelante BBB) entonces:
- lea lo que está escrito aquí habr.com/en/post/448638
- hacer limpio
- hacer beaglebone_defconfig
- hacer
Eso es todo Ahora todo lo que necesita para ejecutar el tablero se encuentra en la carpeta / buildroot / output / images.
Todo parece muy simple y no interesante, por lo que puedes hacer un poco más complicado:
- integre buildroot en su sistema de compilación, descárguelo con un script, recuerde usar una etiqueta estable y no tome el último desarrollo
- escriba su defconfig y arroje el script en la carpeta / buildroot / configs antes de ensamblar buildroot, no olvide que todos los defconfigs deben terminar con * _defconfig, de lo contrario buildroot no lo verá
- copie su post-build.sh a bordo / beaglebone / post-build.sh
- prepara un script que haga n1, n2 y n3 por ti
Como resultado, buildroot generará zImage y rootfs.tar
Selección de la estructura de partición de la tarjeta SD:
En esto, creo, no es necesario centrar mucha atención.
Hice 4 secciones BOOT / ROOT_1 / ROOT_2 / DATA.
La sección BOOT contiene todo lo que necesita para el arranque: MLO, barebox.bin, barebox.env, am335x-boneblack.dtb, zImage, boot.txt.
ROOT_1 y ROOT_2 contienen rootfs, cuya selección se escribe en el archivo boot.txt (ver más abajo). Todas estas particiones se montan como de solo lectura para evitar bloqueos del sistema de archivos cuando se apaga. DATA contiene configuraciones de diseño, al cambiar las cuales no hay necesidad de reconstruir el código.
Tal estructura de particiones en el futuro facilitará la escritura de un componente de actualización de software. Este componente sobrescribirá una de las secciones ROOT_1 / ROOT_2, que no se usa ahora, y luego simplemente cambie el archivo boot.txt si no necesita cambiar el kernel.
Elegir un gestor de arranque.
Tuve muchos experimentos con cargadores de arranque para BBB. Al principio usé, como todos los demás, el U-Boot que BuildRoot genera. Pero no me gustó, tal vez, por supuesto, esto es una cuestión de costumbre, pero me pareció que era demasiado, es muy pesado y difícil de configurar. Entonces, pensé que no sería una mala idea iniciar el sistema rápidamente, en 2-3 segundos, y archivar el X-Loader para que cargara el kernel, lo logré, pero nuevamente hubo un problema de configuración y el tiempo de inicio para mí no es crítico (el sistema en systemd arranca lentamente por sí mismo, incluso si elimina todo lo que no es necesario).
Al final, me decidí por barebox, realmente me gustó su simplicidad, además el sitio tiene toda la documentación (www.barebox.org).
Por ejemplo, para cargar rootfs desde la primera o segunda partición, solo necesita:
1. en la sección de arranque, cree el archivo boot.txt que exportará una variable del tipo "export BOOT_NUM = X"
2. cree dos scripts / env / boot / sdb1 / env / boot / sdb2 en los que describa las opciones de arranque, por ejemplo:
echo "botting with mmcblk0p2 as rootfs..." global.bootm.image=/boot/zImage global.bootm.oftree=/boot/am335x-boneblack.dtb global.linux.bootargs.console="console=ttyO0,115200" global.linux.bootargs.debug="earlyprintk ignore_loglevel" global.linux.bootargs.base="root=/dev/mmcblk0p2 ro rootfstype=ext4 rootwait"
3. Cree un script / env / boot / sd en el que, dependiendo de BOOT_NUM, inicie el script sdb1 o sdb2
4. establecer la variable boot.default
nv boot.default=sd saveenv
5. Cambiando aún más BOOT_NUM en boot.txt cargaremos rootfs desde la primera o segunda partición, que en el futuro se puede usar para actualizar el software.
Cambios en el árbol de dispositivos.
Como uso MODBUS RTU a través de RS485 para comunicarme con los módulos, necesitaba habilitar casi todos los puertos seriales que existen en el BBB. Para hacer esto, debe volver a habilitarlos en el árbol de dispositivos, porque Por defecto, la mayoría de ellos están desactivados.
Sería correcto hacer su parche para el archivo am335x-bone-common.dtsi del paquete buildrut y aplicarlo cada vez antes de ensamblarlo, pero la pereza ganó, y simplemente saqué todos los archivos que necesitaba, cambié todo lo que necesitaba y lo construí con mis manos.
Porque Esto se hace una vez, es posible y así:
1. Cree una carpeta con los archivos necesarios para el ensamblaje:
am335x-bone-common.dtsi am335x-boneblack-common.dtsi am335x-boneblack.dts am33xx-clocks.dtsi am33xx.dtsi am33xx.h gpio.h omap.h tps65217.dtsi
2. En el archivo am335x-bone-common.dtsi, debe configurar correctamente los pines y deshabilitar los controladores de puerto:
uart1_pins: pinmux_uart1_pins { pinctrl-single,pins = < AM33XX_IOPAD(0x980, PIN_INPUT_PULLUP | MUX_MODE0) AM33XX_IOPAD(0x984, PIN_OUTPUT_PULLDOWN | MUX_MODE0) >; }; uart2_pins: pinmux_uart2_pins { pinctrl-single,pins = < AM33XX_IOPAD(0x950, PIN_INPUT_PULLUP | MUX_MODE1) AM33XX_IOPAD(0x954, PIN_OUTPUT_PULLDOWN | MUX_MODE1) >; }; uart4_pins: pinmux_uart4_pins { pinctrl-single,pins = < AM33XX_IOPAD(0x870, PIN_INPUT_PULLUP | MUX_MODE6) AM33XX_IOPAD(0x874, PIN_OUTPUT_PULLDOWN | MUX_MODE6) >; }; uart5_pins: pinmux_uart5_pins { pinctrl-single,pins = < AM33XX_IOPAD(0x8C4, PIN_INPUT_PULLUP | MUX_MODE4) AM33XX_IOPAD(0x8C0, PIN_OUTPUT_PULLDOWN | MUX_MODE4) >; }; &uart1 { pinctrl-names = "default"; pinctrl-0 = <&uart1_pins>; status = "okay"; }; &uart2 { pinctrl-names = "default"; pinctrl-0 = <&uart2_pins>; status = "okay"; }; &uart4 { pinctrl-names = "default"; pinctrl-0 = <&uart4_pins>; status = "okay"; }; &uart5 { pinctrl-names = "default"; pinctrl-0 = <&uart5_pins>; status = "okay"; };
3. Luego, un poco de magia, y el archivo terminado am335x-boneblack.dtb se encuentra en el mismo directorio:
a. sudo apt-get install device-tree-compiler
b. ejecuta el preprocesador:
cpp -Wp,-MD,am335x-boneblack.dtb.d.pre.tmp -nostdinc -Iinclude -Isrc -Itestcase-data -undef -D__DTS__ -x assembler-with-cpp -o am335x-boneblack.dtb.dts.tmp am335x-boneblack.dts
c. ejecuta el compilador en sí:
dtc -O dtb -o am335x-boneblack.dtb -b 0 -i src -d am335x-boneblack.dtb.d.dtc.tmp am335x-boneblack.dtb.dts.tmp
4. am335x-boneblack.dtb debe colocarse en la partición de arranque junto al kernel y en el script de inicio de barebox agregue la siguiente línea: "
global.bootm.oftree=/boot/am335x-boneblack.dtb
"
Elegir un sistema para cobrar débitos negociados.
Como saben, los sistemas sin errores no existen, así como el análisis de un sistema multiproceso sin rastros. Es muy conveniente si estos rastros no se muestran simplemente en la consola, sino que se recopilan utilizando algo especialmente creado para esto, de modo que sea posible ordenarlos por procesos, aplicar filtros, etc. Y solo conozco un buen sistema que es fácil de construir tanto en host como en destino. Esto es DLT, si nunca ha oído hablar de esto, entonces no importa, todas las brechas de conocimiento se pueden cubrir fácilmente al leer
at.projects.genivi.org/wiki/display/PROJ/Diagnostic+Log+and+Trace .
Este sistema consta de dlt-daemon y dlt-viewer. Como su nombre lo indica, dlt-daemon se ejecuta en el destino y dlt-viewer en el host. Además de todo esto, a su binario, desde el que queremos recopilar rastros, debe vincular el dlt lib.

En general, todo es conveniente, cómo recolectar rastros y analizarlos, lo recomiendo.
Escribir un sistema de construcción.
¿Por qué escribir un sistema de compilación? Porque puedes descargar todo de los repositorios, compilarlo con tus manos, construir sobre la base de este rootfs y velo, el controlador funciona. Pero repetir ese truco en un mes será más difícil, y en dos, esto generalmente es imposible. Nuevamente, debe recordar qué, dónde colocar, qué construir y cómo comenzar. Por lo tanto, después de pasar mucho tiempo al principio, lo guarda más tarde, además tiene la oportunidad de construir convenientemente en host y target. El sistema de compilación consiste en un conjunto de scripts que primero preparan al host para la compilación, descargan componentes de terceros, como buildroot, mosquitto, DLT daemon, desde sus repositorios, los compilan y los colocan en sus lugares. Y luego puede iniciar la compilación de su proyecto. Si la compilación bajo el host no es difícil de hacer, entonces siempre debe jugar con la compilación bajo el objetivo, y sería mejor si el script lo hace.
Buildroot se puede configurar de modo que invoque un script posterior a la compilación después de formar rootfs, que se ubicará en buildroot / output / target. Esto le brinda una gran oportunidad para poner todo lo que necesita allí. Y luego, la imagen del sistema de archivos ya contendrá todo lo que necesita para iniciar su sistema.
La receta es algo como esto:
- necesita copiar sus archivos binarios en algún lugar de buildroot / output / target, por ejemplo en / opt / bin
- si hay configuraciones, haga lo mismo con ellas, solo en / opt / etc.
- copiar binarios de terceros, para mí es mosquitto, DLT daemon, sus bibliotecas y configuraciones
- Para iniciar el sistema en sí mismo al cargar el controlador, debe copiar los servicios de systemd, es mejor combinarlos en su destino y volver a habilitarlo haciendo un enlace simbólico en multiusuario.
- copia el fstab modificado (porque, te lo diré más tarde)
Después de eso, solo necesita descomprimir buildroot / output / images / rootfs.tar en la sección deseada de la tarjeta SD y encender la alimentación.
build git repo: https://github.com/azhigaylo/build
Escribir un núcleo de comunicación.
El concepto de esto es tan antiguo como el propio Modbus.
Cada dispositivo de E / S en una red Modbus tiene registros (16 bits) disponibles para lectura, lectura / escritura, en los que se almacenan los datos y a través de los cuales se controlan estos dispositivos. El controlador, a su vez, tiene conjuntos de puntos discretos (estado y valor de byte) y analógicos (estado y valor flotante), en los que almacena el estado de todos los parámetros.
Por lo tanto, la tarea del núcleo de comunicación es simple: recopilar datos de dispositivos de E / S utilizando el protocolo Modbus, asignarlos a puntos de controlador y proporcionar acceso a estos puntos para el nivel superior. Y si necesita administrar algo, entonces todo está en la otra dirección: el dispositivo lógico (más sobre eso más adelante) debe suscribirse al punto del controlador y escribir en este punto inicia la traducción de este parámetro al dispositivo físico de salida de agua.

Para estructurar de alguna manera los datos y trabajar con dispositivos, puede introducir el concepto de un dispositivo lógico que mostrará el estado de un dispositivo físico en su software.
También decidí dividir los dispositivos lógicos en dos grupos:
- Estándar (módulos Aries de entrada / salida discreta), para los cuales se conocen de antemano los números de registros modbus con datos, y es suficiente solo para determinar los puntos del controlador donde guardar estos datos.
- Dispositivos de usuario, para ellos es necesario describir independientemente el mapeo de los registros modbus a los puntos del controlador.
De todo lo anterior, es lógico tener algún tipo de configurador para el controlador, ya sea solo una configuración json o una herramienta autoescrita que genere una configuración binaria, todo vale. Tengo la segunda opción, porque había ideas para escribir un núcleo de comunicación para que pudiera ejecutarse fácilmente no solo en la placa de Linux sino también en Arduin con FreeRtos, cambiando el nivel de PAL en el software.
En el configurador para cada dispositivo, debe configurar el número de puerto del controlador rs485, la dirección del dispositivo y el punto del controlador en el que se muestra el estado de comunicación con el dispositivo, además de para cada dispositivo estándar se describen sus canales, y para un dispositivo de usuario, sus registros se asignan a puntos.


Dicho archivo de configuración, que contiene todos los datos necesarios sobre la construcción de la red Modbus, le permite no modificar el código fuente del proyecto si necesita agregar / eliminar / cambiar dispositivos de entrada / salida, es suficiente cambiar los parámetros en el configurador y guardarlos en el archivo de configuración.
Al inicio, el núcleo de comunicación analiza la configuración y crea en base a listas de dispositivos lógicos para cada puerto rs485 del controlador, luego se crean subprocesos en cada puerto y comienza un sondeo cíclico de dispositivos físicos.
core git repo: https://github.com/azhigaylo/homebrain_core
Escribir mqtt gateway.
En realidad, sus puntos de controlador, tanto discretos como analógicos, con una interfaz patentada para acceder a ellos, son de poco interés para cualquiera. Entonces, solo hay una salida: mqtt. Creo que no exageraré si digo que este es actualmente el protocolo más común para intercambiar mensajes pequeños, además es muy simple y comprensible de usar. Entonces, cuando necesitaba transmitir datos desde el controlador, no pensé mucho en qué usar.

Porque Tengo muchos parámetros, siempre hubo confusiones en el archivo de configuración de la puerta de enlace, donde se registró la asignación de los puntos del controlador a los temas de la puerta de enlace mqtt. Google ayudó a la tabla y escribió un analizador csv de esta tabla en el archivo de configuración json para la puerta de enlace.
gateway git repoanalizador git repoPunto de escritura monitor.
A veces es muy útil ver qué sucede con los puntos del controlador, para esto escribí una pequeña aplicación que se conecta directamente al núcleo de comunicación y lee el estado de los puntos discretos y analógicos. Soy bastante estricto con la interfaz de usuario, así que pude de alguna manera lanzar la aplicación a QML, funcionó con un chirrido, puedes contar el punto, puedes grabarlo, pero no necesito más.
pointmonitor git repo: https://github.com/azhigaylo/pointmonitor
Montar el sistema de archivos de solo lectura.
Por lo general, pocas personas prestan atención a esto, e incluso en proyectos de producción, puede encontrar dispositivos en los que se puede escribir la partición con rootfs. Esto tarde o temprano conduce a la caída de cualquiera, incluso el sistema de archivos más estable. Porque Dado que el controlador se puede apagar en cualquier momento, solo es cuestión de tiempo / caso cuando esto sucede. Para minimizar esta probabilidad, debe jugar un poco con fstab, y antes de construir la imagen rootfs, colóquela allí, como se describió anteriormente. En fstab, en primer lugar, debe montar el sistema de archivos como de solo lectura y, en segundo lugar, todo lo que puede cambiar puede asignarse a tmpfs.
Mi fstab es esto, puede ser diferente para ti:
/dev/root / auto ro 0 1 tmpfs /tmp tmpfs nodev,nosuid,size=50M 0 0 tmpfs /srv tmpfs nodev,size=50M 0 0 tmpfs /var/log tmpfs defaults,noatime,size=50M 0 0 tmpfs /var/tmp tmpfs defaults,noatime,size=50M 0 0 tmpfs /var/run tmpfs defaults,noatime,size=50M 0 0 tmpfs /var/lib tmpfs defaults,noatime,size=10M 0 0
Cuerpo del controlador
Durante mucho tiempo se ha incluido una impresora 3D en las secciones de masthead para cada ingeniero agricultor colectivo, desafortunadamente no la tengo, pero está funcionando. Recientemente, la emoción de otros empleados por él ha desaparecido, lo uso cuando imprimo todo lo que necesito y no necesito, podría estar convencido de esto al leer mi publicación anterior.
Dibujamos en FreeCAD, generamos el gcode en Cura y obtenemos un estuche, sin olvidar hacer asientos para el tablero, recortes para conectores y refrigeración e hipotecas para clips en un riel DIN.


Bueno, eso es todo, ahora tenemos una placa, software en una tarjeta SD y un estuche. Tomamos un archivo (no estoy bromeando) y conectamos todo, conectamos la alimentación, los cables RS485 y todo comienza a funcionar. Y dijiste difícil, difícil ...