为智能家居打造控制器

我们不仅为智能家居制造控制器。

在上一篇文章中,我描述了整个系统的开发。 在本文中,我将描述负责轮询传感器和I / O模块的控制器的开发。 “为什么要重新发明轮子?” -你问。 首先,这很有趣,其次,很奇怪,对于这种同时涵盖软件和硬件的控制器,还没有开放源代码解决方案。 本文针对那些精通电子学和嵌入式Linux开发的人们。

您说,制造控制器是如此复杂-您需要制造电路板,编写软件,打印机箱。 但实际上,一切都有些复杂,这就是它对我造成的影响,但是原则上您是对的:

1.控制器硬件

-选择控制器的CPU板
-IO控制器的选择
-电源选择
-控制器的框图
-开发控制器的交叉板
-开发用于RS-485模块的板
-生产板

2.控制器软件

-选择用于Linux内核和rootfs的构建系统
-SD卡的分区结构
-选择引导程序并加载必要的rootfs
-设备树中的更改
-选择收取交易的系统
-编写构建系统
-编写沟通核心
-编写mqtt网关(离散/模拟控制器点-> mqtt主题)
-编写Google解析器并为网关构建json配置文件
-编写一个点监视器以访问控制器点
-挂载只读文件系统

3.控制器箱

-应该是什么,连接器,冷却装置,电路板的座椅,抵押恐龙的支架的夹子。
-设计和印刷

关于硬件的几句话。

现在也许只有最绝望的人需要一个单独的处理器,内存,闪存,电源控制器,几百个组件,并开始将它们雕刻在一起。 其余的则利用其他人的劳动成果,更快,更容易。 您只需要打开浏览器并编写“单板计算机”,然后剩下的时间就选择合适的浏览器。 我需要大量的串行端口,并且该主板支持-40°C至+ 85°C是可取的,因此选择范围应为BeagleBone Black(BBB)。 同样在BBB上,所有外设都以2.54的增量连接到两个46针的PBD连接器,这方便了原型设计和开发交叉板。 需要一块交叉板将所有组件组合在一块板上,对我来说,这是一块cpu板,电源,IO控制器和RS485通道板。 另外,需要将横板固定到外壳上,并且板上有用于电源和RS485电缆的连接器。



因此,我们找出了CPU板,接下来要决定的是是否有必要在交叉板上放置输入/输出(IO)控制器。 我将其放置在板上,但尚未成功使用。 他唯一要做的就是在通电后将BBB的启动推迟1秒钟,然后按下复位按钮。

控制器的电源是由我准备的现成的MeanWell NSD10-12S5,为单个设备开发它是没有意义的,我只是拿起它来消费。 不要注意LCD,它在板上,但是我没有实现支持。





关于RS485通道卡的几句话。

交叉板上有4个串行BBB接口。 因此,您可以在其中放置所需的任何类型的通道,RS485,CAN,Zigbee模块...

我需要RS485通道,所以我只制作了它们,它们具有自动发送/接收控制和电流隔离功能。 为什么不对BBB使用收发器控制,因为TI正式停止了在串行设备驱动程序中支持RS485的选通脉冲。 您可以找到驱动程序的补丁,也可以自己添加,但是为什么呢? 使通道自锁后,可以将其放在任何板上,例如RaspberyPi,那里从来没有这样的支持,如果有的话,请纠正我。 rs485驱动程序的选通脉冲配置在attiny10上,价格便宜又开朗。

我们回到软件。

为linux内核和rootfs选择一个构建系统。

有几种此类系统,其中最受欢迎的是Yocto和BuildRoot。 如果您需要开发一个大型项目,如果您有很多时间并且希望编写食谱,那么Yocto是您的选择。 借助BuildRoot,您可以收集轻松启动开发板所需的一切,这非常非常简单,因为 然后,我在Beaglebone Black(以下称BBB)上制作一个系统:

  1. 阅读此处写的内容habr.com/en/post/448638
  2. 弄干净
  3. 使beaglebone_defconfig
  4. 使

仅此而已。 现在,运行该板所需的所有文件都位于/ buildroot / output / images文件夹中。

一切看起来都很简单,也没有意思,因此您可以做一些复杂的事情:

  1. 将buildroot集成到构建系统中,使用脚本下载它,记住要使用稳定的标签,而不要进行最后的开发
  2. 在组装buildroot之前,先编写defconfig并将脚本放在/ buildroot / configs文件夹中,不要忘记所有defconfig必须以* _defconfig结尾,否则buildroot看不到它
  3. 将您的post-build.sh复制到board / beaglebone / post-build.sh
  4. 准备一个将为您执行n1,n2和n3的脚本

结果,buildroot将生成zImage和rootfs.tar

选择SD卡的分区结构:

我认为,没有必要对此给予太多关注。
我做了4个部分BOOT / ROOT_1 / ROOT_2 / DATA。
BOOT部分包含引导所需的所有内容:MLO,barebox.bin,beadbox.env,am335x-boneblack.dtb,zImage,boot.txt。

ROOT_1和ROOT_2包含rootfs,其选择内容写在boot.txt文件中(请参见下文)。 所有这些分区都以只读方式安装,以避免在关闭电源时文件系统崩溃。 DATA包含设计配置,更改时无需重新构建代码。

将来,这种分区结构将使编写软件更新组件变得容易。 该组件将覆盖ROOT_1 / ROOT_2部分(现在不使用)之一,然后,如果不需要更改内核,只需更改boot.txt文件。

选择一个引导程序。

我对BBB的引导程序进行了很多实验。 最初,我像其他所有人一样,使用了BuildRoot生成的U-Boot。 但是我不喜欢它,也许这当然是一个习惯问题,但是在我看来,它太多了,非常沉重且难以配置。 然后,我认为在2-3秒内快速启动系统并归档X-Loader以便加载内核是一个不错的主意,我成功了,但是再次出现了配置问题,以及启动时间无关紧要(即使您删除了不需要的所有内容,systemd上的系统也会自动缓慢启动)。

最后,我选择了光秃秃的盒子,我真的很喜欢它的简单性,另外,该站点还提供了所有文档(www.barebox.org)。

例如,要从第一个或第二个分区加载rootfs,您只需要:

1.在启动部分,将文件boot.txt导出,将导出类型为“ export BOOT_NUM = X”的变量

2.创建两个脚本/ env / boot / sdb1 / env / boot / sdb2,在其中描述启动选项,例如:

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.创建一个脚本/ env / boot / sd,其中,根据BOOT_NUM,启动sdb1或sdb2脚本

4.设置变量boot.default

 nv boot.default=sd saveenv 

5.进一步更改boot.txt中的BOOT_NUM,我们将从第一个或第二个分区加载rootfs,将来可将其用于软件更新。

更改到设备树。

由于我通过RS485使用MODBUS RTU与模块进行通信,因此我需要启用BBB上几乎所有的串行端口。 为此,您需要在设备树中重新启用它们,因为 默认情况下,其中大多数处于关闭状态。

从buildrut软件包中为am335x-bone-common.dtsi文件制作补丁并在每次组装之前都应用它是正确的,但是懒惰赢得了胜利,我只是取出了我需要的所有文件,更改了我需要的所有东西,然后用手将其组装起来。

因为 只需完成一次,就有可能,所以:

1.创建一个文件夹,其中包含组装所需的文件:

 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.在am335x-bone-common.dtsi文件中,您需要正确配置引脚并取消启用端口驱动程序:

 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.接下来,一点魔术,完成的文件am335x-boneblack.dtb位于同一目录中:

 a. sudo apt-get install device-tree-compiler 

b。 运行预处理器:

 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。 运行编译器本身:

 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放在内核旁边的启动分区上,并在裸机启动脚本中添加以下行-“ global.bootm.oftree=/boot/am335x-boneblack.dtb

选择收集交易借方的系统。

如您所知,不存在没有错误的系统,也没有对没有跟踪的多线程系统的分析。 如果这些痕迹不是简单地显示在控制台中,而是使用为此专门创建的某种东西收集的,则非常方便,这样就可以按进程对它们进行排序,应用过滤器等。 我只知道一个易于在宿主和目标下构建的良好系统。 这是DLT,如果您从未听说过,那么没关系,可以通过阅读project.genivi.org/wiki/display/PROJ/Diagnostic+Log+and+Trace轻松弥补所有知识差距。
该系统由dlt-daemon和dlt-viewer组成。 顾名思义,dlt守护程序在目标上运行,而dlt-viewer在主机上运行。 除此之外,对于要从中收集跟踪信息的二进制文件,还需要链接dlt lib。



通常,我建议一切都很方便,如何收集痕迹并进行分析。

编写构建系统。

为什么要编写一个构建系统,因为您可以从存储库下载所有内容,可以手动构建它,并且可以在此rootfs和veil的基础上构建,所以控制器可以工作。 但是,要在一个月内重复一次这样的技巧将更加困难,而在两个月内重复一次-通常是不可能的。 同样,您必须记住什么,放置在哪里,构建什么以及如何开始。 因此,起初花了很多时间,之后又保存了,再加上有机会方便地在宿主和目标下进行构建。 该构建系统由一组脚本组成,这些脚本首先为构建做准备,然后从其存储库中下载第三方组件(例如buildroot,mosquitto,DLT守护程序),进行构建,然后将其放置到位。 然后,您可以启动项目的构建。 如果在主机下进行构建并不困难,那么您始终需要修改目标下的构建,如果脚本执行此操作会更好。

可以配置Buildroot,以便它在形成rootfs后调用构建后脚本,该脚本将位于buildroot / output / target中。 这为您提供了一个很好的机会,将您需要的所有东西都放在这里。 然后,文件系统映像将已经包含启动系统所需的一切。

食谱是这样的:

  1. 您需要将二进制文件复制到buildroot / output / target的某个位置,例如/ opt / bin
  2. 如果有配置,则仅在/ opt / etc中对它们执行相同的操作
  3. 复制第三方二进制文件,对我来说是mosquitto,DLT守护程序,它们的库和配置
  4. 为了在加载控制器时启动系统本身,您需要复制systemd服务,最好将它们组合到目标中并通过在多用户中建立符号链接来重新启用它。
  5. 复制修改后的fstab(为什么,我稍后再告诉您)

之后,您只需要将buildroot / output / images / rootfs.tar解压缩到SD卡的所需部分,然后打开电源即可。

 build git repo: https://github.com/azhigaylo/build 

编写通讯核心。

这个概念与modbus本身一样古老。

Modbus网络中的每个I / O设备都有(16位)寄存器,可用于读取,读取/写入,存储数据以及通过这些寄存器控制这些设备。 控制器又具有离散(状态和字节值)和模拟点(状态和浮点值)的数组,在其中存储所有参数的状态。

因此,通信核心的任务很简单-使用Modbus协议从I / O设备收集数据,将它们映射到控制器点,并为上层提供对这些点的访问。 而且,如果您需要管理某些东西,那么一切都将朝着另一个方向发展-必须将逻辑设备(稍后再介绍)预订到控制器点,并且写入该点将启动该参数到物理水输出设备的转换。



为了以某种方式结构化数据并使用设备,您可以引入逻辑设备的概念,该逻辑设备将在软件中显示物理设备的状态。

我还决定将逻辑设备分为两组:

  1. 标准(离散量输入/输出的Aries模块),预先知道带有数据的Modbus寄存器的数量,仅确定控制器点的位置就足以保存该数据。
  2. 用户设备,对于他们来说,有必要独立描述modbus寄存器到控制器点的映射。

综上所述,为控制器配备某种配置器是合乎逻辑的,无论是json配置还是生成二进制配置的自写工具,任何适合的配置。 我有第二种选择,因为有人提出了编写通信核心的想法,以便使它不仅可以在Linux板上轻松运行,而且可以在带有FreeRtos的Arduin上轻松运行,从而改变了软件中的PAL级别。

在每个设备的配置器中,您需要设置控制器端口号rs485,设备地址以及显示与设备通信状态的控制器点,此外还需要为每个标准设备描述其通道,并为用户设备将其寄存器映射到点。





这样的配置文件包含有关Modbus网络构建的所有必要数据,如果您需要添加/删除/更改输入/输出设备,则无需修改项目的源代码,只需在配置器中更改参数并将其保存在配置文件中即可。

在启动时,通信核心将解析配置并在其基础上为每个控制器端口rs485创建逻辑设备列表,然后为每个端口创建线程,并开始对物理设备进行循环轮询。

 core git repo: https://github.com/azhigaylo/homebrain_core 

编写mqtt网关。

实际上-您的控制器点,包括离散点和模拟点,以及用于访问它们的专有接口,对任何人都不感兴趣。 因此,只有一种出路-mqtt。 如果我说这是目前交换小消息的最常用协议,我想也不会夸大其词,而且使用起来非常简单易懂。 因此,当我需要从控制器传输数据时,我并没有考虑太多。



因为 我有很多参数,所以网关配置文件中总是存在混乱,在该文件中注册了控制器指向mqtt网关主题的映射。 Google帮助了该表,并在网关的json配置文件中编写了该表的csv解析器。



网关git repo
解析器git repo

书写点监视器。

有时,查看控制器点的情况非常有用,为此,我编写了一个小型应用程序,该应用程序直接连接到通信核心并读取离散点和模拟点的状态。 我对UI的要求非常严格,因此我能够以某种方式将应用程序放入QML,它的工作非常缓慢,您可以算出要点,可以编写它,但是我不需要更多。

 pointmonitor git repo: https://github.com/azhigaylo/pointmonitor 

挂载只读文件系统。

通常,很少有人注意这一点,即使在生产项目中,您也可以找到可写有rootfs分区的设备。 迟早会导致任何甚至最稳定的文件系统崩溃。 因为 由于可以随时关闭控制器,因此这只是时间/情况的问题。 为了最大程度地降低这种可能性,您需要对fstab进行一些修改,然后在构建rootfs映像之前,将其放在此处,如上所述。 在fstab中,首先,您需要以只读方式挂载文件系统,其次,所有可以更改的内容都可以映射到tmpfs中。

我的fstab是这个,对您来说可能有所不同:

 /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 

控制器主体

早在标题栏中就为每个集体农民工程师配备了3D打印机,很遗憾,我没有它,但是它正在工作。 最近,其他员工对他的兴奋消失了,在打印我需要和不需要的所有东西时,都会用到它,您可以通过阅读我的上一篇文章使您确信这一点。

我们使用FreeCAD绘图,在Cura中生成gcode,然后得到一个案例,而不必忘记为电路板放置座椅,为连接器和散热装置留空并在DIN导轨上固定夹子。





好了,仅此而已,现在我们有了一块板子,SD卡上的软件和一个保护套。 我们拿了一个文件(我不是在开玩笑),然后将所有东西连接在一起,连接电源,RS485电缆,一切开始工作。 你说困难,困难...

Source: https://habr.com/ru/post/zh-CN462259/


All Articles