Xbee、树莓派和 Arduino 传感器网络编程(一)
原文:Beginning Sensor Networks with XBee, Raspberry Pi, and Arduino
一、传感器网络简介
传感器网络不再是昂贵的工业建筑。您可以从容易获得的低成本硬件构建简单的传感器网络。你所需要的只是一些简单的传感器和一个微控制器或具有输入/输出能力的计算机。是的,您的 Arduino 和 Raspberry Pi 是构建传感器网络的理想平台。如果您使用过这两个平台中的任何一个,并且曾经想要监控您的花园池塘、跟踪您家里或办公室的活动、监控您家里的温度、监控环境,或者甚至构建一个低成本的安全系统,那么您已经成功了一半!
虽然听起来很诱人也很简单,但不要马上开始加热烙铁。关于传感器网络,你需要了解很多东西。这并不像把东西插在一起然后打开它们那么简单。如果你想建立一个可靠和信息丰富的传感器网络,你需要知道这样的网络是如何构建的。
另外,你可能听说过一个叫物联网(IoT)的东西。这个短语指的是可以通过网络(本地或互联网)进行通信的设备的使用。因此,物联网设备是网络感知设备,可以向其他资源发送数据,从而虚拟化设备对用户及其体验的影响。传感器网络在物联网中扮演着重要角色。您将在本书中学到的知识将为使用传感器网络构建物联网解决方案奠定坚实的基础。
如果您想从总体上了解更多关于物联网的知识,已经有几本书以此为主题,包括以下内容。如果您有兴趣了解更多有关物联网和传感器网络使用方式的信息,请查看以下书籍:
-
charalappos Doukas(CreateSpace 独立出版平台,2012 年)用 Arduino 构建物联网
-
由 Dieter Uckelmann、Mark Harrison 和 Florian Michahelles (Springer,2011 年)共同构建物联网
-
物联网入门:将传感器和微控制器连接到云作者:库诺·菲斯特(2011 年,奥赖利)
在本章中,我们将通过简要描述什么是传感器网络以及它们是如何构建的来探索传感器网络。我们还将研究构成传感器网络的组件,包括传感器概述、可用的传感器类型以及它们可以感知的事物。
传感器网络的剖析
传感器网络无处不在。它们通常被认为是制造和医疗应用的复杂监控系统。然而,它们并不总是复杂的,它们就在你的周围。
在本节中,我们将研究传感器网络的构建模块以及它们是如何连接的(逻辑上)。首先,让我们来看一些传感器网络的例子,以便直观地了解这些组件。
传感器网络的例子
虽然您可能不太熟悉其中的一些示例,但是在阅读这些示例时,尝试想象一下应用程序的组件是个不错的主意。可视化传感器本身——它们被放置在哪里,它们可能正在读取哪些数据,并将这些数据发送到网络的另一部分进行处理和记录。
汽车的
几乎每辆现代汽车都有一个复杂的传感器网络,用来监控发动机及其子系统的性能。一些汽车有额外的传感器,用于监控外部气温、轮胎压力,甚至物体和其他车辆的接近度。新型汽车有一系列安全机制,包括车道偏离、避障、自动刹车等等。 1
如果你把一辆新型汽车送去维修,并有机会在车库里看看,你可能会注意到几台类似电脑终端、平板电脑或在某些情况下类似 iPad 的机器。这些系统是诊断机器,旨在连接到您的汽车,并读取传感器和计算机存储的所有数据。一些制造商使用称为车载诊断(OBD)的行业标准接口。 2 这个接口及其协议有多个版本;大多数经销商都有支持所有最新协议的设备。
然而,一些制造商使用他们自己的专有诊断系统,但许多制造商使用与 OBD-II 相同的连接器。你可能想在买车前问一下这个问题。如果您的新车需要专有的电子工具进行维护,您可能需要将它送到合格的机械师或其他经销商处进行维修。对于那些生活在农村地区的人来说,找到一家经销商甚至是一名训练有素的机械师来修理你的车可能需要一些旅行,因此需要提前计划。
例如,保时捷使用了它所谓的保时捷综合车间信息系统(PIWIS)。虽然 PIWIS 使用与 OBD-II 相同的连接器,但 Porsche 实施了专有系统来读取和更改数据。只有经过培训(并购买了专用工具)的机械师才能维修车辆。
有趣的是,虽然使用专有诊断系统的制造商要求您在授权经销商处维修您的汽车,但一些有进取心的技术专家已经创建了兼容系统。以保时捷为例,Durametric ( www.durametric.com/default.aspx )制造了一系列产品,这些产品能够实现基本的维护功能,如故障和维修提醒重置,甚至为许多保时捷车型提供高级故障排除功能。图 1-1 显示了从保时捷 Cayman 上读取传感器数据的 Durametric 软件的一个屏幕。
图 1-1
来自 Durametric 的保时捷诊断数据
请注意显示的详细程度。该图显示了跟踪中的三个指标,但是如果您看屏幕的顶部,您会看到更多可以监控的指标。图表中显示的数据是通过保时捷采用的复杂传感器网络实时收集和显示的。
传感器在汽车中的应用已经开始扩展到相关的机械,如摩托车、船,甚至古老的农用拖拉机。许多现代农业机械(如联合收割机)都配有先进的传感器,可实现自动割台高度、自动驾驶等惊人功能。
例如,现代联合收割机可以与一套基于 GPS 的工具一起购买,这些工具允许操作员绘制收割区域的边界,并计算出最短时间和最大收获量的最佳路径。在收割田地非常大的情况下,操作员几乎可以在联合收割机工作时小睡片刻。 3 这与需要手动调节割台的老式联合收割机相去甚远。
环境
环境是许多人关心的问题,许多科学家正在积极地监测它。监控环境的动机包括检查特定区域或房间的气体和跟踪该区域的温度和湿度,以及监控和报告敏感设备的异常情况,例如对洁净室进行化学分析。环境传感器网络的例子包括用于监测空气污染、探测和跟踪森林火灾、探测滑坡、提供地震预警以及提供工业和结构监测的网络。
传感器网络非常适合各种形式的环境监控。由于传感器尺寸小、能耗低、成本低,因此可以轻松安装在特定位置或特定机器上,以获得精确的报告。例如,洁净室环境通常需要非常精确的温度和湿度控制以及极低水平的污染物(漂浮在空气中的松散颗粒)。传感器可用于在关键位置(窗户、门、通风口等)测量这些观察结果;数据被发送到计算机,计算机记录数据并产生阈值警报。大多数复杂的洁净室将过滤、加热和冷却系统连接到同一个计算机系统中(使用它们自己的传感器),以根据从传感器网络收集的数据来控制环境。
环境传感器不限于温度、湿度、露点和空气质量。用于监控电磁干扰和无线电频率的传感器可用于医院,以保护依赖敏感电子医疗设备(如心脏起搏器和类似的救生电子设备)的患者。 4 用于监测水纯度、氧气水平和污染物的传感器可用于养鱼场,以使作物产量最大化。
科学家和工业工程师不是唯一建立环境传感器网络的人。您可以使用成本相对较低的传感器构建自己的传感器。艾米丽·格茨和帕特里克·迪·高山重友在他们的书《Arduino 环境监测:构建简单的设备来收集我们周围世界的数据》中展示了如何构建简单的传感器网络来监测噪音、水的纯度,当然还有天气。
如果这听起来好得不像是真的,现在考虑一下你的普通家庭供暖、通风和制冷系统(HVAC)。它有一个非常简单的传感器网络,通常采用一个或多个环境温度传感器(墙上的恒温器)的形式,将数据输入到控制板,控制板打开机械装置,将气体泵送到系统中,并通过风扇移动空气。一些现代 HVACs 使用额外的传感器来监测空气质量,并使用额外的主动电子过滤器 5 或将热量和冷却转移到最需要的地方。如果你已经购买了一个现代的 Wi-Fi 恒温器,你可能会惊讶地发现它是一个物联网设备,因为大多数允许你从任何房间甚至当你不在家时控制你的 HVAC 系统。
Is a Thermostat a Sensor Node?
如果你曾经在一个有恒温器的家里使用滑动或旋转臂来设置所需的温度,很可能你遇到了一个简单的传感器节点。老式恒温器结合使用温度感应线圈和安装在线圈上的倾斜开关。这个线圈依次安装在一个板上,该板可以向一个方向或另一个方向倾斜,以调节所需的温度。随着室温的变化,线圈会膨胀或收缩,重新调整倾斜开关的方向。一旦线圈膨胀或收缩,倾斜开关断开,流向暖风、通风与空调装置的电压停止,从而关闭该装置。
一些制造商正在制造越来越复杂的恒温器。有些甚至能够记录数据和预测趋势。例如,Nest Learning 恒温器( www.nest.com/living-with-nest/ )可以检测何时有人在家,并可以通过互联网远程访问。
大气的
与环境监测密切相关的是大气监测:一种旨在监测空气质量的传感器网络。大气监测是环境监测的一种形式,但更侧重于研究大气。显而易见的原因是,没有空气,哺乳动物根本无法生存(至少,不会长久)。
与环境传感器网络一样,有专门的传感器来测量所有形式的空气质量,包括游离气体、颗粒污染、烟雾、湿度等。建立大气传感器网络的其他动机包括测量工厂和汽车的污染(大多数汽车在发动机和机舱系统中集成了几个大气传感器),确保来自水处理厂的清洁饮用水,以及测量气溶胶的影响。
幸运的是,对于业余爱好者和有抱负的大气科学家来说,气体传感器有很多,而且很多都不贵。更好的是,互联网上的许多示例项目演示了如何构建大气传感器网络。
Environment vs. Atmosphere: What’s The Difference?
如果你想知道环境和气氛之间的区别,你并不孤单。简单来说,环境是主体周围影响主体的事物(人、物、事)的集合体。因此,它可以是你周围的所有事物,包括环境温度、湿度等等。
大气(字面意思是空气)是指充满物体周围空间的气体集合。气氛是环境的要素之一。科学家已经定义了地球周围的许多层大气。大多数大气传感器设计用于测量特定水平的独特气体。我们居住的低层大气叫做对流层。
像前面讨论的环境监测传感器网络一样,您可以构建自己的大气传感器网络。在他们的书《使用 Arduino 进行大气监测:构建简单的设备来收集环境数据》中,艾米丽·格茨和帕特里克·迪·高山重友还展示了如何构建简单的传感器网络来测量气体,如丁烷和甲烷、光波长、臭氧等。
安全
一些最流行和最多产的传感器网络是用于安全和监视的。您可能不认为安全系统是传感器网络,但让我们考虑一下典型的家庭或办公室安全系统包含哪些内容。
一个基本的安全系统被设计成在门或窗被打开时进行记录和报警。这种网络中的传感器是开关(所有传感器中最简单的一种),可以检测门或窗何时被打开或关闭。中央处理器或微控制器可用于监控传感器并采取行动:例如,用蜂鸣器或铃产生信号。
监控系统不仅仅包括一组开关。通常情况下,这种系统包括视频传感器(相机-具有或不具有红外功能,以增强夜间照片)、边界传感器(运动、视线中断等)。),甚至还有音频传感器(麦克风)。该系统还可以包括某种形式的监视器,该监视器记录数据并使用户能够查看该数据(查看门何时被打开、听音频和看视频)。
大多数家庭监控系统包括数字录像机(DVR)或类似的专用系统以及一个或多个摄像机。一种流行的家庭系统包括四个带音频的摄像机。该系统允许您以编程方式记录来自传感器的数据,并实时查看视频。图 1-2 展示了一个来自 Harbor Freight ( www.harborfreight.com )的典型且经济实惠的家庭监控系统。
图 1-2
安全传感器网络:港口货运的家庭监控系统
企业中使用的监控系统类似于家庭监控系统,但通常包括额外的传感器和数据跟踪,如员工徽章、设备监控和集成,以及非现场支持服务,如守夜人和数据归档。
另一个例子包括在门铃、安全灯和类似的外部设备上增加摄像机。例如,一些最新的摄像头门铃有运动和类似的传感器来检测运动,甚至在晚上增强视频。有些,像门铃,允许两个或更多的门铃链接在一起,形成一个“邻里守望”系统( https://shop.ring.com/pages/neighbors )。最重要的是,它们提供实时警报,可以帮助您更快地发现犯罪并向当局报警。是的,这也是一个物联网设备!
尽管它们不像温度、湿度、光线或气体传感器那样便宜,但麦克风和摄像头正变得越来越便宜。你可以在电子商店找到这些传感器,比如 Adafruit 工业。例如,Adafruit 有一个摄像头( http://adafruit.com/products/397 ),你可以将其连接到你的 Arduino 或 Raspberry Pi 来记录图像和低帧率视频(见图 1-3 )。
图 1-3
Adafruit Industries 的摄像头传感器(由 Adafruit 提供)
许多安全传感器网络可供消费者使用。它们从简单的音频/视频监控到集成到您家中的远程监控系统,可以跟踪从移动到门户漏洞,甚至温度和照明等一切。
传感器网络的拓扑结构
现在您已经看到了一些例子,让我们来讨论一下传感器网络的组件:在本例中,是一个花园池塘监控系统。具体来说,该系统监测鱼塘的健康状况。因此,该系统是一个环境传感器网络。
这样做的动机是为了确保鱼有一个安全的环境。这意味着水温应在鱼类的耐受范围内,水深应保持在适当的深度,以避免水位过高或过低,并且应监测水中的含氧量,以确保有足够的氧气供鱼类生存。类似地,可以使用传感器来确保诸如其他水生动物或藻类的共生生命的健康水平。
大多数池塘主人已经学会了用生命周期来建造池塘,以确保池塘能够维持其环境。然而,事情可能会出错。另一种物种的引入(如两栖动物 6 或可怕的藻类泛滥)会导致失衡,威胁到你珍贵的锦鲤。如果能够检测到不平衡何时开始,那么解决方案就更容易实施。
图 1-4 显示了描述传感器及其位置的简图。在这个系统中,有三个传感器,一个监控或记录系统,以及一个通信介质——传感器向监控器发送数据的一种方式。让我们从讨论传感器开始。
图 1-4
典型的鱼塘监控系统
如果我要建造这个系统,我会使用低压传感器,这样我就可以用电池或太阳能为它们供电。大多数传感器都是分立元件,接收电压并产生数字或模拟数据。它们需要另一个组件来读取数据并将其发送到池塘监控控制系统。如果你认为这将是一个 Arduino 的好用途,你是对的!Arduino 是从一个或多个传感器读取数据并将其发送到另一个系统进行处理的优秀平台。一些有事业心的 Arduino 爱好者仅使用一个 Arduino 和多个传感器构建了监控系统。
让我们假设池塘监控系统是一台连接了 Arduino 的计算机,这样您就可以远程记录、查看或访问数据。现在,您已经将传感器连接到一个 Arduino(称为传感器节点),并将池塘监控系统连接到另一个 Arduino(称为聚合器节点)。缺少的是如何将数据从传感器节点传送到汇聚节点。
有许多方法可以让两个 Arduinos 进行通信或共享数据,但本书将讨论限制在允许长距离通信的媒体上——有线或无线。在这种情况下,有线通信可以通过以太网屏蔽(一种设计用于 Arduino 顶部的特殊子板)或安装在每个 Arduino 上的无线保真(Wi-Fi)屏蔽进行。
正如您所看到的,构建传感器网络涉及许多级别的硬件和协议。现在,您已经对主要组件有了大致的了解,让我们来研究一下通信介质,然后讨论一下传感器节点的类型。
传播介质
现在,您已经了解了传感器网络的拓扑结构,让我们考虑传感器如何将数据传递给网络中的其他节点。他们通过两种基本形式的网络通信来实现:有线和无线。
有线网络
有线网络可以采取多种形式,包括某种形式的硬件,用于通过电线或电缆将电信号从一个设备发送到另一个设备。因此,采用有线通信的传感器网络也必须向网络中的节点添加网络硬件。
正如我前面提到的,您可以使用带有以太网屏蔽的 Arduino 将传感器节点连接到聚合或数据收集节点。如果您的传感器由 Raspberry Pi 计算机托管,您就已经拥有了连接两台 Raspberry Pi 计算机的必要硬件——它们都有 RJ-45 局域网端口。
当然,使用有线以太网并不像将一根电缆插入两台设备那么简单。除非使用交叉电缆,否则需要某种形式的以太网交换机来连接设备。以太网网络和硬件的详细讨论超出了本书的范围,但它是传感器网络的一种可行的通信介质。
虽然由于许多 Wi-Fi 解决方案的可用性,有线网络的使用在今天没有那么流行,但使用有线网络有助于提高传输速度和可靠性,在某些情况下,还可以提高安全性。
无线网络
一种更受欢迎和更通用的媒体是无线通信。在这种情况下,您可以为每个 Arduino 使用无线设备,例如 Wi-Fi shield,或者为 Raspberry Pi 电脑使用 Wi-Fi 适配器。与有线以太网一样,无线以太网(Wi-Fi)需要添加无线路由器。但是,Wi-Fi 的最大距离要短得多,因此可能不适合某些网络。
但是你有另一种无线方式可以使用。您可以使用 XBee 无线模块来代替以太网(Wi-Fi)。XBee 提供了一种专门的轻量级协议,非常适合用于传感器节点、小型微控制器和嵌入式系统。甚至还有支持蓝牙 Mesh 的模块,但我们将把重点放在 Wi-Fi 模块上。本书的其余部分使用 XBee 模块作为示例传感器网络项目的通信机制。
XBee 模块的一个特性是它们是低功率的,并且可以被置于周期性睡眠模式以节省功率。然而,最好的特性是 XBee 模块可以直接连接到传感器,允许您构建更轻(更便宜)的传感器节点。XBee 模块将在第二章中详细讨论。
混合网络
一些复杂的传感器网络需要混合两种通信介质。例如,工业传感器网络可以使用安装在许多不同建筑物或房间中的传感器节点来收集数据。您可能希望将传感器网络隔离成子系统,因为每个区域可能需要不同形式的传感器网络。在这种情况下,对于难以使用有线网络的某些部分(例如移动的工业机器人上的传感器),最好使用无线网络,并使用有线以太网将子系统连接到中央数据记录或数据监控系统。
传感器节点的类型
传感器节点由一个或多个传感器(虽然本书每个节点只使用一个传感器)和一个通信设备组成,用于传输数据。如上所述,通信设备可以是 Arduino 之类的微控制器、嵌入式系统,甚至是 Raspberry Pi 之类的小型计算机。典型地,传感器节点被设计用于无人值守的操作;它们有时安装在移动物体上或有线通信不切实际的地方。在这些情况下,传感器节点可以被设计成在不受电源或通信源限制的情况下工作。
从逻辑上讲,传感器节点可以根据其使用方式分为不同的类型。以下部分详细介绍了本书中使用的传感器节点类型。按角色考虑传感器节点有助于您使用逻辑构建块来设计和规划传感器网络。
基本传感器节点
在传感器网络的最低(或叶)层是基本传感器节点。这是迄今为止所描述的节点类型——它具有单个传感器和通信机制。这些节点不以任何方式存储或处理捕获的数据,它们只是将数据传递给网络中的另一个节点。
数据节点
下一种类型的节点是数据节点。数据节点是存储数据的传感器节点。这些节点可以将数据发送到另一个节点,但通常是将数据发送到数据卡等存储机制、通过计算机发送到数据库或直接发送到 LCD 屏幕、面板仪表或 LED 指示灯等视觉输出设备的设备。
数据节点需要的设备不仅仅是将数据传递给另一个节点。他们需要能够记录或展示数据。这是微控制器的一个很好的用途,你会在后面的章节中看到。XBee 的制造商 Digi 拥有专用的传感器节点,可以测量温度、湿度和光线信息,并在网络上传输数据。那有什么好玩的?在本书中,您将构建自己的传感器节点。
数据节点可用于形成自主或无人值守的传感器网络,记录数据供以后存档。回到鱼池的例子,许多商业池塘监控系统采用具有多个传感器的独立传感器设备,这些传感器将数据发送到数据节点;用户可以访问数据节点并在计算机上读取用于分析的数据。
聚合器节点
另一种类型的节点是聚合节点。这些节点通常使用通信设备和记录设备(或网关)而没有传感器。它们用于从一个或多个数据或传感器节点收集数据。在迄今为止讨论的示例中,监控系统将具有一个或多个聚集器节点来从传感器读取数据。图 1-5 显示了每种类型的节点将如何在一个虚构的传感器网络中使用。
图 1-5
传感器网络中的节点类型
对于更一般的情况,图表可能应该显示多个数据节点(以便聚合器节点聚合东西)。
在这个例子中,顶部的几个传感器节点向中间的一个数据节点无线发送数据。数据节点收集数据并将其保存到安全数字卡中,然后安全数字卡将数据发送到聚合器节点,聚合器节点通过有线计算机网络与数据库服务器通信以存储数据。将数据节点与聚合器节点混合使用,可以确保在聚合器节点出现故障或者记录和监控系统出现故障或离线时不会丢失任何数据。
现在您已经了解了传感器网络中的节点类型,让我们来看看传感器:它们如何测量数据,以及可用于构建低成本传感器网络的传感器示例。
传感器
谈到传感器、什么是传感器网络以及它们如何传递数据,您可能想知道传感器到底是什么,它们有什么意义。本节及其小节回答了这些问题以及更多问题。让我们从传感器的定义开始。
传感器是一种测量物理世界现象的装置。这些现象可以是你看到的东西,像光、气体、水蒸气等等。它们也可以是你感觉到的东西,像温度、电、水、风等等。人类的感觉就像传感器一样,让我们能够体验周围的世界。然而,有些东西你的传感器看不到或感觉不到,比如辐射、无线电波、电压和安培数。在测量这些现象时,传感器的工作是以电压表示或数字的形式传递测量结果。
传感器有多种形式。它们通常是为单一用途设计的低成本设备,处理能力有限。大多数简单的传感器都是分立元件;甚至那些具有更复杂部件的设备也可以被视为单独的组件。传感器可以是模拟的,也可以是数字的,通常只用于测量一种东西。但是越来越多的传感器模块被设计用来测量一系列相关现象,例如来自 SparkFunElectronics(www.sparkfun.com/products/10586)的 USB 天气板(见图 1-6 )。
图 1-6
USB 天气板(由 SparkFun 和胡安·佩尼亚提供)
注意上面写着 XBee 的蓝色模块。这是一个无线模块,允许传感器板将其数据发送到另一个或多个节点。XBee 将在第二章中详细讨论。
以下部分探讨传感器如何测量数据、如何存储数据,以及一些常见传感器的示例。
传感器如何测量
传感器是基于其化学和机械结构的独特属性产生电压的电子设备。他们不能操纵他们被设计用来测量的现象。相反,传感器对一些物理变量进行采样,并将其转换为成比例的电信号(电压、电流、数字信号等)。
例如,湿度传感器测量空气中水(湿气)的浓度。湿度传感器对这些现象做出反应,并产生一个电压,微控制器或类似设备可以读取该电压,并使用该电压来计算秤的数值。一种基本的低成本湿度传感器是 DHT-22,可从大多数电子商店买到(见图 1-7 )。
图 1-7
DHT-22 湿度传感器(由 Adafruit 提供)
DHT-22 设计用于测量温度和湿度。它在输出端(数据引脚)产生一个数字信号。虽然使用简单,但它有点慢,应该用于以合理的慢速度跟踪数据(不超过每 3 或 4 秒一次)。
当该传感器产生数据时,数据以一系列高(解释为 1)和低(解释为 0)电压的形式传输,微控制器可以读取并使用这些电压来形成一个值。在这种情况下,微控制器从传感器读取长度为 40 位(40 个高或低电压脉冲)的值,即 5 个字节,并将其放入程序变量中。前两个字节是湿度值,后两个字节是温度值,第五个字节是校验和值,以确保读数准确。幸运的是,所有这些艰苦的工作都以专门为 DHT-22 和类似传感器设计的库的形式为您完成了。让我们看看这在实践中是如何工作的。
清单 1-1 展示了 Adafruit 为 Arduino 平台提供的 DHT 库的摘录。你可以在 https://github.com/adafruit/DHT-sensor-library 找到这个库。该列表显示了用于从 Arduino 上的 DHT-22 传感器库中读取湿度的方法。
/*
Beginning Sensor Networks, 2nd Edition
This sketch demonstrates a basic sensor node using a DHT22 sensor to read temperature and humidity printing the results
in the serial monitor.
Dr. Charles Bell
*/
#include <DHT.h>
#include <DHT_U.h>
#define DHTPIN 2 // Digital pin connected to the DHT sensor
#define DHTTYPE DHT22 // DHT 22 (AM2302), AM2321
DHT dht(DHTPIN, DHTTYPE);
void setup() {
}
void loop() {
float humidity = dht.readHumidity();
float temperature = dht.readTemperature();
// Make sure they are numbers or fail.
if (isnan(temperature) || isnan(humidity)) {
Serial.println("ERROR: DHT values are not numbers!");
} else {
Serial.print("Temperature (C): ");
Serial.print(temperature);
Serial.print("Humidity: ");
Serial.print(humidity);
}
}
Listing 1-1Reading Temperature and Humidity with a DHT-22
请注意,DHT 库提供了使读取温度(摄氏度)和湿度并显示这些值变得非常容易的方法。 7 没错,就是这么简单!如果你想尝试 DHT-22,在 Adafruit 的网站上有一个很好的教程( http://learn.adafruit.com/dht )。
回想一下,DHT-22 产生一个数字值。不是所有的传感器都这样做;一些产生电压范围。这些被称为模拟传感器。让我们花一点时间来理解它们的区别。这将成为您规划和构建传感器节点的重要信息。
模拟传感器
模拟传感器是产生电压范围的装置,通常在 0 到 5 伏之间。 8 需要一个模数转换电路将电压转换成数字。大多数微控制器都内置了这一功能,Arduino 就是一个很好的例子。Arduino 有一组有限的引脚,用于处理模拟数据,并集成了模数(A/D)转换电路。
但事情并没有那么简单(是吗?).模拟传感器的工作原理类似于电阻,当连接到微控制器时,通常需要另一个电阻来“上拉”或“下拉”电压,以避免称为浮动的虚假电压变化。这是因为流过电阻的电压在时间和幅度上都是连续的。因此,即使传感器不产生值或测量值,仍有电压流过传感器,可能导致虚假读数。您的项目需要明确区分关(零电压)和开(正电压)。上拉和下拉电阻确保您拥有这两种状态之一。模数转换器负责从传感器读取电压,并将其转换为可被解读为数据的值。
What is a Resistor?
电阻器是电子学的标准构件之一。它的作用是阻止电流并降低电压(转化为热量)。它的影响被称为电阻,用欧姆来衡量。电阻可用于降低其它元件的电压,限制频率响应,或保护敏感元件免受过压影响。
当电阻用于上拉电压(通过将一端连接到正电压)或下拉电压(通过将一端连接到地)(电阻是双向的)时,它消除了电压在不确定状态下浮动的可能性。因此,上拉电阻确保稳定状态为正电压,下拉电阻确保稳定状态为零电压(地)。
一本优秀的入门电子书籍是查尔斯·普拉特(O'Reilly,2012)的《电子元件百科全书》。
采样时(从传感器读取值时),电压读数必须解释为给定传感器指定范围内的值。请记住,比方说,一个模拟传感器输出的 2 伏电压可能与另一个模拟传感器输出的 2 伏电压不是一回事。每个传感器的数据表显示了如何解释这些值。
当您使用 Arduino 之类的微控制器时,模数转换器可以方便地将电压转换为 10 位值,从而得到 0 到 1023 之间的整数值。例如,一个传感器可以测量由 200 个点组成的范围内的现象。最低值通常表示 0,最高值表示 1023。在这种情况下,可以对 Arduino 进行编程,将从 A/D 转换器读取的值转换为传感器刻度上的值。
如您所见,使用模拟传感器比使用上一节中的 DHT-22 数字传感器要复杂得多。稍加练习,您会发现,一旦了解如何将模拟传感器连接到微控制器,以及如何在传感器校准工作的范围内解释其电压,大多数模拟传感器都不难使用。
数字传感器
像 DHT-22 这样的数字传感器被设计成使用串行传输产生一串比特(一次一个比特)。然而,一些数字传感器通过并行传输产生数据(一次一个或多个字节 9 )。如前所述,这些位表示为电压,其中高电压(比如 5 伏)或开是 1,低电压(0 甚至-5 伏)或关是 0。这些开和关值序列称为离散值,因为传感器以脉冲形式产生一个或另一个值,即开或关。
与模拟信号相比,数字传感器的采样频率更高,因为它们生成数据的速度更快,而且不需要额外的电路来读取数值(例如 A/D 转换器以及将数值转换为刻度的逻辑或软件)。因此,数字传感器通常比模拟传感器更精确和可靠。但是,数字传感器的精度与其用于采样数据的位数成正比。
数字传感器最常见的形式是按钮或开关。什么,按钮是传感器?为什么,是的,它是一个传感器。考虑一下安装在家庭安全系统窗户上的传感器。这是一个简单的开关,当窗户关闭时关闭,当窗户打开时打开。当开关连接到电路中时,当车窗关闭且开关闭合时,电流是恒定且不间断的(使用上拉电阻测量正电压),但当车窗和开关打开时,电流中断(测量零电压)。这是最基本的开关传感器。
大多数数字传感器都是由几个元件组成的小电路,用于产生数字数据。与模拟传感器不同,读取它们的数据很容易,因为这些值无需转换就可以直接使用(除了转换到其他刻度或测量单位)。有些人可能认为这比使用模拟传感器更困难,但这取决于你的观点。电子爱好者会认为使用模拟传感器更容易,而程序员会认为数字传感器更容易使用。
那么,一旦数据被测量出来,你会怎么处理呢?以下部分简要描述了传感器数据的一些方面以及存储这些数据的注意事项。
存储传感器数据
存储传感器数据取决于如何解释数据以及最终如何使用数据。如果你计划使用计算机,或者更好的是数据库来存储数据,你应该以一种有意义的方式来存储它。
例如,存储模拟信号的电压序列可能被认为是以最纯粹的形式保存数据,但如果没有上下文或模数转换器,数据可能毫无意义。存储电压的数字转换可能也不明智,因为您必须记住刻度和范围,以获得想要表示的值。因此,存储结果转换成比例更有意义。幸运的是,当您使用数字传感器时,您唯一需要记住的是所使用的测量单位(摄氏度、华氏度、英尺、米等等)。因此,最好保存测量的最终形式。
但是你把这些信息存储在哪里呢?商业传感器网络将数据存储在嵌入式数据库或文件存储设备中,将其传输到另一个系统进行存储,或者将其存储在可移动数字介质上。较老的传感器网络(如测谎仪或 EKG 机器)使用图表将数据存储为硬拷贝(使它们非常过时)。
有几种简单的存储设备和技术可以用来构建自己的传感器网络,从 Arduino 的本地设备到 Raspberry Pi 上的现代硬盘。这里列出了这些存储机制,并在本书研究构建传感器网络时使用的硬件类型和技术应用时进行了更详细的讨论:
-
硬拷贝打印机
-
安全数字卡
-
USB 硬盘
-
网络服务器
-
数据库服务器(MySQL)
现在,让我们看看一些可用的传感器及其测量的现象类型。
传感器的例子
所有传感器网络都始于一个传感器和一种读取和解释数据的方法。本章介绍了许多关于传感器的信息。你可能在想各种各样有用的东西,你可以在家里或办公室,甚至在你的院子里或周围测量。你可能想测量你的新阳光房的温度变化,检测邮递员什么时候把最新的通知扔进你的邮箱,或者记录你的狗使用狗门的次数。我希望到现在为止,当你想象你能测量什么的时候,你能看到这些只是冰山一角。你应该在思考你想要建立一个什么样的传感器网络;你可以用这本书来学习如何建造它。
有哪些类型的传感器可用?下表描述了一些比较流行的传感器及其测量内容。这只是可用资源的一个样本。细读 Mouser Electronics ( www.mouser.com )、spark fun Electronics(www.sparkfun.com)和 Adafruit Industries ( http://adafruit.com/ )等在线电子产品供应商的目录,会发现更多的例子:
-
加速度计:这些传感器测量传感器或它所连接的任何东西的运动或移动。它们被设计用来感知几个轴上的运动(速度、倾斜度、振动等)。).一些包括回转仪特征。大多数是数字传感器。Wii 双截棍(或 WiiChuck)包含一个复杂的加速度计,用于跟踪运动。啊哈:现在你知道 Wii 附带的那些有趣的小东西的秘密了。
-
音频传感器:也许这是显而易见的,但是麦克风是用来测量声音的。大多数是模拟的,但一些更好的安全和监控传感器具有数字版本,用于传输数据的更高压缩。
-
条形码读取器:这些传感器用于读取条形码。最常见的是,条形码阅读器生成代表条形码的数字等价物的数字数据。这种传感器通常用在库存跟踪系统中,以在工厂或运输过程中跟踪设备。它们数量众多,而且许多价格经济实惠,使您能够将它们整合到自己的项目中。
-
RFID 传感器:射频识别使用无源设备(有时称为 RFID 标签)通过电磁感应使用射频进行数据通信。例如,RFID 标签可以是信用卡大小的塑料卡、标签或类似的包含特殊天线的东西,通常以线圈、细线或箔层的形式调谐到特定频率。当标签被放置在阅读器附近时,阅读器发射无线电信号;标签可以使用电磁能量以无线电信号的形式传输嵌入在天线中的非易失性消息,该无线电信号然后被转换成字母数字串。 10
-
生物传感器:读取指纹、虹膜或掌纹的传感器包含一个用于识别模式的特殊传感器。鉴于指纹和掌纹等图案的独特性,它们是安全访问系统的优秀组件。大多数生物传感器产生一组代表指纹或掌纹的数字数据。
-
电容传感器:电容传感器的一种特殊应用,脉搏传感器被设计用来测量你的脉搏率,通常使用指尖作为传感部位。称为脉搏血氧仪(一些医疗专业人员称为 pulseox)的特殊设备通过电容传感器测量脉搏率,并通过光传感器确定血液中的含氧量。如果你拥有现代电子设备,你可能会遇到触敏按钮,它们使用特殊的电容传感器来检测触摸和压力。一些更新的版本可以用来测量液位。
-
硬币传感器:这是一种最不寻常的传感器。这些设备就像典型的自动售货机上的投币口。像它们的商业等价物一样,它们可以被校准以感应何时插入一定大小的硬币。虽然不像商业单位那样复杂,可以区分真假硬币,硬币传感器可以用来为您的项目添加一个新的维度。想象一个投币 Wi-Fi 站。现在,这应该可以防止孩子们在互联网上花太多时间!
-
电流传感器:用于测量电压和电流强度。有些是为测量变化而设计的,而有些是为了测量负载。
-
挠曲/力传感器:电阻传感器测量一块材料的挠曲或压力对传感器的影响。弯曲传感器可能有助于测量扭转效应或测量手指运动(就像任天堂的电动手套一样)。当传感器弯曲时,传感器电阻增加。
-
气体传感器:气体传感器种类繁多。有些测量潜在有害气体,如液化石油气和甲烷以及其他气体,如氢气、氧气等。其他气体传感器与光传感器相结合来感测空气中的烟雾或污染物。下次当你从烟雾探测器中听到那种泄露秘密且经常令人讨厌的低电量警告声 12 时,想想那个设备包含了什么。为什么,这是一个传感器节点!
-
光传感器:测量光线强弱的传感器是特殊类型的电阻:光敏电阻(LDRs),有时也称为光敏电阻或光电池。因此,它们本质上是相似的。如果你有一台 Mac 笔记本电脑,当你的发光键盘在弱光下自动打开时,你很可能已经看到了光敏电阻的作用。或者,您的手机可以使用光传感器来改变亮度。特殊形式的光传感器可以检测其他光谱,如红外线(如旧的电视遥控器)。
-
液体流量传感器:这些传感器类似于阀门,内嵌在管道系统中。他们测量液体通过时的流量。基本的流量传感器使用旋转轮和磁铁来产生霍尔效应(快速开/关序列,其频率等于通过的水量)。
-
液位传感器:一种特殊的电阻固态装置可以用来测量水体的相对高度。一个例子是当水位高时产生低电阻,当水位低时产生高电阻。
-
位置传感器:现代智能手机有 GPS 传感器来感应位置,当然 GPS 设备也使用 GPS 技术来帮助你导航。幸运的是,GPS 传感器以低成本的形式提供,使您能够将位置检测添加到您的传感器网络中。GPS 传感器以经度和纬度的形式生成数字数据,但有些传感器也可以感知海拔高度。
-
磁条阅读器:这些传感器从磁条(像信用卡上的磁条)读取数据,并返回数字形式的字母数字数据(实际的字符串)。
-
磁力计:这些传感器通过磁场强度测量方位。指南针是一种用来寻找磁北的传感器。一些磁力计提供多个轴,以便更好地检测磁场。
-
近程传感器:近程传感器通常被认为是距离传感器,它使用红外线或声波来检测物体的距离或范围。受低成本机器人套件的欢迎,视差超声波传感器通过感应发送脉冲和接收脉冲之间的时间量(回声),使用声波来测量距离。对于近似的距离测量, 13 把时间转换成距离是一个简单的数学问题。这有多酷?
-
辐射传感器:比较严重的传感器是那些检测辐射的传感器。这也可能是电磁辐射(也有传感器),但盖革计数器使用辐射传感器来检测有害的电离。事实上,使用一个传感器和一个 Arduino(以及一些电子元件)来建造你自己的盖革计数器是可能的。
-
速度传感器:像流量传感器一样,许多自行车上的简单速度传感器使用磁铁和簧片开关来产生霍尔效应。频率结合车轮的周长可以用来计算速度,以及随着时间的推移,行驶的距离。是的,自行车计算机是简单传感器网络的另一个例子:车轮和前叉上的速度传感器为车把上的监视器提供数据。
-
开关和按钮:这些是最基本的数字传感器,用于检测某个东西是否被设置(开)或重置(关)。
-
倾斜开关:这些传感器可以检测设备何时向某个方向倾斜。虽然非常简单,但它们对于低成本运动检测传感器非常有用。它们是数字的,本质上是开关。
-
触摸传感器:形成小键盘、键盘、定点设备等的触敏薄膜是一种有趣的传感器形式。你可以将像这样的触摸感应设备用于需要从人类身上收集数据的传感器网络。
-
视频传感器:如前所述,有可能获得非常小的视频传感器,这些传感器使用相机和电路来捕捉图像并将其作为数字数据传输。
-
气象传感器:温度、气压、降雨量、湿度、风速等传感器都属于气象传感器。大多数生成数字数据,可以组合起来创建全面的环境传感器网络。是的,通过十几个廉价的传感器、一个 Arduino(或一个 Raspberry Pi)和一些解释和组合数据的程序,你可以建立自己的气象站。
摘要
传感器无处不在。它们在你的办公室、车里、家里和我们的个人电子设备中(对大多数人来说,这意味着我们身边总是有一个传感器)。你遇到的大多数传感器都是分立的,比如烟雾探测器或恒温器。有时它们是一个更大的传感器集合的一部分,旨在实现某些功能,例如当你设置巡航控制时,你车上的传感器可以保持你的速度恒定,下雨时启动挡风玻璃雨刷,或者当你转向太靠近车道分界线时振动你的座位。
现在,您已经了解了更多关于传感器类型及其通信数据的信息,您可能已经开始考虑构建一些很酷的项目了。这本书将帮助你了解那些项目。本章研究了什么是传感器网络,它们是如何构建的,它们是如何通信的,以及传感器是如何工作的。您甚至看到了一些代码!
下一章通过深入到新 XBee 3 无线模块的一个简短教程来关注本书中使用的通信媒介。您将了解如何设置和配置这些设备,以便将传感器数据传输到数据和聚合节点。
Footnotes 1有趣的是,我听说过一些驾驶者轻视这些功能,因为他们的驾驶习惯将车辆更多地放在道路的一侧或另一侧,这触发了车道偏离警告。同样,那些在弯曲道路上驾驶时习惯性越过中心线的人往往会关闭偏离警告。显然,其中一个是可以理解的烦恼,而另一个正是为什么需要这个特性。
2
http://en.wikipedia.org/wiki/On-board_diagnostics
3
可能很难想象一台 46,000 多磅的机器由计算机驱动,它类似于中世纪的刑具或连环杀手的武器,但这是真的。一些最昂贵的联合收割机拥有比你最喜欢的运动轿车更复杂的技术,包括空调、巡航控制和完全可调的座椅。
4
如果你的家庭成员需要这样的设备,这将变得非常重要。
5
对于我们这些生活在自然和人为污染物高浓度地区的过敏症患者来说,电子过滤器是绝对必要的。
6
我建造的每个池塘最终都让青蛙看似自然地诞生了。他们都来自哪里?
7
使用 Arduino IDE 的串行监视器功能。有关如何使用串行监视器的详细信息,请参见第三章。
8
也有输出为 4–20mA 的传感器,但在本书中,我们将重点介绍输出(信号)在 0–5V 范围内的传感器。
9
这取决于并行缓冲区的宽度。8 位缓冲区一次可以传输 1 个字节,16 位缓冲区一次可以传输 2 个字节,依此类推。
10
http://en.wikipedia.org/wiki/Radio-frequency_identification
11
www.sparkfun.com/products/11719
12
这里有一个很好的房主提示。不要跑来跑去试图找出哪个探测器间歇性地发出电池没电的信号(或误报),考虑用有 10 年电池的新版本代替它们( www.lowes.com/pd/First-Alert-Micro-Photoelectric-Smoke-Alarm-with-10-Year-Battery/1000456457 )。
13
精度可能取决于环境变量,如海拔、温度等。
二、微型通话模块:XBee 无线模块简介
传感器网络的应用通常排除了有线传感器的使用。虽然可以使用安装在支持电缆设备的受控环境中的有线传感器,但你很少有这种奢侈。有时,您可以将传感器网络的某些部分连接到有线网络,但传感器位于无法使用有线网络的区域。因此,大多数传感器网络需要使用无线技术将数据从传感器传输到网络中的其他节点。
无线通信有多种形式。这本书使用了最简单的一个:Digi 的 XBee 无线模块。在本章中,您将探索使用 XBee 模块的基础知识,从选择模块到将其配置为与微控制器一起使用,最后创建一个简单的网络。
什么是 XBee?
XBee 是一个独立的、模块化的、经济高效的组件,它使用射频(RF)在 XBee 模块之间交换数据。XBee 模块在 2.4 GHz 或远程 900 MHz 上传输,并有自己的网络协议。
XBee 模块本身非常小,大约只有一个大邮票的大小,这使得它很容易集成到传感器节点这样的小项目中。这些模块也是低功耗的,并且可以使用特殊的睡眠模式来进一步降低功耗。
虽然 XBee 不是一个微控制器,但它的处理能力有限,可以用来控制模块。其中一个功能,睡眠模式,可以帮助延长电池供电(或太阳能供电)传感器节点的电池寿命。您还可以指示 XBee 模块监控其数据引脚,并将读取的数据传输到另一个 XBee 模块。啊哈!因此,您可以使用 XBee 模块将传感器节点链接到数据聚合器节点。
虽然 XBee 可以用来读取传感器数据,但其有限的处理能力可能意味着它并不适合所有的传感器节点。例如,需要算法来解释或推断有意义的数据的传感器可能不适合单独使用 XBee。您可能需要使用微控制器或计算机来执行额外的计算。
更好的是,最新型号的 XBee(版本 3)可以使用 MicroPython 在芯片上编程。您甚至可以与该模块交互(对话)并交互式地执行您的 MicroPython 代码(就像您在 PC 上使用 Python 一样)。我们将在本章的后面看到更多关于这个令人兴奋的新特性的内容。
Note
要配置 XBee 模块,您必须使用 Digi 配置工具 XCTU,它(现在)可以在 Windows、macOS 和 Linux 上使用。旧版本仅限于 Windows。
下面几节探讨如何开始使用 XBee 模块,从如何选择 XBee 模块开始。我鼓励你在着手这个项目之前通读这一章。我在章节总结之前列出了完成本章项目所需的材料。
XBee 第一个
本节描述了可用的 XBee 模块的类型,如何为您的项目选择模块,以及如何配置它们。我让这一节保持简短,同时提供了足够的信息来解释您将使用什么 XBee 模块以及为什么使用。
但是有一件事让 XBee 世界的大多数新手感到困惑:如何配置模块。所以,让我们先澄清一下。XBee 模块有许多控制和设置,允许您为您的项目配置它。有两种方法可以更改这些设置:(1)使用 Digi 提供的 XCTU 桌面应用程序,以及(2)使用带有 XBee USB explorer 板的终端应用程序来手动更改设置。这是在一个特定的模式下(称为 AT 模式)使用一组命令(称为 AT 命令)来完成的。
您将了解不同的连接方法以及两种操作模式。您还将看到配置 XBee 模块的两种方法的演示。现在,让我们从选择 XBee 模块开始,深入我们的 XBee 教程。
选择 XBee 模块
如果你访问 Digi XBee 网站( www.digi.com/products/embedded-systems/digi-xbee/rf-modules ,你会看到一个最新模块的列表可供选择。有支持专有(Digi)协议、WiFi (UART 或 SPI 到 802.11 b/g/n)、ZigBee 1 和 802.15.4 协议的模块。那你怎么知道选哪个呢?
一些最流行的 XBee 模块支持 ZigBee 协议。你将在本书的项目中使用这些模块。如果您单击 ZigBee 模块的链接( www.digi.com/products/embedded-systems/digi-xbee/rf-modules/2-4-ghz-modules/xbee3-zigbee-3 ,您会发现有三种外形规格可供选择,以满足您的硬件要求,并且这些模块支持多种协议,包括 ZigBee 功能集、Digi Mesh、蓝牙低能耗(BLE)和 802.15.4 2 协议。本书使用支持 ZigBee Pro 特性集的模块。
有许多 XBee 模块提供三种外形规格中的一种,包括通孔和两种表面贴装选项。如果您点击Part Numbers & Accessories链接,您将看到一长串按外形分组的模块。图 2-1 显示了左侧通孔格式的两种可用格式。两者在大多数零售商处都有售。
图 2-1
XBee 3 外形规格
Ok, What’s a Zigbee?
ZigBee 是基于 IEEE 802.15.4 标准的网络通信开放标准。该协议支持网状网络的形成,该网状网络可以自动配置(通过协调器和路由器角色),修复断开的链路,并允许使用中间节点在更长的范围内传输数据(数据通过网格从一个节点传递到另一个节点)。尽管名字如此,ZigBee 并不归 Digi 所有,也不局限于类似命名的 XBee 模块。
对于这本书,我们将使用通孔格式。Digi 提供这些选项以允许更大的设计自由度,因此 XBee 模块可以用于几乎任何需要低成本无线选项的应用。
此外,您会注意到每种外形都有两个版本:标准型号和专业型号。两种型号具有相同的引脚排列,您可以在同一网络中混合搭配使用。pro 型号可能稍大(更长),耗电更多,成本稍高,但提供的范围比标准(也称为“常规模块”)模块更大。但是,如果不将 XBee 3 pro 和常规模块连接到电脑,您可能无法区分它们。例如,PCB 选项 XBee 3 pro 和常规模块看起来完全相同,但在电路板顶部用黑色墨水印刷有细微的差异。常规模块(我正在使用的批次)标有“109 202”,而专业模块标有“941 201”如果您使用 XBee 3 模块,您可能想要在 pro 模块上做一个小标记,以便于识别。
虽然你不会在 Digi 网站上找到它们,但 XBee-ZB 模块有几个迭代(称为系列或版本)。系列 1 模块使用支持点对点通信的旧芯片组。32 系列和 2.5 系列拥有更新的芯片组,支持多种通信形式,包括网状网络。Series 3 拥有最新的芯片组和片上 MicroPython,可实现更快、更轻松的开发。在本书中,您将使用系列 2 和系列 3 模块。
Which Series Should I Use?
有这么多系列的 XBee 模块可用,您可能想知道应该选择使用哪个。答案取决于你想如何使用它们以及你自己的经验水平。对于一些人来说,使用系列 2 和 2.5 将满足他们的所有需求。对于其他人来说,series 3 的易用性可能更适合。
另一个方面可能是模块的成本。随着系列 3 模块的发布,旧系列模块可能会更便宜。
那么,你应该使用哪一个呢?如果你已经有了一些模块,那也没关系,因为 series 3 是向后兼容 series 2 模块的。因此,您可以使用现有的模块,并在需要时添加新的系列 3 模块。如果您没有任何模块,使用系列 3 可能是让您的传感器网络运行的最简单的途径。
但是你还没有完成。你还必须选择你想要使用的天线类型。普通或专业模块有三种天线可供选择。 4 图 2-2 描绘了 XBee-ZB 模块可用的每种类型。下面的列表更详细地描述了每一项:
-
U.FL :该选项有一个非常小的连接器,需要一根适配器电缆(称为尾纤)来连接外部天线。这些天线的优点是 XBee 模块可以封装在外壳(甚至是金属)中,并且天线安装在外壳的外部。这些模块往往要多花几美元,并且需要购买尾纤和天线。
-
RPSMA :和 U.FL 选项一样,这款提供了外置天线;但是它使用更大的 RPSMA 连接器。您可以将旋转天线直接安装到连接器上,但天线承受压力的风险太大。因此,您应该使用延长线并将天线安装在外部。像 U.FL 选项一样,这些模块的成本稍高,并且需要购买天线。
-
PCB: The antenna is printed or embedded as a wire trace onto the module itself. This type of module is similar to the chip antenna and may be a bit less expensive to manufacture. Currently, only the PRO modules are available with this antenna option.
图 2-2
XBee 模块天线选项(SparkFun 提供)
Where’s The Whip?
如果你在过去遇到过 XBee 模块,你可能会熟悉一个天线选项,其中一根小电线安装在模块上,称为“鞭”或线天线。这些在系列 2.5 和更早版本中可用。它们稍微便宜一点,并提供全方位信号,使它们在某些应用中更容易使用。如果你能找到这些模块,你也许能在你的项目上节省几美元。
然而,线状天线不耐用,如果弯曲得太频繁,很容易损坏。幸运的是,你可以用一些相同规格和长度的绞合线焊接一个替换天线。通过剥离一点绝缘材料将旧天线焊接回原位是另一种选择,但这确实会稍微改变它的辐射特性。
现在你知道有许多类型的 XBee 模块,并且这本书的项目仅限于 XBee-ZB 系列 2 和 3 模块,让我们讨论如何与模块通信。
与 XBee-ZB 模块交互
当您检查 XBee 模块时,首先注意到的是引脚布局比设计用于试验板的典型分立元件小得多。此外,您不能将电脑直接连接到 XBee。您需要一个 USB 适配器来安装 XBee,以便与模块进行通信。幸运的是,有几种变体可供选择。您使用 USB 适配器来配置模块。
您可以使用 USB 加密狗,如 spark fun Electronics(www.sparkfun.com/products/11697)的 XBee Explorer 加密狗。此选项允许您将 XBee 模块安装在 PCB 上的接头(两排十针连接器)中,并将整个装置插入 USB 端口。由于它只比 XBee 模块本身大一点点,并且不需要电缆,所以它可能是在远程位置使用 XBee 的最佳选择。
图 2-3 显示了不带 XBee 模块的 XBee Explorer 加密狗。它接受系列 1、2、2.5 和 3 标准或专业型号。
图 2-3
XBee Explorer 加密狗(SparkFun 提供)
注意 PCB 右侧 XBee 模块的白色轮廓。这表示模块在电路板上的正确方向。在将其插入 USB 端口之前,请务必检查针脚是否对齐。
一个类似的选项是 XBee Explorer USB,也来自 SparkFun ( www.sparkfun.com/products/11812 )。它不是一个加密狗,而是一个带迷你 USB 连接器的独立 PCB 基座单元。它还支持系列 1、2、2.5 和 3 标准或专业模块。它需要一根 USB 转迷你 USB 线。图 2-4 显示 XBee Explorer USB 设备。
图 2-4
XBee Explorer USB(由 SparkFun 提供)
SparkFun 的两个选项都包括可以与试验板一起使用的接头安装孔,让您可以接触到 XBee 模块的所有引脚。虽然它们不带有焊接到位的引脚,但如果您愿意,也可以轻松添加引脚。在后面的章节中你会看到这将会有所帮助。
SparkFun 还搭载了其他几个 XBee Explorer 板,包括一个可调节的分线板( www.sparkfun.com/products/11373 )和一个 Arduino 盾( www.sparkfun.com/products/12847 )。如果你打算用 Arduino 来托管 XBee,SparkFun 盾是必须的。图 2-5 为火花防护罩。
图 2-5
XBee Arduino shield(spark fun 提供)
在他们的“SparkFun Thing”系列主板中也有几款支持 XBee 模块的主板,如 SparkFun Thing Plus ( www.sparkfun.com/products/15454 )。
What is a Shield?
屏蔽是一种 PCB,通过连接到 Arduino 上的接头,安装在 Arduino 的顶部。屏蔽用于扩展 Arduino 的硬件功能。有用于控制 LCD、以太网、XBees 等等的屏蔽。
Digi 还生产 XBee 3 开发套件,其中包含三个带天线的 XBee 标准表面贴装模块和三个 USB 接口板。它还包括所有你需要开始使用的线缆,包括一个方便的储物盒。虽然价格相当吓人(建议零售价 110.95 美元),但它确实为那些寻求最大实用性和无组装(除了将模块插入接口板之外)的人提供了一站式购买选择。图 2-6 展示了 SparkFun ( www.sparkfun.com/products/15216 )出售的 Digi ZigBee 开发套件。
图 2-6
Digi ZigBee 开发套件
该套件的一个很好的特性是 explorer 板支持即插即用的 Grove 接口(也称为 Grove 系统),这使得将模块连接到支持 Grove 的传感器就像将它们插在一起一样简单。有关 Grove 接口的更多信息,请参见 http://wiki.seeedstudio.com/Grove_System/ 。
虽然许多供应商提供了其他选项,包括旧的串行接口模块,但这些是我发现的使用 XBee 模块的最佳和最容易使用的选项。当您看到我们将 XBee 模块与 Arduino 一起使用时,您会看到一个 XBee 屏蔽示例,它支持 XBee 与 Arduino 引脚的直接连接。
引脚布局
如果您查看 XBee 模块,您会看到总共有 20 个引脚。如果从顶部(带天线的一侧)查看模块,从左上角开始,引脚标记为 1–10,从右下角开始,标记为 11–20。因此,插针 1 在左上角,插针 20 在右上角。但是所有这些东西都有什么作用呢?
你将在后面的章节中更详细地探索这些引脚,但是现在(如果你好奇的话),表 2-1 描述了典型 XBee 模块的引脚布局。在本例中,我展示的是 XBee-ZB 系列 2 模块的引脚布局。系列 3 模块在引脚上有一个区别;3 系列模块不支持针脚 14,因此不应与这些模块一起使用。
表 2-1
XBee 引脚布局
|别针
|
名字
|
描述
|
方向
|
默认
| | --- | --- | --- | --- | --- | | one | VCC | 电源 | 不适用的 | 不适用的 | | Two | 熄 | UART 数据输出 | 在外 | 在外 | | three | DIN/CONFIG | UART 数据输入 | 在…里 | 在…里 | | four | DIO12 | 数字输入/输出 12 | 两者 | 有缺陷的 | | five | 重置 | 模块复位 | 两者 | 带上拉电阻的集电极开路 | | six | RSSI PWM/二极管 10 | RX 信号强度,数字 I/O 10 | 两者 | 在外 | | seven | DIO11 | 数字输入/输出 11 | 两者 | 在…里 | | eight | 内向的; 寡言少语的; 矜持的 | 不连接 | 钠 | 有缺陷的 | | nine | DTR/sleep _ rq/神 08 | 睡眠控制,数字 I/O 8 | 两者 | 在…里 | | Ten | 地线 | 地面 | 不适用的 | 不适用的 | | Eleven | DIO4 | 数字输入/输出 4 | 两者 | 有缺陷的 | | Twelve | CTS/DIO7 | 允许发送,数字 I/O 7 | 两者 | 在外 | | Thirteen | 开启/睡眠 | 状态,数字输入/输出 9 | 在外 | 在外 | | Fourteen | 参考电压(VoltageReference) | 不连接 | 在…里 | 不适用的 | | Fifteen | 助理/DIO5 | 相关指示器,数字输入/输出 5 | 两者 | 在外 | | Sixteen | RTS/DIO6 | 请求发送,数字输入/输出 6 | 两者 | 在…里 | | Seventeen | a3/上帝 3 | 模拟输入/输出 3,数字输入/输出 3 | 两者 | 有缺陷的 | | Eighteen | a2/神 2 | 模拟输入/输出 2,数字输入/输出 2 | 两者 | 有缺陷的 | | Nineteen | a1/神 1 | 模拟 I/O 1、数字 I/O 1 | 两者 | 有缺陷的 | | Twenty | ado/神 0 | 模拟输入输出 0,数字输入输出 0 | 两者 | 有缺陷的 |
要了解 XBee 3 模块硬件的更多信息,请参见 Digi at www.digi.com/resources/documentation/digidocs/pdfs/90001543.pdf 的硬件参考文档。
在下一节中,您将看到如何开始配置在您的项目中使用的模块。
配置模块
配置 XBee 模块并不十分困难。因为您使用的是 ZigBee 模块,所以需要为每个模块设置地址,选择要在网络中执行的角色,并将您的模块配置为与您用来处理传感器数据的任何传感器或微控制器接口。让我们从讨论 ZigBee 寻址开始。
Can Sensors Be Connected Directly To The XBee?
XBee 模块可以通过其 I/O 端口读取传感器数据。然而,并非所有传感器都可以直接连接到 XBee 模块。如果传感器需要使用特殊通信协议的直接 I/O,您需要一个微控制器来读取传感器数据,然后将其发送到 XBee 进行传输。在下一章中,当您探索如何使用 DHT-22 温度传感器时,您将会看到这一点。
地址
XBee 模块标有特定的序列号或地址,位于模块底部。这有点不方便,因为当模块安装好后,你通常看不到它的背面。不过,您可以使用 Digi 配置应用程序或简单的串行终端应用程序来查找地址。
图 2-7 显示了 XBee 3 模块的底面(放大以便更好地观察)。请注意型号下面打印的数字。您一起使用它们来形成一个对每个 XBee 模块唯一的 64 位地址。许多出版物称之为无线电地址,分为两部分:高位和低位地址(或值)。例如,图中所示的地址,0013A200,是“高”地址,4192DA30是“低”地址。在接下来的例子中,我们将需要这些。
图 2-7
XBee 地址印在模块背面
Tip
经常看到 XBee 被称为模块或收音机。这些术语经常互换。当提到无线电本身的发射和接收能力时,我一般用模块和无线电来指代 XBee。
电台的地址用于确定要发送的消息的目标。在许多方面,它类似于 IP 地址,但在这种情况下,它是一个特定的无线电地址。
除了特定的 64 位无线电地址之外,ZigBee 网络在每个网络内使用分配给每个无线电的 16 位地址。此外,您可以指定一个简短的文本字符串来标识每个收音机。除此之外,还有一个个人区域网络(PAN)地址,可用于对网络中的无线电进行逻辑分组。最后,所有无线电必须在相同的信道(频率)上发射和接收。概括地说,当 XBee 无线电想要向另一个无线电发送消息时,它必须使用相同的频道,并设置目的地 PAN 和特定的 16 位无线电地址。在接下来的章节中,您将看到这些选项的作用。
ZigBee 网络
像以太网一样,ZigBee 网络基于预定义的网络堆栈,其中堆栈中的每一层负责数据消息的特定转换。与其他网络一样,ZigBee 网络支持消息路由、自组织网络创建和自愈网状拓扑。因此,需要无线电地址和 PAN 地址来支持这些特征。
通过增加每个节点(无线电)可以在网络中执行的不同角色,对网状拓扑的支持成为可能。以下列表从最复杂的角色开始,更详细地描述了每个角色:
-
协调器:每个网络需要一个协调器。该节点负责管理地址,形成和管理网络。所有其他节点在启动时搜索协调器并交换握手信息。
-
路由器:被配置为路由器的节点被设计为将信息传递(路由)到其他无线电。路由器通过加入网络和交换来自其他节点的消息来实现网状网络的修复。路由器通常由可靠的电源供电,因为它们必须是可靠的。因此,数据聚合节点将是路由器无线电模式的一个好选择。
-
终端设备:终端设备是向路由器节点和协调器发送或接收信息的节点。它的一个优点是正在进行的处理更少,因此功耗更低。终端设备支持睡眠模式,以进一步降低功耗要求。大多数传感器节点将被配置为终端设备。
只要网络中至少有一个协调器,就可以用任何方式配置 XBee 模块。要形成网状网络,只需使用几个路由器,其中一个或多个终端设备交换消息,路由器与协调器交换消息。图 2-8 显示了一个典型的网状网络。
图 2-8
ZigBee 网状网络
配置 XBee 模块有时会出错。发生这种情况时,诊断和纠正该问题会非常困难。我在这一章的结尾包括了一个故障排除部分,它将帮助你解决许多常见的问题。如果您遇到问题,请查看故障排除部分。
更新固件
开始配置 XBee 模块时,您应该做的第一件事是加载最新版本的固件并设置角色。在这种情况下,固件是指 XBee 的嵌入式微控制器的程序。如果您的系列 2 或 2.5 模块使用的是旧版本,或者您想尝试不同的配置,您只需更改固件。同样,如果您想将系列 3 模块与旧模块一起使用,您必须在系列 3 模块上加载不同的固件。例如,如果您想通过 802.15.14 协议同时使用系列 2 和系列 3,您应该在每个模块上安装相同的固件。
Digi 通过提供一个名为 XCTU 的优秀配置应用程序使这变得简单。只能使用 XCTU 应用程序加载固件。图 2-9 显示 XCTU 在没有连接任何模块的情况下运行。
图 2-9
XCTU 主窗口
正如你所看到的,XCTU 软件会给你友好的提醒和如何做事的提示。在下面的段落中,我们将看到如何联系和配置模块。您可以从以下 URL 下载最新版本的 XCTU 软件:
www.digi.com/products/embedded-systems/digi-xbee/digi-xbee-tools/xctu#productsupport-utilities
您将找到大多数平台的安装程序,包括 macOS、Windows 和 Linux,以及发行说明和许可文档的链接。只需下载适合您平台的安装程序,并使用该平台通用的方法进行安装。例如,在 Windows 上,您将运行并执行安装程序,而在 macOS 上,您必须首先从压缩文件中提取安装程序,然后执行安装程序。
一旦安装了软件并且启动了 XCTU 软件,就需要连接到 XBee 模块。要连接到 XBee,只需将 XBee 模块插入适配器,并将其连接到您的电脑。例如,如果您使用 SparkFun Explorer USB 加密狗,您只需首先将 XBee 模块插入加密狗。将加密狗插入 USB 插槽后,您应该会看到电源指示灯亮起。
一旦浏览器连接到您的计算机,您可以使用窗口左上角的任一按钮添加模块,如图 2-10 所示。左边的一个允许您通过指定模块的串行连接参数来添加模块,右边的一个扫描模块的所有串行连接。让我们使用添加模块按钮。
图 2-10
添加和扫描模块按钮
准备就绪后,点击添加无线电按钮。您应该看到一个对话框打开,允许您选择串行连接并设置连接参数。默认串行连接参数为 9600 波特、无流量控制、8 位、无奇偶校验、1 个停止位(也可写成 96008N1)。如果您发现您的 XBee 无法通信,那么它可能是以不同的波特率运行的。如果您更改波特率,您应该更改所有模块的波特率。图 2-11 显示添加模块对话框。如果您选择使用发现选项,则可以选择多个串行配置进行搜索。
图 2-11
添加模块对话框
只需选择串行连接,验证连接参数,然后单击 Finish。XCTU 需要几秒钟来建立连接,一旦建立连接,您将会看到模块出现在主窗口的左栏,如图 2-12 所示。较新的系列 3 模块将以黑色背景出现,较旧的系列模块将以蓝色背景出现。这允许您查看列表并快速识别您的旧模块。
图 2-12
添加了无线电模块
无线电列表还显示了分配给模块的角色。例如,路由器将出现一个“R ”,协调器将出现一个“C”图标在模块图像上。您还会看到协议图标根据加载的固件而变化。每个模块右侧的图标允许您关闭无线电连接,运行发现操作来查找同一网络上的其他模块,以及隐藏模块详细信息。图 2-13 显示了系列 3 模块的示例。
图 2-13
收音机入口—概述
请注意,在中间有附加数据,包括模块名称(您可以更改名称)、功能(加载的固件)、串行连接和媒体访问控制(MAC)地址。 5 回想一下,每个模块都有一个唯一的 MAC 地址,印在模块底部。请注意,还有一个带“R”的菱形。这表示模块当前被编程执行的角色。在这种情况下,它是一个路由器。协调器显示为“C ”,终端设备显示为“E”。这只是 XCTU 应用程序使 XBee 模块工作变得容易的一个非常好的方面。
接下来,我们可以单击左侧列中的模块,查看该模块的详细信息。图 2-14 显示了之前显示的系列 3 模块的无线电模块细节。
图 2-14
XCTU 无线电模块设置
收音机模块设置包括一长串您可以更改的内容。虽然大多数是您通常不会更改的,但有些是您在配置模块时需要更改的。例如,您可能需要设置在 ZigBee 网络中使用的角色和其他参数。幸运的是,这些设置被分组到类别中,您可以折叠或展开这些类别,以便更容易地找到您想要的设置。只需单击每个类别的三角形即可折叠或打开它。还有一个搜索框,可让您搜索设置。这是一个很好的接触。
让我们来看看如何做一个小小的改变。请注意,图中所示的系列 3 模块没有名称。每当对任何设置进行更改时,首先要找到设置,进行更改,然后将更改写入模块。在这种情况下,我们希望名称的参数是节点标识符或 NI。要在设置中找到它,只需在搜索框中键入 NI。然后您可以更改名称,如图 2-15 所示。
图 2-15
更改模块的名称标识符
请注意,我更改了节点标识符。要将更改写入模块,我单击设置旁边的铅笔图标。您也可以对其他参数进行更改,依次应用每一个参数,或者等待并更新所有更改,方法是点击写入按钮将所有更改写入模块。图 2-16 显示了应用了更改的无线电。
图 2-16
无线电模块名称已更改
命名模块也有助于在无线电模块列表中识别它们。事实上,XCTU 允许您以多种方式对模块进行排序,包括按名称排序。设置其他参数也同样简单。例如,您可能想要设置 PAN 地址、目的地址和节点标识符。
关于 XBee 模块,您还应该了解另一个方面——管理固件。如果您有较旧的模块,系统可能会提示您是否要下载旧固件。可能会出现如图 2-17 所示的对话框。您有三个下载选项:
-
寻找并安装新固件:仅下载 XCTU 和“已知”模块的更新。
-
安装旧固件:下载所有旧固件—如果您想使用任何旧系列模块,请选择此项。
-
从文件安装固件:从文件加载固件——如果您已经下载了自定义固件(很少使用),请使用此选项。
第一次使用 XCTU 或第一次使用旧系列模块时,您应该选择第二个选项并安装旧固件包。否则,第一个选项是默认的,事实上,XCTU 会自动检查更新(但是您可以在首选项中关闭它)。
图 2-17
下载固件
根据您的互联网连接速度,可能需要一段时间来下载旧固件,但您应该这样做,以保持您的模块是最新的。拥有较旧的固件还允许您在较新的系列 3 模块上加载正确的固件,以便它们在协议和功能上匹配。
XBee 模块有两种通信模式:AT 或 API。AT 表示模块通过其本地串行连接接受 AT 命令,并使用 Hayes 调制解调器命令集的衍生物以人类可读的格式显示信息。API 意味着模块被配置为通过其协议栈发送和接收数据。因此,当您想要使用控制台连接与模块通信以对其进行配置时,可以使用 AT 方法。API 方法将在本书的其余部分使用。所有模块必须使用相同的通信固件(AT 或 API)和版本用于其角色。
在本章中,我们将使用 AT 模式。为了确保您加载了正确的固件,您应该将您的模块插入浏览器,将浏览器插入您的计算机,然后在 XCTU 中添加无线电模块。然后点击更新按钮,如图 2-18 所示。
图 2-18
选择更新固件
您将看到一个对话框,允许您选择要下载(更新)到模块上的固件。图 2-19 显示了在 AT 模式下选择 ZigBee 协议固件的示例,以及 2.5 系列和旧模块的最新版本。XBee 系列 3 模块的固件以不同方式列出。
图 2-19
选择固件(2.5 系列及更旧版本)
图 2-20 显示了 XBee 系列 3 模块的固件选择。
图 2-20
选择固件(系列 3)
选择所需固件后,点击更新按钮。你可能需要回答一个或多个“你确定吗?”查询。这是因为更新固件会抹掉您所做的任何设置,包括任何编程。显然,只有在你确定不需要先保存任何东西的情况下,才说“OK”。一旦固件更新,你会得到一个“确定”对话框。关闭后,XCTU 会将收音机重新加载到您的收音机列表中。
现在您已经看到了如何管理 XBee 模块的固件和设置,让我们来看看一种更简单的方法来设置用户定义的值。
使用终端应用程序更改设置
XBee 模块的大多数设置都可以使用终端应用程序来更改(AT 模式)。在过去,我们必须使用控制台应用程序,但 XCTU 现在提供了一个控制台模式,效果非常好。该控制台具有使用 XBee 模块所需的所有功能。您可以连接、断开连接,甚至记录您的会话。您也可以发送命令或形成数据包,并将它们发送到模块。在本节中,我们将发送命令。该界面还允许我们看到十六进制的命令,这有助于诊断连接或解密数据。
XBee 模块有两种模式:命令和透明。命令模式由特殊命令+++启动,其中模块通过串行连接发回响应。透明模式是默认模式:模块向指定的无线电目的地发送数据。换句话说,当您想要与模块对话时,请使用命令模式;当您想要通过模块与另一个模块对话时,请使用透明模式。例如,通过 XBee 向另一个 XBee 发送数据使用透明模式。
因此,要在加载正确的固件后配置 XBee 模块,您需要打开一个终端应用程序并发出适当的命令。表 2-2 显示了一些用于配置 XBee 模块的常见 AT 命令。
表 2-2
常见 XBee AT 命令
|命令
|
描述
|
使用
|
反应
|
| --- | --- | --- | --- |
| +++ | 进入命令模式 | 将模块置于命令模式 | 好 |
| ATCN | 退出命令模式 | 返回透明模式 | 好 |
| AT | 注意力 | 查看该模块是否可用 | 好 |
| ATWR | 救援 | 将设置写入固件 | 好 |
| ATID | 平移 ID | 显示平移 ID | 平移 ID |
| ATID nnnn | 平移 ID | 更改平移 ID | 好 |
| ATSH | 64 位串行高电平 | 显示 64 位序列号的高部分 | 地址 |
| ATSL | 64 位串行低电平 | 显示 64 位序列号的低位部分 | 地址 |
| ATDH | 64 位目标高电平 | 显示 64 位目的地址的高位部分 | 地址 |
| ATDH nnnn | 64 位目标高电平 | 设置 64 位目标地址的高部分 | 好 |
| ATDL | 64 位目标低电平 | 显示 64 位目的地址的低位部分 | 地址 |
| ATDL nnnn | 64 位目标低电平 | 设置 64 位目的地址的低位部分 | 好 |
| ATMY | 16 位地址 | 显示协调员分配的 16 位地址 | 地址 |
| ATNI | 节点 ID | 显示文本字符串节点标识符 | 编号 |
| ATNI text | 节点 ID | 设置文本字符串节点标识符 | 好 |
| ATRE | 重置 | 将 XBee 重置为出厂默认值 | 好 |
Tip
有关 AT 命令的更多信息,请参见 XBee 3 手册 at www.digi.com/resources/documentation/digidocs/pdfs/90001539.pdf 。
一些命令需要一个值来设置变量。省略变量会导致显示当前值。除了+++以外的所有命令都需要你按下Enter才能执行。如果你按下+++命令而没有反应,再试一次,在每次尝试之间等待一两秒钟。您也可以尝试键入得快一点(或慢一点),直到命令模式切换生效。
Tip
所有数值都以十六进制值的形式输入。
为了演示这些命令是如何工作的,让我们使用 XCTU 的控制台对话框连接到一个已经加载了 ZigBee 路由器固件的模块。首先在左侧列表中选择你的收音机,然后点击控制台选项卡,如图 2-21 所示。
图 2-21
选择控制台模式
控制台窗格允许您使用左侧的三个图标执行三项操作。您可以打开一个连接(连接到一个模块)、记录会话(仅在连接后可选择)和分离,这允许您将控制台分离到它自己的窗口中。如果你正在跟随,现在继续断开控制台,然后点击打开按钮。图 2-22 显示了一个典型的配置会话,从连接到模块并显示其值开始,然后退出命令模式。
Note
第一次不带参数运行 ATDH、ATDL 或 ATMY 命令时,可能会看到结果为 0。这表明该值尚未设置。
图 2-22
在控制台中获取有关模块(AT 模式)的信息
Tip
在命令模式下,在模块返回透明模式之前,您只有 10 秒钟的时间输入命令。如果发生这种情况,当输入命令时,您将看不到任何响应。只需再次发出++命令,然后重新发出该命令。
接下来,图 2-23 显示了使用 XCTU 控制台设置目的地址(您想要连接的 XBee)及其 PAN ID 的会话。
图 2-23
在控制台中配置模块(AT 模式)
虽然 XCTU 应用程序有一个非常好的串行终端,运行得非常好(当在 at 模式下使用 XBee 模块时,我更喜欢使用它),但是您可以使用任何您想要的终端应用程序,比如 CoolTerm。你只需要设置串行连接来匹配你的 XBee(波特率等。)并连接到 XBee 所连接的串行端口。图 2-24 显示了在 macOS 上使用 CoolTerm 的示例。
图 2-24
使用 CoolTerm 配置模块(AT 模式)
现在,您已经知道了使用 ZigBee 协议组建无线网络所需的模块类型以及如何配置它们,您可以开始构建无线网络了。下一节将解释如何创建最基本的 XBee 项目:“Hello,World!”XBee 等价物。
更多信息
如果你想了解更多关于 XBee 模块以及它们如何通信的信息,Digi 网站( www.digi.com )是一个很好的资源。你也可以在谷歌上搜索“XBee”和“ZigBee ”,找到一些博客、操作页面等,这些都有助于展示使用 XBee-ZB 模块解决的不同项目和解决方案。
如果您正在使用较旧的系列模块,也有一些优秀的书籍可供您参考,以获得更多信息、项目想法等。我在这里列出了两个较好的标题:
-
构建无线传感器网络:使用 ZigBee、XBee、Arduino 和处理,作者 Robert Faludi (O'Reilly,2010 年),ISBN 978-0596807733
-
《XBEE 实验室实践手册:教你 XBEE 无线通信的实验》,作者乔纳森·泰特斯(纽尼斯,2012 年),ISBN 978-0123914040
但是,Digi 提供了一些优秀的资源,您应该考虑研究一下,包括 XCTU 文档、XBee 模块硬件手册和 ZigBee 手册。您可以在以下站点找到所有这些文档:
www.digi.com/support/productdetail?pid=5637&type=documentation
在我们开始使用 XBee 模块的第一个示例应用程序之前,让我们讨论一下 series 3 模块最强大的选项之一:MicroPython。
介绍 MicroPython
使用 Python 语言控制硬件已经有一段时间了。Raspberry Pi、pcDuino 和其他低成本计算机以及类似主板的用户已经拥有了使用 Python 控制硬件的优势。在这种情况下,他们在基于 Linux 的本地操作系统上使用了完整版本的 Python 编程语言。
然而,这需要构建特殊的库来与硬件通信。这些库旨在与通用输入输出(GPIO)引脚接口。GPIO 引脚通常出现在板上的一排或多排公引脚中。一些电路板使用母头引脚。
虽然这些电路板使那些想要开发电子项目的人成为可能,但它要求用户购买电路板以及键盘、鼠标和显示器等外围设备。不仅如此,用户还必须学习操作系统。对于那些不习惯 Linux 的人来说,这本身就是一个挑战。
MicroPython 的愿景是将学习 Python 的简单性与微控制器板的低成本和易用性结合起来,这将允许更多的人在艺术和科学项目中使用电子产品。初学者不必学习新的操作系统或学习更复杂的编程语言。答案是 MicroPython。
MicroPython 6 由 Damien P. George、Paul Sokolovsky 和其他贡献者创建和维护。它被设计成 Python 3 语言的精简、高效版本,并安装在一个小型微控制器上。由于 Python 是一种解释语言,因此(一般来说)比编译语言慢,所以 MicroPython 被设计得尽可能高效,以便它可以在通常比典型的个人计算机慢得多且内存少得多的微控制器上运行。
另一方面,Arduino 等微控制器板需要一个编译步骤,您必须在计算机上执行该步骤,并首先将二进制可执行文件加载到板上。相比之下,由于 MicroPython 的解释器直接在硬件上运行,我们不需要中间步骤来准备代码;我们可以直接在硬件上运行解释语言!
这使得硬件制造商可以制造小型、廉价的主板,在与微处理器相同的芯片上包含 MicroPython(通常情况下)。这使您能够连接到电路板,编写代码,并执行它,而无需任何额外的工作。
您可能会想,将 Python 3 缩减到适合内存有限的小芯片的大小,这种语言被剥离了,缺少了一些特性。这不可能比事实更进一步。事实上,MicroPython 是 Python 3 核心特性的完整实现,包括紧凑的运行时和交互式解释器。它支持读写文件、加载模块、与 GPIO 引脚等硬件交互、错误处理等等。最重要的是,Python 3 代码的优化允许它被编译成需要大约 256K 内存来存储二进制文件的二进制文件,并且运行时只需要 16K 的 RAM。
MicroPython 因此允许 Digi 在 XBee series 3 模块本身上放置函数式编程语言和解释器!是的,这意味着我们可以连接到我们的 XBee 系列 3 板,编写代码,并执行它。这为使用 XBee 模块和传感器网络打开了一个全新的世界。
Note
MicroPython 仅适用于 series 3 模块。
XCTU 有一个 MicroPython 控制台,可以用来连接到 series 3 模块。要使用 MicroPython,我们必须对模块进行一些修改。回想一下,在更改设置后,我们使用右侧的更新图标(铅笔)来设置更改。其中包括更改波特率(更快),启用 API + MicroPython 模式。图 2-25 显示了 XCTU 中的设置。
图 2-25
MicroPython 的安装模块
一旦这些设置被写入模块,只需将 XBee series 3 模块连接到 USB XBee Explorer,将其插入您的计算机,并将无线电添加到 XCTU。在那里,您可以单击工具图标并选择 MicroPython 终端。图 2-26 显示了选择。
图 2-26
打开 MicroPython 控制台
这将打开 MicroPython 控制台,您可以在其中发出 Python 命令。图 2-27 以无处不在的“你好,世界!”的形式展示了一个简单的例子程序。
图 2-27
示例 MicroPythonsession
这个例子是使用 ZigBee 系列 3 模块显示的。根据您使用的模块,MicroPython 的特性可能略有不同。表 2-3 显示了某些 3 系列模块的主要特性及其可用性。
表 2-3
XBee Series 3 按型号划分的 MicroPython 功能
|特征
|
XBee 3 蜂窝电话
|
XBee 3 ZigBee、DigiMesh 和 802.15.4
| | --- | --- | --- | | 数字输入输出 | 是 | 是 | | I2C | 是 | 是 | | 动力管理 | 是 | 是 | | Digi 远程管理器 | 是 1 | 不 | | 辅助 UART | 是 | 不 | | 实时时钟 | 是 | 不 | | 文件系统 | 是 | 是 | | 文件系统—并发文件写入 | 是 | 不 | | 文件系统—重命名 | 是 | 不 | | 文件系统—创建后编辑文件 | 是 | 不 | | 文件系统—删除 | 是 | 否 2 | | 文件系统—安全文件 | 是 | 不 | | 跨更新保留文件系统 | 是 | 不 |
我们将在下一章看到更多关于 MicroPython 的内容。但是首先,让我们通过一个简短的演示项目来看看 XBee 模块的运行情况,在这个项目中,我们使用 AT 模式通过终端连接进行设置和配置。
XBee 无线聊天室
对于这个示例,您需要两个 XBee 模块、两个 USB 适配器和所需的电缆,以及一台或两台计算机。您可以将一台电脑的每个模块连接到不同的 USB 串行端口。
此示例使用一个 series 2 和一个 series 3 XBee 模块来演示如何使用每个系列。如果只有一个系列,可以跳过演示其他系列的部分。最大的区别在于你如何加载固件。在 AT 模式下对模块编程是相同的。
这个项目有点像“你好,世界!”在硬件级别测试 XBee。您将使用两个 XBee 模块,配置为一个简单的点对点网络,带有一个协调器和一个路由器,而不是编写一个简单的程序来打印消息。在示例中,我包括了一个系列 2 和一个系列 3 模块,但是如果您愿意,也可以使用两个相同的系列。只需遵循以下章节中特定系列的说明。
您将把这两个模块都设置为使用 AT 固件,这样您就可以演示透明模式,并看到您以明文形式传递的消息。这是聊天成功的关键。在一个模块上键入的内容或输入的信息会出现在另一个模块上。酷吧。
加载模块的固件
你需要做的第一件事是为每个模块加载固件。回想一下,您使用 XCTU 应用程序来加载固件。我们将回顾为 2.5 系列和更早版本以及 3 系列模块加载固件的细节。
2.5 系列及更早版本
较旧的 2.5 系列和更早的模块具有针对三种角色之一预配置的固件。这由版本号表示。对于我在编写本章时使用的 XBee 模块,协调器的版本号是 20A7。前两位是角色,后两位是版本。如果你的模块有不同于 A7 的版本,这并不重要,只要它们有相同的版本。以下是一些主要角色及其价值:
-
20xx,协调器,AT/透明操作
-
21xx,API 操作协调员
-
22xx,路由器,AT/透明操作
-
23xx,路由器,API 操作
-
28xx,终端设备,AT/透明操作
-
29xx,终端设备,API 操作
系列 3
系列 3 模块的配置略有不同。这些模块对于 ZigBee 固件只有如下三种选择。要将 3 系列模块与旧的 ZigBee 模块一起使用,请确保加载 ZigBee 固件:
-
Digi XBee3 802.15.4 TH :当与使用较旧的 802.15.4 协议的其他模块一起工作时,使用此固件。
-
Digi XBee3 DigiMesh 2.4 TH :与使用 DigiMesh 协议的其他模块一起工作时,使用此固件。
-
Digi XBee3 ZigBee 3.0 TH :与使用 ZigBee 协议的其他模块一起工作时,使用此固件。
由于我们在本书中使用 ZigBee 协议,如果您使用任何 3 系列模块,您将需要加载 ZigBee 固件。目前最新版本是 1008。同样,拥有最新的并不重要,但保持最新也无妨。
您可能想知道如何为三个角色之一配置 series 3 模块。这是通过选择两种设置的组合来完成的。首先,我们选择设备角色 (CE)并将其设置为形成网络 (1)或加入网络 (0)。其次,我们选择睡眠模式 (SM)并将其设置为 0(对于路由器)或> 0(用作终端设备)。表 2-4 显示了帮助您选择正确设置的矩阵。
表 2-4
设置系列 3 角色
| |掌握
| | --- | --- | |
ZigBee 角色
|
设备角色(ce)
|
睡眠模式(SM
| | --- | --- | --- | | 协调者 | one | Zero | | 路由器 | Zero | Zero | | 终端设备 | Zero | > 0 |
例如,要使用 AT 模式将 series 3 模块设置为协调器,我们发出ATCE和ATSM命令。或者,更好的方法是,我们可以使用 XCTU 应用程序并在那里设置参数。
但是还有一个特定于系列 3 模块的设置。请注意,没有 AT 或 API 固件选项。这通过使用 API 使能 (AP)设置来控制。对于 AT 模式,将其设置为 0;对于 API 模式,将其设置为 1。您可以使用 ATAP 命令或使用应用程序来实现这一点。
Tip
更改设置时记得点击设置旁边的写入按钮,或者如果您已经更改了两个或更多设置,点击工具栏上的写入按钮。
为协调器加载固件
使用 XCTU 应用程序将第一个 XBee 模块配置为功能集的协调器。在这种情况下,我们将使用系列 3 模块。
连接模块并点击添加或发现按钮(在 XCTU 窗口的左上角)。按照对话框操作,一旦添加了模块并读取了其配置,就可以更新固件了。
点击更新图标,在对话框中选择 ZigBee 固件。图 2-28 显示了加载 ZigBee 3.0 固件的正确选择。一旦选定,点击对话框中的更新按钮。
图 2-28
为协调器加载固件(系列 3)
写过程完成后,必须设置设备角色和睡眠模式,如表 2-4 所示。您还需要确保将 API 模式设置为 0。下面的代码显示了如何使用终端连接来实现这一点。回想一下,我们可以点击 XCTU 界面右上角的终端图标,然后点击打开按钮开始一个会话。记住键入+++命令(在控制台日志的左侧),并在输入命令前等待几秒钟的响应。
+++
ATCE 1
ATSM 0
ATAP 0
ATWR
ATCN
如果您想使用 XCTU 应用程序设置参数,记得点击配置图标切换到配置模式(或使用菜单)。
为路由器加载固件
现在,让我们将第二个 XBee 模块配置为路由器 AT 功能集。在这种情况下,我们将使用系列 2 模块。
连接模块并点击添加或发现按钮(在 XCTU 窗口的左上角)。按照对话框操作,一旦添加了模块并读取了其配置,就可以更新固件了。
点击更新图标,在对话框中选择 ZigBee 路由器 AT 固件。图 2-29 显示了在固件中加载 ZigBee 路由器的正确选择。一旦选定,点击对话框中的更新按钮。
图 2-29
为路由器加载固件(2.5 系列及更早版本)
接下来,我们需要为每个模块设置目的地址,使它们相互指向对方。因此,点对点的命名。
捕获序列号
回想一下,XBee 无线电需要目标无线电的 64 位地址(序列号,也称为 MAC 地址)来发送数据。您需要在开始项目之前记录这些内容。花点时间记录下每个 XBee 模块的 64 位序列号。
如果您已经将 XBee 模块插入到它们的适配器中,那么使用 XCTU 应用程序就可以很容易地看到地址。图 2-30 显示了连接的系列 3 和系列 2 模块。注意显示的地址。我们将把每个模块的目的地设置为另一个模块的地址。
图 2-30
使用 XCTU 识别 64 位地址
如果您喜欢或需要该操作模式,您也可以使用终端应用程序,使用ATSH和ATSL命令向模块查询地址。
确定地址后,将信息写在表 2-5 中。 7 这里有您将使用的附加信息的空间,因此在您继续项目时请参考此表。
表 2-5
XBee 配置数据
|作用
|
串行高电平
|
串行低电平
|
平移 ID
|
节点 ID
| | --- | --- | --- | --- | --- | | 协调员在 | | | | | | 路由器在 | | | | |
现在,让我们从协调器开始配置 AT 模式参数。
配置协调器
要配置协调器,您需要将此无线电的目的地址设置为另一个无线电(路由器)的序列号。因此,您将协调器上的目的地址设置为路由器的地址。我们还需要为网络设置 PAN ID。
我们将使用 XCTU 应用程序来更改设置,但是如果您愿意,也可以使用终端并发出ATDH、ATDL和ATID命令。
您还必须选择要在网络上使用的 PAN ID。还是用标志性的 8088 吧。 8 在这种情况下,只要网络上的所有模块具有相同的 PAN ID,并且该值在 0000-FFFF(十六进制)范围内,您使用什么都没关系。还要将节点 ID 设置为 COORDINATOR,以便于识别。图 2-31 显示了协调器的配置会话。
图 2-31
配置协调器
配置路由器
要配置路由器,您需要将此无线电(路由器)的目的地址设置为另一个无线电(协调器)的序列号。与协调器一样,您将 PAN ID 设置为 8088。还要将节点 ID 设置为 ROUTER,以便于识别。图 2-32 显示了路由器的配置会话。
图 2-32
配置路由器
让聊天开始
就这样:您已经准备好开始聊天会话了。如果您一直在使用终端进行更改,那么您现在需要做的就是通过使用ATCN命令或者简单地等待 10 秒钟,将模块返回到透明模式。
一旦你为模块设置了目的地地址,它们就会“找到”彼此并开始聊天。要查看聊天的运行情况,只需单击其中一个模块并打开一个终端连接。您应该点击分离按钮,这样您就可以点击另一个模块并为该模块打开一个终端。
接下来,在每个终端点击打开按钮。此时您可能看不到任何事情发生,因为模块正在等待来自另一个模块的数据。我们可以自己点击控制台输出的左侧。继续在那里键入一些东西。
如果您的配置有效,您应该会看到文本从一个终端出现在另一个终端,反之亦然。如果你这样做了,那么恭喜你——你已经建立了你的第一个 XBee 网络(尽管是一个非常简单的点对点网络)。图 2-33 显示了我使用 XCTU 从电脑上运行的测试结果。这个终端功能很好,因为它对消息进行了颜色编码。如果您自己运行它,您应该看到红色文本是文本接收,蓝色是文本发送。
图 2-33
成功聊天
为了加分,拔掉你的 USB 适配器,把它们从一台电脑切换到另一台电脑;然后重启你的终端程序。注意到什么特别的吗?这是一个棘手的问题,因为您应该看到聊天示例像以前一样工作。在这种情况下,谁是协调者并不重要;因为您将值写入 XBee 非易失性存储器,所以即使拔掉电源,模块也会“记住”它们的设置。很好,是吧?
Is Point-to-Point Good Enough?
您可能想知道是否可以在传感器网络中使用点对点网络。简短的回答是,在某些情况下你可以。例如,如果您有少量不太可能脱机的节点,您也许能够形成一个具有点对点网络的网络。在这种情况下,您将形成一个星型拓扑网络。
但是,也有一些限制,例如,如果中间的一个节点发生故障,它会将该节点一侧的所有节点从另一侧孤立出来。您也不能形成多点连接,广播可能需要额外的编程才能完成。由于这些原因以及更多原因,复杂的传感器网络可以从使用网状拓扑中受益。
为了更多乐趣
如果您想使用 chat 示例更多地尝试点对点网络和 AT 固件,请尝试向网络添加第三个 XBee。将其连接到您的路由器节点,并键入一些数据。它出现在哪里:协调器还是路由器?再次尝试,将新模块连接到协调器。文本是否出现在您期望的位置?提示:使新模块也成为路由器,并将其目的地址设置为第一个路由器。
Got Hub?
如果你想测试你的 XBee 网络,但你的电脑没有足够的 USB 端口,端口过于接近, 9 或者你不想使用第二台(或第三台)电脑,USB XBee 探索者将使用一个有电源的集线器,甚至一些更好的无电源集线器,如下所示。
然而,我发现有些集线器不起作用。我有一个非常好(也非常贵)的 USB 3 集线器,除了我的 XBee Explorer 板之外,它对任何东西都很好。因此,您的里程可能会有所不同。奇怪的是,我的老式 USB 2.0 集线器工作良好,并有足够的空间来放置 XBee Explorer 板。
构建 XBee-ZB 网状网络
现在,您已经知道了什么是 XBee 模块,如何选择项目中使用的模型,以及如何配置它们以在点对点网络中发送和接收数据,让我们来看看更复杂、更适合传感器网络的一些内容。
在这个项目中,您将配置三个 XBee 模块:一个作为协调器,另一个作为路由器,最后一个作为终端设备。我将为协调者使用一个系列 3 模块,但是像以前一样,它不是必需的,并且该项目将与早期的模块一起工作。
但是,您可以使用形成网状网络所需的 API 固件,而不是 AT 固件。目标不是深入探究 API 固件;相反,它是为了看看如何使用 XBee 模块通过网络传输数据。
回想一下,API 固件被设计为实现完整的 ZigBee 协议,这意味着数据消息被封装在具有报头层的分组中。换句话说,信息是以二进制数据的形式传输的,而不是像您在 AT 固件中看到的文本。
API 固件和 ZigBee 协议有很多内容。幸运的是,您不必为了使用它而深入了解细节。但是,知道数据包是如何形成的确实有助于您诊断和调试数据消息。在您阅读本书的过程中,我将介绍一些经常遇到的包。
如果您想了解有关 ZigBee 协议及其多种数据包格式(称为帧类型)的更多信息,请参阅以下资源之一:
-
构建无线传感器网络:使用 ZigBee、XBee、Arduino 和处理,作者 Robert Faludi (O'Reilly,2010),ISBN 978-059680773310
-
ZigBee 射频模块,“Digi 国际”,2018,
www.digi.com/resources/documentation/digidocs/pdfs/90000976.pdf
*### 加载模块的固件
您需要做的第一件事是使用 XCTU 应用程序加载每个模块的固件。在本例中,我们将为协调器使用一个系列 3 模块,为路由器和终端设备使用两个系列 2 模块。
由于我们使用的是 series 3 模块,回想一下,我们只需将 API 模式(AP)设置为 1 即可打开 API 模式。对于早期的模块,我们必须加载正确的固件。更具体地说,我们在一个模块上加载终端设备 API 函数集,在另一个模块上加载路由器 API 函数集,如果我们没有 series 3 模块,我们将需要在第三个模块上加载协调器 API 函数集。
配置 XBee 模块
建议在开始新项目时,将 XBee 模块重置为出厂默认值。您可以在配置页面上使用 XCTU 应用程序来完成此操作。点击默认按钮设置出厂默认,然后点击是确认。这就是你需要做的!您也可以在 AT 命令模式下从终端使用ATRE命令。
但是那些地址和身份证什么的呢?简单来说,你不需要他们。模块将自动连接到协调器(或路由器),协调器将为每个模块分配 16 位地址。显然,这比点对点模式更容易配置。
虽然这是真的,但使用 API 固件进行实验也更加困难。回想一下,API 固件以二进制形式发送和接收数据消息。为了观察这个网络的运行,您需要形成称为传输请求包的特殊包。
形成测试消息
测试消息是一个简单的数值,嵌入在一个称为传输请求包的包中。(在某些文档中,它被称为传输请求帧。)数据包需要非常特殊的格式。
如果您曾经使用过以太网中遇到的低级数据包(TCP 数据包)或 MySQL 客户端协议等其他通信协议,那么您应该已经熟悉了这些基本概念。然而,如果你没有,数据的布局可能看起来很奇怪。表 2-6 显示了示例传输请求包的布局。后面会有更详细的描述。
表 2-6
传输请求包
|田
|
抵消
|
例子
|
描述
|
| --- | --- | --- | --- |
| 定界符 | Zero | 7E | 数据包开始定界符 |
| 长度 | one | 00 10 | 长度和校验和之间的字节数 |
| 框架类型 | three | 10 | 请求传输 |
| 框架 ID | four | 01 | UART 数据帧 |
| 64 位目标地址 | five | 00 00 00 00 00 00 00 00 FF FF | 协调器的 64 位地址 |
| 目的地址 16 位 | Thirteen | 00 00 | 16 位目的地址 |
| 广播半径 | Fifteen | 00 | 最大跳数 |
| 选择 | Sixteen | 00 | 选择 |
| 射频数据 | Seventeen | 99 99 | 数据有效载荷 |
| 校验和 | Nineteen | BC | 0xff 减去包中字节的总和 |
该数据包的重要部分是长度、地址和数据有效载荷。这些是你最想改变的部分。在这种情况下,64 位地址是协调器的默认地址。协调器使用默认设置获取该地址。16 位地址也是如此。该示例生成十六进制的有效负载 99 99(十进制的 39321)。因为您对此仅使用了 2 个字节,所以长度(长度部分之后的数据包所有部分的字节长度,直到但不包括校验和部分)为 16 (1+1+8+2+1+1+2)。校验和的计算方法是 0xff 减去长度和校验和之间的字节数的 8 位和。
听起来很复杂?可以的。幸运的是,您不需要经常手动这样做。事实上,当您使用 XBee 发送数据时,您在与 XBee 本身通信时使用的编程库会为您构建这些包。您用来从 Arduino 和 Raspberry Pi 与 XBee 对话的库也将使您的生活变得更加轻松。
那么,你为这个项目做什么?你自己创建包吗?如果你担心计算字节和计算校验和,不用担心。Digi 已经用一个叫做帧生成器工具的漂亮的包创建对话框构建了 XCTU 应用程序。你也可以用它来创建任何你想要的 ZigBee 包。
当您打开终端并连接到要发送数据包的模块时,您可以使用此功能。只需点击界面发送帧部分的添加新帧按钮。图 2-34 显示了一个终端会话的例子。
图 2-34
添加新的帧数据包
点击添加帧按钮后,您将看到一个对话框,允许您使用帧生成器工具编辑现有数据包或添加新数据包。如图 2-35 所示,要添加新的数据包,点击使用‘帧生成器’工具创建帧按钮。
图 2-35
打开框架生成器工具
接下来,您将看到一个允许您填充数据包的对话框,但是首先您必须选择数据包类型。对于这个项目,我们希望选择发送请求包(0x10)。该对话框将自动刷新,显示该数据包类型的正确字段。图 2-36 显示了发送请求包的对话框。回想一下,该数据包用于向另一个模块发送数据。
图 2-36
创建发送请求包
注意这里我用00 00 00 00 00 00 FF FF设置了目的地址。这是广播地址,它将数据包发送到所有模块。如果您想将数据包发送到一个特定的模块,您需要填写该模块的 64 位 MAC 地址。因为我们使用的是广播,所以我们会看到网络中的所有模块都收到了这个数据包。
请注意,我还在 RF 数据框中添加了一条消息。我只是简单地输入了“你好”这个词。因此,这是一个你好,世界!打字练习。
花点时间打开一个终端,自己创建这个框架。新 XCTU 接口的一个非常好的地方是它会保存你创建的数据包帧,这样你就可以一次创建并测试几个。现在,让我们看看如何设置网络。
测试网络
现在是时候启动 XBee 模块了。从协调器开始,然后是第一台路由器,最后是终端设备。然后,您可以将终端应用程序连接到所有模块。
Wait, What About the Baud Rate?
如果您一直在阅读,并且想知道为什么我没有演示如何设置您的模块的串行选项(比如波特率),以及您是否需要设置它们,答案就在 XCTU 应用程序如何工作。它允许你以任何你想要的参数使用模块。另一方面,如果您想使用不同的终端应用程序,您可能需要更改参数。幸运的是,这在 XCTU 应用程序中很容易做到。只需进入每个模块的配置,查找如下所示的串行参数。
更改所需的设置,然后将更改写入模块。但是同样,您不需要这样做,也不需要让所有的模块使用 XCTU 应用程序的相同连接参数。酷吧。
对于这个例子,我使用了一台带有 XCTU 的计算机,使用终端模式连接到所有三个模块。将所有模块连接到终端应用程序后,应该给模块五到十分钟的时间进行自我配置,然后再继续。
那么,你怎么知道一切都正常呢?嗯,其实挺简单的。XCTU 有一个你可以使用的网络模式。通过阅读所有正在创建网络图的模块,您将看到网络的布局。网络按钮位于 XCTU 窗口的右上角。接下来,点击扫描按钮。一旦你点击那个按钮,可能需要一点时间来发现网络,但是它应该向你显示一个类似于图 2-37 的布局。
图 2-37
XCTU 的网络模式
请注意,该图显示了每个模块的 64 位地址(MAC 地址),以及显示为实线的通信路径。很好。
好了,你已经等了,一切都应该正常了。我们来看看。在任一模块连接上,点击添加帧按钮,创建一个传输请求包,其广播地址如图 2-36 所示,包括“Hello”的有效载荷。务必点击添加帧按钮保存数据包。
要发送数据包,在左侧选择它并点击发送所选帧按钮。如果您正在观察其他模块中的终端,您可能会看到一些数据包经过。这是因为默认情况下会显示所有数据包,但是您可以使用过滤器来减少混乱。我把它作为一个练习——只需点击过滤器按钮,并选择您想要查看(或不查看)的数据包类型。所有模块收到数据包后,您可以单击数据包并查看数据。图 2-38 显示了输入命令的结果。
图 2-38
所有模块接收到的广播数据包
请注意每个模块上数据包周围的方框。在这里,我们看到数据包是从协调器发出的,并在其他模块上被接收。但是,数据有效载荷在哪里呢?它就在那里,就嵌在信息里。在这种情况下,它是十六进制形式,十六进制的 Hello 是48 65 6C 6C 6F。看看你能否在图像中找到它。
广播消息可以方便地向所有传感器节点发送断电或睡眠模式命令,或者向所有数据聚合节点发送命令,以将其数据保存到正在使用的介质中。
接下来,让我们创建一个新的传输请求消息,并为其他模块之一指定 64 位地址。为数据添加您想要的任何内容,然后发送帧。例如,我将目的地址设置为我的示例网络中的路由器(0013A200408CCD0F)。我还在射频数据字段中输入了一个随机数(90125 11 )。图 2-39 显示了我创建并添加的框架。
图 2-39
传输带有目的地的请求帧
当我发送数据包时,它显示在路由器上,但没有显示在其它模块上。这是因为这是发送到特定模块的消息;其他人忽略了这条信息。图 2-40 显示了“发送”模块在上方而“接收”模块在下方的结果。
图 2-40
发送和接收的帧
请注意帧日志左侧的小箭头。指向右边的箭头是发送事件,指向左边的箭头是接收事件。不错吧。
Tip
您只看到了 XCTU 应用程序的一小部分功能。您应该花些时间阅读手册,了解更多关于它的功能。例如,精明的用户可能想了解 XBee 模块的无线功能。
至此,您已经看到了如何将消息从一个模块直接发送到协调器,以及如何将广播消息发送到所有模块。如果您能够在自己的 XBee 模块上复制或执行类似的操作,那么恭喜您!你现在有一个非常简单的无线网状网络。
虽然该项目不包含任何传感器节点,但如果您将终端设备视为传感器节点,将键盘视为发送数据的传感器,您就可以看到典型传感器网络的运行情况。在这种情况下,默认情况下,终端设备将其数据包发送到协调器,如果需要,可以广播数据。将所有数据发送到协调器也是关于如何用 XBee 模块配置数据聚集节点的线索。在接下来的章节中,你将建立在这个前提之上。
为了更多乐趣
如果您想更多地练习创建测试包,请尝试从路由器发送网络节点检测命令,看看您会得到什么。提示:您需要一个ATND命令。
部件购物清单
你需要一些组件来完成本章中的项目。表 2-7 列出了它们。
表 2-7
所需组件
|项目
|
供应商
|
是吗?成本美元
|
所需数量
|
| --- | --- | --- | --- |
| XBee-ZB (ZB)系列 2、2.5 或 3 | www.sparkfun.com | 24.95 | 1 |
| xbee 浏览器 | www.sparkfun.com/products/11812 | 3.95 | 1□□□□□□□□□□□□□□□□□□ |
| usb 适配器 | www.adafruit.com/product/247 | $29.95 | 1 |
你只需要三个 USB 适配器。
每个 XBee USB 适配器需要一个-加密狗不需要。
确保获得正确的电缆。还记得你需要三个匹配的 XBee-ZB 模块。适配器板不必相同,但您应该有三个。
Cable Trouble Solution
如果你像我一样,有许多 USB 项目,你可能会遇到的第一个挫折是 USB 连接器选择的随机性。好像每次我买一个新的组件都需要一根不同的 USB 线!我没有随身携带一套电缆,而是从 SparkFun 找到了一个解决方案,让我的生活变得更加轻松。USB Cerberus 电缆( www.sparkfun.com/products/12016 )的一端包括一个标准的 USB A 型插头连接器,另一端包括一组三个通用连接器(B、mini-B 和 micro-B)。我建议为您的每个电子设备套件购买一个。
另一个令人沮丧的问题是用 USB 集线器之类的东西给设备供电。再说一次,我过去常常带着一堆电源线来给我所有的组件供电。在这种情况下,SparkFun 凭借其 Hydra 电源线( sparkfun.com/products/11579 )再次出手相救。该电缆的一端有一个标准的 USB A 型连接器,另一端有一组三个连接器(用于 Arduino、JST 和鳄鱼夹的筒形插头)。非常酷。
故障排除提示和常见问题
如果你在运行这两个项目时遇到问题,不要难过,也不要放弃!尽管它们体积小巧,功能强大,但如果配置不正确,这些小害虫会给你带来很多麻烦。本节探讨了解决一些更常见问题的一些最佳实践。
要检查的内容
以下是帮助您确定问题所在以及如何解决问题的提示列表:
-
布线:这听起来可能有点傻,但是请检查以确保您的所有模块都正确供电——无论是通过主机微控制器还是 USB 适配器(或 USB 集线器!).您会惊讶地发现,告诉您的操作系统弹出 USB 加密狗是多么容易。如果发生这种情况,很可能您的适配器的所有电源指示灯都亮起,但终端无法连接。尝试拔下电缆,然后重新插入。还要检查串行端口,因为如果从一个端口移动到另一个端口,一些操作系统可能会重新分配串行端口。
-
插上电源了吗?您还应该检查模块是否以正确的方向插入其插座,并且没有针脚被跳过(错位)。
-
串行设置:如果您使用的终端与 XCTU 中的不同,请检查您的波特率。如果您在 XBee 上更改了它,您的终端应用程序可能没有存储该设置。如果您想要更改设置,请确保为所有 XBee 模块以及终端应用程序设置相同的设置。
-
AT 地址:如果您正在使用 AT 固件构建点对点系统,请务必检查您的地址!请记住,目的地址需要指向您希望发送数据的模块的地址(
ATDH/ATDL)。确保使用ATWR命令保存数值。 -
版本:最好确保你使用的固件版本在所有模块上都是一样的。有些版本与其他版本不兼容。最好总是使用相同的版本。
-
XBee 死了吗?如果你的 XBee 模块不能被你的终端应用程序读取,或者它停止响应 XCTU,你可能遇到了一些人所说的阻塞,这使得该模块除了作为门挡或砖块之外毫无价值。如果发生这种情况,请尝试重置模块。如果您的适配器没有重置按钮(只有少数有),您可以连接适配器,然后轻轻地(非常轻轻地)移除模块并重新插入。当模块开始响应时,重新加载固件。极端情况见
www.instructables.com/id/Restoring-your-broken-XBee/。 -
旧值持续恢复:如果您在 AT 固件中更改了您的设置,但即使在您使用
ATWR后旧值仍持续恢复,请使用重置命令(ATRE)将所有值恢复至出厂默认值。
常见问题
以下是您可能会遇到的一些更常见的情况以及如何应对:
-
AT 命令不起作用:如果
+++命令不能唤醒模块,确保模块加载了 AT 固件。我忙乱了将近 15 分钟,我以为这是一个死模块,才发现它加载了 API 固件。不要被那个自我诱导的恶作剧所欺骗! -
AT 模式的奇怪错误:确保你的模块配置了相同版本的 AT 固件。您可以使用
ATVR命令来检查每个模块。 -
设置丢失或恢复:最常见的错误之一是做了所有的设置却没有用
ATWR写值。您需要使用此命令来保存这些值。在您完成此操作并返回透明模式之前,XBee 模块可能无法工作。 -
无法使用退格键:尝试使用 AT 固件输入带有值的命令可能会非常令人沮丧,因为退格键在大多数终端应用程序中不起作用。出现错误时,请按 Enter 键,然后再次尝试该命令。始终使用命令的显示选项(没有值的命令)检查设置。
-
API 固件不工作:如果您确定您的所有模块都配置了相同版本的 API 固件,请尝试拔下所有模块,首先插入协调器,然后插入路由器,最后插入终端设备。协调器可能需要长达 10 分钟的时间将所有节点加入网络。
你也可以访问罗伯特·法卢迪的网页了解 XBee 的常见错误( www.faludi.com/projects/common-xbee-mistakes/ )。他列举了很多你对 XBees 不熟悉时会出错的地方,以及如何配置。正如他所说,它们并不像看起来那样不可靠或古怪。大多数怪癖是用户错误的结果。可悲的是,事实并非总是如此。
最后,使用 Digi 网站及其知识库( www.digi.com/support/kbase/ )。外面有大量的信息。可能有人遇到过类似的问题,简单搜索知识库和论坛就能找到解决方案。
摘要
哇,这一章我们讲了很多内容。向您介绍了 XBee-ZB 模块和 ZigBee 协议,并尝试了 AT 和 API 固件。您还了解了大量关于 XBee 的知识,它的许多特性包括 series 3 模块的一些 MicroPython 特性。虽然看起来我已经讨论了很多,但事实是您才刚刚开始了解 XBee 以及如何在您的传感器网络中使用它。
在本书的剩余部分,我将回到 XBee 主题。第四章探讨如何用 XBee 模块托管传感器,第五章探讨如何用 Raspberry Pi 托管传感器,第六章探讨如何用 Arduino 微控制器托管传感器。如果你喜欢本章的项目,下一章的项目可能会更有趣,因为你可以看到一个真正的传感器在工作。
但是首先,让我们了解更多关于编程 MicroPython 的知识。下一章将介绍 MicroPython 以及一个简短的教程。让我们开始编码吧!
Footnotes 1ZigBee 基于 802.15.4 协议,提供电源管理、寻址和错误控制以及网络功能。
2
更多信息请参见 http://en.wikipedia.org/wiki/IEEE_802.15.4 。
3
有时称为电缆替换,因为它无需电缆就能有效地将两台设备连接在一起。
4
系列 2 模块有五个用于专业模块的天线选项和四个用于标准模块的天线选项。
5
https://en.wikipedia.org/wiki/MAC_address
6
版权所有 2014-2017,达米安·p·乔治,保罗·索科洛夫斯基,以及贡献者。最后更新于 2017 年 3 月 05 日。
7
如果你不想写在你的书上,拿一张纸做一个类似于表 2-5 的图表来储存信息。如果纸很薄,你可以把它折叠起来当书签用。
8
这个数字对你有什么意义吗?提示:IBM PC。
9
或者,它们在地板上,你不想倒立着把它们插上。
10
不包括系列 3 模块。
11
一些经典摇滚粉丝可能会声称《随机数》有更大的故事。给你一个提示:是的,确实如此。
*
三、MicroPython 编程
现在我们已经对 XBee 模块有了基本的了解,我们可以学习更多关于 MicroPython 编程的知识,这是一种非常健壮和强大的语言,可以用来编写非常强大的应用程序。掌握 MicroPython 非常容易,有些人可能认为使用它不需要任何正式的培训。这在很大程度上是正确的,因此您应该只需要一点点语言知识就能够编写 MicroPython 脚本。
鉴于 MicroPython 是 Python,我们可以先通过我们 PC 上的例子来学习 Python 语言的基础。因此,本章介绍了 Python 编程基础的速成课程,包括对一些最常用语言特性的解释。因此,本章将为您提供理解互联网上可用的 Python 物联网项目示例所需的技能。本章还通过可以在 PC 上运行的例子演示了如何用 Python 编程。所以,让我们开始吧!
Note
在本章中,我使用术语 Python 来描述同时适用于 MicroPython 和 Python 的编程概念。MicroPython 特有的概念使用术语 MicroPython。
现在让我们学习一些 Python 编程的基本概念。我们将从语言的构件开始,如变量、模块和基本语句,然后进入更复杂的流控制和数据结构的概念。虽然这些材料看起来很仓促,但是本 Python 教程只涵盖了该语言的最基础知识,以及如何在 PC 和 XBee 模块上使用它。它旨在让您开始为 XBee 编写 MicroPython 代码。
如果你知道 Python 编程的基础,请随意浏览这一章。但是,我建议您完成本章末尾的示例项目,尤其是如果您没有编写过很多 Python 应用程序的话。
下面几节介绍了 Python 编程的许多基本特性,您需要了解这些特性才能理解本书中的示例项目。
Note
XBee(以及类似的)模块和主板上的 MicroPython 不支持 PC 版 Python 上可用的完整函数库。但是,您需要在 XBee 模块上使用的所有工具都可以在 MicroPython 中找到。无论如何,在 PC 上学习 Python 是获得编程 XBee 模块所需技能的一个很好的方法。
在开始 Python 编程之前,让我们讨论一下 MicroPython 有哪些可用的特性和库,以及常见的限制。
MicroPython 的特性和局限性
虽然 MicroPython 看起来和感觉上完全像 Python,但是有一些东西 MicroPython 在 Python 3 语言中没有实现。下面几节将向您介绍使用 MicroPython 能做什么,不能做什么。
MicroPython 功能
MicroPython 最大的特点当然是运行 Python。这允许你创建简单的、有效的、易于理解的程序。我认为,这是它相对于 Arduino 等其他主板的最大优势。下面的列表是 MicroPython 支持的一些特性。我们将在本书中更详细地了解这些特性:
-
互动解释器(Interactive interpreter): MicroPython 主板内置了一个特殊的互动控制台,你可以通过 USB 电缆(或者在某些情况下,通过 Wi-Fi)连接到主板来访问它。这个控制台被称为读取-评估-打印循环,它允许您键入代码并一次执行一行。这是一个很好的方法来原型化你的代码或者只是在你开发的时候运行一个项目。
-
Python 标准库 : MicroPython 也支持许多标准 Python 库。总的来说,你会发现 MicroPython 支持 80%以上最常用的库。其中包括解析 JavaScript 对象符号(JSON)、 1 套接字编程、字符串操作、文件输入/输出,甚至正则表达式支持。
-
硬件级库 : MicroPython 内置了一些库,允许您直接访问硬件来打开或关闭模拟引脚、读取模拟数据、读取数字数据,甚至使用脉宽调制(PWM)来控制硬件,这是一种通过快速调制设备功率来限制设备功率的方法,例如,使风扇转速比全功率时慢。
-
可扩展 : MicroPython 也是可扩展的。对于需要在底层(用 C 或 C++)实现一些复杂的库并在 MicroPython 中包含新库的高级用户来说,这是一个很好的特性。是的,这意味着您可以构建自己独特的代码,并使其成为 MicroPython 特性集的一部分。
回答你的问题“但是,我能用 MicroPython 做什么?”,答案还挺多的!您可以控制连接到 XBee 模块的硬件,向其他节点发送数据,等等。您可以连接的硬件包括打开和关闭 led,控制伺服系统和读取传感器。您可以创建任何形式的传感器节点,就像使用 Raspberry Pi 或 Arduino(或其他 MicroPython 板)一样。
但是如果你还没有任何 MicroPython 板,你可以在 https://micropython.org/unicorn/ 查看在线 MicroPython 解释器。
然而,在芯片上运行 MicroPython 有一些限制。
MicroPython 限制
MicroPython 最大的限制是易用性。Python 的易用性意味着代码是动态解释的。尽管 MicroPython 得到了高度优化,但对解释器来说还是有损失的。这意味着需要高精度的项目,如高速数据采样或通过连接(USB、硬件接口等)进行通信。)可能无法以 PC 速度运行。对于这些领域,我们可以通过用处理低级通信的优化库来扩展 MicroPython 语言来克服这个问题。
MicroPython 使用的内存也比 Arduino 等其他微控制器平台多一点。通常,这不是一个问题,但是如果你的程序开始变大,你应该考虑一下。使用大量库的大型程序消耗的内存可能会超出您的预期。这又一次与 Python 的易用性有关——这是要付出的另一个代价。
最后,如前所述,MicroPython 没有实现所有 Python 3 库的所有特性。但是,您应该会发现它拥有构建物联网项目所需的一切(甚至更多)。
现在,让我们学习用 Python 编程吧!
基本概念
Python 是一种高级的、解释性的、面向对象的脚本语言。Python 的最大目标之一是拥有一个清晰、易于理解的语法,读起来尽可能接近英语。也就是说,即使你没有学过 Python 语言,你也应该能够阅读和理解 Python 脚本。Python 也比其他语言有更少的标点符号(特殊符号)和更少的语法机制。下面列出了 Python 的一些关键特性:
-
解释器在运行时处理 Python。不使用外部(单独的)编译器。你只需通过 Python 解释器“运行”你的代码。
-
Python 通过类的方式支持面向对象的编程结构。
-
对于初级程序员来说,Python 是一种很好的语言,并且支持各种应用程序的开发。
-
Python 是一种脚本语言,但可用于广泛的应用。
-
Python 非常受欢迎,在全世界范围内使用,这给了它一个巨大的支持基础。
-
Python 关键字少,结构简单,语法定义清晰。这使得学生能够很快学会这门语言。
-
Python 代码定义更清晰,肉眼可见。
Python 可以在你可能遇到或使用的几乎所有平台上下载(python.org/downloads)——甚至是 Windows!Python 是一种非常容易学习的语言,它的结构非常少,甚至有点难学。与其抛出一个示例应用程序,不如让我们以类似 Python 的方式来学习 Python 的基础知识:一步一步来。
Tip
如果你还没有在你的电脑上安装 Python,你应该现在就安装,这样你就可以运行本章中的例子。
代码块
你应该学习的第一件事是 Python 不像其他语言那样使用用符号划分的代码块。更具体地说,对于诸如函数、条件或循环之类的构造来说,局部的代码是使用缩进来指定的。因此,清单 3-1 中的行是缩进的(通过空格或制表符),这样开始的字符就可以与结构的代码体对齐。清单 3-1 展示了这个概念的实际应用。
Caution
如果缩进不一致,Python 解释器会抱怨并产生奇怪的结果。
if (expr1):
print("inside expr1")
print("still inside expr1")
else:
print("inside else")
print("still inside else")
print("in outer level")
Listing 3-1Using Code Blocks
这里我们看到一个条件或 if 语句。注意函数调用print()是缩进的。这向解释器发出信号,表明这些行属于它上面的结构。例如,提到expr1的两个 print 语句构成了 if 条件的代码块(当表达式的值为 true 时执行)。类似地,接下来的两个 print 语句构成了 else 条件的代码块。最后,非缩进的行不是条件行的一部分,因此在 if 或 else 之后执行,这取决于表达式的计算。
如您所见,缩进是编写 Python 时需要学习的一个关键概念。尽管这很简单,但是在缩进中出错可能会导致代码意外执行,或者解释器出现更糟糕的错误。
Tip
在讨论 Python 时,我将“程序”和“应用程序”与“脚本”互换使用。虽然从技术上讲 Python 代码是一个脚本,但我们经常在“程序”或“应用程序”更合适的上下文中使用它。
有一个你会经常遇到的特殊符号。注意前面代码中冒号(:)的使用。这个符号用来终止一个构造,并向解释器发出信号,说明声明已经完成,代码块的主体跟在后面。我们把它用于条件、循环、类和函数。
评论
任何编程语言中最基本的概念之一是用不可执行的文本注释源代码的能力,这不仅允许您在代码行之间做笔记,还形成了一种记录源代码的方法。
要向源代码添加注释,请使用井号(#)。在行首至少放置一个,为该行创建一个注释,为后面的每一行重复#符号。这将创建所谓的块注释,如图所示。注意,在清单 3-2 中,我使用了不带任何文本的注释来创建空白。这有助于提高可读性,并且是块注释的常见做法。
#
# Beginning Sensor Networks, 2nd Edition
#
# Example Python application.
#
# Created by Dr. Charles Bell
#
Listing 3-2Adding Comments to Source Code
您也可以将注释放在源代码所在的同一行。编译器将忽略从井号到行尾的所有内容。例如,以下代码显示了记录变量的常见样式:
zip = 35012# Zip or postal code
address1= "123 Main St." # Store the street address
算术
您可以在 Python 中执行许多数学运算,包括常见的原语以及逻辑运算和用于比较值的运算。与其详细讨论这些,我在表 3-1 中提供了一个快速参考,显示了操作和如何使用操作的例子。
表 3-1
Python 中的算术、逻辑和比较运算符
|类型
|
操作员
|
描述
|
例子
|
| --- | --- | --- | --- |
| 算术 | + | 添加 | int_var + 1 |
| | – | 减法 | int_var – 1 |
| | * | 增加 | int_var * 2 |
| | / | 分开 | int_var / 3 |
| | % | 系数 | int_var % 4 |
| | – | 一元减法 | –int_var |
| | + | 一元加法 | +int_var |
| 逻辑学的 | & | 按位 and | var1&var2 |
| | | | 按位或 | var1|var2 |
| | ^ | 按位异或 | var1^var2 |
| | ~ | 逐位补码 | ~var1 |
| | and | 逻辑与 | var1 and var2 |
| | or | 逻辑或 | var1 or var2 |
| 比较 | == | 平等的 | expr1==expr2 |
| | != | 不相等 | expr1!=expr2 |
| | < | 不到 | expr1<expr2 |
| | > | 大于 | expr1>expr2 |
| | <= | 小于或等于 | expr1<=expr2 |
| | >= | 大于或等于 | expr1>=expr2 |
按位运算产生对每个位执行的值的结果。逻辑运算符(and、or)产生一个值,该值可为真或为假,通常与表达式或条件一起使用。
Tip
真与假在 Python 中分别表示为True和False(初始大写)。
输出到屏幕
我们已经看到了一些如何将消息打印到屏幕上的例子,但是没有对所显示的语句进行任何解释。虽然不太可能为您部署的项目打印 XBee 模块的输出,但是当您在屏幕上显示消息时,学习 Python 会容易得多。
你可能想要打印的一些东西——正如我们在清单 3-1 中看到的——是为了传达你的程序内部正在发生的事情。这可以包括简单的消息(字符串),但也可以包括变量、表达式等的值。
正如我们所见,内置的print()函数是显示包含在单引号或双引号中的输出文本的最常见方式。我们还看到了一些使用另一个名为format()的函数的有趣例子。format()函数为每个传递的参数生成一个字符串。这些参数可以是其他字符串、表达式、变量等等。该函数与一个特殊字符串一起使用,该字符串包含由花括号{ }分隔的替换键(称为字符串插值 2 )。每个替换键包含一个索引(从 0 开始)或一个命名关键字。这个特殊字符串称为格式字符串。让我们看几个例子来说明这个概念。你可以在你的电脑上自己运行这些。我包括了输出,这样您可以看到每个语句做了什么。
注意清单 3-3 中的>>>符号。这表明我正在使用 Python 解释器执行代码。您可以从任何命令窗口(终端)启动 Python 解释器,方法是键入命令python或python3来运行 3。Python 的 x 版本。
>>> a = 42
>>> b = 1.5
>>> c = "seventy"
>>> print("{0} {1} {2} {3}".format(a,b,c,(2+3)))
42 1.5 seventy 5
>>> print("{a_var} {b_var} {c_var} {0}".format((3*3),c_var=c,b_var=b,a_var=a))
42 1.5 seventy 9
Listing 3-3Python Interpreter Example
Note
对于那些已经学会用另一种语言如 C 或 C++编程的人来说,Python 允许你用分号(;)终止一个语句;然而,包含它是不必要的,并且被认为是不好的形式。
注意,我创建了三个变量(我们将在下一节讨论变量),用等号(=)给它们分配不同的值。然后,我使用带有四个替换键的格式字符串打印了一条消息,这四个替换键使用索引进行标记。请注意打印语句的输出。请注意,我在结尾处包含了一个表达式,以展示format()函数如何计算表达式。
最后一行更有趣。这里,我使用了三个命名参数(a_var、b_var、c_var),并在format()函数中使用了一个特殊的参数选项,在这里我给参数赋值。请注意,我以不同的顺序列出了它们。这是使用命名参数的最大优点;它们可以以任何顺序出现,但被放在格式字符串中指定的位置。
如您所见,这只是用来自format()函数的键替换{ }键的一个例子,该函数将参数转换为字符串。我们在任何需要包含从多个区域收集的数据的字符串的地方使用这种技术。我们可以在前面的例子中看到这一点。
Tip
有关格式字符串和可用选项的更多信息,请参见 https://docs.python.org/3/library/string.html#formatstrings 。
现在让我们看看如何在我们的程序(脚本)中使用变量。
变量和数据类型
现在我们已经看到了简单 Python 代码的基本构造,让我们先来探索一下您需要掌握的基本概念:变量和数据类型。在本节中,我们将了解如何创建变量来存储数据,包括它们的类型(它们可以存储什么类型的数据)以及使用变量的简单语句。我们将在下一节学习更多关于复杂数据类型的知识。
变量
Python 是一种动态类型语言,这意味着变量的类型(它可以存储的数据类型)是由遇到或使用的上下文决定的。这与 C 和 C++等其他语言形成对比,在这些语言中,必须在使用变量之前声明类型。
Python 中的变量只是命名的内存位置,可以用来在执行过程中存储值。我们通过使用等号赋值来存储值。Python 变量名可以是您想要的任何名称,但是大多数 Python 开发人员都遵循一些规则和约定。Python 编码标准中列出了这些规则。 3
然而,一般的、首要的规则要求变量名是描述性的、在上下文中有意义的并且容易阅读。也就是说,您应该避免使用带有随机字符、强制缩写、首字母缩略词以及类似的晦涩难懂的名称。按照惯例,变量名应该长于一个字符(除了一些可接受的循环计数变量),并且足够短以避免过长的代码行。
What is a Long Code Line?
大多数人会说一个代码行不应该超过 80 个字符,但这源于编程的黑暗时代,那时我们使用穿孔卡,每张卡最多允许 80 个字符(或更少),后来的显示设备也有同样的限制。对于现代的宽屏显示器来说,这没什么大不了的,但我仍然建议保持短行以确保更好的可读性。没有人喜欢向下滚动阅读!或者,更糟的是,需要打开自动换行或使用 34 英寸宽屏显示器来阅读代码。
因此,给变量命名有很大的灵活性。在 PEP8 标准中有额外的规则和指南,如果您希望使您的项目源代码与标准保持一致,您应该查看 PEP8 函数、类等的命名标准。有关规则和标准的完整列表,请参见 www.python.org/dev/peps/pep-0008 的 pep 8 Python 编码指南。
清单 3-4 展示了一些简单变量及其动态确定类型的例子。
# floating point number
length = 10.0
# integer
width = 4
# string
box_label = "Tools"
# list
car_makers = ['Ford', 'Chevrolet', 'Dodge']
# tuple
porsche_cars = ('911', 'Cayman', 'Boxster')
# dictionary
address = {"name": "Joe Smith", "Street": "123 Main", "City": "Anytown", "State": "New Happyville"}
Listing 3-4Simple Variable Examples
那么,我们怎么知道可变宽度是一个整数呢?仅仅因为数字 4 是一个整数。同样,Python 将把“Tools”解释为一个字符串。我们将在下一节看到更多关于最后三种类型和 Python 支持的其他类型的内容。
Tip
有关 Python 编码标准(PEP8)管理的命名约定的更多信息,请参见 www.python.org/dev/peps/pep-0008/#naming-conventions 。
类型
如前所述,Python 不像其他语言那样有正式的类型规范机制。但是,您仍然可以定义变量来存储您想要的任何内容。事实上,Python 允许您基于上下文创建和使用变量,并且您可以使用初始化来“设置”变量的数据类型。清单 3-5 展示了几个例子。
# Numbers
float_value = 9.75
integer_value = 5
# Strings
my_string = "He says, he's already got one."
print("Floating number: {0}".format(float_value))
print("Integer number: {0}".format(integer_value))
print(my_string)
Listing 3-5Setting the Variable Data Type
对于需要转换类型或希望确保值以某种方式键入的情况,有许多用于转换数据的函数。表 3-2 显示了一些更常用的类型转换函数。我将在后面的章节中讨论一些数据结构。
表 3-2
Python 中的类型转换
|功能
|
描述
|
| --- | --- |
| int(x [,base]) | 将 x 转换为整数。基数是可选的(例如,十六进制为 16) |
| long(x [,base]) | 将 x 转换为长整数 |
| float(x) | 将 x 转换为浮点 |
| str(x) | 将对象 x 转换为字符串 |
| tuple(t) | 将 t 转换为元组 |
| list(l) | 将 l 转换为列表 |
| set(s) | 将转换为集合 |
| dict(d) | 创建词典 |
| chr(x) | 将整数转换为字符 |
| hex(x) | 将整数转换为十六进制字符串 |
| oct(x) | 将整数转换为八进制字符串 |
但是,您应该小心使用这些转换函数,以避免数据丢失或舍入。例如,将浮点数转换为整数可能会导致截断。同样,打印浮点数会导致舍入。
现在让我们看看一些常用的数据结构,包括这个叫做字典的奇怪的东西。
基本数据结构
到目前为止,你所学到的关于 Python 的知识足以编写最基本的程序,而且对于处理本章后面的示例项目来说也绰绰有余。然而,当您开始需要对来自用户或来自传感器和类似来源的数据进行操作时,您将需要一种方法来组织和存储数据,以及对内存中的数据执行操作。下面几节按照复杂程度的顺序介绍了三种数据结构:列表、元组或字典。
虽然您不太可能在 XBee MicroPython 脚本中使用这些结构,但是任何 Python 教程如果不包含这些都是不负责任的。这并不意味着您不能在 MicroPython 中使用这些构造——您可以——但是大多数小型项目的 MicroPython 脚本可能不会在同一个脚本中使用所有的数据结构。同样,如果你需要使用它们,那么就这样做吧!
列表
列表是 Python 中组织数据的一种方式。这是一种构建集合的自由形式的方法。也就是说,项目(或元素)不必是相同的数据类型。列表还允许你做一些有趣的操作,比如在末尾、开头或特殊索引处添加内容。清单 3-6 展示了如何创建一个列表。
# List
my_list = ["abacab", 575, "rex, the wonder dog", 24, 5, 6]
my_list.append("end")
my_list.insert(0,"begin")
for item in my_list:
print("{0}".format(item))
Listing 3-6Creating a List
这里,我们看到我使用方括号([])创建了列表。列表定义中的项目用逗号分隔。注意,您可以简单地通过设置一个等于[]的变量来创建一个空列表。因为列表和其他数据结构一样,都是对象,所以有几种操作可用于列表,例如:
-
append(x):在列表末尾添加 x。 -
extend(l):将所有项目添加到列表末尾。 -
insert(pos,item):在位置pos.插入项目 -
remove(value):删除第一个匹配(==)值的项目。 -
pop([i]):移除并返回位置i或列表末端的项目。 -
index(value):返回第一个匹配项的索引。 -
count(value):统计值的出现次数。 -
sort():对列表进行排序(升序)。 -
reverse():反向排序列表。
列表就像其他语言中的数组一样,对于构建动态数据集合非常有用。
元组
另一方面,元组是一种限制性更强的集合类型。也就是说,它们是由一组特定的数据构建的,不允许像列表一样进行操作。事实上,您不能更改元组中的元素。因此,我们可以对不应该改变的数据使用元组。清单 3-7 展示了一个元组的例子以及如何使用它。
# Tuple
my_tuple = (0,1,2,3,4,5,6,7,8,"nine")
for item in my_tuple:
print("{0}".format(item))
if 7 in my_tuple:
print("7 is in the list")
Listing 3-7Using Tuples
这里,我们看到我使用括号()创建了元组。元组定义中的各项用逗号分隔。注意,您可以简单地通过设置一个等于()的变量来创建一个空元组。因为元组像其他数据结构一样是对象,所以有几种操作可用,例如下面包括对诸如包含、定位等序列的操作:
-
x in t:判断t是否包含x。 -
x not in t:判断t是否不包含x。 -
s + t:连接元组。 -
s[i]:获取元素i。 -
len(t):长度t(元素个数)。 -
min(t): Minimal(最小值)。 -
max(t):最大值(最大值)。
如果你想在内存中存储更多的数据,你可以使用一个叫做字典的特殊结构(对象)。
字典
字典是一种数据结构,允许您存储键、值对,通过键来评估数据。字典是一种非常结构化的数据处理方式,也是我们在收集复杂数据时想要使用的最符合逻辑的形式。清单 3-8 显示了一个字典的例子。
# Dictionary
my_dictionary = {
'first_name': "Chuck",
'last_name': "Bell",
'age': 36,
'my_ip': (192,168,1,225),
42: “What is the meaning of life?”,
}
# Access the keys:
print(my_dictionary.keys())
# Access the items (key, value) pairs
print(my_dictionary.items())
# Access the values
print(my_dictionary.values())
# Create a list of dictionaries
my_addresses = [my_dictionary]
Listing 3-8Using Dictionaries
这里发生了很多事情!我们看到一个使用花括号创建字典的基本字典声明。在里面,我们可以创建尽可能多的键、值对,用逗号分隔。使用字符串(我习惯使用单引号,但双引号也可以)或整数定义键,值可以是我们想要的任何数据类型。对于my_ip属性,我们也存储了一个元组
按照字典,我们看到在字典上执行的几个操作,包括打印键、打印所有值和只打印值。清单 3-9 显示了从 Python 解释器执行这个代码片段的输出。
[42, 'first_name', 'last_name', 'age', 'my_ip']
[(42, 'what is the meaning of life?'), ('first_name', 'Chuck'), ('last_name', 'Bell'), ('age', 36), ('my_ip', (192, 168, 1, 225))]
['what is the meaning of life?', 'Chuck', 'Bell', 36, (192, 168, 1, 225)]
'42': what is the meaning of life?
'first_name': Chuck
'last_name': Bell
'age': 36
'my_ip': (192, 168, 1, 225)
Listing 3-9Performing Operations on Dictionaries
正如我们在清单 3-9 的例子中看到的,有几个操作(函数或方法)可用于字典,包括如下。这些操作使得字典成为一个非常强大的编程工具:
-
len(d):在d.中的项目数 -
d[k]:带k.键的d项 -
d[k
] = x:给键k赋值x. -
del d[k]:用k.键删除项目 -
k in d:判断d是否有带k.键的项目 -
d.items():返回d.中(键,值)对的列表(视图) -
d.keys():返回d.中按键的列表(视图) -
d.values():返回 d 中值的列表(视图)
最重要的是,对象可以放在其他对象中。例如,您可以像我前面做的那样创建一个字典列表,一个包含列表和元组的字典,以及您需要的任何组合。因此,列表、元组和字典是管理程序数据的强大方法。
在下一节中,我们将学习如何控制程序的流程。
流量控制语句
现在我们对 Python 的基础有了更多的了解,我们可以发现一些完成项目所需的更复杂的代码概念,比如条件语句和循环。
条件语句
我们还看到了一些简单的条件语句:根据一个或多个表达式的计算来改变执行流程的语句。条件语句允许我们根据一个或多个表达式的计算,将程序的执行指向代码段(块)。Python 中的条件语句是if语句。
我们已经在示例代码中看到了if语句。注意,在示例中,我们可以有一个或多个(可选的)else短语,一旦 if 条件的表达式计算为 false,我们就执行这些短语。我们可以链接if / els e 语句来包含多个条件,其中执行的代码取决于几个条件的评估。清单 3-10 显示了 if 语句的一般结构。注意在注释中我是如何解释执行是如何到达每个条件的主体的。
if (expr1):
# execute only if expr1 is true
elif ((expr2) or (expr3)):
# execute only if expr1 is false *and* either expr2 or expr3 is true
else:
# execute if both sets of if conditions evaluate to false
Listing 3-10Conditional Statements
虽然您可以尽可能多地链接语句,但在这里要小心,因为您拥有的elif部分越多,就越难理解、维护和避免表达式中的逻辑错误。
还有另一种形式的条件语句,称为三元运算符。在 Python 中,三元运算符通常被称为条件表达式。这些操作符基于条件的真或假来评估某些东西。在 2.4 版本中,它们成为 Python 的一部分。条件表达式是赋值语句中(通常)使用的 if-then-else 结构的简写符号,如下所示:
variable = value_if_true if condition else value_if_false
这里,我们看到如果条件被评估为真,则使用 if 前面的值,但是如果条件被评估为假,则使用 else 后面的值。清单 3-11 给出了一个简短的例子。
>>> numbers = [1,2,3,4]
>>> for n in numbers:
... x = 'odd' if n % 2 else 'even'
... print("{0} is {1}.".format(n, x))
...
1 is odd.
2 is even.
3 is odd.
4 is even.
>>>
Listing 3-11Evaluation of Conditional Statements
条件表达式允许您快速测试条件,而不是使用多行条件语句,这有助于使您的代码更容易阅读(也更短)。
环
循环用于控制代码块的重复执行。有三种形式的循环,它们的行为略有不同。所有循环都使用条件语句来决定是否重复执行。也就是说,如果条件为真,它们会重复。这两种类型的循环是while和for。我用一个例子来解释每一个。
while循环的条件位于代码块的“顶部”或开始处。因此,while 循环仅当且仅当条件在第一次通过时评估为 true 时才执行主体。下面的代码阐释了 while 循环的语法。只有当某些表达式的计算结果为 true 时,才需要执行代码,这种形式的循环最适合使用,例如,迭代一个元素数量未知的集合(循环,直到集合中的所有元素都用完):
while (expression):
# do something here
由于其独特的形式,循环有时被称为计数循环。For 循环允许您定义一个计数变量和一个要迭代的范围或列表。下面的代码说明了for循环的结构。这种形式的循环最适合用于在集合中执行操作。在这种情况下,Python 会在每次循环中自动将集合中的每一项放入变量中,直到没有更多项可用为止。
for variable_name in list:
# do something here
你也可以做范围循环或计数循环。这使用了一个名为range()的特殊函数,它最多接受三个参数range([start], stop[, step]),其中 start 是起始数字(一个整数),stop 是序列中的最后一个数字,step 是增量。所以,你可以从 1、2、3 等一系列数字开始计数。以下代码显示了一个简单的示例:
for i in range(2,9):
# do something here
你可能会遇到range()的其他用法。更多信息,请参见 https://docs.python.org/3/library/functions.html 中关于此功能和其他内置功能的文档。
Python 还提供了一种使用一些特殊关键字来控制循环流(例如,持续时间或终止)的机制,如下所示:
-
break:立即退出循环体。 -
continue:跳到循环的下一次迭代。 -
else:循环结束时执行代码(如果用 break 语句停止循环,则不执行)。
这些关键字有一些用途,尤其是 break,但它不是终止和控制循环的首选方法。也就是说,专业人士认为条件表达式或错误处理代码应该表现得足够好,不需要这些选项。
模块化:模块、函数和类
最后一组主题是最高级的,包括模块化(代码组织)。正如我们将看到的,我们可以使用函数对代码进行分组,消除重复,并将功能封装到对象中。同样,您可能不会在 XBee 的 MicroPython 代码中使用这些构造,但是您应该了解这些技术,因为您可能会在某个时候遇到它们。
包括模块
Python 应用程序可以从 Python 环境提供的可重用库构建。它们也可以从您自己创建或从第三方下载的自定义模块或库构建。这些通常作为一组 Python 代码文件分发(例如,文件扩展名为.py的文件)。当我们想使用一个库(函数、类等)时。)包含在一个模块中,我们使用import关键字并列出模块的名称。以下代码显示了一些示例:
import os
import sys
前两行演示了如何导入 Python 提供的基本或公共模块。在这种情况下,我们为os和sys模块(操作系统和 Python 系统函数)使用或导入模块。
Tip
习惯上(但不是必需的)按照字母顺序列出您的导入,首先是内置模块,然后是第三方模块,最后是您自己的模块。
功能
Python 允许在代码中使用模块化。虽然它通过类的方式支持面向对象编程(例如,对于 Raspberry Pi 上的大多数 Python GPIO 示例来说,这是一个不太可能遇到的更高级的特性),但在更基本的层面上,您可以使用函数将代码分成更小的块。
函数使用特殊的关键字构造(Python 中很少见)来定义函数。我们简单地使用def,后跟一个函数名和一个用逗号分隔的参数列表,放在括号中。冒号用于终止声明。清单 3-12 显示了一个例子。
def print_dictionary(the_dictionary):
for key, value in the_dictionary.items():
print("'{0}': {1}".format(key, value))
# define some data
my_dictionary = {
'name': "Chuck",
‘age’: 37,
}
Listing 3-12Defining Functions
您可能想知道这个奇怪的代码是做什么的。注意,该循环从items()函数的结果中分配了两个值。这是字典object. 4 中提供的一个特殊函数。items()函数返回键、值对,因此是变量的名称。
下一行打印出这些值。对于 Python 3 应用程序来说,使用格式化字符串(其中花括号定义了从 0 开始的参数编号)是很常见的。有关格式化字符串( https://docs.python.org/3/library/string.html#format-string-syntax )的更多信息,请参见 Python 文档。
函数体是缩进的。该函数声明下缩进的所有语句都属于该函数,并在调用该函数时执行。我们可以通过名字调用函数,提供如下参数。注意我是如何使用键名引用字典中的值的。
print_dictionary(my_dictionary)
print(my_dictionary['age'])
print(my_dictionary['name'])
这个例子和前面的代码一起,在执行时,生成清单 3-13 中所示的输出。
$ python3
Python 3.6.0 (v3.6.0:41df79263a11, Dec 22 2016, 17:23:13)
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> def print_dictionary(the_dictionary):
... for key, value in the_dictionary.items():
... print("'{0}': {1}".format(key, value))
...
>>> # define some data
... my_dictionary = {
... 'name': "Chuck",
... 'age': 41,
... }
>>> print_dictionary(my_dictionary)
'name': Chuck
'age': 41
>>> print(my_dictionary['age'])
41
>>> print(my_dictionary['name'])
Chuck
Listing 3-13Output of Function Example
现在让我们看看 Python 中最复杂的概念——面向对象编程。同样,您可能不希望在您的 MicroPython 中使用这些概念——但是您可以——您仍然应该理解基础知识,以确保您的 Python 知识是完整的。
类别和对象
你可能听说过 Python 是一种面向对象的编程语言。但这意味着什么呢?简单地说,Python 是一种编程语言,它提供了描述对象(事物)以及可以用对象做什么(操作)的工具。对象是数据抽象的一种高级形式,其中数据对调用者是隐藏的,只能由对象提供的操作(方法)来操作。
我们在 Python 中使用的语法是class语句,您可以使用它来帮助您的项目模块化。所谓模块化,我们的意思是源代码被安排得更容易开发和维护。通常,我们将类放在单独的模块(代码文件)中,这有助于更好地组织代码。虽然这不是必需的,但我建议使用这种将类放在它自己的源文件中的技术。这使得修改类或修复问题(错误)更加容易。
那么,什么是 Python 类呢?让我们从将构造视为一种组织技术开始。我们可以使用类将数据和方法分组在一起。类名紧跟在关键字 class 之后,后面是一个冒号。像任何其他方法一样声明其他类方法,除了第一个参数必须是 self,它在执行时将方法绑定到类实例。
Function or Method: Which is Correct?
我更喜欢使用语言设计者或开发人员社区已经采用的术语。例如,有些人使用“函数”,但其他人可能使用“方法”还有一些可能使用子程序、例行程序、过程等等。你使用哪个术语并不重要,但是你应该努力使用一致的术语。一个可能让一些人感到困惑的例子是,我在讨论面向对象的例子时使用了术语方法。也就是说,一个类有方法,没有函数。然而,你可以用函数代替方法,你仍然是正确的(大多数情况下)。
通过使用类(创建实例)和使用点标记来引用数据成员或函数,使用一种或多种方法来访问数据。让我们看一个例子。清单 3-14 显示了一个完整的类,它描述(模拟)了用于运输的车辆的最基本特征。我创建了一个名为vehicle.py的文件来包含这段代码。
#
# Beginning Sensor Networks 2nd Edition
#
# Class Example: A generic vehicle
#
# Dr. Charles Bell
#
class Vehicle:
"""Base class for defining vehicles"""
axles = 0
doors = 0
occupants = 0
def __init__(self, num_axles, num_doors):
self.axles = num_axles
self.doors = num_doors
def get_axles(self):
return self.axles
def get_doors(self):
return self.doors
def add_occupant(self):
self.occupants += 1
def num_occupants(self):
return self.occupants
Listing 3-14Vehicle Class
注意这里的一些事情。首先,有一个名为__init__()的方法。这是构造函数,在创建类实例时调用。您将所有初始化代码像设置变量一样放在这个方法中。我们也有返回轴、门和居住者数量的方法。我们在这个类中有一个方法:添加居住者。
另外,请注意,我们使用self.<name>来处理每个类属性(数据)。这就是我们如何确保我们总是访问与创建的实例相关联的数据。
让我们看看这个类如何被用来定义一个家庭轿车。清单 3-15 显示了使用这个类的代码。我们可以将这段代码放在一个名为sedan.py的文件中。
#
# Beginning Sensor Networks 2nd Edition
#
# Class Example: Using the generic Vehicle class
#
# Dr. Charles Bell
#
from vehicle import Vehicle
sedan = Vehicle(2, 4)
sedan.add_occupant()
sedan.add_occupant()
sedan.add_occupant()
print("The car has {0} occupants.".format(sedan.num_occupants()))
Listing 3-15Using the Vehicle Class
注意,第一行从 vehicle 模块导入了 Vehicle 类。注意,我大写了类名,而不是文件名。这是一种非常常见的命名方案。接下来,在代码中,我们创建类的一个实例。注意我把 2,4 传递给了类名。这将导致在实例化类时调用__init__()方法。变量 sedan 变成了我们可以操作的类实例变量(object ),我添加了三个乘员,然后使用 Vehicle 类中的方法打印出乘员的数量。
我们可以使用下面的命令在 PC 上运行代码。正如我们所看到的,当代码运行时,它告诉我们车上有三个人。不错。
$ python ./sedan.py
The car has 3 occupants.
面向对象编程(OOP)术语
像任何技术或概念一样,有一定数量的术语,您必须学会这些术语,才能理解技术并与他人交流。下面的列表简要描述了一些您需要了解的术语,以便更好地了解面向对象编程:
-
属性:类中的一个数据元素。
-
Class :用于以属性(数据)和对数据进行操作的方法(函数)的形式定义对象的代码构造。Python 中的方法和属性可以使用点符号来访问。
-
类实例变量:用于存储对象实例的变量。它们像其他变量一样使用,与点符号结合,允许我们操作对象。
-
实例:类的可执行形式,通过将类赋给一个将代码初始化为对象的变量来创建。
-
继承:将一个类的属性和方法包含到另一个类中。
-
实例化:创建一个类的实例。
-
方法重载:创建两个或多个同名但参数不同的方法。这允许我们创建具有相同名称的方法,但是根据所传递的参数,操作可能不同。
-
多态性:从基类继承属性和方法,添加额外的方法或者覆盖(改变)方法。
还有很多 OOP 术语,但这些是你最常遇到的。
现在,让我们看看如何使用 vehicle 类来演示继承。在这种情况下,我们将创建一个名为PickupTruck的新类,该类使用 vehicle 类,但是向结果类添加了专门化。清单 3-16 显示了新的类。我将这段代码放在一个名为pickup_truck.py的文件中。如你所见,皮卡是一种交通工具。
#
# Beginning Sensor Networks 2nd Edition
#
# Class Example: Inheriting the Vehicle class to form a
# model of a pickup truck with maximum occupants and maximum
# payload.
#
# Dr. Charles Bell
#
from vehicle import Vehicle
class PickupTruck(Vehicle):
"""This is a pickup truck that has:
axles = 2,
doors = 2,
__max occupants = 3
The maximum payload is set on instantiation.
"""
occupants = 0
payload = 0
max_payload = 0
def __init__(self, max_weight):
super().__init__(2,2)
self.max_payload = max_weight
self.__max_occupants = 3
def add_occupant(self):
if (self.occupants < self.__max_occupants):
super().add_occupant()
else:
print("Sorry, only 3 occupants are permitted in the truck.")
def add_payload(self, num_pounds):
if ((self.payload + num_pounds) < self.max_payload):
self.payload += num_pounds
else
:
print("Overloaded!")
def remove_payload(self, num_pounds):
if ((self.payload - num_pounds) >= 0):
self.payload -= num_pounds
else:
print("Nothing in the truck.")
def get_payload(self):
return self.payload
Listing 3-16Pickup Truck Class
注意这里的一些事情。首先,注意类语句:class PickupTruck(Vehicle) .当我们想从另一个类继承时,我们用基类的名字加上括号。这确保 Python 将使用基类,允许派生类使用其所有可访问的数据和内存。如果要从多个类继承,可以(称为多重继承);只需用逗号分隔的列表列出基(父)类。
接下来,注意__max_occupants变量。按照惯例,在一个类中为一个属性或方法使用两个下划线会使该项成为该类的私有项。 5 也就是说,它应该只能从类内部访问。该类的调用方(通过类变量/实例)不能访问私有项,从该类派生的任何类也不能访问私有项。隐藏属性(数据)总是一个好的做法。
您可能想知道 occupant 方法发生了什么变化。他们为什么不在新班级?它们不在那里,因为我们的新类继承了基类的所有行为。不仅如此,还修改了代码,将乘员限制为三人。
我还想指出我添加到该类中的文档。我们使用文档字符串(前后使用一组三个双引号)来记录这个类。您可以将文档放在这里解释该类及其方法。稍后我们会看到它的一个很好的用途。
最后,请注意构造函数中的代码。这演示了如何调用基类方法,我这样做是为了设置轴和门的数量。如果我们想调用基类方法的版本,我们可以在其他方法中做同样的事情。
现在,让我们写一些代码来使用这个类。清单 3-17 显示了我们用来测试这个类的代码。在这里,我们创建了一个名为 pickup.py 的文件,该文件创建了一个皮卡实例,添加了乘员和有效载荷,然后打印出卡车的内容。
#
# Beginning Sensor Networks 2nd Edition
#
# Class Example: Exercising the PickupTruck class.
#
# Dr. Charles Bell
#
from pickup_truck import PickupTruck
pickup = PickupTruck(500)
pickup.add_occupant()
pickup.add_occupant()
pickup.add_occupant()
pickup.add_occupant()
pickup.add_payload(100)
pickup.add_payload(300)
print("Number of occupants in truck = {0}.".format(pickup.num_occupants()))
print("Weight in truck = {0}.".format(pickup.get_payload()))
pickup.add_payload(200)
pickup.remove_payload(400)
pickup.remove_payload(10)
Listing 3-17Using the PickupTruck Class
注意,我添加了几个对add_occupant()方法的调用,新类继承并覆盖了它。我还添加了一些调用,这样我们就可以在检查过度占用和最大有效负载能力的方法中测试代码。当我们运行这段代码时,我们将看到如下所示的结果:
$ python ./pickup.py
Sorry, only 3 occupants are permitted in the truck.
Number of occupants in truck = 3.
Weight in truck = 400.
Overloaded!
Nothing in the truck.
关于类,我们还应该了解一件事:内置属性。回忆一下__init__()方法。Python 自动提供了几个内置属性,每个都以__开头,您可以使用它们来了解更多关于对象的信息。下面列出了一些可用于类的运算符:
-
__dict__:包含类名称空间的字典 -
__doc__:类文档字符串 -
__name__:类名 -
__module__:定义类的模块名 -
__bases__:继承顺序中的基类
下面的代码显示了这些属性为前面的PickupTruck类返回的内容。我将这段代码添加到 pickup.py 文件中。
print("PickupTruck.__doc__:", PickupTruck.__doc__)
print("PickupTruck.__name__:", PickupTruck.__name__)
print("PickupTruck.__module__:", PickupTruck.__module__)
print("PickupTruck.__bases__:", PickupTruck.__bases__)
print("PickupTruck.__dict__:", PickupTruck.__dict__)
运行此代码时,我们会看到以下输出:
PickupTruck.__doc__: This is a pickup truck that has:
axles = 2,
doors = 2,
max occupants = 3
The maximum payload
is set on instantiation.
PickupTruck.__name__: PickupTruck
PickupTruck.__module__: pickup_truck
PickupTruck.__bases__: (<class 'vehicle.Vehicle'>,)
PickupTruck.__dict__: {'__module__': 'pickup_truck', '__doc__': 'This is a pickup truck that has:\n axles = 2,\n doors = 2,\n max occupants = 3\n The maximum payload is set on instantiation.\n ', 'occupants': 0, 'payload': 0, 'max_payload': 0, ' _PickupTruck__max_occupants': 3, '__init__': <function PickupTruck.__init__ at 0x1018a1488>, 'add_occupant': <function PickupTruck.add_occupant at 0x1018a17b8>, 'add_payload': <function PickupTruck.add_payload at 0x1018a1840>, 'remove_payload': <function PickupTruck.remove_payload at 0x1018a18c8>, 'get_payload': <function PickupTruck.get_payload at 0x1018a1950>}
当您需要关于一个类的更多信息时,您可以使用内置属性。注意字典中的_PickupTruck__max_occupants条目。回想一下,我们制作了一个伪私有变量__max_occupants。在这里,我们看到 Python 是如何通过在变量前添加类名来引用变量的。记住,以两个下划线(而不是一个)开头的变量表示它们应该被认为是类的私有变量,并且只能在类内部使用。
Tip
有关 Python 中类的更多信息,请参见 https://docs.python.org/3/tutorial/classes.html 。
For More Information
如果您需要更深入的 Python 知识,有几本关于这个主题的优秀书籍。我在这里列出了几个我最喜欢的:
-
Pro Python ,第二版(Apress 2014),J. Burton Browning,Marty Alchin
-
学习 Python ,第五版(奥莱利媒体 2013),马克·卢茨
-
用 Python 自动化枯燥的东西:面向所有初学者的实用编程(无淀粉出版社,2015 年),Al Sweigart
Python 网站上的文档是一个很好的资源:python.org/doc/.
摘要
哇!那是一次疯狂的旅行,不是吗?我希望这个简短的 Python 速成课程已经对到目前为止展示的示例程序进行了足够的解释,现在您已经知道它们是如何工作的了。这个速成课程也为理解本书中的其他 Python 和 MicroPython 示例奠定了基础。
如果您正在学习如何使用传感器网络项目,但不知道如何使用 Python 编程,学习 Python 会很有趣,因为它的语法简单易懂。虽然互联网上有很多例子可以使用,但是很少有以这种方式记录的,可以为 Python 新手提供足够的信息来理解,更不用说开始使用和部署示例了!但至少代码很容易阅读。
本章提供了 Python 速成课程,涵盖了您在研究大多数较小的示例项目时将会遇到的基本内容。我们发现了 Python 应用程序的基本语法和结构,包括编写 Python 和 MicroPython 脚本时可能会遇到的所有基本语句和数据结构。
在下一章,我们将更深入地研究 MicroPython 编程,并开始为我们的 XBee 模块编写 MicroPython。
Footnotes 12
https://en.wikipedia.org/wiki/String_interpolation
3
www.python.org/dev/peps/pep-0008
4
没错,字典就是对象!元组、列表和许多其他数据结构也是如此。
5
从技术上来说,它被称为名称篡改,它模拟了将某些内容设为私有,但是如果您提供了正确数量的下划线,仍然可以被访问。更多信息请参见 https://en.wikipedia.org/wiki/Name_mangling 。