MicroPython-物联网教程-全-

MicroPython 物联网教程(全)

原文:MicroPython for the Internet of Things

协议:CC BY-NC-SA 4.0

一、什么是物联网?

如果你最近一直在关注技术世界,那么你很可能会遇到无数次提到物联网这个术语。大多数媒体参考资料和公司广告将这个或那个称为物联网,但很少或没有解释它的含义。即使你找到了它的深度,文本也倾向于关注问题和挑战,或者关注让我们未来生活更美好的承诺。一些人认为,随着我们每天与周围世界的联系越来越紧密,物联网将带来我们社会不可避免的演变。

然而,你不需要陷入如此令人兴奋的概念或背诵修辞来开始物联网。事实上,通过众多开源开发者和厂商的努力,不需要密集的培训,也不需要昂贵的软硬件,就可以探索物联网。最重要的是,您可以探索物联网,而无需学习大量编程知识或花费数月时间学习如何编码!

这本书旨在成为一个指南,帮助您了解物联网,并开始构建您可以用来了解更多关于物联网的解决方案。因为这是一本初学者的书,我们将从检查编程语言和环境开始,然后详细看一下硬件。我们还将学习电子学的基本知识,然后探索几个项目来帮助我们理解如何使用该软件。最终项目将把所有方面结合在一起,以帮助理解什么是物联网,甚至如何编写定制软件来构建物联网解决方案。最重要的是,我们使用最容易使用的编程语言之一和易于使用的开源微控制器板。

那么,这个物联网是什么,也就是 IOT? 1 我们先来解释一下它不是什么。IOT 不是一个新的设备或专有软件或一些新的硬件,也不是一个新的营销计划,通过重命名和发音为“新的和改进的”来卖给你更多你已经拥有的东西 2 虽然 IOT 确实采用了已经存在的技术和技巧,但它们的使用方式,加上从世界任何地方访问解决方案的能力,使得 IOT 成为一个令人兴奋的探索概念。现在让我们讨论什么是 IOT。

IOT 的本质是简单的互联设备,这些设备从观察、事实和其他数据中生成和交换数据,使其可供任何人使用。虽然似乎有一些营销努力试图使任何连接到互联网的东西成为 IOT 的解决方案或设备——这与无耻的将所有东西都贴上“云”的标签没有什么不同,但 IOT 解决方案旨在通过使随时随地获取任何东西的数据成为可能,使我们对周围世界的知识更加及时和相关。

你可以想象,如果我们将我们周围的每一个设备都连接到互联网,并为这些设备提供传感数据,很明显,IOT 设备的数量有可能超过地球上的人口数量,生成的数据也有可能迅速超过除最复杂的数据库系统之外的所有数据库系统的能力。这些概念通常被称为可寻址性和大数据,是 IOT 最活跃和最具争议的两个话题。

然而,IOT 是关于理解我们周围的世界。也就是说,我们可以利用数据让我们的世界和我们对它的理解变得更好。

物联网和你

我们如何观察周围的世界?人体是巧妙的感觉器官的奇迹,它允许我们看、听、尝,甚至通过触摸我们遇到的任何东西。甚至我们的大脑也可以存储视觉和听觉事件,并随意回忆它们。IOT 解决方案模仿了许多这样的感觉能力,因此可以成为我们自身能力的延伸。虽然这听起来有点浮夸(确实如此),但 IOT 解决方案可以记录来自一个或多个传感器的数据形式的观察结果,并通过互联网供任何人在任何地方查看。

传感器是产生模拟或数字值的设备。然后,我们可以使用收集的数据得出关于主题的结论。这可能就像传感器检测门、窗或邮箱何时被打开一样简单。在邮箱开关的情况下,我们从简单的开关打开或关闭(取决于它是如何实现和解释的)中获得的知识可以用于预测传入邮件何时到达或传出邮件何时被取走。我使用“预测”一词是因为传感器(开关)只告诉我们门是开着还是关着的,而不是告诉我们有什么东西被放入邮箱或从邮箱中取出——这需要额外的传感器。

一个更复杂的例子是使用一系列传感器来记录大气数据,如温度、湿度、气压、风速、环境光、降雨量等。,以监控天气,使我们能够对数据进行分析,以预测天气趋势。也就是说,我们可以在合理的确定性范围内预测该地区有降水。

现在,添加不仅实时(当数据出现时)而且从世界任何地方远程查看数据的能力,解决方案就不仅仅是一个简单的气象站了。它成为一种从世界任何地方观察一个地方天气的方法。这个例子可能看起来有点普通,因为你可以收听任何数量的电视、网络和广播,收听世界任何地方的天气预报。但是,请考虑在您的家中构建这样一个解决方案的意义。现在你可以在自己家里看到关于天气的数据!

以同样的方式,但也许在更小的范围内,我们可以建立监测植物的解决方案,以帮助我们了解它们需要水和其他养分的频率。或者,我们可以在外出工作时监控我们的宠物。此外,我们可以记录我们所在地区的野生动物数据,以更好地了解我们对自然的影响。

IOT 不仅仅是连接到互联网

如果一个设备连接到互联网,这是否使其成为 IOT 解决方案?那取决于你问谁。一些人认为答案是肯定的。然而,其他人(比如我自己)认为答案是否定的,除非这样做有好处。例如,如果你把你的烤面包机连接到互联网,这样做有什么好处?如果你的烤面包机给你的手机发短信说你的烤面包准备好了,这是毫无意义的(或者至少是极端古怪的)。所以,在这种情况下,答案是否定的。

然而,如果你想监控一些人,比如负责任的青少年或者老年人,那么查看他们使用烤面包机的频率和时间可能会有所帮助。也就是说,您可以使用这些数据来帮助您对他们的护理和安全做出决策。

对我来说,如果数据没有任何用处,无论是实时查看的数据还是存储起来供以后处理的数据,那么简单地将其连接到互联网并不能使其成为 IOT 的解决方案。使用这种设备肯定会有好处。因此,接入互联网并不能让某些东西成为 IOT。相反,IOT 解决方案必须是那些能提供一些意义的东西——无论多小,只要对某人或其他设备或服务有益。

更重要的是,无论我们构建 IOT 解决方案来做什么,它们都允许我们感知我们周围的世界,并从这些观察中学习。真正棘手的部分在于数据是如何收集、存储和呈现的。我们将在后面的章节中通过例子看到这些。请看侧栏中一个有争议的 IOT 设备的例子——一个普通的家用电器。

但是,IOT 解决方案通常可以利用提供服务的公司,这些服务可以帮助增强或提供您可以在 IOT 解决方案中使用的功能。这些功能通常被称为 IOT 服务,范围从存储和演示到托管等基础设施服务。

Internet-Enabled Appliances: IOT or Marketing Hype?

一个似乎变得流行的想法或概念是将主要的家用电器连接到互联网。虽然制造商可能希望你相信这是一个新的和令人兴奋的 IOT 设备,但事实是,它既不是一个新的想法,也不是一个改变世界的 IOT 解决方案。

我有幸参加了 20 世纪 90 年代末在微软校园举行的设计研讨会。在我们参观校园的过程中,我们看到了世界上第一台能够上网的冰箱(也称为智能冰箱或简称为互联网冰箱)。 3 货架上有传感器来检测食物的重量。有人建议,只要有点独创性,就可以利用传感器在你的牛奶供应不足时通知你的杂货商,这将使人们不仅可以在线购物,而且可以自动购物。

现在,20 年后,我们看到制造商们正在制造连接互联网的冰箱。然而,与第一台智能冰箱不同,这些新设备被定位为家庭的社交媒体焦点。许多人不提供关于冰箱内容的任何有意义的数据,除了小工具一样的不开门就能看到内容的视频图像的功能,这可以通过安装玻璃门来解决。

我只想说,像我这样的 IOT 爱好者对这样的东西怎么可能有用挠头,更不用说卖得好了。可悲的是,这些新的互联网冰箱似乎确实卖得很好,但我想知道消费者是否被炒作所吸引。关于为什么互联网冰箱不适合你的有趣评论,在谷歌上搜索一下,你会发现很多观点——大多数是负面的(然而,人们仍然会购买这些东西)。 4

让我们用我对 IOT 的定义来评价互联网冰箱:它通过向你提供关于你周围世界的信息来改善你的生活吗?好吧,如果你需要在离家 3000 英里之外查看你有多少牛奶,那么我想这可能是有益的,但对于我们大多数人来说,我们宁愿在去商店之前打开门看看,这可能不是一个 IOT 设备。

IOT 服务

可悲的是,有些公司吹嘘拥有 IOT 的产品和服务只不过是营销炒作——就像一些公司所做的那样,在名称前加上“云”或附加“为了云”。幸运的是,有一些好的产品和服务是专为 IOT 打造的。这些范围从数据存储和托管到专门的硬件。

事实上,企业在产品中加入 IOT 服务的速度比任何人都快。它不是通常的嫌疑人,如互联网巨头。我看到思科、美国电话电报公司、惠普以及无数初创公司和小型企业提供 IOT 解决方案和服务。我使用术语 IOT 供应商来描述那些为 IOT 解决方案提供服务的企业。

你可能想知道这些服务和产品是什么,为什么会考虑使用它们。也就是说,什么是 IOT 服务,你为什么会决定购买它?您决定购买服务的最大原因是成本和上市时间。

如果您的开发人员没有资源或专业知识,并且获得这些资源或专业知识所需的费用超过了服务的费用,那么购买服务可能更经济。但是,您还应该考虑在决策中任何必要的额外的软件或硬件变更(有时称为重组)。我曾经遇到过一个善意的、有良好文档记录的合同服务,它允许一个产品比预期的更快地进入市场,从而节省了大量成本。可悲的是,虽然该合同的冠军赢得了技术成就奖,但他们没有考虑到这样一个事实,即系统必须重新装备才能使用新服务。更具体地说,采用新服务比从头开始编写新服务需要更长的时间。因此,该组织不但没有节省资金,反而花费了近三倍的资金,并且上市时间较晚。显然,我们必须考虑所有因素。

同样,如果您的时间不多,或者您很难在截止日期前准备好解决方案,那么购买 IOT 服务可能比创建或修改您自己的服务更快。这可能需要多花一点钱,但在这种情况下,动机是时间,而不是(必然)成本。当然,这是成本和时间的混合体。

那么,IOT 有哪些服务呢?下面列出了最近几年出现的一些。随着 IOT 解决方案和服务的成熟,可能会提供更多的服务。

  • 企业 IOT 数据托管和展示–允许您的用户开发企业 IOT 解决方案的服务,包括连接、管理和定制友好形式的数据展示,如图形、图表等。例如:Xively ( https://xively.com/ )
  • IOT 数据存储–允许您存储 IOT 数据并获得简单报告的服务。例如:Sparkfun 的 IOT 数据服务( https://data.sparkfun.com/ )
  • 网络–为 IOT 提供网络和类似通信协议或平台的服务。大多数专门从事机器对机器(M2M)服务例如:美国电话电报公司的蜂窝全球 SIM 卡服务(business.att.com/enterprise/Family/mobility-services/internet-of-things)
  • IOT 硬件平台——允许您使用硬件平台和一系列受支持的模块和工具快速开发和制作 IOT 设备原型的供应商,用于构建从简单组件到完整设备的设备。例如:英特尔的 IOT 网关开发套件(intel.com/content/www/us/en/embedded/solutions/iot-gateway/development-kits.html)

现在,我们对 IOT 有了更多的了解,让我们来看几个 IOT 解决方案的例子,以便更好地了解 IOT 解决方案能做什么以及如何使用它们。

IOT 解决方案简介

IOT 解决方案就是一组设计用于产生、消费或呈现关于某个事件或一系列事件或观察结果的数据的设备。这可以包括生成数据的设备(如传感器)、组合数据以推断某些东西的设备、设计用于制表和存储数据的设备或服务,以及设计用于呈现数据的设备或系统。任何这些都可以连接到互联网。

无论是将 IOT 解决方案整合到网络摄像头等单一设备中,还是整合到气象站等传感器包和监控单元中,或者整合到由专用传感器、聚合器、数据存储和演示等设备组成的复杂系统中,如完整的家庭自动化系统中,这些解决方案都可能具备上述多种特性。图 1-1 展示了一幅无处不在的连接到互联网的所有设备的未来图景,这些设备可以是数据库、数据收集器或集成器、显示服务,甚至是其他设备。

A447395_1_En_1_Fig1_HTML.jpg

图 1-1。

The future of IOT – all devices, everywhere5

让我们看一些 IOT 解决方案的例子。本节中描述的 IOT 解决方案是多种解决方案的组合,您应该对 IOT 解决方案的规模和复杂性有所了解。我还指出了其中一些解决方案如何利用 IOT 供应商的服务。

传感器网络

传感器网络是最常见的 IOT 解决方案之一。简单地说,传感器网络让你可以观察你周围的世界并理解它。传感器网络可以采取池塘监控系统的形式,提醒您水位、水纯度(污染)、水温,检测捕食者,甚至自动打开功能,如照明或给花园池塘中的鱼喂食。

如果你或你认识的人在医疗机构呆过一段时间,就有可能使用传感器网络来监控身体功能,如体温、心率、呼吸能力,甚至四肢的活动范围。现代汽车还包含传感器网络,专用于监控发动机、气候,甚至一些汽车的路况。例如,车道警告功能使用传感器(通常是摄像头、微处理器和软件)来检测您何时偏离车道或道路分界线太远。制造工厂还采用传感器网络来监控机器、传送带等。货运票据交换所还利用传感器网络来帮助将包裹发送到正确的垃圾箱,并最终发送到正确的卡车或飞机上进行运输。

因此,传感器网络采用一个或多个传感器,这些传感器对事件或状态进行测量(观察),并将该数据传送给网络中的另一个组件或节点,然后以某种形式呈现该数据以供分析。让我们看一个重要的医疗 IOT 解决方案的例子。

医疗应用

包括健康监测和健身在内的医疗应用作为消费品正受到越来越多的关注。这些解决方案涵盖了广泛的功能,如新 Apple Watch 内置的健身功能,以及记录您锻炼情况的健身带,甚至还有帮助您控制危及生命状况的医疗应用。例如,有一些解决方案可以帮助你管理糖尿病。

糖尿病是一种影响全球数百万人的疾病。有几种形式:最严重的是 1 型(diabetes.org/diabetes-basics/type-1/?loc=db-slabnav)。由于遗传缺陷、先天缺陷或胰腺损伤,1 型糖尿病患者不能产生足够的(或任何)胰岛素。胰岛素是一种激素,身体用来从血液中提取一种简单的糖,称为葡萄糖,由糖和淀粉产生,供细胞使用。未能监测您的血糖可能会导致危险的低血糖或高血糖水平,这两种情况都可能危及生命,如果不加以控制,可能会对内脏器官、神经和其他部位造成长期损害。这是最严重的情况。

Athletes and Diabetes

职业运动员是世界上身体最健康的人。许多人都是健康的典范,受到粉丝和其他运动员的赞赏。在过去,如果一名职业运动员感染了像 1 型糖尿病这样的疾病,他们的职业生涯就结束了。现在,随着现代医疗技术的发展,职业运动员开始克服自己的病情,继续比赛。

一个突出的例子是 Ryan Reed,他是 NASCAR Xfinity 赛车的赛车手,也是 16 号 Lily Diabetes Ford 的司机。2011 年,里德被诊断患有 1 型糖尿病,并被告知他将永远不会再参加比赛。从那时起,里德通过仔细监测自己的状况克服了障碍,重返赛场。

里德不仅回到了他热爱的运动,他还在代托纳国际赛道赢得了不止一次而是两次的赛季开幕系列赛冠军。里德证明了教育、警惕和技术可以让我们的生活变得更好。

1 型糖尿病患者必须监测他们的血糖,以确保他们正确使用药物(主要是胰岛素),并与健康的生活方式和饮食保持平衡。如果他们的血糖水平降得过低或过高,他们可能会出现一系列症状。更糟糕的是,极低的血糖水平非常危险,可能是致命的。

一种最新版本的血糖测试仪由一个小型传感器和一个通过蓝牙连接到传感器的监视器组成,该传感器可以留在体内长达一周。您将显示器佩戴在身上(或始终保持在 20 英尺以内)。该解决方案由 Dexcom ( dexcom.com/)销售,被称为连续血糖监测仪(CGM),允许患者通过手机与其他人分享数据。因此,患者将他们的 CGM 与他们的电话配对,然后通过互联网与其他人共享数据。这可能是你所爱的人,那些帮助你照顾他们的人,甚至是医疗专业人士。

图 1-2 显示了 Dexcom CGM 监视器和传感器的示例。监视器在左边,传感器和发射器在右边。这种传感器只有一个小针头那么大,可以在体内停留长达一周的时间。

A447395_1_En_1_Fig2_HTML.jpg

图 1-2。

Dexcom Continuous Glucose Monitor with Sensor

一项名为 Dexcom Share 的功能允许患者通过手机上的应用向他人提供他们的数据。也就是说,患者的手机将数据传输到 Dexcom 云服务器,然后这些数据将被发送给任何拥有 Dexcom Share 应用并被授予查看数据权限的人。图 1-3 显示了来自 Dexcom Share iOS 应用的 Dexcom Share CGM 报告示例,它允许您轻松快速地检查朋友或亲人的血糖。

A447395_1_En_1_Fig3_HTML.jpg

图 1-3。

Dexcom Share App Report

该应用不仅允许数据可视化,还可以传递低血糖或高血糖水平的警报,这对患有其他疾病或糖尿病并发症的患者具有深远的影响。例如,如果患者在独自一人、无行为能力或无法接受治疗时血糖水平下降,使用 Dexcom Share 应用的亲人可以通过检查患者做出反应,并可能避免严重的糖尿病事件。

虽然这种解决方案是通过专有应用连接到互联网的单个传感器,但它是医疗 IOT 设备的一个极好的例子,不仅可以改善患者的生活,还可以改善护理他们的每个人的生活。

Dexcom 还提供了一个名为 Dexcom Studio ( http://dexcom.com/dexcom-studio )的免费 Windows 应用,允许患者查看他们的监视器收集的数据,并生成大量报告,他们可以使用这些报告来查看他们的血糖水平。报告包括平均值、模式、每日趋势等。他们甚至可以与他们的医生分享他们的数据。图 1-4 显示了加载典型数据的 Dexcom Studio 示例。

A447395_1_En_1_Fig4_HTML.jpg

图 1-4。

Dexcom Studio What About Blood Glucose Testers – Glucometers?

在像 Dexcom CGM 这样的解决方案出现之前,糖尿病患者必须使用手动测试仪。传统的血糖测试仪是一次性使用的,需要患者刺破手指或手臂,并在测试条上抽取少量血液。虽然这种设备已经使用了很多年,但直到最近,制造商才开始制造具有记忆功能,甚至可以连接到笔记本电脑或手机等其他设备的血糖测试仪。这些设备的最终发展是像 Dexcom 这样的解决方案,它已经成为一种医疗 IOT 设备,可以提高糖尿病患者的生活质量。

结合可编程警报,您和您所爱的人可以帮助管理糖尿病的影响。如果你有一个患有糖尿病的爱人,CGM 值得每一分钱来让你安心。这是 IOT 的真正力量,体现在一个潜在的救命解决方案中。

汽车 IOT 解决方案

另一个个人 IOT 解决方案是使用互联网连接的汽车功能。最古老的产品之一叫做安吉星(onstar.com ),通用汽车公司(GM)的最新款和新款汽车上都有这种产品。虽然 OnStar 是一种基于卫星的服务,有几个级别和许多收费选项,但它结合了互联网,允许与车主通信。事实上,最新的通用汽车配备了内置的 WiFi 接入点!更好的是,在我看来,有一些基本功能是免费提供给 GM 所有者的,非常有价值。

免费的基本功能包括通过电子邮件发送给您的定期维护报告,以及使用手机上的应用解锁、锁定、远程启动的能力——所有功能都在您的钥匙链上远程实现。如果你曾经把钥匙锁在车里,这是一个很酷的功能!图 1-5 显示了 iOS 上的远程密钥卡应用示例。当然,还有更多的付费功能,包括导航、电话、WiFi 和电话支持。

A447395_1_En_1_Fig5_HTML.jpg

图 1-5。

OnStar App Key Fob Feature

OnStar 应用通过连接到云中的 OnStar 服务,请求通过 OnStar 卫星网络发送到车辆的功能(例如,解锁)。因此,这是 IOT 解决方案如何使用多种通信协议的绝佳范例。

我最喜欢的功能是维护报告。您将收到一封电子邮件,其中概述了您车辆的维护状态。该报告包括机油寿命、轮胎压力、发动机和变速器警告、排放、安全气囊等内容。图 1-6 显示了您将收到的一封典型电子邮件的摘录。

A447395_1_En_1_Fig6_HTML.jpg

图 1-6。

OnStar Maintenance Report

请注意显示的信息。实际数据从您的车辆传输到 OnStar。例如,里程表读数和轮胎压力数据直接取自车辆的车载数据存储器。也就是说,来自传感器的数据被读取、解释并为您生成报告。该功能展示了 IOT 解决方案中的自动数据编辑如何帮助我们保持车辆处于良好的机械状态,并在需要维护时发出预警。这为我们提供了最好的服务,帮助我们保持我们的车辆处于最佳状态,从而处于高转售价值的状态。

我应该指出,通用汽车并不是唯一提供此类服务的汽车制造商。许多其他人正在开发他们自己的解决方案,从类似 OnStar 的功能集到专注于娱乐和连接的解决方案。

车队管理

IOT 解决方案的另一个例子是车队管理系统。 6 车队管理系统是在 IOT 这个词出现之前开发和部署的,它允许企业监控他们的汽车、卡车、船只——几乎是任何移动设备——不仅可以跟踪他们的当前位置,还可以使用位置数据(一段时间内获得的 GPS 坐标)来规划更有效的路线,从而降低运输成本。

车队管理系统不仅仅是为了路由。事实上,车队管理系统还允许企业监控每个单元进行诊断。例如,可以知道每辆卡车中有多少燃料,上次维护是什么时候进行的,或者更重要的是,下次维护是什么时候,等等。车辆地理跟踪和诊断的结合被称为远程信息处理。图 1-7 为车队管理系统图。

A447395_1_En_1_Fig7_HTML.jpg

图 1-7。

Fleet Management Example7

在图中,您将看到 GPS 系统在跟踪位置方面的应用,以及卫星通信在传输诊断、有效负载状态等附加数据方面的应用。所有这些最终都会通过互联网,业务分析师可以访问这些数据。

你可能认为船队管理系统只适用于大型航运公司,但随着 GPS 模块甚至微控制器市场的激增,任何人都可以创建船队管理系统。也就是说,它们不需要花费数百万美元来开发。

例如,如果您拥有一家自行车送货公司,您可以轻松地将 GPS 模块与每个送货人的蜂窝或无线连接相结合,以跟踪他们的位置、平均行程时间等。更具体地说,您可以使用这样的解决方案,通过允许将包裹从一个递送人传递到另一个递送人,而不是在每次完成一组递送后让他们返回仓库,来最大限度地减少递送时间。

Camera Drones and The IOT

IOT 的一个可能用途是让无人机产生的数据在互联网上可用。有些人可能觉得无人机侵犯了隐私,我同意无人机被滥用或违反既定法律的情况。幸运的是,绝大多数无人机所有者遵守当地法律、法规和业主的意愿。 8

然而,无人机有许多合法用途,无论是陆基、空基还是海基。例如,我可以想象家庭监控解决方案,您可以通过查看固定摄像头的数据和移动无人机的数据来远程检查您的家。就我个人而言,我希望看到一种解决方案,允许我编程一个预先确定的哨兵飞行路径,用飞行的无人机监控我的财产。

虽然一些供应商有支持 WiFi 的无人机,但没有多少消费级选项可以通过互联网实时传输数据。但是,有一些选项允许您直接从无人机上发布视频和照片。我的一架无人机是 Yuneec Breeze ( www.yuneec.com/en_US/products/breeze/overview.html ),它允许我将从无人机上收集的照片和视频发布到社交媒体上。有趣的是,这些无人机被称为“自拍无人机”,因为它们具有允许自主模式的功能,包括无人机跟随你拍摄你的滑稽动作,或围绕你的位置,以及其他有趣的功能。

虽然这些新的无人机需要手动操作来发布数据,但我们看到包含无人机的实时 IOT 解决方案只是时间问题。当然,当前的争议以及美国政府注册和跟踪无人机的行动,以及对其使用的越来越多的限制,可能会限制无人机和包括无人机获取的数据的 IOT 解决方案的扩展。

IOT 和安全

最近一连串的大规模数据泄露事件证明,基本的安全性还不够好。我们已经看到了从直接盗窃到利用从 Target 等非常知名的企业(超过 4000 万个信用卡号码可能已被泄露)和美国人事管理办公室等政府机构(超过 2000 万个社会安全号码被泄露)窃取的数据的各种情况。

IOT 解决方案也不能幸免于安全威胁。事实上,随着 IOT 解决方案越来越多地融入我们的生活,我们的个人数据也将越来越多。因此,必须非常重视安全性,并从一开始就将其纳入解决方案中。

这包括我们自己开发的解决方案。更具体地说,如果您设计一个供自己使用的气象站,您应该采取合理的措施来确保数据不会被意外或故意利用。您可能认为天气数据没有高风险,但是考虑一下这样一种情况,您为您的传感器包括 GPS 坐标(一个合理的功能),以便人们可以看到天气在哪里被观察到。如果有人可以看到这些信息,并确定该解决方案使用了互联网连接,他们就有可能获得对互联网设备的物理访问权,并可能利用它来进一步渗透和利用您的系统。因此,安全性不仅仅与数据有关;它应该涵盖解决方案的所有方面,从数据到软件到硬件到物理访问。

您可能需要考虑在以下四个方面花费额外的精力,以确保您的 IOT 解决方案受到良好的安全保护。正如您将看到的,这包括您应该为现有基础设施、计算机甚至安全计算习惯考虑的几件事情。通过利用这些领域,您将构建一个分层的安全方法:通常称为深度防御方法。

Do I Really Need To Worry About Security?

如果您想知道为什么我在一本关于 IOT 和 Python 的初学者书籍中包含了这一部分,请考虑一下您最终想用从这本书中学到的知识做什么。如果您只对学习如何使用新的 MicroPython 板感兴趣,并且没有进一步开发的愿望,那么您可能想浏览这些部分。但是,如果您的目标包括制作您部署的 MicroPython IOT 解决方案——特别是如果您计划将其连接到互联网——您将需要考虑解决方案的安全性。无论哪种方式,我强烈建议阅读并遵循这些提示来保护您的 IOT 解决方案。

安全始于家庭

在将 IOT 解决方案引入您的家庭网络之前,您应该考虑采取预防措施,以确保家庭网络上的机器受到保护。这一点很重要,因为如果有人进入您的家庭网络,他们就可以进行各种各样的恶意活动。

最常见的错误是没有保护家庭 WiFi 网络。这不仅意味着你的邻居可以跳到你的网络上占用你的带宽,还意味着他们可以离开你的 IOT 设备、电脑、电器等进入你家庭网络的系统。,容易受到攻击。

幸运的是,有一些保护家庭网络的最佳实践可以帮助降低这些风险。其中包括以下内容。密码:这似乎是一件简单的事情,但是一定要确保在你所有的电脑和设备上使用密码。此外,采用良好的密码习惯,例如要求较长的字符串、混合大小写、数字和符号,以确保密码不容易被猜到。 9

  • 保护您的 WiFi:如果您有 WiFi 网络,请确保添加密码并使用最新的安全协议,如 WPA2,或者更好的是,一些无线路由器的内置安全设置功能。
  • 使用防火墙:您还应该使用防火墙来阻止所有未使用的端口(TCP 或 UDP)。例如,锁定除您的解决方案使用的端口(如 html 的端口 80)之外的所有端口。
  • 限制物理访问:锁上门!仅仅因为你的网络有一个伟大的密码,你的计算机使用超级世界间谍加密生物识别访问,如果有人可以直接访问你的网络硬件,这些事情就没有意义。对于 IOT 解决方案,这意味着任何外部组件都应安装在防篡改外壳中或锁定起来,以免被发现。这也包括任何网络布线。

其中一些你可能知道如何做,但其他的可能需要一个更了解设备和网络的朋友的帮助。例如,如果你不知道什么是防火墙,请别人帮助你。一点额外的安全是值得努力学习如何设置防火墙的基础知识的。

保护您的设备

如上所述,您的 IOT 设备也需要得到保护。要考虑的一些实践包括以下内容。

  • 使用密码:在任何安装了操作系统的设备上,始终为用户帐户添加密码。这包括确保重命名任何默认密码。例如,您可能会认为一个小小的 Raspberry Pi 太小了,不足以成为安全问题,但是如果您认为这些设备运行的是现有的最强大的操作系统之一(Linux 形式),Raspberry Pi 可能是一个非常强大的黑客工具。
  • 让你的软件保持最新:你应该尽量使用最新版本的软件。这包括操作系统以及您可能正在运行的任何固件或编程工具。较新的版本通常具有更高的安全性或更少的安全漏洞。
  • 如果你的软件提供了安全功能,使用它们:如果你的设备上有服务器或服务在运行,并且它们提供了诸如丢失密码自动锁定的功能,打开它们。并不是所有的软件都有这些功能,但是如果它们是可用的,它们可以是击败重复攻击的一个很好的方法。

使用加密

这是一个经常被忽视的领域。虽然该选项通常仅用于传输机密数据的解决方案,如商用 IOT 设备,但如果您计划发送您认为敏感的数据,如果您在存储数据时加密数据,并在传输数据时加密通信机制,则可以进一步保护您自己和您的数据。如果你加密了你的数据,即使有人获得了存储设备的物理访问权,这些数据也是无用的,因为他们不容易破译加密。像处理计算机密码一样小心处理加密密钥和密码。

安全性不会止于云端

将 IOT 设备连接到云服务需要考虑很多因素。事实上,微软和其他公司已经使得在你的 IOT 解决方案中使用 can 服务变得非常容易。但是,对于安全性和您的 IOT 数据,有两个重要的注意事项。

  • 你需要云吗?:你应该考虑的第一件事是,你是否需要把你的任何数据放在云端。通常情况下,云服务使存储和查看您的数据变得非常容易,但有必要这样做吗?例如,当你在工作时,你可能非常关心并且非常渴望查看你的狗在哪里度过的后勤数据,但是还有谁会关心查看这些数据呢?在这种情况下,没有必要将数据存储在云中以供每个人使用。
  • 不要放松!:许多人似乎在使用云服务时放松了警惕。不管出于什么原因,他们认为云更加安全。事实是,它不是!事实上,在云中工作时,您必须应用与您自己的网络、计算机和安全策略完全相同的安全最佳实践。事实上,如果有的话,你需要更加警惕,因为云服务在防止物理访问方面不受你的控制(无论多么遥远和不太可能),也不能保证你的数据不会与数十、数百甚至数千其他用户的数据在同一设备上。

既然我们已经知道了应该如何在我们的项目中包含安全性,那么让我们简单地看一下我们将在本书中使用的编程语言——Python。

Python 和 IOT

Python 是一种高级的、解释性的、面向对象的脚本语言。Python 的最大特点之一是拥有一个清晰、易于理解的语法,读起来尽可能接近英语。也就是说,即使你没有学过 Python,你也应该能够阅读 Python 脚本并理解它。Python 也比其他语言有更少的标点符号(特殊符号)和更少的语法机制。

以下是 Python 的一些关键特性。

  • 解释器在运行时处理 Python。不使用编译器。
  • Python 通过类和方法支持面向对象的编程结构。
  • 对于初级程序员来说,Python 是一种很好的语言,并且支持各种应用的开发。
  • Python 是一种脚本语言,但可用于广泛的应用。
  • Python 非常受欢迎,在全世界范围内使用,这给了它一个巨大的支持基础。
  • Python 关键字少,结构简单,语法定义清晰。这使得学生能够很快学会这门语言。

起源

Python 由吉多·范·罗苏姆于 20 世纪 80 年代末至 90 年代初在荷兰国家数学和计算机科学研究所开发,并由该研究所的核心开发团队维护。它源自许多语言并受其影响,包括 Modula-3、C、C++,甚至 Unix shell 脚本语言。

关于 Python 的一个有趣的事实是,它是以 BBC 节目“蒙蒂 Python 的飞行马戏团”命名的,与同名的爬行动物没有任何关系。在源代码文档中引用 Monty Python(甚至是对错误消息的幽默转移)是非常常见的,虽然一些专业开发人员可能会对这种暗示感到畏缩,但 Python stas11认为这是展示您的 Python 街头信誉。如果你喜欢 Monty Python,我鼓励你在代码中使用节目片段。我喜欢玩的一个地方是打印信息。我最喜欢的是这样的: 12

> DUPLICATE FILE ERROR: He says they've already got one!

有些人可能想知道像 Python 这样的语言怎么可能有助于编写 IOT 解决方案。答案是一个叫做 MicroPython 的很酷的产品。简而言之,MicroPython 是一个压缩的、优化的 Python 3 代码,已经加载到硬件中。这意味着,MicroPython 芯片可以直接在硬件上运行 Python 代码,而不是必须在操作系统上运行解释器来执行 Python 代码。不需要操作系统。事实上,MicroPython 内置了基本的文件 I/O。

我们将在下一章学习更多关于 MicroPython 的知识。对于那些想探索 IOT,但不想学习复杂的编程语言或花大量时间学习新的操作系统、工具和硬件的人来说,这是一个令人兴奋的新选择。但是首先,让我们看看使用 MicroPython 有多简单。

在线 Python 模拟器

对于那些渴望体验使用 Python 控制硬件的人来说,micropython.org 的好人们有一个在线交互式 MicroPython 模拟器( http://micropython.org/live ),使用最流行的 MicroPython 板之一 pyboard ( www.adafruit.com/products/2390 )。当您访问该网站时,您会看到一个界面,其中有一个 pyboard 连接到几个设备的实时视频图像,这些设备包括伺服系统、led、LCD 面板,甚至还有 SD 驱动器。

模拟器有几个示例脚本(Python 程序称为脚本),您可以试用。有脚本来打开发光二极管,打印到液晶显示器,甚至移动伺服。模拟器允许您选择一个脚本,并将其提交到队列中运行。如果队列中没有其他脚本,您将看到您选择的脚本几乎立即运行。图 1-8 显示了一个打开 LED 的简单脚本示例。在这种情况下,是电路板上的 LED。

A447395_1_En_1_Fig8_HTML.jpg

图 1-8。

MicroPython Simulator - LED on (courtesy of micropython.org)

请注意,在图像中,我们看到几个部分或面板。从左上方顺时针方向,我们可以看到当前代码(当前代码)和板上运行的代码的当前输出(当前输出),运行中的板的视频馈送,显示您执行的代码的脚本输出(使用print()函数)的区域(我的输出),以及允许您滚动可用示例脚本并执行它们的面板(我的代码)。以下按钮允许您控制脚本。

  • 快跑!:执行“我的代码”面板中的当前代码
  • 清除:清除“我的代码”面板
  • 向左箭头:循环查看示例脚本列表
  • 右箭头:在示例脚本列表中向前循环

模拟器最酷的一点是,你可以使用“我的代码”面板编写自己的代码!只需单击 clear 按钮,将光标放在第 2 行(在 import 语句下面),然后开始输入。当你准备好了,点击运行!按钮。图 1-9 显示了一个脚本的例子,它打印了两行代码,显示了我编写的在板上运行的代码。

A447395_1_En_1_Fig9_HTML.jpg

图 1-9。

MicroPython Simulator - Custom code (courtesy of micropython.org)

图 1-10 和 1-11 显示了另外两个示例脚本。

A447395_1_En_1_Fig11_HTML.jpg

图 1-11。

MicroPython Simulator – UART Calculations (courtesy of micropython.org)

A447395_1_En_1_Fig10_HTML.jpg

图 1-10。

MicroPython Simulator – LCD (courtesy of micropython.org)

虽然您可能没有完全理解示例中的 Python 代码,但我建议您花一些时间来尝试一下这些脚本,这样您就可以看到用 MicroPython 控制硬件是多么有趣(而且只需要很少的代码)。如果没有别的,这个模拟器应该会让您有兴趣了解更多关于 MicroPython 的知识。

摘要

对我们所有人来说,物联网是一个激动人心的新世界。我们这些内心年轻但年龄足够大的人还记得杰特森的电视连续剧,回忆起在虚幻的土地上看到的可能。尽管会说话的烤面包机、从公文包里蹦出来的会飞的汽车和有态度的机器人,几十年前的电视幻想正在成为现实。我们有手表兼作电话和视频播放器,我们可以从世界各地解锁我们的汽车,了解我们的狗是否已经出门,甚至可以从城市的另一端开门。所有这些都是可能的,并且随着 IOT 的出现而发挥作用。

在本章中,我们发现了什么是 IOT,并看到了一些著名的 IOT 解决方案的例子。我们还发现了微软如何通过 Raspberry Pi 硬件将其 Windows 10 操作系统扩展到 IOT,从而为 Windows 用户打开大门。对于那些不想学习基于 Linux 的操作系统的细微差别的人来说,这是一个非常令人兴奋的机会,他们可以从一个熟悉的、充分理解的平台探索硬件和 IOT 的世界。

在下一章,我们将发现更多关于 MicroPython 的内容,包括如何在 MicroPython 中编程。正如你将看到的,这并不困难。然后,我们将在第三章中更详细地探讨我们将用于运行 MicroPython 的硬件,以完成我们的 IOT MicroPython 入门之旅。

Footnotes 1

https://en.wikipedia.org/wiki/Internet_of_Things

2

例如,一切似乎都是云——这,云——那时候什么都没有改变。

3

https://en.wikipedia.org/wiki/Internet_refrigerator

4

https://www.howtogeek.com/260896/why-buying-a-smart-fridge-is-a-dumb-idea/

5

https://pixabay.com/en/network-iot-internet-of-things-782707/

6

https://en.wikipedia.org/wiki/Fleet_management

7

埃里克狩猎——通过 cc by-sa 3.0(“??”)。

8

截至 2015 年 12 月 21 日,美国重量超过 0.55 磅的无人机。必须在飞行前注册。 https://registermyuas.faa.gov/

9

您还需要在密码的复杂性和您记忆密码的能力之间取得平衡。如果你一定要写下来,你刚刚打败了你自己的安全!你可能会惊讶地发现,猜测别人密码的第一条规则是看他们的键盘下面。那是大多数人放便利贴和密码的地方。不要那样做。

10

巨蟒剧团指的是一群喜剧演员,而不是一个人。然而,这部喜剧无疑是精彩的。https://en.wikipedia.org/wiki/Monty_Python

11

Pythonistas 是 Python 开发专家,是所有 Python 事物的倡导者。

12

http://mzonline.com/bin/view/Python/HolyGrailScene9/

二、MicroPython 简介

现在,我们已经了解了更多关于物联网的知识,并看到了一些 MicroPython 的演示,是时候了解更多关于 MicroPython 的知识了——我们如何开始,它如何工作,以及您可以使用自己的 MicroPython 板做什么的示例。

即使对于没有任何编程经验的人来说,学习 MicroPython 也是非常容易的。事实上,学习 MicroPython 所需要的只是一点耐心和一点时间来习惯使用 MicroPython、电路板和电子设备所特有的语法和机制。正如我们将会看到的,只要有一点点知识,你就可以做很多事情。

在这一章中,我们将学习更多关于 MicroPython 的知识,包括如何开始使用最流行的主板之一的概述。如果你还没有冲浪板,也不用担心;本章中的例子旨在让你了解你能做什么,而不是一个详细的教程。也就是说,我们将会看到本章中使用的主板以及第三章中其他主板的详细教程。我们还将在第四章中更详细地探讨编程 Python。

Tip

我将本机运行 MicroPython 或可以加载 MicroPython 二进制文件的微处理器板称为“MicroPython 兼容板”或“MicroPython 板”。

我们先来看看什么是 MicroPython,包括为什么创建它,如何入门。

入门指南

使用 Python 语言控制硬件已经有一段时间了。Raspberry Pi、pcDuino 和其他低成本计算机以及类似主板的用户已经拥有了使用 Python 控制硬件的优势。在这种情况下,他们在基于 Linux 的本地操作系统上使用了完整版本的 Python 编程语言。

然而,这需要构建特殊的库来与硬件通信。这些库旨在与通用输入输出(GPIO)引脚接口。GPIO 引脚通常出现在板上的一排或多排公引脚中。一些电路板使用母头引脚。

虽然这些电路板使那些想要开发电子项目的人成为可能,但它要求用户购买电路板以及键盘、鼠标和显示器等外围设备。不仅如此,用户还必须学习操作系统。对于那些不习惯 Linux 的人来说,这本身就是一个挑战。

你可能想知道微控制器,如广受欢迎的 Arduino ( arduino.cc)或 Espressif,也称为 ESP 板(espressif.com)。对于那些板卡,你必须用一种类 C 语言 1 来编程,这可能是有些人不愿意学的。

MicroPython 的愿景是将学习 Python 的简单性与微控制器板的低成本和易用性结合起来,这将允许更多的人在艺术和科学项目中使用电子产品。初学者不必学习新的操作系统或学习更复杂的编程语言。答案是 MicroPython。图 2-1 显示了来自 Adafruit 的技能徽章 2 形式的 MicroPython 标志。

A447395_1_En_2_Fig1_HTML.jpg

图 2-1。

MicroPython Logo Skill Badge (courtesy of adafruit.com)

那很酷,不是吗?是集成电路(芯片)上的一条蛇(巨蟒)。可以在 www.adafruit.com/products/3271 向 Adafruit 订购此技能徽章。如果你没有任何东西可以贴上补丁,Adafruit 还储备了一个俏皮的 MicroPython 贴纸( www.adafruit.com/products/3270 )。当你读完这本书的时候,我建议你买一本,骄傲地展示出来。

起源

MicroPython 3 由 Damien P. George、Paul Sokolovsky 和其他贡献者创建和维护。它被设计成 Python 3 语言的精简、高效版本,并安装在一个小型微控制器上。由于 Python 是一种解释语言,因此(一般来说)比编译语言慢,所以 MicroPython 被设计得尽可能高效,以便它可以在通常比典型的个人计算机慢得多且内存少得多的微控制器上运行。

Compiled VS. Interpreted

编译语言使用称为编译器的程序将源代码从人类可读的形式转换为二进制可执行的形式。这种转换涉及几个步骤,但一般来说,我们获取源代码并将其编译成二进制形式。由于它是二进制形式,处理器可以直接执行生成的语句,而不需要任何额外的步骤(同样,一般情况下)。

另一方面,解释语言不是编译的,而是用一个叫做解释器的程序动态地转换成二进制形式(或中间二进制形式)。Python 3 提供了一个 Python 可执行文件,它既是解释器又是控制台,允许您在输入代码时运行代码。Python 程序一次运行一行代码,从文件的顶部开始。

因此,编译语言比解释语言更快,因为代码是为执行而准备的,并且不需要中间的实时步骤来在执行之前处理代码。

另一方面,Arduino 等微控制器板需要一个编译步骤,您必须在电脑上执行该步骤,并首先将二进制可执行文件加载到板上。相比之下,由于 MicroPython 的解释器直接在硬件上运行,我们不需要中间步骤来准备代码;我们可以直接在硬件上运行解释语言!

这使得硬件制造商可以制造小型、廉价的主板,在与微处理器相同的芯片上包含 MicroPython(通常情况下)。这使您能够连接到电路板,编写代码,并执行它,而无需任何额外的工作。

您可能会想,将 Python 3 缩减到适合内存有限的小芯片的大小,这种语言被剥离了,缺少了一些特性。这不可能比事实更进一步。事实上,MicroPython 是 Python 3 核心特性的完整实现,包括紧凑的运行时和交互式解释器。它支持读写文件、加载模块、与 GPIO 引脚等硬件交互、错误处理等等。最重要的是,Python 3 代码的优化允许它被编译成需要大约 256K 内存来存储二进制文件的二进制文件,并且运行时只需要 16k 的 RAM。

然而,有一些事情 MicroPython 没有从 Python 3 语言中实现。下面几节将向您介绍使用 MicroPython 能做什么,不能做什么。

MicroPython 功能

MicroPython 最大的特点当然是运行 Python。这允许你创建简单的、有效的、易于理解的程序。我认为,这是它相对于 Arduino 等其他主板的最大优势。下面列出了 MicroPython 支持的一些特性。我们将在本书中更详细地看到这些特性。

  • 交互式解释器:MicroPython 板内置了一个特殊的交互式控制台,您可以通过 USB 电缆(或在某些情况下通过 WiFi)连接到板上来访问它。这个控制台被称为读取-评估-打印循环,它允许您键入代码并一次执行一行。这是一个很好的方法来原型化你的代码或者只是在你开发的时候运行一个项目。
  • Python 标准库:MicroPython 也支持许多标准 Python 库。总的来说,你会发现 MicroPython 支持 80%以上最常用的库。其中包括解析 JavaScript 对象符号(JSON), 4 套接字编程,字符串操作,文件输入/输出,甚至正则表达式支持。
  • 硬件级库:MicroPython 具有内置的库,允许您直接访问硬件来打开或关闭模拟引脚、读取模拟数据、读取数字数据,甚至使用脉宽调制(PWM)来控制硬件,这是一种通过快速调制设备功率来限制设备功率的方法。例如,使风扇比满功率运转时旋转得更慢。
  • 可扩展:MicroPython 也是可扩展的。对于需要在底层(用 C 或 C++)实现一些复杂的库并在 MicroPython 中包含新库的高级用户来说,这是一个很好的特性。是的,这意味着您可以构建自己独特的代码,并使其成为 MicroPython 特性集的一部分。

回答你的问题,“我能用 MicroPython 做什么?,“答案还挺多的!您可以控制连接到 MicroPython 板上的硬件,编写代码模块以扩展您的程序的功能,并将它们存储在 SD 卡上以供以后检索(就像您在 PC 上使用 Python 一样),等等。您可以连接的硬件包括打开和关闭 led,驱动伺服系统,读取传感器,甚至在 LCD 上显示文本。一些主板还以 WiFi 无线电的形式提供网络支持。几乎任何你可以用其他微控制器板做的事情,你都可以用 MicroPython 板来做。

然而,在芯片上运行 MicroPython 有一些限制。

MicroPython 限制

MicroPython 最大的限制是易用性。Python 的易用性意味着代码是动态解释的。尽管 MicroPython 得到了高度优化,但对解释器来说还是有损失的。这意味着需要高精度的项目,如高速数据采样或通过连接(USB、硬件接口等)进行通信。)可能跑得不够快。对于这些领域,我们可以通过用处理低级通信的优化库来扩展 MicroPython 语言来克服这个问题。

MicroPython 使用的内存也比 Arduino 等其他微控制器平台多一点。通常,这不是一个问题,但是如果你的程序开始变大,你应该考虑一下。使用大量库的大型程序消耗的内存可能会超出您的预期。这又一次与 Python 的易用性有关——这是另一个要付出的代价。

最后,如前所述,MicroPython 没有实现所有 Python 3 库的所有特性。但是,您应该会发现它拥有构建 IOT 项目所需的一切(甚至更多)。

Are My Python Skills Applicable To Micropython?

如果你已经学会了如何用 Python 编程,你可能会期望看到一些与众不同的东西,甚至是关于 MicroPython 的奇怪的东西。好消息是,使用 MicroPython 只需要掌握 Python 技能。的确,MicroPython 和 Python 使用相同的语法;没有什么新东西要学。正如您将在接下来的几章中看到的,MicroPython 实现了 Python 库的一个子集,但仍然是非常 Python 化的。

MicroPython 运行于什么之上?

由于 MicroPython 越来越受欢迎,运行 MicroPython 的主板有了更多的选择。其中一部分来自开发人员构建特定于处理器和平台的 MicroPython 编译版本,您可以下载并安装到主板上。这是增长最快的类别。

有两类板可以用来运行 MicroPython。首先是从工厂加载了 MicroPython 并且只运行 MicroPython 的主板。其中包括 Pyboard(最初的 MicroPython 板)和 WiPy。接下来是有可用固件选项在板上安装 MicroPython 的板,包括 ESP8266、Teensy 等。我们将在下一章看到更多关于这些板的内容。

接下来,让我们在下一节中从我们的 PC 上探索 Python,让您了解这种语言是什么样的,并有机会在不需要 MicroPython 板的情况下亲自尝试一下。

在您的电脑上体验 Python

由于 MicroPython 是 Python(只是为了优化的目的稍微缩小了一点),您可以在 PC 上运行 Python 并试验这种语言。我建议在你的 PC 上加载 Python,即使你已经有一个 MicroPython 板。你可能会发现用你的电脑来尝试更方便,因为你可以更好地控制环境。然而,您的 PC 将无法与电子元件或硬件(如 MicroPython 板)通信,因此虽然您可以在 PC 上做更多的事情,但您无法测试与硬件通信的代码。但是您可以测试基本的构造,比如函数调用、打印消息等等。

所以,何必呢?简单地说,在 MicroPython 板上尝试之前,使用您的 PC 调试 Python 代码将允许您完成项目的大部分工作。更具体地说,通过在您的 PC 上开发普通的东西,您消除了许多在 MicroPython 板上调试代码的潜在问题。这是新手程序员犯的头号错误——编写完整的解决方案而不测试较小的部分。从小处着手,一次只测试一小部分代码,只添加那些已经测试并证明可以正常工作的部分,这样总是更好。

您需要做的只是下载并安装 Python 3(例如,Python 3.6.2 是最新的,但新版本会定期发布)。以下部分简要描述了如何在各种平台上安装 Python。关于这里没有列出的平台的具体信息,参见 Python wiki:https://wiki.python.org/moin/BeginnersGuide/Download

Caution

Python 有两个版本——Python 2 和 Python 3。因为 MicroPython 是基于 Python 3 的,所以您需要安装 Python 3 版,而不是 Python 2 版。

但是首先,检查您的系统,看看是否已经安装了 Python。打开终端窗口(命令提示符)并键入以下命令。

python --version

如果安装了 Python,您应该会看到如下所示的内容。

$ python --version
Python 3.6.0

如果您看到了类似 Python 2.7.3 的版本,那么您的机器上仍然有机会安装 Python 3。有些系统同时安装了 Python 2 和 Python 3。要运行 Python 3,请使用以下命令。

python3

如果没有安装 Python 3 或者它是旧版本,请使用以下部分在您的系统上安装 Python。您应该始终安装最新版本。可以从 www.python.org/downloads/ 下载 Python 3。

在 Windows 10 上安装 Python 3

大多数 Windows 机器不包含 Python,您必须安装它。可以从 Python 官方网站( https://www.python.org/downloads/windows/ )下载 Python 3 for Windows。你会发现通常的 32 位和 64 位版本的 Windows installer 选项,以及一个基于 web 的安装程序和一个.zip格式。大多数人会使用 Windows installer 选项,但是如果您必须手动安装 Python,您可以使用其他选项。

下载 Python 后,就可以启动安装程序了。例如,在我的 Windows 10 机器上,我在名为最新 Python 3 版本——Python 3 . 6 . 0 的链接下下载了该文件。如果你向下滚动,你可以找到你想要的安装程序。例如,我点击了 Windows 64 位机器的安装程序。这下载了一个名为python-3.6.0-amd64.exe的文件,我将它放在我的Downloads文件夹中,并通过双击该文件来执行。

像大多数 Windows installer 安装一样,您可以通过各种屏幕来同意许可,指定您要安装它的位置,并最终启动安装。图 2-2 显示了安装程序运行的示例。

A447395_1_En_2_Fig2_HTML.jpg

图 2-2。

Installing Python on Windows 10 Tip

如果你遇到困难或者需要更详细的指导,请参阅《如何极客 www.howtogeek.com/197947/how-to-install-python-on-windows/ 的精彩文章。

安装完成后,您可以尝试上一节中的测试来验证安装。如果不修改路径变量,可能需要使用开始菜单上的 Python 控制台快捷方式来启动控制台。

在 macOS 上安装 Python 3

如果您使用的是 macOS,您可能已经安装了 Python,因为大多数 macOS 版本默认安装 Python。但是,如果您无法运行上面的 Python 版本命令或者版本不正确,您仍然可以从 Python 网站( https://www.python.org/downloads/mac-osx/ )下载最新的 Python 3。您会找到几个版本,但您应该下载可用的最新版本。

下载 Python 后,就可以启动安装程序了。例如,在我的 iMac 上,我在名为最新 Python 3 版本——Python 3 . 6 . 0 的链接下下载了最新的 Python 3 文件。如果你向下滚动,你可以找到你想要的安装程序。例如,我点击了 64 位机器的安装程序。这下载了一个名为python-3.6.0-macosx10.6.pkg的文件,我将它放在我的下载文件夹中并执行。

像大多数安装程序一样,您可以一步一步地通过各种屏幕,同意许可,指定您想要安装它的位置,最后开始安装。图 2-3 显示了安装程序运行的示例。

Note

根据您运行的 macOS 版本以及您的安全设置,您可能需要更改它们来运行安装程序,因为它不是由指定的开发人员签名的。请查看“系统偏好设置”中的“安全性与隐私”面板。

A447395_1_En_2_Fig3_HTML.jpg

图 2-3。

Installing Python on macOS

安装完成后,您可以尝试上一节中的测试来验证安装。

在 Linux 上安装 Python 3

如果您使用的是 Linux,安装 Python 的方式会因平台而异。例如,Ubuntu 使用 apt-get 命令,而其他发行版有不同的包管理器。使用您平台的默认包管理器安装 Python 3.6(或更高版本)。

例如,在 Debian 或 Ubuntu 上,我们使用以下命令安装 Python 3.6 包。第一个命令更新包,以确保我们拥有最新的包引用。第二个命令启动必要文件的下载并安装 Python。

sudo apt-get update
sudo apt-get install python3.6

Tip

在 16.10 之前的 Ubuntu 版本上,您可能需要在运行 update 命令之前添加 apt 存储库。使用命令sudo add-apt-repository ppa:jonathonf/python-3.6添加存储库并重新运行sudo apt-get update命令,然后安装 Python 3.6。其他平台可能有类似的解决方案。

图 2-4 展示了一个在 Kubuntu(Ubuntu 的一个变种)上安装的例子。您的平台输出可能略有不同。

A447395_1_En_2_Fig4_HTML.jpg

图 2-4。

Installing Python on Kubuntu (Linux)

注意,在这次安装中,我已经安装了 Python 2.7 和 Python 3.4。一旦我安装了 Python 3.6,我就可以简单地通过使用正确的解释器来运行这三个版本:2.7 版使用python,3.4 版使用python3,3.6 版使用python3.6。如果安装了以前版本的 Python,您的平台可能会有类似的约束。

安装完成后,您可以尝试上一节中的测试来验证安装。

运行 Python 控制台

现在让我们在电脑上运行一些测试。回想一下,我们可以通过打开一个终端窗口(命令提示符)并输入命令python(或python3python3.6,这取决于您的安装)来打开 Python 控制台。看到提示后,在提示处输入下面的代码(>>>)。这段代码将在屏幕上打印一条消息。结尾的--n是一个特殊的非打印字符,它发出回车(类似于按 Enter)来移动到新的一行。

print ("Hello, World!")

当你输入这段代码时,你会马上看到结果。回想一下,解释器的工作方式是每次执行一行代码——每次按回车键。但是,与运行存储在文件中的程序不同,您在控制台中输入的代码不会被保存。图 2-5 显示了一个在 macOS 上运行 Python 控制台的例子。注意,我输入了一个简单的程序——典型的“你好,世界!”例子。

A447395_1_En_2_Fig5_HTML.jpg

图 2-5。

Hello, World!

要退出控制台,输入quit()代码,如上图所示。

虽然这演示了如何在 PC 上运行 Python,但并不有趣。让我们看一些更复杂的东西。

用解释器运行 Python 程序

假设您的项目要求您将数据保存到文件中,或者可能从文件中读取数据。我们可以在我们的 PC 上对文件进行实验,而不是试图找出如何在 MicroPython 板上做到这一点!

在下一个例子中,我将数据写入文件,然后读取数据并打印出来。不要太担心理解代码——通读一下就好——它非常直观。清单 2-1 显示了这个例子的代码。我使用了文本编辑器,并将文件保存为 file_io.py。

# Example code to demonstrate writing and reading data to/from files

# Step 1: Create a file and write some data
new_file = open("log.txt", "w")    # use "write" mode
new_file.write("1,apples,2.5--n")   # write some data
new_file.write("2,oranges,1--n")    # write some data
new_file.write("3,peaches,3--n")    # write some data
new_file.write("4,grapes,21--n")    # write some data
new_file.close()  # close the file

# Step 2: Open a file and read data
old_file = open("log.txt", "r")  # use "read" mode
# Use a loop to read all rows in the file
for row in old_file.readlines():
    columns = row.strip("--n").split(",") # split row by commas
    print(" : ".join(columns))  # print the row with colon separator
    old_file.close()

Listing 2-1.File IO Example

我将代码保存到一个文件中,向您展示如何使用 Python 解释器使用以下命令执行 Python 脚本。如果使用 Windows,可能需要修改类似“python.exe file_io.py”的命令。

python ./file_io.py

清单 2-2 显示了运行脚本的结果。

$ python ./file_io.py
1 : apples : 2.5
2 : oranges : 1
3 : peaches : 3
4 : grapes : 21
Listing 2-2.Output for the File IO Example

请注意,代码通过将最初写入的逗号替换为空格、冒号和另一个空格来更改数据中的分隔符。代码通过用逗号将读取的行(字符串)分成几部分来实现这一点。因此,列数据包含三个部分,我们使用join()方法重新连接字符串并打印出来。花点时间通读代码,你会看到这些方面。如你所见,Python 易于阅读。

既然我们已经在 PC 上试验了 Python,那么让我们看看如何在典型的 MicroPython 板上使用 MicroPython。

MicroPython 如何工作

回想一下,MicroPython 设计用于小型微控制器平台。这些微控制器平台中的一些使用包含 MicroPython 二进制文件(库、基本磁盘 IO、引导等)的特殊芯片。)以及微控制器、存储器和支持组件。

像大多数微控制器一样,当您使用 MicroPython 板时,您必须首先编写代码并将其加载到板上。大多数 MicroPython 主板都有一个 USB 闪存驱动器,当您使用 USB 电缆将它连接到您的计算机时,它就会安装。这个闪存驱动器存储了几个文件,您可以通过修改来改变它的行为。您也可以将您的程序(脚本文件)复制到这个驱动器,以便在引导时执行。我们将在后面的章节中看到如何做到这一点。

您还可以使用 MicroPython 控制台,它非常类似于我们在上一节中看到的 Python 控制台。MicroPython 控制台被称为运行、评估、打印循环或 REPL 控制台。每次开发板通电时,在将程序加载到开发板上执行之前,控制台使启动和调试程序(排除错误)变得非常容易。

运行、评估、打印循环(REPL 控制台)

如果您使用过 Arduino 之类的微控制器板,您可能会熟悉以下一些内容。但是如果您没有使用过这样的程序或者没有使用过终端程序,我提供了您在三个主要平台上需要的所有步骤:Windows、macOS 和 Linux。以下部分将带您完成首次连接到电路板的过程。

我在这些例子中使用 Pyboard。您可以使用默认加载了 MicroPython 的任何其他板,但是在第一次开始使用该板之前,一定要查看供应商的文档。某些主板可能需要在首次使用前加载固件。

连接电路板

要开始使用 REPL 控制台,请使用 USB 转 micro USB 电缆(通常)将该板连接到您的计算机,该电缆通过 USB 供电并连接控制台。一旦主板启动(有些主板第一次启动可能需要 1-2 分钟),您就可以使用终端程序连接到主板。从那里,我们可以像使用 Python 控制台一样使用 REPL 控制台。

Tip

有些主板可能需要一段时间才能启动,因此如果您没有连接,请稍等片刻,然后再试一次。

当主板完成启动时,您可以使用电脑浏览主板上的驱动器。使用 USB 电缆连接 Pyboard 后,您应该会看到 USB 驱动器安装。例如,在 Windows 10 上,您可以使用文件资源管理器查看驱动器上的文件。图 2-6 显示了 Pyboard 上的文件示例。

A447395_1_En_2_Fig6_HTML.jpg

图 2-6。

USB Drive (Pyboard )

注意有两个文件的扩展名为.py:boot.py,在引导时执行(因此得名);和main.py,如果你想独立使用该板,你可以用你自己的程序替换它。同样,我们将在后面的章节中看到这一点。还有一些附加文件,如README.txt,其中包含一些使用电路板的说明;还有pybcdc.inf,这是一个 Windows 的设备驱动安装程序。

Tip

要在 Windows 10 以外的 Windows 版本上使用 Pyboard,您可能需要安装 USB 设备驱动程序。设备驱动程序包含在 Pyboard 的板载闪存驱动器中,以备不时之需。如果您有旧版本的 Windows 或在使用终端应用时遇到问题,您可以按照 http://micropython.org/resources/Micro-Python-Windows-setup.pdf 的优秀指南中所述安装设备驱动程序。

启动 REPL 控制台(Windows)

要使用 Windows 连接到 REPL 控制台,你需要一个像 PuTTY 这样的终端程序,它最初是由西蒙·塔瑟姆( http://www.chiark.greenend.org.uk/∼sgtatham/putty/latest.html )开发的。PuTTY 是一个简单的终端程序,非常容易使用,并且是为 Windows 平台开发的。

要安装 PuTTY,请选择适当的.msi文件(32 位或 64 位),下载它,然后双击该文件启动安装程序。按照提示完成安装。比如我下载了名为putty-64bit-0.68-installer.msi的文件。

现在您已经安装了 PuTTY,您必须知道要使用的正确端口。打开设备管理器,沿树向下导航,直到找到端口(COM & LPT)条目,然后单击将其打开。请注意,Pyboard 必须连接才能工作。您应该在子树中看到一个或多个条目,指示连接到 PC 的 COM 端口(和打印机端口)。Pyboard 将被简单地列为 USB 串行设备(COMn ),其中 n 是类似 COM1、COM2 等的数字。比如在我的 PC 上,它被列为 COM3。图 2-7 显示了一个来自我的电脑的例子。

A447395_1_En_2_Fig7_HTML.jpg

图 2-7。

Finding the COM Port on Windows 10

现在我们可以打开 PuTTY 并连接到 Pyboard。使用开始菜单上的快捷方式启动 PuTTY,或者在搜索框中键入 PuTTY,当 Cortana 找到该条目时单击它。当 PuTTY 打开时,您将看到一个对话框,您可以使用它通过网络上的终端进行连接。因为 Pyboard 是通过 COM 端口连接的,所以我们必须单击标有 Serial 的小单选按钮。然后,我们可以在串行线文本框中输入 COM 端口,并将速度设置为 115200。图 2-8 显示了一个正确配置的 PuTTY,用于连接到 COM3 上的 Pyboard(如设备管理器所示)。

A447395_1_En_2_Fig8_HTML.jpg

图 2-8。

Connecting to Pyboard Using PuTTY

准备就绪后,单击“打开”按钮。这将打开一个新的终端,启动 REPL 控制台,如图 2-9 所示。我输入了一个简单的语句来演示控制台正在工作。

A447395_1_En_2_Fig9_HTML.jpg

图 2-9。

The REPL Console (Windows 10) Tip

如果你不喜欢黑底白字的配色方案,你可以通过点击树控件中的颜色来改变它们。小心行事,因为您必须单独设置这些值(没有方案概念)。

启动 REPL 控制台(macOS 和 Linux)

要使用 macOS 或 Linux 进行连接,您可以使用以下命令。您唯一需要做的就是找到正确的设备。我用列出我的 macOS 系统上的设备的第一个命令来演示这一点。请在连接到 MicroPython 板(Pyboard)后执行此操作。

$ ls /dev/tty.usb*
/dev/tty.usbmodem1422
$ screen /dev/tty.usbmodem1422

继续插入您的主板,然后按照上面的说明打开一个控制台。控制台打开后,输入如下所示的代码。图 2-10 显示了在 Pyboard 上运行的 REPL 控制台的示例。

A447395_1_En_2_Fig10_HTML.jpg

图 2-10。

REPL Console (Pyboard)

你能从你电脑上的 Python 控制台看出区别吗?仔细看。除了输出顶部的版本声明,它们是相同的,这很好。

REPL 游戏机有一个奇怪之处。quit()不工作了。要退出某些板的控制台,您需要重置板或切断连接。虽然这看起来很奇怪,但我确信它在未来会得到改进,回想一下,我们通常不使用 REPL 控制台来运行我们的项目;相反,我们用它来测试代码,所以退出时的一点小问题不是大问题。

Caution

您应该避免简单地拔掉您的 MicroPython 板。有些主板,如 Pyboard,将它们的基本文件系统作为一个可挂载的 USB 驱动器。未弹出板就断开连接可能会导致数据丢失。

现在是时候来看看我们能用 MicroPython 做些什么了。下一节使用几个示例项目向您展示如何使用 MicroPython 板。我再一次用最少的硬件解释和细节来介绍这些例子。我们将在下一章学到更多关于硬件的知识。

使用 MicroPython 关闭并运行

如果你像我一样遇到新技术,你很可能想尽快开始。如果您已经有一个 Pyboard,那么您可以按照本节中的示例进行操作,并查看更多使用 MicroPython 的示例。为了简单起见,我们将只看到那些在板上运行的项目,不需要额外的组件,也不使用互联网。图 2-11 展示了来自 MicroPython.org(https://store.micropython.org/)的 Pyboard 1.1。

A447395_1_En_2_Fig11_HTML.jpg

图 2-11。

Pyboard with Headers

电路板的右侧是微型 USB 连接器,上方是微型 SD 读卡器。如果您查看这些连接器之间,您会看到四个小 led,如方形框所示。指示灯的左侧是一个标有 USR 的按钮。这些是我们将在以下示例中与之交互的组件。

Note

此处显示的 Pyboard 具有从 MicroPython.org 预焊的 GPIO 接头(板周围的插座排)。如果您订购 Pyboard,我建议您购买这个版本,尤其是如果您不知道如何或不想自己焊接接头的话。

我最喜欢这个板的地方是它有一个发光二极管(led)阵列和一个按钮,你可以用它来试验编写 MicroPython 项目。这使得 Pyboard 成为初学者的绝佳首选。

但是,如果你还没有冲浪板,也不用担心。同样,我包括这一部分是为了帮助你通过例子了解更多的可能性。一旦我们讨论了流行的板的细节以及如何在 MicroPython 中编程,我们将深入到更复杂的项目中,您可以在学习的过程中进行实验。

不幸的是,Pyboard 不提供任何形式的网络,因此它在 IOT 项目中的使用可能相当有限。幸运的是,有很多方法可以将你的 Pyboard 连接到互联网。

Note

您可以使用的兼容 MicroPython 的主板越来越多。我选择 Pyboard 来演示 MicroPython,因为 Pyboard 是最容易使用的板,而且相对便宜。我们将在第三章中了解更多关于其他选择的信息。如果你想买一个 Pyboard 来运行这些例子,你可以,但是你可能需要一个不同的板或者一个网络模块来用于连接到互联网的更高级的项目。

附加硬件

虽然下面的例子不需要任何额外的电子元件,但我想介绍几个你在本书后面会用到的关键元件。如果您没有这些组件,现在订购它们是一个好主意,这样当您到达示例项目时,您将拥有您所需要的。

除了 MicroPython 板之外,我认为这些组件是任何想要学习如何使用电子学和 MicroPython 的人的必备物品。这些包括一个基本的电子工具包,其中包含学习电子技术时需要的最常见的组件、试验板和跳线。我将在下面的章节中描述每一个。

基本电子套件

本书中的示例项目使用了几种常见的电子元件,如发光二极管、开关、按钮、电阻等。在爱好层面学习使用电子产品时,最大的挑战之一是买什么。我曾和一些人交谈过,他们多次去当地的电子商店购买他们需要的东西,似乎无论他们买什么都没有合适的组件。

幸运的是,电子产品零售商已经意识到了这个问题,现在他们提供了一个基本的电子套件,其中包含了许多更常见的组件。Adafruit ( adafruit.com/products/2975)和 Sparkfun ( sparkfun.com/products/13973)都提供这样的套件。虽然这两种套件都不会出错,但我最喜欢 Adafruit 套件,因为它有更多的组件(例如,更多的 led)。

Adafruit Parts Pal 封装在一个小型塑料外壳中,带有大量电子元件。图 2-12 显示了零件 Pal 套件。

A447395_1_En_2_Fig12_HTML.jpg

图 2-12。

Adafruit Parts Pal (courtesy of adafruit.com)

该套件包括以下组件:原型制作工具、led、电容器、电阻器、一些基本传感器等。事实上,这个工具包里的组件比你做很多实验所需的都要多。更好的是,该套件的价格仅为 19.95 美元,非常划算(而且这个箱子是一个巨大的奖励)。

  • 1x -带插销的储物箱
  • 1x 半尺寸试验板
  • 20x -公/公跳线- 3 英寸(75 毫米)
  • 10x -公/公跳线- 6 英寸(150 毫米)
  • 5x-5 毫米漫射绿色发光二极管
  • 5x-5 毫米漫射红色发光二极管
  • 1x-10 毫米扩散共阳极 RGB LED
  • 10x - 1.0uF 陶瓷电容
  • 10x - 0.1uF 陶瓷电容
  • 10x - 0.01uF 陶瓷电容器
  • 5x - 10uF 50V 电解电容器
  • 5x - 100uF 16V 电解电容器
  • 10x - 560 欧姆±5%轴向电阻
  • 10x - 1K 欧姆±5%轴向电阻
  • 10x 10K 欧姆±5%轴向电阻器
  • 10x - 47K 欧姆±5%轴向电阻
  • 5x - 1N4001 二极管
  • 5x - 1N4148 信号二极管
  • 5x - NPN 晶体管 PN2222 至-92
  • 5x - PNP 晶体管 PN2907 至-92
  • 2x - 5V 1.5A 线性稳压器- 7805 至-220
  • 1x - 3.3V 800mA 线性稳压器- LD1117-3.3 至-220
  • 1x - TLC555 宽电压范围、低功耗 555 定时器
  • 1x 光电池
  • 1x 热敏电阻(试验板版本)
  • 1x -振动传感器开关
  • 1x 10K 试验板微调电位计
  • 1x - 1K 试验板调整电位计
  • 1x 压电蜂鸣器
  • 5x - 6mm 触觉开关
  • 3x SPDT 滑动开关
  • 1x - 40 针分离式插头带
  • 1x - 40 针插座条

试验板和跳线

试验板是一种特殊的工具,它允许您插入电气元件,并提供各列之间的互连,以便您可以将两个元件的引线插入同一列,从而实现连接。电路板分为两排,便于在电路板中央使用 IC。电线(称为跳线或简称跳线)可用于将试验板上的电路连接到 MicroPython 板上。在本章的后面你会看到一个这样的例子。

幸运的是,Adafruit Parts Pal 配备了如图 2-13 所示的试验板。这是 Adafruit 公司的半块面包板。这种试验板被称为半板,因为它是标准试验板正常长度的一半。最重要的是,它适合零件 Pal 盒。

A447395_1_En_2_Fig13_HTML.jpg

图 2-13。

Half-sized Breadboard (courtesy of adafruit.com)

如果已经有了一些组件,或者决定购买一个不附带试验板的不同的基本电子套件,您可以从 Adafruit ( adafruit.com/products/64)单独购买试验板。

如果您的 MicroPython 板有公头引脚而不是母头引脚,您将需要一套不同的跳线。再说一次,Adafruit 有你需要的东西。如果您需要公/母跳线,请订购优质母/公扩展跳线–20 x 6(ada fruit . com/products/1954)。图 2-14 显示了一组公/母跳线。

A447395_1_En_2_Fig14_HTML.jpg

图 2-14。

Male/Female Jumper Wires (courtesy of adafruit.com)

现在,让我们来看看一些硬件的运行情况!

例 1:

在本例中,我们将编写代码来打开电路板上的一个 led。如果您按照图 2-11 所示的方向放置电路板,四个 led 从左到右排列为不同的颜色,分别为红色、绿色、黄色(橙色)和蓝色。如果你像我一样是右撇子,你可能会调整电路板的方向,使 USB 连接器在左边。在这种情况下,蓝色 LED 位于最左侧。

让我们写一些代码来打开蓝色 LED。与其简单地打开它,不如让我们使用一个称为循环的结构来每隔 250 毫秒打开和关闭它。因此,它会快速闪烁。在我解释代码之前,让我们看一下完整的代码。清单 2-3 展示了代码的样子。不用担心;我将在清单之后解释每一行代码。

#
# MicroPython for the IOT
#
# Example 1
#
# Turn on the blue LED (4 = Blue)
#
# Dr. Charles Bell
#
import pyb              # Import the Pyboard library

led = pyb.LED(4)        # Get the LED instance
led.off()               # Make sure it's off first

for i in range(0, 20):  # Run the indented code 20 times
    led.on()            # Turn LED on
    pyb.delay(250)      # Wait for 250 milleseconds
    led.off()           # Turn LED off
    pyb.delay(250)      # Wait for 250 milleseconds

led.off()               # Turn the LED off at the end
print("Done!")          # Goodbye!

Listing 2-3.Blink the Blue LED

代码的第一行是注释行。这些都被 MicroPython 忽略了,是一种向他人传达你的程序正在做什么的方式。在 REPL 控制台中输入代码时,可以随意跳过注释行。

接下来是一行用于导入 Pyboard 硬件库的代码(pyb)。该库专用于 Pyboard,并提供板上的所有元件。接下来的两行代码通过使用 Pyboard 库(pyb)初始化一个变量(led)来获得第四个 LED(蓝色的那个)。这就创建了一个我们可以使用的对象的实例。在这种情况下,我们立即通过调用led.off()关闭 LED。

接下来是代码的主要部分——一个循环!在这种情况下,它是一个for循环,设计用来运行它下面的代码块,如缩进所指示的那样运行 20 次。for循环使用计数器i和由range(0, 20)函数返回的 0 到 19 的值。在循环体(缩进部分)中,我们首先用led.on()打开 LED,使用 Pyboard delay()方法等待 250 毫秒,然后再次关闭 LED,再等待 250 毫秒。最后,我们关闭 LED 并打印一条消息,说明我们完成了。

您可以在 REPL 控制台中逐行输入该代码。#开头的可以跳过。一旦您输入了for循环语句,您将得到一个没有>>>提示的行。这很正常。使用两个空格键入下一行(用于缩进,为循环建立块)。第二次delay()调用后,在空白处像结束缩进块一样按回车键。现在看一下 Pyboard。蓝色 LED 应该在闪烁。请记住,REPL 控制台与 Python 控制台一样,在您输入代码时运行代码。图 2-15 显示了代码运行的一个例子(我在蓝色 LED 亮着的时候捕捉到的)。如果你想让它闪烁得更慢,只需调整delay()调用,将数值增加到 500 甚至 1000。

A447395_1_En_2_Fig15_HTML.jpg

图 2-15。

Running Example 1 Note

在某些平台上,如 macOS 和 Linux,您可以使用文本编辑器输入代码,然后将其复制并粘贴到 REPL 控制台。然而,这并不适用于所有平台或终端程序。你自己试试。

如果您想再次运行代码或出现错误,您必须首先关闭 PuTTY 窗口,然后弹出 USB 驱动器。一旦弹出,按下重置按钮(板上标有 RST)重新初始化 REPL 控制台。

例 2:

现在,让我们使用不同循环(while 循环)中的计数变量按顺序打开和关闭 led。在这种情况下,我们必须一次打开一个 led,然后在短暂的延迟后关闭。因此,它就像前面的例子一样,但是,正如你将看到的,它展示了更多的复杂性。因为它更复杂,所以在展示完整的代码之前,我将分几部分浏览代码。

像 Pyboard 的所有程序一样,我们从导入 Pyboard 硬件库开始。

import pyb              # Import the Pyboard library

这个例子使用了两个循环。首先,我们循环关闭所有的 led。下面显示了如何做到这一点。请注意,我使用了 1 到 4 范围内的计数变量。在循环体中,我使用 Pyboard 硬件库中的计数变量获取 LED,并将其保存在名为led ( led = pyb.LED(j))的变量中,然后用led.off()将其关闭。在开始时关闭 led 是一个好习惯,它将处理任何让代码运行或中断代码运行的事件,以便一个或多个 led 保持打开。您也可以通过重置电路板来解决这个问题,但这种方法是首选的(也是一种好的做法)。

for j in range(1, 5):   # Turn off all of the LEDs
    led = pyb.LED(j)    # Get the LED
    led.off()           # Turn the LED off

接下来,我们使用另一个初始化为 1 的计数变量。然后,我们用一个无限条件开始 while 循环。也就是说,while 后面的表达式始终为真。在循环体中,我做了与上一个循环类似的事情,从 Pyboard 硬件库中获取 LED,打开它,等待 500 毫秒,关闭它,再等待 500 毫秒。在循环体的末尾,我增加了计数变量(而循环不像 for 循环那样增加计数变量)。如果计数器计数到 5,我从 1 开始计数。检查以下代码以查看这些元素。

while True:             # Loop forever
    led = pyb.LED(i)    # Get next LED
    led.on()            # Turn LED on
    pyb.delay(500)      # Wait for 1/2 second
    led.off()           # Turn LED off
    pyb.delay(500)      # Wait for 1/2 second
    i = i + 1           # Increment the LED counter
    if i > 4:           # If > 4, start over at 1
        i = 1

清单 2-4 显示了完整的代码。通读几遍,直到你确信它可以工作。

#
# MicroPython for the IOT
#
# Example 2
#
# Turn on the four LEDs on the board in order
#
# 1 = Red
# 2 = Green
# 3 = Orange
# 4 = Blue
#
# Dr. Charles Bell
#
import pyb              # Import the Pyboard library

for j in range(1, 5):   # Turn off all of the LEDs
    led = pyb.LED(j)    # Get the LED
    led.off()           # Turn the LED off

i = 1                   # LED counter
while True:             # Loop forever
    led = pyb.LED(i)    # Get next LED
    led.on()            # Turn LED on
    pyb.delay(500)      # Wait for 1/2 second
    led.off()           # Turn LED off
    pyb.delay(500)      # Wait for 1/2 second
    i = i + 1           # Increment the LED counter
    if i > 4:           # If > 4, start over at 1
        i = 1

Listing 2-4.Example 2: Blinking the LEDs

当您运行代码时,您将看到 led 依次打开,首先是红色,然后是绿色、黄色(橙色)和蓝色。由于循环没有结束,它将一直闪烁 led,直到您重置板(记住首先弹出驱动器)。参见图 2-16 。

A447395_1_En_2_Fig16_HTML.jpg

图 2-16。

Running Example 2

例 3:

这个例子演示了如何使用板上的按钮。它还演示了如何使用中断(称为回调函数)。也就是说,我们在代码中创建一个函数,然后告诉 MicroPython 在发生中断时执行该函数——在本例中,就是当按钮被按下时。由于这个例子也很复杂,我将带您浏览一下代码。

首先,我们导入 Pyboard 硬件库,为绿色 LED(列表中的第二个)设置一个变量,并关闭它。

import pyb                  # Import the Pyboard library
led = pyb.LED(2)            # Get LED instance (2 = green)
led.off()                   # Make sure the LED if off

接下来,我们定义一个函数。这相当容易。我们只是使用 def 指令并给函数一个名字。姑且称之为 flash_led 吧。在这个函数中,我们让 LED 快速闪烁(100 毫秒延迟)25 次。我们已经在前面的例子中看到了这样做的代码。

def flash_led():
    for i in range(0, 25):
        led.on()
        pyb.delay(100)
        led.off()
        pyb.delay(100)

接下来,我们通过获取用户按钮(在库中称为开关)来创建另一个变量。然后,我们使用方法 callback()并传入我们创建的函数的名称。这两个语句将建立连接,以便在按钮被按下时运行 flash_led()函数。

button = pyb.Switch()       # Get the button (switch)
button.callback(flash_led)  # Register the callback (ISR)

最后,我们打印一条消息,说明代码已经可以测试了。

print("Ready for testing!")

清单 2-5 显示了完整的代码。

#
# MicroPython for the IOT
#
# Example 3
#
# Flash the green LED when button is pushed
#
# Dr. Charles Bell
#
import pyb                  # Import the Pyboard library
led = pyb.LED(2)            # Get LED instance (2 = green)
led.off()                   # Make sure the LED if off

# Setup a callback function to handle button pressed
# using an interrupt service routine
def flash_led():
    for i in range(0, 25):
        led.on()
        pyb.delay(100)
        led.off()
        pyb.delay(100)

button = pyb.Switch()       # Get the button (switch)
button.callback(flash_led)  # Register the callback (ISR)

print("Ready for testing!")

Listing 2-5.Example 3: Using a Button

一旦你看到,准备测试!在 REPL 控制台中,您可以测试它。如果你胆小或有异常高的静电倾向,使用非导电探针并按下标有 USR 的按钮。图 2-17 显示了一个按钮按下调用 flash_led()方法的例子。尝试几次,直到你满意为止。

A447395_1_En_2_Fig17_HTML.jpg

图 2-17。

Running Example 3

拔下或重置 Pyboard 之前,不要忘记弹出驱动器。

摘要

MicroPython 是微控制器世界中一个非常令人激动的新成员。第一次,初学者不需要学习新的操作系统或复杂的编程语言如 C 或 C++来编程微控制器。MicroPython 允许有一些甚至没有编程经验的人试验电子产品并构建有趣的项目。因此,MicroPython 为更多的业余爱好者和爱好者提供了机会,他们只想在没有陡峭的学习曲线的情况下完成他们的项目。

在本章中,我们发现了 MicroPython 的主要特性。我们还发现 MicroPython 是基于我们在个人电脑上发现的 Python。我们甚至在 PC 上测试 Python 来展示相似之处。最重要的是,我们亲眼看到了 MicroPython 如何在微控制器板上工作。在这种情况下,我们使用原始的 MicroPython 板 Pyboard 来演示为在 Pyboard 上运行组件而编写的三个 Python 代码示例。

在下一章中,我们将发现一系列可以用来运行 MicroPython 和构建我们的 IOT 项目的硬件。正如您将看到的,有许多选项,从已经加载了 MicroPython 的板(允许您无需额外设置即可构建项目)到您可以自己加载 MicroPython 的普通板。

Footnotes 1

有些人可能会说 C++,我想这是有一定道理的,但它们更像是通用的、基本的 C 语言。

2

是的,我有一个。

3

版权所有 2014–2017,Damien P. George,Paul Sokolovsky 和贡献者。最后更新于 2017 年 3 月 5 日。

4

https://www.json.org/

三、MicroPython 硬件

现在我们已经快速了解了如何使用 MicroPython 板,包括几种形式的 MicroPython 项目的演示,是时候浏览一下可用的板和相关硬件了。正如我们将看到的,有几个板可以选择,包括那些已经加载了 MicroPython 的板,以便它们可以开箱即用;那些允许你在板上加载 MicroPython 的;那些现有的电路板,只要稍加努力,就值得研究。

本章还包括如何开始使用所讨论的每种板,包括如何连接板、加载固件等。您应该通读整个章节,因为虽然许多概念是相同的,但程序可能会因主板不同而略有不同。此外,本章的重点是支持 MicroPython 的主板。虽然我简要讨论了您可以使用的替代板,但这些部分中的信息对于使用本章中未列出的板是有用的。

在我们开始查看可用的主板之前,让我们讨论一些使用 MicroPython 主板的最佳实践和其他实用建议。这些适用于本章中的所有主板,也可能适用于任何新兴的主板,因此在选择主板时,你应该将这些考虑在内。

开始使用 MicroPython 板

虽然你可以买一个这样的主板,然后直接进入你的 IOT 项目,但是在使用你的主板时,有一些事情你应该考虑和记住。具体来说,有些主板可能需要更新,有些可能需要组装硬件(焊接),而其他的可能有局限性要考虑。我将在本节中讨论这些以及更多内容。我还包括一些在你的电脑上使用主板的技巧。

固件更新

那些在芯片上包含 MicroPython 的主板可能需要固件更新。固件 1 是用来描述芯片上软件的术语。大多数主板都有特殊的芯片,允许您自己更新 MicroPython(和其他方面),使您能够跟上供应商、MicroPython 等的变化。

加载固件是特定于主板的,因此您应该参考供应商的文档来了解如何更新固件。例如,Pycom 在 https://docs.pycom.io/chapter/gettingstarted/installation/firmwaretool.html 记录了 WiPy 板的更新过程。

当您第一次收到主板时,或者当您发现无法访问某些库函数、操作失败或发布新硬件(附加主板)时,您应该考虑更新固件。但是,您应该避免过于频繁地更新固件。

我的理念很简单:我只在主板是新的时候更新一次固件,并且只在某些东西不再工作的时候更新。如果您的项目正在运行,并且没有遇到任何问题,就没有必要更新它。过于频繁地更新固件的一个风险是,如果固件中发生了一些变化,如旧的库或硬件不再受支持,对库的更改破坏了您的代码,以及其他恼人的问题,您可能会使您的项目变得无用。我对这个规则的唯一例外是,如果供应商已经修复了一个错误或改善了用户体验,在这种情况下,我会更新固件。底线是,如果有用,就不要乱来!

Tip

当您的主板是新的时,只在以后需要时更新您的固件。

特别要提到的是那些必须自己加载 MicroPython 的板。其中一些主板可能需要您更新系统的其他部分,如引导机制、在非易失性内存中加载 MicroPython 等。如果您选择使用必须加载 MicroPython 的主板,请务必查看供应商的文档,了解关于更新主板上软件的建议(可能不仅限于固件)。最后,有些主板是新的,因此它们的固件可能不是测试版或候选版本,也不是产品质量。在这些情况下,我喜欢更频繁地更新固件,直到发布生产版本。

网络问题

如果您的主板具有联网功能,您在学习如何使用主板时应该格外小心。具体来说,请按照供应商的说明设置网络并将您的主板连接到网络。例如,某些主板具有非常特殊的机制来连接到 WiFi 网络。未能正确配置您的网络连接将导致很多挫折,尤其是如果您的项目旨在生成通过网络访问的数据。

如果您的主板具有联网功能,我建议通读文档并运行任何示例,向您展示如何将主板连接到网络。花时间学习如何做到这一点(并重复),将消除大量浪费的时间。事实上,初学者犯的头号错误是连接他们的电路板,编写代码将数据发送到网络上的另一个系统,却发现它不起作用。在这种情况下,他们在第一次使用它之前没有确保网络部分正常工作。

这也适用于将主板连接到您的 PC。对于那些允许您将板载存储器作为文件系统进行访问的主板,这可能不是问题,但对于其他需要通过特殊软件通过 USB 进行连接的主板,您应该确保在尝试编写第一个 Python 程序之前可以连接到主板。

一步一个脚印!

新手常犯的另一个错误是坐下来一次写完所有代码,而没有提前测试任何东西。这就造成了这样一种情况:如果某件事不成功,它可能会被一大堆问题掩盖。例如,如果有一些逻辑错误或产生的数据不正确,它可能会导致项目的其他部分失败或产生不正确的结果。当项目根本不工作时,情况会变得更糟——有太多的部分需要尝试和诊断出了什么问题。这往往会让初学者陷入困惑和沮丧的绝望境地。你们这些学生完全知道我在说什么。

这可以通过一步一步地构建项目来轻松避免。也就是说,一次构建一个方面的项目。例如,如果你用发光二极管发出信号,首先让那部分工作。同样,如果你从一个传感器读取数据,在把它们连接在一起并希望它们都能正常工作之前,确保你能独立正确地完成这项工作。即使是非常有经验的人也可能犯这种错误,但是如果出现问题,他们更有能力解决(他们知道得更多,但是这是一种照我说的做而不是照我做的情况)。我们将一步一步地构建本书中的示例。有些很小,可能只有一个步骤,但是这种实践是你从事任何项目都应该注意的。

程序设计工具

一些微控制器供应商提供软件开发(编程)工具。可以说最成功的一个是 Arduino 集成开发环境(IDE)。Arduino IDE 提供了操作(编程)Arduino 所需的所有工具,从编写代码到编译,再到将其安装到电路板上。

一些供应商提供了用于其主板的软件。最有希望的包括来自 pycom.io 的工具,包括用于流行编程编辑器 2 的 PyMakr 插件和用于移动设备的 PyMate ( https://pycom.io/pymate/ )应用。PyMakr 插件是一个特殊的编辑器和工具,用于编写 Python 程序并将其安装在 WiPy 板上。PyMate 应用是一个令人兴奋的新解决方案,允许您远程使用 WiPy,包括将其连接到您的网络和读取设备,在您的移动设备上显示数据。我们将在研究 WiPy 板时看到这些例子。

Caution

Atom、Sublime、Visual Studio Code 和 PyCharm 的 PyMakr 插件仍在开发中。目的是用 PyMakr 桌面应用来替换它们。目前,建议用户使用 FileZilla 等 FTP 客户端将代码/项目上传到他们的设备。

如果您的主板供应商提供用于其主板的软件,您应该考虑使用它。然而,编写 Python 脚本并将其加载到主板上所需的大部分工作都可以在没有特殊软件的情况下完成。事实上,有几个支持 Python 的编程编辑器包括 Komodo Edit ( www.activestate.com/komodo-ide/downloads/edit )、PyCharm ( www.jetbrains.com/pycharm/ )和用于 Eclipse 的 PyDev IDE 插件( www.pydev.org/ )。PyCharm IDE 作为社区编辑器(免费)和付费应用提供。Komodo Edit 也是一个更强大的免费版本,付费的 Komodo Edit IDE。

我最喜欢的编辑器是 Komodo Edit 和 PyCharm。两者都有 Python 语法高亮显示——文本的颜色会改变以指示语法、字符串等。,还有一些可以完成你的语句,甚至可以根据你使用的构造自动缩进。

两者都是编写 Python 的绝佳选择。Komodo Edit 是一个简单的编辑器,但它的工作做得非常好。PyCharm 是一个 Python IDE,它能做更多的事情,包括交互式调试。虽然您不能使用 MicroPython 板的一些功能,但是如果您想在 PC 上使用 Python,您应该考虑使用 IDE(或类似的工具)。图 3-1 显示了使用 Komodo Edit 编辑下一章中的 Python 示例的例子。虽然您看不到突出显示的彩色文本,但是请注意,编辑器提供了链接编号、用于编辑多个文件的选项卡等等。

A447395_1_En_3_Fig1_HTML.jpg

图 3-1。

Komodo Edit Example

虽然不是必需的,但强烈建议您使用包含 Python 语法突出显示的编辑器。它不仅能帮助你写代码,还能帮助你写出更正确的代码。我发现代码完成功能是一个真正的时间节省。您不必选择这里讨论的任何一种,因为除了 PyMakr 或 PyMate 之外,没有一种是为特定的主板设计的。记住,为 MicroPython 板编写 Python 与为 PC 编写 Python 脚本没有什么不同——语法是相同的。

需要一些组件

一些供应商,如 Adafruit 和 George Robotics Limited/micropython . org(py board 的原始制造商)提供焊接和不焊接接头的电路板。不焊接接头节省了生产成本,在某些情况下还节省了运输成本,使电路板更便宜。如果你知道如何焊接(或者知道谁知道),你可以用不带接头的电路板来节省一点。

您可能需要不带接头的电路板的另一个原因是,如果您想将电路板安装在工程外壳或其他形式的嵌入式安装中。在这种情况下,焊接头部可能会占用更多的空间,或者使整个项目变得更大。

您还可能会遇到一些附加板、分线板或其他不与接头(或连接器)焊接的分立元件。如果您想使用这些,您可能需要自己焊接接头或连接器。例如,Adafruit ( adafruit.com)和 Sparkfun ( sparkfun.com)的大多数分线板都没有焊接接头。

GPIO 引脚

在上一章中,我们了解了一些通用输入输出(GPIO)引脚。各种板的区别之一是 GPIO 引脚的排列方式,甚至是引脚的数量。虽然大多数板支持一系列引脚,包括模拟和数字引脚,但有些板提供的引脚比其他板少。图 3-2 显示了 WiPy 上可用的 GPIO 引脚。您可以从主板制造商或供应商处找到类似的图纸(也称为数据手册、映射或引脚排列)。

A447395_1_En_3_Fig2_HTML.jpg

图 3-2。

WiPy GPIO Pins (courtesy of pycom.io)

如果您的项目需要几个模拟或数字引脚,您应该计划选择一个能够支持所需引脚数量的电路板。幸运的是,大多数会;然而,一些可以加载 MicroPython 的较新的主板可能不能。例如,无处不在的 ESP8266 3 (一种低成本的 WiFi 芯片)的一些版本可能只支持几个引脚,这对于有许多组件的项目来说不太理想。

其他提示

本节包括几个使用 MicroPython 板时可能遇到的问题的提示。把这些建议当作你在实验前或实验过程中应该做的事情。让我们从基础开始。

访问社区论坛

您应该做的第一件事(甚至在购买主板之前)是访问您选择的主板的社区论坛。许多供应商主持和管理一个在线消息论坛,供人们提出问题,社区中的人们提供他们的见解、答案、建议,甚至帮助那些陷入困境的人。

除了阅读这本书,访问社区论坛是绝对必要的。事实上,你应该考虑在任何时候对你的董事会有疑问或问题的时候访问论坛。你很可能会遇到一个人,他曾经遇到过同样的(或非常相似的)问题,以及他们(和其他人)对如何解决这个问题的建议。

下面列出了更流行的 MicroPython 板的论坛。如果你在这里没有看到你的公告板,下次你上网的时候谷歌一下;然后,如果你喜欢这个论坛,把它加入书签以便更快地访问。

Note

我在第十二章中提供了更多与社区论坛互动的技巧。

小心轻放!

您应该将 MicroPython 板视为一种非常敏感的设备,容易受到静电放电(ESD)的影响。除非您将您的主板放在箱子里,否则您应该小心处理您的主板,在通电之前,请始终将它放在不导电的表面上。ESD 可能是由许多事情引起的(回想一下你小时候在地毯上穿着运动鞋的时候)。这种放电会损坏电路板。请务必处理好您的主板,以便控制 ESD 并将其降至最低。

图 3-3 显示了供应商装运的保护箱中的 Pyboard 示例。这种蛤壳式外壳仅比主板大一点点,当它在您的工具包底部发出嘎嘎声时,可以通过卡扣关闭,以确保主板的安全。

A447395_1_En_3_Fig3_HTML.jpg

图 3-3。

The Pyboard v1.1with Headers in its Protective Case

您也不应该在主板通电时移动它。为什么呢?MicroPython 板上焊接有元件,两侧有许多裸露的引脚。如果任何两个或两个以上的引脚接触到导电的东西,您可能会损坏电路板。

此外,请务必将您的主板存放在 ESD 安全容器中——专门用于存放电子产品的容器。应该避免使用普通、日常、廉价的塑料盒。但是,如果您没有为电子产品制作的容器,您可以使用防静电袋来存放电路板。您购买的许多主板和组件都采用这种包装。所以,不要扔掉!

您应该注意确保您的身体、工作区和项目接地,以避免静电放电(ESD)。避免这种情况的最佳方法是使用接地带,环绕在手腕上,并连接到像这些uline.com/BL_7403/Anti-Static-Table-Mats的防静电垫上。

最后,将 USB 电缆连接到主板时要格外小心。大多数主板都配有微型 USB 连接器,这种连接器容易损坏(比其他连接器更容易损坏)。在大多数情况下,不是电缆坏了,而是电路板上的连接器坏了。发生这种情况时,可能很难修复(或者可能无法修复)。也有可能电缆本身会停止工作,或者只有当您将电缆固定到位时才能工作。如果发生这种情况,请尝试使用新的电缆,如果这样可以解决问题,请扔掉旧的电缆。如果不能解决问题,可能是主板上的连接器有问题。幸运的是,插拔电缆时格外小心可以避免这些问题。例如,始终首先插入微型 USB 端,然后使用全尺寸 USB 端插入和拔出您的 PC。使用 micro USB 连接器的次数越少,损坏的几率就越小。

电脑无法识别 SD 驱动器

当人们第一次使用带有微型 SD 卡/驱动器的主板时,最常遇到的问题之一是操作系统无法识别板载 USB 驱动器。如果您的主板带有 USB 就绪驱动器(内置或外置),而您在文件浏览器(或 finder)中看不到它,您可以做几件事情来解决这个问题。首先,如果主板有一个可移动的 micro SD 驱动器,请确保它被正确插入并且被格式化为 FAT。如果你提供了自己的微型 SD 卡,这通常是一个问题。把它重新格式化成 FAT,应该就可以了。

另一方面,如果主板有一个可移动的 micro SD 驱动器和一个板载驱动器,请取出 micro SD 卡,然后再次尝试将主板连接到您的 PC。有时,外部 micro SD 卡优先于板载驱动器。

如果您的主板根本不显示,或者您通过 USB 连接到它有问题,有几种可能的原因。首先,确保你的 USB 线是供应商推荐的。如果你像我一样,已经积累了一个名副其实的 USB 电缆坑,很可能有一条或多条你“最喜欢”的电缆是充电电缆,而不是数据线。也就是说,电缆可以用来给设备充电,但用于传输数据的引脚(导线)缺失或未连接。先检查你的电缆。

下一种可能的情况是你的电脑没有安装正确的驱动程序。请务必查看供应商网站,了解您需要安装哪些驱动程序。有些需要特定操作系统的特殊驱动程序。例如,如果您使用 Windows 10,您将需要大多数主板的驱动程序。

另一个可能的问题是电路板需要外部电源。我在一些早期的原型板上见过这种情况,在某些情况下也在生产板上见过这种情况(但本章没有列出)。在这种情况下,您需要先打开主板电源,然后再将其连接到 PC。

跳线松动或丢失

使用带有跳线(设计用于完成电路的小型塑料连接器)的电路板时,可能会出现一个或多个跳线丢失的情况。跳线用于启用或禁用某些功能。图 3-4 显示了 WiPy 扩展板上跳线的引脚排列示例。

A447395_1_En_3_Fig4_HTML.jpg

图 3-4。

Jumpers on the WiPy Expansion Board

跳线非常小,很容易丢失或放错地方。如果您丢失了跳线并需要更换,您通常可以从您所在地区的计算机维修店获得一些跳线。如果你所在的地区没有任何电脑维修店,如果你找到一个自己组装电脑的 PC 爱好者,你应该可以从他们那里获得几个跳线。为什么呢?因为这些人通常手头都有很多。当我管理一家电脑公司时,我不能放弃这些东西。我还有几百个在某个地方。底线是你不应该买它们——我敢肯定,你身边有人有一些备用的。

如果一个或多个跳线脱落,您可以拧紧它们,使它们更难移除。要拧紧跳线,使用一对尖嘴钳,并轻轻(我提到轻轻?)压缩母插座。做那件事有一点技巧。您也可以在引脚顶部轻轻向外弯曲跳线引脚,使跳线更加贴合。当然,如果它仍然太松或者你损坏了它,你可以找到那个电脑爱好者并得到一个新的。

有些板卡,比如 WiPy 扩展板, 4 有几个跳线,拆下来不影响操作。例如,如果你没有使用锂聚合物 5 电池,通过 USB 连接等。,那么如果移除一些跳线,您就可以使用未使用的跳线来代替丢失的跳线。

现在我们已经了解了开始使用 MicroPython 板的一些注意事项,让我们开始浏览可用的板,从那些已经加载并准备使用 MicroPython 的板开始。每个部分都包括对该板的简要概述,以及关于如何开始使用该板以及在哪里可以购买该板的说明。

支持 MicroPython 的主板

我们将探讨的第一类主板是那些安装了 MicroPython 并随时可以使用的主板。这些主板不需要安装任何软件就可以使用(尽管您可能需要定期更新固件或在 PC 上安装驱动程序)。因此,对于那些刚接触 MicroPython 和电子产品的人来说,这些主板是最好的选择。然而,这并不意味着主板不强大——它们是强大的!事实上,本书中的项目是使用这些板演示的,您会发现它们对于大多数中小型 IOT 项目来说绰绰有余。

在撰写本文时,支持 MicroPython 的板是 Pyboard 和 WiPy。两者都是不错的选择,但是正如我们将看到的,对于 IOT 项目来说,其中一个更容易使用。

Pyboard

Pyboard MicroPython 板是第一批用于托管 MicroPython 的板之一。在某些方面,它为 MicroPython 板应该如何配置和如何操作设定了标准。它由 Damien George 创建,于 2013 年作为 Kickstarter 活动开始,旨在实现片上 Python 以在微控制器上运行。Pyboard 及其附件板由乔治机器人有限公司(micropython.org)生产和销售。

概观

该板最显著的特点是尺寸小。该板尺寸约为 40 毫米 x 40mm 毫米,有两个分离式“耳”用于安装该板。没有耳朵,板子大约是 32 毫米乘 40 毫米。也就是说,如果你不想安装板,你可以打破耳朵,减少尺寸更大。电路板的三面都有放置接头的空间。一侧包含一个微型 USB 电缆连接器,用于连接到您的 PC(或为主板供电)以及微型 SD 驱动器。图 3-5 显示了 Pyboard v1.1 的特写视图。

A447395_1_En_3_Fig5_HTML.jpg

图 3-5。

The Pyboard v1.1 without Headers (courtesy of adafruit.com)

注意棋盘左上角和右下角的小耳朵。这些都是穿孔的,便于拆卸。到目前为止,他们并没有碍事;因此,除非你有一个更小的,紧配合的情况下,你可以让他们重视。

你还可以在微型 USB 连接器附近的板上看到两个小按钮。一个是用户自定义按钮,您可以编程(USR ),另一个是复位按钮(RST ),用于复位电路板。主板的中心是 ARM 处理器芯片。

最后,在 micro SD 卡插槽旁边的板上有四个 led。这些是用来交流错误,通电,并可以打开或关闭你的代码!

关于硬件的更多信息

现在,让我们了解更多关于硬件的知识,更具体地说,一些您可能不知道但可能方便知道的细节和规格。

当您使用插入了 micro SD 卡的主板时(并且该卡被格式化为 FAT ),主板将使用该驱动器而不是内部驱动器进行引导,并且当您将 Pyboard 连接到 PC 时,它会显示出来。但是,您将看不到内部驱动器。这是许多人困惑的根源。如果您想使用内部驱动器进行引导,您必须在 micro SD 上创建一个名为flash的文件夹,并在该文件夹中创建一个名为SKIPSD的空文件。这样做,然后断开并重新连接您的板。之后,您可以从您的脚本访问 micro SD 卡。

回想一下引导驱动器是用来保存boot.pymain.py文件的;boot.py文件在启动时执行,一旦开发板运行,就会调用main.py。默认情况下,更改这些选项以添加您的自定义程序就是您让电路板引导到您的项目中的方式。

然而,这两个驱动器都可以从 Python 中访问,不管哪一个用于引导。内部驱动器在文件夹/flash下,micro SD 卡(当 SKIPSD trick 发出时)在文件夹/sd下(分别使用0:/1:/的旧版本板)。在 Python 代码中使用这些路径来访问正确的驱动器。

该板还支持其他引导模式,可通过 USR 按钮启动。要更改模式,请在插入(或打开)主板时按住 USR 按钮。按住按钮时,板上的 led 将按如下方式点亮。当电路板循环到您想要的模式时,释放 USR 按钮。

  • 当绿色 LED 亮起时,主板正常启动。
  • 当橙色 LED 亮起时,主板会在启动时跳过运行 boot.py 和 main.py 文件。
  • 当绿色和橙色指示灯亮起时,主板会重置内部驱动器上的文件系统,将 boot.py 和 main.py 恢复为出厂内容。

最后一种模式是非常有用的,如果你设法砖板。更具体地说,如果您的主板变得不可用或无法正确启动,请先尝试此模式,看看它是否能解决问题。

led 的另一个用途是指示存在 Python 错误。如果红色和绿色指示灯闪烁,则表明从 main.py 调用的 Python 脚本中存在错误。如果所有指示灯都循环亮起,则表明可能存在硬件故障。尝试关闭主板电源并重置文件系统。如果那不起作用,你的板可能被损坏。

Pyboard 由几个主要组件组成。我在这里列出它们是为了让那些有兴趣了解支持哪些功能的人知道。我从微控制器和内存规格开始。

  • STM32F405RG 微控制器
  • 带硬件浮点的 168 MHz Cortex M4 CPU
  • 1024 千字节闪存 ROM 和 192 千字节 RAM
  • 三轴加速度计(MMA7660)
  • 带可选备用电池的实时时钟
  • 24 个 GPIO 引脚
  • (2) 12 位数模(DAC)转换器,引脚 X5 和 X6 上提供
  • (3) 12 位模数转换器,16 个引脚,4 个带屏蔽
  • 4 个发光二极管
  • 1 个微型 SD 插槽
  • 1 个微型 USB 接口,用于与您的电脑通信
  • 1 个复位(RST)和 1 个用户(USR)开关
  • 3.3V LDO 电压调节器,能够向输入电压范围为 3.6V 至 10V 的组件提供高达 300mA 的电流
  • ROM 中的 DFU 引导程序便于固件升级

如果您有兴趣了解其中一些器件的更多信息,以下是主要器件的附加数据和数据表链接。

如前所述,Pyboard 有多种版本,包括焊接和不焊接接头的版本,以及速度稍慢、硬件功能较少的早期版本(1.0 版)。我建议您的所有项目都使用 1.1 版的开发板。

What, No Internet?

Pyboard 中可能缺少您期望找到的一个特性。没有联网功能。虽然最新的固件包括使用两个网络硬件(CC3000 和 WizNet5000)的规定,但您必须购买支持其中一个芯片组/库的模块(分线板)来将您的 Pyboard 连接到互联网(或您的本地网络)。有些人可能会发现在没有网络的情况下使用 Pyboard 对大多数项目来说是很好的,但是本书后面的项目将需要连接到网络。毕竟,这是一本 IOT 的书!因此,如果您打算构建一个 IOT 项目,您可能需要考虑一个不同的电路板,或者查看下一节中的网络示例,了解如何将 Pyboard 连接到您的网络。

如果您想使用外部电源或电池为主板供电,您必须确保电源设置为 3.6-10V。连接更高的电源会损坏电路板。较低的功率会使电路板不稳定(可能无法正常运行)。您可以将电源(正极)连接到 VIN,将接地(负极)连接到 GND。图 3-6 显示了这些引脚位于微型 USB 连接器附近的特写。

A447395_1_En_3_Fig6_HTML.jpg

图 3-6。

Pins for connecting external power (Pyboard) Tip

在拔下驱动器插头或重置主板之前,请始终弹出驱动器。

现在,让我们从连接到我们的 PC 并运行一个简单的 Python 程序的简单演练开始,来看看 Pyboard 的运行情况。

Pyboard 入门

我们在第二章中讨论了 REPL 控制台,包括如何将主板连接到你的电脑。让我们再看一遍,这样我们就能确保理解它是如何工作的。要连接到主板,请使用 USB 转 micro USB 电缆,将一端连接到 Pyboard,另一端连接到 PC。过一会儿,您应该会看到一个名为PYBFLASH的驱动器被挂载(或者在 Windows 上,一个新的驱动器盘符出现在文件资源管理器树中)。

Tip

如果你使用 Windows 10,Pyboard 需要很长时间(超过 5 分钟)才能显示为 USB 驱动器,那么你需要更新你的固件。事实上,这是一个已知的问题,并已在最近的固件版本中得到解决。

然后,您可以打开 USB 驱动器,通过在驱动器上编辑文件或将文件复制到驱动器来修改文件。例如,如果您想要修改 main.py 脚本以包含您自己的代码或加载您自己的脚本文件,您可以进行更改,然后弹出驱动器并重置板。一旦你这样做了,你添加的代码或者你设置调用的脚本将会在主板启动时触发。我们将在后面的章节中看到如何做到这一点。

你也可以像我们在第二章看到的那样启动 REPL 控制台。图 3-7 显示了连接到 Pyboard 的 REPL 控制台示例。在这种情况下,代码只是打印一条语句——现在已经是老生常谈的 Hello,World!消息。

A447395_1_En_3_Fig7_HTML.jpg

图 3-7。

The REPL Console (Pyboard)

加载固件

回想一下,我们讨论过定期加载固件的必要性。这适用于所有的主板,不管它们是否准备好了 MicroPython。这是因为 MicroPython 的版本在不断完善,缺陷也在不断修复。因此,为了确保您拥有最新的更新,您应该在获得主板时至少更新一次固件,并且只在以后需要时更新,例如当您发现缺陷或向库中添加新的硬件组件时。在每块板上加载固件略有不同,但我们将从 Pyboard 开始详细介绍,然后在本章中提及其他板的不同之处。

在开始之前,您应该检查加载的固件版本。您可以使用以下 Python 语句来实现这一点。在这种情况下,代码在通过 REPL 控制台连接的 Pyboard 上运行。

>>> import os
>>> os.uname()
(sysname='pyboard', nodename='pyboard', release='1.9.1', version='v1.9.1-80-g3e82bedf on 2017-06-27', machine='PYBv1.1 with STM32F405RG')

这里我们看到固件版本是 1.9.1,这是在撰写本文时可用的最新版本。它还显示固件加载的日期以及固件文件的名称。最后,我们还看到了板名。

您可以从 http://micropython.org/download/ 下载固件模块。举例来说,现在用于将 Pyboard 更新到最新版本的文件 6 被命名为pybv10-network-20170629-v1.9.1-95-g1942f0ce.dfu (latest)并且包含网络硬件支持。图 3-8 显示了显示文件及其描述的网站摘录。当您访问该网站时,您会发现各种主板的固件。如果你想用其他的板,一定要标记这一页,因为你需要它。

A447395_1_En_3_Fig8_HTML.jpg

图 3-8。

Excerpt of Firmware Available for the Pyboard

注意,这些文件以文件扩展名.dfu命名,代表设备固件更新7——一种特殊的固件二进制格式。你应该下载最新版本的固件。一定要选择和你的板子相匹配的!例如,我选择了标题为适合 PYBv1.1 板的文件,其中包括 CC30000 和 WIZ820io 的网络驱动程序,因为我想将我的 Pyboard 与网络模块一起使用。您应该选择具有帮助节省空间(内存)所需的最小支持集的固件,但是,到目前为止,这还不是一个问题。

接下来,你将需要一个 DFU 程序员。如果使用 Windows,从 http://www.st.com/en/development-tools/stsw-stm32080.html 下载编程器。选择与您的系统匹配的版本(32 位或 64 位)并安装它。您可能需要首先通过电子邮件注册您的请求。

如果你用的是 macOS 或者 Linux,可以从 https://github.com/micropython/micropython 下载加载的纯 Python 版本的 DFU。单击克隆或下载按钮,然后下载 Zip 按钮,并将文件保存到您的 PC。这将下载所有用于 MicroPython 的工具和实用程序。下载完成后,解压文件并在 tools 文件夹中找到名为pydfu.py的文件。参见 https://github.com/micropython/micropython/wiki/Pyboard-Firmware-Update 了解更多关于如何在 Linux 或 macOS 上使用这个 DFU 编程器的信息。

接下来你必须做的是将 Pyboard 设置为 DFU 模式。如图 3-9 所示,在 DFU 和 3.3V 引脚之间放置一根跳线即可。3.3V 引脚标记在电路板边缘,DFU 引脚就在它旁边(未标记,但位于第二行)。在将评估板连接到 PC 之前,请执行此操作。

A447395_1_En_3_Fig9_HTML.jpg

图 3-9。

DFU Mode Jumper (Pyboard) Note

首次在 DFU 模式下将主板连接到 PC 时,Windows 10 可能会安装驱动程序。如果驱动程序安装开始,请选择自动选项,并允许 Windows 搜索驱动程序。该过程完成后,您就可以加载固件了。

现在我们来看看如何使用 Windows 10 更新固件。一旦你下载了你想加载的固件并安装了 DFU 编程器,你可以启动它,然后点击“更新或验证操作”部分的“选择”按钮(见下文),搜索你下载的文件,然后打开它。读取文件后,单击升级按钮开始更新。该过程将开始更新电路板,并显示如图 3-10 所示的完整信息。

A447395_1_En_3_Fig10_HTML.jpg

图 3-10。

DFU Programmer (Windows 10)

该过程完成后,单击“退出”关闭 DFU 编程器,断开主板与 PC 的连接,并移除跳线。主板现在将使用最新的固件启动。

与 Pyboard 联网

本节将向您展示如何使您的 Pyboard 成为支持网络的设备。我们将简要介绍这一点,因为只有拥有 Pyboard 的用户才需要将这一步作为他们设置和入门的一部分,并且只有在我们使用互联网的示例项目中才会用到。

首先,你必须购买一个网络模块。这是困难的部分,因为迄今为止支持的唯一两个芯片组/库是用于 WiFi 的 CC3000 (CC3K)和用于以太网的 WIZNET5000 (WIZNET5K)。可悲的是,找到一个 CC3K 模块可能是一个挑战,因为制造它们的零售商已经更新到更新的芯片。

幸运的是,Adafruit 制作了一个 CC3K 模块(和 Arduino shield ),可以工作。不幸的是,他们不再生产这种主板,而是用一种使用不同芯片组的更好的主板取代了它(因此不兼容)。你可以在流行的互联网拍卖网站上找到类似的主板,但大多数供应商都在亚洲,所以运输时间会稍长一些。寻找这些模块的其他地方包括 DigiKey ( digikey.com)、Mouser ( mouser.com)和 Amazon ( amazon.com),但库存有限。

无论你买哪个,确保它有可用的分线针,这样你就可以把它连接到你的 Pyboard。查找标记为 MISO、MOSI、CLK/SCK、IRQ 和 VBAT_EN/VBEN 的引脚。它们应该在模块上排成一行。即使它和我将要展示给你的不完全一样,它也应该可以工作。

Caution

确保您已经加载了具有网络支持的固件。标准固件不包括网络模块。

为了证明您确实可以将 Pyboard 连接到互联网(您可能会发现一些帖子认为这是不可能的),让我们重新利用 Adafruit 的 CC3000 Arduino shield。是的,这意味着我们可以在没有 Arduino 的情况下使用 Arduino 盾!Arduino 的许多屏蔽和模块可以与其他微处理器一起使用。您只需要知道如何将引脚连接到 Pyboard。别担心,我会告诉你具体怎么做。图 3-11 显示未安装接头的屏蔽。

A447395_1_En_3_Fig11_HTML.jpg

图 3-11。

Adafruit CC3000 Arduino Shield (courtesy of adafruit.com)

Adafruit 是如此神奇的资源的原因之一是它们包括所有产品的数据表和操作指南。他们甚至维护停产产品的链接。在简要回顾了他们的文档后,需要将屏蔽和 Pyboard 一起使用的引脚在屏蔽上和文档中都有标注。

MicroPython 文档( https://docs.micropython.org/en/latest/pyboard/library/network.html )对如何使用支持的库有很好的指导。在这种情况下,对 CC3000 部分的检查揭示了将 CC3000 模块连接到 Pyboard 所需的引脚。幸运的是,由于 CC3000 shield 与 Arduino 一起使用,因此该 shield 增加了 Arduino 接头。我们将使用这些来连接到 Pyboard。

表 3-1 显示了两块板上的正确引脚。只需使用一根公母跳线将 Pyboard 上的引脚连接到 CC3000 屏蔽上的正确引脚。将公端连接到屏蔽上的引脚(通常会插入 Arduino ),母端连接到 Pyboard 引脚。你总共需要八根跳线。注意,其他模块可能具有类似的布置。引脚的标签与表中所示的相同,但编号可能不同,或者排列在专用接头中。

表 3-1。

Mapping Pins from the Pyboard to the CC3000 Shield

| Pyboard | CC3000 屏蔽 | | --- | --- | | 个人识别码 | 插脚数 | 个人识别码 | 插脚数 | | --- | --- | --- | --- | | V+ |   | 5V |   | | 地线 |   | 地线 |   | | Y3 | Y3 | 伊拉克 | 数字 3 | | Y4 | Y4 | vbat _ en | 数字 5 | | Y5 | Y5 | WCS | 数字 10 | | Y6 | Y6 | 血清肌酸激酶 | 数字 13 | | Y7 | Y7 | 军事情报部门组织(Military Intelligence Service Organization) | 数字 12 | | Y8 | Y8 | 工业博物馆 | 数字 11 |

图 3-12 显示了所有连接的连接图,图 3-13 显示了完成后连接的样子。有点乱,但是很管用。请务必将屏蔽和电路板放在不导电的工作面上。

A447395_1_En_3_Fig13_HTML.jpg

图 3-13。

Pyboard with Adafruit CC3000 Arduino Shield

A447395_1_En_3_Fig12_HTML.jpg

图 3-12。

Connecting the CC3000 to the Pyboard Note

如果你仔细检查 CC3000 屏蔽,你会看到有一个单独的头孔,如果你不想使用 Arduino 头可以使用。

如果您有 Pyboard 和 CC3000 shield,请继续进行连接。首先要确保你的 Pyboard 没有连接到你的电脑上。完成所有连接后,您就可以将 Pyboard 连接到 PC 了。

现在,让我们编写一些代码来测试模块,并将我们的 Pyboard 连接到互联网。由于这是一个 WiFi 盾,我们需要知道您的 WiFi 接入点(路由器)的名称和密码。我们需要使用的代码如清单 3-1 所示。

# connect/ show IP config a specific network interface
import network
import pyb

def test_connect():
    nic = network.CC3K(pyb.SPI(2), pyb.Pin.board.Y5, pyb.Pin.board.Y4, pyb.Pin.board.Y3)
    nic.connect('YOUR_ROUTER_HERE', 'YOUR_ROUTER_PASSWORD_HERE')
    while not nic.isconnected():
        pyb.delay(50)
    print(nic.ifconfig())

    # now use usocket as usual
    import usocket as socket
    addr = socket.getaddrinfo('micropython.org', 80)[0][-1]
    s = socket.socket()
    s.connect(addr)
    s.send(b'GET / HTTP/1.1\r\nHost: micropython.org\r\n\r\n')
    data = s.recv(1000)
    print(data)
    s.close()

Listing 3-1.Using the CC3000 Module

on a Pyboard

花点时间通读代码。该代码旨在连接到micropython.org网站并返回标题数据。请注意,除了网络代码之外,还有打印网络信息和从网站返回的数据的语句。另外,注意代码是作为名为test_connect()的函数实现的。我们将从 REPL 控制台调用该函数。别担心,这很容易。

继续编辑代码以包含您的路由器和密码,然后以名称CC3K.py将其保存到您的 Pyboard USB 驱动器。接下来,用 REPL 控制台连接到您的 Pyboard,然后发出以下命令。这将加载模块,然后运行代码并测试连接。

>>> from CC3K import test_connect
>>> test_connect()

连接可能需要一些时间来执行,但是当它完成时,您应该会看到一行打印出连接信息,在这之后,您会注意到连接到网站所返回的数据。如果您看到连接错误,请确保验证您的路由器名称和密码。

('10.0.1.123', '255.255.255.0', '10.0.1.1', '10.0.1.1', '10.0.1.1', '08:00:28:59:23:cb', 'MY_ROUTER')
b'HTTP/1.1 200 OK\r\nServer: nginx/1.8.1\r\nDate: Thu, 29 Jun 2017 02:21:50 GMT\r\nContent-Type: text/html; charset=utf-8\r\nTransfer-Encoding: chunked\r\nConnection: keep-alive\r\nVary: Accept-Encoding\r\nX-Frame-Options: SAMEORIGIN\r\n\r\n3dc3\r\n<!DOCTYPE html>\n\n\n\n<html lang="en">\n  <head>\n    <meta charset="utf-8">\n    <meta http-equiv="X-UA-Compatible" content="IE=edge">\n    <meta name="viewport" content="width=device-width, initial-scale=1">\n    <!-- The above 3 meta tags *must* come first in the head -->\n\n    <link rel="icon" href="/static/img/favicon.ico">\n\n    <title>MicroPython - Python for microcontrollers</title>\n\n    <link href="/static/bootstrap-3.3.7-dist/css/bootstrap.min.css" rel="stylesheet">\n\n    <link href="/static/css/sty'

如果这看起来工作量很大,不要担心,因为这和将你的 Pyboard 连接到互联网一样复杂。现在不用太担心理解所有的代码。在接下来的几章中,我们将更深入地探索 Python 和 MicroPython 库。

现在,让我们看看您可以在哪里购买我们自己的 Pyboard。

去哪里买

你可以在网上找到 micropython.org 的 py board(https://store.micropython.org/store/#/store)。主板从欧盟发货,但运费合理。如果你在北美,你也可以从 Adafruit ( https://www.adafruit.com/?q=pyboard &)购买板子和相关配件。根据货币兑换和运输(您希望主板交付的速度),您可能会发现从欧盟订购主板会稍微便宜一些。你也可以在各种拍卖网站和亚马逊等零售网站上找到这个公告板。你可以为你的 Pyboard 支付 35-40 美元。

您可以购买安装或不安装接头的 Pyboard。旧版本的电路板可以以同样的方式购买,有一个版本没有加速度计,这使得它稍微便宜一些。不过我推荐买最新的,1.1 版的板。

至于网络模块,最好的办法是等到更多的硬件支持被添加到 MicroPython 库(并上传固件)或者找到一个旧的 Adafruit CC3000 模块。查看网上拍卖网站,寻找仍在出售这些物品的卖家或有二手物品出售的人。 8

WiPy

WiPy MicroPython 板是第一批支持 MicroPython 的板中的另一个。顾名思义,WiPy 内置了充当主机的 WiFi 网络(想想路由器或接入点)。事实上,你使用 WiPy 的默认方式是通过 WiFi。因此,您不必使用另一个模块来使您的项目互联网就绪。这个特性使得 WiPy 成为本书中项目的更好选择。作为奖励,该板还具有蓝牙,使其更容易适应您的项目。

概观

该板最显著的特点是尺寸小。该板尺寸约为 40 毫米 x 40mm 毫米,有两个。WiPy 是一个比一包口香糖大不了多少的小模块,尺寸仅为 42 毫米 x 20mm 毫米,厚度仅为 3.5 毫米(不含头部)。

该板有或没有接头(获得一个有接头或自己安装)。您可以在试验板上使用该板,也可以购买像 dock 一样工作的特殊板。Pycom 将其称为扩展板,并提供了一个微型 SD 驱动器,微型 USB 连接器,led,复制所有的插头引脚,并包括一个电池连接器。扩展板使 WiPy 的使用变得相当容易,正如我们将看到的,使用它只有一个技巧。图 3-14 显示了 Pycom 的 WiPy 板。

A447395_1_En_3_Fig14_HTML.jpg

图 3-14。

Pycom WiPy (courtesy of pycom.io)

扩展板允许您通过 USB 连接到 WiPy,并使用 LIPO 电池或通过 USB 为板供电。我喜欢扩展板,因为它很方便。此外,它在每个角落都有孔,可以将其安装在竖板上,以便更容易地接触到引脚或将其永久安装在机箱或工具架上。它甚至有一个用户可访问的(可编程)按钮和 LED。图 3-15 显示了 Pycom 扩展板。

A447395_1_En_3_Fig15_HTML.jpg

图 3-15。

Pycom Expansion Board (courtesy of pycom.io) Tip

WiPy 的当前版本是 2.0 版。如果您购买了 WiPy 并想要扩展板,请确保您还购买了 2.0 版本的扩展板。

当然,扩展板要大得多,尺寸约为 50 毫米 x 65mm 毫米,安装的 WiPy 厚度约为 14 毫米。仍然很小,但没有 WiPy 本身小。

微型 SD 驱动器和微型 USB 连接器是您在使用 WiPy 时可能需要的功能。扩展板的另一个特点是,LIPO 电池连接器也是一个充电器,因此当您的板插入 USB 电源时,它可以为 LIPO 电池充电。

关于硬件的更多信息

现在,让我们了解更多关于硬件的知识,更具体地说,一些您可能不知道但可能方便知道的细节和规格。

首先,扩展板上的 micro SD 卡(WiPy 没有 SD 驱动器)的工作方式与 Pyboard 不同。WiPy 无法从 micro SD 驱动器启动,但仍然可以存储您的文件并从您的程序中访问它们。我们将在下一节看到更多关于如何使用 micro SD 驱动器的内容。

接下来我们要讨论的是引导模式。您可以像 Pyboard 一样更改引导模式,这在出现问题时很有帮助,但与 Pyboard 不同的是,引导模式与配置(固件)更改一起工作,而不是与板的引导方式一起工作。更具体地说,您可以将开发板设置为安全引导,这将跳过boot.pymain.py脚本。安全引导模式包括以下几种。

  • 使用最新固件安全启动
  • 使用以前的用户更新安全启动
  • 带出厂设置的安全启动

要激活和选择保存引导模式,请在 P12 引脚(GPIO20)和 3.3V 之间放置一根跳线。这将有效地“上拉”引脚,以便固件可以检测到选择。握住主板,使 USB 连接器位于顶部时,P12 引脚是左侧最后一个引脚,3.3V 引脚是右侧第三个引脚。图 3-16 显示了 WiPy 扩展板上正确安装的跳线。

A447395_1_En_3_Fig16_HTML.jpg

图 3-16。

Jumper Installed for Safe Boot Selection (WiPy Expansion Board)

当您打开主板电源时,LED 将变为橙色并开始缓慢闪烁。如果您在 3 秒钟后将跳线留在原位,LED 将开始快速闪烁,主板将使用最新的固件启动。如果您将跳线留在原位超过 3 秒钟,主板将使用之前的用户更新选择启动。最后,如果您将跳线留在原位 3 秒钟以上,LED 将会更快地闪烁,表示主板将会以出厂设置启动。在此过程中的任何时候移除跳线,以选择所需的安全启动模式。

WiPy 板支持 WiFi 和蓝牙通信机制,是 IOT 项目的绝佳选择。它采用 Espressif ESP32 芯片组和双处理器。其他功能包括以下内容。

  • 强大的 CPU、BLE 和最新的 WiFi 无线电
  • 1 公里 WiFi 范围
  • 适合标准试验板(带接头)
  • 超低功耗:与其他连接的微控制器相比微不足道
  • 哈希/加密:SHA、MD5、DES、AES
  • 无线局域网(wireless fidelity 的缩写)
    • 802.11b/g/n 16mbps
    • 安全性:SSL/TLS 支持和 WPA 企业安全性
  • 蓝牙:低能耗和经典
  • RTC:以 32KHz 运行
  • 电源:3.3V 至 5.5V,3V3 输出,能够提供高达 500mA 的电流
  • 记忆
    • RAM: 512KB
    • 外部闪存 4MB
    • 硬件浮点加速
  • Python 多线程

有趣的是,Pycom 还制造了其他几种 MicroPython 板,您可以用它们来进行更高级的项目。每块主板都支持不同的通信机制(WiFi、无线电、蜂窝和蓝牙)和嵌入式解决方案的高级功能。它们都具有与 WiPy 相同的外形。其中包括以下电路板。有关更多信息,请参见产品描述页面。

最棒的是,Pycom 扩展板可以与这些板一起工作。显然,Pycom 的业务是向 IOT 世界提供一系列强大的微处理器——全部运行 MicroPython!

如果您想使用外部电源或电池为主板供电,您必须确保电源设置为 3.3-5V。连接更高的电源会损坏电路板。较低的功率会使电路板不稳定(程序可能会失败)。您可以将电源(正极)连接到 VIN,将接地(负极)连接到 GND。图 3-17 显示了这些引脚位于大 LED 附近的特写。

A447395_1_En_3_Fig17_HTML.jpg

图 3-17。

Pins for connecting external power (WiPy - courtesy of pycom.io)

现在,让我们来看看 WiPy 的运行情况,从连接到我们的 PC 并运行一个简单的 Python 程序开始。

WiPy 入门

让我们再次从基础开始——REPL 控制台。由于 WiPy 是一种 WiFi 设备,因此最简单的连接方式是通过 WiFi。如果您使用的是扩展板,您只需要用连接到 PC 的 USB 电缆或 USB 电源给板通电。如果您使用不带扩展板的 WiPy,您可以如上所示给板通电。

WiPy 通电后,它将设置默认为 192.168.4.XXX 的 WiFi 网络,WiPy 的 IP 地址为 192.168.4.1。此过程可能需要几分钟,在此期间,WiPy 上的 LED 将熄灭。当它开始闪烁蓝色时,您的 WiPy 网络已准备好连接。

要通过 WiFi 连接到主板,您必须将 PC 连接到 WiPy WiFi 网络。网络 SSID 应该像wipy-wlan-b454。密码是 www.pycom.io 。按照操作系统的正常程序连接到网络。如果您的电脑仅使用 WiFi,您将无法同时访问互联网,但在试用主板时,您不需要这样做。

连接到 WiPy WiFi 网络后,您可以使用 telnet 连接到 REPL 控制台,如下所示。

$ telnet 192.168.4.1

请注意,在 REPL 控制台出现之前,连接可能需要一些时间。这很正常。一旦建立连接,您将被要求输入用户名和密码。使用micro作为用户,使用python作为密码。一旦你输入了密码,你应该会看到控制台并且可以和 WiPy 进行交互,如图 3-18 所示。

A447395_1_En_3_Fig18_HTML.jpg

图 3-18。

The REPL Console (WiPy)

如果使用扩展板,您还可以通过 USB 连接到 WiPy。但是,您必须首先在 WiPy 上复制 UART 通信。这是一个常见的疏忽,有些人抱怨他们的 WiPy 不能通过 USB 连接。他们没有做到的是(除了阅读在线文档之外)在通过网络连接的 WiPy 上运行下面的代码。这个代码复制了扩展板上的 UART,这样你就可以通过 USB 连接了。但是,如果您计划通过网络使用 WiPy,则不需要这样做。

from machine import UART
import os
uart = UART(0, 115200)
os.dupterm(uart)

Note

较新的 WiPy 板应该已经有这个代码。

如果您的 WiPy 在boot.py文件中没有这个代码,您应该考虑添加它。

连接到您的 WiFi 网络

如果您想将 WiPy 连接到 WiFi 网络,您需要对 boot.py 脚本进行一些小的更改。最简单的方法是从 WiPy 中复制它,修改它,然后将新版本复制到 WiPy 中。最好的方法是使用文件传输协议(ftp)。大多数操作系统都有 ftp 客户端,但是如果你的系统没有,你可以找到几个下载选项。使用用户micropassword python 进行连接。清单 3-2 展示了如何连接到 WiPy 并将boot.py文件复制到你的 PC 上。

$ mkdir wipy_files
$ cd wipy_files/
$ ftp 192.168.4.1
Connected to 192.168.4.1.
220 Micropython FTP Server
Name (192.168.4.1:cbell): micro
Password:
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> cd flash
ftp> get boot.py
local: boot.py remote: boot.py
227 (192,168,4,1,7,232)
   108        6.33 KiB/s
108 bytes received in 00:00 (6.23 KiB/s)
ftp> get main.py
local: main.py remote: main.py
227 (192,168,4,1,7,232)
    34        2.53 KiB/s
34 bytes received in 00:00 (2.47 KiB/s)
ftp> quit
MacBook-Pro:wipy_files cbell$ ls
boot.py  main.py
Listing 3-2.Copying Files From the WiPy

注意,我在本地 PC 上创建了一个目录,然后用命令 ftp 192.168.4.1 连接到 WiPy。然后,我切换到闪存目录,用 get 命令复制文件。一旦我复制了文件,我就退出 ftp 应用,现在文件就在我的本地机器上了。

Tip

备份您的原始 WiPy 文件,以便在需要时可以恢复到出厂设置。

现在我们可以修改 boot.py 文件来连接到我们的本地网络。我们需要做的是使用网络模块中的 WLAN 类来配置它,以扫描网络并通过名称(SSID)和密码连接到您的 WiFi 网络。清单 3-3 显示了您需要添加到您的boot.py文件中的代码(新行以粗体显示)。在将代码保存到 WiPy(以粗体显示)之前,请确保在代码中替换您的 SSID 和密码。

Caution

我建议等到您准备好运行您的 IOT 项目时再对您的boot.py文件进行这种更改。继续在默认模式下使用 WiPy,直到你完善了你的项目。

# boot.py -- run on boot-up
from machine import UART

from network import WLAN

import os
uart = UART(0, 115200)
os.dupterm(uart)

wlan = WLAN(mode=WLAN.STA)

wlan.scan()

wlan.connect(ssid='Your Network SSID', auth=(WLAN.WPA2, 'Your Network Password'))

while not wlan.isconnected():

    pass

print(wlan.ifconfig()) # prints out local IP

Listing 3-3.Enabling WiFi Connection on Boot (WiPy)

注意复制 UART 的代码也包括在内。另外,请注意末尾的打印。在将文件保存到 WiPy 之前,您应该使用 USB 上的 REPL 控制台测试代码,并粘贴它以确保它能够工作。我们必须使用 USB,因为 WiFi 会在执行过程中重置。下面显示了从 REPL 控制台运行代码的结果。

>>> from network import WLAN
>>> wlan = WLAN(mode=WLAN.STA)
>>> wlan.scan()
[(ssid='SSIDHERE1', bssid=b' \xc9\xd0\x18W\x11', sec=3, channel=1, rssi=-70), (ssid='SSIDHERE2', bssid=b'\x88\x1f\xa16X\x1c', sec=3, channel=6, rssi=-78), (ssid='SSIDHERE3', bssid=b'\xb8\x8d\x12bX\xd3', sec=3, channel=1, rssi=-90)]
>>> wlan.connect(ssid="SSIDHERE1", auth=(WLAN.WPA2, "SSIDPASSWORD"))
>>> while not wlan.isconnected():
...     pass
>>> print(wlan.ifconfig())
('10.0.1.128', '255.255.255.0', '10.0.1.1', '10.0.1.1')

注意,返回的 IP 地址(print 语句中的第一个)是 10.0.1.128。现在,您可以使用 telnet 连接到您的 WiPy,如下所示。

$ telnet 10.0.1.128
Trying 10.0.1.128...
Connected to 10.0.1.128.
Escape character is '^]'.
MicroPython v1.8.6-689-g095792e0 on 2017-06-27; WiPy with ESP32
Login as: micro
Password:
Login succeeded!
Type "help()" for more information.

一旦您对它的工作感到满意(您有正确的 SSID 和密码),您就可以进行更改,并将文件复制到您的 WiPy 中,如清单 3-4 所示。

$ ftp 192.168.4.1
Connected to 192.168.4.1.
220 Micropython FTP Server
Name (192.168.4.1:cbell): micro
Password:
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> cd flash
ftp> put boot.py
local: boot.py remote: boot.py
227 (192,168,4,1,7,232)
100% |***********************************|   336     1.84 MiB/s   00:00 ETA
336 bytes sent in 00:00 (0.86 KiB/s)
ftp> quit
Listing 3-4.Copying Files To the WiPy

现在你可以重启你的 WiPy,等待蓝色 LED 闪烁;那你应该能在你的 WiFi 网络上看到。酷吧。

使用 SD 驱动器

与 Pyboard 一样,WiPy 在与扩展板配合使用时,也有一个微型 SD 驱动器。您可以使用 SD 卡来存储数据、脚本等。然而,与 Pyboard 不同的是,SD 驱动器在主板启动时并不使用(您不能从它启动),当通过 USB 电缆连接到您的 PC 时也不可安装。

您的电脑上既没有安装内置驱动器,也没有安装 SD 卡(如果连接到扩展卡,并且插入了一张 FAT 格式的卡)。您必须使用文件传输协议(FTP)应用来访问您的文件,就像我们在上一节中看到的那样。但是,您可以通过 REPL 控制台安装 SD 卡。如果您总是想要访问 SD 驱动器,可以将此代码添加到闪存驱动器上的 boot.py 文件中。

我们需要做的是运行一些 MicroPython 代码来启用 SD 卡并挂载它。以下显示了如何安装 SD 卡。

from machine import SD
try:
    sd = SD()
    os.mount(sd, '/sd')
    print('Card mounted at /sd')
except:
    sd = None
   print('ERROR: Card not mounted. ')

现在,当您访问您的文件时,您会看到 SD 卡。

$ ftp 192.168.4.1
Connected to 192.168.4.1.
Micropython FTP Server
Name (192.168.4.1:cbell): micro
Password:
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> ls
drw-rw-r--   1 root  root         0 Jan  1  1985 flash
drw-rw-r--   1 root  root         0 Jan  1  1985 sd

加载固件

在 WiPy 上加载固件比在 Pyboard 上加载要简单一些。Pycom 为 Windows、macOS 和 Linux 提供了非常易于使用的固件升级工具,帮助您逐步完成自动选择最新版本的过程。你可以从 https://docs.pycom.io/chapter/gettingstarted/installation/firmwaretool.html 下载。固件升级过程最好使用扩展板来完成。

Tip

如果你打算用 WiPy,一定要买扩展板。这是非常值得的。

您只需为您的平台下载升级工具,安装它,然后运行应用。例如,在 macOS 上,你可以点击链接,下载工具,打开存档,并安装它。

像 Pyboard 一样,您也必须跳线其中一个引脚。在这种情况下,我们将通过将跳线连接到 GND 来拉低引脚 G23。握住主板,使 USB 连接器位于顶部时,G23 引脚是左上第四个引脚,GND 引脚是右上第二个引脚。图 3-19 显示了 WiPy 扩展板上正确安装的跳线。

A447395_1_En_3_Fig19_HTML.jpg

图 3-19。

Jumper Installed for Firmware Upgrade Tool (WiPy Expansion Board)

现在,让我们简单看看 Pycom 固件升级工具是如何在 macOS 上运行的。该对话框有四个主要面板:欢迎、设置、通信和结果。您可以单击“继续”按钮前进到每个面板。在设置面板上,会提醒您需要进行的连接以及如何设置跳线。在通信面板上,选择 WiPy 连接的通信(USB)端口。升级完成后,结果面板会告诉您何时可以断开板的连接。图 3-20 按顺序显示对话框,从左上顺时针方向。

A447395_1_En_3_Fig20_HTML.jpg

图 3-20。

Upgrading the Firmware (WiPy)

特殊应用

还有一点是 WiPy(和所有 Pycom 板)所独有的。Pycom 还为一些流行的编辑器提供了一个特殊的编辑器插件,允许你在 WiPy 上连接和处理你的文件。该插件名为 PyMakr,目前可用于 Atom 编辑器(但很快将可用于其他编辑器)。Pycom 还为您的移动设备提供了一个名为 PyMate 的应用,允许您在移动设备上与 WiPy 进行交互。

PyMakr 插件是 WiPy 体验的一个很好的补充。该插件是 Pycom 最初发布的原始 PyMakr 编辑器的替代品,但他们意识到开发一个新的编辑器不如开发一个允许用户使用他们现有的喜爱的编辑器的插件有效率。

该插件允许您在编辑器中打开 REPL 控制台,移动(同步)文件,打开 Python 脚本并运行它们,配置控制台以连接到其他网络上的 WiPy(设置),并获取有关 WiPy 的信息,如固件版本、SSID 等。图 3-21 显示了一个在 Atom 编辑器中运行的插件的例子。

A447395_1_En_3_Fig21_HTML.jpg

图 3-21。

PyMakr Plugin (Atom)

如果你喜欢这里的早期版本的例子,你可以访问下面的 URL 并查看其他编辑器的未来版本: https://docs.pycom.io/chapter/gettingstarted/installation/pymakr.html

用于移动设备的 PyMate 应用还允许您与您的 WiPy 进行交互,类似于使用 WiPy 的 PyMakr 插件,但它可以提供一些小部件,您可以使用这些小部件从您的 WiPy 中读取数据并显示数据。小部件可用于在移动设备上显示脚本中的数据。小部件包括线形图和条形图、控制 led 的按钮等等。

PyMate 的设置需要从苹果或谷歌应用商店下载,并在您的移动设备上进行配置。它还需要向 Pycom 注册应用。设置 WiPy 需要首先连接到您的 WiFi(或蜂窝)网络以下载正确的文件,然后选择您的设备,最后连接到 WiPy WiFi 以上传文件。该过程会修改boot.py文件,所以一定要先备份该文件。如果您遇到问题或者无法再访问您的 WiPy,您可以引导到一个安全引导模式,并将原始 boot.py 文件复制到 WiPy(或者编辑 PyMate 为您创建的文件)。一旦在 PyMate 中设置好设备,就可以连接到它并执行脚本,配置想要与之交互的小部件。

PyMate 应用目前非常新,关于如何使用它的文档非常少。出于这个原因,您可能想等到完成本书中的项目后再使用 PyMate 应用。这主要是由于对boot.py文件的修改,但也因为您必须首先学习如何使用 MicroPython 访问硬件,这样您才能使用小部件。图 3-22 显示了 PyMate 应用。

A447395_1_En_3_Fig22_HTML.jpg

图 3-22。

PyMate Application (courtesy of pycom.io)

如果您已经决定购买 WiPy(和扩展板),我鼓励您查看这些应用。

Deep Sleep Issue Resolved

Pycom 最近宣布了他们的主板关于深度睡眠模式的一个小问题,深度睡眠模式是一种您可以通过编程将主板置于低功耗模式并稍后唤醒的状态。使用深度睡眠模式可以让主板省电,对于使用太阳能或电池供电的项目尤其方便。该问题被发现是一个硬件问题,Pycom 正在构建一个特殊的附加板(称为 shield ),您可以使用它来纠正旧主板的深度睡眠问题。请访问 pycom.io 了解深度睡眠盾的最新信息。本书中的项目不需要深度睡眠模式,但是你自己的 IOT 项目可能需要。

去哪里买

您可以直接从 Pycom ( https://www.pycom.io/webshop/ )购买 WiPy 和其他板。Adafruit 也有主板,但可能没有所有最新的配件( https://www.adafruit.com/?q=pycom& )。Pycom 还有一个便利的零售商链接,你可以用它来找到离你更近的经销商( https://www.pycom.io/where-to-buy/ )。WiPy 的价格约为 25 美元,扩展板约为 20 美元。你应该考虑买一块扩展板,让你的 WiPy 工作起来更轻松。

您可以购买带或不带接头的电路板。为了在本书中使用,最好安装标题。这允许您将电路板插入试验板。

兼容 MicroPython 的主板

下一组要探索的硬件是那些没有配备 MicroPython 的主板,但可以加载 MicroPython 固件或使用特殊软件执行 MicroPython。

这个类别的板需要一些努力来配置,可能不适合初学者(除了 BBC micro:bit)。它们也可能不支持相同的硬件功能,如 GPIO 引脚,具有 MicroPython(目前)不支持的附加硬件,或者在某种情况下可能需要 MicroPython 的特殊变体才能正常工作。因此,我不会花太多时间来描述这些板或它们的硬件。相反,我将简要概述这些板,然后描述如何开始,包括加载 MicroPython 所需的过程。如果您决定使用这些板中的一种,请查看供应商的网站,了解有关使用 MicroPython 板的最新信息。

这里涉及的板是广受欢迎的 BBC micro:bit 板,它在学校的使用趋势越来越明显,来自 Adafruit 的最新电路板称为 Circuit Playground Express,以及 Adafruit Feather Huzzah。这些板是按照让 MicroPython 在板上工作所需的复杂程度排列的。

BBC 微:比特

BBC micro:bit 板是专门设计的,非常容易使用。事实上,它旨在帮助教孩子更多关于硬件和软件的知识。在这一点上,BBC micro:bit 是一个巨大的成功。这一成功的部分原因在于该板的易用性——它的外形尺寸只有大约 52 毫米 x 42mm 毫米,两侧都有元件。

一侧是可编程 led 阵列和两个按钮。另一侧是包括处理器、微型 USB 连接器、复位按钮和电池连接器在内的组件。该板的 GPIO 接头沿底部边缘排列,也是双面的。一组大孔引脚(称为鳄鱼引脚)均匀分布,允许接地、电源(3V)和(3) GPIO 引脚。这使得使用带有边缘连接器的板变得简单。图 3-23 显示了 BBC micro:bit 板的正面和背面。

A447395_1_En_3_Fig23_HTML.jpg

图 3-23。

The BBC micro:bit Board (front and back)

该板成功的另一个原因是为支持它而创建的软件。开发人员已经开发了一个易于使用的软件来与电路板一起工作。由于该板旨在像类 C 语言中的 Arduino 编码一样使用,可用的软件超出了本书的范围,但您可以在 http://microbit.org/ 了解更多信息。然而,我们将很快看到一个特殊的应用,它使我们能够在 BBC micro:bit 上创建、编辑和运行 MicroPython。酷。

BBC micro:bit 没有联网功能,但有蓝牙,可以用来连接另一台设备,将数据转发到互联网上。因此,它可以用于 IOT 项目,但不像带有 WiFi 模块的 WiPy 或 Pyboard 那么容易,可能需要一个中间节点,如 PC 或小型计算机,如 Raspberry Pi 甚至 Arduino。

以下列出了 BBC micro:bit 板的一些硬件特性的概述。当主板通过 USB 电缆连接到 PC 时,您也可以访问板载 USB 驱动器。

  • 32 位 ARM Cortex 处理器
  • 16K 帧
  • 用于检测方向的圆规
  • 用于检测运动变化(速度)的加速度计
  • 低能耗蓝牙(BLE)
  • (2)可编程按钮
  • 可编程 led 的 5x5 阵列
  • (3)鳄鱼数字/模拟引脚
  • 边缘连接器上的 20 个 GPIO 引脚
  • 电池连接器

现在我们已经对硬件有了一个简单的了解,让我们看看如何将开发板与 MicroPython 一起使用。

使用 MicroPython 启动并运行

BBC micro:bit 板是与 MicroPython 一起使用的最简单的替代板。这要归功于两个软件应用——一个名为 Mu 的桌面应用,一个名为 uFlash 的命令行工具。Mu 是一个完整的编辑器,你可以在你的 PC 上使用,当通过 USB 电缆连接到你的 BBC micro:bit 时,可以保存和执行脚本。uFlash 工具可用于手动将 Python 脚本传输到电路板。这两个选项都可以在 Windows、macOS 和 Linux 上使用。图 3-24 显示了一个使用 Mu 编写一个简短的 MicroPython 脚本来使用 led 滚动消息的例子。

A447395_1_En_3_Fig24_HTML.jpg

图 3-24。

The Mu Editor for MicroPython on the BBC micro:bit

与我们必须首先安装固件才能使用 MicroPython 的其他主板不同,BBC micro:bit 可以使用这些工具之一来运行 MicroPython 脚本。例如,我们可以使用 Mu 编写我们的 MicroPython 脚本,然后用该脚本的编译版本(称为十六进制文件)来“刷新”BBC micro:bit 板。是的,这意味着你可以写你的脚本,编译它,并直接闪存(加载十六进制文件)到主板上!我们只需在编辑器中编写代码,然后点击 Flash 将其加载到板上。最棒的是,该板被编程为每次启动时总是运行脚本。因此,这意味着我们可以直接将自己的代码加载到电路板上,而无需额外的工作。酷。

您仍然可以通过单击 REPL 按钮,通过管理部门应用使用 REPL 控制台访问电路板。图 3-25 显示了在 Windows 10 上运行于 Mu 的 BBC micro:bit 的 REPL 控制台。

A447395_1_En_3_Fig25_HTML.jpg

图 3-25。

REPL Console via Mu on Windows 10 (BBC micro:bit) Tip

你可以在 BBC micro:bit at https://microbit-micropython.readthedocs.io/en/latest/ 上读到关于使用 MicroPython 的最新消息。

去哪里买

你可以从一些零售商那里购买 BBC micro:bit 以及一些配件,包括 Adafruit ( https://www.adafruit.com/?q=micro%3Abit& )、Sparkfun ( https://www.sparkfun.com/categories/284 )、Pi Hut ( https://thepihut.com/collections/micro-bit-store )和 Kitronik ( https://www.kitronik.co.uk/microbit.html )。你也可以在特别的“买一送一”促销活动中从开发商那里购买主板,该活动在 https://give.microbit.org/ 向世界各地的学校免费赠送 BBC micro:bit 主板。该板的成本约为 25 美元。

如果您计划将该板用于 IOT 项目或本书中的项目,我建议您也购买一个 edge 连接器分线板,如 Sparkfun ( https://www.sparkfun.com/products/13989 )的分线板。

Circuit Playground Express(开发者版)

下一个最容易使用的替代板是 Adafruit 的 Circuit Playground Express 板。实际上,Circuit Playground Express(开发者版)是一个奇怪的替代板。它的模式是在其他几个 Adafruit 可穿戴板的基础上设计的,这样它就可以融入到衣服中。 9 因此,它是圆形的,直径约 50 毫米。

Note

当前的主板附加了开发人员版的名称,因为它仍处于早期版本。我怀疑将来会有更完整的生产形式的板。

电路板外侧有 10 个带大孔的引脚(称为鳄鱼垫),可以与鳄鱼夹配合使用,也可以用导线缝合。Adafruit 还向男性跳线出售短吻鳄皮,你可能会发现这是将该板与面包板配合使用所必需的。参见 https://www.adafruit.com/product/3255 处的小鳄鱼夹到公跳线束。另一个有趣的功能是一组 10 个 RGB 新像素,这是一种明亮的 led,你可以通过代码改变颜色。该板还装有传感器,是 IOT 项目实验的一个很好的选择。图 3-26 为赛车场快速板。

A447395_1_En_3_Fig26_HTML.jpg

图 3-26。

Circuit Playground Express, Developer Edition (courtesy of adafruit.com)

下面列出了 Circuit Playground Express 板的一些硬件功能的概述。当主板通过 USB 电缆连接到 PC 时,您也可以访问板载 USB 驱动器。

  • ATSAMD21 ARM Cortex M0 处理器,运行频率为 3.3V 和 48MHz
  • 2mb SPI 闪存,主要用于 CircuitPython 存储代码和库
  • 用于编程和调试的微型 USB 端口。USB 端口可以充当串行端口、键盘、鼠标、操纵杆或 MIDI
  • 10 倍迷你 RGB 新像素
  • 运动传感器
  • 温度传感器(热敏电阻)
  • 光传感器(光电晶体管)。也可以作为颜色传感器和脉冲传感器。
  • 声音传感器(MEMS 麦克风)
  • 迷你音箱!
  • (2)按钮
  • (1)滑动开关
  • 红外接收器和发射器。也可以作为接近传感器。
  • (8)鳄鱼夹友好输入/输出引脚
  • I2C,UART,8 个引脚,可进行模拟输入,多路 PWM 输出
  • (7)触摸板可以作为电容式触摸输入,剩下的 1 个是真正的模拟输出
  • 红色“#13”可编程 LED
  • 复原按钮

Note

还有 Adafruit 的 Circuit Playground 经典板。不要把它和游乐场快车混淆。这些板是不一样的;只有 Express board 可以运行 MicroPython。

然而,与 Pyboard 一样,Circuit Playground Express(开发者版)不具备任何联网功能。因此,您必须使用外部模块连接到您的网络。我发现最好的选择是蓝牙模块,但其他模块如 CC3000 可能是替代品。

该板也不运行 MicroPython。相反,它运行一个名为 CircuitPython 的特殊版本的 MicroPython。CircuitPython 是由 Adafruit 设计和维护的 MicroPython 的衍生产品,专为 Circuit Playground Express 和许多其他电路板而构建。CircuitPython 被设计成可以在多种电路板上运行,包括 Circuit Playground Express、Feather 和其他流行的电路板。CircuitPython 目前与 MicroPython 版本 1.8.4 兼容,但会定期更新。

虽然 CircuitPython 与 MicroPython 兼容,但对于某些特性,您可能需要使用不同的硬件库。存在一些差异,这些差异记录在 https://github.com/adafruit/circuitpython#differences-from-micropython 中。但是对于我们的使用来说,它的工作方式几乎和我们对 MicroPython 的期望完全一样。

Adafruit 有一套很好的教程和博客来帮助你。更多关于 CircuitPython 的信息,参见 https://github.com/adafruit/circuitpythonhttps://learn.adafruit.com/search?q=circuitpython&

现在我们已经对硬件有了一个简单的了解,让我们看看如何将开发板与 MicroPython 一起使用。

使用 CircuitPython 启动和运行

虽然 Circuit Playground Express 板没有像 BBC micro:bit 那样的花哨应用,但一旦安装了正确的驱动程序,在板上加载 CircuitPython 二进制文件(固件)就出奇地容易。事实上,你需要做的就是下载当前的二进制文件,并将其复制到主板的驱动器上。所涉及的步骤总结如下。

如果使用 Windows 10,则需要先下载 Adafruit 的专用设备驱动程序。你可以在 https://github.com/adafruit/Adafruit_Windows_Drivers/releases/download/1.0.0.0/adafruit_drivers.exe 得到司机。只需下载并运行安装程序。驱动程序支持许多 Adafruit 板。当您进入安装选项页面时,您可以选择您想要支持的板。我建议选择所有板,这样您就可以使用任何支持 CircuitPython 的 Adafruit 板。正如您将看到的,有许多这样的选项。图 3-27 显示了安装选项的示例。

A447395_1_En_3_Fig27_HTML.jpg

图 3-27。

Adafruit Boards Driver – Installation Options

接下来,我们需要下载固件。Adafruit 以一种称为 USB 闪存格式(UF2)的特殊格式构建固件。 10 前往 https://github.com/adafruit/circuitpython/releases 下载最新版本。比如我下载的文件就被命名为adafruit-circuitpython-circuitplayground_express-2.0.0-beta.1.uf2

Note

当你进入那个页面时,你会发现每个板都有两个选项:一个.bin文件和一个.uf2文件。.bin文件与一个名为 bossac、 11 的命令行工具一起使用,但是我们不会使用那个工具,因为 UF2 格式更容易使用。

现在,您可以将 Circuit Playground Express 连接到您的电脑。当 u 盘挂载(应该命名为CIRCUITPY)时,将旧的名为CURRENT.UF2的 UF2 文件复制到你的 PC 上。我们将用它作为备份。接下来,将新的 UF2 拖到CIRCUITPY文件夹中。文件复制完成后,电路板将重启并运行 CircuitPython。就这样!您刚刚加载了 CircuitPython!看,这比其他任何一块板都容易,是吧?

Caution

您必须保存原始的 UF2 文件,以便可以反向安装 Python。

您现在应该看到驱动器返回,但没有 UF2 文件。例如,我的板只显示了一个名为 boot_out.txt 的文件。现在,您可以使用 screen 或 Windows 上的 PUTTY 连接到您的板。但是首先,检查一下主板使用的是哪个 COM 端口。图 3-28 显示了在 Windows 上使用 PUTTY 运行 REPL 控制台。

A447395_1_En_3_Fig28_HTML.jpg

图 3-28。

REPL Console on Windows (Circuit Playground Express)

幸运的是,将电路板恢复到默认配置也很容易。您可以通过单击两次重置按钮将主板置于引导加载程序模式来完成此操作。当处于引导模式时,led 将变为红色,在此期间,您可以复制我们保存的名为 CURRENT 的原始文件。UF2 到弹出的驱动器(名为 CIRCUITPY)。复制完成后,主板将重新启动并运行原始固件。

去哪里买

Circuit Playground Express(开发者版)板可从 Adafruit 和 Adafruit 产品经销商处获得。你可以在 https://www.adafruit.com/product/3333 找到棋盘。成本约为 25 美元。

如果您决定尝试 Circuit Playground Express 板,我建议您购买一些鳄鱼夹,这样您就可以将该板与试验板一起使用,或者将其连接到其他组件。请访问 Adafruit 商店,了解更多将电路板连接到您的组件的想法和附件。

阿达果羽毛 Huzzah

ada fruit Feather huz zah w/ESP8266 WiFi 是另一个非常受欢迎的板,但在它上面加载 MicroPython 仅限于使用命令行工具,在 Windows 上使用有点奇怪。因此,我认为这是最难使用的 MicroPython 兼容板。幸运的是,它确实使用了 MicroPython,而不是 Circuit Playground Express 这样的 CircuitPython。

事实上,该板具有许多特性,使其成为 MicroPython IOT 项目的绝佳替代板。其中最主要的是它有网络,在 WiFi 网络上工作得很好,与 WiPy 不相上下。

该板如此受欢迎的原因之一是因为它基于 ESP8266 12 WiFi 微控制器,时钟频率为 80 MHz,并在一个小巧轻便的封装中结合了丰富的功能(因此得名羽毛)。该板也非常小,大约是一包口香糖的大小,尺寸为 51 毫米 x 23mm 毫米 x 8 毫米,适合放在试验板上,这也使它成为一种优秀的原型制作工具。该板有两行 GPIO 接口,包括通信(RX、TX)、I2C 和 SPI 协议。图 3-29 为阿达果羽毛 Huzzah 板。

A447395_1_En_3_Fig29_HTML.jpg

图 3-29。

Adafruit Feather Huzzah (courtesy of adafruit.com)

下面列出了 Circuit Playground Express 板的一些硬件功能的概述。当主板通过 USB 电缆连接到 PC 时,您也可以访问板载 USB 驱动器。

  • ESP8266 @ 80MHz,带 3.3V 逻辑/电源
  • 4MB 闪存
  • 内置 WiFi 802.11 b/g/n
  • 峰值电流输出为 500mA 的 3.3V 稳压器
  • 微型 USB 连接器
  • (9) GPIO 引脚-也可用作 I2C 和 SPI
  • (1)最大 1.0V 时的模拟输入
  • 100mA LiPoly 充电器,带充电状态指示灯 LED
  • 红色可编程 LED (#0)
  • 蓝色可编程发光二极管(#2)
  • 电源/使能引脚
  • 4 个安装孔
  • 复原按钮

现在我们已经对硬件有了一个简单的了解,让我们看看如何将开发板与 MicroPython 一起使用。

使用 MicroPython 启动并运行

在 Feather Huzzah(或任何 ESP8266 板)上使用 MicroPython 的过程需要确保您加载了正确的驱动程序,使用基于 Python 的串行 bootloader 实用程序分两步进行,首先擦除闪存,然后写入新的固件。

首先,确保您的电脑安装了正确的驱动程序来识别主板。同样,Adafruit 有一个我们可以使用的驱动程序,可以在 https://www.silabs.com/products/development-tools/software/usb-to-uart-bridge-vcp-drivers 找到。只需下载并安装驱动程序,然后将您的主板重新连接到 PC。您应该能够在您的 PC 上找到 COM 端口(或/ dev/文件)。

Feather Huzzah 的一个优点是,它支持在固件上传之前进入引导模式。当我们试图访问引导程序时,该功能会自动检测并将其置于正确的模式。酷!

Tip

在其他 ESP8266 板上加载 MicroPython 可能需要将板设置为 bootloader 模式。查看您的文档了解如何做到这一点。

我们唯一需要的是正确的固件文件。可以从 http://micropython.org/download/#esp8266 下载最新固件。我选择了支持调试的最新版本,但是关闭了基于 Web 的 REPL——一个名为esp8266-ota-20170613-v1.9.1-4-g6ed4581f.bin的文件。下载文件,并记住你把它(和它的名字)放在哪里,因为我们很快就会需要它。

要开始加载固件,我们需要来自 https://github.com/espressif/esptool 的 ESP8266 和 ESP32 串行引导程序实用程序。使用克隆或下载按钮下载工具,然后解压缩文件。您不必安装该工具。我们将使用 Python 解释器从目录中运行它。

一旦安装了驱动程序,打开控制台(终端)并切换到解压esptool.py实用程序的目录,并且您的主板通过 USB 电缆连接到您的 PC,我们就可以开始使用esptool.py脚本和erase_flash命令擦除主板上的闪存驱动器,如清单 3-5 所示。

$ python ./esptool.py --port /dev/tty.SLAB_USBtoUART erase_flash
esptool.py v2.0.1
Connecting........_
Detecting chip type... ESP8266
Chip is ESP8266
Uploading stub...
Running stub...
Stub running...
Erasing flash (this may take a while)...
Chip erase completed successfully in 8.8s
Hard resetting...
Listing 3-5.
Erasing

Firmware (Feather Huzzah)

这个过程只需要几分钟。完成后,我们可以使用清单 3-6 中所示的esptool.py脚本和write_flash命令上传固件。

$ python ./esptool.py --port /dev/tty.SLAB_USBtoUART --baud 460800 write_flash --flash_size=detect 0 ∼/Downloads/esp8266-ota-20170613-v1.9.1-4-g6ed4581f.bin
esptool.py v2.0.1
Connecting........_
Detecting chip type... ESP8266
Chip is ESP8266
Uploading stub...
Running stub...
Stub running...
Changing baud rate to 460800
Changed.
Configuring flash size...
Auto-detected Flash size: 4MB
Flash params set to 0x0040
Compressed 819888 bytes to 545209...
Wrote 819888 bytes (545209 compressed) at 0x00000000 in 12.7 seconds (effective 514.6 kbit/s)...
Hash of data verified.
Leaving...
Hard resetting...
Listing 3-6.
Uploading

Firmware (Feather Huzzah)

这个过程需要一点时间,但一旦完成,您可以重置板或断开连接,然后重新连接;如图 3-30 所示,您可以用 REPL 控制台连接到板上。

A447395_1_En_3_Fig30_HTML.jpg

图 3-30。

REPL Console (Feather Huzzah)

我应该指出,我遇到了一些与最新固件的小通信问题,所以在该版本中可能会有一些不稳定。确保使用最新的固件以避免问题。

What About Other ESP8266 Boards?

如果您想知道是否可以在其他 ESP8266 和类似的板上加载 MicroPython,答案是肯定的,可以!请参见 http://micropython.org/download/#esp8266 中的“ESP8266 板固件”,了解可用的最新固件。

去哪里买

羽毛 Huzzah 板可从 Adafruit 和 Adafruit 产品经销商处获得。你可以在 https://www.adafruit.com/product/2821 找到棋盘。您可以购买不带接头、带引脚接头或带堆叠接头的电路板。我建议购买堆叠接头,这样您就可以将公/公跳线与试验板一起使用,但普通引脚接头也很好,因为您可以将板直接插入试验板。堆叠式割台的成本约为 20 美元。

现在,让我们简要讨论一下您可以使用的其他一些电路板。正如你可能会怀疑的,这种类型的板是最难使用的,并不适合所有人。

其他电路板

随着 MicroPython 越来越受欢迎,您可能会看到更多可用的板。事实上,目前正在努力使 MicroPython 在几个主板上可用,包括 Teensy、Arduino 和 ESP8266 (Espressif)芯片组主板的几个变量。有一些早期的,有限的版本可用,如 Teensy 3。x 版本的板,可以用 MicroPython 加载,但是这个过程需要交叉编译的经验,因此不适合初学者(但是欢迎您尝试!). 十三

然而,请记住,第三方板可能在功能上有些滞后,文档通常最多是不完整的。但是现在您已经阅读了关于生产板的内容,您应该已经掌握了使用新板所需的知识。

我们也开始看到 Pyboard 的几种变体出现。到目前为止,我已经看到了另外两个来自亚洲的板子,看起来像是 Pyboard 的克隆版。我试用过其中一个克隆板,虽然 GPIO 引脚布局不同,但它的行为与 Pyboard 完全一样。如果您发现您想要在一个大型项目或一个多次完成的项目中使用多个电路板(如在教学环境中),您可能需要考虑克隆电路板,因为它们可以节省大量成本。

如果本章没有介绍您要使用的主板,您可以查看 MicroPython 网站( http://micropython.org/download ),了解适用于您主板的 MicroPython 固件安装的最新信息。

现在我们已经看到了一些可用的 MicroPython 板,让我们深入了解一下您可以用来构建项目的附件。

分线板和附加组件

我们将探讨的最后一点硬件是那些可与 MicroPython 板一起使用的板。这些可以是特殊的、独立的模块(称为分线板),以及专门设计用于 MicroPython 板的板(取决于板,称为屏蔽板或面板)。以下部分简要描述了一些分线板、屏蔽/外壳和一些附件,您可以根据选择的板来考虑这些附件。这个清单既不全面,也不是你必须买的东西的清单。在讨论完 MicroPython 之后,我们将在每个示例章节中看到推荐的硬件。

分线板

分线板是业余爱好者和爱好者在创建 MicroPython(或任何基于微控制器的)IOT 解决方案时将使用的关键元素之一。这是因为分线板是小型电路板,包含支持传感器、网络接口甚至显示器等功能所需的所有组件。分线板还支持几种通信协议中的一种,这种协议只需要连接几个引脚,非常易于使用。总的来说,它们为开发人员节省了大量时间,让他们知道如何设计电路来支持传感器或芯片。

要在我们的项目中使用分线板,我们只需要知道使用哪个接口以及如何连接。幸运的是,大多数供应商都会提供数据手册和其它文档来帮助您连接电路板。即使供应商只有 Arduino 的文档,学习如何进行连接仍然是有帮助的。诀窍是学习如何编写 MicroPython 代码。我们将在第 5 和 6 章中了解更多关于 MicroPython 库和硬件支持的信息。现在,让我们探索一些可用的分线板。同样,这里显示的只是一个非常小的样本。

事实上,Sparkfun 有大量的分线板可供选择。你可以找到各种有用的板子。完整列表见 https://www.sparkfun.com/categories/20 。Adafruit 也有大量的分线板可供选择。你可以在 https://www.adafruit.com/category/42 看到他们的产品。任何一家供应商都有公认运行良好的主板,并且有足够的文档支持它们。但是,如前所述,您可能需要对它们进行修改,以便与 MicroPython 硬件库一起使用。

回想一下前面的讨论,有用于提供网络连接的分线板。此外,回想一下,MicroPython 目前支持使用 CC3K WiFi 芯片组的分线板以及使用 WIZNET5K 以太网芯片组的分线板。我们之前看到了一种使用 Arduino CC3K 屏蔽的有趣方法,但图 3-31 显示的是 Adafruit CC3000 WiFi 分线板。我给你看这个有两个原因:首先,你可以看到它比 Arduino 盾小得多;第二,您可以看到一排引脚(目前没有接头)。请注意,这里只有几个引脚(如果不是通过阅读文档,而是根据经验),我们可以看到它支持与屏蔽层相同的 SPI 协议/接口。 14 就像重新利用的 Arduino shield 一样,我们可以使用这个分线板为不具备该功能的 MicroPython 板添加网络。

A447395_1_En_3_Fig31_HTML.jpg

图 3-31。

Adafruit CC3000 Module (courtesy of adafruit.com)

下一个分线板是我们将在后面的章节中用来读取天气数据的。有许多分线板包括用于天气数据的传感器。最常见和最容易使用的是那些读取温度,湿度和大气压力。图 3-32 显示了两个这样的分线板的例子:一个来自 Adafruit ( https://www.adafruit.com/product/992 ),另一个来自 Sparkfun ( https://www.sparkfun.com/products/13676 )。Adafruit MPL115A2 读取气压和温度,而 Sparkfun BME 280 测量气压、湿度和温度。两者都使用 I2C 接口/协议。 15

A447395_1_En_3_Fig32_HTML.jpg

图 3-32。

Weather Breakout Boards (courtesy of adafruit.com and sparkfun.com)

下一个分线板本身通常被认为是一个传感器,因为它只执行一个功能,但它仍然是一个分线板。许多传感器以这种方式封装,当它们是分线板时,通常被称为传感器。幸运的是,如果你用错了术语,大多数人会明白你的意思。您可以通过安装传感器的电路板是否包含其他分立元件和一排接头引脚来判断它是否是分线板。

在这种情况下,分线板测量土壤湿度,可以方便地创建一个工厂监测解决方案,我们将在后面的章节。图 3-33 展示了来自 Sparkfun ( https://www.sparkfun.com/products/13322 )的土壤水分分线板。

A447395_1_En_3_Fig33_HTML.jpg

图 3-33。

Soil Moisture Sensor (courtesy of sparkfun.com)

注意板子的独特形状。两臂或长叉是测量湿度的传感器设备的一部分。请注意,顶部是一组引脚(未安装接头),用于连接您的主板。此分线板不使用特殊接口;相反,传感器产生的电压可以在 MicroPython 板上的一个模拟引脚上测量。事实上,我们只需要三条线:5V,GND,和信号(连接到我们板上的模拟引脚)。同样,我们将在后面的章节中看到如何使用这个分线板。

最后一个分线板是 BBC micro:bit 板的专用配件。这是一个边缘连接器,您可以使用它将 BBC micro:bit 板插入试验板,从而更容易将引脚连接到其他组件。你可以在 Sparkfun ( https://www.sparkfun.com/products/13989 )找到这个分线板。如果你选择使用 BBC micro:bit board 来运行 MicroPython,我强烈推荐这个或者类似的东西。

A447395_1_En_3_Fig34_HTML.jpg

图 3-34。

BBC micro:bit Edge Breakout Board (courtesy of sparkfun.com)

特定于主板的屏蔽/外壳

MicroPython 板供应商,实际上是各种微控制器和类似板的供应商,封装他们的组件,以便他们可以用于称为 shields 或 skins 或类似东西的附加板。例如,Beaglebone 附加板称为 capes,Raspberry Pi 附加板称为 hats。这些板具有匹配的头部,用于插入(或堆叠在)主 MicroPython 板上以增加功能。许多屏蔽或外壳都有直通或堆叠接头,允许一次添加多个屏蔽。例如,Arduino 及其屏蔽格式可以配置为添加两个、三个或更多屏蔽。

在本节中,我们将看到一些可用于 Pyboard 和 WiPy MicroPython 板的附加板。如果您决定在本书中的项目中使用另一种板,请向供应商或零售商了解附加板的可用性。因为有几块板相对较新,所以你应该每隔几周左右检查一下,看看是否有新的板被添加进来。

Pyboard

Pyboard 附加板称为皮肤。 16 可以从micropython.org ( https://store.micropython.org/store/#/store )购买 Pyboard 皮肤(以及其他配件)。其他零售商可能会出售这种水果,但 Adafruit 目前不出售这种水果皮。皮肤通常被包装并作为未组装的套件出售,因此如果你想使用一个但不知道如何焊接,你可能需要学习或找到可以为你焊接组件的人。幸运的是,小的表面贴装元件已经组装好了,所以你最需要做的就是焊接更大的元件,比如接头。

第一个皮肤是一个原型板,允许您在皮肤上构建一个电路,以便您可以将其与 Pyboard 一起使用。它带有一套完整的标题。我喜欢它,因为虽然它比 Pyboard 略大,但有足够的空间,如果你想组装一个电路,你可以。此外,它允许您删除皮肤,以便您可以在其他项目中使用您的 Pyboard。图 3-35 显示了来自 micropython.org 的原型皮肤通孔 XY 尺寸皮肤

A447395_1_En_3_Fig35_HTML.jpg

图 3-35。

Pyboard Protoskin Through-hole XY-Size

下一个皮肤是一个奇怪而有趣的功能,它为 Pyboard 添加了音频。它被简单地称为音频皮肤,允许你播放和录制短声音,以及播放某些波形(声音)。我展示这本书是为了激起你对本书以外的项目的兴趣。图 3-36 显示了来自 micropython.org 的音频皮肤。

A447395_1_En_3_Fig36_HTML.jpg

图 3-36。

Pyboard Audio Skin

下一个皮肤是另一个非常有趣的特性,它允许你以触摸 LCD 的形式给你的 Pyboard 添加一个视觉元素。具有电阻式触摸和头部的彩色 LCD 皮肤增加了使用 160x128 像素图形 LCD 显示器和背光以及覆盖整个屏幕的电阻式触摸传感器来添加交互界面的能力。这意味着您可以为您的 MicroPython 项目构建小型图形界面。酷!图 3-37 显示了带有电阻式触摸的彩色 LCD 面板的正面和背面以及来自 micropython.org 的 headers 面板。幸运的是,你可以购买这种皮肤作为一个完整的组装套件。

A447395_1_En_3_Fig37_HTML.jpg

图 3-37。

Pyboard LCD Skin

WiPy 及相关

WiPy 和相关的附加板称为 shields。Pycom 包装和销售他们完全组装好并随时可用的盾牌。你可以从 Pycom ( pycom.io)或者 Adafruit ( adafruit.com)等其他零售商那里购买盾牌。他们的所有屏蔽都提供了一个带 LIPO 充电器的电池连接器、用于与电路板通信的微型 USB、一组适合所有 Pycom 电路板(WiPy、SiPy、LoPy、GPy 和 FiPy)的接头和一个微型 SD 读卡器。

我们看到的第一个屏蔽是扩展板,我认为它是 WiPy 和相关板的必需附件。下一个是设计用来容纳几个传感器的盾,称为 PySense 盾( https://www.pycom.io/product/pysense/ )。图 3-38 显示了重力感应盾。

A447395_1_En_3_Fig38_HTML.jpg

图 3-38。

PySense Shield (courtesy of pycom.io)

该盾牌的功能列表令人印象深刻,包括以下内容(由 pycom.io 提供)。显然,这里有很多你可以利用的东西,所以如果你正在考虑使用 WiPy,这个盾和下一个盾可能会打破平衡。事实上,我将在后面的章节中向您展示如何使用这个屏蔽来读取天气数据。

  • 环境光传感器
  • 大气压力传感器
  • 湿度传感器
  • 3 轴 12 位加速度计
  • 温度传感器
  • 带串行访问的 USB 端口
  • LiPo 电池充电器
  • MicroSD 卡兼容性
  • 超低功耗运行(深度睡眠时 1uA)

WiPy 和相关电路板的下一个(目前唯一的)盾牌是 PyTrack 盾牌( https://www.pycom.io/product/pytrack/ )。这个防护罩类似于 PySense 防护罩,但它没有一系列传感器(它有一个加速度计),而是有一个 GPS 芯片,你可以用它来记录位置。图 3-39 所示为 PyTrack 盾。

A447395_1_En_3_Fig39_HTML.jpg

图 3-39。

PyTrack Shield (courtesy of pycom.io)

该盾牌的功能列表令人印象深刻,包括以下内容(由 pycom.io 提供)。很明显,这里有很多你可以利用的东西,所以如果你考虑使用 WiPy,这个盾和之前的盾可能会扭转局势。

  • 超级精确的 GNSS + Glonass GPS
  • 3 轴 12 位加速度计
  • 带串行访问的 USB 端口
  • LiPo 电池充电器
  • Micro SD 卡兼容性
  • 超低功耗运行(深度睡眠时 1uA)

特定于主板的附件

我们将讨论的最后一类硬件是本章中讨论的几种主板可用的附件。就像生活中的几乎所有事情一样,我们可以为我们的电路板添加配件,以达到深远(且昂贵)的目的。虽然 MicroPython 主板可用的附件列表还没有达到 Raspberry Pi 或 Arduino 的书呆子比例,但仍有一些附件是您应该考虑的。

在这一节中,我将介绍 Pyboard、WiPy 和 BBC micro:bit 的一些我认为必不可少的附件。这并不意味着你应该冲出去买,或者这本书需要它们;相反,我认为如果你打算在完成本书中的例子后继续用这些板进行开发,你应该考虑它们。

例如,我觉得每个项目都应该放在一个盒子里,这样电路板和任何其他元件都不会受到意外触摸、跌落和其他可能损坏电子设备的事件的影响。如果您计划将电路板安装在某个地方进行长期操作,这一点尤其正确。因此,我为每块板列出一个案例。

Pyboard

除了 USB 线和皮肤等必需品之外,Pyboard 没有太多附件。但我发现有一件配饰是 Pyboard 不可或缺的——来自 micropython.org 的盒子。

Tip

白板配件的最佳来源是 micropython.org(https://store.micropython.org/store/#/store)。他们以欧盟为基地,向美国和其他国家发货。

你可以从 micropython.org 购买一个铝制外壳,这是我见过的最好的外壳之一。其他主板上也有一些类似的,但是它们的价格通常都过高。那根本不适用于这个例子。这款保护套有三种型号:一种是打开的盖子,可以接触到接头;另一种是关闭的盖子,可以安装触摸屏。它们都与带耳板相适应,因此使用这种情况时,您无需将它们折断。图 3-40 显示了打开盖子的纸板盒。

A447395_1_En_3_Fig40_HTML.jpg

图 3-40。

PyBoard Case

我对这个案子的质量怎么说都不为过。这简直是我见过的最好的。打开和关闭的盖子上有文字,标记了割台上的所有针脚。这个案子你不会失望的。它有点贵,大约 35 美元,但物有所值。他们经常缺货,所以经常检查新订单。

WiPy 和相关板

WiPy 的配件清单正在增加。你可以买到 USB 线等日常必需品,但 Pycom 也出售天线和各种各样的外壳等物品。

Tip

WiPy 配件的最佳来源是 Pycom ( https://www.pycom.io/webshop#accessories -)。虽然他们的总部设在欧盟,但他们确实以令人印象深刻的交货时间表向美国发货。

WiPy 的外壳有不同的颜色和形状。有尺寸适合 WiPy 和 shield 的轻型外壳,也有密封用于户外的外壳,包括一个足以容纳 WiPy、shield 和 LIPO 电池的外壳。图 3-41 显示了用于 WiPy 的烟色外壳,其可以容纳 WiPy 和护罩。

A447395_1_En_3_Fig41_HTML.jpg

图 3-41。

WiPy Case (courtesy of pycom.io)

接下来是一个电池盒,带有正确的连接器,可连接到您的主板上。您可以得到一个电池盒,其中包含用于正确电压的正确数量的电池(首先检查您的主板规格)以及带有正确连接器的电池盒。我选择购买一个通用的外壳,可以容纳四节 1.5V AA 电池,并自己添加了正确的连接器。图 3-42 显示了我在项目中经常使用的电池盒。我已经在 WiPy 部分列出了电池盒,但它也可以用于其他板,如 BBC micro:bit。

A447395_1_En_3_Fig42_HTML.jpg

图 3-42。

Battery Case Caution

请确保您的电池盒包含适合您的主板的大小和额定功率。

BBC 微:比特

拥有最令人印象深刻的附件列表的板是 BBC micro:bit。我确信这是由于它的流行,但它也有利于我们的努力。您可以找到用于建造机器人、电源配件、箱子等的工具包!

Tip

BBC micro:bit 配件的两个优秀来源是 Kitronik ( https://www.kitronik.co.uk/microbit.html )和 Pi Hut ( https://thepihut.com/collections/micro-bit-store )。两者都在欧盟,但都以令人印象深刻的交货时间表运往美国。

我喜欢的 BBC micro:bit 的电源选项之一是 Kitronik ( https://www.kitronik.co.uk/5610-mipower-board-for-the-bbc-microbit.html )提供的 MI:power board。这个板很好,因为它使用大型鳄鱼引脚(0,3V 和 GND)安装到您的 BBC micro:bit 板上,仅使用一个小型 3V 硬币电池供电。挂载的时候不占太多空间。它包括一个开关,允许您关闭电路板,并提供一个小扬声器(因此通过#0 引脚连接),以便您可以将声音添加到您的项目中。图 3-43 显示了 Kitronik 的电源屏蔽。

A447395_1_En_3_Fig43_HTML.jpg

图 3-43。

MI:power Board for the BBC micro:bit

如果你决定使用这种电池板,我强烈推荐它超过外部电池——特别是对于 BBC micro:bit,并且你决定使用外壳,你应该知道大多数外壳不是为支持 MI:power 板而设计的。然而,我确实找到了至少一个。正如你所猜测的,它是由制造 MI:power 的同一批人制造的!

BBC micro:bit 提供了许多案例,似乎每天都有另一个案例出现(包括那些可以在 3D 打印机上打印的案例)。然而,我发现我喜欢的那个和我在其他板上用过的一样。它由丙烯酸制成,使用尼龙螺栓将几层连接在一起。这是一个干净的组件,可以清晰地看到电路板,考虑到 BBC micro:bit 有很多 led,这很好!图 3-44 显示了 BBC micro:bit 的 MI:pro 案例。

A447395_1_En_3_Fig44_HTML.jpg

图 3-44。

BBC micro:bit MI:pro Case Kit

最后,如果你要经常使用 BBC micro:bit 进行实验,包括本书中的例子以及互联网上的几十个例子,你应该考虑 Kitronik ( https://www.kitronik.co.uk/5609-prototyping-system-for-the-bbc-microbit.html )的原型系统。该套件带有一个板,您可以安装一个附带的边缘连接器分线点和试验板,使该套件成为一个漂亮整洁的包装。图 3-45 显示了 Kitronik 的原型系统。该工具包包含在更大的发明人工具包中,如下侧栏所述。

A447395_1_En_3_Fig45_HTML.jpg

图 3-45。

Prototyping System for the BBC micro:bit (courtesy of kitronik.uk.co)

现在,让我们简单地讨论一下,在 IOT 之旅中,您应该为您的 MicroPython 购买哪种主板。

我应该买哪块板?

所以,你想买一个 MicroPython 板但是不知道选哪个。幸运的是,本章中的所有电路板都是理想的选择。可能使某些人比其他人更适合某些人的特征包括以下几点。

  • 无需组装–如果您不知道如何焊接或不想花时间加载软件、自举和固件,那么您应该考虑现成的主板,如 Pyboard 或 WiPy。
  • 连接性–如果您计划公开您的 IOT 解决方案(相对于学术练习),您应该考虑具有内置 WiFi 或类似网络功能(如 WiPy 或 Feather)的主板。
  • 现有硬件——如果您已经投资并拥有平台和设备(附加板、分线板等)。),你可能会考虑留在董事会。例如,如果你在 Arduino 上投资了很多,你应该考虑在 Arduino 板上加载 MicroPython。

显然,选择购买哪种主板需要事先考虑。话说回来,如果你是一个真正的发烧友,你可能会决定买几块这样的板, 17 自己试验一下哪一块最适合你的项目。不管怎样,我觉得最适合 IOT 学习 MicroPython 的是那些有网络的板,比如 WiPy 和 Feather Huzzah。然而,Pyboard 和 BBC micro:bit 板也是学习 MicroPython 的很好的板,但是需要一个外部模块来添加网络功能。

Consider Buying a Kit

许多主板都附带一个套件,其中包含各种附件,包括一个包含主板和电源适配器的入门套件。其他套件可能包括试验板,通常还有传感器或附加板。例如,您可以为 BBC micro:bit 板购买几个套件,包括 Sparkfun 的两个不错的选项。

其他供应商可能有针对其他主板的附加或类似套件。例如,Kitronik 为 BBC micro:bit 提供了一个优秀的发明家工具包(如下所示,由 kitronik.co.uk 提供),里面有许多你需要的部件。

A447395_1_En_3_Figa_HTML.jpg

如果您刚刚开始,没有任何主板或组件,入门套件可能是最经济的选择。

摘要

哇,信息量真大。如您所见,有几种 MicroPython 板可用。有些工具,比如 Pyboard 和 WiPy,可以直接使用,除了插入它并编写您的第一个 Python 程序之外,不需要任何东西。其他人可能要求您首先加载 MicroPython,其他人仍然需要一点魔法来使它们工作。然而,这里介绍的所有电路板都是本书实验中使用的绝佳选择。

在本章中,我们探索了一些使用 MicroPython 板的最佳实践和技巧。我们讨论了可能出错的常见问题,以及在哪里寻找您可能遇到的其他问题的解决方案。最后,我们看了几个流行的插件和分线板,您可以使用它们来开发您的项目,包括本书后面使用的那些。

在下一章,我们将深入学习使用 Python 和 MicroPython 的编程教程。这一章在很大程度上是一个闪电之旅,旨在帮助指导你能够写出(并理解)本书中的例子。

Footnotes 1

https://en.wikipedia.org/wiki/Firmware

2

PyMakr 插件取代了独立的 PyMakr 应用。更多信息见 https://forum.pycom.io/topic/635/pymakr-time-of-death-09-02

3

https://en.wikipedia.org/wiki/ESP8266

4

https://docs.pycom.io/pycom_esp32/_downloads/exp_v03_pinout_v13.pdf

5

https://en.wikipedia.org/wiki/Lithium_polymer_battery

6

版本号可能不会改变,但日期或内部版本号可能会经常改变。

7

https://en.wikipedia.org/wiki/USB#Device_Firmware_Upgrade

8

我觉得micropython.org提供网络板(皮肤)或类似的东西只是时间问题。检查他们的网站更新。

9

最新 Adafruit 可穿戴产品见 https://www.adafruit.com/category/65

10

https://github.com/Microsoft/uf2

11

https://learn.adafruit.com/adafruit-feather-m0-express-designed-for-circuit-python-circuitpython/circuitpython#flashing-with-bossac

12

的确是最普遍的芯片!

13

如果你喜欢冒险,有一个小小的 3。关于如何加载 MicroPython,请参见 https://github.com/micropython/micropython/wiki/Board-Teensy-3.1-3.5-3.6 ,但是要预先警告这不是一件简单的事情。

14

注意 MOSI 和 MISO 引脚,这是一个 SPI 接口。

15

注意 SDA 和 SLA 引脚,这是一个 I2C 接口。

16

只是一个名字。每个人都试图成为独一无二的,但我仍然称它们为盾牌——这是我修补 Arduino 时养成的习惯。

17

是的,我拥有这里列出的董事会(一些多重),以及一个不断增长的,不要脸的其他董事会。

四、如何在 MicroPython 中编程

现在我们已经对各种 MicroPython 板有了基本的了解,我们可以学习更多关于 MicroPython 编程的知识——这是一种非常健壮和强大的语言,可以用来编写非常强大的应用。掌握 MicroPython 非常容易,有些人可能认为使用它不需要任何正式的培训。这在很大程度上是正确的,因此您应该只需要一点点语言知识就能够编写 MicroPython 脚本。

鉴于 MicroPython 是 Python,我们可以先通过我们 PC 上的例子来学习 Python 语言的基础。因此,本章介绍了 Python 编程基础的速成课程,包括对一些最常用语言特性的解释。因此,本章将为您提供理解互联网上的 Python IOT 项目示例所需的技能。本章还通过可以在 PC 或 MicroPython 板上运行的例子演示了如何用 Python 编程。所以,让我们开始吧!

Note

在本章中,我使用术语 Python 来描述同时适用于 MicroPython 和 Python 的编程概念。MicroPython 特有的概念使用术语 MicroPython。

现在让我们学习一些 Python 编程的基本概念。我们将从语言的构件开始,比如变量、模块和基本语句,然后进入更复杂的流控制和数据结构的概念。虽然这些材料看起来很仓促,但是本 Python 教程只涵盖了该语言最基本的知识,以及如何在 PC 和 MicroPython 板上使用它。它旨在帮助您开始编写 Python IOT 应用。

如果你知道 Python 编程的基础,请随意浏览这一章。但是,我建议您完成本章末尾的示例项目,尤其是如果您没有编写过很多 Python 应用的话。

下面几节介绍了 Python 编程的许多基本特性,您需要了解这些特性才能理解本书中的示例项目。

基本概念

Python 是一种高级的、解释性的、面向对象的脚本语言。Python 的最大目标之一是拥有一个清晰、易于理解的语法,读起来尽可能接近英语。也就是说,即使你没有学过 Python 语言,你也应该能够阅读和理解 Python 脚本。Python 也比其他语言有更少的标点符号(特殊符号)和更少的语法机制。下面列出了 Python 的一些关键特性。

  • 解释器在运行时处理 Python。不使用外部(单独的)编译器。
  • Python 通过类的方式支持面向对象的编程结构。
  • 对于初级程序员来说,Python 是一种很好的语言,并且支持各种应用的开发。
  • Python 是一种脚本语言,但可用于广泛的应用。
  • Python 在全世界范围内都非常流行和使用,这给了它一个巨大的支持基础。
  • Python 关键字少,结构简单,语法定义清晰。这使得学生能够很快学会这门语言。
  • Python 代码定义更清晰,肉眼可见。

Python 可以在你可能遇到或使用的几乎所有平台上下载(python.org/downloads)——甚至是 Windows!Python 是一种非常容易学习的语言,它的结构非常少,甚至有点难学。与其抛出一个示例应用,不如让我们以类似 Python 的方式来学习 Python 的基础知识:一步一步来。

Note

如果你没有 MicroPython 板,也没有在你的 PC 上安装 Python,你应该现在就安装,这样你就可以运行本章中的例子。

代码块

你应该学习的第一件事是 Python 不像其他语言那样使用用符号划分的代码块。更具体地说,对于诸如函数、条件或循环之类的构造来说,局部的代码是使用缩进来指定的。因此,下面的行是缩进的(通过空格或制表符),以便起始字符与结构的代码体对齐。

Tip

下面展示了这个概念的实际应用。如果缩进不一致,Python 解释器会抱怨并产生奇怪的结果。

if (expr1):
    print("inside expr1")
    print("still inside expr1")
else:
    print("inside else")
    print("still inside else")
print("in outer level")

这里我们看到一个条件或 if 语句。注意函数调用print()是缩进的。这向解释器发出信号,表明这些行属于它上面的结构。例如,提到 expr1 的两个 print 语句构成了 if 条件的代码块(当表达式的计算结果为 true 时执行)。类似地,接下来的两个 print 语句构成了 else 条件的代码块。最后,非缩进的行不是条件行的一部分,因此在 if 或 else 之后执行,这取决于表达式的计算。

如您所见,缩进是编写 Python 时需要学习的一个关键概念。尽管这很简单,但是在缩进中出错可能会导致代码意外执行,或者解释器出现更糟糕的错误。

Note

在讨论 Python 时,我将“程序”和“应用”与“脚本”互换使用。虽然从技术上讲,保存在文件中的 Python 代码是一个脚本,但我们通常在“程序”或“应用”更合适的上下文中使用它。

有一个你会经常遇到的特殊符号。注意上面代码中冒号(:)的使用。这个符号用来终止一个构造,并向解释器发出信号,说明声明已经完成,代码块的主体跟在后面。我们把它用于条件、循环、类和函数。

评论

任何编程语言中最基本的概念之一是用不可执行的文本注释源代码的能力,这不仅允许您在代码行之间做笔记,还形成了一种记录源代码的方法。

要向源代码添加注释,请使用井号(#)。在行首放置至少一个符号,为该行创建注释,对后续的每一行重复使用#符号。这将创建所谓的块注释,如图所示。注意,我使用了不带任何文本的注释来创建空白。这有助于提高可读性,并且是块注释的常见做法。

#
# MicroPython for the IOT
#
# Example Python application.
#
# Created by Dr. Charles Bell
#

您也可以将注释放在源代码所在的同一行。编译器将忽略从井号到行尾的所有内容。例如,下面显示了记录变量的常见样式。

zip = 35012                # Zip or postal code
address1= "123 Main St."  # Store the street address

算术

您可以在 Python 中执行许多数学运算,包括常见的原语以及逻辑运算和用于比较值的运算。与其详细讨论这些,我在表 4-1 中提供了一个快速参考,显示了操作和如何使用操作的例子。

表 4-1。

Arithmetic, Logical, and Comparison Operators in 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` | |   | 和 | 逻辑与 | `var1and var2` | |   | 或者 | 逻辑或 | `var1or var2` | | 比较 | == | 平等的 | `expr1==expr2` | |   | != | 不相等 | `expr1!=expr2` | |   | < | 不到 | `expr1 | 大于 | `expr1>expr2` | |   | <= | 小于或等于 | `expr1<=expr2` | |   | >= | 大于或等于 | `expr1>=expr2` |

按位运算产生对每个位执行的值的结果。逻辑运算符(and、or)产生一个值,该值可为真或为假,通常与表达式或条件一起使用。

输出到屏幕

我们已经看到了一些如何将消息打印到屏幕上的例子,但是没有对所显示的语句进行任何解释。虽然不太可能为您部署的项目打印 MicroPython 板的输出,但是当您可以在屏幕上显示消息时,学习 Python 会容易得多。

正如我们在前面的例子中看到的,你可能想要打印的一些东西是为了传达你的程序内部正在发生的事情。这可以包括简单的消息(字符串),但也可以包括变量、表达式等的值。

正如我们所见,内置的print()函数是显示包含在单引号或双引号中的输出文本的最常见方式。我们还看到了一些使用另一个名为format()的函数的有趣例子。format()函数为每个传递的参数生成一个字符串。这些参数可以是其他字符串、表达式、变量等。该函数与一个特殊字符串一起使用,该字符串包含由花括号{ } (called string interpolation 1 )分隔的替换键。每个替换键包含一个索引(从 0 开始)或一个命名关键字。这个特殊字符串称为格式字符串。让我们看几个例子来说明这个概念。您可以在您的 PC 或 MicroPython 板上运行这些程序。我包括了输出,这样您可以看到每个语句做了什么。

>>> 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

请注意,我创建了三个变量(我们将在下一节讨论变量),用等号(=)给它们分配不同的值。然后,我使用带有四个替换键的格式字符串打印了一条消息,这四个替换键使用索引进行标记。请注意打印语句的输出。请注意,我在结尾处包含了一个表达式,以展示format()函数如何计算表达式。

最后一行更有趣。这里,我使用了三个命名参数(a_varb_varc_var),并在format()函数中使用了一个特殊的参数选项,在这里我给参数赋值。请注意,我以不同的顺序列出了它们。这是使用命名参数的最大优点;它们可以以任何顺序出现,但被放在格式字符串中指定的位置。

如您所见,这只是用 format()函数中的键替换{ }键的一个例子,format()函数将参数转换为字符串。我们在任何需要包含从多个区域或类型收集的数据的字符串的地方使用这种技术。我们可以在上面的例子中看到这一点。

Tip

有关格式字符串的更多信息,请参见 https://docs.python.org/3/library/string.html#formatstrings

现在让我们看看如何在我们的程序(脚本)中使用变量。

Tip

对于那些已经学会用另一种语言如 C 或 C++编程的人来说,Python 允许你用分号(;)终止一个语句;然而,包含它是不必要的,并且被认为是不好的形式。

变量

Python 是一种动态类型语言,这意味着变量的类型(它可以存储的数据类型)是由遇到或使用的上下文决定的。这与 C 和 C++等其他语言形成对比,在这些语言中,必须在使用变量之前声明类型。

Python 中的变量只是命名的内存位置,可以用来在执行过程中存储值。我们通过使用等号赋值来存储值。Python 变量名可以是您想要的任何名称,但是大多数 Python 开发人员都遵循一些规则和惯例。Python 编码标准中列出了这些规则。 2

然而,一般的、首要的规则要求变量名是描述性的、在上下文中有意义的并且容易阅读。也就是说,您应该避免使用带有随机字符、强制缩写、首字母缩略词以及类似的晦涩难懂的名称。按照惯例,变量名应该长于一个字符(除了一些可接受的循环计数变量),并且足够短以避免过长的代码行。

What is a Long Code Line?

大多数人会说一个代码行不应超过 80 个字符,但这是从编程的黑暗时代听来的,那时我们使用穿孔卡,每张卡最多允许 80 个字符,后来的显示设备也有同样的限制。对于现代的宽屏显示器来说,这没什么大不了的,但我仍然建议保持短行以确保更好的可读性。没有人喜欢向下(或向右)滚动阅读!

因此,给变量命名有很大的灵活性。在 PEP8 标准中有额外的规则和指南,如果您希望使您的项目源代码与这些标准保持一致,您应该查看 PEP8 函数、类等的命名标准。有关规则和标准的完整列表,请参见 https://www.python.org/dev/peps/pep-0008 的 pep 8 Python 编码指南。

下面显示了一些简单变量及其动态确定类型的示例。

# 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"}

那么,我们怎么知道变量width是一个整数呢?仅仅因为数字 4 是一个整数。同样,Python 将“工具”解释为字符串。我们将在下一节看到更多关于最后三种类型和 Python 支持的其他类型的内容。

Tip

有关 Python 编码标准(PEP8)管理的命名约定的更多信息,请参见 https://www.python.org/dev/peps/pep-0008/#naming-conventions

类型

如前所述,Python 不像其他语言那样有正式的类型规范机制。但是,您仍然可以定义变量来存储您想要的任何内容。事实上,Python 允许您基于上下文创建和使用变量,并且您可以使用初始化来“设置”变量的数据类型。下面给出了几个例子。

# 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)

对于需要转换类型或希望确保值以某种方式键入的情况,有许多用于转换数据的函数。表 4-2 显示了一些更常用的类型转换函数。我将在后面的章节中讨论一些数据结构。

表 4-2。

Type Conversion in 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 的知识足以编写最基本的程序,而且对于处理本章后面的示例项目来说也绰绰有余。然而,当您开始需要对数据进行操作时——无论是来自用户还是来自传感器和类似来源——您将需要一种方法来组织和存储数据,以及对内存中的数据执行操作。下面按复杂程度介绍三种数据结构:列表、元组和字典。

列表

列表是 Python 中组织数据的一种方式。这是一种构建集合的自由形式的方法。也就是说,项目(或元素)不必是相同的数据类型。列表还允许你做一些有趣的操作,比如在末尾、开头或特殊索引处添加内容。下面演示了如何创建列表。

# 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))

这里我们看到我使用方括号([])创建了列表。列表定义中的项目用逗号分隔。注意,您可以简单地通过设置一个等于[]的变量来创建一个空列表。因为列表和其他数据结构一样,都是对象,所以有几种操作可用于列表,如下所示。

  • append(x):在列表末尾添加 x
  • extend(l):将所有项目添加到列表末尾
  • insert(pos,item):在 pos 位置插入项目
  • remove(value):删除第一个匹配(==)值的项目
  • pop([i]):移除并返回位置 I 或列表末尾的项目
  • index(value):返回第一个匹配项的索引
  • count(value):统计值的出现次数
  • sort():列表排序(升序)
  • reverse():反向排序列表

列表就像其他语言中的数组一样,对于构建动态数据集合非常有用。

元组

另一方面,元组是一种限制性更强的集合类型。也就是说,它们是由一组特定的数据构建的,不允许像列表一样进行操作。事实上,您不能更改元组中的元素。因此,我们可以对不应该改变的数据使用元组。下面显示了一个元组的示例以及如何使用它。

# 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")

这里我们看到我使用括号()创建了元组。元组定义中的各项用逗号分隔。注意,只需将变量设置为()就可以创建一个空元组。因为元组像其他数据结构一样是对象,所以有如下几种可用的操作,包括对诸如包含、定位等序列的操作。

  • x in t:确定 t 是否包含 x
  • x not in t:确定 t 是否不包含 x
  • s + t:连接元组
  • s[i]:获取元素 I
  • len(t):t 的长度(元素数)
  • min(t):最小(最小值)
  • max(t):最大值(最大值)

如果你想在内存中存储更多的数据,你可以使用一个叫做字典的特殊结构(对象)。

字典

字典是一种数据结构,允许您存储键、值对,通过键来评估数据。字典是一种非常结构化的数据处理方式,也是我们在收集复杂数据时想要使用的最符合逻辑的形式。下面是一个字典的例子。

# 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]

这里发生了很多事情!我们看到一个使用花括号创建字典的基本字典声明。在里面,我们可以创建尽可能多的键、值对,用逗号分隔。使用字符串(我习惯使用单引号,但双引号也可以)或整数定义键,值可以是我们想要的任何数据类型。对于 my_ip 属性,我们还存储了一个元组。

按照字典,我们看到在字典上执行的几个操作,包括打印键、打印所有值和只打印值。下面显示了从 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)

正如我们在这个例子中看到的,有几个操作(函数或方法)可用于字典,包括如下。这些操作使得字典成为一个非常强大的编程工具。

  • 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 / else 语句来包含多个条件,其中执行的代码取决于几个条件的评估。下面显示了 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

虽然您可以尽可能多地链接语句,但在这里要小心,因为您拥有的elif部分越多,就越难理解、维护和避免表达式中的逻辑错误。

还有另一种形式的条件语句,称为三元运算符。在 Python 中,三元运算符通常被称为条件表达式。这些操作符基于条件的真或假来评估某些东西。在 2.4 版本中,它们成为 Python 的一部分。条件表达式是在赋值语句中使用的 if-then-else 结构的简写符号,如下所示。

variable = value_if_true if condition else value_if_false

这里我们看到如果条件被评估为真,则使用 if 前面的值,但是如果条件被评估为假,则使用 else 后面的值。下面是一个简短的例子。

>>> 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.
>>>

条件表达式允许您快速测试条件,而不是使用多行条件语句,这有助于使您的代码更容易阅读(也更短)。

循环用于控制代码块的重复执行。有三种形式的循环,它们的行为略有不同。所有循环都使用条件语句来决定是否重复执行。也就是说,只要条件为真,它们就会重复。两种类型的循环是 while 和 for。我用一个例子来解释每一个。

while 循环的条件位于代码块的“顶部”或开始处。因此,while 循环仅当且仅当条件在第一次通过时评估为 true 时才执行主体。下面说明了 while 循环的语法。只有当某些表达式的计算结果为 true 时,才需要执行代码,此时最好使用这种形式的循环。例如,遍历一个元素个数未知的事物集合(循环,直到集合中的事物用完)。

while (expression):
   # do something here

For 循环由于其独特的形式,有时也被称为计数循环。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,但它不是终止和控制循环的首选方法。也就是说,专业人士认为条件表达式或错误处理代码应该表现得足够好,不需要这些选项。

模块化;模块、函数和类

最后几组主题是最高级的,包括模块化(代码组织)。正如我们将看到的,我们可以使用函数对代码进行分组,消除重复,并将功能封装到对象中。

包括模块

Python 应用可以从 Python 环境提供的可重用库构建。它们也可以从您自己创建或从第三方下载的自定义模块或库构建。这些文件通常作为一组 Python 代码文件分发(例如,文件扩展名为.py的文件)。当我们想使用一个库(函数、类等)时。)包含在一个模块中,我们使用import关键字并列出模块的名称。下面是一些例子。

import os
import sys

前两行演示了如何导入 Python 提供的基本或公共模块。在这种情况下,我们为 os 和 sys 模块(操作系统和 Python 系统函数)使用或导入模块。

Tip

习惯上(但不要求)按字母顺序列出您的导入,首先是内置模块,然后是第三方模块,最后是您自己的模块。

功能

Python 允许在代码中使用模块化。虽然它通过类的方式支持面向对象编程(对于大多数 Python GPIO 示例来说,这是一个不太可能遇到的更高级的特性),但在更基本的层面上,您可以使用函数将代码分成更小的块。

函数使用特殊的关键字构造(Python 中很少见)来定义函数。我们简单地使用def,后跟一个函数名和一个用逗号分隔的参数列表(在括号中)。冒号用于终止声明。下面显示了一个示例。

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,
}

您可能想知道这个奇怪的代码是做什么的。注意,该循环从items()函数的结果中分配了两个值。这是 dictionary 对象提供的一个特殊函数。3items()函数返回键、值对:因此得名变量。

下一行打印出这些值。对于 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'])

这个示例和上面的代码一起执行时,会生成以下内容。

$ 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

现在让我们看看 Python 中最复杂的概念——面向对象编程。

类和对象

你可能听说过 Python 是一种面向对象的编程语言。但这意味着什么呢?简单地说,Python 是一种编程语言,它提供了描述对象(事物)以及可以用对象做什么(操作)的工具。对象是数据抽象的一种高级形式,其中数据对调用者是隐藏的,只能由对象提供的操作(方法)来操作。

我们在 Python 中使用的语法是class语句,您可以使用它来帮助您的项目模块化。所谓模块化,我们的意思是源代码被安排得更容易开发和维护。通常,我们将类放在单独的模块(代码文件)中,这有助于更好地组织代码。虽然这不是必需的,但我建议使用这种将类放在它自己的源文件中的技术。这使得修改类或修复问题(错误)更加容易。

那么,什么是 Python 类呢?让我们从将构造视为一种组织技术开始。我们可以使用类将数据和方法分组在一起。类名紧跟在关键字 class 后面,后面跟一个冒号。像其他方法一样声明其他类方法,除了第一个参数必须是self,它在执行时将方法绑定到类实例。

Note

我更喜欢使用语言设计者或开发人员社区已经采用的术语。例如,有些使用“函数”,但有些可能使用“方法”。还有一些可能使用子程序、例行程序、过程等。你使用哪个术语并不重要,但是你应该努力使用一致的术语。一个可能让一些人感到困惑的例子是,我在讨论面向对象的例子时使用了术语方法。也就是说,一个类有方法而没有函数。然而,你可以用函数代替方法,你仍然是正确的(大多数情况下)。

通过使用类(创建实例)和使用点标记来引用数据成员或函数,使用一种或多种方法来访问数据。让我们看一个例子。清单 4-1 展示了一个完整的类,它描述(模拟)了用于运输的车辆的最基本特征。我创建了一个名为vehicle.py的文件来包含这段代码。

#
# MicroPython for the IOT
#
# 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 4-1.
Vehicle class

注意这里的一些事情。首先,有一个名为 init()的方法。这是构造函数,在创建类实例时调用。您将所有初始化代码像设置变量一样放在这个方法中。我们也有返回轴、门和居住者数量的方法。我们在这个类中有一个方法:添加居住者。

还要注意,我们使用self.<name>来处理每个类属性(数据)。这就是我们如何确保我们总是访问与创建的实例相关联的数据,而不是全局变量或其他局部变量。

让我们看看这个类如何被用来定义一个家庭轿车。清单 4-2 展示了使用这个类的代码。我们可以将这段代码放在一个名为sedan.py的文件中。

#
# MicroPython for the IOT
#
# 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 4-2.Using the Vehicle class

注意,第一行从 vehicle 模块导入了 Vehicle 类。注意,我大写了类名,而不是文件名。这是一种非常常见的命名方案。接下来,在代码中,我们创建类的一个实例。注意我把 2,4 传递给了类名。这将导致在实例化类时调用__init__()方法。变量sedan变成了我们可以操作的类实例变量(对象),我通过添加三个居住者然后使用Vehicle类中的方法打印出居住者的数量来实现。

我们可以使用下面的命令在 PC 上运行代码。正如我们所看到的,当代码运行时,它告诉我们车上有三个人。不错。

$ python ./sedan.py
The car has 3 occupants.

Object-Oriented Programming (OOP) Terminology

像任何技术或概念一样,有一定数量的术语,您必须学会这些术语,才能理解技术并与他人交流。下面简要描述了一些您需要了解的术语,以便更好地了解面向对象编程。

属性:类中的数据元素。

类:用于以属性(数据)和对数据进行操作的方法(函数)的形式定义对象的代码构造。Python 中的方法和属性可以使用点符号来访问。

类实例变量:用于存储对象实例的变量。它们像其他变量一样使用,与点符号结合,允许我们操作对象。

实例:类的可执行形式,通过将类赋给变量来创建,将代码初始化为对象。

继承:将一个类的属性和方法包含在另一个类中。

实例化:创建一个类的实例。

方法重载:创建两个或多个同名但参数不同的方法。这允许我们创建具有相同名称的方法,但是根据所传递的参数,操作可能不同。

多态性:从基类继承属性和方法,添加额外的方法或覆盖(改变)方法。

还有很多 OOP 术语,但这些是你最常遇到的。

现在,让我们看看如何使用 vehicle 类来演示继承。在这种情况下,我们将创建一个名为PickupTruck的新类,它使用了 vehicle 类,但是向结果类添加了专门化。清单 4-3 显示了新的类。我将这段代码放在一个名为pickup_truck.py的文件中。如你所见,皮卡是一种交通工具。

#
# MicroPython for the IOT
#
# 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 4-3.
Pickup Truck class

注意这里的一些事情。首先注意类语句:class PickupTruck(Vehicle) :。当我们想从另一个类继承时,我们用基类的名字加上括号。这确保 Python 将使用基类,允许派生类使用其所有可访问的数据和内存。如果你想从一个以上的类继承,你可以(称为多重继承),只要用逗号分隔的列表列出基(父)类。

接下来,注意__max_occupants变量。按照惯例,在一个类中为一个属性或方法使用两个下划线会使该项成为该类的私有项。 4 也就是说,它应该只能从类内部访问。该类的调用方(通过类变量/实例)不能访问私有项,从该类派生的任何类也不能访问私有项。隐藏属性(数据)总是一个好的做法。

您可能想知道 occupant 方法发生了什么变化。他们为什么不在新班级?它们不在那里,因为我们的新类继承了基类的所有行为。不仅如此,还修改了代码,将乘员限制为三人。

我还想指出我添加到该类中的文档。我们使用文档字符串(前后使用三个双引号的字符串)来记录类。您可以将文档放在这里解释该类及其方法。稍后我们会看到它的一个很好的用途。

最后,请注意构造函数中的代码。这演示了如何调用基类方法,我这样做是为了设置轴和门的数量。如果我们想调用基类方法的版本,我们可以在其他方法中做同样的事情。

现在,让我们写一些代码来使用这个类。清单 4-4 显示了我们用来测试这个类的代码。在这里,我们创建了一个名为 pickup.py 的文件,该文件创建了一个皮卡实例,添加了乘员和有效载荷,然后打印出卡车的内容。

#
# MicroPython for the IOT
#
# 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 4-4.Using 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.

我再次在我的 PC 上运行这段代码,但是我可以在 MicroPython 板上运行所有这些代码,并且会看到相同的结果。

关于类,我们还应该了解一件事:内置属性。回忆一下__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

现在,让我们看几个可以用来练习的 Python 程序的例子。与前面的例子一样,您可以在 PC 或 MicroPython 板上编写和执行这些代码。

通过示例学习 Python

学习如何用任何语言编程的最好方法是用例子练习。在这一节中,我将展示几个例子,您可以用它们来练习 Python 编码。您可以使用 MicroPython 板或 PC 来运行这些示例。我通过 Python 控制台在我的 PC 上展示了前两个例子,通过 REPL 控制台使用 MicroPython 板展示了后两个例子。

我详细解释了每个示例的代码,并展示了执行代码时的示例输出,以及让您自己尝试对每个示例进行一两次修改的挑战。我鼓励你实现这些例子,并在本书后面的项目实践中自己找出挑战。

示例 1:使用循环

这个例子演示了如何使用 for 循环在 Python 中编写循环。我们试图解决的问题是将整数从十进制转换成二进制、十六进制和八进制。通常在 IOT 项目中,我们需要看到一种或多种格式的值,在某些情况下,我们使用的传感器(和相关文档)使用十六进制而不是十进制。因此,这个例子不仅对如何使用 for 循环,而且对如何将整数转换成不同的格式都有帮助。

写代码

该示例从要转换的整数元组开始。可以使用 for 循环遍历元组和列表(按顺序读取值)。回想一下,一个元组是只读的,所以在这种情况下,因为它是输入的,所以它是好的,但在其他情况下,您可能需要更改值,您将需要使用列表。回想一下,元组和列表之间的语法差异是元组使用括号,而列表使用方括号。

这里演示的 for 循环称为“for each”循环。注意,我使用了语法“for value in values,”,它告诉 Python 遍历名为values的元组,每次遍历元组时,将每个条目提取(存储)到value变量中。

最后,我使用print()format()函数替换两个占位符{0}{1},使用方法bin()为二进制、oct()为八进制、hex()为十六进制打印出不同格式的整数。清单 4-5 展示了将整数转换成不同形式的例子。

#
# MicroPython for the IOT
#
# Example: Convert integer to binary, hex, and octal
#
# Dr. Charles Bell
#

# Create a tuple of integer values
values = (12, 450, 1, 89, 2017, 90125)

# Loop through the values and convert each to binary, hex, and octal
for value in values:
    print("{0} in binary is {1}".format(value, bin(value)))
    print("{0} in octal is {1}".format(value, oct(value)))
    print("{0} in hexadecimal is {1}".format(value, hex(value)))

Listing 4-5.Converting Integers

执行代码

您可以将这段代码保存在 PC 上一个名为conversions.py的文件中,然后打开一个终端(控制台窗口)并使用命令python ./conversions.py运行这段代码(如果您安装了多个版本的 python,则使用 python3)。清单 4-6 显示了输出。

$ python3 ./conversions.py
12 in binary is 0b1100
12 in octal is 0o14
12 in hexadecimal is 0xc
450 in binary is 0b111000010
450 in octal is 0o702
450 in hexadecimal is 0x1c2
1 in binary is 0b1
1 in octal is 0o1
1 in hexadecimal is 0x1
89 in binary is 0b1011001
89 in octal is 0o131
89 in hexadecimal is 0x59
2017 in binary is 0b11111100001
2017 in octal is 0o3741
2017 in hexadecimal is 0x7e1
90125 in binary is 0b10110000000001101
90125 in octal is 0o260015
90125 in hexadecimal is 0x1600d
Listing 4-6.Conversions Example Output

注意元组中所有被转换的值。

你的挑战

为了使这个示例更好,不使用静态元组来包含硬编码的整数,而是重写这个示例,以便从命令行上的参数中读取整数以及格式。例如,代码将按如下方式执行。

$ python3 ./conversions.py 123 hex
123 in hexadecimal is 0x7b

要从命令行读取参数,请使用参数解析器,argparse ( https://docs.python.org/3/howto/argparse.html )。如果你想从命令行读取整数,你可以使用argparse模块来添加一个参数,如下所示。

import argparse

# Setup the argument parser
parser = argparse.ArgumentParser()

# We need two arguments: integer, and conversion
parser.add_argument("original_val")
parser.add_argument("conversion")

# Get the arguments
args = parser.parse_args()

当您使用参数解析器(argparse)模块时,参数的值都是字符串,因此您需要在使用bin()hex()oct()方法之前将值转换为整数。

您还需要确定所请求的转换。我建议只使用十六进制、二进制和十进制进行转换,并使用一组条件来检查所请求的转换。类似于下面的内容会起作用。

if args.conversion == 'bin':
    # do conversion to binary
elif args.conversion == 'oct':
    # do conversion to octal
elif args.conversion == 'hex':
    # do conversion to hexadecimal
else:
    print("Sorry, I don't understand, {0}.".format(args.conversion))

请注意,最后一个 else 通知参数未被识别。这有助于管理用户错误。

关于参数解析器,还有一点你应该知道。添加参数时,可以传入一个帮助字符串。参数解析器还免费为您提供帮助参数(-h)。请注意以下事项。注意,我使用 help=参数添加了几个字符串。

# We need two arguments: integer, and conversion
parser.add_argument("original_val", help="Value to convert.")
parser.add_argument("conversion", help="Conversion options: hex, bin, or oct.")

现在,当我们完成代码并使用-h选项运行它时,我们会得到下面的输出。酷吧。

$ python3 ./conversions.py -h
usage: conversions.py [-h] original_val conversion

positional arguments:
  original_val  Value to convert.
  conversion    Conversion options: hex, bin, or oct.

optional arguments:
  -h, --help    show this help message

  and exit

示例 2:使用复杂的数据和文件

这个例子演示了如何在 Python 中使用 JavaScript 对象符号 5 (JSON)。简而言之,JSON 是一种用于交换数据的标记语言。它不仅可读,还可以直接在应用中使用,在其他应用、服务器甚至 MySQL 之间存储和检索数据。事实上,程序员对 JSON 很熟悉,因为它类似于其他标记方案。JSON 也非常简单,因为它只支持两种类型的结构:1)包含(名称,值)对的集合,2)有序列表(或数组)。当然,您也可以混合和匹配一个对象中的结构。当我们创建一个 JSON 对象时,我们称之为 JSON 文档。

我们试图解决的问题是向/从文件中写入和读取数据。在这种情况下,我们将使用一个名为json的特殊 JSON 编码器和解码器模块,它允许我们轻松地将文件(或其他流)中的数据与 JSON 相互转换。正如您将看到的,访问 JSON 数据很容易,只需使用键名(有时称为字段)来访问数据。因此,这个例子不仅对将来如何使用读写文件有帮助,而且对如何使用 JSON 文档也有帮助。

写代码

此示例存储和检索文件中的数据。这些数据是关于宠物的基本信息,包括名字、年龄、品种和类型。该类型用于确定广泛的类别,如鱼、狗或猫。

我们从导入 JSON 模块(名为json)开始,该模块内置于 MicroPython 平台中。接下来,我们通过构建 JSON 文档并将其存储在 Python 列表中来准备一些初始数据。我们使用json.loads()方法传入一个 JSON 格式的字符串。结果是一个 JSON 文档,我们可以将它添加到我们的列表中。这些例子使用了一种非常简单的 JSON 文档——一组(名称,值)对。下面显示了一个使用的 JSON 格式字符串的例子。

{"name":"Violet", "age": 6, "breed":"dachshund", "type":"dog"}

注意,我们用花括号将字符串括起来,并使用一系列键名、一个冒号和一个用逗号分隔的值。如果这看起来很熟悉,那是因为与 Python 字典的格式相同。这证明了我的评论,即程序员对 JSON 语法很熟悉。

JSON 方法json.loads()获取 JSON 格式的字符串,然后分析字符串的有效性并返回一个 JSON 文档。然后,我们将该文档存储在一个变量中,并将其添加到列表中,如下所示。

parsed_json = json.loads('{"name":"Violet", "age": 6, "breed":"dachshund", "type":"dog"}')
pets.append(parsed_json)

一旦数据被添加到列表中,我们就把数据写到一个名为my_data.json的文件中。为了处理文件,我们首先用open()函数打开文件,它接受一个文件名(如果您想把文件放在一个目录中,还包括一个路径)和一个访问模式。我们用“r”读,用“w”写。如果你想打开一个文件并添加到末尾,你也可以使用“a”进行追加。请注意,当您写入文件时,“w”访问将会覆盖该文件。如果open()函数成功,您将获得一个 file 对象,它允许您调用额外的函数来读取或写入数据。如果文件不存在(并且您已经请求了读取权限)或者您没有权限写入文件,那么open()将会失败。

如果你想知道还有哪些访问模式,表 4-3 显示了open()功能可用的模式列表。

表 4-3。

Python File Access Modes

| 方式 | 描述 | | --- | --- | | `r` | 以只读方式打开文件。文件指针放在文件的开头。这是默认模式。 | | `rb` | 以二进制格式打开只读文件。文件指针放在文件的开头。这是默认模式。 | | `r+` | 打开文件进行读写。文件指针放在文件的开头。 | | `rb+` | 以二进制格式打开文件进行读写。文件指针放在文件的开头。 | | `w` | 打开一个只写的文件。如果文件存在,则覆盖文件。如果该文件不存在,它将创建一个新文件进行写入。 | | `wb` | 以二进制格式打开一个只写的文件。如果文件存在,则覆盖文件。如果该文件不存在,它将创建一个新文件进行写入。 | | `w+` | 打开文件进行读写。如果文件存在,则覆盖现有文件。如果文件不存在,它会创建一个新文件进行读写。 | | `wb+` | 以二进制格式打开文件进行读写。如果文件存在,则覆盖现有文件。如果文件不存在,它会创建一个新文件进行读写。 | | `a` | 打开要追加的文件。如果文件存在,文件指针在文件的末尾。也就是说,文件处于追加模式。如果该文件不存在,它将创建一个新文件进行写入。 | | `ab` | 以二进制格式打开要追加的文件。如果文件存在,文件指针在文件的末尾。也就是说,文件处于追加模式。如果该文件不存在,它将创建一个新文件进行写入。 | | `a+` | 打开文件进行追加和读取。如果文件存在,文件指针在文件的末尾。文件以追加模式打开。如果文件不存在,它会创建一个新文件进行读写。 | | `ab+` | 以二进制格式打开文件进行追加和读取。如果文件存在,文件指针在文件的末尾。文件以追加模式打开。如果文件不存在,它会创建一个新文件进行读写。 |

文件打开后,我们可以通过遍历列表将 JSON 文档写入文件。迭代意味着从第一个元素开始,按顺序(它们在列表中出现的顺序)一次访问列表中的一个元素。回想一下,Python 中的迭代非常容易。我们简单地说,“对于列表中的每一项”,for 循环如下。

for pet in pets:
  // do something with the pet data

为了将 JSON 文档写入文件,我们使用了json.dumps()方法,这将产生一个 JSON 格式的字符串,使用 file 变量和write()方法将该字符串写入文件。因此,我们现在看到如何从字符串构建 JSON 文档,然后将它们解码(转储)成一个字符串。

一旦我们将数据写入文件,我们就用close()函数关闭文件,然后重新打开它并从文件中读取数据。在这种情况下,我们使用 for 循环的另一种特殊实现。我们使用 file 变量通过readlines()方法读取文件中的所有行,然后用下面的代码遍历它们。

json_file = open("my_data.json", "r")
for pet in json_file.readlines():
  // do something with the pet string

我们再次使用json.loads()方法读取从文件中读取的 JSON 格式的字符串,将其转换为 JSON 文档,并将其添加到另一个列表中。然后我们关闭文件。现在数据已经读回到我们的程序中,我们可以使用它了。最后,我们遍历新列表,并使用键名从 JSON 文档中打印出数据,以检索我们想要的数据。清单 4-7 显示了这个例子的完整代码。

#
# MicroPython for the IOT
#
# Example: Storing and retrieving JSON objects in files
#
# Dr. Charles Bell
#

import json

# Prepare a list of JSON documents for pets by converting JSON to a dictionary
pets = []
parsed_json = json.loads('{"name":"Violet", "age": 6, "breed":"dachshund", "type":"dog"}')
pets.append(parsed_json)
parsed_json = json.loads('{"name": "JonJon", "age": 15, "breed":"poodle", "type":"dog"}')
pets.append(parsed_json)
parsed_json = json.loads('{"name": "Mister", "age": 4, "breed":"siberian khatru", "type":"cat"}')
pets.append(parsed_json)
parsed_json = json.loads('{"name": "Spot", "age": 7, "breed":"koi", "type":"fish"}')
pets.append(parsed_json)
parsed_json = json.loads('{"name": "Charlie", "age": 6, "breed":"dachshund", "type":"dog"}')
pets.append(parsed_json)

# Now, write these entries to a file. Note: overwrites the file
json_file = open("my_data.json", "w")
for pet in pets:
    json_file.write(json.dumps(pet))
    json_file.write("\n")
json_file.close()

# Now, let's read the JSON documents

then print the name and age for all of the dogs in the list
my_pets = []
json_file = open("my_data.json", "r")
for pet in json_file.readlines():
    parsed_json = json.loads(pet)
    my_pets.append(parsed_json)
json_file.close()

print("Name, Age")
for pet in my_pets:
    if pet['type'] == 'dog':
        print("{0}, {1}".format(pet['name'], pet['age']))

Listing 4-7.Writing and Reading JSON Objects to/from Files

注意写数据的循环。我们添加了第二个write()方法,传入一个奇怪的字符串(它实际上是一个转义字符)。\n是一个叫做换行符的特殊字符。这迫使 JSON 格式的字符串在文件中位于单独的行上,有助于提高可读性。

Tip

要更深入地了解如何在 Python 中处理文件,请参见 https://docs.python.org/3/tutorial/inputoutput.html#reading-and-writing-files

那么,文件看起来像什么?下面是使用 more 实用程序的文件转储,它显示了文件的内容。注意,该文件包含 JSON 格式的字符串,就像我们在代码中一样。

$ more my_data.json
{"age": 6, "breed": "dachshund", "type": "dog", "name": "Violet"}
{"age": 15, "breed": "poodle", "type": "dog", "name": "JonJon"}
{"age": 4, "breed": "siberian khatru", "type": "cat", "name": "Mister"}
{"age": 7, "breed": "koi", "type": "fish", "name": "Spot"}
{"age": 6, "breed": "dachshund", "type": "dog", "name": "Charlie"}

现在,让我们看看运行这个脚本会发生什么。

执行代码

您可以将此代码保存在 PC 上名为rw_json.py的文件中,然后打开一个终端(控制台窗口)并使用命令python ./rw_json.py运行代码(如果您安装了多个版本的 python,则使用 python3)。下面显示了输出。

$ python ./rw_json.py
Name, Age
Violet, 6
JonJon, 15
Charlie, 6

虽然输出可能不是很令人印象深刻,但是通过完成这个示例,您已经学到了很多关于使用 JSON 文档处理文件和结构化数据的知识。

你的挑战

为了让这个例子更有挑战性,您可以修改它,以包含更多关于您的宠物的信息。我建议你从一个简单的文本文件开始,为你的宠物输入 JSON 格式的字符串。要增加复杂性,请尝试添加与宠物类型相关的信息。例如,您可以为一个或多个宠物添加一些关键点,为其他宠物添加其他关键点,等等。这样做将显示 JSON 文档的一个强大之处;JSON 文档的集合不必具有相同的格式。

有了这个文件后,修改代码,从文件中读取并打印出每只宠物的所有信息,方法是打印键名和值。提示:您将需要使用特殊的代码来打印出键名和值,称为“漂亮打印”例如,下面的代码将以易读的格式打印出 JSON 文档。注意,我们使用sort_keys选项来打印键(字段),我们可以控制缩进的空格数。

for pet in my_pets:
    print(json.dumps(pet, sort_keys=True, indent=4))

运行时,输出将如下所示。

{
    "age": 6,
    "breed": "dachshund",
    "name": "Violet",
    "type": "dog"
}
{
    "age": 15,
    "breed": "poodle",
    "name": "JonJon",
    "type": "dog"
}

示例 3:使用函数

这个例子演示了如何创建和使用函数。回调函数用于帮助我们的代码更加模块化。函数也是避免代码重复的重要工具。也就是说,我们可以通过将部分代码放在函数中来重复使用它们。函数也用于帮助隔离特殊操作的代码,例如数学公式。

我们在这个例子中探索的问题是如何创建函数来执行计算。我们还将探索一种常见的计算机科学技术,称为递归 6 ,在这种技术中,函数会重复调用自身。我还将向您展示以迭代方式实现的相同功能(通常使用循环)。虽然有些人建议避免递归,但是递归函数写起来更短,但是如果出错的话,调试起来会更困难。我能提供的最好的建议是,几乎每个递归函数都可以写成迭代函数,新手程序员应该坚持迭代解决方案,直到他们对使用函数有了信心。

写代码

这个例子是用来计算斐波那契数列的。 7 斐波纳契数列是以数列中前两个值的和来计算的。该序列从 1 开始,后跟 1(无加 1),然后是 1 + 1 = 2,依此类推。对于这个例子,我们将要求用户输入一个整数,然后计算斐波那契数列中值的个数。如果输入为 5,则序列为 1、1、2、3、5。

我们将创建两个函数:一个使用迭代计算数列的代码计算斐波那契数列,另一个使用递归函数计算第 n 个斐波那契数列。我们先来看迭代函数。

为了定义一个函数,我们使用语法def func_name(<parameters>):,其中我们提供一个函数名和一个零个或多个参数的列表,后跟一个冒号。这些参数可以在函数内部使用。我们使用参数将数据传递给函数。下面显示了斐波那契数列代码的迭代版本。我们把这个函数命名为fibonacci_iterative

def fibonacci_iterative(count):
    i = 1
    if count == 0:
        fib = []
    elif count == 1:
        fib = [1]
    elif count == 2:
        fib = [1,1]
    elif count > 2:
        fib = [1,1]
        while i < (count - 1):
            fib.append(fib[i] + fib[i-1])
            i += 1
    return fib

这段代码简单地计算序列中的前 N 个值,并在一个列表中返回它们。参数 count 是序列中值的数量。该函数首先检查是否请求了小值:值已知的 0、1 或 2。如果计数值大于 2,我们从已知的数列[1,1]开始,然后使用一个循环,通过将前两个值相加来计算下一个值。花点时间注意我是如何使用列表索引来获取列表中的前两个值的(ii-1)。我们将使用这个函数和代码中直接返回的列表来查找序列中的特定值并打印出来。

现在让我们看看函数的递归版本。下面显示了代码。我们将这个函数命名为fibonacci_recursive

def fibonacci_recursive(number):
    if number == 0:
        return 0
    elif number == 1:
        return 1
    else:
        # Call our self counting down.
        value = fibonacci_recursive(number-1) + fibonacci_recursive(number-2)
        return value

在这种情况下,我们不返回整个系列;相反,我们返回序列中的特定值——第 n 个值。与迭代示例一样,我们对返回所请求数字的平凡值做同样的事情。否则,我们对每个数字再次调用相同的函数。你可能需要一些时间来理解它是如何工作的,但是它确实计算了第 n 个值。

现在,您可能想知道将函数放在代码中的什么位置。我们需要将它们放在代码的顶部。Python 将解析函数,并继续执行定义之后的语句。因此,我们将“主”代码放在函数之后。

本例的主要代码从请求斐波那契数列的第 n 个值开始,然后首先使用递归函数来计算该值。然后,我们询问用户是否想要查看整个系列,如果是,我们使用函数的迭代版本来获取列表并将其打印出来。我们打印出第 n 个值,并再次给出查看整个系列的选项,以显示使用两个函数的结果是相同的。清单 4-8 展示了这个例子的完整代码。我们将这个代码命名为fibonacci.py

#
# MicroPython for the IOT
#
# Example: Fibonacci series using recursion
#
# Calculate the Fibonacci series based on user input
#
# Dr. Charles Bell
#

# Create a function to calculate Fibonacci series (iterative)
# Returns a list.
def fibonacci_iterative(count):
    i = 1
    if count == 0:
        fib = []
    elif count == 1:
        fib = [1]
    elif count == 2:
        fib = [1,1]
    elif count > 2:
        fib = [1,1]
        while i < (count - 1):
            fib.append(fib[i] + fib[i-1])
            i += 1
    return fib

# Create a function to calculate the nth Fibonacci number

(recursive)
# Returns an integer.
def fibonacci_recursive(number):
    if number == 0:
        return 0
    elif number == 1:
        return 1
    else:
        # Call our self counting down.
        value = fibonacci_recursive(number-1) + fibonacci_recursive(number-2)
        return value

# Main code
print("Welcome to my Fibonacci calculator!")
index = int(input("Please enter the number of integers in the series: "))

# Recursive example
print("We calculate the value using a recursive algoritm.")
nth_fibonacci = fibonacci_recursive(index)
print("The {0}{1} fibonacci number is {2}."
      "".format(index, "th" if index > 1 else "st", nth_fibonacci))
see_series = str(input("Do you want to see all of the values in the series? "))
if see_series in ["Y","y"]:
    series = []
    for i in range(1,index+1):
        series.append(fibonacci_recursive(i))
    print("Series: {0}: ".format(series))

# Iterative example
print("We calculate the value using an iterative algoritm.")
series = fibonacci_iterative(index)
print("The {0}{1} fibonacci number is {2}."
      "".format(index, "th" if index > 1 else "st", series[index-1]))
see_series = str(input("Do you want to see all of the values in the series? "))
if see_series in ["Y","y"]:
    print("Series: {0}: ".format(series))

print("bye!")

Listing 4-8.Calculating Fibonacci Series

花一些时间通读代码。虽然要解决的问题比前一个例子简单一点,但是需要阅读更多的代码。准备就绪后,连接 MicroPython 板并创建文件。您在 PC 上为这个例子创建了一个文件,并将其命名为fibonacci.py。在下一节中,我们将把它复制到我们的 MicroPython 板上。

Tip

要更深入地了解如何在 Python 中创建和使用自己的函数(方法),请参见 https://docs.python.org/3/tutorial/controlflow.html#defining-functions

现在,让我们看看运行这个脚本会发生什么。回想一下,我们将在我们的 MicroPython 板上运行这段代码,因此如果您正在跟进,请确保设置好您的板并将其连接到您的 PC。

执行代码

回想一下第三章,当我们想要将代码移动到我们的 MicroPython 板上时,我们需要创建文件,然后将它复制到 MicroPython 板上,然后执行它。在这一节中,我将向您展示如何在 macOS 上做到这一点。在 Windows 10 上如何做到这一点的示例如示例 4 所示。

当您将 MicroPython 板插入 PC 上的 USB 端口时,闪存盘将出现在您的 Finder 和桌面上。图 4-1 显示了 macOS 上的一个例子。

A447395_1_En_4_Fig1_HTML.jpg

图 4-1。

MicroPython board Flash Drive Mounted

我们现在需要将fibonacci.py文件复制到闪存驱动器。在 macOS 上,打开闪存驱动器,然后将文件拖到驱动器上。当你复制完文件后,你应该会看到类似图 4-2 的东西。

A447395_1_En_4_Fig2_HTML.jpg

图 4-2。

Files Copied to the MicroPython board Flash Drive

请注意,我对图像进行了注释以显示文件。注意,我们还看到了 MicroPython 文件boot.pymain.py。我们不会修改main.py文件来运行我们的例子,因为我们不希望程序在每次启动设备时都运行。相反,我们将从 REPL 控制台运行代码。

使用终端窗口打开 REPL 控制台。您应该会看到来自 MicroPython 的欢迎消息,后面是 RELP 提示(>>>)。在 REPL 提示符下,发出以下代码语句。

import fibonnaci

这段代码导入我们刚刚复制的fibonnaci.py文件,当它导入时,它执行这段代码。因此,就像我们从 Python 控制台在我们的 PC 上运行它一样。继续通过请求斐波纳契数列中的第十二个值来测试程序。您应该会看到如图 4-3 所示的输出。

A447395_1_En_4_Fig3_HTML.jpg

图 4-3。

Output of Fibonacci Example (MicroPython board)

注意,我对查询做出了响应,显示了整个系列。还要注意,我们看到代码已经使用了我们编写的两个版本的函数:迭代和递归。最后,我们看到两个函数的值或输出是相同的数据。

Note

如果您还没有 MicroPython 板,您可以在 PC 上使用命令python ./fibonacci.py运行代码。您将看到类似的输出,如图所示。

如果再次运行代码,只需按 CTRL-D 进行软重置,然后再次发出 import 语句。这将重新运行整个代码。或者,如果您想运行其中一个函数,您可以通过使用下面的代码导入它来再次调用它。图 4-4 显示了在 MicroPython 板上执行时的样子。

A447395_1_En_4_Fig4_HTML.jpg

图 4-4。

Experimenting with the Fibonacci Functions

from fibonacci import fibonacci_iterative
fibonacci_iterative(6)
from fibonacci import fibonacci_recursive
fibonacci_recursive(6)

以这种方式导入后,您可以再次运行这些函数。继续尝试使用不同的值来显示它们的行为。回想一下,函数的实现是不同的——迭代版本返回列表,递归版本只返回最后一个或第 n 个值。

当您完成了示例的实验后,记得在拔下驱动器之前关闭终端并弹出闪存驱动器。

你的挑战

为了让这个例子更有用,修改代码,在斐波那契数列中搜索特定的整数。要求用户提供一个整数,然后确定该值是否是有效的斐波那契值。例如,如果用户输入 144,代码应该告诉用户该值是有效的,并且是序列中的第十二个值。虽然这个挑战将要求您为“主要”功能重写大部分代码,但是您必须找出如何以新的方式使用这些功能。

示例 4:使用类

这个例子通过引入面向对象的编程概念:类,大大增加了复杂性。回想一下前面的内容,类是模块化代码的另一种方式。类用于建模数据和数据上的行为。此外,类通常放在它们自己的代码模块(文件)中,进一步模块化代码。如果您需要修改一个类,您可能只需要更改类模块中的代码。

我们在这个例子中探索的问题是如何使用类和代码模块开发解决方案。我们将创建两个文件:一个用于类,另一个用于主代码。

写代码

这个例子是用来把罗马数字 8 转换成整数的。也就是说,我们将输入一个类似 VIII 的值,即 8,并期望看到整数 8。为了让事情变得更有趣,我们还将把我们得到的整数转换回罗马数字。罗马数字是用字符 I 表示 1,V 表示 5,X 表示 10,L 表示 50,C 表示 100,D 表示 500,M 表示 1000 组成的字符串。其他数字的组合是通过将字符数值加在一起(例如,3 = III),或者在另一个字符之前加一个单独的低位字符来表示代表减去该字符(例如,4 = IV)。下面展示了一些如何工作的例子。

3 = III
15 = XV
12 = XII
24 = XXIV
96 = LXLVI
107 = CVII

这听起来像是很多额外的工作,但是考虑一下:如果我们可以从一种格式转换到另一种格式,我们应该能够没有错误地转换回来。更具体地说,我们可以使用一个转换的代码来验证另一个转换。如果我们在把它转换回来的时候得到了不同的值,我们知道我们有一个需要解决的问题。

为了解决这个问题,我们将把转换罗马数字的代码放到一个单独的文件(代码模块)中,并构建一个名为Roman_Numerals的类来包含这些方法。在这种情况下,数据是整数到罗马数字的映射。

# Private dictionary of roman numerals
__roman_dict = {
    'I': 1,
    'IV': 4,
    'V': 5,
    'IX': 9,
    'X': 10,
    'XL': 40,
    'L': 50,
    'XC': 90,
    'C': 100,
    'CD': 400,
    'D': 500,
    'CM': 900,
    'M': 1000,
}

注意字典名称前的两个下划线。这是一种特殊的符号,它将字典标记为类中的私有变量。这是用于信息隐藏的 Python 方面,是设计对象时推荐使用的技术;总是努力隐藏在类内部使用的数据。

还要注意,我没有使用基本字符及其值,而是使用了其他几个值。我这样做是为了让转换变得更容易(也有一点欺骗)。在本例中,我添加了表示先前转换的一个值的条目,如 4 (IV)、9 (IX)等。这使得转换更容易(也更准确)。

我们还将添加两个方法;convert_to_int(),取一个罗马数字串,转换成整数;和convert_to_roman(),它接受一个整数并将其转换为罗马数字。我没有解释方法中的每一行代码,而是让您阅读代码,看看它是如何工作的。

简单地说,convert to integer 方法获取每个字符,并从字典中对这些值求和得到它的值。这里有一个技巧,需要对出现在较高值之前的较低值字符进行特殊处理(例如,IX)。convert to Roman 方法稍微容易一些,因为我们只需将该值除以字典中的最高值,直到达到零。清单 4-9 显示了类模块的代码,它保存在一个名为roman_ numerals .py的文件中。

#
# MicroPython for the IOT
#
# Example: Roman numerals class
#
# Convert integers to roman numerals
# Convert roman numerals to integers
#
# Dr. Charles Bell
#

class Roman_Numerals:

    # Private dictionary of roman numerals
    __roman_dict = {
        'I': 1,
        'IV': 4,
        'V': 5,
        'IX': 9,
        'X': 10,
        'XL': 40,
        'L': 50,
        'XC': 90,
        'C': 100,
        'CD': 400,
        'D': 500,
        'CM': 900,
        'M': 1000,
    }

    def convert_to_int(self, roman_num):
        value = 0
        for i in range(len(roman_num)):
            if i > 0 and self.__roman_dict[roman_num[i]] > self.__roman_dict[roman_num[i - 1]]:
                value += self.__roman_dict[roman_num[i]] - 2 * self.__roman_dict[roman_num[i - 1]]
            else:
                value += self.__roman_dict[roman_num[i]]
        return value

    def convert_to_roman(self, int_value):
        # First, get the values of all of entries in the dictionary
        roman_values = list(self.__roman_dict.values())
        roman_keys = list(self.__roman_dict.keys())
        # Prepare the string
        roman_str = ""
        remainder = int_value
        # Loop through the values in reverse
        for i in range(len(roman_values)-1, -1, -1):
            count = int(remainder / roman_values[i])
            if count > 0:
                for j in range(0,count):
                    roman_str += roman_keys[i]
                remainder -= count * roman_values[i]
        return roman_str

Listing 4-9.
Roman Numeral Class

如果你按照这一章来做,那么就在你的 PC 上为这段代码创建一个文件,并把它命名为roman_numerals.py。在下一节中,我们将把它复制到我们的 MicroPython 板上。

现在让我们看看主要代码。为此,我们只需从代码模块导入新的类,如下所示。这是进口指令的一种稍微不同的形式。在这种情况下,我们告诉 Python 包含名为Roman_Numerals的文件中的roman_ numerals类。

from roman_numerals import Roman_Numerals

Note

如果代码模块在一个子文件夹中,比如说roman,我们将把 import 语句写成from roman import Roman_Numerals,在这里我们使用点符号而不是斜线列出文件夹。

代码的其余部分很简单。我们首先要求用户输入一个有效的罗马数字字符串,然后将其转换为整数,并使用该值转换回罗马数字,打印结果。所以,你可以看到,将类放在一个单独的模块中简化了我们的代码,使它更短,更容易维护。清单 4-10 显示了保存在一个简单命名为roman.py的文件中的完整主代码。

#
# MicroPython for the IOT
#
# Example: Convert roman numerals using a class
#
# Convert integers to roman numerals
# Convert roman numerals to integers
#
# Dr. Charles Bell
#

from roman_numerals import Roman_Numerals

roman_str = input("Enter a valid roman numeral: ")
roman_num = Roman_Numerals()

# Convert to roman numberals
value = roman_num.convert_to_int(roman_str)
print("Convert to integer:        {0} = {1}".format(roman_str, value))

# Convert to integer
new_str = roman_num.convert_to_roman(value)
print("Convert to Roman Numerals: {0} = {1}".format(value, new_str))

print("bye!")

Listing 4-10.Converting Roman Numerals

如果你按照这一章来做,那么就在你的 PC 上为这段代码创建一个文件,并把它命名为roman.py。在下一节中,我们将把它复制到我们的 MicroPython 板上。

Tip

要更深入地了解如何使用 Python 中的类,请参见 https://docs.python.org/3/tutorial/classes.html

现在,让我们看看运行这个脚本会发生什么。回想一下,我们将在我们的 MicroPython 板上运行这段代码,因此如果您正在跟进,请确保设置好您的板并将其连接到您的 PC。

执行代码

在这个例子中,我们将看到如何在 Windows 10 上将代码复制到我们的 MicroPython 板上。像上一个例子一样,我们将首先在我们的 PC 上创建文件,连接 MicroPython 板,复制文件,然后执行它们。

当您在 Windows 10 上连接您的 MicroPython 板时,该驱动器将显示在文件资源管理器中,如图 4-5 所示。

A447395_1_En_4_Fig5_HTML.jpg

图 4-5。

MicroPython board Flash Drive Mounted (Windows 10)

如果您还没有创建文件,现在就创建。准备好后,我们会把它们复制到 MicroPython 板上。我没有使用文件浏览器,而是选择演示如何从控制台(命令提示符)复制文件。打开控制台,然后切换到文件资源管理器中显示的驱动器号。接下来,将文件roman_numerals.pyroman.py复制到驱动器。图 4-6 显示了一个复制文件的例子。

A447395_1_En_4_Fig6_HTML.jpg

图 4-6。

Copying the File to the MicroPython board Flash Drive (Console)

现在,我们准备好连接到 MicroPython 板并运行代码。回想一下,连接到电路板的方法之一是在 Windows 上使用 PuTTY。图 4-7 为油灰控制台。注意,我选择了 COM3 和串行连接。回想一下,您可以从设备管理器中找到 COM 端口。准备就绪后,单击打开。

连接完成后,PuTTY 将打开 REPL 控制台。您应该会看到来自 MicroPython 的欢迎消息,后面是 RELP 提示符(> > >)。在 REPL 提示符下,发出以下代码语句。

import roman

这段代码导入我们刚刚复制的roman.py文件,当它这样做时,它执行这段代码,如果您还记得的话,这段代码将调用 roman _ numerals.py 代码模块中的代码。同样,发布导入就像我们在 PC 上从 Python 控制台运行它一样。

A447395_1_En_4_Fig7_HTML.jpg

图 4-7。

Connecting with PuTTy (Windows 10)

一旦发出 import 语句,代码就会执行。去试试吧。从值 LXLIV 开始,它是罗马数字中的 SSS。图 4-8 显示了输出的样子。

A447395_1_En_4_Fig8_HTML.jpg

图 4-8。

Executing the Roman Numerals Example

您还可以尝试从终端导入代码并再次执行它,或者只是弹出驱动器,重置板,并重新连接以使用其他值再次运行它。

当您完成执行示例时,记得在拔下驱动器之前关闭终端并弹出闪存驱动器。

你的挑战

除了一些用户友好性(更好使用)之外,这个例子没有太多需要改进的地方。如果您想改进代码或类本身,我建议添加一个名为validate()的新方法,用于验证罗马数字字符串。该方法可以获取一个字符串,并确定它是否包含有效的字符序列。提示:首先,检查字符串是否只包含字典中的字符。

但是,您可以使用该模板构建其他用于转换格式的类。例如,作为练习,您可以创建一个新的类来将整数转换为十六进制甚至八进制。是的,有一些功能可以帮我们做到这一点,但是自己构建它会很有启发性和令人满意。去吧,试一试——创建一个新的类来将整数转换成其他格式。我建议先做一个十六进制到整数的函数,当这个函数工作正常时,创建一个倒数函数来把整数转换成十六进制。

更高级的挑战是重写类以接受构造函数中的字符串(当类变量被创建时),并使用该字符串进行转换,而不是使用convert_to*方法传递字符串或整数。例如,该类可以有一个构造函数和私有成员,如下所示。

__roman_str = ""
...
def __init__(self, name):
        self.name = name

当您创建实例时,您将需要传递该字符串,否则您将会得到一个缺少必需参数的错误。

roman_str = input("Enter a valid roman numeral: ")
roman_num = Roman_Numerals(roman_str)

更多信息

如果您需要更深入的 Python 知识,有几本关于这个主题的优秀书籍。下面我列出了几个我最喜欢的。Python 站点上的文档是一个很好的资源:python.org/doc/

  • Pro Python,第二版(apress,2014 年)J. Burton Browning,Marty Alchin
  • 学习 Python,第五版(O'Reilly Media,2013)马克·卢茨
  • 用 Python 自动化枯燥的东西:完全初学者实用编程(无淀粉出版社,2015),Al Sweigart

摘要

哇哦!那是一次疯狂的旅行,不是吗?我希望这个简短的 Python 速成课程已经对到目前为止展示的示例程序进行了足够的解释,现在您已经知道它们是如何工作的了。这个速成课程也是理解本书中其他 Python 例子的基础。

如果您正在学习如何使用 IOT 项目,但不知道如何使用 Python 编程,鉴于 Python 易于理解的语法,学习 Python 会很有趣。虽然互联网上有很多例子可以使用,但是很少有以这种方式记录的,可以为 Python 新手提供足够的信息来理解,更不用说开始使用和部署示例了!但至少代码很容易阅读。

本章提供了 Python 速成课程,涵盖了您在研究大多数较小的示例项目时将会遇到的基本内容。我们发现了 Python 应用的基本语法和结构,包括构建一个闪烁 LED 的真实 Python 应用的演练。通过这个例子,我们学习了如何使用无头应用,包括如何管理一个启动后台应用。

在下一章,我们将深入探究 MicroPython 编程。我们将看到更多关于在 MicroPython 板上运行的 IOT 项目中可用的特殊库。

Footnotes 1

https://en.wikipedia.org/wiki/String_interpolation

2

https://www.python.org/dev/peps/pep-0008/

3

没错,字典就是对象!元组和列表以及许多其他数据结构也是如此。

4

从技术上来说,它被称为名称篡改,它模拟了将某些内容设为私有,但是如果您提供了正确数量的下划线,仍然可以被访问。更多信息请参见 https://en.wikipedia.org/wiki/Name_mangling

5

http://www.json.org/

6

[T0](https://en.wikipedia.org/wiki/Recursion_(computer_science)

7

https://en.wikipedia.org/wiki/Fibonacci_number

8

https://en.wikipedia.org/wiki/Roman_numerals

五、MicroPython 库

现在我们已经很好地掌握了如何用 Python 和 MicroPython 编写代码,是时候看看组成固件的支持库了。正如您将看到的,MicroPython 中可用的库反映了 Python 中的库。事实上,固件中的库(有时称为应用编程接口或 API 或固件 API)包含大量 Python 中的相同库。

对于标准库来说,有一些值得注意的例外,在 MicroPython 中有一个等价的库,但它已经被重命名,以区别于 Python 库。在这种情况下,要么通过删除不常用的特性来缩小库的范围,要么通过某些方式进行修改以适应 MicroPython 平台——所有这些都是为了节省空间(内存)。

还有一些特定于 MicroPython 的库,以及一些硬件,这些硬件提供的功能在一些通用 Python 版本中可能有,也可能没有。这些库旨在简化微控制器和硬件的使用。

因此,固件中有三种类型的库:标准的,与 Python 中的基本相同;专用于 MicroPython 的;专用于硬件的。还有另一种类型的库,有时称为用户提供的库或简单的自定义库。这些是我们自己创建的库(API ),我们可以将它们部署到我们的板上,从而使我们所有的项目都可以使用这些功能。在本章中,我们将会看到所有类型的库的概述。

Note

模块这个词,或者代码模块,有时用来指一个库;然而,模块通常是单个代码文件,而库可能由许多代码文件组成。因此,如果库是一个单独的文件(代码模块),交换名字是可以的。有时用于库的另一个词是包,它意味着多个文件。你可能也会遇到这个术语。

我们不是简单地解释或复制现有的文档,而是以快速参考表的形式查看这些库的概述,您可以使用这些表来熟悉可用的内容。简单地知道什么是可用的,通常可以加速开发并使您的程序更强大,而不必重新发明一些东西或花费大量时间去寻找您可能(或可能不)需要的库。然而,有几个库对于学习如何开发 MicroPython IOT 项目至关重要。我们将通过代码片段来更详细地发现它们,以帮助您学习它们。因此,虽然本章旨在向您介绍更常见的库,但它也是一个参考指南,可以通过官方文档的链接更深入地了解 MicroPython 固件。

让我们先来看看 MicroPython 中的那些“标准”Python 库。

内置和标准库

众所周知,MicroPython 是建立在 Python 之上的。人们做了大量的工作来精简内容,以便大部分 Python 库可以放在一个芯片上。由此产生的 MicroPython 固件比你电脑上的 Python 小得多,但一点也不差。也就是说,MicroPython 是 Python,因此 MicroPython 有许多与 Python 相同的库。

有些人可能称这些库为“内置的”,但更正确的说法是称它们为“标准”库,因为这些库与 Python 中的库是一样的。更具体地说,它们与 Python 中的类具有相同的类和相同的功能。因此,你可以在你的 PC 上写一个脚本并在那里执行它,然后在你的 MicroPython 板上执行相同的脚本。不错!正如您所猜测的,这在开发一个复杂的项目时非常有帮助。

回想一下,我们在上一章中看到过这种技术的演示。在那里我们看到了开发你的脚本的一部分——那些使用标准库的部分——并在你的 PC 上调试它们是可能的。也就是说,一旦您让它们正常工作,您就可以进入下一个需要 MicroPython 库或硬件库的部分。这是因为在我们的 PC 上开发要容易得多。我们不需要打开主板电源,将它连接到我们的 WiFi 等。,让它发挥作用。另外,个人电脑的速度要快得多。只是到处都更容易。通过实践这一实践,我们可以通过确保我们项目的“标准”部分正确工作来潜在地为我们自己节省一些挫折。

在这一节中,我们将探索标准的 Python 库,首先简要概述可用的库,然后详细介绍如何使用一些更常见的库。

Tip

参见 http://docs.micropython.org/en/latest/pyboard/library/index.html#python-standard-libraries-and-micro-libraries 了解更多关于任何标准库的细节。

概观

MicroPython 中的标准库包含一些对象,您可以使用这些对象来执行数学函数、操作编程结构、通过 JSON 处理可传输的文档(文档存储)、与操作系统和其他系统函数进行交互,甚至按时执行计算。表 5-1 包含了当前标准 MicroPython 库的列表。第一列是我们在import语句中使用的名称,第二列是简短的描述,第三列包含指向在线文档的链接。

表 5-1。

Standard Python Libraries in MicroPython

| 名字 | 描述 | 文件 | | --- | --- | --- | | `array` | 使用数组 | [`http://docs.micropython.org/en/latest/pyboard/library/array.html`](http://docs.micropython.org/en/latest/pyboard/library/array.html) | | `cmath` | 复杂数学 | [`http://docs.micropython.org/en/latest/pyboard/library/cmath.html`](http://docs.micropython.org/en/latest/pyboard/library/cmath.html) | | `gc` | 垃圾收集工 | [`http://docs.micropython.org/en/latest/pyboard/library/gc.html`](http://docs.micropython.org/en/latest/pyboard/library/gc.html) | | `math` | 数学函数 | [`http://docs.micropython.org/en/latest/pyboard/library/math.html`](http://docs.micropython.org/en/latest/pyboard/library/math.html) | | `sys` | 系统级功能 | [`http://docs.micropython.org/en/latest/pyboard/library/sys.html`](http://docs.micropython.org/en/latest/pyboard/library/sys.html) | | `ubinascii` | 二进制/ASCII 转换 | [`http://docs.micropython.org/en/latest/pyboard/library/ubinascii.html`](http://docs.micropython.org/en/latest/pyboard/library/ubinascii.html) | | `ucollections` | 容器 | [`http://docs.micropython.org/en/latest/pyboard/library/ucollections.html`](http://docs.micropython.org/en/latest/pyboard/library/ucollections.html) | | `uerrno` | 错误代码 | [`http://docs.micropython.org/en/latest/pyboard/library/uerrno.html`](http://docs.micropython.org/en/latest/pyboard/library/uerrno.html) | | `uhashlib` | 哈希算法 | [`http://docs.micropython.org/en/latest/pyboard/library/uhashlib.html`](http://docs.micropython.org/en/latest/pyboard/library/uhashlib.html) | | `uheapq` | 堆队列 | [`http://docs.micropython.org/en/latest/pyboard/library/uheapq.html`](http://docs.micropython.org/en/latest/pyboard/library/uheapq.html) | | `uio` | 输入/输出流 | [`http://docs.micropython.org/en/latest/pyboard/library/uio.html`](http://docs.micropython.org/en/latest/pyboard/library/uio.html) | | `ujson` | JSON 文档 | [`http://docs.micropython.org/en/latest/pyboard/library/ujson.html`](http://docs.micropython.org/en/latest/pyboard/library/ujson.html) | | `uos` | 操作系统 | [`http://docs.micropython.org/en/latest/pyboard/library/uos.html`](http://docs.micropython.org/en/latest/pyboard/library/uos.html) | | `ure` | 正则表达式 | [`http://docs.micropython.org/en/latest/pyboard/library/ure.html`](http://docs.micropython.org/en/latest/pyboard/library/ure.html) | | `uselect` | 流事件 | [`http://docs.micropython.org/en/latest/pyboard/library/uselect.html`](http://docs.micropython.org/en/latest/pyboard/library/uselect.html) | | `usocket` | 插座(网络) | [`http://docs.micropython.org/en/latest/pyboard/library/usocket.html`](http://docs.micropython.org/en/latest/pyboard/library/usocket.html) | | `ustruct` | 打包和解包 struts | [`http://docs.micropython.org/en/latest/pyboard/library/ustruct.html`](http://docs.micropython.org/en/latest/pyboard/library/ustruct.html) | | `utime` | 时间和日期函数 | [`http://docs.micropython.org/en/latest/pyboard/library/utime.html`](http://docs.micropython.org/en/latest/pyboard/library/utime.html) | | `uzlib` | 压缩算法 | [`http://docs.micropython.org/en/latest/pyboard/library/uzlib.html`](http://docs.micropython.org/en/latest/pyboard/library/uzlib.html) |

请注意,这些链接是 Pyboard 文档的链接,其中大多数也适用于其它电路板。如果有疑问,请访问您的主板的 MicroPython 文档。

Tip

这里介绍的大多数库都是 Pyboard 和 WiPy 所共有的。我们还将在下一章看到一些不同之处。

如您所见,有许多库以u开头,表示它们是 Python 等价库的特殊版本。有趣的是,一些平台可能包含原始的 Python 库。也就是说,如果您需要访问原始 Python 版本——如果它存在的话——您仍然可以通过使用原始名称(没有u前缀)来访问它。在这种情况下,MicroPython 将尝试通过原始名称查找模块,如果不存在,则默认为 MicroPython 版本。例如,如果我们想使用原始的 io 库,我们可以使用import io。但是,如果平台上没有名为io的模块,MicroPython 将使用名为uio的 MicroPython 版本。

Caution

import语句的格式允许我们指定目录。所以,如果我们使用import mylibs.io,MicroPython 将试图在mylibs文件夹中找到名为io.py的库(代码模块)。这可能会影响到如何使用不带 u 前缀的模块。如果我们使用了import io并且io.py不是一个代码模块,它将使用io作为一个文件夹的名称,如果它存在,在那个文件夹中查找模块。因此,如果您使用与 Python 库名称相同的文件夹名称,您可能会遇到麻烦。别这样。

接下来,我们将查看一些更常用的标准库,并查看每个标准库的一些代码示例。但是首先,我们应该讨论两类标准函数。

Interactive Help for Libraries

在学习 MicroPython 中的库时,一个鲜为人知的名为help()的函数会非常有帮助。您可以在 REPL 会话中使用该函数来获取有关库的信息。下面显示了uos库输出的摘录。

>>> help(uos)
<module 'uos'>object is of type module
  __name__ -- uos
  uname -- <function>
  chdir -- <function>
  getcwd -- <function>
  listdir -- <function>
  mkdir -- <function>

请注意,我们看到了所有函数的名称,以及常数(如果有的话)。当学习库和它们包含的内容时,这是一个真正的帮助。试试看!

公共标准库

现在让我们看看一些更常用的标准库的例子。接下来的只是你可以用每个库做什么的一个例子。有关所有功能的完整描述,请参见联机文档。像大多数 MicroPython 库一样,公共标准库可能是完整 Python3 (CPython)实现的子集。

Note

本章中的例子旨在运行在 MicroPython 板上。您可能需要做一些更改才能在您的电脑上运行它。

[计]系统复制命令(system 的简写)

sys 库提供对执行系统的访问,例如常量、变量、命令行选项、流(stdout、stdin、stderr)等等。该库的大多数特性都是常量或列表。可以直接访问这些流,但通常我们使用print()函数,默认情况下它会将数据发送到 stdout 流。

该库中最常用的函数包括。清单 5-1 展示了这些变量和exit()函数。

  • sys.argv:从命令行传递给脚本的参数列表
  • sys.exit(r):退出程序,返回值 r 给调用者
  • sys.modules:加载(导入)的模块列表
  • sys.path:搜索模块的路径列表——可以修改
  • sys.platform:显示 Linux、MicroPython 等平台信息。
  • sys.stderr:标准误差流
  • sys.stdin:标准输入流
  • sys.stdout:标准输出流
  • sys.version:当前执行的 Python 版本
# MicroPython for the IOT - Chapter 5
# Example use of the sys library
import sys
print("Modules loaded: " , sys.modules)
sys.path.append("/sd")
print("Path: ", sys.path)
sys.stdout.write("Platform: ")
sys.stdout.write(sys.platform)
sys.stdout.write("\n")
sys.stdout.write("Version: ")
sys.stdout.write(sys.version)
sys.stdout.write("\n")
sys.exit(1)
Listing 5-1.Demonstration of the sys library features

注意我们从import语句开始,然后我们可以使用print()函数打印 sys 库中的常量和变量。我们还将看到如何使用sys.path.append()函数将路径添加到搜索路径中。如果我们在引导驱动器(SD 驱动器)上创建自己的目录来放置代码,这将非常有帮助。没有这个添加,import语句将失败,除非代码模块在lib目录中。

在示例的最后,我们将看到如何使用 stdout 流将内容写入屏幕。注意,您必须提供回车(换行符)命令来将输出推进到新的一行(\n)。print()函数为我们处理了这个问题。下面显示了在 MicroPython 板上运行这个脚本的输出。

>>> import examples.sys_example
Modules loaded:  {'examples': <module 'examples'>, 'examples.sys_example': <module 'examples.sys_example' from 'examples/sys_example.py'>}
Path:  ['', '/flash', '/flash/lib', '/sd']
Platform: WiPy
Version: 3.4.0

请注意,我们看到了预期的数据,并且这个示例运行在一个 WiPy 模块上。你在import语句中看出了什么奇怪的地方吗?这里,代码被放在一个名为examples的新目录中,然后将代码模块复制到我的引导驱动器上的那个目录中。因此,我们可以使用语句中的目录名来查找代码模块并导入它(examples/sys_examples.py)。如果您想在自己的 MicroPython 板上运行这段代码,您可以将代码写入一个文件,比如sys_example.py,然后将该文件复制到您的引导驱动器,或者使用 ftp 来复制该文件。这允许你保持你的引导(闪存)驱动器整洁,并把你的代码放在一个共同的位置。

最后,请注意没有命令行参数。这是因为我们使用了一个import语句。然而,如果我们在 PC 上运行提供命令行参数的代码,我们会看到它们。下面显示了在 PC 上运行该脚本的输出。

$ python ./sys_example.py
Modules loaded:  {'builtins': <module 'builtins' (built-in)>, 'sys': <module 'sys' (built-in)>, ... '/sd']
darwin
Version: 3.6.0 (v3.6.0:41df79263a11, Dec 22 2016, 17:23:13)
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)]

奥斯陆大学

uio库包含额外的函数来处理流和类似流的对象。有一个名为uio.open()的函数可以用来打开文件(但是大多数人使用名为open()的内置函数)以及用于字节流和字符串流的类。事实上,这些类有类似的文件函数,比如read()write()seek()flush()close(),以及一个getvalue()函数,它返回包含数据的流缓冲区的内容。让我们看一个例子。

在本例中,我们首先打开一个新的文件进行写入,并将一个字节数组写入该文件。使用的技术是将每个 bye 的十六进制值传递给write()函数。当您从传感器读取数据时,它们通常是二进制的(一个字节或一串字节)。你用转义符\x表示一个字节,如图所示。

将数据写入文件后,我们通过将 1 传递给read()函数来一次读取一个字节。然后,我们打印读取的原始 for 值(从read(1)调用返回的值)、十进制值和十六进制值。清单 5-2 展示了如何使用 uio 以二进制模式读取文件。FileIo()类。写入的字节包含一个秘密的字(一个用十六进制值模糊的字)-你能看到它吗?

# MicroPython for the IOT - Chapter 5
# Example use of the uio library
# Note: change uio to io to run this on your PC!
import uio
# Create the binary file
fio_out = uio.FileIO('data.bin', 'wb')
fio_out.write("\x5F\x9E\xAE\x09\x3E\x96\x68\x65\x6C\x6C\x6F")
fio_out.write("\x00")
fio_out.close()
# Read the binary file and print out the results in hex and char.
fio_in = uio.FileIO('data.bin', 'rb')
print("Raw,Dec,Hex from file:")
byte_val = fio_in.read(1)  # read a byte
while byte_val:
    print(byte_val, ",", ord(byte_val), hex(ord(byte_val)))
    byte_val = fio_in.read(1)  # read a byte
fio_in.close()
Listing 5-2.Demonstration of the uio library features

这就像我们在上一章看到的普通内置函数一样。清单 5-3 显示了在 WiPy 上运行时的输出。

>>> import examples.uio_example
Raw,Dec,Hex from file:
b'_' , 95 0x5f
b'\xc2' , 194 0xc2
b'\x9e' , 158 0x9e
b'\xc2' , 194 0xc2
b'\xae' , 174 0xae
b'\t' , 9 0x9
b'>' , 62 0x3e
b'\xc2' , 194 0xc2
b'\x96' , 150 0x96
b'h' , 104 0x68
b'e' , 101 0x65
b'l' , 108 0x6c
b'l' , 108 0x6c
b'o' , 111 0x6f
b'\x00' , 0 0x0
Listing 5-3.Demonstration of the uio library features (Pyboard)

如果您想知道文件是什么样子,您可以使用一个像 hexdump 这样的工具来打印内容,如下所示。你能看到隐藏的信息吗?

$ hexdump -C data.bin
00000000  5f 9e ae 09 3e 96 68 65  6c 6c 6f 00              |_...>.hello.|
0000000c

乌伊松

在 IOT 项目中处理数据时,ujson库是您可能会经常使用的库之一。它提供了 JavaScript 对象符号(JSON)文档的编码和解码。这是因为许多可用的 IOT 服务要么需要要么能够处理 JSON 文档。因此,您应该考虑养成在 JSON 中格式化数据的习惯,以便更容易与其他系统集成。该库实现了以下函数,您可以使用这些函数来处理 JSON 文档。

  • ujson.dumps(obj):返回从 JSON 对象解码的字符串。
  • ujson.loads(str):解析 JSON 字符串,返回一个 JSON 对象。如果格式不正确,将引发错误。
  • ujson.load(fp):解析文件指针(包含 JSON 文档的文件字符串)的内容。如果格式不正确,将引发错误。

回想一下,我们在上一章看到了一个 JSON 文档的简单例子。这个例子是专门为 PC 编写的,但是做了一点小小的改动就可以在 MicroPython 板上运行。让我们看一个类似的例子。清单 5-4 展示了一个使用ujson库的例子。

# MicroPython for the IOT - Chapter 5
# Example use of the ujson library
# Note: change ujson to json to run it on your PC!
import ujson

# Prepare a list of JSON documents for pets by converting JSON to a dictionary
vehicles = []
vehicles.append(ujson.loads('{"make":"Chevrolet", "year":2015, "model":"Silverado", "color":"Pull me over red", "type":"pickup"}'))
vehicles.append(ujson.loads('{"make":"Yamaha", "year":2009, "model":"R1", "color":"Blue/Silver", "type":"motorcycle"}'))
vehicles.append(ujson.loads('{"make":"SeaDoo", "year":1997, "model":"Speedster", "color":"White", "type":"boat"}'))
vehicles.append(ujson.loads('{"make":"TaoJen", "year":2013, "model":"Sicily", "color":"Black", "type":"Scooter"}'))

# Now, write these entries to a file. Note: overwrites the file
json_file = open("my_vehicles.json", "w")
for vehicle in vehicles:
    json_file.write(ujson.dumps(vehicle))
    json_file.write("\n")
json_file.close()

# Now, let's read the list of vehicles and print out their data
my_vehicles = []
json_file = open("my_vehicles.json", "r")
for vehicle in json_file.readlines():
    parsed_json = ujson.loads(vehicle)
    my_vehicles.append(parsed_json)
json_file.close()

# Finally, print a summary of the vehicles
print("Year Make Model Color")
for vehicle in my_vehicles:
    print(vehicle['year'],vehicle['make'],vehicle['model'],vehicle['color'])

Listing 5-4.Demonstration of the ujson library features

下面显示了在 WiPy 上运行的脚本的输出。

>>> import examples.ujson_example
Year Make Model Color
2015 Chevrolet Silverado Pull me over red
2009 Yamaha R1 Blue/Silver
1997 SeaDoo Speedster White
2013 TaoJen Sicily Black

终极指标

uos库实现了一组用于基本操作系统的函数。如果你为你的电脑写过程序,有些功能你可能会很熟悉。大多数函数允许您处理文件和目录操作。下面列出了几个比较常用的函数。

  • uos.chdir(path):改变当前目录
  • uos.getcwd():返回当前工作目录
  • uos.listdir([dir]):如果缺少目录,列出当前目录,或者列出指定的目录
  • uos.mkdir(path):新建一个目录
  • uos.remove(path):删除文件
  • uos.rmdir(path):删除一个目录
  • uos.rename(old_path, new_path):重命名文件
  • uos.stat(path):获取文件或目录的状态

在这个例子中,我们看到了如何更改工作目录,以便简化我们的导入语句。我们还将看到如何创建一个新目录,重命名它,在新目录中创建一个文件,列出目录,最后清理(删除)更改。清单 5-5 展示了使用uos l库函数的例子。

# MicroPython for the IOT - Chapter 5
# Example use of the uos library
# Note: change uos to os to run it on your PC!
import sys
import uos

# Create a function to display files in directory
def show_files():
    files = uos.listdir()
    sys.stdout.write("\nShow Files Output:\n")
    sys.stdout.write("\tname\tsize\n")
    for file in files:
        stats = uos.stat(file)
        # Print a directory with a "d" prefix and the size
        is_dir = True
        if stats[0] > 16384:
            is_dir = False
        if is_dir:
            sys.stdout.write("d\t")
        else:
            sys.stdout.write("\t")
        sys.stdout.write(file)
        if not is_dir:
            sys.stdout.write("\t")
            sys.stdout.write(str(stats[6]))
        sys.stdout.write("\n")

# List the current directory
show_files()
# Change to the examples directory
uos.chdir('examples')
show_files()

# Show how you can now use the import statement with the current dir
print("\nRun the ujson_example with import ujson_example after chdir()")
import ujson_example

# Create a directory
uos.mkdir("test")
show_files()

Listing 5-5.Demonstration of the uos library features

虽然这个例子有点长,但它展示了一些有趣的技巧。注意,我们创建了一个函数来打印目录列表,而不是打印返回的文件列表。我们还检查了文件的状态,以确定该文件是否是一个目录,如果是,我们打印一个 d 来表示该名称指的是一个目录。我们还使用 stdout 流来控制制表符(\t)和换行符(\n)的格式。

您还将看到如何使用更改目录来改进我们使用import语句的方式。由于我们将目录改为 examples,import ujson_example将首先尝试在当前目录中找到那个模块(库)。这是一个很好的技巧,你可以在你自己的项目中使用,将你的代码部署到你的开发板的一个单独的目录中,这意味着你可以使用它将多个项目部署到你的开发板,将每个项目放在它自己的目录中。

现在让我们看看输出。清单 5-6 显示了在 WiPy 上运行时的输出。花一些时间查看输出,看看这些函数是如何工作的。另外,请注意 JSON 示例的输出,因为我们在脚本中间导入了该代码。不错。

>>> import examples.uos_example

Show Files Output:
        name    size
        main.py 34
d       sys
d       lib
d       cert
        boot.py 336
d       examples
        data.bin        15
        my_vehicles.json        377

Show Files Output:
        name    size
        sys_example.py  395
        uio_example.py  609
        ujson_example.py        1370
        uos_example.py  1163
        data.bin        15

Run the ujson_example with import ujson_example after chdir()
Year Make Model Color
2015 Chevrolet Silverado Pull me over red
2009 Yamaha R1 Blue/Silver
1997 SeaDoo Speedster White
2013 TaoJen Sicily Black

Show Files Output:
        name    size
        sys_example.py  395
        uio_example.py  609
        ujson_example.py        1370
        uos_example.py  1163
        data.bin        15
        my_vehicles.json        377
d       test

Listing 5-6.Demonstration of the uos library features (output)

尤迪米特

utime库是另一个在许多项目中使用的流行库。该库用于获取当前时间、日期,并处理时间数据,例如计算时差。请注意,某些功能仅适用于作为硬件扩展或通过网络时间服务器安装的实时时钟(RTC)。详见第六章。下面列出了几个与在脚本中插入延迟相关的常用函数。当我们需要暂停处理以允许传感器读取或等待来自/去往其他源的数据时,延迟是有帮助的。该库的另一个常见用途是记录事件或传感器数据读取的日期和时间。

  • utime.sleep(seconds):将板卡置于睡眠模式,持续指定的秒数
  • utime.sleep_ms(ms):将板卡置于睡眠模式,持续指定的毫秒数
  • utime.sleep_us(us):将板卡置于睡眠模式,持续指定的微秒数

让我们看一个如何使用时间延迟的简短示例。这里,我们使用一个随机函数来休眠一段随机时间,并提供随机值。不用担心 RTC 代码;我们将在第六章中看到更多相关内容。

# MicroPython for the IOT - Chapter 5
# Example use of the utime library
# Note: This example only works on the WiPy
# Note: This example will not on your PC.
import machine
import sys
import utime

# Since we don't have a RTC, we have to set the current time
# initialized the RTC in the WiPy machine library
from machine import RTC

# Init with default time and date
rtc = RTC()
# Init with a specific time and date. We use a specific
# datetime, but we could get this from a network time
# service.
rtc = RTC(datetime=(2017, 7, 15, 21, 32, 11, 0, None))

# Get a random number from 0-1 from a 2²⁴ bit random value
def get_rand():
    return machine.rng() / (2 ** 24 - 1)

# Format the time (epoch) for a better view
def format_time(tm_data):
    # Use a special shortcut to unpack tuple: *tm_data
    return "{0}-{1:0>2}-{2:0>2} {3:0>2}:{4:0>2}:{5:0>2}".format(*tm_data)

# Generate a random number of rows from 0-25
num_rows = get_rand() * 25

# Print a row using random seconds, milleseconds, or microseconds
# to simulate time.
print("Datetime             Value")
print("-------------------  --------")
for i in range(0,num_rows):
    # Generate random value for our sensor
    value = get_rand() * 100
    # Wait a random number of seconds for time
    utime.sleep(int(get_rand() * 15))  # sleep up to 15 seconds
    print("{0}  {1:0>{width}.4f}".format(format_time(rtc.now()), value, width=8))

Listing 5-7.Demonstration of the utime library sleep features

Note

此示例还展示了不同主板的固件差异。这个例子只适用于 WiPy 和类似的 Pycom 板。

注意,在这个例子中有很多事情要做,而不仅仅是简单地等待几秒钟。此示例向您展示了如何做到这一点,以及如何处理时间数据和格式数据。在记录数据时,通常必须这样做。让我们走一遍。

除了 RTC 代码,我们还创建了两个函数:一个使用 WiPy 的机器库生成一个随机数(Pyboard 固件中没有这个函数),另一个以用户友好的格式格式化日期时间。

因为该函数创建了一个 24 位的随机数,所以我们用 24 位的最大值除,得到一个介于 0 和 1 之间的值。然后我们可以把它乘以一个整数,得到一个范围的值。例如,如果我们想要一个 0 到 6 之间的值,我们可以使用get_rand() * 6

format_time()函数返回一个表示日期时间的字符串。在这里,我们看到一些高级格式化选项,用每个部分的正确数字来格式化日期。具体来说,4 位数字代表年份,2 位数字代表月、日、小时、分钟和秒。:0>2选项告诉format()函数用前导零将值分隔在 2 个位置(数字)上。酷吧。注意最后一个print()语句。注意,我们使用了另一个技巧,用一个命名参数(width)来传递宽度。

最后,我们来看示例代码,其中我们生成了从 0 到 25 的随机行数,然后循环这些迭代,生成一个随机值(0-100),然后等待(休眠)随机秒数(0-15),然后打印新的日期时间和值。这模拟了我们用来从传感器读取数据的典型循环。由于大多数传感器需要时间来读取值,睡眠功能允许我们等待这段时间。 1

现在我们对这段代码的作用有了更多的了解,让我们看看它是如何工作的一个例子。下面显示了在 WiPy 上运行这段代码的输出。

>>> import examples.utime_example
Datetime             Value
-------------------  --------
2017-07-15 21:32:12  014.1520
2017-07-15 21:32:25  003.5380
2017-07-15 21:32:28  044.8298
2017-07-15 21:32:35  099.0981
2017-07-15 21:32:41  086.8839
2017-07-15 21:32:47  083.8304
2017-07-15 21:32:55  083.0670
2017-07-15 21:32:59  064.3214

Note

如前几章所述,MicroPython 有一些元素是特定于单个 MicroPython 板或硬件的。我们将在下一章讨论这些。

也有不属于任何特定库的内置函数,也有允许我们捕获错误条件的异常。在深入研究一些更常用的标准库之前,让我们先看看这些。

内置函数和类

Python 附带了许多内置函数:可以直接从脚本中调用而无需导入的函数。有许多类可以用来定义变量、处理数据等等。它们是对象,因此您可以使用它们来包含数据并对数据执行操作(功能)。到目前为止,我们已经在示例中看到了一些。

让我们看看一些主要的内置函数和类。表 5-2 包含了对每一项的简短描述。你可以在 https://docs.python.org/3/library/functions.html 找到更多关于每个的信息。虽然这是 Python 文档,但它也适用于 MicroPython。类用“类”指定;其余的都是函数。

您应该浏览这个列表,找到您感兴趣的链接,并在开发项目时参考这个列表,以便使用最合适的函数或类。你可能会惊讶有多少是“内置的”但是,请再次检查您选择的 MicroPython 板的文档,以了解固件中可用的最新功能和类。

表 5-2。

MicroPython Built-in Functions and Classes

| 名字 | 描述 | | --- | --- | | `abs(x)` | 返回一个数的绝对值。 | | `all(iterable)` | 如果 iterable 的所有元素都为 true(或者 iterable 为空),则返回 True。 | | `any(iterable)` | 如果 iterable 的任何元素为 true,则返回 True。 | | `bin(x)` | 将整数转换为二进制字符串。 | | `class bool([x])` | 返回一个布尔值,即 True 或 False 之一。 | | `class bytearray([source[, encoding[, errors]]])` | 返回新的字节数组。 | | `class bytes([source[, encoding[, errors]]])` | 返回一个新的“字节”对象,它是一个不可变的整数序列,范围是 0 <= x < 256 | | `callable(object)` | 如果对象参数是可调用的,则返回 True,否则返回 False。 | | `chr(i)` | 返回表示字符的字符串,该字符的 Unicode 码位是整数 I。 | | `classmethod(function)` | 返回函数的类方法。 | | `class complex([real[, imag]])` | 返回一个值为 real + imag*1j 的复数,或将字符串或数字转换为复数。 | | `delattr(obj, name)` | 这是`setattr()`的亲戚。参数是一个对象和一个字符串。该字符串必须是对象属性之一的名称。 | | `class dict()` | 创建新词典。 | | `dir([object])` | 不带参数,返回当前本地范围内的名称列表。使用参数,尝试返回该对象的有效属性列表。 | | `divmod(a,b)` | 使用整数除法时,将两个(非复数)数字作为参数,并返回由它们的商和余数组成的一对数字。 | | `enumerate(iterable, start=0)` | 返回一个枚举对象。Iterable 必须是序列、迭代器或其他支持迭代的对象。 | | `eval(expression, globals=None, locals=None)` | 在本地命名空间中使用全局变量和局部变量作为字典来计算表达式。 | | `exec(object[, globals[, locals]])` | 在本地名称空间中使用全局变量和局部变量作为字典来执行一组 Python 语句或对象。 | | `filter(function, iterable)` | 从 iterable 中函数返回 true 的元素构造一个迭代器。 | | `class float([x])` | 返回由数字或字符串构造的浮点数。 | | `class frozenset([iterable])` | 返回一个新的`frozenset`对象,可选地包含来自 iterable 的元素。 | | `getattr(object, name[, default])` | 返回对象的命名属性的值。名称必须是字符串。 | | `globals()` | 返回表示当前全局符号表的字典。 | | `hasattr(object, name)` | 参数是一个对象和一个字符串。如果字符串是对象属性之一的名称,则结果为 True,否则为 False。 | | `hash(object)` | 返回对象的哈希值(如果有的话)。哈希值是整数。 | | `hex(x)` | 将整数转换为以“0x”为前缀的小写十六进制字符串。 | | `id(object)` | 返回一个对象的“身份”。 | | `input([prompt])` | 如果 prompt 参数存在,它将被写入标准输出,并且没有尾随换行符。然后,该函数从 input 中读取一行,将其转换为一个字符串(去掉尾随的换行符),并返回该字符串。 | | `class int(x)` | 返回由数字或字符串 x 构成的整数对象,如果没有给定参数,则返回 0。 | | `isinstance(object, classinfo)` | 如果对象参数是`classinfo`参数或其(直接、间接或虚拟)子类的实例,则返回 true。 | | `issubclass(class, classinfo)` | 如果类是`classinfo`的子类(直接、间接或虚拟),则返回 true。 | | `iter(object[, sentinel])` | 返回一个迭代器对象。 | | `len(s)` | 返回一个对象的长度(项目数)。 | | `class list([iterable])` | 列表顺序。 | | `locals()` | 更新并返回一个表示当前局部符号表的字典。 | | `map(function, iterable, …)` | 返回一个迭代器,将函数应用于 iterable 的每一项,产生结果。 | | `max([iterable|arg*])` | 返回 iterable 中最大的项或两个或多个参数中最大的项。 | | `class memoryview(obj)` | 返回从给定参数创建的“内存视图”对象。 | | `min([iterable|arg*])` | 返回 iterable 中最小的项或两个或多个参数中最小的一个。 | | `next(iterator[, default])` | 通过调用迭代器的 __next__()方法,从迭代器中检索下一项。 | | `class objectO` | 返回一个新的无特征对象。对象是所有类的基。 | | `oct(x)` | 将整数转换为八进制字符串。 | | `open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)` | 打开文件并返回相应的 file 对象。使用`close()`关闭文件。 | | `ord(c)` | 给定一个表示一个 Unicode 字符的字符串,返回一个表示该字符的 Unicode 码位的整数。 | | `pow(x, y[, z])` | 将 x 返回到 y 的幂;如果 z 存在,返回 x 的 y 次方,以 z 为模(比`pow(x, y) % z`计算更有效)。 | | `print(*objects, sep=' ', end='\n', file=sys.stdout, flush=False)` | 将对象打印到文本流文件中,用`sep`分隔,后跟`end. sep`,end,file,flush 如果存在,必须作为关键字参数给出。 | | `class property(fget=None, fset=None, fdel=None, doc=None)` | 返回一个属性。 | | `range([stop|[start, stop[, step]]])` | 距离序列。 | | `repr(object)` | 返回包含对象的可打印表示形式的字符串。 | | `reversed(seq)` | 返回一个反向迭代器。 | | `round(number[, ndigits])` | 返回小数点后四舍五入到`ndigits`精度的数字。 | | `class set([iterable])` | 返回一个新的 set 对象,可以选择使用 iterable 中的元素。 | | `setattr(object, name, value)` | 这是 getattr()的对应物。参数是一个对象、一个字符串和一个任意值。 | | `class slice(start, stop[, step])` | 返回一个切片对象,表示由`range(start, stop, step)`指定的一组索引。 | | `sorted(iterable[, key][, reverse])` | 从 iterable 中的项目返回一个新的排序列表。 | | `staticmethod(function)` | 返回函数的静态方法。 | | `class str(object)` | 返回对象字符串版本。 | | `sum(terable[, start])` | 从左到右对 start 和 iterable 的项求和,并返回总和。 | | `super([type[, object-or-type]])` | 返回一个代理对象,该对象将函数调用委托给。 | | `class tuple([iterable])` | 元组序列。 | | `type(object)` | 返回对象的类型。 | | `zip(*iterables)` | 创建一个迭代器,聚合每个可迭代对象的元素。 |

Tip

有关内置函数和类的更多信息,请参见 https://docs.python.org/3/library/functions.html 。请参见 http://docs.micropython.org/en/latest/pyboard/library/builtins.html 了解 MicroPython 中内置函数和类的最新列表。

让我们看一个使用其中一个类的例子——字典。下面展示了我们如何使用内置类来创建一个类型为dict()的变量,并在以后使用它。因为该类是内置功能的一部分,所以它可以在 Python 和 MicroPython 上工作。

>>> my_addr = dict()
>>> print(my_addr)
{}
>>> my_addr['street'] = '123 Main St'
>>> my_addr['city'] = 'Anywhere'
>>> my_addr['zip'] = 90125
>>> print(my_addr)
{'city': 'Anywhere', 'street': '123 Main St', 'zip': 90125}
>>> my_addr = {'street':'201 Cherry Tree Road', 'city':'Gobye', 'zip':12345}
>>> print(my_addr)
{'city': 'Gobye', 'street': '201 Cherry Tree Road', 'zip': 12345}

这里我们看到我们可以使用 dictionary 类来创建该类型的变量。我们可以在第一个print()调用中看到这一点。回想一下,定义字典的语法是一组花括号。接下来,我们使用访问元素的特殊语法将值添加到字典中。最后,我们使用更熟悉的字典语法给变量重新分配一组新的数据。

现在我们来谈谈一个我们没怎么谈过的话题——异常。异常是 Python 内置模块的一部分,可能是您想要使用的一项非常重要的编程技术。也许不是马上,但是最终你会体会到在你的代码中使用异常的力量和方便。

例外

Python(和 MicroPython)中还有一个强大的机制,我们可以使用它来帮助管理或捕获发生错误时的事件,并针对特定错误执行代码。这种结构称为异常,我们可以捕获的异常(错误)称为异常类。它使用一种称为try语句的特殊语法(也称为子句,因为它需要至少一个其他子句才能形成有效的语句)来帮助我们在错误生成时捕获它们。用raise()函数可以在代码的任何地方生成异常。也就是说,如果出现问题,程序员可以“引发”一个特定的、命名的异常,并且可以使用try语句通过except子句来捕获它。表 5-3 显示了 MicroPython 中可用的异常类列表,以及何时(如何)引发异常的简短描述。

表 5-3。

MicroPython Exception Classes

| 异常类 | 使用说明 | | --- | --- | | 断言错误 | assert()语句失败 | | 属性错误 | 属性引用失败 | | 例外 | 基本异常类 | | 导入错误 | 一个或多个模块无法导入 | | 索引错误 | 下标超出范围 | | 键盘中断 | 键盘 CTRL-C 被发出或模拟 | | 键错误 | 字典中的键映射不在键列表中 | | 存储器错误 | 内存不足情况 | | 名称错误 | 局部或全局名称(变量、函数等。)未找到 | | notimplemontederror | 遇到了抽象函数(不完整) | | 操作系统错误 | 来自操作系统的任何系统相关错误 | | 运行时错误 | 执行时可能遇到致命错误 | | 停止迭代 | 迭代器的下一个函数表示可迭代对象中不再有值 | | 句法误差 | 遇到代码语法错误 | | 系统退出 | 调用或模拟了 sys.exit()函数 | | 类型错误 | 函数或操作应用于不适当的类型(如类型不匹配) | | 值错误 | 找到了正确的类型但错误的值(如超出界限) | | 零除法错误 | 数学函数的结果为/ 0 |

try语句的语法如下所示。该结构的每个部分称为一个子句。

try_stmt  ::=  try1_stmt | try2_stmt
try1_stmt ::=  "try" ":" code block
               ("except" [expression ["as" identifier]] ":" code block)+
               ["else" ":" code block]
               ["finally" ":" code block]
try2_stmt ::=  "try" ":" code block
               "finally" ":" code block

注意这里有四个子句:tryexceptelsefinallytry子句是我们放置代码(代码块)的地方——一行或多行代码将包含在异常捕获中。只能有一个tryelsefinally,但是可以有任意数量的except子句来命名一个异常类。事实上,exceptelse一起运行,如果在运行try子句中的任何代码行时检测到异常,它将搜索except子句,当且仅当不满足 except 子句时,它将执行else子句。finally子句用于在所有异常被处理和执行后执行。还要注意该语句有两个版本:一个包含一个或多个except和可选的一个elsefinally,另一个只包含tryfinally子句。

让我们看看使用语句来捕获代码中的错误的一种方法。假设您正在从一批传感器中读取数据,如果读取的值超出范围或无效,这些传感器的库(模块)将引发ValueError。如果一个或多个传感器出现故障,您也可能不想要来自任何其他传感器的数据。因此,我们可以使用如下代码“尝试”读取每个传感器,如果有一个ValueError,发出警告并继续,或者如果遇到其他错误,在读取期间将其标记为错误。注意,通常我们不会在这一点上停止程序;相反,我们通常会记录下来并继续下去。研究以下内容,直到你确信例外很酷。

values = []
print("Start sensor read.")
try:
    values.append(read_sensor(pin11))
    values.append(read_sensor(pin12))
    values.append(read_sensor(pin13))
    values.append(read_sensor(pin17))
    values.append(read_sensor(pin18))
except ValueError as err:
    print("WARNING: One or more sensors valued to read a correct value.", err)
except:
    print("ERROR: fatal error reading sensors.")
finally:
    print("Sensor read complete.")

我们使用异常的另一种方式是当我们想要导入一个模块(库)但不确定它是否存在时。例如,假设有一个名为 piano.py 的模块,它有一个名为 keys()的函数,您希望导入该模块,但是该模块可能在系统上,也可能不在系统上。在这种情况下,我们可以使用其他代码来创建我们自己版本的keys()。为了测试模块是否可以导入,我们可以将导入放在 try 块中,如下所示。然后,我们可以检测导入是否失败,并采取适当的措施。

# Try to import the keys() function from piano. If not present,
# use a simulated version of the keys() function.
try:
    from piano import keys
except ImportError as err:
    print("WARNING:", err)
    def keys():
        return(['A','B','C','D','E','F','G'])
print("Keys:", keys())

如果我们像这样添加代码,而模块不存在,我们不仅可以用警告消息来响应,还可以定义我们自己的函数在模块不存在时使用。

最后,您可以引发任何想要的异常,包括创建自己的异常。创建自定义异常是一个高级主题,但让我们看看如何引发异常,因为如果我们编写自己的自定义库,我们可能希望这样做。假设您有一个正在读取值的代码块,但是值可能会超出范围。也就是说,对于整数来说太大,对于预期值的有效范围来说太小,等等。您可以简单地使用 raise 语句和有效的异常类声明来引发 ValueError,在自定义错误消息中传递,如下所示。

raise ValueError("ERROR: the value read from the sensor ({0}) is not in range.".format(val_read))

然后,您可以使用try语句来捕获这种情况,因为您知道这是可能的,并且您的代码可以绕过它。例如,如果您正在读取数据,您可以选择跳过读取,继续循环。然而,如果在运行您的代码时遇到这个异常,并且没有try语句,您可能会得到一个类似下面的错误,尽管它是致命的,但仍然可以提供信息。

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: ERROR: the value read from the sensor (-12) is not in range.

您可以使用这里所示的类似技术来使您的 MicroPython 代码更加健壮和容错。更好的是,您可以编写代码来预测错误,并以优雅、可控的方式对错误做出反应。

Tip

要更深入地了解 Python 异常,请参见 https://docs.python.org/3/tutorial/errors.html

MicroPython 库

也有专门为 MicroPython 系统构建的库。这些库旨在帮助在硬件上使用 MicroPython。像内置库一样,有一些 MicroPython 库适用于一种或另一种板,而那些库又因板而异。也就是说,库之间存在细微的差异,这使得它们不能在多个板上工作。有关可用的 MicroPython 库的完整列表,请始终查阅您的主板固件文档。

概观

MicroPython 库提供了特定于 Python 的 MicroPython 实现的功能。有些函数库具有直接处理硬件、MicroPython 系统和网络的功能。在接下来的章节中,我们将会看到这些库的一些特性的例子。表 5-4 包含了大多数主板通用的当前 MicroPython 库的列表。第一列是我们在import语句中使用的名称,第二列是简短的描述,第三列包含指向在线文档的链接。

Tip

参见 http://docs.micropython.org/en/latest/pyboard/library/index.html#micropython-specific-libraries 了解更多关于任何 MicroPython 库的细节。

表 5-4。

MicroPython-Specific Libraries

| 名字 | 描述 | 文件 | | --- | --- | --- | | `framebuf` | 帧缓冲 | [`http://docs.micropython.org/en/latest/pyboard/library/framebuf.html`](http://docs.micropython.org/en/latest/pyboard/library/framebuf.html) | | `machine` | 硬件相关功能 | [`http://docs.micropython.org/en/latest/pyboard/library/machine.html`](http://docs.micropython.org/en/latest/pyboard/library/machine.html) | | `micropython` | MicroPython 内部构件 | [`http://docs.micropython.org/en/latest/pyboard/library/micropython.html`](http://docs.micropython.org/en/latest/pyboard/library/micropython.html) | | `network` | 建立关系网 | [`http://docs.micropython.org/en/latest/pyboard/library/network.html`](http://docs.micropython.org/en/latest/pyboard/library/network.html) | | `uctypes` | 访问二进制数据 | [`http://docs.micropython.org/en/latest/pyboard/library/uctypes.html`](http://docs.micropython.org/en/latest/pyboard/library/uctypes.html) |

接下来,我们将查看一些更常见的 MicroPython 库,并查看每个库的一些代码示例。

常见的 MicroPython 库

现在让我们看看一些更常用的 MicroPython 库的例子。接下来的只是你可以用每个库做什么的一个例子。有关所有功能的完整描述,请参见联机文档。同样,不同的主板(固件)上的 MicroPython 库可能略有不同。事实上,Pycom 文档对此表述得非常清楚( https://docs.pycom.io/chapter/firmwareapi/pycom/ ).

)。 These modules are specific to Pycom devices and may be slightly different from other variants of MicroPython (for example, for non-Pycom devices). The modules include modules that support accessing the underlying hardware, such as I2C, SPI, WLAN, Bluetooth, etc.

机器

机器库包含与硬件相关的功能,提供了一个抽象层,您可以编写代码来与硬件交互。因此,这个库是您将用来访问定时器、通信协议、CPU 等特性的主库。由于此功能直接与硬件通信,因此在试验时应小心避免改变甚至潜在地损坏主板的性能或配置。例如,不正确地使用库可能会导致锁定、重新启动或崩溃。

Caution

使用低级机库时要小心,避免改变甚至潜在地损坏主板的性能或配置。

由于机器库是一个低级的硬件抽象,我们在本章中不会深入讨论它。相反,我们将在下一章看到更多的硬件特性。同时,让我们通过向您展示如何通过 help 函数发现一个库包含什么来探索另一个有趣的 MicroPython 知识宝库。例如,清单 5-8 显示了当我们在连接到扩展板的 WiPy 上发出语句help(machine)时,通过 REPL 控制台报告的内容的摘录。help()函数将显示库中所有可用的函数和常量。虽然它不能代替详细的解释,甚至不能代替完整的例子,但在第一次遇到一个库时,它会很有用。

>>> import machine
>>> help(machine)
<module 'umachine'>object  is of type module
  __name__ -- umachine
  mem8 -- <8-bit memory>
  mem16 -- <16-bit memory>
  mem32 -- <32-bit memory>
  reset -- <function>
...
  Pin -- <class 'Pin'>
  UART -- <class 'UART'>
  SPI -- <class 'SPI'>
  I2C -- <class 'I2C'>
  PWM -- <class 'PWM'>
  ADC -- <class 'ADC'>
  DAC -- <class 'DAC'>
  SD -- <class 'SD'>
  Timer -- <class 'Timer'>
  RTC -- <class 'RTC'>
  WDT -- <class 'WDT'>
  PWRON_RESET -- 0
  HARD_RESET -- 1
  WDT_RESET -- 2
...
Listing 5-8.The machine Library Help

注意这里有很多信息!这给我们最多的是我们可以用来与硬件交互的类的列表。这里我们看到有 UART、SPI、I2C、PWM 等类。让我们对比一下 Pyboard 的相同输出。下面显示了 Pyboard 类的相同摘录。

  Pin -- <class 'Pin'>
  Signal -- <class 'Signal'>
  I2C -- <class 'I2C'>
  SPI -- <class 'SPI'>
  UART -- <class 'UART'>
  WDT -- <class 'WDT'>

注意有几个不见了。我们将在下一章探讨这些差异。在你第一次使用的板上检查帮助(机器)的输出总是一个好主意。它可以让你省去很多为不存在的硬件寻找支持的麻烦!

网络

Pyboard 上的network库用于安装网络驱动程序(与网络抽象交互的类)和配置设置。虽然讨论的其他库在不同的电路板上略有不同,但是这个库在 Pyboard 和 WiPy(以及其他)电路板上是非常不同的。事实上,WiPy 有相同的库,但是包含不同的类。

这主要是因为 WiPy 内置了 WiFi,它有自己的网络工作机制。回想一下,我们在第三章中看到了 Pyboard 的网络库,我们将在后面的示例章节中再次看到它的使用。也就是说,我们了解到 Pyboard(以及类似的)板只有两个网络驱动程序:CC3KWINNET5K。WiPy network库包含三个类:WLANBluetoothServer。还记得我们在第三章中看到的 WLAN 类示例。

我们将在下一章看到更多关于网络类和有趣的蓝牙类的内容。

自定义库

构建自己的定制库似乎是一项艰巨的任务,但事实并非如此。可能有点挑战的是弄清楚你想要这个库做什么,并使这个库抽象(足够)到可以被任何脚本使用。编程的规则和最佳实践在这里发挥了作用,比如数据抽象、API 不变性等。

我们在上一章讨论了创建你自己的模块。在这一节中,我们将看看如何将我们的代码模块组织成一个库(包),我们可以将它部署(复制)到我们的 MicroPython 板上,并在我们所有的程序中使用。这个例子虽然简单,但却是一个完整的例子,如果您决定创建自己的定制库,可以将它用作模板。

对于这个例子,我们将创建一个包含两个模块的库:一个包含为传感器执行值转换的代码,另一个包含我们项目的帮助函数——我们希望重用的通用函数。我们将这个库命名为my_helper。它将包含两个代码模块:sensor_convert.pyhelper_functions.py。回想一下,我们还需要一个__init__.py文件来帮助 MicroPython 正确地导入函数,但是我们稍后会回到这个问题上。让我们看看第一个代码模块。

我们将文件放在名为my_helper(与库名相同)的目录中。这是典型的约定,您可以输入您想要的任何名称,但是您必须记住它,因为我们将在代码中导入库时使用该名称。图 5-1 显示了文件的布局示例。这张照片是我的 Pyboard 闪存盘拍的。

A447395_1_En_5_Fig1_HTML.jpg

图 5-1。

Directory/File Layout for the my_helper Sample Library on Pyboard

请注意,我们将包含这三个文件的目录移动(复制)到了我的 Pyboard 中。如果您将文件复制到 WiPy,您将需要使用清单 5-9 中的命令来使用 ftp。还要注意,我们在连接到 WiPy 时创建了目录,并且我们位于包含我们想要复制的文件的目录中。

Note

如果您使用 Linux,您可能需要为 ftp 命令添加-p 选项(被动)。

$ ftp 192.168.4.1
Connected to 192.168.4.1.
220 Micropython FTP Server
Name (192.168.4.1:cbell): micro
Password:
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> cd flash
250
ftp> ls
227 (192,168,4,1,7,232)
150
-rw-rw-r--   1 root  root        34 Jan  1  1980 main.py
drw-rw-r--   1 root  root         0 Jan  1  1980 sys
drw-rw-r--   1 root  root         0 Jan  1  1980 lib
drw-rw-r--   1 root  root         0 Jan  1  1980 cert
-rw-rw-r--   1 root  root       336 Jan  1  2098 boot.py
drw-rw-r--   1 root  root         0 Jan  1  2098 examples
-rw-rw-r--   1 root  root        15 Jan  1  2098 data.bin
-rw-rw-r--   1 root  root       377 Jan  1  2098 my_vehicles.json
226
ftp> mkdir my_helper
250
ftp> cd my_helper
250
ftp> put __init__.py
local: __init__.py remote: __init__.py
227 (192,168,4,1,7,232)
150
100% |***********************************|   255      1.24 MiB/s    00:00 ETA
226
255 bytes sent in 00:00 (0.61 KiB/s)
ftp> put helper_functions.py
local: helper_functions.py remote: helper_functions.py
227 (192,168,4,1,7,232)
150
100% |***********************************|   703      5.49 MiB/s    00:00 ETA
226
703 bytes sent in 00:00 (1.34 KiB/s)
ftp> put sensor_convert.py
local: sensor_convert.py remote: sensor_convert.py
227 (192,168,4,1,7,232)
150
100% |***********************************|   448      4.10 MiB/s    00:00 ETA
226
448 bytes sent in 00:00 (1.27 KiB/s)
Listing 5-9.Using ftp to copy files to the WiPy

现在让我们看看代码。第一个模块被命名为helper_functions.py,包含前面例子中的两个助手函数。因为这些是通用的,所以我们把它们放在这个代码模块中。然而,我们想在我所有的板上使用这个代码。问题是,machine.rng()函数为随机数返回不同的大小。在 Pyboard 上是 30 位,但在 WiPy 上只有 24 位。因此,我们使用一个try语句来检测库函数是否可用,设置一个可以在get_rand()中使用的全局变量来返回正确的值。清单 5-10 显示了模块的完整代码。

# MicroPython for the IOT - Chapter 5
# Example module for the my_helper library
# This module contains helper functions for general use.

try:
    import pyb
    _PYBOARD = True
except ImportError:
    import machine
    _PYBOARD = False

# Get a random number from 0-1 from a 2²⁴ bit random value
# if run on the WiPy, return 0-1 from a 2³⁰ bit random
# value if the Pyboard is used.
def get_rand():
    if _PYBOARD:
        return pyb.rng() / (2 ** 30 - 1)
    return machine.rng() / (2 ** 24 - 1)

# Format the time (epoch) for a better view
def format_time(tm_data):
    # Use a special shortcut to unpack tuple: *tm_data
    return "{0}-{1:0>2}-{2:0>2} {3:0>2}:{4:0>2}:{5:0>2}".format(*tm_data)

Listing 5-10.The helper_functions.py module

第二个代码模块名为 sensor_convert.py,它包含的函数有助于将传感器原始值转换为字符串,以便进行定性比较。例如,函数get_moisture_level()根据原始值的阈值返回一个字符串。传感器的数据手册将定义这些值,除非能够校准传感器,否则应在代码中使用这些值。在这种情况下,如果该值小于下限,则土壤是干燥的;如果大于上限,土壤是湿的。清单 5-11 显示了模块的完整代码。

# MicroPython for the IOT - Chapter 5
# Example module for the my_helper library

# This function converts values read from the sensor to a
# string for use in qualifying the moisture level read.

# Constants - adjust to "tune" your sensor

_UPPER_BOUND = 400
_LOWER_BOUND = 250

def get_moisture_level(raw_value):
    if raw_value <= _LOWER_BOUND:
        return("dry")
    elif raw_value >= _UPPER_BOUND:
        return("wet")
    return("ok")

Listing 5-11.The sensor_convert.py module

现在让我们检查一下__init__.py文件。这是一个非常神秘的文件,开发人员经常对此感到困惑。如果您的库目录中没有包含,您应该手动导入您想要使用的内容。也就是用类似import.my_helper.helper_functions的东西。但是有了这个文件,你可以通过一个简单的import my_helper语句一次性导入所有文件。我们来看一下__init__.py文件。下面显示了该文件的内容。

# Metadata

__name__ = "Chuck's Python Helper Library"
__all__ = ['format_time', 'get_rand', 'get_moisture_level']
# Library-level imports
from my_helper.helper_functions import format_time, get_rand
from my_helper.sensor_convert import get_moisture_level

请注意,在第一行,我们使用了一个特殊的常量来设置库的名称。下一个常量限制了 import 语句的* (all)选项将导入的内容。因为它列出了所有的方法,所以这只是一个练习,但却是一个很好的习惯,特别是当你的库和模块包含许多你不想让其他人使用的内部函数时。最后两行显示了用于从模块导入函数的 import 语句,使导入库的任何人都可以使用这些函数。下面给出了一个简短的例子,说明如何做到这一点以及如何使用别名。这里,我们用myh作为my_ helper的别名。

>>> import my_helper as myh
>>> myh.get_rand()
0.2830396

我们现在可以通过别名访问(使用)所有三个函数,如下所示。

r_int = myh.get_rand() * 10
print(myh.format_time(value))
print(myh.get_moisture_level(sensor_raw))

如果您想知道,帮助功能也可以在这个自定义库上工作!

>>> import my_helper
>>> help(my_helper)
object <module 'Chuck's Python Helper Library' from 'my_helper/__init__.py'> is of type module
  __path__ -- my_helper
  helper_functions -- <module 'my_helper.helper_functions' from 'my_helper/helper_functions.py'>
  __name__ -- Chuck's Python Helper Library
  sensor_convert -- <module 'my_helper.sensor_convert' from 'my_helper/sensor_convert.py'>
  get_rand -- <function get_rand at 0x20004270>
  format_time -- <function format_time at 0x20004300>
  __file__ -- my_helper/__init__.py
  __all__ -- ['format_time', 'get_rand', 'get_moisture_level']
  get_moisture_level -- <function get_moisture_level at 0x20004920>

一旦您开始尝试 MicroPython 并完成了几个项目,您就可以开始构建一组不时重用的函数。这些是放入图书馆的完美候选。如果这些函数不是一个更大的类或对象的一部分,那就再好不过了。只要你把它们组织成功能相似的模块,你就不需要担心把它们变成类。另一方面,如果涉及到数据或者函数集作用于一组数据,那么您应该考虑将函数集作为一个类,以便更容易使用和更高质量的代码。

Wait, What about Circuitpython?

回想一下第三章中,我们在看 Adafruit 的 Circuit Playground 电路板时讨论了 CircuitPython。本章没有更详细地介绍 CircuitPython,因为它是 MicroPython 的一个端口,因此我们对 MicroPython 库的了解也适用。不同的是一些特定于电路板的库和函数,CircuitPython 有一些特定于 Adafruit 电路板的高级库。有关 CircuitPython 的更多信息,请参见 https://circuitpython.readthedocs.io/en/stable/

摘要

MicroPython 固件为 IOT 项目提供了很多功能。事实上,我们可以使用许多不同的类,从内置函数编写健壮而复杂的 MicroPython 程序,这些内置函数为语言提供了处理数据、执行计算、甚至处理时间值以及直接与硬件接口的广泛能力。

使用硬件是 MicroPython 与其他主板最大的不同。这是因为这些板非常不同。有些有网络,有些没有。有些芯片的板载特性比其他芯片更多,有些芯片的内存更少,GPIO 引脚甚至更少。因此,当涉及到硬件抽象层时,不同主板的固件会有所不同也就不足为奇了。

在这一章中,我们探讨了一些更常用的内置库和 MicroPython 库,它们通常适用于所有的主板。在下一章中,我们将更深入地探讨 MicroPython 中的底层库和硬件支持,包括我们可以使用的特定于主板的库,如 Pyboard、WiPy 等。

Footnotes 1

此外,请记住这个时间间隔——称为采样率——也必须对项目有意义。在受控气候中每秒钟对环境温度采样 30 次可能会产生大量无用的数据,因为它很少变化。

六、低级硬件支持

对于所有支持的通用 Python 语言和许多内置函数,MicroPython 固件在最基本的功能上是相同的。然而,MicroPython 固件中的一些库在不同的主板上有一些细微的差别。在某些情况下,可用的库或类比其他的多,或者类的组织方式不同,但大多数都以某种形式实现了相同的核心库。在较低级别的硬件抽象层,情况就不一样了。这仅仅是因为一个主板供应商可能实现不同的硬件。在某些情况下,该板具有其它板上不存在的特性。例如,WiPy 有 WiFi 和蓝牙,而 Pyboard 两者都没有。

在这一章中,我们将看几个 MicroPython 中低级硬件支持的例子。我们将了解特定于主板的库以及一些低级别的专用库,如蓝牙、SPI、I2C 等库。我们还将看到一些简短的代码示例来说明特定于主板的库的功能。其中一些将是简短的代码片段,而不是您可以自己实现的实际项目。

但是,有几个是完整的项目,您可能想探索一下,但是大多数都没有深入的解释,只是作为例子,而不是详细的演练。此外,请记住,它们可能需要特定的分线板和 MicroPython 板以及其他附件来实现。同样,这些只是为了演示的目的。我们将在后面的章节中看到更完整的、循序渐进的例子,包括如何组装硬件。

为了保持简洁,我们将探索 Pyboard 和 WiPy 上不同的板专用库。其他电路板可能会有进一步的不同,但您需要查看供应商文档,以了解它们之间的差异。本章将为你提供发现这些差异的洞察力。我们还将重温使用分线板来演示前几章中讨论的一些库和硬件协议和技术。

让我们先来看一下 Pyboard 和 WiPy 的板专用库。

特定于主板的库

我们已经看到 Pyboard 和 WiPy 的机器库中存在差异。但是还有其他的不同:特定于每块板的库,因此只能用于同一块板的另一块板上。这些包括具有用于低级硬件的函数和类的库、特定于电路板的函数和常数等。

第一次使用新的 MicroPython 板时,您应该记住的一件事是,硬件级别的固件很可能与您使用的上一个 MicroPython 板不同。当您查看 BBC micro:bit、Circuit Playground、ESP8266 等板的固件端口时,情况尤其如此。

Tip

请务必查阅您的主板文档,以获得主板专用库的完整功能、用途和更多示例。

Pyboard

特定于 Pyboard 的库是独一无二的,因为它们包含对特定于供应商的皮肤的支持。正如我们在第三章中了解到的,Pyboard 有几种皮肤可供使用。幸运的是,固件中的 Pyboard 特定类直接支持所有的皮肤。这包括音频皮肤、LCD 等。

还有一些特定于 Pyboard 本身的库,如pyb库,它支持一些特定于电路板的功能,包括时间、电源、中断等。我们还将简单地浏览一下这个库,然后看看 LCD 皮肤的一个库:lcd160cr类。

下面几节将更详细地介绍这些库,并包括一些常见的功能示例。有关每个库的内容和功能的完整详细信息,请参见指定的文档。

标准型

pyb库是 Pyboard 相关函数和类的总括库。如果您正在寻找与 Pyboard 或其任何板载硬件相关的函数或库,这个库是您应该首先寻找的地方。下面列出了可用的函数组和类。

  • 时间:与时间相关的函数,以毫秒或微秒为单位延迟,并计算事件发生后的毫秒或微秒数(保存的变量)
  • 重置:允许您打开调试、启动引导加载程序模式或重置主板的功能
  • 中断:启用或禁用中断
  • 电源:使主板进入睡眠或深度睡眠模式并改变性能特征的功能(轻踩)
  • 杂项:信息、控制 UART、USB 和安装块设备的各种功能

现在让我们看几个简短的例子。前两个展示了复位函数的例子,后两个是杂项函数,最后一个展示了如何使用其中一个类与板上的硬件进行交互。

您可以使用pyb.hard_reset()功能执行硬复位,如下所示。如果您在 REPL 控制台中输入此语句,板将以与您按下重置按钮相同的方式重置。如果您需要从严重错误或硬件故障中中止,这可能很方便。

MicroPython v1.9.1-154-g4d55d880 on 2017-07-11; PYBv1.1 with STM32F405RG
Type "help()" for more information.
>>> import pyb
>>> pyb.hard_reset()

Note

hard_reset()功能可能会导致您的电脑抱怨 SD 卡在没有停止文件系统的情况下被弹出,因此请谨慎使用该功能。

pyb.bootloader()功能将板卡置于引导加载模式。回想一下第三章中的,要安装固件,必须先关闭主板电源,安装跳线,然后打开主板电源,才能加载固件。通过此功能,您可以从 REPL 控制台执行此操作。也就是说,如果你能拿到 REPL 控制台的话。如果您的主板损坏或固件损坏,您仍然可以执行跳线过程,将主板置于引导加载程序模式以加载新固件。

pyb.info()函数被用作信息转储。它打印出大量关于电路板的底层信息,包括硬件地址等等。除非您正在编写低级驱动程序或类似的东西,否则大多数都没有用,但是您可以将 True 传递给该函数,并以 GC 内存布局转储的形式获得更多信息,如下面的摘录所示。如果您对这些数据感到好奇,请参阅在线参考手册中的 Pyboard 硬件规格部分或forum.micropython.org论坛。

>>> pyb.info(True)
...
LFS free: 52224 bytes
GC memory layout; from 20003e40:
00000: h=hhhhhBShh==Sh=hhhh==h==Bh=hBh=hBhThShh=h==hh=BhhhBhBh=hh=hBh=h
00400: =hTh==Shhhh==B........h=....................................h===
00800: ====h===========================================================
00c00: ==============..................................................
       (92 lines all free)
18000: ............................

pyb.main(filename)函数是最有用的函数之一。它设置 boot.py 完成后运行的主脚本的文件名。您可以将该功能与几个可选的main.py代码模块结合使用。如果您想实现一些自动启动和运行的项目,这可能是一个有用的工具。您可以使用这个函数告诉 Pyboard 从一个替代文件开始,而不是手动进入boot.py并改变它。这样,你就可以有“配置文件”来改变董事会的行为。酷。只需用有效的代码模块路径和名称(作为字符串)调用函数。在您更改之前,此设置一直有效。

Note

您应该只从boot.py内部调用该函数。

pyb库也为支持的硬件主机提供了低级硬件类。注意,我们在机器库中看到了 WiPy 的一组类似的类。在 Pyboard 上,它们在pyb库中。表 6-1 显示了可用硬件类别的列表。如您所见,它支持 led、LCD(我们将在后面的章节中看到)、GPIO 引脚等等。记住类名是区分大小写的。

表 6-1。

Pyboard Low-Level Hardware Classes (pyb library)

| 班级 | 描述 | | --- | --- | | 加速的 | 加速度计控制 | | 物理输出核心 | 模数转换 | | 能 | 控制器局域网通信总线 | | 数(字)-模(拟)转换器 | 数模转换 | | 灭绝 | 将 I/O 引脚配置为外部事件中断 | | I2C | 双线串行协议 | | 液晶显示 | LCD 触摸传感器 pyskin 的 LCD 控制 | | 发光二极管 | 发光二极管物体 | | 别针 | 控制 I/O 引脚 | | 皮纳夫 | 引脚替代功能 | | 雷达跟踪中心(Radar Tracking Centre 的缩写) | 实时时钟 | | 伺服系统 | 三线业余伺服驱动器 | | 精力 | 主机驱动的串行协议 | | 转换 | 切换对象 | | 计时器 | 控制内部定时器 | | 时间通道 | 为计时器设置通道 | | 通用非同步收发传输器(Universal Asynchronous Receiver/Transmitter) | 双工串行通信总线 | | USB_HID | USB 人机接口设备(HID) | | USB_VCP | USB 虚拟通信端口 |

现在,让我们来看看其中的一个。我们将使用加速度计对电路板进行简单测试。也就是说,我们将编写一些代码,您可以使用这些代码来运行和检测 Pyboard 何时在三个方向上发生了物理移动。下面显示了可用于Accel类的方法。

  • Accel.filtered_xyz():获取一个过滤后的 x、y 和 z 值的三元组
  • Accel.tilt():获取倾斜寄存器
  • Accel.x():获取 x 轴数值
  • Accel.y():获取 y 轴值
  • Accel.z():获取 z 轴值

加速度计是一种用于测量速度随时间变化的设备:换句话说,就是物体移动的方式和速度。虽然 Pyboard 上的加速度计不是一种能够超精细检测微小运动的精密仪器,但您可以使用它来检测电路板何时移动,甚至是有限程度的移动方向(移动轴)。因此,如果您想知道项目何时因安全原因移动,或者想检测或更改基于方向的行为,可以将加速度计集成到项目中。有很多很酷的方式可以使用加速度计!

现在,让我们看一些代码!下面显示了如何使用加速度计获得 X、Y 和 Z 轴的原始值。值的范围从-32 到 32。只要稍加观察,我们就可以推断出哪些值表示棋盘在每个方向上移动了多远。尝试下面的代码,轻轻地拿起棋盘,通过向不同的方向旋转,在三维空间内移动它。慢慢地做,然后稍微快一点,注意数值是如何变化的。然后,将板放回原来的位置并观察。您可以按CTRL-C停止循环。

import pyb
from pyb import Accel
acc = Accel()
print("Press CTRL-C to stop.")
while True:
    pyb.delay(500)    # Short delay before sampling
    print("X = {0:03} Y = {1:03} Z = {2:03}".format(acc.x(), acc.y(), acc.z()))

Use Soft Reset to Refresh Import

如果您像我一样,喜欢在 PC 上编写 MicroPython 代码并在那里进行测试,然后将其移动到板上进行更多的开发和测试,那么您可能会感到困惑,因为每次您复制想要包含和测试的代码模块或库的新版本时,都必须重置板。当连接到 REPL 控制台时,您可以使用CTRL+D键进行软复位,而不是关闭电路板电源或进行硬复位。这将执行软重启,允许您再次运行导入。事实上,您必须再次发出import——它刚刚重启了!最好的部分是,你将不必重新连接你的董事会或 REPL 控制台!试试看。

下面显示了在 REPL 控制台中运行时的输出摘录。

X = 002 Y = 000 Z = 021
X = 000 Y = -01 Z = 023
X = 005 Y = 020 Z = 000
X = -05 Y = 016 Z = -11
X = -11 Y = -13 Z = 014
X = 022 Y = -04 Z = -05
X = -03 Y = 019 Z = 012
...

如果你喜欢挑战,你可以试着写一个脚本来使用加速度计检测电路板何时被颠倒。提示:在实验和编写代码时观察 X、Y 和 Z 的原始值,以寻找特定的范围。

Caution

在 Pyboard 上,加速度计使用 I2C(1),因此您不能同时使用加速度计和 I2C(1)。I2C 必须使用不同的引脚才能同时使用加速度计和 I2C 设备。

这些例子只是这个库中可用内容的一小部分。如果您正在使用 Pyboard,您应该首先查看这个库,以满足您所有的低级硬件和特定于电路板的控制需求。我们将在后面的章节中看到一些可用类的例子以及它们的用法。

Tip

参见 http://docs.micropython.org/en/latest/pyboard/library/pyb.html 获取 pyb 库中可用函数和类的完整列表。

液晶显示器 160cr

Pyboard 的制造商制作了一个有趣的皮肤,到目前为止,在提供 MicroPython 板的供应商中是独一无二的。这种皮肤称为 lcd160cr 因此,LCD 是一个触摸感应 LCD 屏幕,您可以直接将其连接到 Pyboard,为您提供一个非常漂亮的触摸屏,您可以用它来制作一个现代化的用户界面。想想看——你可以制作一个 MicroPython 手表,天气传感器,或者任何需要用户界面的东西。

Pyboard 固件中内置的库(名为lcd160cr)允许你感知屏幕被触摸的时间(和位置),你可以发送文本或绘制形状。这意味着,只要稍加想象,您就可以构建简单的图形用户界面。非常好。

LCD 可以安装在两个位置之一,称为位置 X 和 y。然后在构造函数中使用这些值。图 6-1 显示了在 X 和 Y 位置(从左到右显示)连接 LCD 和 Pyboard 的位置。

A447395_1_En_6_Fig1_HTML.jpg

图 6-1。

Positions for Mounting the LCD (Pyboard)

位置差异指的是液晶屏在主板上的方向。在 X 位置,LCD 安装在 X 范围的 GPIO 引脚上,在 Y 位置,它安装在 Y 范围的 GPIO 引脚上。请注意,在 Y 位置,LCD 必须旋转 180 度,如图所示。

现在,让我们来看一个使用 LCD 的例子。在这个例子中,我们将创建一个简单的用户界面来检测屏幕四角的触摸。为了让它更有趣,我们还会在每次触摸时打开不同的 LED。这将给你一个视觉反馈,告诉你已经触摸了屏幕。

这个例子是本书中较长的例子之一,但不难理解。让我们从代码的高级演练开始。

我们应该做的第一件事是编写import语句,这样我们就可以检测代码何时在 Pyboard(或另一个板)上运行。如果它不是 Pyboard,我们应该中止,因为其他板没有我们需要的库(lcd160cr)。如果您在自己的代码中采用这种技术,您可以避免奇怪的导入异常和其他可能不清楚程序失败原因的问题。另外,这是很好的编程。下面显示了用于检测所需库是否存在的代码,以及如何在其中一个库无法导入时退出程序。

# First, make sure this is running on a Pyboard
try:
    import pyb
except ImportError:
    print("ERROR: not on a Pyboard!")
    sys.exit(-1)

# Next, make sure the LCD skin library in the firmware

try:
    import lcd160cr
except ImportError:
    print("ERROR: LCD160CR library missing!")
    sys.exit(-1)

接下来,我们将编写一个函数来打开 LED。这是一个如何编写可重用代码的例子。也就是说,我们不想重复的重复同样的代码。在这种情况下,我们可以按颜色打开特定的 LED。为了帮助我们做到这一点,我们可以编写一个函数来检索基于颜色的 LED(1 =红色,2 =绿色,等等)。).下面显示了 helper 函数。

def led_color(num):
    if num == 1: return "red"
    elif num == 2: return "green"
    elif num == 3: return "orange"
    else: return "blue"

以下显示了打开 LED 的可重复使用的功能。如您所见,我们首先关闭旧的 LED,然后打开新的(选定的)LED。

def turn_on_led(num, led_last):
    # turn last LED off
    led = pyb.LED(led_last)
    led.off()
    led = pyb.LED(num)
    led.on()
    sys.stdout.write("Turning off ")
    sys.stdout.write(led_color(led_last))
    sys.stdout.write(" - Turning on ")
    sys.stdout.write(led_color(num))
    sys.stdout.write("\n")

接下来,我们可以编写代码来读取 LCD 上被触摸的位置,并根据触摸发生的位置,打开该角的 LED。我们将用红色表示左上角,绿色表示右上角,橙色表示右下角,蓝色表示右下角。

由于pyb.LED()用数字指代发光二极管,因此使用数字来简化代码。精明的读者可能会发现一种通过枚举甚至使用常量来改进代码的方法。如果你看到这些潜在的改进,请随意把这些改进作为练习。清单 6-1 展示了这个例子的完整代码。

# MicroPython for the IOT - Chapter 6
# Example module for the LCD skin on a Pyboard
#
# Note: The LCD is positioned in the "X" position.
#
import sys

# First, make sure this is running on a Pyboard
try:
    import pyb
except ImportError:
    print("ERROR: not on a Pyboard!")
    sys.exit(-1)

# Next, make sure the LCD skin library in the firmware
try:
    import lcd160cr
except ImportError:
    print("ERROR: LCD160CR library missing!")
    sys.exit(-1)

# Return color of LED
def led_color(num):
    if num == 1: return "red"
    elif num == 2: return "green"
    elif num == 3: return "orange"
    else: return "blue"

# Use a method to turn off last LED and the next one on
def turn_on_led(num, led_last):
    # turn last LED off
    led = pyb.LED(led_last)
    led.off()
    led = pyb.LED(num)
    led.on()
    sys.stdout.write("Turning off ")
    sys.stdout.write(led_color(led_last))
    sys.stdout.write(" - Turning on ")
    sys.stdout.write(led_color(num))
    sys.stdout.write("\n")

# Setup the LCD in the "X" position
lcd = lcd160cr.LCD160CR('X')

for j in range(1, 4):   # Turn off all of the LEDs
    led = pyb.LED(j)    # Get the LED
    led.off()           # Turn the LED off

# Now, let's play a game. Let's change the LEDs to
# different colors depending on where the user touches
# the screen.
print("Welcome to the touch screen demo!")
print("Touch the screen in the corners to change the LED lit.")
print("Touch the center to exit.")
center = False
last_led = 1
while not center:
    pyb.delay(50)
    if lcd.is_touched:
        touch = lcd.get_touch()
        if (touch[0] == 0):
            continue
        # Upper-left corner
        if ((touch[1] <= 60) and (touch[2] <= 60)):
            turn_on_led(1, last_led)
            last_led = 1
        # Upper-right corner
        elif ((touch[1] >= 100) and (touch[2] <= 60)):
            turn_on_led(2, last_led)
            last_led = 2
        # Lower-right corner
        elif ((touch[1] >= 100) and (touch[2] >= 100)):
            turn_on_led(3, last_led)
            last_led = 3
        # Lower-left corner
        elif ((touch[1] <= 60) and (touch[2] >= 100)):
            turn_on_led(4, last_led)
            last_led = 4
        # Center
        elif ((touch[1] > 60) and (touch[1] < 100) and (touch[2] > 60) and (touch[2] < 100)):
            led = pyb.LED(last_led)
            led.off()
            center = True

print("Thanks for playing!")
sys.exit(0)

Listing 6-1.Using the Pyboard LCD (lcd160cr)

如果你有液晶显示器,继续尝试这个例子。一旦你这样做,你应该看到发光二极管灯,因为你触摸每个角落,你会看到在你的 REPL 控制台输出如下。

>>> import pyboard_lcd
Welcome to the touch screen demo!
Touch the screen in the corners to change the LED lit.
Touch the center to exit.
Turning off red - Turning on green
Turning off green - Turning on red
Turning off red - Turning on red
Turning off red - Turning on orange
Turning off orange - Turning on green
Turning off green - Turning on green
Turning off green - Turning on orange
Turning off orange - Turning on blue
Turning off blue - Turning on red
Thanks for playing!

Tip

液晶屏上有一层薄薄的保护膜。您需要删除它,并使用手写笔或类似的软指针来测试脚本。如果你的手指很大,触摸指定的小区域可能会有一些困难。

如果你决定自己尝试这个例子,你会发现它给了你一些实现一个有趣的目标的满足感。如果你喜欢这个例子,并且想为你自己的项目得到一个 LCD,请到 MicroPython 商店订购一个( https://store.micropython.org/store/#/products/LCD160CRv1_0H )。你会发现你可以买一个有标题或没有。如果你决定购买漂亮的(可选)铝制外壳,你可以选择不带外壳的,这需要安装不同的、有角度的头部(你必须自己订购和焊接)。但是,您可以使用带有标题的 LCD,如下例所示。

Tip

参见 http://micropython.org/resources/LCD160CRv10-refmanual.pdf 了解 lcd160cr 的参考手册,其中包括使用皮肤的低级规范。

WiPy

特定于 WiPy 的库是独一无二的,因为它们支持完整的 Pycom 板,包括扩展板、PySense 和 PyTrack shields。

还有一些特定于 Pyboard 本身的库,以pycom库的形式出现,其中包括支持更改心跳 LED,包括更改颜色。AES库包含一个保护数据的有趣特性——AES 加密。我们将更详细地探索这个库。此外,回想一下,WiPy 固件包含 I2C、SPI 等底层硬件。与 Pyboard 相反,py board 在pyb库中有这些。

下面几节将更详细地介绍这些库,包括一些常见的功能示例。有关每个库的内容和功能的完整详细信息,请参见指定的文档。

派克姆

pycom库具有控制 Pycom 设备特定功能的功能,例如心跳 RGB LED。事实上,您可以改变 LED 的颜色,打开或关闭它,并获得 LED 的当前状态。虽然这看起来非常初级,但您可以使用心跳 LED 来传达状态信息。例如,您可以更改颜色来指示不同的操作(或状态),如从传感器读取、保存数据、传达错误等。

让我们看一个使用心跳 LED 设置状态的简短示例。请注意,LED 颜色由代表红、绿、蓝(RGB)的 24 位值定义,其中红色由最高的 8 位表示,绿色接下来是 8 位,蓝色是最低的 8 位。我们通常用十六进制表示这些值。 1 比如亮白色是 0xFFFFFF,0xFFFF00 是黄色,0x00FFFF 是海绿色。

在本例中,我们将使用一组辅助函数和一个测试循环来处理心跳 LED。注意,要打开(使用)心跳 LED,必须先用heartbeat(False)关闭心跳内部功能。清单 6-2 展示了使用心跳 LED 显示状态的示例代码。

# MicroPython for the IOT - Chapter 6
# Example for working with the hearbeat LED as a
# status/state indicator
#
import utime

# First, make sure this is running on a Pyboard
try:
    import pycom
except ImportError:
    print("ERROR: not on a WiPy (or Pycom) board!")
    sys.exit(-1)

# State/status enumeration
_STATES = {
    'ERROR'   : 0xFF0000, # Bright red
    'READING' : 0x00FF00, # Green
    'WRITING' : 0x0000FF, # Blue
    'OK'      : 0xFF33FF, # Pinkish
}

# Clear the state (return to normal operation)
def clear_status():
    pycom.heartbeat(True)

# Show state/status with the heatbeat LED
def show_status(state):
    pycom.heartbeat(False)
    pycom.rgbled(state)

# Now, demonstrate the state changes
for state in _STATES.keys():
    show_status(_STATES[state])
    utime.sleep(3)

# Return heartbeat to normal
clear_status()

Listing 6-2.Using the Heartbeat LED for Status/State (WiPy)

如果您运行这段代码,您会注意到 LED 会呈现一些非常明亮的颜色。所以,不要直接盯着领导!你可以尝试使用一些更微妙的颜色值来降低亮度。

Tip

参见在线计算器 http://www.rapidtables.com/web/color/RGB_Color.htm 确定所需颜色。

俄歇电子能谱

WiPy 有一个名为crypto的特殊库,它有支持 AES(高级加密标准)的AES库,AES 是由 NIST 标准化的对称分组密码。虽然您可能不需要这个特性,但是对于我们这些关心保护敏感数据的人来说,这可能是一个值得考虑的漂亮特性。

例如,您可能希望通过互联网将数据传输到另一个系统。如果您没有用加密来保护数据,那么如果数据以某种方式被利用,解密数据并不太困难。如果您加密数据(并保护密钥!),你可以让人们更难看到数据。

Note

加密是一项高级功能,大多数初学者不会使用。因此,这一节很简短。如果你想了解更多关于这个库的信息,请看 https://docs.pycom.io/chapter/firmwareapi/pycom/aes.html

AES 库提供了几个常量来控制加密模式和两个函数:一个用来加密,另一个用来解密。您需要提供一个密钥,用于由常量AES.MODE_ECB定义的最简单加密形式的加密算法,该常量表示电子代码簿。其他表单可能需要附加参数。

有一个警告。您加密的数据必须是 16 字节块的倍数。注意我们是如何使用空格使字符串变成 16 字节的。因此,如果您要加密从传感器或网络上的其他节点创建或读取的数据,需要确保使用 16 字节倍数的块进行加密。

让我们看一个例子。在本例中,我们将创建一个包含加密数据的文件,以展示如何保护您的数据,以便保存或发送到另一个节点(计算机)。然后,我们将打开文件并读取解密数据,这样我们就可以展示如何解密加密数据。我们将使用字符串使它更容易,但你也可以加密二进制数据。清单 6-3 显示了使用 AES 加密保护数据的示例代码。

# MicroPython for the IOT - Chapter 6
# Simple example for working with encrypting data
#
import sys

# First, make sure this is running on a WiPy (pycom)
try:
    import pycom
    from crypto import AES
except ImportError:
    print("ERROR: not on a WiPy (or Pycom) board!")
    sys.exit(-1)

# Setup encryption using simple, basic encryption
# NOTICE: you normally would protect this key!
my_key = b'monkeybreadyummy' # 128 bit (16 bytes) key
cipher = AES(my_key, AES.MODE_ECB)

# Create the file and encrypt the data
new_file = open("secret_log.txt", "w")    # use "write" mode
new_file.write(cipher.encrypt("1,apples,2.5   \n"))   # write some data
new_file.write(cipher.encrypt("2,oranges,1    \n"))   # write some data
new_file.write(cipher.encrypt("3,peaches,3    \n"))   # write some data
new_file.write(cipher.encrypt("4,grapes,21    \n"))   # write some data
new_file.close()  # close the file

# Step 2: Open the file and read data
old_file = open("secret_log.txt", "r")  # use "read" mode
# Use a loop to read all rows in the file
for row in old_file.readlines():
    data = cipher.decrypt(row).decode('ascii')
    columns = data.strip("\n").split(",") # split row by commas
    print(" : ".join(columns))  # print the row with colon separator

old_file.close()

Listing 6-3.Using Encryption to Protect Data (WiPy)

代码很简单,你应该没有问题。但是,请注意,我们结合了 write 方法和 encrypt 调用,使事情变得更简短。如果你愿意,你可以用两个语句加上一个中间变量。

当这个程序运行时,它会在 WiPy 的闪存驱动器上创建一个名为 secret_log.txt 的文件。如果我们用十六进制转储实用程序检查这个文件,我们可以看到它确实被加密了(我们无法读取数据,因为它被打乱了)。下面显示了文件在十六进制转储中的样子。

$ hexdump -C secret_log.txt
00000000  c1 02 97 87 74 28 4f 4e  de 83 8d 8d 49 4a f8 93  |....t(ON....IJ..|
00000010  c9 e7 f8 00 f3 ba e2 f8  7c 6e ca 41 13 0c 09 35  |........|n.A...5|
00000020  a6 83 f6 fc 2c de ba eb  f6 3a af fe c0 b5 c6 ee  |....,....:......|
00000030  7a 3b 3a 36 90 da dc 36  3d 61 7e 31 75 a3 ca 96  |z;:6...6=a∼1u...|
00000040

该代码还打印出从文件中读取的字符串,以确认其工作正常。如果您通过 REPL 控制台运行该程序,您应该会看到以下输出。

>>> import wipy_encryption
1 : apples : 2.5
2 : oranges : 1
3 : peaches : 3
4 : grapes : 21

既然我们已经看到了几个板专用库的例子,让我们来看看几个直接使用硬件特性的例子。我们在前面的例子中已经看到了一些,但是没有详细解释。下面几节将深入探讨一些使用 Pyboard 和 WiPy 的更常见的低级硬件访问。

低级示例

使用底层硬件(有些人会说是“硬件”或“设备”)是使用 MicroPython 的所有动作和焦点(以及相对难度)发生的地方。MicroPython 和分线板供应商在让事情变得更容易方面做得非常出色,但是在解释方面还有改进的空间。

也就是说,当提供使用低级硬件的例子时,在线文档有点简洁。部分原因是这些例子通常需要额外的特定硬件。例如,要使用 I2C 接口,您需要一个支持 I2C 的分线板。因此,在线示例仅提供了最基本的示例和解释。

然而,这并不意味着缺少底层硬件的其他文档。事实上,该文档提供了您可以使用的所有类和函数的极好概述。接下来的挑战是利用这个概述并应用它来解决你的编程需求,这也是本书的目的之一!

除了可能存在的车载传感器或其他组件,如 led、RTC、按钮等。大多数低级通信将通过 I2C、单线、模拟或数字引脚,甚至 SPI 接口。I2C 和 SPI 接口是您在使用硬件时可能会遇到最大困难的接口。这是因为您使用的每个设备(分线板)都需要一个非常具体的协议。也就是说,该设备可能需要特殊的序列来触发该设备的不同于其他分支板的传感器或特征。因此,使用 I2C 或 SPI(以及其他一些)类型的设备可能是一个挑战,要弄清楚究竟如何与它们“交谈”。

司机和图书馆来救援!

幸运的是,有一小部分人正在制作帮助我们使用这些设备的类和函数集,但是人数在不断增加。这些称为库或更常见的驱动程序,以一个或多个代码模块的形式出现,您可以下载、复制到您的主板,并将功能导入到您的程序中。驱动程序的开发者已经为你做了所有繁重的工作,让你很容易使用这个设备。

因此,对于大多数刚开始使用 MicroPython 的人来说,他们希望使用某些传感器、设备、分线板等。,您应该将您计划使用的内容限制在那些您可以找到与之一起工作的驱动程序的范围内。那么,如何为你的设备找到一个驱动程序呢?有几个地方可以看看。

首先,您应该查看 MicroPython 上的论坛和文档。在这种情况下,不要只局限于那些迎合你的选择的论坛。相反,看看他们所有人!很有可能,您可以找到一个只需稍加修改就能适应的库。除了下载并复制到板上,大多数都可以轻松使用。下面列出了在寻找驱动程序时应该经常访问的论坛和文档。

注意最后一项。如果您搜索 MicroPython,您会发现许多精彩的(完整的)教程,包括越来越多的硬件主题。

第二,使用你最喜欢的互联网搜索引擎,搜索硬件的例子。在搜索中使用硬件设备的名称和“MicroPython”。如果设备是新的,你可能找不到任何搜索条件。一定要探索其他搜索词。

一旦你找到了司机,乐趣就开始了!您应该下载驱动程序,并将其复制到您的主板上进行测试。请务必遵循驱动程序附带的示例,以避免以意外的方式使用驱动程序。

这让我想起了在决定是否使用这个驱动程序时应该考虑的一件重要事情。如果驱动程序有很好的文档记录,并且有例子,特别是如果例子包括您的主板,您应该可以放心使用它。但是,如果驱动程序根本没有文档,或者没有或只有很少的示例代码,就不要使用它!很有可能它是半生不熟的、陈旧的、正在进行的工作,或者只是糟糕的编码。不是所有分享的人都能很好的分享和交流。

让我们看两个低级的例子:使用实时时钟和通过中断回调。我们将从实时时钟(RTC)开始。这些只是可用的一小部分样本,代表了你在本书和大多数小型 IOT 项目中需要用到的最常见的东西。

实时时钟(RTC)

大多数电路板都有实时时钟(RTC)。RTC 是一种特殊的计时电路(有时是集成电路或芯片)。这是因为大多数处理器(微控制器、微处理器等。)与保持处理器以某一速度(例如,Mhz)运行的晶体或时钟芯片同步操作。遗憾的是,这通常不容易划分为一个时间变量(值)。RTC 是用来记录时间的,这样我们就可以用时间来记录事件。

要使用 RTC,我们首先用当前的日期和时间初始化起始值(就像设置一个新的桌面时钟一样),我们可以随时读取当前的日期和时间。然而,当板断电时,没有备用电池的 RTC 将丢失其值。因此,我们必须在每次启动电路板时设置它。幸运的是,互联网上有一个时间服务,我们可以用它来获取当前的日期和时间。它被称为网络时间协议(NTP)。 2

让我们看一个如何使用这个服务的例子。我们将在 WiPy 上创建一个程序,将评估板连接到我们的本地 WiFi,WiFi 已连接到互联网。一旦连接上,我们将使用 NTP 来设置当前时间,然后执行一个测试来查看当前的日期和时间。我们应该在运行代码时看到确切的日期和时间!清单 6-4 展示了完整的例子。

# MicroPython for the IOT - Chapter 6
# Example module for using the ntptime server to set the datetime
# Note: only works on WiPy!

from network import WLAN
from machine import RTC
import machine
import sys
import utime

wlan = WLAN(mode=WLAN.STA)

def connect():
    wifi_networks = wlan.scan()
    for wifi in wifi_networks:
        if wifi.ssid == "YOUR_SSID":
            wlan.connect(wifi.ssid, auth=(wifi.sec, "YOUR_SSID_PASSWORD"), timeout=5000)
            while not wlan.isconnected():
                machine.idle() # save power while waiting
            print("Connected!")
            return True
    if not wlan.isconnected():
        print("ERROR: Cannot connect! Exiting...")
        return False

if not connect():
    sys.exit(-1)

# Now, setup the RTC with the NTP service.
rtc = RTC()
print("Time before sync:", rtc.now())
rtc.ntp_sync("pool.ntp.org")
while not rtc.synced():
    utime.sleep(1)
    print("waiting for NTP server...")
print("Time after sync:", rtc.now())

Listing 6-4.Using an NTP Time Server to set the RTC (WiPy)

这些代码的大部分应该是熟悉的,因为我们在前一章已经看到了 WiFi 连接。在这个例子中,我们将代码放在一个方法中,使它更容易使用。 3 但是,RTC()类的用法是新的。注意,一旦建立了网络连接,我们需要做的就是调用ntp_sync()方法,以 NTP 服务的名义传递。是的,它内置在图书馆里!酷。之后,我们只需要等到 RTC 与 NTP 同步,就可以开始了。

Note

Pyboard 在机器库中提供了RTC()类,但它与 WiPy 固件中的略有不同。您可以修改这个示例,作为在 Pyboard 上使用的练习。

当您在 WiPy 上运行它时,您将看到如下输出。注意,我们打印时间值——这是开始时间——然后在 RTC 与 NTP 同步后再次打印时间。

>>> import wipy_ntp
Connected!
Time before sync: (1970, 1, 1, 0, 0, 36, 560190, None)
waiting for NTP server...
waiting for NTP server...
waiting for NTP server...
Time after sync: (2017, 7, 13, 16, 19, 51, 402976, None)

这个例子非常有帮助,在某些情况下,在读取数据以进行后续分析时是必须的。知道数据何时保存或传感器何时被读取通常是至关重要的。您可能希望将此代码标记为以后在您的 IOT 项目中使用。

Note

我们可以使用具有板载时钟的专用 RTC 模块,以便在离线操作或板断电期间保持时钟同步。我们将在第八章中看到如何做到这一点。

现在,让我们来看看回调,这是一种可以用来处理硬件中断的编程机制。

回收

如果你想执行一些代码来响应传感器或用户输入,你会怎么做?利用我们到目前为止所学的知识,我们可以编写一个循环程序来轮询传感器或用户可操作的设备(如按钮),并在被触发时执行代码。这种轮询技术是可行的,但是有一种更好的结构叫做回调。

回调是我们定义的函数,当某个事件发生时,它与固件相关联来执行。如果硬件抽象允许使用回调,我们就可以使用它。好在 Pyboard 固件(pyb.Switch)中的 Switch 类就有这样的机制。我们也可以用同样的方式使用硬件中断。然而,硬件中断是一个高级话题。让我们使用Switch类来使事情变得更简单。

回调的使用允许我们继续执行代码来完成诸如读取传感器、显示数据等工作。,当事件(中断)发生时,MicroPython 将执行回调函数,然后返回执行我们的代码。这是通过将回调函数绑定到中断上来实现的。开关类有一个为按钮按压定义的中断。也就是说,当按钮被按下时,回调被触发(执行)。

使用开关回调有一些注意事项。首先,switch 回调函数不能带参数,因此您不能定义回调函数并向它传递任何数据。事实上,回调函数不允许创建数据。例如,您不能创建字典、元组等。,在函数内部。虽然回调函数可以访问全局变量,但如果使用状态变量,它们可能会引发异常。最后,您可以关闭(断开)回拨。例如,您可以用switch.callback(none)断开交换机的回拨。

现在,让我们看一个例子。在本例中,我们希望创建一个回调函数来循环显示板上的 led。每按一次按钮,另一个 LED 就会亮起,直到我们循环所有的 LED,然后重新开始。这意味着我们需要保存最后点亮的 LED 或 LED 的状态。为此,我们可以使用一个具有局部变量的类,我们可以在回调函数中访问这个局部变量。

设置回调很容易。我们只是调用开关的回调函数,并传入函数名。我们通过类的构造函数来实现这一点。也就是说,当我们创建一个类的新实例时,我们传入pyb.Switch对象,然后调用该对象上的回调函数,传入类函数的名称。

让我们看看代码,你会看到这是如何工作的。清单 6-5 显示了 Pyboard 回调示例的完整代码。

# MicroPython for the IOT - Chapter 6
# Simple example for working with interrupts using a class
# to store state.
#
import sys

# First, make sure this is running on a Pyboard
try:
    import pyb
except ImportError:
    print("ERROR: not on a Pyboard!")
    sys.exit(-1)

# Initiate the switch class
switch = pyb.Switch()

class Cycle_LED(object):
    # Constructor
    def __init__(self, sw):
        self.cur_led = 0
        sw.callback(self.do_switch_press)

    # Switch callback function
    def do_switch_press(self):#
        # Turn off the last led unless this is the first time through the cycle
        if self.cur_led > 0:
            pyb.LED(self.cur_led).off()
        if self.cur_led < 4:
            self.cur_led = self.cur_led + 1
        else:
            self.cur_led = 1
        # Turn on the next led
        pyb.LED(self.cur_led).on()

# Initiate the Cycle_LED class and setup the callback
cycle = Cycle_LED(switch)

# Now, simulate doing something else
print("Testing the switch callback. Press CTRL-C to quit.")
while True:
    sys.stdout.write(".")  # Print something to show we're still in the loop
    pyb.delay(1000)        # Wait a second...

Listing 6-5.Callback Example (Pyboard)

注意我们创建的类,以及它如何为交换机设置回调函数。还要注意我们是如何实例化 switch 实例中传递的类的。之后,我们设置了一个简单的循环来打印出点,直到用 CTRL-C 停止程序。这演示了即使按钮被按下,程序仍继续运行。

如果您正在使用 Pyboard,请对这个示例进行测试。正如您将看到的,使用回调是非常强大的。

Tip

如果你想学习如何编写自己的中断处理程序,请参见 https://docs.micropython.org/en/latest/pyboard/reference/isr_rules.html

Communication Via Bluetooth Low Energy (BLE)

WiPy 的另一个有趣的特性是它带有蓝牙低能耗(BLE ),固件支持它作为一个网络类。虽然您可以通过 UART(串行通信)将 ble 模块与其他电路板一起使用,但要了解更高级、更完整的示例,请参见 https://github.com/dmazzella/uble ,WiPy 的 BLE 功能使您可以创建读/写服务,用于传输小数据包:例如,对传感器进行采样并通过 BLE 发送数据。

可悲的是,固件中的蓝牙支持仍然非常新,对于那些刚刚接触 MicroPython(和 BLE)的人来说还不够完整。因此,对于一本入门书来说,它太超前了。事实上,文档目前非常简洁,示例很难理解。随着越来越多的人使用它并找到创造性的方法来应用它,随着新功能和现有功能的增加,您可能会看到文档得到增强。查看 Pycom 在线文档,了解蓝牙支持(https ://docs.pycom.io/chapter/firmwareapi/pycom/network/bluetooth/).)的更新

现在,让我们看看如何使用 I2C 和 SPI 协议与分线板通信。

使用分线板

我们在第三章简要介绍了分线板,其中我们看到了一个使用 Arduino 屏蔽将 Pyboard 连接到无线网络的有趣示例。这只是分线板的一个例子。您还可以找到承载传感器和其他设备的分线板,从而简化您的 IOT 项目。使用这些分线板的诀窍是找到正确的、有效的驱动程序。

回想一下,使用分线板有两种方法:找到您可以使用的驱动程序,或者构建您自己的驱动程序。对于不熟悉 MicroPython 和 I2C 或 SPI 的人,不建议构建自己的驱动程序。花时间去寻找一个你能使用(或适应)的驱动程序比自己写一个要容易得多。这是因为您必须能够获得、阅读和理解分线板如何通信(理解其协议)。根据支持的传感器或设备,每个板的通信方式会有所不同。也就是说,BMP180 传感器的驱动程序与 BME280 传感器的驱动程序看起来或工作起来不一样。在查找和使用驱动程序时,您必须非常具体。

搜索一个驱动程序可能是一项单调乏味的工作,这需要一些耐心,并且可能需要在论坛上使用不同的搜索词进行多次搜索,例如“micropython BME280”一旦你找到一个驱动程序,你可以通过查看包含的例子快速判断它是否是一个可行的选择。如前所述,如果没有例子或者例子与你在本书或在线文档中看到的不相似,就不要使用它。

Tip

要查找驱动程序,请访问 https://forum.micropython.org/viewforum.php?f=14 并搜索您的主板。例如,如果你有一个 BME280 分线板,用它作为搜索词。使用“供应商 XYZ 型号#40113”这样的精确术语可能过于具体。首先尝试使用传感器/设备的通用名称。

让我们来看两个分线板示例:一个使用 I2C 协议,另一个使用 SPI 协议。

Online Examples

如果你想在你的 IOT 项目中使用一个分组讨论板,一定要花些时间不仅在论坛上,也看看各种博客和教程,比如那些在hackaday.comlearn.sparkfun.comlearn.adafruit.com上的。最好的博客和教程不仅解释了如何编写代码,还解释了分组讨论板的作用和使用方法。这些网上参考资料很少,但这三个网站的都是最好的。另外,也可以看看一些关于这个话题的视频。其中一些值得一看——尤其是如果它们来自 Adafruit 或 Sparkfun 的好人们。 4

内部集成电路(I2C)

I2C 协议可能是你在分组讨论板上看到的最常见的协议。我们在前面的章节中遇到过这个术语几次,因此我们只知道它是一个通信协议。I2C 是一种快速数字协议,使用两条线(加上电源和地)从电路(或设备)读取数据。该协议旨在允许多个设备(从设备)与一个主设备(MicroPython 板)一起使用。因此,每个 I2C 分线板都有自己的地址或标识,您将在驱动程序中使用这些地址或标识来连接设备并与之通信。

让我们看一个如何使用 I2C 分线板的例子。在这个例子中,我们想要使用 Adafruit ( https://www.adafruit.com/product/1334 )的 RGB 传感器来读取对象的颜色。是的,你可以让你的 MicroPython 板看到彩色!我们将使用这个分线板和 WiPy。

如果您没有或不想购买 Adafruit RGB 传感器分线板(虽然不贵),也不用担心。此示例作为使用 I2C 分线板的教程提供。我们将在本书后面的一个示例项目中使用另一个 I2C 分线板。图 6-2 显示了 Adafruit RGB 传感器。

A447395_1_En_6_Fig2_HTML.jpg

图 6-2。

Adafruit RGB Sensor (courtesy of adafruit.com)

配线分线板也非常容易,因为我们只需要电源、接地、SCL 和 SDA 连接。SCL 是时钟信号,SDA 是数据信号。这些引脚在您的 MicroPython 板上(或在文档中)以及分线板上有标签。当您连接分线板时,请确保电源要求匹配。也就是说,一些分线板可以采用 5V,但许多限于 3 或 3.3V。如果您有任何疑问,请查看供应商的网站。

我们只需要连接 3V、地、SDA、SCL 和 LED 引脚。LED 引脚用于打开分线板上的明亮 LED,以表示可以读取了。我们将让它保持 10 秒钟,以便有时间读取颜色值,然后显示它。然后,我们将再等待 5 秒钟来读取下一个读数。

要连接电线,您可以使用五根公母跳线插入 WiPy(或扩展板)和分线板。图 6-3 显示了您需要进行的连接。请注意,电线被插入标有“GXX”而不是“PinXX”的引脚中在使用电路板时,这可能会引起混淆。最好参考引脚排列图,以确保使用正确的引脚。在这种情况下,我们需要 P9 和 P10 用于 I2C 连接,我们将使用 P8 用于 LED。

A447395_1_En_6_Fig3_HTML.jpg

图 6-3。

Wiring the RBG Sensor (WiPy) Tip

参见 https://docs.pycom.io/chapter/datasheets/development/wipy2.html 了解 WiPy 板的引脚排列,参见 https://docs.pycom.io/chapter/datasheets/boards/expansion.html 了解扩展板的引脚排列。请注意,扩展板“镜像”WiPy,但其标签完全不同。

一旦连接好硬件,就把它放在一边。我们需要下载驱动程序并将其复制到主板上,然后才能进行进一步的实验。你可以在 https://github.com/adafruit/micropython-adafruit-tcs34725 找到司机。这是一个完全工作的,经过测试的驱动程序,演示了使用 I2C 分线板是多么容易。不要担心库的内部。我所指的代码如下所示。请注意,默认地址是 0x29,但由于地址是一个参数,如果同一 RGB 传感器在不同的地址有另一个分线板,您可以覆盖它。这意味着您可以使用多个相同的驱动程序。

class TCS34725:
    def __init__(self, i2c, address=0x29):

        self.i2c = i2c
        self.address = address
        self._active = False
        self.integration_time(2.4)
        sensor_id = self.sensor_id()
        if sensor_id not in (0x44, 0x10):
raise RuntimeError("wrong sensor id 0x{:x}".format(sensor_id))
...

要下载驱动程序,首先导航到 https://github.com/adafruit/micropython-adafruit-tcs34725 and,然后单击下载按钮,再单击下载 Zip 按钮。文件下载完成后,解压。在生成的文件夹中,您应该会找到名为tcs34725.py的文件。这是驱动程序代码模块。准备好后,我们将使用 FTP 将其复制到我们的 WiPy,如下所示。请确保首先连接到您的 WiPy Wi-Fi 网络,并在与文件相同的目录中打开一个终端。

$ ftp 192.168.4.1
Connected to 192.168.4.1.
220 Micropython FTP Server
Name (192.168.4.1:cbell): micro
Password:
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> cd flash
ftp> put tcs34725.py
local: tcs34725.py remote: tcs34725.py
227 (192,168,4,1,7,232)
100% |***********************************|  5222      23.05 MiB/s    00:00 ETA
5222 bytes sent in 00:00 (6.57 KiB/s)
ftp> quit

既然驱动程序已经复制到我们的板上,我们就可以写代码了。在本例中,我们将建立到分线板的 I2C 连接,并运行一个循环来从传感器读取值。听起来很简单,但是有一点技巧。我们将放弃对代码的冗长讨论,而是提供一些关键方面,让您自己阅读代码,看看它是如何工作的。

关键组件是设置 I2C、传感器、用于控制 LED 的引脚以及从传感器读取数据。通过将引脚设为高电平(开)或低电平(关),可以打开和关闭板上的 LED。首先,I2C 电码如下。这里,我们初始化一个对象,然后调用init()函数将总线设置为主机模式。scan()功能打印出总线上的设备。如果你看到一个空的设置显示,你的 I2C 接线是不正确的。请检查并再次尝试该代码。注意,完成导入后,您可以手动运行这段代码。

i2c = I2C(0, I2C.MASTER)             # create and init as a master
i2c.init(I2C.MASTER, baudrate=20000) # init as a master
i2c.scan()

接下来是传感器本身。司机使这变得容易。我们需要做的就是传递 I2C 构造函数,如图所示。

# Setup the sensor
sensor = tcs34725.TCS34725(i2c)

设置 LED 引脚也很容易。我们需要做的就是调用Pin()类构造函数,传入引脚名称(P8)并将其设置为输出模式,如下所示。

# Setup the LED pin
led_pin = Pin("P8", Pin.OUT)
led_pin.value(0)

最后,我们用传入 True 的sensor.read()函数从传感器读取数据,它告诉驱动程序返回 RGBC 值。然后我们会按顺序把这些打印出来。清单 6-6 显示了完整的代码。花一些时间通读它,这样你就能理解它是如何工作的。

# MicroPython for the IOT - Chapter 6
# Example of using the I2C interface via a driver
# for the Adafruit RGB Sensor tcs34725
#
# Requires library:
# https://github.com/adafruit/micropython-adafruit-tcs34725
#
from machine import I2C, Pin
import sys
import tcs34725
import utime

# Method to read sensor and display results
def read_sensor(rgb_sense, led):
    sys.stdout.write("Place object in front of sensor now...")
    led.value(1)                # Turn on the LED
    utime.sleep(5)              # Wait 5 seconds
    data = rgb_sense.read(True) # Get the RGBC values
    print("color detected: {")
    print("    Red: {0:03}".format(data[0]))
    print("  Green: {0:03}".format(data[1]))
    print("   Blue: {0:03}".format(data[2]))
    print("  Clear: {0:03}".format(data[3]))
    print("}")
    led.value(0)

# Setup the I2C - easy, yes?
i2c = I2C(0, I2C.MASTER)             # create and init as a master
i2c.init(I2C.MASTER, baudrate=20000) # init as a master
i2c.scan()

# Setup the sensor
sensor = tcs34725.TCS34725(i2c)

# Setup the LED pin
led_pin = Pin("P8", Pin.OUT)
led_pin.value(0)

print("Reading Colors every 10 seconds. When LED is on, place object in front of sensor.")
print("Press CTRL-C to quit. (wait for it)")
while True:
    utime.sleep(10)               # Sleep for 10 seconds
    read_sensor(sensor, led_pin)  # Read sensor and display values

Listing 6-6.Using the Adafruit RGB Sensor (WiPy)

一旦你有了代码,你可以把它复制到你的主板上,就像我们用 ftp 工具复制驱动程序一样,如下所示。

$ ftp 192.168.4.1
Connected to 192.168.4.1.
220 Micropython FTP Server
Name (192.168.4.1:cbell): micro
Password:
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> cd flash
ftp> put wipy_RGB.py
local: wipy_RGB.py remote: wipy_RGB.py
227 (192,168,4,1,7,232)
100% |***********************************|  1202        4.51 MiB/s    00:00 ETA
1202 bytes sent in 00:00 (2.17 KiB/s)
ftp> quit

剩下的工作就是运行示例并测试它。清单 6-7 展示了如何在 WiPy 上运行这个例子以及一个输出示例。如果您在 WiPy 上运行此示例,您可以将任何想要的对象放在传感器前面,它将读取颜色,并将其作为表示 RGB 值加上透明值的元组返回,如图所示。

>>> import wipy_RGB
Reading Colors every 10 seconds. When LED is on, place object in front of sensor.
Press CTRL-C to quit. (wait for it)
Place object in front of sensor now...color detected: {
    Red: 057
  Green: 034
   Blue: 032
  Clear: 123
}
Place object in front of sensor now...color detected: {
    Red: 054
  Green: 069
   Blue: 064
  Clear: 195
}
Place object in front of sensor now...color detected: {
    Red: 012
  Green: 013
   Blue: 011
  Clear: 036
}
...
Listing 6-7.Output from using the Adafruit RGB Sensor (WiPy)

如果你想再做一次练习,你可以从传感器获取这些值,并将它们映射到一个 RGB LED。是的,你可以这样做!来吧,试试看。见 https://github.com/JanBednarik/micropython-ws2812 获取灵感。在您阅读了 SPI 的下一部分之后,再着手解决这个问题。

Tip

关于 I2C 的深入讨论见 https://learn.sparkfun.com/tutorials/i2c

串行外设接口(SPI)

串行外设接口(SPI)设计用于在两个器件之间通过专用线路双向发送和接收数据。也就是说,它使用两条数据线、一个时钟和一个从机选择引脚。因此,它需要六个连接进行双向通信,或者只需要五个连接进行读写。一些 SPI 器件可能需要第七个引脚,称为复位线。

让我们看一个如何使用 SPI 分线板的例子。本例中,我们希望使用 Adafruit 热电偶放大器 MAX31855 分线板( https://www.adafruit.com/product/269 )和一个 K 型热电偶传感器( https://www.adafruit.com/product/270 )来读取高温。我们将使用这个分线板和一个 Pyboard。

如果您没有或不想购买 Adafruit 热电偶放大器 MAX31855 分线板(虽然不贵),也不用担心。此示例作为使用 SPI 分线板的教程提供。我们将在本书后面的一个示例项目中使用另一个 I2C 分线板。图 6-4 显示了 Adafruit 的 Adafruit 热电偶放大器和 H 型传感器。

A447395_1_En_6_Fig4_HTML.jpg

图 6-4。

Adafruit Thermocouple Breakout Board and Type-K Sensor (courtesy of adafruit.com)

该传感器可用于通过接近或触摸来测量高温。该传感器可以以 0.25 度的增量读取-200°C 至+1350°C 范围内的温度输出。这种传感器的一个可能用途是读取 3D 打印机上喷嘴的温度或任何类似的高热输出。应该注意的是,分线板是未组装的,因此您需要焊接接头和接线柱。

现在,让我们看看如何将分线板连接到我们的 Pyboard。我们将只使用五根电线,因为我们只从分线板上的传感器读取数据。这需要连接到电源(地(GND)、主输入(MOSI)、时钟(CLK)和从选择(SS)。图 6-5 显示了连接。

A447395_1_En_6_Fig5_HTML.jpg

图 6-5。

Wiring the Adafruit Thermocouple Module (Pyboard)

表 6-2 中显示了 Pyboard 上使用的正确引脚。你可以在 https://docs.micropython.org/en/latest/pyboard/pyboard/quickref.html 找到的 Pyboard 引脚上找到这些引脚。参见图 6-5 进行确认。

表 6-2。

Connecting the Thermocouple Breakout Board (Pyboard )

| Pyboard | Pyboard 上的位置 | 分线板 | | --- | --- | --- | | 车辆识别号码 | 最右上引脚 | 3V0 | | 地线 | 在右侧,从上往下数第三个销 | 地线 | | Y5 | 在电路板的左侧,从顶部数第五个引脚 | CS(从机选择) | | Y6 | 在电路板的左侧,从顶部数第六个引脚 | 时钟信号 | | Y7 | 在电路板的左侧,从顶部数第七个引脚 | D0 |

现在,让我们看看代码!在这个例子中,我们不打算使用驱动程序;相反,我们将了解如何使用 SPI 直接从分线板读取数据。为此,我们首先设置 SPI 接口的对象实例,然后选择一个引脚用于从机选择(也称为芯片或代码选择)。从那里,我们需要做的就是读取数据并解释它。我们将循环读取传感器,并编写一个函数来转换数据。

这是棘手的部分。这个例子向您展示了驱动程序作者必须做些什么来使设备的使用更容易。在这种情况下,我们必须从分线板中读取数据并对其进行解释。我们可以只读取原始数据,但这没有任何意义,因为它是二进制形式的。因此,我们可以从 Adafruit 借用一些代码来读取原始数据并理解它。

该函数名为normalize_data(),它执行一些位移和算术运算,将原始数据转换为摄氏度值。这些信息来自分线板的数据手册,但是 Adafruit 的友好人员让我们很容易就知道了。

设置 SPI 类很容易。我们使用 SPI 选项中传递的类构造函数来初始化 SPI 对象。这对于 Pyboard 是唯一的,可以是值 1-使用 X 位置,或值 2-使用 Y 位置。请注意,在上面的连接中,我们使用 Y 引脚,因此我们将使用值 2 作为第一个参数。其他参数告诉 SPI 类设置为主机,设置波特率、极性和相位(可在数据手册中找到)。接下来,我们只需选择用于读取数据的引脚,然后将引脚设为高电平。下面显示了激活 SPI 接口所需的代码。

spi = SPI(2, SPI.MASTER, baudrate=5000000, polarity=0, phase=0)
cs = machine.Pin("Y5", machine.Pin.OUT)
cs.high()

现在,让我们看看完整的代码。清单 6-8 显示了使用 Adafruit 热电偶放大器分线板的完整代码。

# MicroPython for the IOT - Chapter 6
# Simple example for working with the SPI interface
# using the Adafruit Thermocouple Amplifier. See
# https://www.adafruit.com/product/269.
#
# Note: this only runs on the Pyboard
#
import machine
import ubinascii

# First, make sure this is running on a Pyboard
try:
    import pyb
    from pyb import SPI
except ImportError:
    print("ERROR: not on a Pyboard!")
    sys.exit(-1)

# Create a method to normalize the data into degrees Celsius
def normalize_data(data):
    temp = data[0] << 8 | data[1]
    if temp & 0x0001:
        return float('NaN')
    temp >>= 2
    if temp & 0x2000:
        temp -= 16384
    return (temp * 0.25)

# Setup the SPI interfaceon the "Y" interface
spi = SPI(2, SPI.MASTER, baudrate=5000000, polarity=0, phase=0)
cs = machine.Pin("Y5", machine.Pin.OUT)
cs.high()

# read from the chip
print("Reading temperature every second.")
print("Press CTRL-C to stop.")
while True:
    pyb.delay(1000)
    cs.low()
    print("Temperature is {0} Celsius.".format(normalize_data(spi.recv(4))))
    cs.high()

Listing 6-8.The Adafruit Thermocouple Module Example (Pyboard)

此时,您可以连接硬件并接通 Pyboard 的电源。然后,您可以将该文件复制到您的 Pyboard 并执行它,如下所示。

>>> import pyboard_SPI
Reading temperature every second.
Press CTRL-C to stop.
Temperature is 32.0 Celsius.
Temperature is 31.75 Celsius.
Temperature is 32.0 Celsius.
Temperature is 32.5 Celsius.
Temperature is 33.5 Celsius.
Temperature is 34.0 Celsius.
Temperature is 34.25 Celsius.
Temperature is 34.5 Celsius.
Temperature is 34.5 Celsius.
...

运行该示例后,您应该会看到它产生以摄氏度为单位的值。如果您看到 0.00,您可能没有正确连接 SPI 接口。对照上图检查您的接线。如果您看到数值,但当您将热电偶暴露在高温下时,数值下降,您需要反转导线。确保先关闭主板电源,以免损坏传感器、分线板或您的 Pyboard!

如果你想用 WiPy 运行这个例子,你可以!只需修改代码,使用 WiPy SPI 类及其初始化序列,如 https://docs.pycom.io/chapter/firmwareapi/pycom/machine/SPI.html 所示。您还必须对 includes 做一些修改,但是这对于示例项目章节来说是一个很好的实践。

Tip

参见 https://learn.sparkfun.com/tutorials/serial-peripheral-interface-spi 对 SPI 的深入讨论。

摘要

通过固件访问底层硬件是使用 MicroPython 的真正优雅之处,在某些情况下也是复杂性的开始。鉴于可用的主板不同,固件中的低级支持也不同也就不足为奇了。因此,在规划 MicroPython IOT 项目时,我们必须考虑我们想要做什么,以及我们的主板(和固件)是否支持它。我们还需要知道我们想要连接到什么分线板和设备,以及是否有我们可以用来访问它们的驱动程序或其他库。在这种情况下,大多数带 I2C 或 SPI 接口的分线板都需要某种形式的驱动器。

在本章中,我们探讨了固件中的一些低级支持以及对 Pyboard 和 WiPy 的专门支持。正如我们所发现的,这是代码变得非常专门化的地方。正如我们所看到的,有时需要选择不同的库来导入,但是有时类、函数、甚至如何使用函数在不同的板上是不同的。

我们在这一章中也看到了很多代码——比之前的任何一章都多。本章中的例子旨在让你看到事情是如何完成的,而不是你自己去实现的项目(尽管我们欢迎并鼓励你这样做)。在后面的章节中,我们将会看到更多更详细的实践项目。

在下一章中,我们将以电子学的简短教程的形式进行一次简短的探讨。如果你以前从未接触过电子学,下一章将为你提供完成本书中的项目所需的信息,并让你为一个令人兴奋的新爱好做好准备——构建 MicroPython IOT 项目!

Footnotes 1

https://en.wikipedia.org/wiki/Hexadecimal

2

https://en.wikipedia.org/wiki/Network_Time_Protocol

3

每当你发现自己在为常用代码编写函数时,是时候考虑将它添加到你最喜欢的函数库中,以使这些代码对你所有的程序都可用,正如我们在第五章中所讨论的。

4

不,我与这两者都没有关系——我只是一个忠实的粉丝和常客。看看他们!

5

但是如果你好奇的话,请随意。如果您这样做了,您将会看到一段有趣的代码,它展示了 I2C 设备的寻址能力。

七、初学者电子学

如果你刚接触硬件,对电子产品很少或没有经验,你可能会好奇如何完成本书中的项目。幸运的是,本书中的项目将指导您如何使用 MicroPython 板将各种电子器件连接在一起。也就是说,您可以在没有额外技能或经验的情况下完成项目。

然而,如果你想知道组件是做什么的,除了“把这一端插在这里”之外,你还需要更多的信息如果出了问题,尤其如此。此外,如果你想自己创建项目,你需要足够了解组件如何工作以成功完成你的项目——无论是完成本书中的例子还是在互联网上找到的例子。

幸运的是,你不需要正式的培训,甚至不需要理论上的大学学位,就能学会如何与电子打交道。你可以在业余爱好者的水平上学习很多关于使用电子学的知识,而不用花费几个月或几年的时间去研究。为了确保哪怕是最基本的成功,你需要知道的不仅仅是如何简单地将组件组装在一起。

本章不是试图提供一个全面的电子学教程,这需要几卷,而是为那些想使用 IOT 项目中常见的电子元件类型的人提供一个电子学概述。我包括一些基础知识的概述、常见组件的描述以及对传感器的介绍。如果你是电子学新手,这一章会给你额外的帮助,帮助你理解本书项目中使用的元件。

如果您有电子爱好者或发烧友级别的经验,或者有电子方面的经验或正式培训,您可能希望浏览本章或阅读您希望复习的主题部分。

让我们先来看看电子学的基础知识。再说一次,这绝不是一个涵盖所有知识的教程,但它将带您了解项目在如何连接和使用组件方面的意义。

基础知识

本节简要概述了使用电子设备时需要用到的一些最常用的工具和技术。正如您将看到的,您只需要最基本的工具,技能或技术并不难学习。然而,在我们进入这些之前,让我们看看你在 IOT 项目中需要用到的一些工具。

工具

构建 IOT 项目所需的绝大多数工具都是普通的手工工具(螺丝刀、小扳手、钳子等)。).对于较大的项目或创建外壳,您可能需要额外的工具,例如电动工具,但是我将只关注那些用于构建项目的工具。以下是我推荐给你的入门工具列表。

  • 面包板
  • 试验板电线(也称为跳线)
  • 静电放电安全镊子
  • 帮助手或印刷电路板(PCB)固定器
  • 万用表
  • 尖嘴钳
  • 螺丝刀–各种尺寸(微型、小型)
  • 焊料
  • 烙铁
  • 焊料去除器(焊料吸盘)
  • 工具箱、卷轴或储存箱
  • 剥线钳

然而,如果你想买一套完整的电子工具,比如来自 spark fun(sparkfun.com/categories/47)或 Adafruit(Adafruit . com/categories/83)的工具,那就错不了。你经常可以在各大品牌电子商店和家装中心找到电子套件。如果你足够幸运,住在一家电器店附近,你可以找到几乎所有制造的电子工具。大多数电子工具包都有你需要的所有手工工具。有些甚至带有万用表,但更多时候你必须单独购买。

列表中的大多数工具不需要任何解释,只是说你应该在预算允许的情况下购买最好的工具。以下段落描述了一些用于特殊任务的工具,例如剥线、焊接以及测量电压和电流。

万用表

万用表是构建 IOT 解决方案时需要的工具之一。你还需要它来对你的电路进行几乎所有的电气维修。有许多不同的万用表,价格从便宜的基本单位;到复杂、功能丰富、价格昂贵的设备。对于大多数 IOT 项目,包括大多数 IOT 工具包,一个基本单位是你所需要的。然而,如果你计划建立一个以上的 IOT 解决方案或想要组装自己的电子产品,你可能要多投资一点,在一个更复杂的万用表。图 7-1 左边是一个基本的数字万用表(成本约 10 美元),右边是 BK Precision 的专业万用表。

A447395_1_En_7_Fig1_HTML.jpg

图 7-1。

Digital multimeters

请注意,更好的血糖仪具有更精细的设置和更多功能。同样,你可能不需要超过基本单位。你至少需要测量电压、电流和电阻。无论你买哪个电表,确保它有测量交流和 DC 电压、连续性测试(有声音警报)和检查电阻的模式。我将在后面的章节中解释如何使用万用表。

Tip

大多数万用表,包括便宜的万用表,都有一个小说明书,告诉你如何测量电压、电阻和其他功能。

烙铁

本书中的任何项目都不需要烙铁,因为我们将使用试验板来布置和连接组件。然而,如果你计划建立一个简单的 IOT 解决方案,你需要将电线焊接在一起,或者可能需要几个连接器,从电子商店如 Radio Shack 购买一个基本的烙铁就可以了。另一方面,如果你打算自己组装电子产品,你可能要考虑买一个好的专业烙铁,比如 Hakko。专业型号包括允许您设置魔杖温度的功能,有更广泛的提示可用,并且往往持续更长时间。图 7-2 显示了 Radio Shack 的一个常用入门级。图 7-3 显示的是专业型号的 Hakko 烙铁。

Tip

为获得最佳结果,选择含铅量在 37%-40%范围内的低焊料。如果您使用专业烙铁,请调整温度以匹配焊料的熔点(列在标签上)。

A447395_1_En_7_Fig2_HTML.jpg

图 7-2。

Entry-level soldering iron Do I Need to Learn to Solder?

如果你不知道如何焊接,或者已经有一段时间没有使用烙铁了,你可以看看 Brian Jepson、Tyler Moskowite 和 Gregory Hayes 所著的《学习焊接》( O'Reilly Media,2012)或者谷歌的焊接视频。或者你也可以从 Maker Shed ( makershed.com/products/make-getting-started-kit-soldering)购买入门焊接套件,它配有烙铁、剪线钳、供应品等等——学习焊接所需的一切。

A447395_1_En_7_Fig3_HTML.jpg

图 7-3。

Professional soldering iron

剥线器

剥线钳有几种类型。事实上,可能有一打或更多的设计。但是有两种:一种是当你从电线上拉下来的时候,它只能夹住并切断绝缘层;以及抓持、切割和移除绝缘体的那些。第一种类型更常见,经过一些练习后,对于大多数小工作(比如修理断了的电线)来说就很好了;但是第二种类型的工作量更大,比如用裸线(没有预制连接器)连接电子设备,速度更快。可以想象,第一种要便宜得多。图 7-4 显示了两种类型的剥线钳。两者都是不错的选择。

A447395_1_En_7_Fig4_HTML.jpg

图 7-4。

Wire strippers

援助之手

还有一个你可能想要得到的工具,特别是如果你需要做任何焊接的话:它被称为帮助之手或第三手工具。大多数都有一对鳄鱼夹,用来在焊接时夹住电线、印刷电路板或元件。图 7-5 显示了一个简单的帮助工具的例子。

A447395_1_En_7_Fig5_HTML.jpg

图 7-5。

Helping hands tool

现在让我们来看看在使用高级 IOT 项目时可能需要的一些技能。

ESD is the Enemy

您应该注意确保您的身体、工作区和项目接地,以避免静电放电(ESD)。ESD 会永久损坏您的电子设备。避免这种情况的最佳方法是使用接地带,环绕在手腕上,并连接到防静电垫上,就像uline.com/BL_7403/Anti-Static-Table-Mats中的这些。

让我们来看看如何使用一个你在学习电子学时可能会用得最多的工具——万用表。

使用万用表

IOT 项目所需的电气技能各不相同,从在试验板上插上电线(正如我们迄今为止在项目中看到的那样),到需要将元件焊接在一起或焊接到印刷电路板(PCB)上。不管你是否需要焊接电子元件,你都需要能够使用基本的万用表来测量电阻和检查电压和电流。

对于任何电子爱好者来说,万用表都是非常有用和必不可少的工具,对于任何有价值的发烧友来说,这是绝对需要的。典型的万用表有一个数字显示器【2】(通常是 LCD 或类似的数字显示器)、一个刻度盘和两个或多个用于插入带有探针端的测试引线的接线柱或端口。大多数万用表都有低电流端口(你最常用的)和大电流端口。测试引线使用红色表示正极,黑色表示负极(接地)。接地端口是插入黑色测试引线的地方,通常标有破折号或 COM。您使用的其他端口将取决于您正在测试的内容。

表盘上有一点需要注意,那就是有很多设置(有些值是重复的)或者那些看起来很相似的设置。例如,您会看到一组欧姆值(有时称为标度),一组或两组安培值,以及一组或两组伏特值。带有 V 形实线和虚线的一组电压值适用于 DC,而带有 V 形波浪线的范围适用于交流。安培范围以同样的方式标记。图 7-6 显示了一个万用表表盘的特写,上面标有我提到的几组数值。

A447395_1_En_7_Fig6_HTML.jpg

图 7-6。

Multimeter Dial (typical) Tip

不使用时,一定要将万用表拨盘转到 off 或某个电压范围,如果它有单独的 off 按钮的话。

用万用表可以做很多事情。你可以检查电压,测量电阻,甚至检查连续性。大多数基本的万用表将完成这些功能。然而,有些万用表有更多的功能,如测试电容,测试交流和 DC。

让我们看看如何使用万用表来完成 IOT 项目中最常见的任务:测试连续性、测量 DC 电路中的电压、测量电阻和测量电流。

测试连续性

我们测试连续性,以确定是否有带电粒子流动的路径:也就是说,我们的电线和组件连接正确。例如,您可能想要检查以确保导线已正确接合。

为了测试连续性,将万用表刻度盘转到标有声音符号、铃或有箭头穿过的三角形的位置。将黑色测试引线插入 COM 端口,将红色测试引线插入标有 Hz VΩ或类似标志的端口。现在,您可以一起触摸测试引线的探针端,以听到声音或嘟嘟声。有些万用表没有声音,但可能显示“1”或类似的数字来表示连续性。查看您的手册,了解您的万用表是如何指示连续性的。图 7-7 显示了如何设置万用表来检查连续性,包括插入测试引线的端口。

请注意,在照片中,我只是将探针接触在一起,以演示如何检查连续性。我喜欢这样做,只是为了确保我的万用表是打开的,并且设置正确。 3

A447395_1_En_7_Fig7_HTML.jpg

图 7-7。

Settings for checking continuity

连续性测试的另一个出色用途是在诊断或发现电缆接线方式时。例如,您可以使用连续性测试来发现电缆两端连接的是哪一个连接器(有时称为电线分类或旧电话时代的振铃)。

测量电压

我们的 IOT 项目使用 DC。为了测量电路中的电压,我们将使用万用表上的 DC 量程。注意,DC 山脉有几个站点。这是一个音阶选择。选择与您想要测试的电压范围最接近的刻度。例如,对于我们的 IOT 项目,我们通常会测量 3.3-12V,因此我们在表盘上选择 20。接下来,将黑色测试引线插入 COM 端口,将红色测试引线插入标有 Hz VΩ的端口。

现在我们需要一些东西来衡量!拿起你家里的任何一个电池,用黑色探针接触负极,用红色探针接触正极。您应该会看到显示屏上出现一个接近电池范围的值。例如,如果我们使用 1.5V 的电池,我们应该看到接近 1.5V。如果电池耗尽,它可能不会正好是 1.5-1.6V。现在你知道如何测试电池的新鲜度了吧!图 7-8 显示了如何测量电池电压。

A447395_1_En_7_Fig8_HTML.jpg

图 7-8。

Measuring voltage of a battery

注意读数显示 1.50,这是该 AA 电池的正确电压。如果我把探针颠倒过来——红色的是负极,黑色的是正极,显示屏上的读数应该是-1.50。这是可以的,因为它表明电流的流动方向与探头的方向相反。

Note

如果在 DC 电路中测量电压时使用了错误的探针,大多数万用表会将电压显示为负数。用你的电池试试。不会伤到万用表(或者电池)!

我们可以在项目中使用这种技术来测量电压。注意将探针放在适当的位置,尽量不要用一个探针头一次接触多个元件而造成交叉或短路。

测量电流

电流以安培数(毫安-毫安)来衡量。因此,我们将使用标有 A 的直线和虚线的范围(不是波浪线,那是 AC)。我们串联测量电流。也就是说,我们必须将万用表放在电路中。这可能有点棘手,因为我们必须中断电流,并将电表放在线路上。

如果你熟悉如何使用试验板,你可以跟着做这个实验。然而,如果你没有使用过试验板,你可能想通读这个实验,然后在你读完这一章后再返回。在本实验中,我们将使用试验板电源、LED 和电阻。我们将连接电路,这样我们将使用万用表来完成电路。图 7-9 显示了如何使用万用表在线设置电路。

A447395_1_En_7_Fig9_HTML.jpg

图 7-9。

Measuring current

在接通试验板电源之前,将黑色测试引线插入 COM 端口,将其他测试引线插入标有 mA 的端口。有些万用表用同一个端口测量电压和电流。将万用表的旋钮转到 200 毫安的设定值。然后接通试验板电源,将引线接触到指示的位置。注意只触摸试验板电源上的 VCC 引脚。一旦电路通电,您应该会在万用表上看到一个值。图 7-10 显示了如何使用万用表测量电路中的电流

A447395_1_En_7_Fig10_HTML.jpg

图 7-10。

Measuring current

测量电流还有一个棘手的问题。如果您试图测量大于端口最大值的电流(例如,照片中的仪表在一个端口上的最大值为 20mA)。如果超过这个值,比如说 5A,我可能会烧断万用表的保险丝。这是不可取的,但至少有一个保险丝,我们可以更换,如果我们犯了错误,选择了错误的端口。

测量电阻

电阻以欧姆(ω)为单位测量。我们用来在电路中引入电阻的最常见的元件是电阻器。我们可以用万用表通过电阻测试电荷的电阻。要测试电阻,选择最接近电阻额定值的欧姆表。例如,我要测试一个电阻,我认为它大约为 200 欧姆,但由于我不确定,我将选择 2k 设置。

接下来,将黑色测试引线插入 COM 端口,将红色测试引线插入标有 HzVΩ的端口。现在,将一个探针接触电阻器的一侧,另一个探针接触另一侧。无论选择哪一侧,电阻都可以双向工作。注意读数。仪表将读取以下三个值之一:0.00、1 或实际电阻值。

这种情况下,电表读数为 0.219,意味着该电阻的阻值为 220ω。回想一下,我使用 2k 标度,这意味着 1k 的电阻读数为 1.0。由于该值是一个小数,我可以将小数点向左移动,得到一个整数。

如果万用表显示另一个值,如 0 或 1,这表明标度是错误的,你应该尝试更高的标度。这不是问题。只是说明你需要选择更大的尺度。另一方面,如果显示器显示 0 或较小的数字,则需要选择较低的刻度。当我在一个未知的元件或电路中测试电阻时,我喜欢按下旋钮。

图 7-11 显示了一个测量电阻器电阻的例子。注意显示屏上显示的是 219。我正在测试一个额定 220 欧姆的电阻。之所以是 219 而不是 220,是因为我使用的电阻额定值为 220 +/- 5%。因此,该电阻器的可接受范围是 209-231 欧姆。

A447395_1_En_7_Fig11_HTML.jpg

图 7-11。

Measuring resistance of a resistor

现在我们知道了如何测试一个电阻来发现它的额定值。正如我们将看到的,电阻体周围的环是我们了解其额定值的主要方式,但如果我们不确定,有人在上面涂了漆(嘿,这种情况时有发生),或者我们懒得去查,我们总是可以测试它。

现在,让我们来讨论使用电子设备时必须理解的最基本概念——为您的项目供电!

为您的电子设备供电

4 简单地定义为电荷的流动,当使用时为我们的电子设备提供电力——从普通的灯泡或吊扇到我们的高清电视或我们的新平板电脑。无论你是用电池还是电源给你的电子设备供电,你都是在启动一个电子以特定模式流动的电路。你将使用两种形式的权力。你的家用交流电供电,你的电子设备用直流电供电。

术语交流电(AC)用于描述以特定速率(或周期)周期性改变方向的带电粒子流,使电压与电流反向。因此,交流系统被设计成在特定的周期和电压范围内工作。通常,交流系统比直流系统使用更高的电压。

术语直流电(DC)用于描述不改变方向的带电粒子的流动,因此总是以特定的“方向”流动大多数电子系统由 DC 电压供电,通常比交流系统的电压低。例如,IOT 项目通常在 3.3-24V 范围内的较低直流(DC)电压下运行。

Tip

有关交流电流和 DC 电流及其差异的更多信息,请参见 https://learn.sparkfun.com/tutorials/alternating-current-ac-vs-direct-current-dc

由于 DC 是单向流动的,在 DC 上工作的元件有一个正的和一个负的“面”,电流从正流向负。这些面的方向——一个是正极,一个是负极——叫做极性。有些元件,如电阻,可以在任何一个“方向”上工作,但你应该始终确保按照极性连接你的元件。大多数部件都有明确的标记,但那些没有标记的部件却有着众所周知的排列方式。例如,LED 的正极(侧面)是两条腿中较长的一条(称为阳极,而负极或较短的腿称为阴极)。

尽管电压较低,但我们不能认为它们完全无害或安全。电子设备接线错误(极性颠倒)或短路(将正极和负极连接在一起)会损坏您的电子设备,在某些情况下会导致过热,在极端情况下,会导致电子设备着火。

Caution

不要认为在 3.3 或 5.5 伏电压下工作是“安全的”即使少量电压连接不当也可能导致潜在的灾难性后果。不要认为低 DC 电压是无害的。

几年前,我就知道这种情况有多真实。我在换烟雾探测器的电池。我把旧电池拿出来,放在口袋里。我忘了在同一个口袋里还有一把小铅笔刀。其中一个电池在刀上短路,在大约十分钟内,电池加热到惊人的温度。它不足以燃烧,但如果我没有注意到这样的东西,它可能会很糟糕。

这是个可怕的想法,不是吗?把它当作一个告诫和警告;即使是低压项目,我们也不应放松安全操作实践。

最后,DC 组件通常针对特定的电压范围进行额定。回想一下我们对各种低成本计算板和 GPIO 接头的讨论,一些板的工作电压为 5V,而另一些板的工作电压为 3.3V(或更低)。幸运的是,我们有几种方法可以调整在不同电压下工作的元件——通过使用其他元件!

Note

我有意让关于权力的讨论保持简单。电流——甚至 DC——远不止我在这里描述的那样。一旦你理解了这些基础知识,你就能使用本书及更多书中的项目。

电子元件

除了学习如何使用万用表和可能的学习焊接,你还需要知道一些关于电子元件可用于建立你的项目。在这一节中,我将按名称的字母顺序提供一个简短的列表和一些常见组件的描述,您将在构建 IOT 解决方案时遇到这些组件。我还介绍了分线板和逻辑电路,它们是用一组组件构建的小电路,提供一种功能或解决一个问题。例如,您可以获得用于 USB 主机连接、以太网模块、逻辑移位器、实时时钟等的分线板。

纽扣

按钮(有时称为瞬时按钮)是一种按下时产生连接的机制。更具体地说,按钮在被按下时将两个或多个电极连接在一起。按钮的一个常见(也许是过度使用的)例子是家庭门铃。当按下时,它完成一个电路,触发钟声、铃声、音调或音乐播放。有些老式门铃在按下按钮时会继续响。

在 IOT 项目中,我们将使用按钮来触发事件、开始和停止动作以及类似的操作。按钮是开关的一种简单形式,但与开关不同的是,您必须持续按下按钮才能进行电气连接。大多数按钮至少有两个脚(或引脚),当按钮被按下时,这两个脚连接在一起。一些具有成对连接的两个以上的腿,并且其中一些可以允许多个连接。图 7-12 显示了几个按钮。

A447395_1_En_7_Fig12_HTML.jpg

图 7-12。

Momentary buttons

瞬时按钮有一种特殊的变体,称为闩锁瞬时按钮。这个版本使用一个缺口或棘爪来保持电极连接,直到它再次被推动。如果你在音响或汽车上看到一个按钮一直按下,直到再次按下,这很可能是一个瞬时锁定按钮。

有各种各样的按钮,包括可以用于试验板的按钮(引脚间距允许其插入试验板),可以安装在面板上的按钮,或者焊接到印刷电路板上的按钮。

电容器

电容器是用来储存电荷的。当电流流过电容器时,它会积累电荷,并在电流断开后放电。这样,它就像一个电池,但不像电池,电容器充电和放电非常快。我们使用电容来存储各种电流,包括阻挡电流、降低电源和音频电路中的噪声等。图 7-13 显示了几个电容。

A447395_1_En_7_Fig13_HTML.jpg

图 7-13。

Capacitors

电容器有几种类型,但在为 IOT 项目构建电源时,我们最常遇到的是电容器。大多数电容有两个极化的引脚。即一个是正的,一个是负的。确保在电路中以正确的极性连接电容器。

二极管

二极管被设计成只允许电流单向流动。大多数都标有指向一条线的箭头,指示流向。在交流-DC 转换器(将交流转换为 DC 电压的设备)中,二极管通常用作整流器,与其他元件一起使用,以抑制电压尖峰,或保护元件免受反向电压的影响。它们通常用于防止电流流入设备。

大多数二极管的形状像一个小圆柱体,它们通常是黑色的,有银色的字迹,有两条腿。它们看起来有点像电阻器。我们在电源中使用一种称为齐纳二极管的特殊变体来帮助调节电压。图 7-14 显示了几个齐纳二极管。

A447395_1_En_7_Fig14_HTML.jpg

图 7-14。

Diodes

融合

保险丝旨在保护设备(整个电路)免受超过元件安全工作电流的影响。保险丝直列放置在正极上。当过大的电流流过保险丝时,内部零件会触发电流中断。

一些保险丝内部使用一种特殊的导线,该导线会熔化或断开(从而使其无用,但可以保护您的设备),而其他保险丝使用一种类似开关的机制(其中许多是可复位的)。当这种情况发生时,我们说保险丝“烧断”或“跳闸”保险丝的额定电流安培数表示保险丝在不跳闸的情况下允许流动的最大安培数。

保险丝有许多形状和种类,可以用交流或 DC 电压工作。我们要用的是一次性的。图 7-15 显示了两个保险丝的示例:左边是汽车式刀片保险丝,右边是玻璃筒保险丝。

A447395_1_En_7_Fig15_HTML.jpg

图 7-15。

Fuses

如果你熟悉家中装有断路器的配电板,它们就是可复位保险丝。所以,下一次当其中一个“咔嚓”一声,灯灭了,你可以说,“嘿,保险丝断了!”更好的是,现在你知道为什么了——你已经超过了断路器的最大额定值。

这可能在你不小心让红外线加热器开着的情况下是没问题的,当你弄掉烤面包和启动微波炉时(这种情况会发生),但是如果你经常在没有任何负载的情况下跳闸断路器,你应该叫电工来检查电路。

发光二极管

正如我们在第三章中了解到的,LED 有两个引脚,较长的引脚为正极,较短的引脚为负极。发光二极管也有一个平边,表示负支路。它们有各种尺寸,小到 3 毫米,大到 10 毫米。图 7-16 显示了一些较小的发光二极管的例子。

A447395_1_En_7_Fig16_HTML.jpg

图 7-16。

Light emitting diodes

回想一下,我们还需要使用一个带 LED 的电阻。我们需要这一点来帮助减少电路的流量,以降低流经 LED 的电流。led 可以在较低的电流下使用(它们会比正常情况下稍微暗一点),但不应该在较高的电流下使用。

为了确定我们需要多大的电阻,我们需要了解 LED 的几个方面。该数据可从制造商处获得,制造商以数据表的形式提供数据,或者在商业包装产品的情况下,在包装上列出数据。我们需要的数据包括最大电压、电源电压(LED 的电压)和 LED 的额定电流。

例如,如果我有一个像我们在上一章中使用的 LED,在这种情况下,一个 5 毫米的红色 LED,我们在 Adafruit 的网站(adafruit.com/products/297)上发现,该 LED 的工作电流为 1.8-2.2 伏和 20 毫安。假设我们希望使用 5V 电源电压。然后我们可以将这些值代入公式 5 :

R = (Vcc-Vf)/I

使用更具描述性的变量名称,我们得到如下结果。

Resistor = (Volts_supply - Volts_forward) / Desired_current

把我们的数据代入,我们得到这个结果。注意,我们有 mA,所以我们必须使用正确的十进制值(除以 1000)。在这种情况下,它是 0.020,我们将在中间选择一个电压。

Resistor = (5 - 2.0) / 0.020
         = 3.0 / 0.020
         = 150

因此,我们需要一个 150 欧姆的电阻。酷毙了。有时该公式会产生与任何现有电阻都不匹配的值。在这种情况下,选择一个最接近的值,但稍微大一点。记住,我们想要限制,因此宁可多限制也不要少限制。例如,如果你发现你需要一个 95 欧姆的电阻,你可以用一个 100 欧姆的,这比用一个 90 欧姆的更安全。

Tip

当公式产生一个没有可用电阻的值时,总是偏向限制性更强的电阻。

此外,如果您串联或并联使用 led,公式会有所不同。参见 https://learn.adafruit.com/all-about-leds 了解更多关于在项目中使用 led 以及计算 led 使用的电阻大小的信息。

继电器

继电器是一种有趣的元件,可以帮助我们用低压电路控制高压。例如,假设您想要控制一个由 MicroPython 板提供 12V 电源的设备,而 MicroPython 板只能产生最大 3V 的电压。继电器可以与 3V 电路一起使用,以打开(或中继)来自较高电源的电源。在本例中,我们将使用 MicroPython 板输出来触发继电器接通 12V 电源。因此,继电器是开关的一种形式。图 7-17 显示了典型的继电器以及引脚的排列方式。

A447395_1_En_7_Fig17_HTML.jpg

图 7-17。

Relay

继电器可以采取多种不同的形式,通常具有略微不同的接线选项,例如连接电源电压的位置和连接触发电压的位置,以及初始状态是打开(无流量)还是关闭(有流量)以及如何控制电压的行为。一些继电器安装在印刷电路板上,带有明显标记的端子,用于改变其开关特性和所有插件的位置。如果您想在项目中使用继电器,请务必查看数据表,以确保根据其配置正确接线。

你也可以使用继电器让你的 DC 电路像 Adafruit ( https://www.adafruit.com/product/2935 )的那些开关交流电器。

电阻器

电阻器是电子学的标准构件之一。它的作用是阻止电流并降低电压(转化为热量)。它的影响被称为电阻,用欧姆来衡量。电阻可用于降低其它元件的电压,限制频率响应,或保护敏感元件免受过压影响。图 7-18 显示了几个电阻。

A447395_1_En_7_Fig18_HTML.jpg

图 7-18。

Resistors

当电阻用于上拉电压(通过将一端连接到正电压)或下拉电压(通过将一端连接到地)(电阻是双向的)时,它消除了电压在不确定状态下浮动的可能性。因此,上拉电阻确保稳定状态为正电压,下拉电阻确保稳定状态为零电压(地)。

转换

开关设计用于控制两个或多个引脚之间的电流。开关有各种形状、尺寸和包装。一些被设计为简单的开/关,而另一些可以用来改变从一组引脚到另一组引脚的电流。像按钮一样,开关有多种安装选择,从 PCB(也称为通孔)到面板安装,以便安装在外壳中。图 7-19 显示了各种开关。

A447395_1_En_7_Fig19_HTML.jpg

图 7-19。

Various switches

只有一个电极(腿或边)的开关称为单极开关。能够将电流从一组电极转移到另一组电极的开关叫做双极开关。每极只有一个次级连接的开关称为单投开关。从一组极点断开并连接到另一组极点同时保持公共输入的开关称为双掷开关。这些通常被组合并形成如下的开关类型(或种类)。

  • SPST:单刀单掷
  • DPST:双刀单掷
  • SPDT:单刀双掷
  • DPDT:双刀双掷
  • 3PDT:三杆双掷

您可能会遇到其他变体。我喜欢这样保持笔直;如果我有刚开/关的情况,我想要一个单掷开关。多少极取决于我想同时接通或断开多少根电线或电路。对于双掷开关,当我有“A”条件和“B”条件时,我使用这些开关,当 B 断开时,我希望 A 接通,反之亦然。当我需要“A”、“B”和“off”时,我有时会使用多个投掷开关,其中我使用中间位置(投掷)作为 off。你可以非常有创意地使用开关!

晶体管

晶体管(双极晶体管)被设计成在一个周期内开关电流或放大电流的波动。有趣的是,用来放大电流的晶体管取代了真空管。如果你是一个音响发烧友,你可能知道很多关于真空管。当电阻器在开关模式下工作时,其行为类似于继电器,但其“关”位置仍允许少量电流流动。晶体管用于音频设备、信号处理和开关电源。图 7-20 显示了两种晶体管。

A447395_1_En_7_Fig20_HTML.jpg

图 7-20。

Transistors

晶体管有各种各样的种类、封装和额定值,适合不同的解决方案。

稳压器

电压调节器(线性电压调节器)被设计成保持电流恒定。当我们需要调节或降低来自电源的电流时,电压调节器经常出现在电子设备中。例如,我们想为一个电路提供 5V 电压,但只有 9V 电源。电压调节器(大致)通过吸收电流并通过散热器耗散多余的电流来实现这一点。因此,电压调节器有三个分支:正电流输入、负电流和正电流输出。它们的典型形状如图 7-21 所示,但也存在其他种类。

A447395_1_En_7_Fig21_HTML.jpg

图 7-21。

Voltage regulators

延伸出电压调节器的板上的小孔是安装散热器的地方。电压调节器通常被编号以匹配其额定值。例如,LM7805 产生 5V,而 LM7833 产生 3.3V。

图 7-22 显示了一个使用电压调节器向试验板上的 3.3V 电路供电的示例。该电路设计有电容来帮助平滑或调节电源。请注意,电容的额定值为 uF,即微法。

A447395_1_En_7_Fig22_HTML.jpg

图 7-22。

Power supply circuit on a breadboard with voltage regulator

分线板和电路

分线板是我们 IOT 解决方案的模块化构建模块。它们通常将几个组件组合在一起,形成一种功能,如测量温度、读取 GPS 数据、通过蜂窝服务进行通信等。图 7-23 显示了两个分线板。左边是 adafruit 模数转换器(adafruit.com/products/1083),右边是 Adafruit 气压传感器转接板( https://www.adafruit.com/product/1603 )。

A447395_1_En_7_Fig23_HTML.jpg

图 7-23。

Breakout boards

无论何时设计电路或 IOT 解决方案,都应该考虑尽可能使用分线板,因为它们可以简化元件的使用。以大气压力传感器为例。Adafruit 设计了这种板,因此我们使用它所需要做的就是连接电源,并通过其 I2C 总线将其连接到我们的 IOT 设备。I2C 总线是一种快速数字协议,它使用两条线(加上电源和地)从电路(或设备)读取数据。

因此,没有必要担心如何将传感器连接到其他组件来使用它-只需像任何 I2C 设备一样连接它并开始读取数据!我们将在本书后面的项目中使用几个分线板。

使用试验板构建电路

如果你一直跟随书中的项目,你已经遇到了一个制作非常简单电路的试验板。回想一下第三章,试验板是我们用来插入元件形成电路的工具。从技术上讲,我们使用的是无焊试验板。焊料试验板具有相同的布局,只是在 PCB 上只有通孔焊点。

Why are they Called Breadboards?

在微电子和分立元件广泛用于实验的辉煌时代,当我们想要制作电路原型时,有些人会使用一块钉有钉子的木头(有时是网格图案),通过在钉子上缠绕电线来连接(称为“线路”)。一些人使用厨房里的试验板来制作绕线原型。这个名字一直沿用至今。

试验板允许我们为电路或简单的临时电路创建原型,而不必花费时间(和成本)来制作印刷电路板。原型制作是通过构建和测试您的想法来试验电路的过程。事实上,一旦我们让电路正常工作,就可以利用试验板布局来帮助我们设计 PCB。图 7-24 显示了几个试验板。

A447395_1_En_7_Fig24_HTML.jpg

图 7-24。

Assorted breadboards

回想一下,大多数试验板(有几个品种)都有一个中心凹槽(称为峡谷)或沿着板中心的印刷线。这表示垂直于通道延伸的端子排未连接。也就是说,一侧的端子排没有连接到另一侧。这允许我们插入封装为两排引脚的集成电路(IC)或芯片。因此,我们可以将 IC 插入试验板,在试验板的每一侧都有一组引脚。我们在下面的例子中看到了这一点。

大多数试验板还有一组或多组电源轨,它们平行于沟道连接在一起。如果有两个集合,则这两个集合不会连接在一起。电源轨可能有一条彩色参考线,但这仅用于参考;你可以把其中一个变成积极的,另一个变成消极的。最后,一些试验板对端子排进行编号。这些仅供参考,没有其他意义。然而,它们可以方便地在你的工程笔记本上做笔记。图 7-25 显示了试验板的命名以及端子板和电源轨是如何连接在一起的。

Note

电源轨组没有连接在一起。如果你想在试验板的两侧都有电源,你必须使用跳线来连接它们。

A447395_1_En_7_Fig25_HTML.jpg

图 7-25。

Breadboard layout Fritzing: A Breadboarding Software Application

这本书里的面包板图是用一个叫 Fritzing ( http://fritzing.org/home/ )的程序制作的。这个开源应用允许你在试验板上创建一个电路的数字表示。使用起来相当方便。如果你发现自己想设计一个原型电路,使用 Fritzing 可以帮助你节省大量的试验和错误。另外,Fritzing 允许您在电子原理图或 PCB 布局视图中看到相同的电路。我建议下载并试用这个应用。

有时需要将电路与代码分开测试。例如,如果我们想确保所有设备都正确连接在一起,我们可以使用试验板电源为电路供电。这样,如果出现严重问题,我们就不会冒损坏 IOT 设备的风险。大多数试验板电源都建立在一个小 PCB 上,带有一个用于壁式电源的桶形插孔,两组用于插入试验板上电源轨的引脚,一个关闭开关(非常方便),有些可以产生不同的电压。图 7-26 展示了我最喜欢的 Sparkfun ( sparkfun.com/products/13157)的试验板电源之一。

A447395_1_En_7_Fig26_HTML.jpg

图 7-26。

Breadboard power supply

如果我们的电路需要比单个试验板更多的空间,您可以通过简单地跳过电源轨并继续电路来使用多个试验板。为了方便起见,一些试验板可以使用侧面的小块和插槽进行连接。最后,大多数试验板还带有背胶,您可以用它来安装在板上或外壳或类似的工作空间内。如果你决定使用背胶,要预先警告他们不能轻易拆开-他们会很好地保持不动。

现在我们对试验板的工作原理有了更多的了解,让我们来讨论一下我们的 IOT 解决方案中用来收集数据的组件:传感器。

什么是传感器?

传感器是一种测量物理世界现象的装置。这些现象可以是你看到的东西,像光、气体、水蒸气等等。它们也可以是你感觉到的东西,像温度、电、水、风等等。人类的感觉就像传感器一样,让我们能够体验周围的世界。然而,有些东西你的传感器看不到或感觉不到,比如辐射、无线电波、电压和安培数。在测量这些现象时,传感器的工作是以电压表示或数字的形式传达测量结果。

传感器有多种形式。它们通常是为单一用途设计的低成本设备,处理能力有限。大多数简单的传感器都是分立元件;甚至那些具有更复杂部件的设备也可以被视为单独的组件。传感器可以是模拟的,也可以是数字的,通常只用于测量一种东西。但是越来越多的传感器模块被设计用来测量一系列相关的现象,例如来自 spark fun Electronics(www.sparkfun.com/products/10586)的 USB 天气板。

以下部分探讨传感器如何测量数据、如何存储数据,以及一些常见传感器的示例。

传感器如何测量

传感器是基于其化学和机械结构的独特属性产生电压的电子设备。一些人对传感器的常见误解之一是,它们不会操纵它们被设计来测量的现象(改变事件或数据)。相反,传感器对一些物理变量进行采样,并将其转换为成比例的电信号(电压、电流、数字信号等)。

例如,湿度传感器测量空气中水(湿气)的浓度。湿度传感器对这些现象做出反应,并产生一个电压,微控制器或类似设备可以读取该电压,并使用该电压来计算秤的数值。一种基本的低成本湿度传感器是可从大多数电子商店( https://www.adafruit.com/product/385 )买到的 DHT-22。图 7-27 显示了一个典型的 DHT-22 传感器。

A447395_1_En_7_Fig27_HTML.jpg

图 7-27。

DHT-22 Humidity Sensor

DHT-22 设计用于测量温度和湿度。它在输出端(数据引脚)产生一个数字信号。虽然使用简单,但它有点慢,应该用于以合理的慢速度跟踪数据(不超过每 3 或 4 秒一次)。

当该传感器产生数据时,数据以一系列高(解释为 1)和低(解释为 0)电压的形式传输,微控制器可以读取并使用这些电压来形成一个值。在这种情况下,微控制器从传感器读取长度为 40 位(40 个高或低电压脉冲)的值,即 5 个字节,并将其放入程序变量中。前两个字节是湿度值,后两个字节是温度值,第五个字节是校验和值,以确保读数准确。幸运的是,所有这些艰苦的工作都以专门为 DHT-22 和类似传感器设计的库的形式为您完成了。

DHT-22 产生一个数字值。不是所有的传感器都这样做;一些产生电压范围。这些被称为模拟传感器。让我们花一点时间来理解它们的区别。这将成为您规划和构建传感器节点的重要信息。

模拟传感器

模拟传感器是产生电压范围的装置,通常在 0 到 5 伏之间。需要一个模数转换电路将电压转换成数字。但事情并没有那么简单(是吗?).模拟传感器的工作原理类似于电阻,当连接到 GPIO 引脚时,通常需要另一个电阻来“上拉”或“下拉”电压,以避免被称为浮动的杂散电压变化。这是因为流过电阻的电压在时间和幅度上都是连续的。

因此,即使传感器不产生值或测量值,仍有电压流过传感器,可能导致虚假读数。您的项目需要明确区分关(零电压)和开(正电压)。上拉和下拉电阻确保您拥有这两种状态之一。模数转换器负责从传感器读取电压,并将其转换为可被解读为数据的值。

采样时(从传感器读取值时),电压读数必须解释为给定传感器指定范围内的值。请记住,比方说,一个模拟传感器输出的 2 伏电压可能与另一个模拟传感器输出的 2 伏电压不是一回事。每个传感器的数据手册都向您展示了如何解释这些值。

如您所见,使用模拟传感器比使用 DHT-22 数字传感器要复杂得多。稍加练习,您会发现,一旦了解如何将模拟传感器连接到微控制器,以及如何在传感器校准工作的范围内解释其电压,大多数模拟传感器都不难使用。

数字传感器

像 DHT-22 这样的数字传感器被设计成使用串行传输产生一串比特(一次一个比特)。然而,一些数字传感器通过并行传输产生数据(一次一个或多个字节 6 )。如前所述,这些位表示为电压,其中高电压(比如 5 伏)或开是 1,低电压(0 甚至-5 伏)或关是 0。这些开和关值序列称为离散值,因为传感器以脉冲形式产生一个或另一个值,即开或关。

与模拟信号相比,数字传感器的采样频率更高,因为它们生成数据的速度更快,而且不需要额外的电路来读取数值(例如 A/D 转换器以及将数值转换为刻度的逻辑或软件)。因此,数字传感器通常比模拟传感器更精确和可靠。但是,数字传感器的精度与其用于采样数据的位数成正比。

数字传感器最常见的形式是按钮或开关。什么,按钮是传感器?为什么,是的,这是一个传感器。考虑一下安装在家庭安全系统窗户上的传感器。这是一个简单的开关,当窗户关闭时关闭,当窗户打开时打开。当开关连接到电路中时,当车窗关闭且开关闭合时,电流是恒定且不间断的(使用上拉电阻测量正电压),但当车窗和开关打开时,电流中断(测量零电压)。这是最基本的开关传感器。

大多数数字传感器都是由几个元件组成的小电路,用于产生数字数据。与模拟传感器不同,读取它们的数据很容易,因为这些值无需转换就可以直接使用(除了转换到其他刻度或测量单位)。有些人可能认为这比使用模拟传感器更困难,但这取决于你的观点。电子爱好者会认为使用模拟传感器更容易,而程序员会认为数字传感器更容易使用。

现在,让我们看看一些可用的传感器及其测量的现象类型。

传感器的例子

观察某物的 IOT 解决方案可能使用至少一个传感器和一种方法来读取和解释数据。你可能在想各种各样有用的东西,你可以在家里或办公室,甚至在你的院子里或周围测量。你可能想测量新日光浴室的温度变化,检测邮递员何时将最新的通知扔进你的邮箱,或者记录你的狗使用狗门的次数。我希望到现在为止,当你想象你能测量什么的时候,你能看到这些只是冰山一角。

有哪些类型的传感器可用?以下部分描述了一些比较流行的传感器及其测量内容。我还提供了一些关于如何在 IOT 项目中使用传感器的提示。然而,这只是不断增长的可用传感器阵列中的一部分。细读 Mouser Electronics ( www.mouser.com )、spark fun Electronics(www.sparkfun.com)和 Adafruit Industries ( www.adafruit.com )等在线电子产品供应商的目录,会发现更多的例子。

我还包括一些传感器类型的流行例子的照片。

加速计

这些传感器测量传感器或它所连接的任何东西的运动或移动。它们被设计用来感应几个轴上的运动(速度、倾斜度、振动等等)。一些包括回转仪特征。大多数是数字传感器。Wii 双截棍(或 WiiChuck)包含一个复杂的加速度计,用于跟踪运动。啊哈:现在你知道 Wii 附带的那些有趣的小东西的秘密了吧!如果您的 IOT 项目涉及运动中的物体,并且对该运动的观察提供了有用的信息,您可能需要添加加速度计。

音频传感器

也许这是显而易见的,但麦克风是用来测量声音的。大多数是模拟的,但一些更好的安全和监控传感器具有数字版本,用于传输数据的更高压缩。家庭安全、儿童监控、捉鬼或听觉健康等 IOT 项目都可以从集成音频传感器中受益。

条形码阅读器

这些传感器被设计用来读取条形码。最常见的是,条形码阅读器生成代表条形码的数字等价物的数字数据。这种传感器通常用在库存跟踪系统中,以在工厂或运输过程中跟踪设备。它们数量众多,而且许多价格经济实惠,使您能够将它们整合到自己的项目中。如果您的 IOT 项目需要从对象中捕获数据,您可能需要考虑条形码。

例如,如果您希望感应停车场用户何时进入或离开无人值守的停车场,您可以在门口放置一个条形码读取器,读取您设计并分发给用户的条形码。当汽车停在大门前时,条形码阅读器可以读取条形码,记录入口,并升起大门。如果你曾经在大城市生活过,在受控的办公大楼工作过,或者是通勤学生,你可能会遇到这样的停车解决方案。

生物传感器

读取指纹、虹膜或掌纹的传感器包含一个用于识别模式的特殊传感器。鉴于指纹和掌纹等图案的独特性,它们是安全访问系统的优秀组件。大多数生物传感器产生一组代表指纹或掌纹的数字数据。需要更高安全级别的 IOT 项目可能希望包括生物传感器来帮助识别系统的用户。

电容式传感器

脉搏传感器是电容传感器的一种特殊应用,设计用于测量您的脉搏率,通常使用指尖作为感测部位。称为脉搏血氧仪(一些医疗专业人员称为 pulse-ox)的特殊设备通过电容传感器测量脉搏率,并通过光传感器确定血液中的含氧量。

如果你拥有现代电子设备,你可能会遇到触敏按钮,它们使用特殊的电容传感器来检测触摸和压力。如果你的 IOT 项目需要测量任何类型的运动,或对触摸做出反应,电容传感器可以帮助提供一个未来的非触觉界面。最新 MacBook Pro 上的 Touch Bar 就是这种解决方案的一个例子。

图 7-28 展示了两个你可以从 Adafruit 买到的触摸感应模块的例子。在这种情况下,我们看到用于瞬时开关( www.adafruit.com/products/1374 )和拨动开关( www.adafruit.com/products/1375 )的分线板。

A447395_1_En_7_Fig28_HTML.jpg

图 7-28。

Touch Capacitive Sensor Breakout Boards

硬币传感器

这是最不寻常的传感器之一。这些装置 7 就像典型自动售货机上的投币口。像它们的商业等价物一样,它们可以被校准以感应何时插入一定大小的硬币。虽然不像商业单位那样复杂,可以区分真假硬币,硬币传感器可以用来为您的项目添加一个新的维度。对于父母来说,一个伟大而实用的 IOT 项目将是一个投币式 WiFi 站,孩子们可以在那里购买自己的上网时间。这不仅能让他们避免过度使用互联网,还能帮助他们学会如何安排自己的零花钱。现在,这应该可以防止孩子们在互联网上花太多时间!

电流传感器

这些设计用于测量电压和电流。有些是为测量变化而设计的,而有些是为了测量负载。IOT 的项目集成电路或需要监测电流将需要一个电流传感器。这些可能是一些更深奥的项目,但您可以使用这些传感器来监控现有解决方案的行为,而无需修改它们。

例如,如果您想采用传感器来观察制造机器,您可以添加传感器来监控各种组件的电流。也就是说,您可以记录电压何时施加到电机、致动器,甚至警告灯,以确定设备何时(或多少)被激活。然而,作为一个业余爱好者,你更可能有兴趣建立自己的万用表或类似的工具。

弯曲/力传感器

电阻传感器测量一块材料的弯曲或压力对传感器的影响。弯曲传感器可能有助于测量扭转效应或测量手指运动(就像任天堂的电动手套一样)。当传感器弯曲时,传感器电阻增加。例如,如果您想创建一个 IOT 解决方案来实时报告您的钓鱼体验,您可能希望在您的鱼竿上使用一个 flex 传感器来报告您每次投掷或击中鱼饵的情况。

气体传感器

有很多种气体传感器。有些测量潜在有害气体,如液化石油气和甲烷以及其他气体,如氢气、氧气等。其他气体传感器与光传感器相结合来感测空气中的烟雾或污染物。下次当你从烟雾探测器中听到那种警告性的、通常令人讨厌的低电量警告声 8 时,想想那个设备包含了什么。为什么,这是一个传感器节点!如果您的 IOT 项目需要观察或检测任何形式的气体,特别是如果它涉及到对某些气体或其水平的反应,您将需要使用适当的气体传感器。

光传感器

测量光强或缺光的传感器是特殊类型的电阻:光敏电阻(ldr),有时也称为光敏电阻或光电池。因此,它们本质上是模拟的。

如果你有一台 Mac 笔记本电脑,当你的发光键盘在弱光下自动打开时,你很可能已经看到了光敏电阻的作用。特殊形式的光传感器可以检测其他光谱,如红外线(如旧的电视遥控器)。例如,如果您希望您的 IOT 项目自动调整其显示器的亮度,光传感器就是您需要的组件。

A447395_1_En_7_Fig29_HTML.jpg

图 7-29。

Mini Photocell

下面显示了光传感器的两个例子。图 7-29 显示了一个典型的微型光电池( www.sparkfun.com/products/9088 ),图 7-30 显示了一个颜色传感器( www.adafruit.com/products/1334 )。

A447395_1_En_7_Fig30_HTML.jpg

图 7-30。

Color Sensor Breakout Board

液体流量传感器

这些传感器类似于阀门,内嵌在管道系统中。他们测量液体通过时的流量。基本的流量传感器使用旋转轮和磁铁来产生霍尔效应(快速开/关序列,其频率等于通过的水量)。如果你的 IOT 项目涉及任何形式的液体,如花园池塘或灌溉系统,了解水的流动可能有助于学习或观察某些东西。

液位传感器

一种特殊的电阻固态器件可以用来测量水体的相对高度。一个例子是当水位高时产生低电阻,当水位低时产生高电阻。像液体流量传感器一样,液位传感器通常用于相同的解决方案中。图 7-31 显示了一个典型的液位传感器,当水位上升时,浮子关闭开关。

A447395_1_En_7_Fig31_HTML.jpg

图 7-31。

Water-Level Sensor

位置传感器

现代智能手机有 GPS 传感器来感应位置,当然 GPS 设备使用 GPS 技术来帮助你导航。幸运的是,GPS 传感器以低成本的形式提供,使您能够将位置检测添加到您的项目中。GPS 传感器生成经度和纬度形式的数字数据,大多数还可以感知海拔高度。如果你的 IOT 项目需要报告它的位置,GPS 传感器可以给你非常准确的读数。然而,像大多数传感器一样,GPS 传感器可能有一定程度的不准确性。根据你需要定位的距离,你可能需要多花一点钱买一个更精确的 GPS 传感器。

磁条阅读器

这些传感器从磁条(像信用卡上的磁条)读取数据,并返回数字形式的字母数字数据(实际的字符串)。包含安全组件的 IOT 项目可能希望使用磁条读取器来帮助识别用户。当与密码和生物传感器结合使用时,安全性可以大大提高。也就是说,某人必须知道某样东西(密码或 pin),拥有某样东西(带有磁条的安全卡,磁条上有关键短语、数字、用户 id 等编码)。),并在获得访问权限之前被验证为某人(指纹)。

磁力计

这些传感器通过磁场强度来测量方向。指南针是一种用来寻找磁北的传感器。一些磁力计提供多个轴,以便更好地检测磁场。这是另一个你可能不会经常遇到的传感器,但是如果你的 IOT 项目需要测量来自电机或大气现象的磁场,你可能想看看磁力计。

湿度传感器

湿度传感器测量物质(如土壤)或空气中的湿度。它们通常以电压读数的形式发送数据,低值表示水分较少。您经常会在大气项目甚至工厂监控解决方案中发现湿度传感器。图 7-32 显示了一个典型的土壤湿度传感器()。请注意,尖头是传感器插入土壤的部分。

A447395_1_En_7_Fig32_HTML.jpg

图 7-32。

Soil Moisture Sensor

接近传感器

接近传感器通常被认为是距离传感器,它使用红外线或声波来检测物体的距离、运动或距离。受低成本机器人套件的欢迎,视差超声波传感器通过感应发送脉冲和接收脉冲之间的时间量(回声),使用声波来测量距离。对于近似的距离测量, 9 把时间转换成距离是一个简单的数学问题。如果您正在构建一个检测移动或接近的 IOT 项目,如运动感应相机,您可能需要使用接近传感器。

下图显示了两种类型的接近传感器。图 7-33 展示了一个流行的被动红外传感器(PIR)运动传感器( www.sparkfun.com/products/13285 )。

A447395_1_En_7_Fig33_HTML.jpg

图 7-33。

PIR Motion Sensor

图 7-34 展示了一个超声波传感器( www.sparkfun.com/products/13959 ),用在从机器人到无人机的很多项目中。

A447395_1_En_7_Fig34_HTML.jpg

图 7-34。

Ultrasonic Proximity Sensor

辐射传感器

更重要的传感器是那些探测辐射的传感器。这也可能是电磁辐射(也有传感器),但盖革计数器使用辐射传感器来检测有害的电离。事实上,使用一个传感器和一个 Arduino(以及一些电子元件)来建造你自己的盖革计数器是可能的。作为业余爱好者,你可能不会遇到这种传感器。然而,有几个工具包可以用来构建你自己的盖革计数器,比如 Adafruit ( www.adafruit.com/products/483 )的那些。

RFID 传感器

射频识别使用无源设备(有时称为 RFID 标签)通过电磁感应使用射频来传输数据。例如,RFID 标签可以是信用卡大小的塑料卡、标签或类似的包含特殊天线的东西,通常以线圈、细线或箔层的形式调谐到特定频率。

当标签被放置在阅读器附近时,阅读器发射无线电信号;标签可以使用电磁能量以无线电信号的形式传输嵌入在天线中的非易失性消息,该无线电信号然后被转换成字母数字串。 10 RFID 传感器是安防系统的另一个好选择。如果你有宠物,你可能想拜访你的兽医询问 RFID 传感器作为隐藏的主人识别标签。如果你知道频率,你甚至可以用它来帮助检测你的宠物何时通过宠物门。图 7-35 显示了一个 RFID 阅读器(传感器),您可以使用它通过 USB ( www.sparkfun.com/products/9963 )读取 RFID 标签。

A447395_1_En_7_Fig35_HTML.jpg

图 7-35。

RFID Reader

速度传感器

像流量传感器一样,许多自行车上的简单速度传感器使用磁铁和簧片开关来产生霍尔效应。频率结合车轮的周长可以用来计算速度,以及随着时间的推移,行驶的距离。如果您的 IOT 解决方案需要读取运动,您可以使用磁开关和磁铁来检测旋转。例如,自行车速度计通常使用磁铁和磁性开关来检测转数、车轮周长和动作频率,以计算速度。

开关和按钮

这些是最基本的数字传感器,用于检测某个东西是被设置(开)还是被重置(关)。尽管如此,你可以使用开关和按钮来建立一个用户界面,用于控制其他设备,甚至是打开它!

倾斜开关

这些传感器可以检测设备何时向某个方向倾斜。虽然非常简单,但它们对于低成本运动检测传感器非常有用。它们是数字的,本质上是开关。如果您的 IOT 解决方案需要检测设备何时倾斜,您可以使用倾斜传感器在特定倾斜角度触发。例如,一些现代摩托车使用倾斜传感器来打开转向灯——前照灯倾斜,以改善夜间转弯时的视野。

触摸传感器

形成小键盘、键盘、定点设备等的触敏膜是一种有趣的传感器形式。你可以使用像这样的触摸感应设备来收集人类的数据。触摸传感器可以帮助您为您的 IOT 项目构建一个用户界面,该界面可以以低调的形式呈现,或者节省控制台、项目箱等的空间。

视频传感器

如前所述,有可能获得非常小的视频传感器,这些传感器使用摄像机和电路来捕捉图像并将其作为数字数据传输。如果要将视频元素整合到 IOT 项目(如安全解决方案)中,可以添加摄像机或视频传感器来捕捉视觉组件,以帮助提供事件测量之外的信息。也就是说,你可以查看一张照片,了解更多信息,而不仅仅是移动或靠近设备的东西。例如,您可以构建一个 IOT 项目,该项目可以检测运动,并在某个物体足够接近或者运动速度超过某个阈值时拍摄照片。 11

天气传感器

温度、气压、降雨量、湿度、风速等传感器都属于天气传感器。大多数生成数字数据,并可以结合起来创建全面的环境解决方案。图 7-36 显示了 BMP280 压力和温度传感器( www.adafruit.com/products/2651 )的通用转接板。

A447395_1_En_7_Fig36_HTML.jpg

图 7-36。

BMP280 Pressure and Temperature Sensor

有了这个和其他易于使用的传感器,就有可能用大约十几个廉价的传感器、你的 MicroPython 板和一点解释和组合数据的程序来建立你自己的气象站。事实上,我们将在第十章中讨论这个问题!

提示–如果您想查看更多传感器,您可以从 Adafruit ( www.adafruit.com/category/35 )或 Sparkfun ( www.sparkfun.com/categories/23 )购买任意数量的传感器。

I Want to Learn More!

如果你发现你需要或想要学习比我在这一章中介绍的更多的关于电子学的知识,或者你想要学习更多的关于你在一个更高级的 IOT 项目中需要的电子学知识,你可能想要考虑在一所社区大学上一门课程或者尝试一门自定进度的电子学课程。

我发现的最好的自定进度课程之一包括查尔斯·普拉特的一套电子书籍。我发现这些书写得非常好,为许多人打开了学习电子学的大门,而不必花数年时间学习乏味的(但同样重要的)电子学理论和数学。最重要的是,它们不是以沉闷的教科书式的事实-事实-事实-问题的节奏写成的。它们是由一位世界著名的专家撰写的,这位专家具有以易于阅读和理解的风格呈现材料的天赋。我向任何想学习更多电子知识的人推荐以下书籍。

  • 制作:电子,第二版(奥赖利,2015),查尔斯·普拉特
  • 制造商:更多电子公司(奥赖利,2014 年),查尔斯·普拉特
  • 电子元件百科全书第 1 卷(O'Reilly,2012),查尔斯·普拉特
  • 电子元件百科全书第 2 卷(O'Reilly,2014),查尔斯·普拉特
  • 电子元件百科全书第 3 卷(O'Reilly,2016),查尔斯·普拉特

他的百科全书系列的第三卷包括对传感器的深入研究:高级 IOT 项目的必备。

maker shed(makershed.com/collections/electronics)出售配套套件,其中包含完成《制作:电子》和《制作:更多电子书籍》中的实验所需的所有部件。这些书和工具包一起构成了一个极好的自定进度的学习体验。

摘要

学习如何把使用电子产品作为一种爱好或者创造一个 IOT 解决方案并不需要终生学习或者改变职业。事实上,学习如何使用电子设备是体验 IOT 乐趣的一部分!我遇到过许多自学电子的人,虽然大多数人承认正式学习对于掌握这个主题是必不可少的,但你可以自学很多东西——足以熟练使用 IOT 项目中常见的基本电子元件。

本章介绍了电子元件的基础知识,包括试验板、常见元件和示例电路的使用。这一点和一些如何使用万用表的关键知识将会让你在精通电子学的道路上走得很远。我们还了解了 IOT 解决方案的关键组件之一——传感器。我们发现了它们通信的两种方式(数字和模拟)以及一些可用的传感器类型。

在下一章,我们将深入我们的第一个电子项目——相当于“你好,世界!”硬件项目。我们将看到如何将我们的 MicroPython 板连接到几个组件,并编写一个 Python 程序来控制它们。酷!

Footnotes 1

但是,正规的训练是没有替代品的!如果你想在本章的教程之外探索电子学,你可以考虑正式的培训,甚至是一个自定进度的课程,如侧栏“我想了解更多!”

2

旧的万用表有一个模拟仪表。如果你想要一点老学校的感觉,你仍然可以找到他们。

3

是的,有点强迫症。检查,再检查,再检查。

4

https://learn.sparkfun.com/tutorials/what-is-electricity

5

欧姆定律的变体( https://en.wikipedia.org/wiki/Ohm's_law )。

6

这取决于并行缓冲区的宽度。8 位缓冲区一次可以传输 1 个字节,16 位缓冲区一次可以传输 2 个字节,依此类推。

7

www.sparkfun.com/products/11719

8

我相信越多越好的理论,在家里有很多探测器,这很好,但是当电池耗尽时,我永远也不会知道哪个探测器在响!当它们只发出一两声哔哔声,然后就无声无息了,这就变得令人沮丧。幸运的是,我已经用最新的 10 年电池变体替换了几乎所有的电池。不再有野鹅追逐!

9

精度可能取决于环境变量,如海拔、温度等。

10

http://en.wikipedia.org/wiki/Radio-frequency_identification

11

你有没有想过,如果你能抓拍到一张正在你的花园里吃东西的动物的照片,那该有多好?用一个接近传感器和一个红外线照相机建造你自己的动物照相机!

八、项目一:MicroPython 风格的你好世界

这是本书最有趣的部分——从事 MicroPython 项目!正是在这一点上,我们已经学会了如何用 MicroPython 编写代码,现在对硬件甚至如何使用分立电子器件和分线板有了更多的了解。

本章介绍了如何构建 MicroPython 项目。因此,我们还需要学习一些东西,包括在我们的 MicroPython 板上安装和运行项目的技术和程序。这一章将介绍那些使你的 MicroPython 项目成功所需要的东西。因此,这一章有点长,即使你不打算实现这个项目,也应该被认为是必读的。

为了帮助实现这些目标并使事情变得简单一点(因为这是您的第一个真正的 MicroPython 项目),我们将放弃将这个项目连接到互联网,这样我们可以使这个项目更容易实现。后面的章节会有更少的介绍性信息,并且会更多地关注项目,因为随着我们的进展,它们会变得更加复杂。

正如您将看到的,所有项目章节的格式都是相同的:首先是项目概述,然后是所需组件的列表以及如何组装硬件。一旦我们掌握了如何连接硬件,我们就可以看到如何连接一切,并开始编写代码。每章都以如何执行项目结束,并附有一个项目运行的例子和美化项目的建议。

如果你现在想知道你需要哪种板来完成这些项目,不要担心,因为我们将在本章中看到如何在 WiPy 和 Pyboard 上实现这个项目,以及不同之处的演示。

那么,让我们开始我们的第一个 MicroPython 项目吧!

概观

在这一章中,我们将设计并构建一个 MicroPython 时钟。我们将同时使用 SPI 和 I2C 分线板。我们将使用小型有机发光二极管(有机发光二极管) 1 显示器,该显示器使用 SPI 接口和基于硬件的实时时钟(RTC),该时钟使用 DS1307 芯片和电池在项目关闭时记录时间。我们将使用基于硬件的 RTC,并在小型有机发光二极管显示器上显示当前日期和时间,而不是简单地连接到互联网上的网络时间协议(NTP)服务器。这不仅使项目变得更小,还演示了如何为可能没有连接到互联网的项目使用 RTC。

虽然 Pyboard 具有 RTC 电路,您可以连接外部电池,以便在电路板关闭时保持 RTC 供电(参见 http://docs.micropython.org/en/latest/pyboard/pyboard/quickref.html 的 VBAT 引脚参考),但大多数片上系统(SOC)板,如 WiPy 和其他基于 Espressif 的板,不支持此功能。因此,我们将使用外部 RTC,以便您可以在其他板上使用该项目。

正如您将看到的,需要相当多的布线,并且理解硬件功能是编写代码的必要条件,这就是为什么我们在前几章花时间讨论固件和各种低级硬件控制。你需要这些技能和知识来完成这个项目。

虽然一个时钟可能听起来很简单,但这个项目将引导您完成组装硬件和编写代码所需的所有步骤。正如您将看到的,除了硬件布局和引脚数之外,WiPy 和 Pyboard 之间还有一些明显的差异。事实上,我们将看到在我们如何为每一个写代码方面有一些不同。

此外,该项目很小而且很简单,因此我们可以专注于过程,然后我们可以将其应用于更高级的项目。事实上,我们会看到,即使是一个相对简单的项目也可能有意想不到的难度。但是不要担心,因为这一章记录了你完成这个项目需要做的所有事情。

这个项目的来源很多。以下链接包括本项目使用的背景数据。学习如何应用来自这些网站的知识不是一件容易解释或学习的事情;然而,通过查看这些资源(如果你愿意的话),它应该可以帮助你理解如何研究和计划你自己的项目。

注意使用的网站。一个好的实践是从 Adafruit 和 MicroPython 学习、blobs 和论坛开始。然后检查驱动程序。也就是说,先做研究,找到所有你能找到的参考资料。如果你从 Adafruit 或 Sparkfun 上找到了不错的教程,你可以把它们下载到你的平板电脑上,或者打印出来供以后阅读。更重要的是,在开始使用硬件或编写代码之前,花时间阅读参考资料,以便尽可能多的理解。通过了解一些简单的事情,如如何将电路板连接到设备以及如何使用驱动程序,您可以节省大量时间。 3

Which Driver Do I Use?

您可能会遇到这样的情况:您为您想要使用的硬件找到了多个驱动程序。事实上,我为有机发光二极管展示会找到了三个司机。它们之间的差异是微妙的,看起来至少有一个是为特定平台编写的。

其实上面列出的是最好用的一个。尽管如此,它需要做一些小的改动才能与 WiPy 和 Pyboard 一起使用。我将向您展示这些变化,正如您将看到的,它们并不难发现和修复(例如,当 MicroPython 抛出异常时,它将向您显示问题的根源)。

如果您遇到类似的情况——有多个驱动程序可供选择,您可能希望尝试每个驱动程序,直到找到最适合您的硬件和项目的驱动程序。有时,在这种情况下,这是真的,一个驱动程序可能不可行,或者另一个可能缺乏您需要的功能。例如,我发现的一个驱动程序不支持文本,所以它不能用于这个项目,另一个需要进行重大修改才能用于 Pyboard。诀窍是找到修改量最少但效果最好的驱动程序。

现在让我们看看这个项目需要哪些组件,然后我们将看看如何将所有组件连接在一起。

必需的组件

表 8-1 列出了你需要的部件。您可以从 Adafruit ( adafruit.com)、Sparkfun ( sparkfun.com)或任何出售电子元件的电子商店单独购买元件。如果您想购买这些组件,可以链接到供应商。当列出同一对象的多行时,您可以选择其中一行——您不需要两个都要。此外,您可能会找到销售这些组件的其他供应商。你应该货比三家,找到最好的交易。显示的成本是估计值,不包括任何运输成本。

表 8-1。

Required Components

| 成分 | 数量 | 描述 | 费用 | 链接 | | --- | --- | --- | --- | --- | | MicroPython 板 | one | 带标题的 Pyboard v1.1 版 | $45-50 | [`https://www.adafruit.com/product/2390`](https://www.adafruit.com/product/2390)[`https://www.adafruit.com/product/3499`](https://www.adafruit.com/product/3499) | | [`https://www.sparkfun.com/products/14413`](https://www.sparkfun.com/products/14413) | | [`https://store.micropython.org/store`](https://store.micropython.org/store) | | WiPy | $25 | [`https://www.adafruit.com/product/3338`](https://www.adafruit.com/product/3338) | | [`https://www.pycom.io/product/wipy/`](https://www.pycom.io/product/wipy/) | | 有机发光二极管显示器 | one | 基于 ssd1306 的 SPI 显示器 | $18 | [`https://www.adafruit.com/product/661`](https://www.adafruit.com/product/661) | | RTC 分线板 | one | 带备用电池的 RTC 模块 | $15 | [`https://www.sparkfun.com/products/12708`](https://www.sparkfun.com/products/12708) | | 试验板 | one | 原型板,半尺寸 | $5 | [`https://www.sparkfun.com/products/12002`](https://www.sparkfun.com/products/12002) | | 跳线 | Twelve | M/M 跳线,6”(成本是一套 10 根跳线) | $4 | [`https://www.sparkfun.com/products/8431`](https://www.sparkfun.com/products/8431) | | 力量 | one | 从电脑获取电源的 USB 电缆 |   | 使用您的备件 | | one | USB 5V 电源和电缆 |   | 使用您的备件 | | 纽扣电池 | one | 参见 RTC 分线板数据表 | $3-5 | 常见的 |

请注意,在源代码中,最后两项是“使用您的备件”。这是指我们大多数人手头都有这些东西,而且在某些情况下还很丰富。例如,如果你买了一块 MicroPython 板,如果你还没有电缆,你也需要买一根。此外,随着 USB 充电设备的激增,很难想象任何拥有一台以上智能设备的人没有额外的 USB 充电器或电源。

如果您选择使用 WiPy,建议您也从 Adafruit ( https://www.adafruit.com/product/3344 )或 Pycom ( https://www.pycom.io/product/wipy-expansion-board/ ).)购买扩展板

本项目中使用的有机发光二极管分线板是 Adafruit 的一个小模块。它有一个很小但很亮的显示屏,可以安装在试验板上。分辨率为 128 像素宽,32 像素高。有机发光二极管分线板没有安装接头,但如果你知道如何焊接(现在可能是练习的好时机)或者你可以让朋友帮助你,它们很容易添加。图 8-1 显示了 Adafruit 有机发光二极管 SPI 分线板。

A447395_1_En_8_Fig1_HTML.jpg

图 8-1。

Monochrome 128x32 SPI OLED Graphic Display (courtesy of adafruit.com)

有几种有机发光二极管分线板可用,只要它们具有 SPI 接口并使用 ssd1306 控制器芯片(说明会告诉你这一点),就可以使用备用有机发光二极管显示器。我们之所以需要在控制器芯片上使用一个,是因为驱动程序是为该控制器编写的。其他控制器芯片将需要不同的驱动器。

本项目中使用的 RTC 分线板是 Adafruit 的 DS1307 分线板。该板也没有安装接头(但包括接头),也没有配备电池,因此您必须购买 CR1220 纽扣电池。如果你不想去商店的话,Adafruit 也有这些东西。图 8-2 显示了 RTC 分线板。

A447395_1_En_8_Fig2_HTML.jpg

图 8-2。

DS1307 Real-Time Clock Assembled Breakout Board (courtesy of adafrui.com)

有几种 DS1307 RTC 时钟可用。事实上,Sparkfun 有一个,或者您可以自己创建一个!参见侧栏“构建您自己的 RTC 模块”了解更多细节。幸运的是,我们将使用的库支持带有 DS1307、DS3231 或 PCF8523 RTC 芯片的分线板。

Tip

小型分立元件,如 led、电阻等。,甚至跳线和试验板都可以在第二章提到的套件中找到——Adafruit Parts Pal(https://www.adafruit.com/product/2975)或者 Sparkfun 初学者配件套件( https://www.sparkfun.com/products/13973 )。我推荐其中一套。

现在,让我们看看如何将组件连接在一起。

安装硬件

这个项目有很多联系。有机发光二极管需要 7 个,RTC 需要 4 个,电源和接地需要一些额外的跳线。为了让事情更简单,我们将计划事情应该如何连接。我们将使用试验板来安装分线板,使连接更容易。更具体地说,我们将了解每个元件需要什么样的连接,以及它们需要在哪里连接到我们的电路板,将它们写下来以保持事情的连续性。做少量的家庭作业会为你节省时间(并且不会有一点点的挫败感)。

在这一节和下一节中,我们将看到如何为 WiPy 和 Pyboard 实现这个项目。在硬件的情况下,分线板侧的连接都是相同的,但是每个板的管脚号不同。表中显示了两种板,左边显示了带有 pin 标签的 MicroPython 板,右边显示了带有 pin 标签的分线板。我们将使用公/公跳线通过试验板进行这些连接。

正如您将看到的,像这样绘制连接使得检查连接变得容易。这个表和一个接线图是你将在本书和互联网或其他地方的其他示例项目中看到的工具。因此,学习如何阅读地图和接线图是一项让你的项目成功的技能。

表 8-2 显示了本项目所需的连接。注意“导线颜色”列。使用此列记录跨接导线的颜色,以帮助管理连接。使用什么颜色的电线并不重要,但是为每个连接使用不同的颜色有助于管理您将要连接的许多电线。如果您记下每个连接使用的是哪种颜色的电线,还可以更容易地检查您的连接。传统上,我们至少用黑色代表地(负极),红色代表电源(正极)。

表 8-2。

Connections for the MicroPython Clock (WiPy and Pyboard)

| MicroPython 板 | 分线板 | 电线颜色 | | --- | --- | --- | | WiPy | Pyboard | 板 | 个人识别码 |   | | --- | --- | --- | --- | --- | | P9 | X9 | 雷达跟踪中心(Radar Tracking Centre 的缩写) | 圣地亚哥 |   | | P8 | X10 | 雷达跟踪中心(Radar Tracking Centre 的缩写) | 国家药品监督管理局 |   | | 5V | V+ | 雷达跟踪中心(Radar Tracking Centre 的缩写) | 5V |   | | 地线 | 地线 | 雷达跟踪中心(Radar Tracking Centre 的缩写) | 地线 |   | | P6 | Y3 | 有机发光二极管(Organic Light Emitting Diode 的缩写) | 英特尔的快速储存技术 |   | | 孕烯醇酮 | Y4 | 有机发光二极管(Organic Light Emitting Diode 的缩写) | 跟单信用证 |   | | P7 | Y5 | 有机发光二极管(Organic Light Emitting Diode 的缩写) | 特许测量员 |   | | P10 | Y6 | 有机发光二极管(Organic Light Emitting Diode 的缩写) | 时钟信号 |   | | P11 | Y8 | 有机发光二极管(Organic Light Emitting Diode 的缩写) | 数据 |   | | 3V3 | 3V3 | 有机发光二极管(Organic Light Emitting Diode 的缩写) | 车辆识别号码 |   | | 地线 | 地线 | 有机发光二极管(Organic Light Emitting Diode 的缩写) | 地线 |   |

哇,联系真多啊!让我们来复习一些元件接线的技巧。将元件连接到电路板的最佳方式是使用试验板。正如我们在第七章中看到的,试验板允许我们插入组件,并使用跳线进行连接。这简化了项目的布线,并允许您在需要腾出更多空间时移动物品。试验板也有助于布线电源和接地连接,因为试验板的两侧都有电源轨,我们可以将其用作公共连接。也就是说,用一根跳线将电源和接地连接到试验板,用另一根跳线连接到分线板。

插入组件时,务必确保针脚与中央通道平行安装。回想一下,试验板的引脚以垂直于中央通道的方式成行布线。这允许您对元件(或电路板上的引脚)进行多个连接。

Caution

项目通电时,切勿插拔跳线。

最后,务必确保您的项目布线时仔细检查所有连接,尤其是电源、接地和用于信号的任何引脚(将设置为“高”或“开”),如用于 SPI 接口的引脚。最重要的是,当项目(或您的板)通电时,切勿插拔跳线。这很可能会损坏您的主板或组件。 4

首先,拿出你的试验板,插上元件;然后,使用不同颜色的跳线,将所有需要的电线插入试验板,并在图表中注明您使用的颜色。当您开始将跳线的另一端插入 MicroPython 板时,这将对您有很大帮助。

在这个项目中,我将有机发光二极管安装在一个半尺寸试验板的左侧,就在中央通道的下方,RTC 模块安装在右侧,也在通道的下方。请注意,这些板使用不同的电源连接。有机发光二极管板使用 3.3V,RTC 板使用 5V。打开项目电源之前,请务必检查组件的电源要求。再三检查你的连接。

现在,让我们看看如何连接 WiPy 和 Pyboard,如图所示。

WiPy

WiPy 的布线最好将扩展板上的 USB 连接器朝向左侧。这将允许您读取电路板上的引脚数,即使在电线插入电路板之后。然而,这只是一个建议。只要电线连接正确,方向并不重要。图 8-3 显示了 WiPy、有机发光二极管和 RTC 分线板的接线图。

A447395_1_En_8_Fig3_HTML.jpg

图 8-3。

Wiring the Clock Project (WiPy)

Pyboard

Pyboard 的布线最好将 USB 连接器朝向左侧。这将允许您读取电路板上的引脚数,即使在电线插入电路板之后。图 8-4 显示了 Pyboard、有机发光二极管和 RTC 分线板的接线图。

A447395_1_En_8_Fig4_HTML.jpg

图 8-4。

Wiring the Clock Project (Pyboard) Caution

总是再三检查你的连接,尤其是所有的电源和接地连接。请务必检查电源连接,以确保正确的电源(3V 或 5V)正确连接到组件。连接错误的电压会损坏部件。

如果您选择了不同于图中所示的 RTC 板,请务必根据需要调整连接。例如,Sparkfun DS1307 分线板的引脚顺序不同,所以不要只看这张图,尤其是在使用替代组件的情况下!

同样,在给主板通电之前,请务必仔细检查您的连接。现在,我们来谈谈我们需要编写的代码。暂时不要启动您的主板——在我们准备好测试该项目之前,还需要进行大量的讨论。

写代码

现在是时候为我们的项目编写代码了。由于我们正在使用几个新组件,我将依次介绍每个组件的代码。代码并不太复杂,但可能不像以前项目中的一些代码那样清晰。让我们先来看看这个项目的设计。

设计

一旦你整理好硬件以及如何将元件连接到你的电路板上,就该开始设计代码了。 5 幸运的是,这个项目足够小,可以让设计变得简单。简而言之,我们希望在有机发光二极管上每秒显示一次时间。因此,代码的“工作”是从 RTC 读取日期和时间,然后在有机发光二极管上显示出来。下面列出的步骤总结了如何为这个项目或任何项目设计和实现代码。

  1. 库:我们需要为 RTC 和有机发光二极管选择和导入库
  2. 设置:我们需要为 I2C 和 SPI 设置接口
  3. 初始化:我们需要为库中的类初始化对象实例
  4. 功能:我们将需要一个新的功能,让我们的项目启动和启动运行
  5. Main:修改我们的 MicroPython 板上的 main.py 文件来运行我们的代码

这五个元素是我们将在本书的所有项目中使用的,事实上,它是所有 MicroPython 项目都要遵循的一个好模式。新的功能步骤是我们还没有看到的新步骤。简而言之,我们将代码的操作部分包装在一个单独的函数中,以便在启动时从main.py文件中调用。当我们执行和测试这个项目时,我们将会看到更多关于它的内容。

现在我们知道了项目代码将如何实现,让我们回顾一下所需的库。

所需的库

回想一下之前我们需要两个驱动程序:一个用于有机发光二极管显示,另一个用于 RTC。有机发光二极管显示器的驱动程序可在 https://github.com/adafruit/micropython-adafruit-ssd1306 找到,RTC 的驱动程序可在 https://github.com/adafruit/Adafruit-uRTC 找到。

现在就下载这两个驱动程序吧。您应该能够访问这些站点,单击克隆或下载链接,然后单击下载 Zip 按钮将文件下载到您的 PC。然后,打开下载文件的位置并解压缩。您应该找到以下文件。将这些内容复制到您电脑上的某个位置。我们复制它们是因为我们需要修改它们,如果我们犯了错误或者事情不顺利,复制它们可以让我们回到原来的样子。

  • ssd1306.py:有机发光二极管显示驱动
  • urtc.py:RTC 驱动

我们必须修改一个库有两个主要原因;在这种情况下,我们看到了两者的例子。首先,Adafruit 驱动程序针对的目标平台存在差异。更具体地说,它是为不同的 MicroPython 板编写的,但是我们可以很容易地根据我们的需要对它进行修改。第二,主板上的固件之间存在差异。这将需要更改驱动程序代码,以使用与特定 MicroPython 板相关的正确的类和函数。

同样,不要担心,因为对库的更改很小。但是,这是一个很常见的现象,你应该习惯于做这样的小改动——尤其是如果你使用的是 WiPy 或 Pyboard 之外的板!

使用驱动程序时,还有一个需要考虑的问题。您应该查看驱动程序文档以了解任何限制。例如,驱动程序可能需要较新版本的固件。在某些情况下,可能还需要旧版本的固件(但这种情况很少见)。如果您的驱动程序与您的主板不兼容,而文档声称它可以与您的主板兼容,请在文档中检查您的固件版本是否有任何不兼容之处。

在接下来的章节中,我们将会看到在 WiPy 和 Pyboard 上使用驱动程序所需的一般变化。

更改 WiPy 的ssd1306.py

有机发光二极管驱动程序需要对 WiPy 固件进行少量的修改。WiPy 上的引脚类没有high()low()功能。取而代之的是,WiPy 固件使用一个函数value(),我们将它作为参数传递,1 表示高,0 表示低。幸运的是,对于那些知道如何读取差异文件(diff 命令 6 的输出)的人来说,只需将所有出现的.high()更改为.value(1)并将.low()更改为.value(0).,差异文件如清单 8-1 所示。

--- ./Pyboard/ssd1306.py        2016-10-30 14:06:02.000000000 -0400
+++ ./WiPy/ssd1306.py           2017-07-20 21:39:31.000000000 -0400
@@ -146,23 +146,23 @@

     def write_cmd(self, cmd):
         self.spi.init(baudrate=self.rate, polarity=0, phase=0)
-        self.cs.high()
-        self.dc.low()
-        self.cs.low()
+        self.cs.value(1)
+        self.dc.value(0)
+        self.cs.value(0)
         self.spi.write(bytearray([cmd]))
-        self.cs.high()
+        self.cs.value(1)

     def write_framebuf(self):
         self.spi.init(baudrate=self.rate, polarity=0, phase=0)
-        self.cs.high()
-        self.dc.high()
-        self.cs.low()
+        self.cs.value(1)
+        self.dc.value(1)
+        self.cs.value(0)
         self.spi.write(self.buffer)
-        self.cs.high()
+        self.cs.value(1)

     def poweron(self):
-        self.res.high()
+        self.res.value(1)
         time.sleep_ms(1)
-        self.res.low()
+        self.res.value(0)
         time.sleep_ms(10)
-        self.res.high()
+        self.res.value(1)

Listing 8-1.Changes for the ssd1306.py code module for the WiPy (difference file)

What’s a Difference File?

差异文件是diff命令的输出。它逐行显示同一文件的两个版本之间的变化或差异。在统一差异文件中,以减号开头的行是要删除的行,而以加号开头的行是要添加的行。文件中没有序言(符号)的行被用作定位更改的上下文(以及一个特殊的头)。差异文件可与patch命令一起使用,将更改应用于文件。因此,差异文件有时被称为“补丁”或“差异”文件。

对 Pyboard 的uRTC. py进行更改

RTC 驱动程序将无法在 Pyboard 上正确运行(但在 WiPy 上无需更改即可正常运行)。所需的更改是由于 I2C 类的功能不同。驱动程序使用以下方法来读取和写入数据。

i2c.readfrom_mem(address, register, num_to_read)
i2c.writeto_mem(address, register, buffer)

但是,Pyboard 固件有不同的函数名,参数顺序也不同,如下所示。请注意,read 函数的参数顺序是不同的。

i2c.mem_read(num_read, address, register)
i2c.mem_write(buffer, address, register)

因此,我们需要改变库中的所有读写功能,以匹配我们的板的固件。你所需要做的就是打开文件并进行修改——只需要重命名函数并重新排列 read 函数的参数。清单 8-2 显示了您需要做出的更改。

--- ./Pyboard/urtc.py    2017-07-20 19:20:38.000000000 -0400
+++ ./WiPy/urtc.py    2017-04-21 16:52:32.000000000 -0400
@@ -40,8 +40,8 @@

     def _register(self, register, buffer=None):
         if buffer is None:
-            return self.i2c.mem_read(1, self.address, register)[0]
-        self.i2c.mem_write(buffer, self.address, register)
+            return self.i2c.readfrom_mem(self.address, register, 1)[0]
+        self.i2c.writeto_mem(self.address, register, buffer)

     def _flag(self, register, mask, value=None):
         data = self._register(register)
@@ -56,8 +56,8 @@

     def datetime(self, datetime=None):
         if datetime is None:
-            buffer = self.i2c.mem_read(7, self.address,
-                                           self._DATETIME_REGISTER)
+            buffer = self.i2c.readfrom_mem(self.address,
+                                           self._DATETIME_REGISTER, 7)
             if self._SWAP_DAY_WEEKDAY:
                 day = buffer[3]
                 weekday = buffer[4]
@@ -128,8 +128,8 @@

     def alarm_time(self, datetime=None, alarm=0):
         if datetime is None:
-            buffer = self.i2c.mem_read(3, self.address,
-                                           self._ALARM_REGISTERS[alarm])
+            buffer = self.i2c.readfrom_mem(self.address,
+                                           self._ALARM_REGISTERS[alarm], 3)
             day = None
             weekday = None
             second = None
@@ -145,8 +145,8 @@
                     if not buffer[1] & 0x80 else None)
             if alarm == 0:
                 # handle seconds
-                buffer = self.i2c.mem_read(1,
-                    self.address, self._ALARM_REGISTERS[alarm] - 1)
+                buffer = self.i2c.readfrom_mem(
+                    self.address, self._ALARM_REGISTERS[alarm] - 1, 1)
                 second = (_bcd2bin(buffer[0] & 0x7f)
                           if not buffer[0] & 0x80 else None)
             return datetime_tuple(
@@ -219,8 +219,8 @@

     def alarm_time(self, datetime=None):
         if datetime is None:
-            buffer = self.i2c.mem_read(4, self.address,
-                                           self._ALARM_REGISTER)
+            buffer = self.i2c.readfrom_mem(self.address,
+                                           self._ALARM_REGISTER, 4)
             return datetime_tuple(
                 weekday=_bcd2bin(buffer[3] &
                                  0x7f) if not buffer[3] & 0x80 else None,

Listing 8-2.Changes for the urtc.py code module for the Pyboard (difference file)

幸运的是,没有那么多你需要做的改变,所以做这些改变很容易也很快。如果你知道如何应用补丁的差异文件, 7 你可以这样做,所有的变化都会为你做。提示:您需要使用 patch 命令。否则,只需打开文件并手动进行更改。只需找到所有标有减号(-)的行,并更改它们以匹配带加号(+)的行。如果你复制粘贴,别忘了去掉加号。

更改 Pyboard 的ssd1306.py

有机发光二极管驱动程序也不能在 Pyboard 上正确运行。所需的改变是由于我们在 RTC 驱动中看到的 I2C 类函数的相同差异。您可能想知道,如果我们使用 SPI 接口,为什么需要进行这些更改。事实证明,该驱动程序既可用于 I2C 接口,也可用于 SPI 接口,由于 I2C 接口不同,因此仍需进行更改。如果我们不这样做,MicroPython 会抱怨,我们将无法使用该驱动程序。是的,即使我们不用那部分代码。这是因为 MicroPython 将检查导入的整个代码模块的语法。

像 RTC 驱动程序一样,只需打开文件并进行更改。清单 8-3 显示了使 RTC 驱动程序兼容 WiPy 和 Pyboard 的差异文件。

--- /Users/cbell/Downloads/Adafruit-uRTC-master_orig/urtc.py    2017-04-21 16:52:32.000000000 -0400
+++ /Users/cbell/Downloads/Adafruit-uRTC-master/urtc.py         2017-07-20 19:20:38.000000000 -0400
@@ -40,8 +40,8 @@

     def _register(self, register, buffer=None):
         if buffer is None:
-            return self.i2c.readfrom_mem(self.address, register, 1)[0]
-        self.i2c.writeto_mem(self.address, register, buffer)
+            return self.i2c.mem_read(1, self.address, register)[0]
+        self.i2c.mem_write(buffer, self.address, register)

     def _flag(self, register, mask, value=None):
         data = self._register(register)
@@ -56,8 +56,8 @@

     def datetime(self, datetime=None):
         if datetime is None:
-            buffer = self.i2c.readfrom_mem(self.address,
-                                           self._DATETIME_REGISTER, 7)
+            buffer = self.i2c.mem_read(7, self.address,
+                                           self._DATETIME_REGISTER)
             if self._SWAP_DAY_WEEKDAY:
                 day = buffer[3]
                 weekday = buffer[4]
@@ -128,8 +128,8 @@

     def alarm_time(self, datetime=None, alarm=0):
         if datetime is None:
-            buffer = self.i2c.readfrom_mem(self.address,
-                                           self._ALARM_REGISTERS[alarm], 3)
+            buffer = self.i2c.mem_read(3, self.address,
+                                           self._ALARM_REGISTERS[alarm])
             day = None
             weekday = None
             second = None
@@ -145,8 +145,8 @@
                     if not buffer[1] & 0x80 else None)
             if alarm == 0:
                 # handle seconds
-                buffer = self.i2c.readfrom_mem(
-                    self.address, self._ALARM_REGISTERS[alarm] - 1, 1)
+                buffer = self.i2c.mem_read(1,
+                    self.address, self._ALARM_REGISTERS[alarm] - 1)
                 second = (_bcd2bin(buffer[0] & 0x7f)
                           if not buffer[0] & 0x80 else None)
             return datetime_tuple(
@@ -219,8 +219,8 @@

     def alarm_time(self, datetime=None):
         if datetime is None:
-            buffer = self.i2c.readfrom_mem(self.address,
-                                           self._ALARM_REGISTER, 4)
+            buffer = self.i2c.mem_read(4, self.address,
+                                           self._ALARM_REGISTER)
             return datetime_tuple(
                 weekday=_bcd2bin(buffer[3] &
                                  0x7f) if not buffer[3] & 0x80 else None,

Listing 8-3.Changes for the ssd1306.py code module for the Pyboard (difference file)

现在我们已经修改了库,可以在我们的板上工作了,让我们看看需要编写的代码。

规划代码

既然我们已经有了自己的设计,下载并修改了库,我们就可以开始编写代码了。与其给你看一个长长的清单,说“要么理解,要么毁灭”,不如让我们先浏览一下代码的所有部分,以便理解每一部分。在我们遍历代码的过程中,您可以自己测试各个部分,但是如果您愿意等到最后再测试代码,您也可以这样做。此外,当我们浏览代码时,我们将看到 WiPy 和 Pyboard 所需的差异。让我们从进口部分开始。

进口

项目的 imports 部分在所有其他语句之前,但在文件顶部的注释块之后。正如你在前面的例子和第四章中看到的,你应该在文件的顶部包含一些文档来解释代码的作用。你不需要写一个冗长的教程,只需要一个简短的陈述来描述这个项目,包括你的名字和其他信息。如果您希望与他人共享您的代码,并且如果您以后要重新使用这些代码,这是非常重要的。8

如果您想在我们进行的过程中键入代码,您可以用您喜欢的代码(或文本)编辑器打开一个名为clock.py的新文件。回想一下,最好的编辑器是那些具有 Python 语法检查功能的编辑器,比如 Komodo Edit。

WiPy 的进口不同于我们对 Pyboard 的需求;然而我们需要相同的库——我们只是从不同的地方导入它们。下面显示了 WiPy 的导入。这是两种板的三个不同之处之一。

import urtc
import utime
from machine import SPI, I2C, RTC as rtc, Pin as pin
from ssd1306 import SSD1306_SPI as ssd

我们需要 Pyboard 的导入包括以下内容。注意我们需要很多库。您将看到我们导入了 Pyboard 库、RTC 库、ssd1306 库以及 I2C 和 SPI 类。还要注意的是,我为一些库使用了别名(“as XXXX”短语),以使键入更容易一些(键入的字符更少)。

# Imports for the project
import pyb
import urtc
from machine import SPI, Pin as pin
from pyb import I2C
from ssd1306 import SSD1306_SPI as ssd

建立

接下来,我们需要设置用于 RTC 和 ssd1306 库的 I2C 和 SPI 接口。也就是说,这些库中的类需要传递给构造函数的接口的对象实例。我们将使用的代码类似于我们在前面的例子中看到的代码。

这是 WiPy 和 Pyboard 的另一个主要区别。下面显示了 WiPy 的接口设置代码。

# Setup SPI and I2C
spi = SPI(0, SPI.MASTER, baudrate=2000000, polarity=0, phase=0)

i2c = I2C(0, I2C.MASTER, baudrate=100000, pins=("P9", "P8"))下图显示了 Pyboard 的 I2C 和 SPI 设置。

spi = SPI(2, baudrate=8000000, polarity=0, phase=0)
i2c = I2C(1, I2C.MASTER)
i2c.init(I2C.MASTER, baudrate=500000)

请注意,我们对 SPI 使用不同的参数,并为 I2C 指定引脚。我们必须为 I2C 接口指定引脚的原因是,SPI 和 WiPy 上的 I2C 共享时钟引脚。因此,我们必须手动指定用于 I2C 接口的引脚。如果您愿意,可以使用其他引脚,但在将组件连接在一起时,请记住使用正确的引脚。最后,注意我们不需要 WiPy 上的init()函数。

初始化

接下来,我们为库中的类初始化对象实例。此时,您需要阅读每个库的文档,以了解初始化对象所需的内容。对于 ssd1306 驱动程序,类构造函数需要显示器的像素数(分辨率是行、列中的像素数)、接口实例(上一节中的 SPI)以及我们将用于 D/C、RST 和 CS 引脚的引脚。对于 RTC 驱动程序,我们只需要传入接口实例(上一节的 I2C)。下面显示了如何为 WiPy 执行这两个步骤。

# Setup the OLED : D/C, RST, CS
oled_module = ssd(128,32,spi,pin('P5'),pin('P6'),pin('P7'))

# Setup the RTC
rtc_module = urtc.DS1307(i2c)

Pyboard 的初始化代码非常相似。唯一的区别是我们指定的引脚。

# Setup the OLED : D/C, RST, CS
oled_module = ssd(128,32,spi,pin("Y4"),pin("Y3"),pin("Y5"))

# Setup the RTC
rtc_module = urtc.DS1307(i2c)

还有一件事我们需要考虑。当我们第一次使用 RTC 或更换电池时,我们必须初始化日期和时间。我们可以使用驱动程序特性来做到这一点。在这种情况下,我们简单地调用 RTC 实例的datetime()函数,传递一个包含新开始日期和时间的元组——元组元素的顺序如下所示。一旦设置,我们不需要再次运行它。事实上,再次运行它将重置 RTC,我们不需要这样做。因此,对于正常操作,我们将这段代码注释掉,并在需要重置 RTC 时取消注释。下面显示了所需的代码。第一次运行项目时,取消对这段代码的注释,提供正确的当前日期和时间,但稍后将其注释掉。

#       (year, month, day, weekday, hour, minute, second, millisecond)
#start_datetime = (2017,07,20,4,9,0,0,0)
#rtc_module.datetime(start_datetime)

新功能

现在所有的设置或 boiler plate 工作都完成了,我们可以创建一个新的函数来启动并运行我们的项目。也就是说,一旦导入代码模块,我们就可以调用这个函数。例如,我们将文件命名为clock.py,如果我们创建一个名为run()的函数,我们可以用下面的语句开始代码。当我们将项目部署到董事会时,我们将看到更多关于如何使用这些语句的内容。

import clock
clock.run()

回想一下,我们希望该项目从 RTC 读取日期和时间,并在有机发光二极管上每秒显示一次。因此,我们期望看到某种执行这两个步骤的循环。但是,我们必须再次参考驱动程序文档,在那里我们发现 RTC 以元组(年、月、日、工作日、小时、分钟、秒、毫秒)的形式返回数据。这意味着我们必须格式化日期和时间,以使人们更容易阅读,并适应小有机发光二极管屏幕。

这是一个或三个助手函数的完美候选。幸运的是,这两种板的这些功能是相同的,因此不需要预测任何变化。

让我们创建一个名为write_time()的函数,它获取有机发光二极管显示器和 RTC 的一个实例,然后使用datetime()函数(不带参数)读取日期和时间,并使用text()函数将其打印到有机发光二极管屏幕上,该函数获取一个起始列(在文档中称为 X 位置)和一行(Y 位置)作为屏幕上的位置,以便在调用show()函数时打印消息。这是项目的精髓。将它放在一个单独的函数中,可以让您隔离行为,并使维护或修改代码变得更容易——因为“核心”在一个地方。

# Display the date and time
def write_time(oled, rtc):
    # Get datetime
    dt = rtc.datetime()
    # Print the date
    oled.text("Date: {0:02}/{1:02}/{2:04}".format(dt[1], dt[2], dt[0]), 0, 0)
    # Print the time
    oled.text("Time: {0:02}:{1:02}:{2:02}".format(dt[4], dt[5], dt[6]), 0, 10)
    # Print the day of the week
    oled.text("Day:  {0}".format(get_weekday(dt[3])), 0, 20)
    # Update the OLED
    oled.show()

注意,我们使用print()函数和format()函数从 RTC 获取数据,并将其格式化为大多数时钟使用的预期格式:HH:MM::SSMM/DD/YYYY。注意这里有一个名为get_weekday()的附加函数。这个函数获取从 RTC 返回的一周中的第几天,并返回一个字符串作为这一天的名称。下面显示了该函数的代码。

# Return a string to print the day of the week
def get_weekday(day):
    if day == 1: return "Sunday"
    elif day == 2: return "Monday"
    elif day == 3: return "Tuesday"
    elif day == 4: return "Wednesday"
    elif day == 5: return "Thursday"
    elif day == 6: return "Friday"
    else: return "Saturday"

增加了一个功能——清除屏幕的功能。该功能只是清空屏幕,让我们用新数据覆盖屏幕。通常这是不需要的,但是如果驱动程序没有为你清除屏幕,这是一个很好的实践。在这种情况下,现在确实如此。这个函数被命名为clear_screen(),如下所示。它只是使用 ssd1306 驱动程序中的fill()show()函数。为fill()函数传入 o 告诉驱动程序用无数据填充屏幕(空白或关闭)。

# Clear the screen
def clear_screen(oled):
    oled.fill(0)
    oled.show()

现在我们准备为项目编写新的run()函数。我们已经开发了我们的助手函数,所以我们只需要调用它们,并在每次调用时等待一秒钟。下面显示了我们项目的run()函数。这是 Pyboard 与 WiPy 的最后一个不同之处。以下是 Pyboard 的代码。你能找出需要为 WiPy 改变的那一行吗?

# Here, we make a "run" function to be used from the main.py
# code module. This is preferable to direct "main" execution.
def run():
    # Display the deate and time every second
    while True:
        clear_screen(oled_module)
        write_time(oled_module, rtc_module)
        pyb.delay(1000)

注意这个函数有多“干净”——我们只能看到三个语句:清空屏幕,显示时间,等待一秒钟。需要为 WiPy 修改的那一行代码是最后一行。在 WiPy 上,我们使用不同的类,如下所示。

utime.sleep(1)

既然我们已经看到了代码各部分的完整演练,那么让我们来讨论一下代码的测试。

测试代码的各个部分

既然我们已经计划了代码,并且知道如何对每个部分进行编码,我们还有一件事要做——分别测试分线板。我们通过连接一个分线板并对其进行测试,然后断开该分线板的电源和布线,再连接另一个分线板并对其进行测试。

出于一个主要原因,这是养成习惯的好习惯。通过一次一个地测试项目的各个部分——尤其是硬件——您将会省去很多麻烦。这不仅可以更容易地缩小问题范围,还可以确保您能够确定问题的根源。也就是说,如果您插上所有硬件,连接所有设备,编写代码,部署它,然后启动它,但没有任何东西工作,您如何知道是哪个部分出了问题?这是我的口头禅之一:一次构建并测试一个部分。

Tip

一次测试一部分代码对我来说是一种熟悉的模式,强烈建议您自己采用这个过程。也就是说,一次编写项目的一部分,并单独测试每一部分。

对于这个项目,有两个部分 RTC 和有机发光二极管。让我们看看如何单独测试它们。我们将看到测试 Pyboard 组件的代码。您可以根据需要使用上面的信息修改代码,以便在您自己的板上工作。提供的代码旨在通过 REPL 控制台运行。我展示了 Pyboard 的代码,并将修改它以在 WiPy 上运行作为练习。提示:回头看看前面的部分,看看需要做哪些更改。

测试 RTC 分线板

要测试 RTC,使用下面的代码。这是我们在 Pyboard 演练中看到的代码的压缩形式。

import urtc
from pyb import I2C
from machine import Pin as pin
i2c = I2C(1, I2C.MASTER)
i2c.init(I2C.MASTER, baudrate=500000)
i2c.scan()
rtc = urtc.DS1307(i2c)
# year, month, day, weekday, hour, minute, second, millisecond)
start_datetime = (2017,07,20,4,7,25,0,0)
rtc.datetime(start_datetime)
print(rtc.datetime())

注意,我们在这个测试中设置了日期和时间。当您自己运行这个测试时,您应该更改日期和时间元组,以包含运行测试时的当前日期和时间。您应该在 REPL 控制台中看到一个表示日期和时间的元组。它应该与您设置的值相同,因为从您设置它到您查询 RTC 这段时间,代码的执行时间不到一秒钟。继续重新输入最后一条语句几次,以确保时间如您所料发生变化。也就是说,等待几秒钟,然后再试一次——应该已经过了几秒钟。

如果任何陈述失败,请务必检查您的接线并查找任何打字错误。此外,请确保您使用的是正确的、经过修改的驱动程序版本(并且您已经将它们复制到主板上)。

测试有机发光二极管分线板

要测试有机发光二极管,请使用下面的代码。这是我们在演练中看到的代码的精简形式。

import machine
from machine import Pin as pin
from ssd1306 import SSD1306_SPI as ssd
spi = machine.SPI(2, baudrate=8000000, polarity=0, phase=0)
oled = ssd(128,32,spi,pin("Y4"),pin("Y3"),pin("Y5"))
oled.fill(0)
oled.show()
oled.fill(1)
oled.show()
oled.fill(0)
oled.show()
oled.text("Hello, World!", 0, 0)
oled.show()

当您运行这段代码时,您应该会看到屏幕空白(从一开始就应该是空白的),然后填充白色-参见fill(1)-然后空白屏幕,最后显示文本消息。如果看不到任何输出,请关闭主板电源,检查所有连接,验证是否使用了正确的引脚,以及是否将正确的驱动程序修改版本复制到主板上。

Tip

Adafruit 的有机发光二极管分线板(可能还有其他的)在镜头上有一个保护罩。你可以也应该把它留在原处,以确保镜头不会被损坏。此外,有机发光二极管足够亮,可以透过保护罩看到东西。

现在,在我们将它部署到我们的 MicroPython 板上之前,让我们看看两个板的完整代码。

完整代码

在这一节中,我们将看到 WiPy 和 Pyboard 的完整代码。这些列表供您参考,以确保您拥有适合您的主板的正确代码。以后的项目将展示其中一个电路板的最终代码,并说明如何修改它以用于其他电路板。清单 8-4 显示了在 WiPy 上运行项目的完整代码。

# MicroPython for the IOT - Chapter 8
#
# Project 1: A MicroPython Clock!
#
# Required Components:
# - Pyboard
# - OLED SPI display
# - RTC I2C module
#
# Note: this only runs on the WiPy. See chapter text
#       for how to modify this to run on the Pyboard
#

# Imports for the project
import urtc
import utime
from machine import SPI, I2C, RTC as rtc, Pin as pin
from ssd1306 import SSD1306_SPI as ssd

# Setup SPI and I2C
spi = SPI(0, SPI.MASTER, baudrate=2000000, polarity=0, phase=0)
i2c = I2C(0, I2C.MASTER, baudrate=100000,pins=("P9", "P8"))

# Setup the OLED : D/C, RST, CS
oled_module = ssd(128,32,spi,pin('P5'),pin('P6'),pin('P7'))

# Setup the RTC
rtc_module = urtc.DS1307(i2c)
#
# NOTE: We only need to set the datetime once. Uncomment these
#       lines only on the first run of a new RTC module or
#       whenever you change the battery.
#       (year, month, day, weekday, hour, minute, second, millisecond)
#start_datetime = (2017,07,20,4,9,0,0,0)
#rtc_module.datetime(start_datetime)

# Clear the screen
def clear_screen(oled):
    oled.fill(0)
    oled.show()

# Return a string to print the day of the week
def get_weekday(day):
    if day == 1: return "Sunday"
    elif day == 2: return "Monday"
    elif day == 3: return "Tuesday"
    elif day == 4: return "Wednesday"
    elif day == 5: return "Thursday"
    elif day == 6: return "Friday"
    else: return "Saturday"

# Display the date and time
def write_time(oled, rtc):
    # Get datetime
    dt = rtc.datetime()
    # Print the date
    oled.text("Date: {0:02}/{1:02}/{2:04}".format(dt[1], dt[2], dt[0]), 0, 0)
    # Print the time
    oled.text("Time: {0:02}:{1:02}:{2:02}".format(dt[4], dt[5], dt[6]), 0, 10)
    # Print the day of the week
    oled.text("Day:  {0}".format(get_weekday(dt[3])), 0, 20)
    # Update the OLED
    oled.show()

# Here, we make a "run" function to be used from the main.py
# code module. This is preferable to direct "main" execution.
def run():
    # Display the deate and time every second
    while True:
        clear_screen(oled_module)
        write_time(oled_module, rtc_module)
        utime.sleep(1)

Now, let’s see the completed code for the Pyboard. As mentioned, the changes are mostly in the imports and set up with one minor change in the run function. Listing 8-5 shows the complete code for running the project on the Pyboard.

Listing 8-4.MicroPython Clock Code Module clock.py (WiPy)

# MicroPython for the IOT - Chapter 8
#
# Project 1: A MicroPython Clock!
#
# Required Components:
# - Pyboard
# - OLED SPI display
# - RTC I2C module
#
# Note: this only runs on the Pyboard. See chapter text
#       for how to modify this to run on the WiPy
#

# Imports for the project
import pyb
import urtc
from machine import SPI, Pin as pin
from pyb import I2C
from ssd1306 import SSD1306_SPI as ssd

# Setup SPI and I2C
spi = SPI(2, baudrate=8000000, polarity=0, phase=0)
i2c = I2C(1, I2C.MASTER)
i2c.init(I2C.MASTER, baudrate=500000)

# Setup the OLED : D/C, RST, CS
oled_module = ssd(128,32,spi,pin("Y4"),pin("Y3"),pin("Y5"))

# Setup the RTC
rtc_module = urtc.DS1307(i2c)
#
# NOTE: We only need to set the datetime once. Uncomment these
#       lines only on the first run of a new RTC module or
#       whenever you change the battery.
#       (year, month, day, weekday, hour, minute, second, millisecond)
#start_datetime = (2017,07,20,4,9,0,0,0)
#rtc_module.datetime(start_datetime)

# Clear the screen
def clear_screen(oled):
    oled.fill(0)
    oled.show()

# Return a string to print the day of the week
def get_weekday(day):
    if day == 1: return "Sunday"
    elif day == 2: return "Monday"
    elif day == 3: return "Tuesday"
    elif day == 4: return "Wednesday"
    elif day == 5: return "Thursday"
    elif day == 6: return "Friday"
    else: return "Saturday"

# Display the date and time
def write_time(oled, rtc):
    # Get datetime
    dt = rtc.datetime()
    # Print the date
    oled.text("Date: {0:02}/{1:02}/{2:04}".format(dt[1], dt[2], dt[0]), 0, 0)
    # Print the time
    oled.text("Time: {0:02}:{1:02}:{2:02}".format(dt[4], dt[5], dt[6]), 0, 10)
    # Print the day of the week
    oled.text("Day:  {0}".format(get_weekday(dt[3])), 0, 20)
    # Update the OLED
    oled.show()

# Here, we make a "run" function to be used from the main.py
# code module. This is preferable to direct "main" execution.
def run():
    # Display the deate and time every second
    while True:
        clear_screen(oled_module)
        write_time(oled_module, rtc_module)
        pyb.delay(1000)

Listing 8-5.MicroPython Clock Code Module clock.py (Pyboard)

好了,现在我们准备好执行这个项目了。

执行!

我们终于可以将所有文件复制到我们的板上并执行项目代码了。这个过程中有几个推荐的步骤,如下所示。每次您想要部署和测试项目时,您都应该遵循这个过程。

  1. 连接:仔细检查所有硬件连接
  2. 电源:主板上的电源
  3. 复制文件:将驱动程序和代码文件复制到板上
  4. 测试:使用 REPL 控制台测试代码,修复任何问题,并重新复制文件
  5. 执行:满意后,修改main.py然后重启板卡

第一步怎么说都不为过。每次接通主板电源时,请务必检查您的连接。这是为了防止好奇的手经过并“检查”你的项目,或者你移动了它,或者发生了一些其他事件来拔掉电线。格外小心无妨。

接下来,我们打开主板电源,检查是否有任何问题。没错,这就是冒烟测试!只需确保所有应该点亮的 led 都点亮(就像板上的那些),不应该点亮的都熄灭。例如,如果您在开机时看到有机发光二极管上有一个实心条,这不是一个好现象。如果有任何疑问,请拔掉电源并检查您的连接。如果事情仍然不对劲,断开所有连接并测试您的主板。有时损坏的组件会导致奇怪的行为。

接下来,我们将所有想要使用的驱动程序和代码复制到板上。完成后,我们就可以测试代码了。因为我们使用了一个run()函数来包含主代码,所以我们可以简单地导入代码并如下调用该函数。

>>> import clock
>>> clock.run()

现在启动您的主板,运行所示代码。您应该会看到类似图 8-5 的东西,它展示了项目运行的所有荣耀。

A447395_1_En_8_Fig5_HTML.jpg

图 8-5。

A MicroPython Clock !

在这一点上,您应该沐浴在您的第一个成功的 MicroPython 硬件项目的奇迹中。花些时间沉浸在出色完成工作的喜悦中。

这个过程的最后一步是让您的项目在您打开评估板时默认运行。这是一个可选步骤,只有当您想让项目在每次上电时都运行时,才需要执行这个步骤。也就是说,您将在项目中使用该板一段时间。回想一下,板上的main.py代码模块将在启动时执行,因此您的项目将在板通电时执行。您需要做的就是将代码添加到该文件中,以启动您的项目,如下所示。现在就进行更改(您可以在以后撤消更改)。

# main.py -- put your code here!
import clock
clock.run()

现在是最后的测试:如果你修改了main.py文件,关掉你的主板,然后再打开。如果日期和时间在几秒钟后出现,您已经完成了!您已经成功地创建了一个项目,您可以打包并在任何您想要的地方运行,只要硬币电池有电,它就不会浪费时间。

更进一步

这个项目有很大的修饰潜力。如果你喜欢这个项目,你应该考虑花时间探索一些修饰。这里有一些你可以考虑的。有些很容易,有些可能是一个挑战。

  • 使用不同的 RTC
  • 计算 AM/PM 并显示
  • 将您的主板连接到互联网,并使用 NTP 服务而不是 RTC
  • 使用更大的显示器并显示儒略日
  • 在阳光直射的情况下,使用光传感器关闭或调暗显示器
  • 添加一个扬声器并实现一个警报功能(提示:一些 RTC 有这个功能)
  • 使用不同的世界标准格式化日期和时间,例如 YYYY/MM/DD

当然,如果你想继续下一个项目,欢迎你这样做,但是花些时间探索这些潜在的修饰——这将是一个很好的实践。

既然我们已经解决了库的问题,如果你认为这个项目是初级的,考虑一下这个:大多数基于传感器的项目,事实上大多数生成数据的项目必须与事件采样的日期和时间相关联。因此,使用 RTC 读取日期和时间将是许多 IOT 项目的考虑因素。

Building Your Own RTC Module

如果你像我一样,喜欢修补,你可以使用 RTC DS1307 芯片、两个电阻、一个晶体和一个硬币电池分线板来构建自己的 RTC 模块。你可以在 Adafruit ( www.adafruit.com )、Sparkfun ( www.sparkfun.co m )、Mouser ( www.mouser.com )等大多数网上电子商店找到这些组件。组件列表如下。

  • DS1307 芯片
  • 纽扣电池分线板
  • 3v 纽扣电池
  • 32.768 千赫晶体
  • (2) 1K 电阻器

就这样!下图显示了如何连接试验板上的元件。如果你想实现这个侧边项目,参见 http://www.learningaboutelectronics.com/Articles/DS1307-real-time-clock-RTC-circuit.php 的示例演练。

A447395_1_En_8_Figa_HTML.jpg

如果您计划构建大量使用 RTC 的项目,批量购买这些组件并连接您自己的 RTC 1307 模块可能更具成本效益。此外,它增加了你的工具包的酷的因素。

摘要

使用分线板等硬件以及我们需要通过 I2C 和 SPI 等专用接口与之对话的驱动程序可能是一项挑战。有时候,就像我们在本章中看到的,你需要修改一个驱动程序来使用你的 MicroPython 板。其原因是由于主板阵列的不断增长,供应商正在创建专门版本的 MicroPython 固件用于这些主板。在某些情况下,比如 ssd1306 驱动程序,我们只需做一些小改动,就可以在我们的电路板上使用该驱动程序。诀窍是理解为什么改变是必要的,并花时间自己去改变。当事情不成功时,放弃是很容易的——不要这样做!慢慢来,了解问题,然后系统解决。

在本章中,我们看到了一个 MicroPython 时钟的详细演练。我们使用有机发光二极管显示器来显示我们从 RTC 读取的时间。在这个过程中,我们学习了如何规划项目、建立硬件连接,以及编写用于在我们的 MicroPython 板上部署的代码。

在下一章中,我们将探索一个项目,该项目使用 led、电阻和按钮等分立元件形式的更多低级硬件。这些是构建更复杂的解决方案所需的构件。

Footnotes 1

https://en.wikipedia.org/wiki/OLED

2

是的,我是那些喜欢印刷材料的触觉传感器印象的人之一。此外,你几乎可以在任何包或口袋里放一小叠文件。

3

这就是为什么我只使用那些写得很好并且有完整例子的参考文献。试图猜测一个驱动作者的想法是不值得花费时间和精力的。

4

猜猜我是怎么知道的。有时候打破东西是最好的学习方式。

5

我想其他工程师可能会说你应该先写代码,但我的看法正好相反。

6

https://en.wikipedia.org/wiki/Diff_utility

7

[T0](https://en.wikipedia.org/wiki/Patch_(Unix)

8

我有时发现自己想知道是谁写了一段代码,却发现是我!记录或“签署”代码可以帮助你记住你写了什么和为什么写。

九、项目 2:交通信号灯模拟器

既然我们已经有了如何设计、连接和实现 MicroPython 项目的教程,现在让我们来看一个更高级的项目。在这种情况下,我们将使用一些非常基本的组件来进一步了解如何使用硬件。该项目的硬件选择将是发光二极管,电阻和一个按钮。按钮是最基本的传感器。也就是说,当按钮被按下时,我们可以让我们的 MicroPython 代码响应这个动作。

使用 led 可能更多的是一句“你好,世界!”风格项目的硬件,因为打开和关闭发光二极管很容易,除了搞清楚什么大小的限流电阻是必要的,接线发光二极管也很容易。像大多数基于 LED 的项目一样,我们将实现一个模拟。更具体地说,我们将实现一个交通灯和一个行人行走按钮。步行按钮是一个按钮,行人可以使用它来触发交通信号以改变和停止交通,以便他们可以穿过街道。

模拟项目可以很有趣,因为我们已经有了它应该如何工作的想法,我们正在模拟我们遇到的东西。例如,除非你住在非常偏远的地区,否则你很可能在十字路口遇到过带有按钮的“步行/禁止步行”标志的交通信号。如果你生活在城市,你会在各种配置中遇到这些。当一个行人(或骑自行车的人)按下步行按钮时,交通灯全部变成红色,步行标志被点亮。一段时间后(大约 30 秒),步行标志闪烁,然后大约 15 秒后,步行信号变为禁止步行,交通信号恢复正常周期。

Note

“循环”一词指的是一组线性作用的状态。因此,循环是指从一种状态到另一种状态的变化。

我们也将增加一个新的交通灯模拟概念。我们将使用一个网页来触发 walk 请求。但首先,我们将学习如何连接和编码 LED 灯,并在第二步添加 web 界面。

概观

在这一章中,我们将实现一个带有行人行走按钮的交通信号。这个项目使用 led,它允许我们看到代码执行时的状态。对于交通灯(也称为停止灯),我们将使用红色、黄色和绿色 led 来匹配交通灯上相同颜色的灯。我们还将使用一个红色和黄色的 LED 来对应禁止行走(红色)和行走(黄色)灯。

我们将使用按钮(也称为瞬时按钮),因为它只有在被按下时才会触发(开启)。释放后,它不再被触发(关闭)。Trigger 是用于描述按钮状态的词,其中 triggered 表示从按钮的一侧到另一侧的连接被连接(on)。保持触发(锁定)状态的按钮称为锁定按钮,通常必须再次按下才能关闭。

我们将通过首先仅打开绿色交通灯 LED 和红色步行 LED 信号来模拟交通灯和步行信号。这是我们将使用的正常状态。当按钮被按下时,交通灯将变为黄色几秒钟,然后变为红色。几秒钟后,行走信号将变为黄色,几秒钟后开始闪烁。几秒钟后,步行信号将返回红色,交通灯将返回绿色。

为了让事情变得更有趣,我们还将看到如何修改这个项目来使用一个从网页模拟的按钮。是的,我们将看到如何通过网络远程控制硬件和我们的代码。如果您使用的是 Pyboard 或另一种没有任何网络功能的 MicroPython 板,您将需要一个网络模块。随着项目的发展,我们将重新审视 Pyboard 的网络。

现在让我们看看这个项目需要哪些组件,然后我们将看看如何将所有组件连接在一起。

必需的组件

表 9-1 列出了你需要的部件。您可以从 Adafruit ( adafruit.com)、Sparkfun ( sparkfun.com)或任何出售电子元件的电子商店单独购买元件。如果您想购买这些组件,可以链接到供应商。当列出同一对象的多行时,您可以选择其中一行——您不需要两个都要。此外,您可能会找到销售这些组件的其他供应商。你应该货比三家,找到最好的交易。显示的成本是估计值,不包括任何运输成本。

表 9-1。

Required Components

| 成分 | 数量 | 描述 | 费用 | 链接 | | --- | --- | --- | --- | --- | | MicroPython 板 | one | 带标题的 Pyboard v1.1 版 | $45-50 | [`https://www.adafruit.com/product/2390`](https://www.adafruit.com/product/2390)[`https://www.adafruit.com/product/3499`](https://www.adafruit.com/product/3499) | | [`https://www.sparkfun.com/products/14413`](https://www.sparkfun.com/products/14413) | | [`https://store.micropython.org/store`](https://store.micropython.org/store) | | WiPy | $25 | [`https://www.adafruit.com/product/3338`](https://www.adafruit.com/product/3338) | | [`https://www.pycom.io/product/wipy/`](https://www.pycom.io/product/wipy/) | | 发光二极管 | Two | 红色指示灯 | 装备 | [`https://www.adafruit.com/product/2975`](https://www.adafruit.com/product/2975) | | 发光二极管 | Two | 黄色发光二极管 | 装备 | [`https://www.adafruit.com/product/2975`](https://www.adafruit.com/product/2975) | | 发光二极管 | one | 绿色发光二极管 | 装备 | [`https://www.adafruit.com/product/2975`](https://www.adafruit.com/product/2975) | | 电阻器 | five | 220 或 330 欧姆电阻 | $8-12 | [`https://www.sparkfun.com/products/10969`](https://www.sparkfun.com/products/10969) | | 纽扣 | one | 瞬时按钮,试验板友好 | 装备 | [`https://www.sparkfun.com/products/12708`](https://www.sparkfun.com/products/12708) | | 面包板 | one | 原型板,半尺寸 | $5 | [`https://www.sparkfun.com/products/12002`](https://www.sparkfun.com/products/12002) | | 网络模块(Pyboard) | one | CC3000 分线板(或同等产品) | $15+ | 各种各样的 | | 跳线(WiPy) | nine | M/M 跳线,6”(10 根跳线的成本) | $4 | [`https://www.sparkfun.com/products/8431`](https://www.sparkfun.com/products/8431) | | 跳线(线路板) | Seventeen | M/M 跳线,6”(10 根跳线的成本) | $4 | [`https://www.sparkfun.com/products/8431`](https://www.sparkfun.com/products/8431) | | 力量 | one | 从电脑获取电源的 USB 电缆 |   | 使用您的备件 | | one | USB 5V 电源和电缆 |   | 使用您的备件 |

注意在成本中,led 和按钮的“套件”。这是指这些组件可以在我们在第二章看到的 Adafruit 的零件 Pal 套件中找到。其他供应商可能有类似的套件。成套购买 led、按钮和电阻等基本元件要便宜得多。

同样,你可以买一套各种尺寸的电阻,比一次买几个便宜得多。事实上,你很可能会发现,购买一个每种尺寸 5 或 10 个电阻的小套件比购买一套要贵得多。Sparkfun 的套件将为您提供大多数项目所需的所有电阻器。

另外,请注意,WiPy 需要的跳线要少得多。这是因为我们将使用网络分线板(在本例中为 CC3000 模块)来允许 Pyboard 连接到我们的网络。

最后,注意我们需要一个 Pyboard 友好的网络模块。同样,目前这必须是基于 CC3000 的板或基于 WIZNET5K 的板。有关与 Pyboard 配合使用的网络分线板的示例,请参考前面的章节。

回想一下第七章,LED 需要一个限流电阻来将电流降低到 LED 的安全水平。为了确定我们需要多大的电阻,我们需要了解 LED 的几个方面。该数据可从制造商处获得,制造商以数据表的形式提供数据,或者在商业包装产品的情况下,在包装上列出数据。我们需要的数据包括最大电压、电源电压(LED 的电压)和 LED 的额定电流。

例如,如果我有一个像 Adafruit Parts Pal 中的 LED,在这种情况下是一个 5 毫米红色 LED,我们在 Adafruit 的网站(adafruit.com/products/297)上发现该 LED 的工作电流为 1.8-2.2 伏和 20 毫安。假设我们希望使用 5V 电源电压。然后我们可以将这些值代入这个公式:

R = (Vcc-Vf)/I

使用更具描述性的变量名称,我们得到如下结果。

Resistor = (Volts_supply - Volts_forward) / Desired_current

把我们的数据代入,我们得到这个结果。注意,我们有 mA,所以我们必须使用正确的十进制值(除以 1000)。在这种情况下,它是 0.020,我们将在中间选择一个电压。

Resistor = (5 – 1.8) / 0.020
         = 3.2 / 0.020
         = 160

因此,我们需要一个 160 欧姆的电阻。然而,没有这种额定值的电阻。当这种情况发生时,我们用大一号的。例如,如果您只有 220 欧姆甚至 330 欧姆的电阻,您可以使用这些电阻。结果将是发光二极管将不会那么亮,但是具有较高的电阻比使用太小的电阻要安全得多。电流太大,LED 会烧坏。

现在,让我们看看如何将组件连接在一起。

安装硬件

虽然这个项目需要使用 WiPy 连接很多电线,Pyboard 甚至需要更多电线,但我们将使用的元件很容易插入试验板。表 9-2 显示了本项目所需的连接。

表 9-2。

Connections for the MicroPython (Pyboard and WiPy)

| MicroPython 板 |   |   | | --- | --- | --- | | WiPy | Pyboard | 成分 | 电线颜色 | | --- | --- | --- | --- | | P3 | X7 | 停车灯:红色 LED |   | | P4 | X6 | 停车灯:黄色 LED |   | | 孕烯醇酮 | X5 | 停车灯:绿色 LED |   | | P6 | X4 | 行走信号:红色 LED |   | | P7 | X3 | 行走信号:黄色 LED |   | | P23 | X1 | 纽扣 |   | | 地线 | 地线 | 试验板 |   |

让我们来复习一些元件接线的技巧。将元件连接到电路板的最佳方式是使用试验板。正如我们在第七章中看到的,试验板允许我们插入组件,并使用跳线进行连接。在本项目中,我们将使用一根跳线从 MicroPython 板接地到试验板,然后在试验板上跳线连接到按钮。事实上,我们将使用试验板一侧的接地轨来插入 led 的一侧。

只要插销的方向如图所示,按钮在任何位置都可以工作–两条腿在中心槽的一侧。如果按钮的支脚可以伸到槽的任何一侧,按钮的方向就会正确。如果你把它移开 90 度,按钮要么不起作用,要么总是被触发。如果有任何疑问,使用万用表测试按钮连接的连续性。你会发现连接在未按下时打开,按下时关闭。

唯一极化的组件是 LED(它有一个正极和一个负极引脚)。当您查看 LED 时,您会看到 LED 的一条腿(引脚)比另一条腿长。这条较长的边是正面。我们将插入 led,使负极引脚插入接地轨,正极引脚插入试验板的主要区域。然后我们插入电阻,跳过中心槽,将电阻连接到 MicroPython 板上的 GPIO 引脚。不管你往哪个方向插电阻,它们都可以双向工作。

如果这听起来令人困惑,不要担心,因为接线图使连接更加明显。让我们看看如何连接 WiPy 和 Pyboard,如图所示。

WiPy

WiPy 的布线最好也是将 USB 连接器朝向扩展板的右侧。图 9-1 显示了 WiPy 的接线图。注意发光二极管、电阻和按钮的方向。您应该能够使用图纸和布线图来连接您自己的组件。另外,请注意 WiPy 的方向。这应该有助于您更容易地将引脚与试验板的导线对齐,但只要您使用正确的 GPIO 引脚,物理方向并不重要。

A447395_1_En_9_Fig1_HTML.jpg

图 9-1。

Wiring the Stoplight Simulation (WiPy)

现在,让我们看看 Pyboard 的接线图。由于需要添加一个网络分线板,所以变得有点复杂。

Pyboard

Pyboard 的布线最好将 USB 连接器朝向左侧。这将允许您读取电路板上的引脚数,即使在电线插入电路板之后。网络模块需要额外的连接。回想一下,CC3000 分线板需要额外的布线来将 SPI 接口连接到 Pyboard。就像我们对 led、电阻器和按钮所做的那样,我们应该计划如何连接分线板。表 9-3 显示了与 CC3000 分线板一起使用时 Pyboard 所需的连接。如果您使用不同的分线板,请确保使用您的连接对此计划进行注释。

表 9-3。

Additional Connections for the Pyboard and CC3000 Breakout Board

| Pyboard | CC3000 | 电线颜色 | | --- | --- | --- | | Y3 | 伊拉克 |   | | Y4 | VBEN .好吧 |   | | Y5 | 特许测量员 |   | | Y6 | 时钟信号 |   | | Y7 | 军事情报部门组织(Military Intelligence Service Organization) |   | | Y8 | 工业博物馆 |   | | V+ | 车辆识别号码 |   | | 地线 | 地线 |   |

由于需要这些额外的导线,Pyboard 布线图有点复杂。图 9-2 显示了带有 CC3000 分线板的 Pyboard 的接线图。

A447395_1_En_9_Fig2_HTML.jpg

图 9-2。

Wiring the Stoplight Simulation (WiPy)

哇,好多电线啊!您可以看到使用内置 WiFi 的 MicroPython 板的优势——您不必使用额外的电线,这些电线有时会使一个简单的项目变得更加复杂,或者当您看着您的项目全部连接起来时,它看起来像一个电线窝。

Note

如果您使用 Pyboard,您可以使用板载指示灯,而不是单独的指示灯。

最后,一定要确保仔细连接项目,仔细检查所有连接,尤其是电源、接地和用于信号的任何引脚(将设置为“高”或“开”),如用于 SPI 接口的引脚。最重要的是,当项目(或您的板)通电时,切勿插拔跳线。这很可能会损坏您的主板或组件。

Caution

项目通电时,切勿插拔跳线。您可能会损坏您的主板或组件。

同样,在给主板通电之前,请务必仔细检查您的连接。现在,我们来谈谈我们需要编写的代码。暂时不要启动您的主板——在我们准备好测试该项目之前,还需要进行大量的讨论。

写代码

现在是时候为我们的项目编写代码了。代码并不太复杂,但是比迄今为止的例子要长一点。因此,我们将分两部分编写代码。在第一部分中,我们将看到如何编写代码来模拟行人人行横道按钮和交通灯。在第二部分中,我们将放弃使用硬件按钮,而是使用 web 浏览器来远程控制按钮动作。

Note

这些部分演示了 WiPy 的代码,并描述了 Pyboard 的不同之处。有关每块电路板的代码,请参见本章末尾的完整代码列表。

正如您将看到的,第二部分将重用第一部分的大部分代码,但是 HTML 服务器代码会稍微复杂一些。让我们从项目的第一部分开始。

第一部分:交通信号灯模拟器-使用按钮

该项目的第一部分的代码将需要监控按钮,当按下时,如上所述循环灯。我们还需要代码来初始化 led,将它们设置为初始关闭。我们可以编写函数来监控按钮和循环发光二极管。我们将使用一个中断来将按钮的功能绑定到硬件上,这样我们就可以避免使用轮询循环。

进口

该项目的导入将需要来自machine库和utime库的Pin类。下面显示了 WiPy 的导入。

from machine import Pin
import utime

Pyboard 的导入还需要来自pyb库的Pin类以及delay函数和ExtInt类。ExtInt类用于设置按钮被按下时触发的中断。

from pyb import Pin, delay, ExtInt

设置

这个项目的设置代码将需要初始化按钮和 LED 实例,然后关闭所有的 LED(作为一种预防措施),打开绿色的交通信号灯 LED 和红色的步行信号 LED。清单 9-1 显示了设置和初始化的代码。

# Setup the button and LEDs
button = Pin('P23', Pin.IN, Pin.PULL_UP)
led1 = Pin('P3', Pin.OUT)
led2 = Pin('P4', Pin.OUT)
led3 = Pin('P5', Pin.OUT)
led4 = Pin('P6', Pin.OUT)
led5 = Pin('P7', Pin.OUT)

# Setup lists for the LEDs
stoplight = [led1, led2, led3]
walklight = [led4, led5]

# Turn off the LEDs
for led in stoplight:
    led.value(0)
for led in walklight:
    led.value(0)

# Start with green stoplight and red walklight
stoplight[2].value(1)
walklight[0].value(1)

Listing 9-1.Setup and Initialization of the Button and LEDs (WiPy)

需要注意的一点是按钮是如何初始化的。这是一个被设置为输入(读取)的Pin对象实例,上拉电阻打开。这使得电路板能够检测到按钮何时被按下,因为在建立连接时(按钮被按下),引脚的值将为正值。

还要注意,我创建了一个列表,其中包含交通灯和步行信号的 led(代码中称为 walklight)。这主要是为了演示,这样您可以看到如何管理类对象列表。正如您所看到的,使用一个循环为列表中的所有对象调用同一个函数更容易。请注意这项技术,因为在其他项目中您会不时地需要它。

Pyboard 的代码基本相同。不同之处包括 led 和按钮的不同引脚编号(见接线图),引脚初始化的不同选项(Pin.OUT_PP而非Pin.OUT),以及Pin类使用不同的功能:high()用于value(1),而low()用于value(0)

功能

项目的这一部分需要两个功能。首先,我们需要一个函数来循环灯光。其次,我们需要一个功能来监控按钮的按下。我们来看一下循环灯功能。

我们将周期灯功能命名为cycle_lights()。回想一下,我们需要控制灯光如何改变状态。如前所述,我们以特定的周期来完成这项工作。总的来说,当我们想要模拟按下行走请求按钮时交通灯的变化时,我们调用这个函数。因此,这个函数将从按钮的代码中调用。清单 9-2 显示了cycle_lights()按钮的代码。正如您将看到的,代码相当简单。唯一棘手的部分可能是用于闪烁黄色行走 LED 的回路。一定要通读一遍,这样你才能理解它是如何工作的。

# We need a method to cycle the stoplight and walklight
#
# We toggle from green to yellow for 2 seconds
# then red for 20 seconds.
def cycle_lights():
    # Go yellow.
    stoplight[2].value(0)
    stoplight[1].value(1)
    # Wait 2 seconds
    utime.sleep(2)
    # Go red and turn on walk light
    stoplight[1].value(0)
    stoplight[0].value(1)
    utime.sleep_ms(500)  # Give the pedestrian a chance to see it
    walklight[0].value(0)
    walklight[1].value(1)
    # After 10 seconds, start blinking the walk light
    utime.sleep(1)
    for i in range(0,10):
        walklight[1].value(0)
        utime.sleep_ms(500)
        walklight[1].value(1)
        utime.sleep_ms(500)

    # Stop=green, walk=red
    walklight[1].value(0)
    walklight[0].value(1)
    utime.sleep_ms(500)  # Give the pedestrian a chance to see it
    stoplight[0].value(0)
    stoplight[2].value(1)

Listing 9-2.The 
cycle_lights() Function

(WiPy)

Pyboard 的代码与前面提到的控制 led 和使用delay()功能代替utime类睡眠功能的变化非常相似。

我们将这个按钮功能命名为button_pressed()。该函数用作按钮按下中断的回调函数。从技术上讲,我们告诉 MicroPython 将这个方法与 pin 中断相关联,但是我们马上就会看到这一点。然而,这一功能还有另一个要素需要解释。

当我们使用像按钮这样的组件并且用户(您)按下按钮时,按钮中的触点不会立即从关闭状态变为打开状态。在很短的一段时间内,读数不稳定。因此,我们不能简单地说“当引脚变高时”,因为引脚上读取的值可能会从低到高(或从高到低)快速“反弹”。这叫弹跳。我们可以通过代码(以及其他技术)人为地克服这一点,称为去抖动。

在这种情况下,我们可以随时检查 pin(按钮)的值,并且只有当值在这段时间内保持稳定时才“触发”按钮按压。清单 9-3 中显示了引脚去抖动的代码。注意,在循环中,我们等待的值是 50。这是 50 毫秒。如果触发器足够长,我们调用cycle_lights()函数。

def button_pressed(line):
    cur_value = button.value()
    active = 0
    while (active < 50):
        if button.value() != cur_value:
            active += 1
        else:
            active = 0
        utime.sleep_ms(1)
        print("")
    if active:
        cycle_lights()
    else:
        print("False press")
Listing 9-3.The 
button_pressed() Function

(WiPy)

Tip

有关去抖和避免去抖技术的更多信息,请参见 http://www.eng.utah.edu/∼cs5780/debouncing.pdf .

最后,我们需要设置当板卡检测到中断时调用button_pressed()函数的按钮。下面设置了 WiPy 上的回调函数。

# Create an interrupt for the button
button.callback(Pin.IRQ_FALLING, button_pressed)

Pyboard 上的代码也是单行代码,但是在这种情况下,我们必须使用ExtInt类来设置中断处理程序,如下所示。

# Create an interrupt for the button
e = ExtInt('X1', ExtInt.IRQ_FALLING, Pin.PULL_UP, button_pressed)

Tip

有关对 WiPy 使用引脚回调和对 Pyboard 使用中断的更多信息,请参见在线 MicroPython 文档。

现在我们已经准备好测试代码了。继续打开一个名为ped_part1_wipy.py(或者 Pyboard 的ped_part1_pyb.py)的新文件,输入上面的代码。清单 9-4 显示了项目第一部分的完整代码。如果你正在使用 Pyboard,通过查看本章末尾的完整列表来作弊是可以的。

# MicroPython for the IOT - Chapter 9
#
# Project 2: A MicroPython Pedestrian Crosswalk Simulator
#            Part 1 - controlling LEDs and button as input
#
# Required Components:
# - WiPy
# - (2) Red LEDs
# - (2) Yellow LEDs
# - (1) Green LED
# - (5) 220 Ohm resistors
# - (1) breadboard friendly momentary button
#
# Note: this only runs on the WiPy.
#
# Imports for the project
from machine import Pin
import utime

# Setup the button and LEDs
button = Pin('P23', Pin.IN, Pin.PULL_UP)
led1 = Pin('P3', Pin.OUT)
led2 = Pin('P4', Pin.OUT)
led3 = Pin('P5', Pin.OUT)
led4 = Pin('P6', Pin.OUT)
led5 = Pin('P7', Pin.OUT)

# Setup lists for the LEDs
stoplight = [led1, led2, led3]
walklight = [led4, led5]

# Turn off the LEDs
for led in stoplight:
    led.value(0)
for led in walklight:
    led.value(0)

# Start with green stoplight and red walklight
stoplight[2].value(1)
walklight[0].value(1)

# We need a method to cycle the stoplight and walklight
#
# We toggle from green to yellow for 2 seconds
# then red for 20 seconds.
def cycle_lights():
    # Go yellow.
    stoplight[2].value(0)
    stoplight[1].value(1)
    # Wait 2 seconds
    utime.sleep(2)
    # Go red and turn on walk light
    stoplight[1].value(0)
    stoplight[0].value(1)
    utime.sleep_ms(500)  # Give the pedestrian a chance to see it
    walklight[0].value(0)
    walklight[1].value(1)
    # After 10 seconds, start blinking the walk light
    utime.sleep(1)
    for i in range(0,10):
        walklight[1].value(0)
        utime.sleep_ms(500)
        walklight[1].value(1)
        utime.sleep_ms(500)

    # Stop=green, walk=red
    walklight[1].value(0)
    walklight[0].value(1)
    utime.sleep_ms(500)  # Give the pedestrian a chance to see it
    stoplight[0].value(0)
    stoplight[2].value(1)

# Create callback for the button
def button_pressed(line):
    cur_value = button.value()
    active = 0
    while (active < 50):
        if button.value() != cur_value:
            active += 1
        else:
            active = 0
        utime.sleep_ms(1)
        print("")
    if active:
        cycle_lights()
    else:
        print("False press")

# Create an interrupt for the button
button.callback(Pin.IRQ_FALLING, button_pressed)

Listing 9-4.Stoplight Simulation – Part 1 (WiPy)

测试和调试代码

测试项目的这一部分需要将源代码文件复制到板上,然后执行它。由于代码是在没有run()函数的情况下编写的,所以只需简单地导入它就可以运行代码了。在给主板通电之前,请务必检查所有连接。回想一下,连接到电路板后,我们用来导入和运行代码的命令如下所示。

>>> import ped_part1_wipy

一旦导入,代码将运行,您可以按下按钮来查看灯光循环通过阶段。如果您没有看到任何灯亮起(绿色停车灯和红色行走信号灯应该亮起),请检查设置和初始化代码。如果按下按钮时灯不闪烁,请检查按钮的代码以确保它是正确的。如果它们没有按顺序亮起,可能是针脚接线不正确。遇到问题时,请务必检查所有接线连接,在断开或重新连接任何电线或组件之前,请务必关闭主板电源。

当项目运行时,尝试几次,以确保它按预期运行。那你应该恭喜你自己!您刚刚将几个分立的电子元件连接在一起,制作了一个行人行走按钮和交通灯的工作模拟。酷!

现在,让我们把这个项目提升一个档次,让它可以通过互联网访问。毕竟,这就是 IOT 的全部!

第二部分:交通信号灯模拟器 HTML 远程控制

这一部分的代码将使用第一部分的所有代码,但我们不需要按钮的代码。相反,我们将使用Socket类创建一个监听器来监听来自 web 浏览器的连接。该代码将向客户端发送一个简短的基于 HTML 的响应(一个简单的 web 页面),其中包括一个包含两个按钮的表单——一个用于 walk 请求,另一个用于关闭服务器代码。侦听器将在端口 80 上侦听。

Note

如果您正在使用 WiPy,您不需要添加任何网络代码,但是如果您想在自己的网络上运行该项目或者将它连接到 Internet,我将向您展示如何添加代码。

HTML 服务器的概念非常简单。代码监听套接字端口上的连接,然后接收请求(在本例中以 HTML GET 方法的形式)并发送 HTML 响应(网页)。然后,代码检查请求是否包含来自两个按钮之一的表单数据:一个按钮请求步行周期,另一个按钮关闭服务器。正如您将看到的,关闭服务器迫使我们将代码写得更加整洁。

如果您以前从未使用过 HTML 代码,不要担心,因为示例代码将提供您需要的一切。您不必学习 HTML 来使用本章(或下一章)中的项目,但是如果您想详细说明该项目或在您自己的项目中使用 HTML 服务器概念,一些基础知识会很有帮助。在 https://www.w3schools.com/html/ 可以找到关于 HTML 的一个很好的信息源。

让我们看看 imports 部分需要做的更改。

进口

在导入部分我们还需要几个库。我们需要socket库(为了清楚起见,用别名重新命名了)machine库,以及来自network库的WLAN类。以这种方式编写导入是完全正常的,但是只要有一点想象力,您就可以简化它们。你明白了吗?下面显示了 WiPy 的完整导入部分。

from machine import Pin
import usocket as socket
import utime
import machine
from network import WLAN

Pyboard 的导入有点短。我们需要为网卡添加SPI库和network库以及socket库。下面显示了 Pyboard 的完整导入部分。

from pyb import Pin, delay, ExtInt, SPI
import network
import usocket as socket

现在,让我们看看设置部分的变化。

设置

对于项目的这一部分,我们需要添加代码,通过无线连接将我们的电路板连接到我们的网络(或互联网)。我们还需要将 HTML 代码以字符串的形式放在这里。这通常是在 Python 中定义大字符串的地方(和方式)。在任一侧使用三重引号会使字符串成为文档字符串(也称为 docstring)。详见 https://www.python.org/dev/peps/pep-0257/

WiPy 的网络代码与我们在本书前面看到的代码相同。在这种情况下,它设置无线网络功能以连接到现有的无线网络,而不是 WiPy 的默认接入点行为。清单 9-5 显示了 WiPy 的网络代码。

# Setup the board to connect to our network.
wlan = WLAN(mode=WLAN.STA)
nets = wlan.scan()
for net in nets:
    if net.ssid == 'YOUR_SSID':
        print('Network found!')
        wlan.connect(net.ssid, auth=(net.sec, 'YOUR_PASSWORD'), timeout=5000)
        while not wlan.isconnected():
            machine.idle() # save power while waiting
        print('WLAN connection succeeded!')
        print("My IP address is: {0}".format(wlan.ifconfig()[0]))
        break
Listing 9-5.
Wireless Network Setup

(WiPy)

Pyboard 的网络代码与我们在本书前面看到的代码相同。在这种情况下,它首先设置 SPI 接口到网络板(在本例中是 CC3000 分线板),然后启动到现有无线网络的无线连接,而不是 WiPy 的默认接入点行为。清单 9-6 显示了 Pyboard 的网络代码。

# Setup network connection
nic = network.CC3K(SPI(2), Pin.board.Y5, Pin.board.Y4, Pin.board.Y3)
# Replace the following with yout SSID and password
nic.connect("YOUR_SSID", "YOUR_PASSWORD")
print("Connecting...")
while not nic.isconnected():
    delay(50)
print("Connected!")
print("My IP address is: {0}".format(nic.ifconfig()[0]))
Listing 9-6.Wireless Network Setup (Pyboard)

Tip

您必须更改代码中的 SSID 和密码以匹配您的网络。

设置的另一部分是 HTML 响应代码。这似乎是代码中很难的部分,但是很简单。我们正在构造一个字符串,我们将通过套接字把它发送回客户机。该字符串包含以头开始的 HTML 代码。然后,我们提供一个标题(出现在浏览器的标题栏中),一些显示在页面上的文本,以及一个包含这两个按钮的表单。这些按钮的形式是,当每个按钮被按下时,都会通过套接字向服务器发送一个 HTML GET 请求。简单!清单 9-7 显示了项目的 HTML 字符串。还是那句话,不用担心细节。你可以以后再改进网页。

# HTML web page for the project
HTML_RESPONSE = """<!DOCTYPE html>
<html>
  <head>
    <title>MicroPython for the IOT - Project 2</title>
  </head>
  <center><h2>MicroPython for the IOT - Project 2</h2></center><br>
  <center>A simple project to demonstrate how to control hardware over the Internet.</center><br><br>
  <form>
    <center>
        <button name="WALK" value="PLEASE" type="submit" style="height: 50px; width: 100px">REQUEST WALK</button>
        <br><br>
        <button name="shutdown" value="now" type="submit" style="height: 50px; width: 100px">Stop Server</button>
    </center>
  </form>
</html>
"""
Listing 9-7.
HTML Response String

注意代码(<button></button>标签中的按钮)。名称和值将在请求中发送到服务器。按钮的类型被定义为 submit,当按钮被放置在表单上时,当按钮被按下时,会使客户端将表单数据发送到服务器。最后,结束标记前的字符串是将显示在按钮上的标签。

如果你担心这是一个占用内存的大字符串,你是对的,确实如此。如果您正在计划一个使用大量 HTML 响应或者多个响应的项目,您可能希望考虑将这些响应存储在一个文件中(每个文件一个响应),并在将数据发送到客户端之前从文件中读取数据。这将节省一些数据空间,并可能对使用更多内存的大型项目产生影响。

不要在意这里使用的行数。空白主要是用来装饰的,所以如果你想减少它的整体视觉尺寸,你可以去掉空白,但是通常的做法是像这样缩进 HTML 代码以便于阅读。

Tip

如果您担心安全性(谁不担心呢?),您可以使用安全套接字层连接来使您与 MicroPython 板的连接更加安全。MicroPython 提供了一个名为ussl的类供您使用。更多信息参见ussl类文档,或者你可以看看 https://github.com/micropython/micropython/blob/master/examples/network/http_server_ssl.py 的例子。

关于在 MicroPython 板上使用 HTML,还有一件重要的事情需要弄清楚:这个例子和您可能在互联网上找到的许多其他例子不应该与健壮的 web 服务器混淆。更具体地说,这个例子只是在侦听网络套接字时,一旦检测到来自客户机的 HTML GET 方法(请求),就将 HTML 发送回客户机。所以,这个例子仅仅是一个简单的 HTML 服务器,而不是一个 web 服务器,因为它能做 web 服务器能做的所有事情——它不能。因此,您应该注意对项目进行编码,将操作限制在特定的 GET(或 POST)请求上,并返回适当的 HTML 响应。

Using Web Pages with Images

由于 MicroPython 中还没有内置的 web 服务器,因此为 web 页面提供图像或任何其他媒体是有问题的。然而,你可以使用一个小技巧和你的电脑在你的项目中使用图像。我们可以在我们的 PC 上使用 Python 中的SimpleHTTPServer来提供一个基本的 web 服务器。在我们的 MicroPython 板的 HTML 中,我们可以对图像使用img标签,它使用一个 URL 指向我们 PC 上的文件。例如,下面的 HTML 将引用与我们 PC 上的 HTTP 服务器在同一个文件夹中的图像。

<html>
  <head>
    <title>MicroPython for the IOT - Project 2</title>
    <meta http-equiv="refresh" content="10">
  </head>
  <body>
    <p>Pedestrian Stoplight Simulation</p>
    <form>
        <img src="http://localhost:8000/red.png">
    </form>
  </body>
</html>

注意,img标签使用了一个到本地主机 8000 端口的 URL。当我们在 PC 上运行 Python 中的SimpleHTTPServer时,我们确定了这一点,如下所示。在您想要使用的图像所在的文件夹中运行此命令。

$ python -m SimpleHTTPServer
Serving HTTP on 0.0.0.0 port 8000 ...
127.0.0.1 - - [07/Aug/2017 14:42:30] "GET /red.png HTTP/1.1" 200 -

这创建了一个混合解决方案,它将允许您在您的 MicroPython 基于 web 的项目中使用图像,尽管需要我们的 PC(或您网络上的任何其他 PC)的一点帮助。

现在我们来看看如何制作run()函数。

功能

代码中剩下要做的就是更改硬件按钮的代码,并创建一个包含代码主要部分的run()函数。在这个项目中,包括设置套接字的代码以及监听和响应代码。您可以保留button_pressed()功能,但它不是必需的。清单 9-8 显示了run()函数的代码。

# Setup the socket and respond to HTML requests
def run():
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.bind(('', 80))
    sock.listen(5)
    server_on = True
    while server_on:
        client, address = sock.accept()
        print("Got a connection from a client at: %s" % str(address))
        request = client.recv(1024)
        # Process the request from the client. Payload should appear in pos 6.
        # Check for walk button click
        walk_clicked = (request[:17] == b'GET /?WALK=PLEASE')
        # Check for stop server button click
        stop_clicked = (request[:18] == b'GET /?shutdown=now')
        if walk_clicked:
            print('Requesting walk now!')
            cycle_lights()
        elif stop_clicked:
            server_on = False
            print("Goodbye!")
        client.send(HTML_RESPONSE)
        client.close()
Listing 9-8.The 
run() Function

(WiPy)

注意,我们从设置套接字并将其绑定到端口 80 开始这个函数。bind()函数使用空字符串来指示套接字使用当前的 IP 地址。接下来,我们使用listen(5)函数告诉套接字侦听套接字,超时时间为 5 秒。然后,我们设置一个布尔变量,当它为真时,在监听和响应代码上循环。在这种情况下,我们使用布尔值,并在按下关闭按钮时将其设置为 False。

循环内部是事情变得有趣的地方。我们做的第一件事是使用sock.accept()接受来自套接字的连接,它返回两项:客户机对象实例和客户机的地址。我们可以打印出客户端的地址以供调试之用。

接下来,我们告诉客户端通过client.recv(1024)调用接收多达 1024 个字节。这将一直等到有来自客户端的响应,所以请注意这不是一个抢占式循环——它将一直等到客户端响应。当客户端发送数据时(比如第一次连接或按下按钮时),我们可以搜索为命令发送的字符串。

注意,在代码中,我们检查搜索b'GET /?WALK=PLEASE'的行走请求或搜索b'GET /?shutdown=now'的关闭事件。这里有一点需要解释的诡计。我们使用请求字符串作为一个数组,从字符串中的字符数(17 或 18)开始搜索,用[:17][:18]表示。

如果检测到 walk 请求,我们就调用cycle_lights()函数,这是第一部分中没有修改的函数。如果检测到关机事件,我们将循环设置为终止,然后退出run()功能。

花些时间通读代码,直到你理解它是如何工作的。准备好后,打开一个名为ped_part2_wipy.py(或者 Pyboard 的ped_part2_pyb.py)的新文件,输入上面的代码。

最后,您必须删除设置按钮回调(或 Pyboard 上的ExtInt()调用)的代码行。把它留在里面不会损害代码,但是因为我们不使用按钮,所以不需要它,所有不必要的代码都应该删除。

现在,让我们看看 WiPy 和 Pyboard 的完整代码。

完整代码

在本节中,我们将看到 WiPy 和 Pyboard 的最终完整代码。这些列表供您参考,以确保您拥有适合您的主板的正确代码。清单 9-9 显示了在 WiPy 上运行项目的完整代码。

# MicroPython for the IOT - Chapter 9
#
# Project 2: A MicroPython Pedestrian Crosswalk Simulator
#            Part 3 - controlling LEDs over the Internet
#
# Required Components:
# - WiPy
# - (2) Red LEDs
# - (2) Yellow LEDs
# - (1) Green LED
# - (5) 220 Ohm resistors
#
# Note: this only runs on the WiPy.
#
# Imports for the project
from machine import Pin
import usocket as socket
import utime
import machine
from network import WLAN

# Setup the board to connect to our network.
wlan = WLAN(mode=WLAN.STA)
nets = wlan.scan()
for net in nets:
    if net.ssid == 'YOUR_SSID':
        print('Network found!')
        wlan.connect(net.ssid, auth=(net.sec, 'YOUR_PASSWORD'), timeout=5000)
        while not wlan.isconnected():
            machine.idle() # save power while waiting
        print('WLAN connection succeeded!')
        print("My IP address is: {0}".format(wlan.ifconfig()[0]))
        break

# HTML web page for the project
HTML_RESPONSE = """<!DOCTYPE html>
<html>
  <head>
    <title>MicroPython for the IOT - Project 2</title>
  </head>
  <center><h2>MicroPython for the IOT - Project 2</h2></center><br>
  <center>A simple project to demonstrate how to control hardware over the Internet.</center><br><br>
  <form>
    <center>
        <button name="WALK" value="PLEASE" type="submit" style="height: 50px; width: 100px">REQUEST WALK</button>
        <br><br>
        <button name="shutdown" value="now" type="submit" style="height: 50px; width: 100px">Stop Server</button>
    </center>
  </form>
</html>
"""

# Setup the LEDs (button no longer needed)
led1 = Pin('P3', Pin.OUT)
led2 = Pin('P4', Pin.OUT)
led3 = Pin('P5', Pin.OUT)
led4 = Pin('P6', Pin.OUT)
led5 = Pin('P7', Pin.OUT)

# Setup lists for the LEDs
stoplight = [led1, led2, led3]
walklight = [led4, led5]

# Turn off the LEDs
for led in stoplight:
    led.value(0)
for led in walklight:
    led.value(0)

# Start with green stoplight and red walklight
stoplight[2].value(1)
walklight[0].value(1)

# We need a method to cycle the stoplight and walklight
#
# We toggle from green to yellow for 2 seconds
# then red for 20 seconds.
def cycle_lights():
    # Go yellow.
    stoplight[2].value(0)
    stoplight[1].value(1)
    # Wait 2 seconds
    utime.sleep(2)
    # Go red and turn on walk light
    stoplight[1].value(0)
    stoplight[0].value(1)
    utime.sleep_ms(500)  # Give the pedestrian a chance to see it
    walklight[0].value(0)
    walklight[1].value(1)
    # After 10 seconds, start blinking the walk light
    utime.sleep(1)
    for i in range(0,10):
        walklight[1].value(0)
        utime.sleep_ms(500)
        walklight[1].value(1)
        utime.sleep_ms(500)

    # Stop=green, walk=red
    walklight[1].value(0)
    walklight[0].value(1)
    utime.sleep_ms(500)  # Give the pedestrian a chance to see it
    stoplight[0].value(0)
    stoplight[2].value(1)

# Setup the socket and respond to HTML requests
def run():
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.bind(('', 80))
    sock.listen(5)
    server_on = True
    while server_on:
        client, address = sock.accept()
        print("Got a connection from a client at: %s" % str(address))
        request = client.recv(1024)
        # Process the request from the client. Payload should appear in pos 6.
        # Check for walk button click
        walk_clicked = (request[:17] == b'GET /?WALK=PLEASE')
        # Check for stop server button click
        stop_clicked = (request[:18] == b'GET /?shutdown=now')
        if walk_clicked:
            print('Requesting walk now!')
            cycle_lights()
        elif stop_clicked:
            server_on = False
            print("Goodbye!")
        client.send(HTML_RESPONSE)
        client.close()
    sock.close()

Listing 9-9.Complete Code for the Stoplight Simulation (WiPy)

现在,让我们看看 Pyboard 的代码。正如您将看到的,除了网络代码、导入和 pin 函数调用之外,它基本上是相同的。清单 9-10 显示了在 Pyboard 上运行项目的完整代码。

# MicroPython for the IOT - Chapter 9
#
# Project 2: A MicroPython Pedestrian Crosswalk Simulator
#            Part 2 - Controlling the walklight remotely
#
# Required Components:
# - Pyboard
# - (2) Red LEDs
# - (2) Yellow LEDs
# - (1) Green LED
# - (5) 220 Ohm resistors
# - (1) breadboard friendly momentary button
# - (1) CC3000 breakout board
#
# Note: this only runs on the Pyboard.
#
# Imports for the project
from pyb import Pin, delay, ExtInt, SPI
import network
import usocket as socket

# Setup network connection
nic = network.CC3K(SPI(2), Pin.board.Y5, Pin.board.Y4, Pin.board.Y3)
# Replace the following with yout SSID and password
nic.connect("YOUR_SSID", "YOUR_PASSWORD")
print("Connecting...")
while not nic.isconnected():
    delay(50)
print("Connected!")
print("My IP address is: {0}".format(nic.ifconfig()[0]))

# HTML web page for the project
HTML_RESPONSE = """<!DOCTYPE html>
<html>
  <head>
    <title>MicroPython for the IOT - Project 2</title>
  </head>
  <center><h2>MicroPython for the IOT - Project 2</h2></center><br>
  <center>A simple project to demonstrate how to control hardware over the Internet.</center><br><br>
  <form>
    <center>
        <button name="WALK" value="PLEASE" type="submit" style="height: 50px; width: 100px">REQUEST WALK</button>
        <br><br>
        <button name="shutdown" value="now" type="submit" style="height: 50px; width: 100px">Stop Server</button>
    </center>
  </form>
</html>
"""

# Setup the LEDs
led1 = Pin('X7', Pin.OUT_PP)
led2 = Pin('X6', Pin.OUT_PP)
led3 = Pin('X5', Pin.OUT_PP)
led4 = Pin('X4', Pin.OUT_PP)
led5 = Pin('X3', Pin.OUT_PP)

# Setup lists for the LEDs
stoplight = [led1, led2, led3]
walklight = [led4, led5]

# Turn off the LEDs
for led in stoplight:
    led.low()
for led in walklight:
    led.low()

# Start with green stoplight and red walklight
stoplight[2].high()
walklight[0].high()

# We need a method to cycle the stoplight and walklight
#
# We toggle from green to yellow for 2 seconds
# then red for 20 seconds.
def cycle_lights():
    # Go yellow.
    stoplight[2].low()
    stoplight[1].high()
    # Wait 2 seconds
    delay(2000)
    # Go red and turn on walk light
    stoplight[1].low()
    stoplight[0].high()
    delay(500)  # Give the pedestrian a chance to see it
    walklight[0].low()
    walklight[1].high()
    # After 10 seconds, start blinking the walk light
    delay(10000)
    for i in range(0,10):
        walklight[1].low()
        delay(500)
        walklight[1].high()
        delay(500)

    # Stop=green, walk=red
    walklight[1].low()
    walklight[0].high()
    delay(500)  # Give the pedestrian a chance to see it
    stoplight[0].low()
    stoplight[2].high()

# Setup the socket and respond to HTML requests
def run():
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.bind(('', 80))
    sock.listen(5)
    server_on = True
    while server_on:
        client, address = sock.accept()
        print("Got a connection from a client at: %s" % str(address))
        request = client.recv(1024)
        # Process the request from the client. Payload should appear in pos 6.
        # Check for walk button click
        walk_clicked = (request[:17] == b'GET /?WALK=PLEASE')
        # Check for stop server button click
        stop_clicked = (request[:18] == b'GET /?shutdown=now')
        if walk_clicked:
            print('Requesting walk now!')
            cycle_lights()
        elif stop_clicked:
            server_on = False
            print("Goodbye!")
        client.send(HTML_RESPONSE)
        client.close()
    sock.close()

Listing 9-10.Complete Code for the Stoplight Simulation (Pyboard)

好了,现在我们准备好执行这个项目了。

执行!

现在是有趣的部分!我们已经设置好了控制 led 的代码,从第一部分我们就知道它是有效的。我们还有代码来设置一个套接字侦听器,以通过我们的 MicroPython 板上的端口 80 接受连接。我们现在所需要的就是那块板的 IP 地址来指向我们的网络浏览器。我们可以通过运行代码从我们的调试语句中得到答案。清单 9-11 显示了项目在 WiPy 上的初始运行(Pyboard 的结果类似)。

MicroPython v1.8.6-694-g25826866 on 2017-06-29; WiPy with ESP32
Type "help()" for more information.
>>> import ped_part3_wipy as w
Network found!
WLAN connection succeeded!
My IP address is: 192.168.42.128
>>> w.run()
Got a connection from a client at: ('192.168.42.127', 49236)
Got a connection from a client at: ('192.168.42.127', 49237)
Got a connection from a client at: ('192.168.42.127', 49243)
Requesting walk now!
Got a connection from a client at: ('192.168.42.127', 49254)
Goodbye!
>>>
Listing 9-11.Running the Stoplight Simulation (WiPy)

注意,在这种情况下,IP 地址是 192.168.42.127。我们需要做的就是把它放入我们的浏览器,如图 9-3 所示。

Caution

如果您使用 WiPy 和 telnet 打开 REPL 控制台,请注意,一旦您运行代码的网络部分,您的 REPL 控制台将断开连接。这是因为 IP 地址会变!因此,当使用 WiPy 连接到您的网络时,最好通过 USB 使用屏幕或其他终端程序来获得 REPL 控制台。

A447395_1_En_9_Fig3_HTML.jpg

图 9-3。

Stoplight Simulation Project

输入 URL 后,您应该会看到如图所示的网页。如果没有,一定要检查代码中的 HTML,确保它和显示的完全一样;否则,页面可能无法正常显示。您还应该确保您的 PC 所连接的网络可以连接到您的主板所连接的网络。如果您的家庭办公室像我一样设置,可能有几个 WiFi 网络可供您使用。最好你的主板和你的电脑在同一个网络(和同一个子网)。

一旦你解决了这个问题,继续按下按钮。请记住,步行按钮将启用,您将看到灯光循环,但您将无法做任何事情,直到步行循环完成。这是因为我们直到循环完成后才返回响应 HTML(请看代码来说服自己)。此外,当您单击 shutdown 按钮时,您将需要重新启动代码来重新运行它。只需再次调用run()函数。

再看一下上面的清单。注意,每次客户端连接时都会打印调试消息(代码接受连接和 GET 请求)以及一个关于它正在做什么的声明。您应该会在 REPL 控制台中看到类似的内容。

在这一点上,你应该沐浴在你的第一个成功的 MicroPython IOT 远程控制硬件项目的奇迹中。花些时间享受出色完成的工作。

这个过程的最后一步对于这个项目来说是可选的,因为除了测试之外,您不太可能希望它运行更多。然而,如果您确实想让它在每次启动您的板时运行,您可以修改板上的main.py代码模块来导入您的代码并调用run()函数。

When Things Go Wonky1

有时,当处理更大的脚本或更复杂的逻辑、许多库(驱动程序),甚至当使用许多字符串(内存)时,有时会遇到奇怪、简洁的错误。下面是一个例子。

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "ped_part2_wipy.py", line 97, in run
OSError: [Errno 12] ENOMEM

在这种情况下,该错误是一个内存不足错误,意味着您的 MicroPython 板已经为您的代码分配了所有(或几乎所有)内存,并且它无法继续。此时你唯一能做的就是重启主板。您可以尝试软启动(CTRL-D),但这通常不能解决问题,但硬重启可以。

至此,您已经完成了第一个真正的 MicroPython IOT 项目。我们可以称之为 IOT 项目,因为它使用互联网,但它只是一个模拟,并不实际控制真正的交通灯,但我们在这个项目中看到的技术允许我们将我们的 MicroPython 板连接到互联网并与之交互-这就是 IOT 的全部内容!

更进一步

这个项目展示了在其他项目中重用这些技术的良好前景。对于 HTML 服务器方面来说尤其如此。如果您喜欢通过互联网控制您的 MicroPython 板,您应该考虑花时间探索一些修饰。这里有一些你可以考虑的。有些很容易,有些可能是一个挑战或需要更多的研究。 2

  • 使用新像素( https://www.adafruit.com/category/168 )代替 led。这些是 RGB 发光二极管,所以你只需要两个——一个用于交通信号灯,一个用于步行灯。更多信息和示例见 https://github.com/JanBednarik/micropython-ws2812
  • 修改 HTML 响应以显示灯的状态。
  • 探索 HTML 代码,根据您的喜好更改网页。考虑使用级联样式表来更改按钮被按下时的背景。
  • 将您的板连接到互联网,并呼叫朋友连接到您的板并进行测试。
  • 使用显示屏代替行走标志的发光二极管来显示“行走”或“不要行走”

当然,如果你想继续下一个项目,欢迎你这样做,但是花些时间探索这些潜在的修饰——这将是一个很好的实践。

如果您准备迎接真正的挑战,您可以在这个项目中重用代码,并用继电器板替换按钮逻辑,这样您就可以使用 MicroPython 板等低压设备来打开或关闭高压电路。在这种情况下,您可以使用 HTML 按钮通过互联网打开或关闭中继。

摘要

使用分立的电子元件会非常有趣。当你刚开始接触电子学时,光是让电路工作就令人兴奋不已。现在我们对 MicroPython 有了更多的了解,我们可以看到拥有像 Python 这样的易于编程的语言来直接处理硬件——甚至通过互联网——是多么强大。

在这一章中,我们实现了一个行人人行横道按钮和交通信号灯的模拟。我们用一系列发光二极管来代表交通信号灯和步行信号。我们还添加了一个硬件按钮来模拟按下真正的步行按钮,然后通过简单的 HTML 从我们的 MicroPython 板上将它转换成一个遥控按钮。如果你喜欢这个项目,你会更喜欢接下来的两个项目。

在下一章中,我们将探索一个项目,该项目使用传感器读取值,然后将数据存档,并在需要时显示在网页上。这是我们构建真正的 MicroPython IOT 项目的倒数第二步。本章和下一章将向您展示如何通过互联网提供数据,项目的最后一章将向您展示如何通过云服务提供您的传感器数据。

Footnotes 1

一个高度技术性的术语,描述一种极度混乱和普遍执行失败的状态。不要与 hinky 混淆,hinky 表示当某件事情工作但不太正确时,情况稍微不太严重。

2

我把这些包括在内,这样你可以增长你的知识,超越这本书的范围。当你完成书中的其他项目时,试试这些。

十、项目 3:工厂监控

IOT 项目最常见的形式之一是那些使用传感器监控事件的项目,这些传感器将数据提供给另一台机器、云服务或本地服务器(如 HTML 服务器)。一种方法是将 MicroPython 板连接到一组传感器上,然后记录数据。您可以在互联网上找到几个通用数据记录器的示例,但是很少将数据记录与可视化组件结合起来。事实上,理解数据是制定成功的 IOT 解决方案的关键。

在这个项目中,我们将探索如何将数据记录与数据可视化结合起来。我们将使用与上一章相同的 HTML 服务器技术以及前几章的一些技术。我们还将了解如何使用模拟传感器——这种传感器产生模拟数据,然后我们必须对其进行解释。事实上,我们将依靠电路板的模数转换(ADC)功能来将电压读数变为我们可以使用的值。当我们利用从第四章中学到的关于类和模块的知识时,我们还会在代码中看到更多的复杂性。

这个项目增加的复杂性不是新的硬件或接口,尽管我们将看到如何使用模数转换器类;复杂性在于代码的复杂性。正如你将看到的,本章中使用的代码比以前的项目更加模块化,使用了更多的功能。仅仅因为这个原因,它就更复杂了。但是,正如您将看到的,代码并不难学,并且使用了我们在前几章中看到的概念。

概观

在这一章中,我们将实现一个植物土壤湿度监测解决方案(简称植物监测)。这将涉及到使用一个或多个连接到我们的 MicroPython 板上的土壤湿度传感器。我们将设置一个定时警报(中断)来定期运行,以从传感器读取数据,并将其存储在一个逗号分隔值(CSV)文件中。

图 10-1 描绘了该项目的概念图。MicroPython 板将从土壤湿度传感器读取数据,然后根据请求通过 HTML 网页显示出来。

A447395_1_En_10_Fig1_HTML.jpg

图 10-1。

Plant Monitoring Project Concept

这个项目的用户界面是一个由一个表组成的网页,该表包含从日志文件中读取的所有数据。这就是我们如何克服在循环中运行 HTML 服务器的潜在问题。也就是说,我们不必中断循环来读取传感器——这是通过定时器警报回调来完成的。遗憾的是,这种技术只适用于 WiPy 和类似的电路板。我们将不得不对 Pyboard 使用不同的技术。

通过将传感器读数从显示中分离出来,我们可以重用或修改它们,而不会在钻研代码时感到困惑。例如,只要可视化组件从文件中读取传感器数据,读取代码的传感器如何使用它就无关紧要。这两部分之间唯一的接口或连接是文件的格式,因为我们使用的是 CSV 文件,所以在我们的代码中很容易阅读和使用。

为了让事情变得更有趣和更容易编码,我们将把所有传感器代码放在一个单独的代码模块中。回想一下,这是一种技术,用于帮助减少任何一个模块中的代码量,从而使其更容易编写和维护。

现在让我们看看这个项目需要哪些组件,然后我们将看看如何将所有组件连接在一起。

Note

由于我们已经进入了第三个项目,并且已经看到了该项目中采用的许多技术,所以关于所需组件、布线和硬件设置的讨论应该是简短的。

必需的组件

表 10-1 列出了你需要的部件。您可以从 Adafruit ( adafruit.com)、Sparkfun ( sparkfun.com)或任何出售电子元件的电子商店单独购买元件。如果您想购买这些组件,可以链接到供应商。当列出同一对象的多行时,您可以选择其中一行——您不需要两个都要。此外,您可能会找到销售这些组件的其他供应商。你应该货比三家,找到最好的交易。显示的成本是估计值,不包括任何运输成本。

表 10-1。

Required Components

| 成分 | 数量 | 描述 | 费用 | 链接 | | --- | --- | --- | --- | --- | | MicroPython 板 | one | 带标题的 Pyboard v1.1 版 | $45-50 | [`https://www.adafruit.com/product/2390`](https://www.adafruit.com/product/2390)[`https://www.adafruit.com/product/3499`](https://www.adafruit.com/product/3499) | | [`https://www.sparkfun.com/products/14413`](https://www.sparkfun.com/products/14413) | | [`https://store.micropython.org/store`](https://store.micropython.org/store) | | WiPy | $25 | [`https://www.adafruit.com/product/3338`](https://www.adafruit.com/product/3338) | | [`https://www.pycom.io/product/wipy/`](https://www.pycom.io/product/wipy/) | | 土壤湿度传感器 | 1+ | 土壤湿度传感器 | $6 | [`https://www.sparkfun.com/products/13637`](https://www.sparkfun.com/products/13637) | | 网络模块(Pyboard) | one | CC3000 分线板(或同等产品) | $15+ | 各种各样的 | | 实时时钟 | one | 对于 Pyboard 和其他不支持 NTP 的板,RTC 是可选的 | $10+ | [`https://www.sparkfun.com/products/12708`](https://www.sparkfun.com/products/12708) | | 跳线(WiPy) | 3* | M/M 跳线,6”(10 根跳线的成本) | $4 | [`https://www.sparkfun.com/products/8431`](https://www.sparkfun.com/products/8431) | | 跳线(线路板) | 15+ | M/M 跳线,6”(10 根跳线的成本) | $4 | [`https://www.sparkfun.com/products/8431`](https://www.sparkfun.com/products/8431) | | 力量 | one | 从电脑获取电源的 USB 电缆 |   | 使用您的备件 | | one | USB 5V 电源和电缆 |   | 使用您的备件 |

需要的跳线数量将根据您使用的传感器数量以及您是否使用需要网络模块的 MicroPython 板而有所不同。每个传感器需要三根跳线,如果使用 CC3000 SPI 分线板,还需要八根额外的电线。如果您计划使用 Pyboard,您还需要添加一个实时时钟,以便在关机时保持时间。

Pyboard 固件不支持网络时间协议(NTP)服务器。因此,每次给电路板上电时,您必须初始化板载 RTC,将备用电池连接到电路板上(查看电路板的底部,您将看到需要焊接的引脚),或者添加实时时钟模块。

土壤湿度传感器有多种形式,但大多数都有两个插入土壤的插脚,使用少量电荷测量插脚之间的电阻。读数越高,土壤中的水分越多。但是,要获得可靠或现实的阈值,需要进行一些配置。虽然制造商会有阈值建议,但可能需要一些实验来找到正确的值。

这些传感器也会受到环境因素的影响,包括植物所处的花盆类型、土壤成分和其他因素。因此,用一种已知的过度湿润的土壤、干燥的土壤和适当养护的土壤做实验,将有助于你缩小你的环境门槛。

图 10-2 显示了 Sparkfun 的土壤湿度传感器,它有一个端子座,而不是引脚。你可以找到几种不同的传感器。只需选择您想要使用的一个,记住您可能需要不同的跳线将其连接到您的主板。

A447395_1_En_10_Fig2_HTML.jpg

图 10-2。

Soil Moisture Sensor (courtesy of sparkfun.com)

特别值得注意的是这些土壤湿度传感器是如何工作的。如果让传感器一直通电,它们会随着时间的推移而退化。尖头上的金属会由于电解而降解,从而大大缩短其寿命。当您想要读取值时,可以通过打开 GPIO 引脚来使用 GPIO 引脚技术为传感器供电。请记住,传感器稳定时会有一个小的延迟,但我们可以使用一个简单的延迟来等待,然后读取值并关闭传感器。这样,我们可以大大延长传感器的寿命。

幸运的是,这个项目的布线没有前两个项目复杂。现在,让我们看看如何将组件连接在一起。

安装硬件

表 10-2 显示了本项目所需的连接。这里只显示了两个传感器,但是如果你愿意的话,你可以增加几个。但是,建议您从一个传感器开始,直到项目正常运行,然后添加其他传感器。如果更简单,您可以使用试验板将传感器连接到 MicroPython 板,但根据您计划放置板的位置,您可能不需要它。这是你的选择。

表 10-2。

Connections for the MicroPython (Pyboard and WiPy)

| MicroPython 板 |   | | --- | --- | | WiPy | Pyboard | 成分 | 电线颜色 | | --- | --- | --- | --- | | P13 | X19 | 传感器 1: VCC |   | | 地线 | 地线 | 传感器 1: GND |   | | P19 | X20 | 传感器 1:Mr |   | | P14 | X21 | 传感器 2: VCC |   | | 地线 | 地线 | 传感器 2: GND |   | | P20 | X22 | 传感器 2:Mr |   |

当然,你必须将土壤湿度传感器插入植物的土壤中。如果您的工厂离您的电源较远,您可能需要使用更长的电线来连接传感器。您应该从一个小型设备和一个传感器(或者为了测试,一个设备中有两个传感器)开始,将它们放在靠近您的 PC(或电源)的地方。

Caution

你需要能够在 3.3-5V 电压下工作的土壤湿度传感器。一些 MicroPython 板可能会将引脚上的输出限制为 3.3v。spark fun 的传感器是兼容的。

让我们看看如何连接 WiPy 和 Pyboard,如图所示。

WiPy

WiPy 的布线最好是将 USB 连接器朝向左侧。图 10-3 显示了 WiPy 的接线图。注意使用小试验板来帮助接地连接。还要注意,传感器 1 在左侧,传感器 2 在右侧。

A447395_1_En_10_Fig3_HTML.jpg

图 10-3。

Wiring the Plant Monitor (WiPy)

Pyboard

Pyboard 的布线最好将 USB 连接器朝向左侧。图 10-4 显示了 Pyboard 的接线图。您可能需要使用试验板来连接网络模块,如图所示。还要注意,传感器 1 在左侧,传感器 2 在右侧。

A447395_1_En_10_Fig4_HTML.jpg

图 10-4。

Wiring the Plant Monitor (Pyboard)

同样,在给主板通电之前,请务必仔细检查您的连接。现在,我们来谈谈我们需要编写的代码。暂时不要启动您的主板——在我们准备好测试该项目之前,还需要进行大量的讨论。

写代码

现在是时候为我们的项目编写代码了。代码比我们到目前为止看到的要长,由于我们正在处理的所有零零碎碎的东西,最好将项目分成几个部分。因此,我们将分两个阶段编写代码。我们直到最后才会有一个工作项目,所以大部分的讨论都是关于各个部分的。在测试这个项目之前,我们将把它们放在一起。

回想一下概述,我们将有两个主要组件:主代码和封装土壤传感器的代码模块。我们将把 HTML 服务器代码和支持函数放在主代码模块中。然而,在我们开始项目的代码之前,我们应该校准我们的传感器。让我们现在做那件事。

Note

这些部分演示了 WiPy 的代码。该项目最适合具有类似功能的 WiPy 和主板:即 WiFi 和 NTP 支持。本章末尾给出了在 Pyboard 和 Pyboard 克隆板上实现项目的差异。正如您将看到的,让项目在这些板上运行需要做更多的工作。

校准传感器

传感器的校准非常重要。对于土壤湿度传感器来说尤其如此,因为有许多不同的版本可供选择。这些传感器对土壤成分、温度,甚至植物所在的花盆类型也非常敏感。因此,我们应该用已知的土壤湿度进行实验,这样我们就知道在我们的代码中使用什么样的范围。

更具体地说,我们希望对来自传感器的观察结果进行分类,以便我们可以确定植物是否需要浇水。我们将使用值“干”、“正常”和“湿”来对从传感器读取的值进行分类。看到这些标签,我们一眼就能轻松确定植物是否需要浇水。在这种情况下,原始数据如 1756 的值可能没有太大的意义,但如果我们看到“干燥”,我们就知道它需要水。

由于传感器是模拟传感器,我们将在电路板上使用模数转换。当我们从 pin 读取数据时,我们将得到一个 0-4096 范围内的值。该值与传感器在土壤中读取的电阻有关。低值表示土壤干燥,高值表示土壤潮湿。

但是,不同供应商的传感器读取的值可能会有很大差异。例如,Sparkfun 的传感器倾向于读取 0-1024 范围内的值,但其他供应商的传感器可以读取高达 4096 的值。幸运的是,它们似乎都是一致的,数值越低,土壤越干燥。

因此,我们必须确定这三种分类的阈值。同样,有几个因素会影响从传感器读取的值。因此,你应该选择几盆土壤,其中一盆你觉得干燥,另一盆浇水适当,第三盆浇水过多。最好的办法是选择一个干燥的,测量,然后浇水,直到土壤湿度合适,测量,然后再次浇水,直到有太多的水。 1

为了确定阈值,我们必须首先编写一小段代码来设置我们的电路板,以便从传感器读取值。这包括选择支持 ADC 的 GPIO 引脚。查看您的主板的参考图,确定包括哪些 GPIO 引脚。例如,在 WiPy 上,有几个 ADC GPIO 部分,包括 P13 至 P18 范围内的部分。

我们还需要选择一个引脚来为电路板供电。这也是一个模拟输出引脚。最后,我们将编写一个循环,每 5 秒钟读取几个值,然后对它们进行平均。五秒是一个任意值,它是通过阅读传感器的数据手册得出的。检查您的传感器,查看读数稳定所需的时间(可能在“读数频率”标题下)。清单 10-1 显示了设置模数通道所需的代码、用于为传感器供电的引脚以及读取 10 个值并求平均值的循环。

# MicroPython for the IOT - Chapter 10
#
# Project 3: MicroPython Plant Monitoring
#
# Threshold calibration for soil moisture sensors
#
# Note: this only runs on the WiPy.
from machine import ADC, Pin
import utime

# Setup the ADC channel for the read (signal)
# Here we choose pin P13 and set attn to 3 to stabilize voltage
adc = ADC(0)
sensor = adc.channel(pin='P13', attn=3)

# Setup the GPIO pin for powering the sensor. We use Pin 19
power = Pin('P19', Pin.OUT)
# Turn sensor off
power.value(0)

# Loop 10 times and average the values read
print("Reading 10 values.")
total = 0
for i in range (0,10):
    # Turn power on
    power.value(1)
    # Wait for sensor to power on and settle
    utime.sleep(5)
    # Read the value
    value = sensor.value()
    print("Value read: {0}".format(value))
    total += value
    # Turn sensor off
    power.value(0)

# Now average the values
print("The average value read is: {0}".format(total/10))

Listing 10-1.Calibrating the Soil Moisture Threshold (WiPy)

如果您在一个名为 threshold.py 的文件中输入这个代码,那么您可以将它复制到您的 WiPy 中并执行它。清单 10-2 显示了在正确灌溉的植物中运行此校准代码的输出。

>>> import threshold
Reading 10 values.
Value read: 1724
Value read: 1983
Value read: 1587
Value read: 1702
Value read: 1634
Value read: 1525
Value read: 1874
Value read: 1707
Value read: 1793
Value read: 1745
The average value read is: 1727.4
>>>
Listing 10-2.Running the Calibration Code (WiPy)

这里我们看到的平均值是 1727(总是取整数——你需要整数)。在干燥的土壤上运行该代码的进一步测试得到的值是 425,而在潮湿的植物上是 3100。因此,这个例子的阈值是 500 代表干,2500 代表湿。但是,您的结果可能会有很大差异,因此请确保使用您选择的传感器、电路板和设备运行此代码。

Tip

为了简化阈值校准,请使用同一供应商的传感器。否则,您可能需要为每个受支持的传感器使用不同的阈值集。

请注意读取的值。如您所见,这些值随时都可能发生变化。这对于这些传感器来说是正常的。众所周知,它们会产生一些跳动的值。因此,您应该考虑对传感器进行多次采样,以获得短时间内的平均值,而不是单个值。如果一个或多个样本相差很大,即使取平均值也会有轻微的偏差。然而,即使取样 10 个值并取平均值也将有助于降低获得异常读数的可能性。我们将在项目代码中这样做。

现在我们已经有了传感器的阈值,我们可以从传感器的代码模块开始。

第一部分:传感器代码模块

该项目的第一部分将是创建一个代码模块,包含一个名为PlantMonitor的新类,该类包含从传感器读取数据并将数据保存到文件中的所有功能。在这一节中,我们将看到如何编写模块的代码。如果您想继续编写代码,您可以打开一个新文件并将其命名为plant_monitor.py。先从高层设计来看。

高层设计

正如我们前面所学的,为我们想要使用的每个代码模块(类)创建一个设计是一个好主意。我们将使用主代码中的代码模块。因此,我们需要一些函数来告诉这个类读取传感器,以及一种方法来获取这个类用于数据的文件名。

通常,人们会设计一个代码模块来完全隐藏一个文件和其上的所有操作,但在这种情况下,该类只关心读取传感器和写入数据。另外,由于主代码需要读取数据并用 HTML 标记对其进行格式化,因此将 read 函数放在主代码中更合适。也就是说,您应该努力将相似的代码功能放在一起,这有助于维护代码。例如,将所有的 HTML 代码保存在一个文件中使得修改 HTML 代码(或者重用它)变得更加容易。

我们将使用定时器中断来读取传感器。这允许我们设置一个函数来定期调用,而不需要监控或轮询时间,直接调用函数。为了方便起见,我们提供了清除日志功能。因此,我们只需要两个公共函数:一个清除日志,另一个获取文件名。除了初始化类之外,我们只需要在需要刷新数据时获取文件名(将数据发送给客户机)。表 10-3 显示了工厂监控类的功能。

表 10-3。

High-Level Design (Functions) Plant Monitor Class

| 功能 | 因素 | 描述 | | --- | --- | --- | | `__init__()` | 雷达跟踪中心(Radar Tracking Centre 的缩写) | 类的初始化(构造函数) | | `clear_log()` |   | 清除日志 | | `get_filename()` |   | 检查 SD 卡后,获取我们正在使用的文件名 | | `_get_value()` | 传感器、电源 | 读取传感器 10 次,并计算读数的平均值 | | `_read_sensors()` | 线条 | 监控传感器,读取数值,并保存它们 | | `_convert_value()` | 价值 | 将原始传感器值转换为枚举 |

注意第一个名为__init__()的函数。这是该类的构造函数,将在从我们的主代码实例化该类时被调用。还要注意私有方法是用一个下划线命名的。

以下部分解释了初始化代码和所需的函数。我们将在后面的部分看到完整的代码。

设置和初始化

在本节中,我们将讨论设置和初始化代码模块所需的代码。首先,我们需要一些导入,包括模数转换器、pin、安全磁盘(SD)、定时器和操作系统库。

我们还需要为这个类定义一些常量。回想一下,我们想用枚举法对土壤湿度读数进行分类。为此,我们需要使用我们为分类确定的阈值。我们可以在文件的顶部使用常量,以便在以后需要调整代码以用于其他传感器或我们的工厂条件发生变化(不同的花盆、土壤、环境等)时更容易更改它们。).我们可以使用相同的原理来设置包含数据的文件名。

我们还使用一个常数来定义读取传感器的频率。由于我们将使用一个循环来读取传感器,每次读取需要等待 5 秒,因此我们至少需要 50-55 秒来读取 10 个值。因此,我们不能将更新频率设置为少于一分钟。频率以秒为单位。虽然您可能希望将此设置为较低的测试值,但您肯定不希望每分钟都检查植物的土壤湿度。也就是说,你多久检查一次你的植物?几天一次还是一天一次?为什么比平时检查得早?

Sampling Frequency

在设计传感器网络时,经常会忽略从传感器采样数据的频率(也称为采样率)。趋势是存储尽可能多的值;认为数据越多越好。但这在一般情况下是不适用的。考虑工厂监控项目。如果您通常每天检查一次工厂,那么每 5 分钟对传感器进行一次采样对您有什么好处呢?不会,而且只会产生多余的数据!

采样率必须仔细计算,以提供您需要的数据,从而在不创建太多数据的情况下得出结论。虽然更多的数据总是比太少的数据好,但以不切实际的频率过于频繁地保存数据会产生太多的数据,可能会超出设备的存储容量。

设计传感器采样项目时,应仔细考虑采样速率。选择基于现实预期的采样率。通常,如果您正在对变化非常缓慢的数据进行采样,则采样率应该较低。变化更快的采样数据应该具有更高(采样间隔时间更短)的采样率。

最后,我们需要一个将时间结构转换成字符串的函数。回想一下前面的例子,我们可以使用一个简单的格式规范。我们将为该特性使用一个模块级的私有函数。

清单 10-3 显示了设置和初始化部分的代码。把这个放在文件的顶部。

from machine import ADC, Pin, SD, Timer
import os
import utime

# Thresholds for the sensors
LOWER_THRESHOLD = 500
UPPER_THRESHOLD = 2500
UPDATE_FREQ = 120   # seconds

# File name for the data
SENSOR_DATA = "plant_data.csv"

# Format the time (epoch) for a better view
def _format_time(tm_data):
    # Use a special shortcut to unpack tuple: *tm_data
    return "{0}-{1:0>2}-{2:0>2} {3:0>2}:{4:0>2}:{5:0>2}".format(*tm_data)

Listing 10-3.Plant Monitor Class Setup and Initialization (WiPy)

构造器

类的构造函数是所有主要工作发生的地方。我们需要做几件事,包括以下几件。

  • 规范化数据文件的位置(路径)
  • 在存储在列表中的字典中设置传感器
  • 设置定时器中断以定期读取传感器

我们通过尝试使用 SD 卡来规范化数据文件的路径。如果找不到 SD 卡,我们默认使用闪存盘。但是,您应该避免将数据写入闪存驱动器,因为驱动程序较小,可能会被填满,并且写入闪存驱动器会增加损坏驱动器或在执行过程中导致问题的风险。

我们为每个传感器使用一个字典,这样我们就可以定义传感器的 pin、为传感器供电的 pin、传感器号(任意标识)以及传感器的位置。然后,我们将字典放在一个列表中,以便使用循环同时读取所有传感器。

最后,我们通过 timer alarm 类设置一个中断来定期读取传感器。清单 10-4 显示了类构造函数的代码。

def __init__(self, rtc):
    self.rtc = rtc

    # Try to access the SD card and make the new path
    try:
        sd = SD()
        os.mount(sd, '/sd')
        self.sensor_file = "/sd/{0}".format(SENSOR_DATA)
        print("INFO: Using SD card for data.")
    except:
        print("ERROR: cannot mount SD card, reverting to flash!")
        self.sensor_file = SENSOR_DATA
    print("Data filename = {0}".format(self.sensor_file))

    # Setup the dictionary for each soil moisture sensor
    adc = ADC(0)
    soil_moisture1 = {
        'sensor': adc.channel(pin='P13', attn=3),
        'power': Pin('P19', Pin.OUT),
        'location': 'Green ceramic pot on top shelf',
        'num': 1,
    }
    soil_moisture2 = {
        'sensor': adc.channel(pin='P14', attn=3),
        'power': Pin('P20', Pin.OUT),
        'location': 'Fern on bottom shelf',
        'num': 2,
    }
    # Setup a list for each sensor dictionary
    self.sensors = [soil_moisture1, soil_moisture2]
    # Setup the alarm to read the sensors
    a = Timer.Alarm(handler=self._read_sensors, s=UPDATE_FREQ,
                    periodic=True)
    print("Plant Monitor class is ready...")

Listing 10-4.Plant Monitor Class Constructor (WiPy)

请注意计时器警报的代码。在这里,我们定义了中断的处理程序(回调),使用我们在图块顶部定义的常量来定义频率,并将其设置为每 N 秒触发一次(定期)。

公共职能

只有两个公共函数。第一个,clear_log(),简单地打开文件进行写入,然后关闭它。这实际上清空了文件。提供该功能是为了方便。第二个函数get_filename()只是返回用于存储数据的文件的名称。这个名字与常量SENSOR_DATA中的名字不同,因为我们在构造函数中规范化了路径,如前一节所示。

私人功能

有三个私有函数。_get_value()函数与我们的阈值校准代码相同,我们对传感器采样 10 次并取平均值。_read_sensors()函数是定时器报警中断的回调函数,它读取我们定义的所有传感器并将数据保存到文件中。_convert_value()功能是基于传感器数据确定土壤分类的辅助功能。该函数返回一个字符串或“干”、“好”或“湿”。

完全码

现在我们已经看到了代码模块的所有部分,让我们来看看完整的代码。清单 10-5 显示了工厂监控代码模块的完整代码。同样,我们可以将这个文件保存为plant_monitor.py

# MicroPython for the IOT - Chapter 10
#
# Project 3: MicroPython Plant Monitoring
#
# Plant monitor class
#
# Note: this only runs on the WiPy.
from machine import ADC, Pin, SD, Timer
import os
import utime

# Thresholds for the sensors
LOWER_THRESHOLD = 500
UPPER_THRESHOLD = 2500
UPDATE_FREQ = 120   # seconds

# File name for the data
SENSOR_DATA = "plant_data.csv"

# Format the time (epoch) for a better view
def _format_time(tm_data):
    # Use a special shortcut to unpack tuple: *tm_data
    return "{0}-{1:0>2}-{2:0>2} {3:0>2}:{4:0>2}:{5:0>2}".format(*tm_data)

class PlantMonitor:
    """
    This class reads soil moisture from one or more sensors and writes the
    data to a comma-separated value (csv) file as specified in the constructor.
    """

    # Initialization for the class (the constructor)
    def __init__(self, rtc):
        self.rtc = rtc

        # Try to access the SD card and make the new path
        try:
            sd = SD()
            os.mount(sd, '/sd')
            self.sensor_file = "/sd/{0}".format(SENSOR_DATA)
            print("INFO: Using SD card for data.")
        except:
            print("ERROR: cannot mount SD card, reverting to flash!")
            self.sensor_file = SENSOR_DATA
        print("Data filename = {0}".format(self.sensor_file))

        # Setup the dictionary for each soil moisture sensor
        adc = ADC(0)
        soil_moisture1 = {
            'sensor': adc.channel(pin='P13', attn=3),
            'power': Pin('P19', Pin.OUT),
            'location': 'Green ceramic pot on top shelf',
            'num': 1,
        }
        soil_moisture2 = {
            'sensor': adc.channel(pin='P14', attn=3),
            'power': Pin('P20', Pin.OUT),
            'location': 'Fern on bottom shelf',
            'num': 2,
        }
        # Setup a list for each sensor dictionary
        self.sensors = [soil_moisture1, soil_moisture2]
        # Setup the alarm to read the sensors
        a = Timer.Alarm(handler=self._read_sensors, s=UPDATE_FREQ,
                        periodic=True)
        print("Plant Monitor class is ready...")

    # Clear the log
    def clear_log(self):
        log_file = open(self.sensor_file, 'w')
        log_file.close()

    # Get the filename we're using after the check for SD card
    def get_filename(self):
        return self.sensor_file

    # Read the sensor 10 times and average the values read
    def _get_value(self, sensor, power):
        total = 0
        # Turn power on
        power.value(1)
        for i in range (0,10):
            # Wait for sensor to power on and settle
            utime.sleep(5)
            # Read the value
            value = sensor.value()
            total += value
        # Turn sensor off
        power.value(0)
        return int(total/10)

    # Monitor the sensors, read the values and save them
    def _read_sensors(self, line):
        log_file = open(self.sensor_file, 'a')
        for sensor in self.sensors:
            # Read the data from the sensor and convert the value
            value = self._get_value(sensor['sensor'], sensor['power'])
            print("Value read: {0}".format(value))
            time_data = self.rtc.now()
            # datetime,num,value,enum,location
            log_file.write(
                "{0},{1},{2},{3},{4}\n".format(_format_time(time_data),
                                               sensor['num'], value,
                                               self._convert_value(value),
                                               sensor['location']))
        log_file.close()

    # Convert the raw sensor value to an enumeration
    def _convert_value(self, value):
        # If value is less than lower threshold, soil is dry else if it
        # is greater than upper threshold, it is wet, else all is well.
        if (value <= LOWER_THRESHOLD):
            return "dry"
        elif (value >= UPPER_THRESHOLD):
            return "wet"
        return "ok"

Listing 10-5.Plant Monitor Code Module Complete Code (WiPy)

哇,代码真多!花些时间通读它,直到你理解了代码的所有部分。

白板的更改

要在 Pyboard 上运行这个项目,还需要进行相当多的更改。其主要原因包括我们需要对导入进行的常规更改、引脚类别高/低与值的对比,以及我们使用实时时钟的方式的差异。最后,Pyboard 不支持定时器警报类中断,因此我们必须使用轮询技术来读取传感器。这最后一个变化意味着我们必须使read_sensors()函数成为一个公共函数,这样我们就可以从主代码中调用它。

由于变化很多,差异文件几乎与实际代码一样长。因此,我们将看到 Pyboard 的完整代码。清单 10-6 显示了代码模块的完整代码,不同之处在于代码模块需要适应 Pyboard(粗体)。虽然大多数变化很小,但如果您使用 Pyboard 或 Pyboard 克隆,请注意传感器使用的引脚。

# MicroPython for the IOT - Chapter 10
#
# Project 3: MicroPython Plant Monitoring
#
# Plant monitor class
#

# Note: this only runs on the Pyboard.

from pyb import ADC, delay, Pin, SD

import os

import pyb

# Thresholds for the sensors
LOWER_THRESHOLD = 500
UPPER_THRESHOLD = 2500
UPDATE_FREQ = 120   # seconds

# File name for the data
SENSOR_DATA = "plant_data.csv"

# Format the time (epoch) for a better view
def _format_time(tm_data):
    # Use a special shortcut to unpack tuple: *tm_data
    return "{0}-{1:0>2}-{2:0>2} {3:0>2}:{4:0>2}:{5:0>2}".format(*tm_data)

class PlantMonitor:
    """
    This class reads soil moisture from one or more sensors and writes the
    data to a comma-separated value (csv) file as specified in the constructor.
    """

    # Initialization for the class (the constructor)
    def __init__(self, rtc):
        self.rtc = rtc

        # Try to access the SD card and make the new path
        try:
            self.sensor_file = "/sd/{0}".format(filename)

            f = open(self.sensor_file, 'r')

            f.close()

            print("INFO: Using SD card for data.")
        except:
            print("ERROR: cannot mount SD card, reverting to flash!")
            self.sensor_file = SENSOR_DATA
        print("Data filename = {0}".format(self.sensor_file))

        # Setup the dictionary for each soil moisture sensor
        soil_moisture1 = {
            'sensor': ADC(Pin('X19')),

            'power': Pin('X20', Pin.OUT_PP),

            'location': 'Green ceramic pot on top shelf',
            'num': 1,
        }
        soil_moisture2 = {
            'sensor': ADC(Pin('X20')),

            'power': Pin('X21', Pin.OUT_PP),

            'location': 'Fern on bottom shelf',
            'num': 2,
        }
        # Setup a list for each sensor dictionary
        self.sensors = [soil_moisture1, soil_moisture2]
        # Setup the alarm to read the sensors

        self.alarm = pyb.millis()

        print("Plant Monitor class is ready...")

    # Clear the log
    def clear_log(self):
        log_file = open(self.sensor_file, 'w')
        log_file.close()

    # Get the filename we're using after the check for SD card
    def get_filename(self):
        return self.sensor_file

    # Read the sensor 10 times and average the values read
    def _get_value(self, sensor, power):
        total = 0
        # Turn power on
        power.high()

        for i in range (0,10):
            # Wait for sensor to power on and settle
            delay(5000)

            # Read the value
            value = sensor.read()

            total += value
        # Turn sensor off
        power.low()

        return int(total/10)

    # Monitor the sensors, read the values and save them
    def read_sensors(self):

        if pyb.elapsed_millis(self.alarm) < (UPDATE_FREQ * 1000):

            return

        self.alarm = pyb.millis()

        log_file = open(self.sensor_file, 'a')
        for sensor in self.sensors:
            # Read the data from the sensor and convert the value
            value = self._get_value(sensor['sensor'], sensor['power'])
            print("Value read: {0}".format(value))
            time_data = self.rtc.datetime()

            # datetime,num,value,enum,location
            log_file.write(
                "{0},{1},{2},{3},{4}\n".format(_format_time(time_data),
                                               sensor['num'], value,
                                               self._convert_value(value),
                                               sensor['location']))
        log_file.close()

    # Convert the raw sensor value to an enumeration
    def _convert_value(self, value):
        # If value is less than lower threshold, soil is dry else if it
        # is greater than upper threshold, it is wet, else all is well.
        if (value <= LOWER_THRESHOLD):
            return "dry"
        elif (value >= UPPER_THRESHOLD):
            return "wet"
        return "ok"

Listing 10-6.Plant Monitor Code Module Complete Code (Pyboard)

好了,现在我们准备看看主要代码。

第二部分:主代码

主要代码就像上一个项目的代码。但是,这一次我们将使用一个文件来存储 HTML 代码(因为它不会改变)和一个单独的 HTML 字符串,以便用文件中的数据填充 HTML 表。我们还将添加代码来从网络时间协议(NTP)服务器读取日期和时间。

与上一个项目不同,HTML 代码不包括按钮,但我们可以在 URL 上手动设置命令的格式。我们可以使用这种技术来允许在不使用按钮或其他用户界面功能的情况下访问命令。这也有助于使这些命令更难使用,以防止过度使用。例如,我们可以提供一个清除日志命令。我们将使用类似于http://192.168.42.140/CLEAR_LOG的 URL,它向 HTML 服务器提交一个GET请求。我们可以捕获该命令,并在发出该命令时清除日志。

Caution

如果您像这样构建命令,请务必小心使用它们。也就是说,将您的 URL 设置为http://192.168.42.140/CLEAR_LOG并按下回车键发出命令。刷新页面将重新发出命令!当您使用该命令时,请确保在刷新之前清除您的 URL,或者最好使用一次并关闭页面/选项卡。

以下部分解释了初始化代码和所需的函数。我们将在后面的部分看到完整的代码。让我们从 HTML 代码开始。

HTML 代码(文件)

我们将把所需的 HTML 代码存储在文件中以节省内存。回想一下一次读取一行——我们不必在代码中用字符串来占用空间。随着您的项目变得越来越复杂,这可能会成为一个问题。因此,这个项目演示了一种节省内存的方法。

这个项目的 HTML 创建了一个带有简单表格的网页,该表格包含了请求时文件中的所有数据。为了方便起见,我们将使用三个文件。第一个文件(名为part1.html)将包含直到表格行的 HTML 代码;第二个文件(名为plant_data.csv),由 PlantMonitor 类填充;第三个(名为part2.html)将包含剩余的 HTML 代码。

第一个文件part1.html,如清单 10-7 所示。这个文件建立了表格 HTML 代码。它还建立了表格的特征,包括文本对齐、边框大小和填充——全部通过级联样式(<style>标记。不要担心这看起来奇怪或陌生。你可以谷歌一下 W3C 标准,看看我们如何使用标签来控制网页的样式。

<!DOCTYPE html>
<html>
  <head>
    <title>MicroPython for the IOT - Project 3</title>
    <meta http-equiv="refresh" content="30">
    <style>
      table, th, td {
          border: 1px solid black;
          border-collapse: collapse;
      }
      th, td {
          padding: 5px;
      }
      th {
          text-align: left;
      }
    </style>
  </head>
  <center><h2>MicroPython for the IOT - Project 3</h2></center><br>
  <center>A simple project to demonstrate how to retrieve sensor data over the Internet.</center>
  <center><br><b>Plant Monitoring Data</b><br><br>
    <table style="width:75%">
      <col width="180">
      <col width="120">
      <col width="100">
      <col width="100">
      <tr><th>Datetime</th><th>Sensor Number</th><th>Raw Value</th><th>Moisture</th><th>Location</th></tr>
Listing 10-7.HTML Code (part1.html)

请注意表格代码。同样,如果这看起来很奇怪,也不要担心。它是可行的,而且是非常基本的。熟悉 HTML 的人可能想修饰和改进代码。最后一行建立了表格的标题。

第二个文件 plant_data.csv 包含数据。我们将使用一个常量来填充一个格式正确的 HTML 表格行。下面的示例展示了文件中的一行数据是什么样子,以及这些数据是如何转换成 HTML 的。我们将在下一节看到表格行的 HTML。

# Raw data
2017-08-08 20:26:17,1,78,dry,Small fern on bottom shelf
# HTML table row
<tr><td>2017-08-08 20:26:17</td><td>1</td><td>78</td><td>dry</td><td>Small fern on bottom shelf </td></tr>

最后一个文件part2.html包含结束标记,所以它不是很大。但是因为我们是从文件中读取,所以我们包含了这个文件。下面显示了第二个文件中的代码。

    </table>
  </center>
</html>

那么,我们如何使用这些文件呢?当我们将响应发送回客户端(网页)时,我们一次读取发送一行的第一个文件,然后一次读取发送一行的数据文件,然后一次读取发送一行的最后一个文件。我们将使用一个助手函数来读取数据文件。清单 10-8 显示了用来做这件事的代码。

# Read HTML from file and send to client a row at a time.
def send_html_file(filename, client):
    html = open(filename, 'r')
    for row in html:
        client.send(row)
    html.close()

# Send the sensor data to the client.
def send_sensor_data(client, filename):
    send_html_file("part1.html", client)
    log_file = open(filename, 'r')
    for row in log_file:
        cols = row.strip("\n").split(",") # split row by commas
        # build the table string if all parts are there
        if len(cols) >= 5:
            html = HTML_TABLE_ROW.format(cols[0], cols[1], cols[2],
                                         cols[3], cols[4])
            # send the row to the client
            client.send(html)
    log_file.close()
    send_html_file("part2.html", client)

Listing 10-8.Reading the HTML and Data File (WiPy)

进口

项目所需的导入包括实时时钟、sys、usocket、utime、machine 和 WLAN。这些现在已经很熟悉了。最后一行从plant_monitor代码模块导入PlantMonitor类。完整的导入列表如下所示。如果你想继续,打开一个新文件,命名为plant_wipy.py

# Imports for the project
from machine import RTC
import sys
import usocket as socket
import utime
import machine
from network import WLAN
from plant_monitor import PlantMonitor

我们还需要一个字符串来创建表的行。出现在这一行之前的 HTML 代码如上所述保存在文件中。下面显示了使用的字符串。注意,我们使用替换语法,这样我们就可以使用format()函数来填充细节。

# HTML web page for the project
HTML_TABLE_ROW = "<tr><td>{0}</td><td>{1}</td><td>{2}</td><td>{3}</td><td>{4}</td></tr>"

进口

我们还想将项目连接到我们的网络。我们使用了与之前的项目和示例中相同的代码,但是将它变成了一个名为connect()的函数,我们将从主run()函数中调用它。确保更改 SSID 和密码以匹配您的网络。

网络时间协议

由于我们保存的数据具有时间元素(您想知道何时对土壤湿度进行采样),因此我们需要存储数据的日期和时间。最简单的方法是使用网络时间协议(NPT)服务器:也就是说,假设电路板连接到互联网。如果它没有连接到互联网,我们必须使用 RTC 模块或在启动时初始化板载 RTC。我们在第五章中看到了如何使用 NTP 服务器。我们在这个项目中将其作为名为get_ntp()的函数重复,我们将从主run()函数中调用它。

run()函数

run()函数的 HTML 服务器部分类似于上一个项目,但是我们没有处理表单请求,而是默认将网页发送回客户端。唯一支持的命令是CLEAR_LOG命令,它需要在客户端的 URL 上指定它,如上所述。

另一个区别是,我们没有将代码放在代码文件的全局部分(这样当我们在 REPL 控制台或main.py文件中导入文件时,它就会执行),而是使用函数连接到网络,设置 NTP,并将 HTML 代码发送到客户端。这是一种复杂性的升级,您应该开始将其作为常规做法使用。我们在早期的项目中没有看到这一点,因此我们可以专注于完成代码。在编写自己的项目时,一定要使用函数来包含代码,并从其他代码中调用这些函数。

由于这与上一个项目不同,我们将查看代码。清单 10-9 显示了run()函数的代码。

# Setup the socket and respond to HTML requests
def run():
    # Connect to the network
    if not connect():
        sys.exit(-1)

    # Setup the real time clock
    rtc = get_ntp()

    # Setup the plant monitoring object instance from the plant_monitoring class
    plants = PlantMonitor(rtc)
    filename = plants.get_filename()

    # Setup the HTML server
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.bind(('', 80))
    sock.listen(5)
    print("Ready for connections...")
    server_on = True
    while server_on:
        client, address = sock.accept()
        print("Got a connection from a client at: %s" % str(address))
        request = client.recv(1024)

        # Allow for clearing of the log, but be careful! The auto refresh
        # will resend this command if you do not clear it from your URL
        # line.

        if (request[:14] == b'GET /CLEAR_LOG'):
            print('Requesting clear log.')
            plants.clear_log()
        send_sensor_data(client, filename)
        client.close()
    sock.close()

Listing 10-9.Plant Monitor Run() Function (WiPy)

花点时间通读这段代码。请注意我们是如何以更加模块化的方式实现该功能的。将公共代码放在函数中不仅有助于将问题分解成几个部分,还可以缩短主代码(run()函数)。

让我们看看完整的代码。

完全码

现在我们已经看到了代码模块的所有部分,让我们来看看完整的代码。清单 10-10 显示了工厂监控代码模块的完整代码。同样,我们可以将这个文件保存为 Pyboard 的plant_wipy.pyplant_pyboard.py

# MicroPython for the IOT - Chapter 10
#
# Project 3: MicroPython Plant Monitoring
#
# Required Components:
# - WiPy
# - (N) Soil moisture sensors (one for each plant)
#
# Note: this only runs on the WiPy.
#
# Imports for the project
from machine import RTC
import sys
import usocket as socket
import utime
import machine
from network import WLAN
from plant_monitor import PlantMonitor

# HTML web page for the project
HTML_TABLE_ROW = "<tr><td>{0}</td><td>{1}</td><td>{2}</td><td>{3}</td><td>{4}</td></tr>"

# Setup the board to connect to our network.
def connect():
    wlan = WLAN(mode=WLAN.STA)
    nets = wlan.scan()
    for net in nets:
        if net.ssid == 'YOUR_SSID':
            print('Network found!')
            wlan.connect(net.ssid, auth=(net.sec, 'YOUR_PASSWORD'), timeout=5000)
            while not wlan.isconnected():
                machine.idle() # save power while waiting
            print('WLAN connection succeeded!')
            print("My IP address is: {0}".format(wlan.ifconfig()[0]))
            return True
    return False

# Setup the real time clock with the NTP service
def get_ntp():
    rtc = RTC()
    print("Time before sync:", rtc.now())
    rtc.ntp_sync("pool.ntp.org")
    while not rtc.synced():
        utime.sleep(3)
        print("Waiting for NTP server...")
    print("Time after sync:", rtc.now())
    return rtc

# Read HTML from file and send to client a row at a time.
def send_html_file(filename, client):
    html = open(filename, 'r')
    for row in html:
        client.send(row)
    html.close()

# Send the sensor data to the client.
def send_sensor_data(client, filename):
    send_html_file("part1.html", client)
    log_file = open(filename, 'r')
    for row in log_file:
        cols = row.strip("\n").split(",") # split row by commas
        # build the table string if all parts are there
        if len(cols) >= 5:
            html = HTML_TABLE_ROW.format(cols[0], cols[1], cols[2],
                                         cols[3], cols[4])
            # send the row to the client
            client.send(html)
    log_file.close()
    send_html_file("part2.html", client)

# Setup the socket and respond to HTML requests
def run():
    # Connect to the network
    if not connect():
        sys.exit(-1)

    # Setup the real time clock
    rtc = get_ntp()

    # Setup the plant monitoring object instance from the plant_monitoring class
    plants = PlantMonitor(rtc)
    filename = plants.get_filename()

    # Setup the HTML server
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.bind(('', 80))
    sock.listen(5)
    print("Ready for connections...")
    server_on = True
    while server_on:
        client, address = sock.accept()
        print("Got a connection from a client at: %s" % str(address))
        request = client.recv(1024)

        # Allow for clearing of the log, but be careful! The auto refresh
        # will resend this command if you do not clear it from your URL
        # line.

        if (request[:14] == b'GET /CLEAR_LOG'):
            print('Requesting clear log.')
            plants.clear_log()
        send_sensor_data(client, filename)
        client.close()
    sock.close()

Listing 10-10.Plant Monitor Main Code (WiPy)

白板的更改

像前面的代码模块一样,在 Pyboard 上运行代码需要做大量的修改。其主要原因包括通常的更改,但代码也有很大不同,因为我们必须使用轮询方法,因为没有像在 WiPy 上那样设置定时器中断的(简单)方法。

由于变化很多,差异文件几乎与实际代码一样长。因此,我们将看到 Pyboard 的完整代码。清单 10-11 显示了代码模块的完整代码,其中包含使代码模块适应 Pyboard 所需的差异(粗体)。虽然大多数变化很小,但如果您使用 Pyboard 或 Pyboard 克隆,请注意传感器使用的引脚。

run()功能中需要两个主要的改变。首先,Pyboard 固件中不支持 NTP,因此我们必须在每次启动项目时使用 RTC 模块或初始化板载 RTC。第二,由于没有办法设置允许访问文件的定时器中断,我们必须改变 HTML 服务器以使用非阻塞套接字技术。这些变化以粗体突出显示。

# MicroPython for the IOT - Chapter 10
#
# Project 3: MicroPython Plant Monitoring
#
# Required Components:
# - Pyboard
# - (N) Soil moisture sensors (one for each plant)
#
# Note: this only runs on the Pyboard.
#
# Imports for the project
from pyb import delay, SPI
from pyb import I2C
import network
import urtc
import usocket as socket
from plant_monitor import PlantMonitor

# Setup the I2C interface for the rtc
i2c = I2C(1, I2C.MASTER)
i2c.init(I2C.MASTER, baudrate=500000)

# HTML web page for the project
HTML_TABLE_ROW = "<tr><td>{0}</td><td>{1}</td><td>{2}</td><td>{3}</td><td>{4}</td></tr>"

# Setup the board to connect to our network.
def connect():
    nic = network.CC3K(SPI(2), Pin.board.Y5, Pin.board.Y4, Pin.board.Y3)
    # Replace the following with yout SSID and password
    print("Connecting...")
    nic.connect("YOUR_SSID", "YOUR_PASSWORD")
    while not nic.isconnected():
        delay(50)
    print("Connected!")
    print("My IP address is: {0}".format(nic.ifconfig()[0]))
    return True

# Read HTML from file and send to client a row at a time.
def send_html_file(filename, client):
    html = open(filename, 'r')
    for row in html:
        client.send(row)
    html.close()

# Send the sensor data to the client.
def send_sensor_data(client, filename):
    send_html_file("part1.html", client)
    log_file = open(filename, 'r')
    for row in log_file:
        cols = row.strip("\n").split(",") # split row by commas
        # build the table string if all parts are there
        if len(cols) >= 5:
            html = HTML_TABLE_ROW.format(cols[0], cols[1], cols[2],
                                         cols[3], cols[4])
            # send the row to the client
            client.send(html)
            delay(50)
    log_file.close()
    send_html_file("part2.html", client)

# Setup the socket and respond to HTML requests
def run():
    # Connect to the network
    if not connect():
        sys.exit(-1)

    # Setup the real time clock

    rtc = urtc.DS1307(i2c)

    #
    # NOTE: We only need to set the datetime once. Uncomment these
    #       lines only on the first run of a new RTC module or
    #       whenever you change the battery.
    #       (year, month, day, weekday, hour, minute, second, millisecond)
    #start_datetime = (2017,07,20,4,9,0,0,0)
    #rtc.datetime(start_datetime)

    # Setup the plant monitoring object instance from the plant_monitoring class
    plants = PlantMonitor(rtc)
    filename = plants.get_filename()

    # Setup the HTML server
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.bind(('', 80))
    sock.setblocking(False)   # We must use polling for Pyboard.

    sock.listen(5)
    print("Ready for connections...")
    server_on = True
    while server_on:
        try:

            client, address = sock.accept()

        except OSError as err:

            # Do check for reading sensors here

            plants.read_sensors()

            delay(50)

            continue

        print("Got a connection from a client at: %s" % str(address))
        request = client.recv(1024)

        # Allow for clearing of the log, but be careful! The auto refresh
        # will resend this command if you do not clear it from your URL
        # line.

        if (request[:14] == b'GET /CLEAR_LOG'):
            print('Requesting clear log.')
            plants.clear_log()
        send_sensor_data(client, filename)
        client.close()
    sock.close()

Listing 10-11.Plant Monitor Main Code (Pyboard)

注意run()函数中的 try 块。当没有客户端连接时,sock.accept()函数将抛出异常。这与阻塞调用的工作方式不同。阻塞调用将简单地等待,直到客户端连接。这里,我们必须尝试接受一个连接,如果失败,继续等待连接。也就是说,我们不能发送 HTML 到客户端,因为没有客户端!

这些变化突出了在没有网络支持的情况下使用 Pyboard 和其他 MicroPython 板的额外困难,那些需要 RTC 模块的,以及那些对定时器报警中断支持有限以允许准异步执行的(它不是真正的异步)。换句话说,我们可以断开一些代码,由中断来调用(触发)。

现在,让我们运行这个项目!

Wait, What about the Data File?

如果您对数据文件有疑问,您不必担心。该代码旨在创建文件,即使它不存在。下面显示了您可以在测试中使用的数据模型。

2017-08-08 20:26:17,1,78,dry,Small fern on bottom shelf on porch
2017-08-08 20:26:32,2,136,dry,Green pot creeper thing on floor in living room
2017-08-08 20:26:47,1,128,dry,Small fern on bottom shelf on porch
2017-08-08 20:27:02,2,112,dry,Green pot creeper thing on floor in living room

如果您想从一些示例数据开始,您可以这样做,但是要确保用逗号分隔,没有空格,每行一行数据。

执行!

现在是有趣的部分!我们已经设置好代码,可以从我们的工厂读取土壤湿度,并将收集到的所有传感器数据发送给客户。回想一下,我们需要将代码复制到我们的板上。我们可以 ftp 两个代码文件(plant_monitor.pyplant_wipy.py)以及两个 HTML 文件(part1.htmlpart2.html)。现在这样做,你就可以测试这个项目了。

我们现在所需要的就是那块板的 IP 地址来指向我们的网络浏览器。我们可以通过运行代码从我们的调试语句中得到答案。清单 10-12 显示了项目在 WiPy 上的初始运行(Pyboard 的结果类似)。

MicroPython v1.8.6-694-g25826866 on 2017-06-29; WiPy with ESP32
Type "help()" for more information.
>>> import plant_wipy as p
>>> p.run()
Network found!
WLAN connection succeeded!

My IP address is: 192.168.42.128

Time before sync: (1970, 1, 1, 0, 1, 44, 382593, None)
Waiting for NTP server...
Time after sync: (2017, 8, 9, 14, 26, 1, 92051, None)
INFO: Using SD card for data.
Data filename = /sd/plant_data.csv
Plant Monitor class is ready...
Ready for connections...
Got a connection from a client at: ('192.168.42.110', 50395)
Listing 10-12.Running the Plant Monitor (WiPy)

注意,在这种情况下,IP 地址是 192.168.42.128。我们需要做的就是把它放入我们的浏览器,如图 10-5 所示。

A447395_1_En_10_Fig5_HTML.jpg

图 10-5。

Plant Monitor Project

输入 URL 后,您应该会看到如图所示的网页。如果没有,一定要检查代码中的 HTML,确保它和显示的完全一样;否则,页面可能无法正常显示。您还应该确保您的 PC 所连接的网络可以连接到您的主板所连接的网络。如果您的家庭办公室像我一样设置,可能有几个 WiFi 网络可供您使用。最好你的主板和你的电脑在同一个网络(和同一个子网)。

至此,您已经完成了另一个真正的 MicroPython IOT 项目。在这种情况下,我们看到了一个收集和显示数据的 IOT 项目。酷!

更进一步

和上一个项目一样,这个项目展示了在其他项目中重用这些技术的良好前景。对于 HTML 服务器方面来说尤其如此。如果你喜欢在互联网上看到你的传感器数据,你应该考虑花时间探索一些修饰。这里有一些你可以考虑的。有些很容易,有些可能是一个挑战或需要更多的研究。

  • 添加更多传感器,将您的项目扩展到更多工厂。
  • 添加一个温度传感器来记录环境温度并显示在网页上。
  • 重写 HTML 代码以生成 JSON 字符串。
  • 重写 HTML 代码以生成 XML。
  • 探索 HTML 代码,根据您的喜好更改网页。考虑使用级联样式表来更改按钮被按下时的背景。
  • 将您的板连接到互联网,并呼叫朋友连接到您的板并进行测试。
  • 在你的板上安装发光二极管,当植物需要浇水时,发光二极管就会发光。

当然,如果你想继续下一个项目,欢迎你这样做,但是花些时间探索这些潜在的修饰——这将是一个很好的实践。

摘要

IOT 解决方案可以采取多种形式。一种更常见的形式是生成我们可以通过互联网查看的数据的形式(有时称为数据收集器)。数据收集器的实现可以有很大不同,但它们通常将数据存储在某个位置,并提供查看数据的方法。最简单的形式是在本地、远程服务器、数据库或云服务中记录数据(有时称为数据记录器)。数据的可视化也可以随着最基本的通过网页提供数据而变化。

在本章中,我们看到了一个 MicroPython IOT 项目,它记录从一系列土壤湿度传感器读取的数据。我们创建了一个工厂监控解决方案,将数据保存到本地 SD 卡中。该项目还通过 HTML 服务器提供数据,这样我们就可以随时看到数据。这个项目可以作为许多数据收集项目的模板。您可以简单地遵循本章中建立的模式,构建自己的基于 HTML 的数据记录器。

在下一章中,我们将通过让我们的 MicroPython 板将数据发送到基于云的 4 存储和可视化服务来结束我们的 MicroPython IOT 项目之旅。酷!

Footnotes 1

一定要选择一种足够健壮的植物来抵御过度浇水。

2

这种情况经常发生。如果出现这种情况,最好查看实际代码,因为差异文件可能更难阅读。

3

这种情况经常发生。如果出现这种情况,最好查看实际代码,因为差异文件可能更难阅读。

4

可悲的是,有些人会认为这不是 IOT,除非它涉及某种形式的云服务。

十一、项目 4:使用天气传感器

构建一个成熟的 IOT 项目需要使用允许您的小型 MicroPython 板收集数据并将其发送到互联网上的服务的技术,这些服务可以存储、检索和可视化数据。这些互联网服务通常是基于云的服务。很有可能,你已经在不知情的情况下使用了这些技术。在上一章中,我们已经看到了早期的简单形式——使用 HTML 服务器通过互联网向客户机发送数据。

对于某些项目来说,通过 HTML 发送数据可能没什么问题,但是对于需要以其他形式显示数据或者需要对数据进行分析的项目来说,则需要更高级的机制来传输和存储数据。幸运的是,我们可以使用许多技术,包括那些允许控制数据传输的技术。

这种技术有一个定义好的协议(一种通信方式),通常由专门的编程接口支持。微控制器最容易使用的方法之一是定义一个具有发布和订阅角色的协议。也就是说,您可以发布数据(写),也可以订阅数据(读),甚至可以在有新数据时得到通知。最容易使用的发布/订阅协议之一叫做消息队列遥测传输(MQTT)。

在本章中,我们将使用 MQTT 来查看一个完整的 IOT 项目的示例,该项目从传感器读取数据并将数据发送到服务器,然后使用 MQTT 订阅数据的客户端可以访问该服务器。最重要的是,一些 MQTT 服务,比如我们将在本章中使用的服务,还提供了可视化工具,允许您在数据生成时查看数据。

幸运的是,本章中使用的传感器比其他章节中的传感器更容易使用。这个项目的复杂性来自于使用云服务托管数据和 MQTT 发布数据。正如您将看到的,构建它并不困难。让我们为这一章找到更多关于这个项目的信息。

概观

在本章中,我们将实现一个简化的气象站 IOT 解决方案。我们将使用分线板上的小传感器记录温度、气压和湿度。虽然这三个数据点并不完全是一个完整的专业(业余爱好者更少)级别的天气解决方案,但它向您展示了将数据从传感器发送到云是多么容易。

我们将使用 MQTT 将数据发送到云中以发布数据。我们还将使用订阅来监控连接到交换机的另一个数据元素(称为 feed)。这个想法是从以前的项目中借鉴来的,在以前的项目中,我们使用网页上的按钮来触发事件。在这种情况下,我们将使用开关和数据订阅来打开和关闭我们的传感器,其中打开意味着没有数据发布。从这个意义上说,我们的 MicroPython 解决方案变成了一个传感器节点。

这个项目的用户界面将使用 MQTT 服务的一个特性来构建,这个特性允许您创建一个使用各种模块化组件(称为块)显示数据的站点,比如图形和刻度盘。正如您将看到的,创建用户界面比我们在以前的项目中编写的 HTML 要容易得多。 1 但是它有一个限制——它只显示自启动以来保存的数据。当我们测试这个项目时,我们将会了解更多。

在我们看这个项目的硬件之前,让我们绕一小段路来了解一下 MQTT 和我们将在本章中使用的服务。

消息队列遥测传输

发布/订阅模型已经存在一段时间了,至少在理论和概念上是这样。还有实现角色的编程结构。发布者将数据发布到允许订阅者获取数据的位置(服务器、数据库或结构化数据的存储库)。因此,出版者是作者,订阅者是读者。

在 IOT 项目和传感器网络的情况下,我们有一个或多个传感器节点或数据提供者向存储库发送数据。我们可以使用消息队列来记录包含数据的消息,而不是使用结构化存储机制(如 SQL)。当订阅者订阅数据时,他们按照收到消息的顺序获取消息,并解析消息中的数据。因此,他们不必像我们在数据库服务器中使用的那样添加数据抽象层。在这种情况下,MQTT 协议就是您所需要的。

MQTT 是一个简单且非常轻量级的协议(这意味着它不需要一个庞大的库和一组复杂的步骤来使用),您可以在 MicroPython(和其他微控制器平台)上使用它。因为 MQTT 是基于消息队列的,所以该协议非常容忍不可靠的数据传输。由于它不需要大量的内存来使用,因此可以在小型设备上使用。这意味着 MQTT 是一种确保您的小型 IOT 设备可以将数据发送到服务器(称为代理)的方法,并且对发布者和订阅者都有合理的交付保证。这使得 MQTT 成为在 IOT 项目中使用的完美工具。

有趣的是,MQTT 从 1999 年就已经存在了。它是由 IBM 的 Andy Stanford-Clark 博士和 Arcom (Eurotech)的 Alren Nipper 发明的。从那以后,它几乎没有什么变化,并且已经适应了越来越多的平台。有关 MQTT 的更多信息,请参见 http://mqtt.org/faq

让我们更详细地看看 MQTT 是如何工作的。

它是如何工作的

基本 MQTT 服务有三个组件:生成包含数据的消息的传感器节点或发布者、读取消息的客户机或订阅者,以及存储消息并将其分发给订阅者的代理或服务器。图 11-1 显示了三个部件如何工作的概念。

A447395_1_En_11_Fig1_HTML.jpg

图 11-1。

MQTT Concept

请注意,左边是可以发布数据的传感器节点(用实线表示),右边是可以订阅消息的客户端(用虚线表示),以及由代理提供的可视化组件(用虚线表示)。还要注意,传感器节点可以发布和订阅,客户端可以可视化数据和订阅消息。事实上,您可以选择发布和订阅的任意组合。我们将看到如何发布和订阅来自我们的 MicroPython 项目的消息。

客户

MQTT 客户机只是一个具有 MQTT 库的设备(或计算机),您可以使用它对设备进行编程,向代理发送(发布)消息或从代理读取(订阅)消息。幸运的是,WiPy 和 MicroPython 的其他一些基于 ESP 的微控制器端口包含一个 MQTT 库。不幸的是,没有适合 Pyboard 的。因此,在本章中,我们将只看 WiPy MicroPython 板。

经纪人

您可以使用几个代理,包括那些基于云的代理,以及一个您可以在自己的服务器上使用的代理。大多数代理都有自己的 MQTT 客户机实现(有些需要特定的驱动程序或库),并且可能运行在特定的平台上或需要其他组件。无论如何,它们支持来自传感器节点和客户机的相同 MQTT 协议。因此,您可以选择在 MQTT broker 上使用,并在以后转移到另一个上,而不必从头重写代码。

对于 IOT 的项目,你会希望选择一个基于云的代理,这样你就可以将你的板连接到云,并从任何地方访问数据。这个项目的首选代理是 Adafruit IO ( io.adafruit.com )。Adafruit IO 目前处于公测阶段,这意味着任何想要使用它的人都可以使用它。有一些限制——没有一个会影响像本章项目这样的实验。如果您计划基于 Adafruit IO 开发一个商业产品,您可能希望等到它退出测试版,或者考虑一个替代的代理服务。

Alternative MQTT Brokers

网站 https://github.com/mqtt/mqtt.github.io/wiki/servers 包含 MQTT 经纪人列表。您会发现一些迎合特定平台或编程语言(如 Java)的工具。您还会发现一些商业 MQTT 服务,您可以将它们用于更大的项目或您想赚钱的项目。

但是,如果您想在您的网络上建立自己的 MQTT 服务器,您可以使用 Mosquito ( http://mosquitto.org/ ),),它是开源的,非常受爱好者的欢迎。你甚至可以使用一个公共测试服务器。更好的是,您不需要对这个项目中的代码做太多的修改就可以使用它。您应该将 Mosquito 视为从 Adafruit IO 这样的免费 MQTT 代理到商业 MQTT 代理的敲门砖。

Adafruit IO 入门

Adafruit IO ( io.adafruit.com)是一个基于云的数据可视化系统,简单易用,使用起来只需要很少的编程知识。这是通过支持表述性状态转移(REST)和 MQTT APIs 来实现的。我们将在这个项目中使用 MQTT API。

REST 是许多基于云的服务所使用的协议,例如亚马逊网络服务(AWS)提供的服务,以及许多基于云的商业解决方案。它最出名的是通过 HTML 操作与基于 web 的解决方案一起使用。参见 https://en.wikipedia.org/wiki/Representational_state_transfer 了解更多关于休息的信息。在本章中,我们将只使用 MQTT。

Adafruit IO 的目标是消除当前数据记录器和基于云的数据服务解决方案的所有复杂性,并使其易于使用,Adafruit 在这方面做得非常好。简而言之,我们使用 MQTT 客户机驱动程序(库),编写代码来连接、订阅数据或发布数据。可视化部分发生在 Adafruit IO 服务器上,我们创建自己的用户界面来查看数据。Adafruit IO 入门有四个步骤。

  1. 创建一个帐户。
  2. 为您的数据设置源(消息队列)。
  3. 设置一个仪表板来可视化数据。
  4. 连接您的设备并开始发布和订阅。

开始使用 Adafruit IO 所需要的只是一个用户帐户。要为 Adafruit IO 创建一个帐户,只需前往 https://io.adafruit.com/ 。如果你已经在 Adafruit 的服务器上有一个帐户,你可以使用那个帐户,只需注册就可以访问 Adafruit IO。这一过程简单易行。登录后,您将看到管理界面,您可以在其中创建提要和控制面板。

提要是 Adafruit IO 的核心组件。订阅源是您将数据以消息的形式从设备中发布的地方。提要可以存储数据(通过发布),设备(客户端)可以通过订阅提要来读取数据。您可以有多个提要,每个提要都由其名称定义,并通过您的用户 id 引用。

仪表板是提要中数据的视图。Adafruit IO 提供了一个拖放界面,可以使用预定义的用户界面控件(称为“块”)非常快速地构建简单的数据视图。每个仪表板可以有一个或多个块,这些块可以连接到您的提要。然后,数据显示在块中,当新数据到达时会自动更新。

一旦我们设置了提要和仪表板,我们就可以编写代码来使用 MQTT 库并发送(或接收)数据。然后,我们可以返回到我们的控制面板并查看数据。在本章的后面部分,我们将看到为项目设置提要和仪表板的详细演练。

如果你想了解更多关于 io.adafruit.com 的知识,可以看看 Adafruit 的这些优秀教程。还可以在 https://learn.adafruit.com/search?q=io.adafruit.com &找到一些使用 Adafruit IO 的有趣项目创意。

现在,让我们看看这个项目需要哪些组件。

必需的组件

表 11-1 列出了你需要的部件。您可以从 Adafruit ( adafruit.com)、Sparkfun ( sparkfun.com)或任何出售电子元件的电子商店单独购买元件。如果你想购买组件,可以提供供应商链接。当列出同一对象的多行时,您可以选择其中一行——您不需要两个都要。此外,您可能会找到销售这些组件的其他供应商。你应该货比三家,找到最好的交易。显示的成本是估计值,不包括任何运输成本。

表 11-1。

Required Components

| 成分 | 数量 | 描述 | 费用 | 链接 | | --- | --- | --- | --- | --- | | MicroPython 板 | one | WiPy | $25 | [`https://www.adafruit.com/product/3338`](https://www.adafruit.com/product/3338) | | [`https://www.pycom.io/product/wipy/`](https://www.pycom.io/product/wipy/) | | 天气传感器 | one | 宝马 280 (I2C 接口) | $20 | [`https://www.sparkfun.com/products/13676`](https://www.sparkfun.com/products/13676) | | 跳线 | four | M/M 跳线,6”(10 根跳线的成本) | $4 | [`https://www.sparkfun.com/products/8431`](https://www.sparkfun.com/products/8431) | | 面包板 | one | 原型板,半尺寸 | $5 | [`https://www.sparkfun.com/products/12002`](https://www.sparkfun.com/products/12002) | | 力量 | one | 从电脑获取电源的 USB 电缆 |   | 使用您的备件 | | one | USB 5V 电源和电缆 |   | 使用您的备件 |

BME280 分线板大气传感器支持 I2C 或 SPI 接口。Sparkfun 的 BME280 支持两者,这使其成为 IOT 零件套件的理想选择。图 11-2 显示了 Sparkfun 的 BME280 天气传感器。如果您有来自其他供应商的,确保它支持 I2C(或者相应地调整这个项目中的代码和接线图)。它没有头文件,所以你可以自己添加。订购传感器时只需订购一套,自己焊接即可(或找朋友帮忙)。

A447395_1_En_11_Fig2_HTML.jpg

图 11-2。

Atmospheric Sensor Breakout (courtesy of sparkfun.com)

幸运的是,这个项目的布线没有前两个项目复杂。现在,让我们看看如何将组件连接在一起。

安装硬件

这个项目的简单之处在于,我们使用一个传感器和 I2C 接口,因此我们不需要很多连接。图 11-3 显示了 WiPy 的接线图。

A447395_1_En_11_Fig3_HTML.jpg

图 11-3。

Wiring the Weather Sensor (WiPy) Caution

Sparkfun BME280 限于 3.3V,确保仅将其连接到 WiPy 上的 3.3V 引脚。

同样,在给主板通电之前,请务必仔细检查您的连接。暂时不要给你的开发板加电——在我们准备好测试项目之前,我们需要设置我们的 Adafruit IO feeds 和仪表板,然后编写代码。

配置 Adafruit IO

现在我们已经准备好配置 Adafruit IO 了。回想一下,我们的项目将从 BME280 传感器读取三种类型的数据:温度、湿度和大气压力。因此,我们将为其中的每一个创建一个提要。我们还将使用第四个提要,这样我们就可以通过仪表板控制我们的 MicroPython 板。仪表板将有几个块来显示每个提要。让我们看看如何为我们的项目创建提要和仪表板。

设置订阅源

要设置 feed,请登录 Adafruit IO 并从屏幕左侧的链接列表中选择 Feed,然后单击 Actions 下拉列表并选择 Create a New Feed,如图 11-4 所示。

A447395_1_En_11_Fig4_HTML.jpg

图 11-4。

Create a new Feed

接下来,您可以为提要提供名称和描述(可选)。命名第一个进料温度,如图 11-5 所示。当您对输入的数据感到满意时,单击 Create 创建提要。

A447395_1_En_11_Fig5_HTML.jpg

图 11-5。

Create a New Feed dialog

重复该过程,并创建用于存储湿度(名为湿度)、大气压力(名为压力)的提要,以及一个用于控制传感器(名为传感器)的提要。完成后,您应该会看到如图 11-6 所示的新提要。

A447395_1_En_11_Fig6_HTML.jpg

图 11-6。

Feeds for the Weather Project

请注意,此时提要中没有任何数据。在我们设置并连接好 MicroPython 板之前,我们看不到任何数据。既然提要已经设置好了,我们就可以创建一个仪表板来查看发布后的数据。

设置仪表板

您可以创建任意数量的仪表板来查看您的订阅源中的数据。事实上,您可以为每个提要创建一个仪表板,或者创建几个连接到一个或多个提要的仪表板。对于这个项目,我们将创建一个连接到所有四个提要的仪表板。

要设置仪表板,请从屏幕左侧的链接列表中选择仪表板,然后单击操作下拉列表并选择创建新仪表板,如图 11-7 所示。

A447395_1_En_11_Fig7_HTML.jpg

图 11-7。

Create a new Dashboard

接下来,您可以为仪表板提供名称和描述(可选)。将仪表板命名为 WeatherData 并提供描述(可选),如图 11-8 所示。当您对输入的数据感到满意时,单击创建以创建仪表板。

A447395_1_En_11_Fig8_HTML.jpg

图 11-8。

Create a New Dashboard dialog

创建仪表板后,我们必须编辑它以添加块。要添加块,请单击您的仪表板。此时,您有一个空白的仪表板,我们可以在其中添加一个或多个块。可用区块及其用途如表 11-2 所示。正如您将看到的,有些块可以用于以各种方式显示数据(在 Adafruit 文档中称为输出),有些块可以用于在提要中生成数据(称为输入)。一些块可以连接到一个且仅一个 feed,而其他块可以连接到多个 feed。

表 11-2。

Adafruit IO Blocks

| 名字 | 类型 | 描述 | | --- | --- | --- | | 开关按钮 | 投入 | 在文本或数字两个值之间选择。把它想象成一个开关。 | | 瞬时按钮 | 投入 | 向提要发送一个值,就像一个硬件瞬时按钮。 | | 数字滑块 | 投入 | 从您定义的指定范围中选择一个数字。 | | 测量 | 输出 | 显示馈送的当前值。如果设置了最小值和最大值,可以显示百分比。 | | 文本框 | 两者 | 显示静态文本或提要中的文本。 | | 溪流 | 输出 | 显示来自一个或多个源的消息。 | | 图像 | 输出 | 在源中显示图像。 | | 线图 | 输出 | 在折线图中显示来自馈送的数据。 | | 颜色选择器 | 投入 | 选择一个 RGB 值并将其发送到源。 | | 地图 | 输出 | 跟踪订阅源数据的位置(如果地理数据可用)。 | | 远程控制 | 投入 | 模仿 Adafruit 卖的迷你遥控器。 |

对于我们的项目,我们将添加以下块。

  • 线图:每个模块连接一个温度、湿度和压力输入。
  • 切换按钮:连接传感器馈电。
  • Stream:连接所有四个提要。

如图 11-9 所示,点击仪表板编辑屏幕右上角的加号图标,添加一个模块。

A447395_1_En_11_Fig9_HTML.jpg

图 11-9。

Add a Block

当您单击加号时,将会显示可用块的列表。表 11-2 中列出的方块如图 11-10 所示,从左上开始依次排列。要将您选择的块添加到您的仪表板,请单击该块。

A447395_1_En_11_Fig10_HTML.jpg

图 11-10。

Available Blocks

选择块时,必须为块选择馈送。图 11-11 显示了为温度进给的线图选择进给的示例。如您所见,您可以选择任意数量的提要。但是,在对多个提要使用折线图之前,一定要考虑每个数据的规模。在这个项目中,我们对三种类型的数据有不同的尺度。如果我们使用一个折线图,具有最低范围的 feed 将出现在图的底部,并可能掩盖数据随时间的变化(它可能显示为一条平线)。

A447395_1_En_11_Fig11_HTML.jpg

图 11-11。

Selecting the feeds for a block

通过勾选提要名称旁边的框选择了提要后,单击下一步配置块。对于每种类型的块,对话框看起来会有所不同。图 11-12 显示了温度线图块的设置。

A447395_1_En_11_Fig12_HTML.jpg

图 11-12。

Block Settings (Temperature)

注意,我选择将块的名称设置为与提要相同的名称,并更改了 X 轴和 Y 轴的标签。我还设置了 Y 轴的最小值和最大值。如果对设置感到满意,请单击“创建块”将块添加到面板中。

继续为湿度和压力输入创建另外两个线图。您可以用类似的方式命名块,但是使用范围 0,100 表示湿度,使用范围 80000,120000 表示压力。如果地理位置的高程不同,导致范围略有不同,您可能希望或需要调整这些值。

接下来,我们添加一个用于记录所有数据的块。为此,我们将使用流块。添加流块并选择所有四个提要。您可以随意命名,默认设置也可以。图 11-13 显示了我使用的设置。在设置新的仪表板时,这个块是一个很好的块,因为它让您可以在数据到达每个字段时看到数据。

A447395_1_En_11_Fig13_HTML.jpg

图 11-13。

Block Settings (Stream Block )

最后,我们为开关添加一个块来打开和关闭数据记录。添加一个切换按钮块,并使用图 11-14 所示的设置。

A447395_1_En_11_Fig14_HTML.jpg

图 11-14。

Block Settings (Sensors)

还有一步。将块添加到您的仪表板会将它们按出现的顺序和默认大小放置在屏幕上。如果你看看你的仪表板,你会看到每个块都有边,你可以用来调整每个块的大小。你也可以点击一个块,按住鼠标按钮,拖动你喜欢的块。现在就去做吧。您可以使用如图 11-15 所示的布局。

A447395_1_En_11_Fig15_HTML.jpg

图 11-15。

Rearranging Blocks on the Dashboard

编辑完仪表板后,单击完成编辑。如果您想要更改其中一个块的比例或四处移动块,您可以随时编辑您的仪表板。要编辑您已经添加的块,请单击仪表板编辑屏幕上您想要编辑的块的小齿轮图标。

获取您的证书

你还需要做一件事。连接到 Adafruit IO 是通过您的用户帐户和系统生成的特殊密钥完成的。我们使用这些数据代替用户 id 和密码。要取回您的密钥,请转到主页并单击“设置”,然后单击“查看 AIO 密钥”按钮。你会看到一个如图 11-16 所示的对话框。请注意,我已经模糊了数据。您应该像对待任何其他密码一样对待这些数据。如果您想查看如何使用这些值的示例,请单击 Show Code Samples。

A447395_1_En_11_Fig16_HTML.jpg

图 11-16。

AIO Key

源代码中需要您的用户名和 IO 键,所以最好现在就获取这些值,并将它们保存在某个安全的地方。如果不想将这些值保存在文件中,您可以随时参考此页面。另外,如果你需要重新生成你的密钥,你可以点击重新生成 AIO 密钥,但是不要这样做,除非你需要一个新的密钥!

好了,现在我们准备开始发送一些数据。但是首先,我们需要编写代码。

写代码

一旦理解了 MQTT 库,这个项目的代码就相对容易编写。正如您将看到的,在易用性方面,它与 Adafruit IO 配合得很好。为了使这个项目更有趣,更适用于您如何为 MicroPython 平台编写 IOT 项目,我们将把读取传感器和发布数据的所有代码放在一个类中。然后我们将在一个代码模块中使用这个类,我们可以在启动时从我们的main.py代码模块中调用它。由于代码没有使用任何我们还没有见过的结构,所以代码的描述将会很简短,只关注新概念和 MQTT 代码。

然而,我们首先需要为 WiPy 下载一个 MQTT 库和 BME280 驱动程序。

MQTT 驱动程序

WiPy 的 MQTT 库可以在 https://github.com/pycom/pycom-libraries 下载。我建议下载整个存储库。为此,请单击“克隆”或“下载”按钮,然后单击“下载 Zip”并将文件保存在您的 PC 上。下载完成后,您可以解压缩文件,然后在/lib/mqtt文件夹中找到 MQTT 库。我们想要的文件被命名为mqtt.py。在后面的步骤中,您需要将该文件复制到 WiPy 中。

BME280 库

BME280 的驱动程序可从 https://bitbucket.org/oscarBravo/wipy_bme280 下载。要下载驱动程序,请单击屏幕左侧的下载图标(看起来像一朵带有向下箭头的云),然后单击可用下载列表中的下载库。下载完成后,解压文件并找到名为bme280.py的驱动程序。

虽然驱动程序将使用 WiPy,但传感器的地址可能需要更改。确保检查您选择的 BME280 传感器的地址,并确保驱动程序文件(bme280.py)中的地址设置正确。当您运行代码时,未能使用正确的地址将导致奇怪的、致命的错误。下面显示了以十六进制指定地址的代码行。

# BME280 default address.

BME280_I2CADDR = 0x77

回想一下,您可以使用 I2C 类的scan()函数找到连接到您的板的 I2C 设备的地址,如下所示。该函数返回一个十进制值,但您可以将其转换为十六进制,如下所示。确保首先连接 I2C 传感器!

MicroPython v1.8.6-694-g25826866 on 2017-06-29; WiPy with ESP32
Type "help()" for more information.
>>> from machine import I2C
>>> i2c = I2C(0, I2C.MASTER, baudrate=100000)
>>> devices = i2c.scan()
>>> for device in devices:
...         print("Address: {0}".format(hex(device)))
Address: 0x77

这里我们看到地址是 0x77。如果您查看bme280.py文件,您会看到默认值是 0x76。只要改变它来匹配你的传感器,它就会正常工作。

天气等级

我们在第四章中学到了职业。在那里,我们了解到我们可以用这样一种方式包装我们的代码,使得对数据的一组操作更容易使用。我们将在这里这样做。我们将围绕 BME280 传感器和 Adafruit IO 包装我们的代码,以创建一个易于使用的类或库,用于读取天气数据并将其发布到云。

然而,我们不会随意地将通常在函数中使用的代码扔进一个类中。我们将把这个类设计成可重用和易于配置的。最好的方法是让构造函数——类实例化时调用的函数——接受参数。

例如,我们将传递 Adafruit IO 连接的凭证(设备 id、用户名、AIO 密钥)以及我们希望发布数据的频率(回想一下,这也称为采样率)。通过构造函数将信息传递到类中,我们可以让类中的所有函数都可以使用这些数据,这样我们就不用使用全局变量了! 2

本质上,我们将打包所有繁重的代码,用于初始化 BME280 库、MQTT 库、从传感器读取数据并发布数据,以及订阅传感器提要。

如果您想继续学习,我们将把这个类命名为WeatherNode,因为它封装了传感器并发布数据。我们将代码模块命名为weathernode.py。让我们考虑一下这个课程的设计。

设计

让我们讨论一下这个类的设计。这是一个非常重要的步骤,你应该始终考虑。对于你的类,有一个好的设计(或者至少是一个计划),以你想要实现的特性的形式,并且用函数来实现它们,可以对你的代码质量产生很大的影响。

对于这个类,我们将创建一个可以从主代码中调用的函数。该函数将负责实现一个循环,从传感器读取数据,发布数据,并监控来自传感器 feed 的任何消息。如果出现传感器馈送的消息,该功能也将对其进行操作。回想一下,这意味着根据提要中的开关位置消息打开和关闭发布数据。

因此,我们需要一个函数来启动 read 循环,另一个函数用于读取数据,还有一个函数可以作为回调函数来响应提要中的消息。以下部分描述了每个区域以及执行这些操作的功能。让我们从进口部分开始。

进口

导入部分需要几个库。我们需要 I2C 和 MQTT 库、BME280 驱动程序和延迟时间库。下面显示了该类所需的导入。

from machine import I2C
from mqtt import MQTTClient
import bme280
import utime

构造器

构造函数需要接受 Adafruit IO 标识标签或设备 id、用户名、AIO 密钥和频率(采样率)。设备 id 可以是您想要使用的任何字符串,但是建议您使用设备的短名称。我们还将为 MQTT 服务器的端口使用一个参数,以防您想要更改它,但是我们将使用默认值 1883,这是 Adafruit IO 的端口。清单 11-1 显示了构造函数的代码。构造函数总是被命名为__init__()

# Constructor
def __init__(self, io_id, io_user, io_key, frequency, port=1883):
    # Turn sensors on/off
    self.sensor_on = False

    # Save variables passed for use in other methods
    self.io_id = io_id
    self.io_user = io_user
    self.io_key = io_key
    self.update_frequency = frequency
    self.port = port

    # Now, setup the sensor
    i2c = I2C(0, I2C.MASTER, baudrate=100000)
    self.sensor = bme280.BME280(i2c=i2c)
    utime.sleep_ms(100)
    print("Weather MQTT client is ready.")

Listing 11-1.Constructor (WeatherNode class)

注意,这个函数只是初始化了几个类变量(用self.表示),用于存储连接值和频率。该构造函数还初始化 I2C 接口和 BME280 传感器类实例。

读取数据功能

我们将命名为read_data()的 read data 函数将指向 BME280 传感器对象实例的指针作为参数,从传感器读取数据,并以元组的形式返回。下图显示了read_data()功能。

# Reads the sensor. Returns a tuple of (temperature, humidity, pressure)
def read_data(self):
    utime.sleep_ms(50)
    temperature = self.sensor.read_temperature() / 100.00
    humidity = self.sensor.read_humidity() / 1024.00
    pressure = self.sensor.read_pressure() / 256.00
    return (temperature, humidity, pressure)

请注意,我们对读取的值执行了一些缩放操作。这些计算来自记录 BME280 驱动程序的网站( https://bitbucket.org/oscarBravo/wipy_bme280 )。

消息回调函数

我们需要的下一个函数是一个可以用来回调来自提要的 MQTT 消息的函数。我们将这个函数命名为message_function()。在这种情况下,我们只需要主题和消息参数。下面显示了message_callback()函数的代码。

# A simple callback to print the message from the server
def message_callback(self, topic, msg):
    print("[{0}]: {1}".format(topic, msg))
    self.sensor_on = (msg == b'ON')

注意,我们打印收到的消息,如果出现打开传感器的消息,我们将布尔类变量设置为 True。我们将看到如何在 run 函数中使用这个特性。

运行功能

我们需要的最后一个函数将完成从传感器收集数据并将其发布到 Adafruit IO 上的所有工作。这是使用 MQTT 库的核心所在,所以我们将稍微慢一点来介绍这一点。

我们做的第一件事是获得一个MQTTClient类的实例。我们传入设备 id、服务器的主机名(在本例中是io.adafruit.com)、用户 ide、AIO 密钥和端口。

一旦我们有了客户端的实例,我们就可以设置回调来从与我们的设备 id 相关联的订阅提要中获取任何消息,我们使用set_callback()函数传入我们的方法的名称来处理消息。回想一下,这是message_callback()

接下来,我们使用如下所示的client.subscribe()函数订阅名为<user id>/feeds/sensors的传感器提要。

client.subscribe(topic="{0}/feeds/sensors".format(self.io_user))

我们使用topic参数指定进给。提要名称总是由用户 id 和提要名称组成。你可以把它想象成一条路径。请务必仔细检查提要名称,以确保您选择了正确的名称。你可以随时查看你的 Adafruit IO 页面进行复查。

这就完成了使用 MQTT 的设置代码。很简单,不是吗?

现在我们编写 while 循环来从传感器读取数据。为了使这变得容易,我们将无限循环(但是如果你计划使这个项目不仅仅是一个实验,你可能想要改变这一点)。在循环内部,我们将检查是否可以从传感器读取数据。回想一下,我们使用仪表板上的切换按钮来控制我们的电路板。如果切换按钮为“开”,我们读取数据,否则不读取数据。

如果我们正在读取数据(切换按钮打开),我们使用read_data()函数读取数据,然后使用client.publish()函数将每个数据元素发布到适当的提要。例如,为了向temperature提要发送数据,我们使用topic参数指定用户 id 和提要,并通过msg参数发送数据,如下所示。

client.publish(topic="{0}/feeds/temperature".format(self.io_user), msg=str(data[0]))

在循环的最后,我们做几件事。我们睡眠的秒数在更新频率参数中指定。这就是我们如何避免过于频繁地发送太多数据,并使我们的数据收集与实际时间间隔相匹配。接下来,我们使用client.check_msg()函数检查消息。这个函数只是检查是否有新消息到达。如果有,因为我们指定了一个回调函数,每个新消息都会调用这个回调函数。看,不需要轮询!最后,我们仅仅休眠一秒钟,以便有时间触发回调函数。

清单 11-2 显示了WeatherNode类的完整运行函数。

def run(self):
    # Now we setup our MQTT client
    client = MQTTClient(self.io_id, "io.adafruit.com", user=self.io_user,
                        password=self.io_key, port=self.port)
    client.set_callback(self.message_callback)
    client.connect()
    client.subscribe(topic="{0}/feeds/sensors".format(self.io_user))

    while True:
        if self.sensor_on:
            data = self.read_data()
            print(" >", data)
            client.publish(topic="{0}/feeds/temperature".format(self.io_user),
                           msg=str(data[0]))
            client.publish(topic="{0}/feeds/humidity".format(self.io_user),
                           msg=str(data[1]))
            client.publish(topic="{0}/feeds/pressure".format(self.io_user),
                           msg=str(data[2]))
            utime.sleep(self.update_frequency)
        client.check_msg()
        utime.sleep(1)     # Check messages only once per second

Listing 11-2.Run Function (WeatherNode)

现在让我们看看这个类的完整代码。

完整代码

清单 11-3 显示了WeatherNode类代码模块(weather_node.py)的完整代码。

# MicroPython for the IOT - Chapter 11
#
# Project 4: MicroPython Weather Node - BME280 MQTT Client class
#
# Note: this only runs on the WiPy.
#
# Imports for the project
from machine import I2C
from mqtt import MQTTClient
import bme280
import utime

class WeatherNode:
    """Sensor node using a BME280 sensor to send temperature, humidity, and
       barometric pressure to io.adafruit.com MQTT broker."""

    # Constructor
    def __init__(self, io_id, io_user, io_key, frequency, port=1883):
        # Turn sensors on/off
        self.sensor_on = False

        # Save variables passed for use in other methods
        self.io_id = io_id
        self.io_user = io_user
        self.io_key = io_key
        self.update_frequency = frequency
        self.port = port

        # Now, setup the sensor
        i2c = I2C(0, I2C.MASTER, baudrate=100000)
        self.sensor = bme280.BME280(i2c=i2c)
        utime.sleep_ms(100)
        print("Weather MQTT client is ready.")

    # Reads the sensor. Returns a tuple of (temperature, humidity, pressure)
    def read_data(self):
        utime.sleep_ms(50)
        temperature = self.sensor.read_temperature() / 100.00
        humidity = self.sensor.read_humidity() / 1024.00
        pressure = self.sensor.read_pressure() / 256.00
        return (temperature, humidity, pressure)

    # A simple callback to print the message from the server
    def message_callback(self, topic, msg):
        print("[{0}]: {1}".format(topic, msg))
        self.sensor_on = (msg == b'ON')

    def run(self):
        # Now we setup our MQTT client
        client = MQTTClient(self.io_id, "io.adafruit.com", user=self.io_user,
                            password=self.io_key, port=self.port)
        client.set_callback(self.message_callback)
        client.connect()
        client.subscribe(topic="{0}/feeds/sensors".format(self.io_user))

        while True:
            if self.sensor_on:
                data = self.read_data()
                print(" >", data)
                client.publish(topic="{0}/feeds/temperature".format(self.io_user),
                               msg=str(data[0]))
                client.publish(topic="{0}/feeds/humidity".format(self.io_user),
                               msg=str(data[1]))
                client.publish(topic="{0}/feeds/pressure".format(self.io_user),
                               msg=str(data[2]))
                utime.sleep(self.update_frequency)
            client.check_msg()
            utime.sleep(1)     # Check messages only once per second

Listing 11-3.WeatherNode Class (weather_node.py)

现在,让我们看看主要代码。

主代码

主代码文件名为 weather.py。正如您将看到的,它非常简短。这是因为所有的工作都是在我们的类模块中完成的!下面几节简要描述了主要代码的主要部分,从导入开始。

进口

因为所有的工作都是在WeatherNode类中完成的,我们只需要导入代码模块(weather_node)和那些将我们的电路板连接到我们的 WiFi 网络所需的库,如下所示。

from network import WLAN
from weather_node import WeatherNode
import machine

全局定义

WeatherNode 类需要我们的 Adafruit IO 帐户的连接数据。为了更容易修改,我们可以在主代码中使用一些定义来包含这些变量。下面显示了一种方法来做到这一点。这里,我们看到了设备 id、用户 id、AIO 键和频率(用于更新数据)的定义。确保更改这些以匹配您的 Adafruit IO 凭证,并相应地设置频率。

# Define out user id and key

_IO_ID = "YOUR_DEVICE_ID"

_IO_USERNAME ="YOUR_USER_ID"

_IO_KEY = "YOUR_AIO_KEY"

_FREQUENCY = 5 # seconds

您可能希望将频率保持在示例中的 5 秒,这样您就不必等待很长时间才能看到结果,但是在您为任何长期运行部署项目时,一定要更改这个频率。

连接功能

connect()函数与我们在前面章节中看到的函数相同。清单 11-4 显示了完整的代码。正如您将看到的,您必须提供您的 SSID 和 SSID 密码。

# Setup the board to connect to our network.
def connect():
    wlan = WLAN(mode=WLAN.STA)
    nets = wlan.scan()
    for net in nets:
        if net.ssid == 'YOUR_SSID_HERE':
            print('Network found!')
            wlan.connect(net.ssid, auth=(net.sec, 'YOUR_WIFI_PASSWORD'), timeout=5000)
            while not wlan.isconnected():
                machine.idle() # save power while waiting
            print('WLAN connection succeeded!')
            print("My IP address is: {0}".format(wlan.ifconfig()[0]))
            return True
    return False
Listing 11-4.Connection Function (weather.py)

现在,让我们来看看 run 函数。

运行功能

run()功能非常简单。我们需要做的就是调用connect()函数将我们的 WiPy 连接到我们的 WiFi 网络,然后实例化WeatherNode类,然后调用该类的run()函数,如下所示。

connect()
# Run the weather MQTT client
weather_mqtt_client = WeatherNode(_IO_ID, _IO_USERNAME, _IO_KEY, _FREQUENCY)
weather_mqtt_client.run()

就这样!如果我们使用类来包含项目的核心,你能看到这会使我们的代码变得多么容易吗?希望您会看到好处,并开始以类似的方式构建自己的项目。现在,让我们看看weather.py代码模块的完整代码。

完整代码

清单 11-5 显示了天气主代码模块(weather.py)的完整代码。请注意,在运行此代码之前,您必须提供设备 id、您的 AIO 用户名、AIO 密钥、更新频率、SSID 名称和 SSID 密码。

# MicroPython for the IOT - Chapter 11
#
# Project 4: MicroPython Weather Node
#
# Required Components:
# - WiPy
# - (1) BME280 Weather Sensor
#
# Note: this only runs on the WiPy.
#
# Imports for the project
from network import WLAN
from weather_node import WeatherNode
import machine

# Define out user id and key

_IO_ID = "YOUR_DEVICE_ID"

_IO_USERNAME ="YOUR_USER_ID"

_IO_KEY = "YOUR_AIO_KEY"

_FREQUENCY = 5 # seconds

# Setup the board to connect to our network.
def connect():
    wlan = WLAN(mode=WLAN.STA)
    nets = wlan.scan()
    for net in nets:
        if net.ssid == 'YOUR_SSID':

            print('Network found!')
            wlan.connect(net.ssid, auth=(net.sec, 'YOUR_SSID_PASSWORD'), timeout=5000)

            while not wlan.isconnected():
                machine.idle() # save power while waiting
            print('WLAN connection succeeded!')
            print("My IP address is: {0}".format(wlan.ifconfig()[0]))
            return True
    return False

def run():
    # Setup our Internet connection
    connect()

    # Run the weather MQTT client
    weather_mqtt_client = WeatherNode(_IO_ID, _IO_USERNAME, _IO_KEY, _FREQUENCY)
    weather_mqtt_client.run()

Listing 11-5.Main Code (weather.py)

现在,让我们运行这个项目!

执行!

现在是有趣的部分!我们已经设置好了从传感器读取天气数据并在 Adafruit IO 中发布数据的代码。现在您可以将这两个文件(weather.pyweather_node.py)复制到您的 WiPy 中,并如下启动代码。请记住,使用 USB 连接到您的 WiPy,因为一旦建立了 WiFi 连接,TCP 或 WebREPL 连接将会断开。

>>> import weather
>>> weather.run()

您应该会看到连接到网络的打印消息,几秒钟后,会看到从传感器读取的数据。因为我们还打印出了我们订阅的提要中的消息,所以我们也会看到这些消息。如果 MQTT 连接出现错误,请确保仔细检查 Adafruit IO 凭证,进行任何更改,然后重启您的主板并重试。

Note

您必须在代码中提供设备 id、您的 AIO 用户名、AIO 密钥、更新频率、SSID 名称和 SSID 密码。看到那些用粗体标记的行了吗?

在您开始在仪表板中查看任何数据之前,还有一件事您必须做。回想一下,我们编写的类只在仪表板上的切换按钮设置为“on”时发布数据。这样做的机制是读取 sensors feed 中的消息,寻找切换按钮的状态变化。当您第一次连接时,该订阅源中可能没有这样的消息。因此,您必须打开仪表板并打开切换按钮。如果它已经打开,请将其关闭并再次打开。然后,您应该会在 REPL 控制台的调试消息中看到该消息。图 11-17 显示了处于正确位置的拨动按钮。

A447395_1_En_11_Fig17_HTML.jpg

图 11-17。

Turn Sensors ON (toggle button)

一旦打开切换按钮,您应该开始看到发布数据的调试消息。清单 11-6 展示了一个在 WiPy 上运行项目的例子。请注意为传感器馈送显示的消息。

>>> import weather
>>> weather.run()
Network found!
WLAN connection succeeded!
My IP address is: 192.168.42.128
Weather MQTT client is ready.
 [b'my_user_id/feeds/sensors']: b'OFF'
 [b'my_user_id/feeds/sensors']: b'ON'
 > ['23.09', '41.92773', '101964.1']
 > ['23.09', '42.0166', '101972.2']
 [b'my_user_id/feeds/sensors']: b'OFF'
 [b'my_user_id/feeds/sensors']: b'ON'
 > ['23.08', '41.93848', '101974.9']
 [b'my_user_id/feeds/sensors']: b'OFF'
 [b'my_user_id/feeds/sensors']: b'ON'
 > ['23.07', '41.90332', '101974.9']
 > ['23.07', '41.92578', '101980.3']
 > ['21.07', '41.90332', '101974.9']
 > ['20.97', '41.92578', '101980.3']
 > ['20.07', '40.13824', '101974.9']
 > ['19.96', '39.32481', '101972.1']
 > ['20.06', '40.62481', '101974.8']
 > ['21.05', '40.95801', '101974.8']
 > ['22.05', '41.93555', '101982.9']
 > ['23.05', '41.91308', '101977.5']
...
Listing 11-6.Running the Weather Project (WiPy)

好了,一旦您的项目运行并发布数据,您就可以开始在您的仪表板中看到它了。继续登录 Adafruit IO 并选择您的仪表板。现在,您应该可以在所有线图和流块中看到数据。图 11-18 显示了如果您以 5 秒的默认频率采样数据,您的仪表板将会是什么样子。

A447395_1_En_11_Fig18_HTML.jpg

图 11-18。

Data Visualization via Adafruit IO Dashboard (Weather Project)

注意压力和湿度线变化不大。这是意料之中的,因为这个例子是在很短的时间内在室内运行的。然而,你可能会注意到温度数据的上升和下降。这是由于人为改变了温度。也就是说,我将热源移近传感器,使该区域变得更温暖,从而记录更高的值。类似地,我使用冷空气源来冷却该区域,导致数值下降。当我这样做的时候,它引起了湿度值的轻微变化。你能想到为什么会这样吗? 3

你可以自己尝试一下,但是要小心!不要触摸传感器,以免损坏它,也不要使用任何明火或其他可能导致烧伤(或更糟)的热源。

Caution

抵制触摸传感器的诱惑。您可能会损坏传感器。

如果您退出仪表板并重新打开它,您可能会注意到一些奇怪的事情。Adafruit IO 仪表板目前只显示从仪表板打开时起提要中的消息(数据)。最有可能的情况是,您运行项目一段时间,然后停止它,稍后回到您的仪表板,自豪地向您的朋友展示您的成果,却发现仪表板中没有任何数据。虽然这可能是仪表板的一个功能,但您的订阅源和其中的历史仍然存在。

这是因为当您创建新的提要时,默认情况下会存储历史记录(数据)。您可以对此进行更改,以便只存储最后一个值,但是大多数 IOT 项目都希望数据存在一段时间。因此,使用仪表板来查看历史数据是行不通的。但是,您仍然可以查看提要中的数据,并通过直接打开提要以图形方式查看。只需点击您的 feed,您就会看到如图 11-19 所示的数据。

A447395_1_En_11_Fig19_HTML.jpg

图 11-19。

Displaying Historical Data in Feeds

注意底部附近的 Actions 下拉框。请注意,您可以勾选复选框来选择消息,顶部有一个复选框来选择所有消息。选择邮件后,您可以使用操作下拉列表删除选定的数据或下载选定的数据。您甚至可以添加数据(一次一条消息)。这使得获取和修改数据变得容易。下载数据的能力意味着您可以在其他项目或工具中使用它来分析数据。图 11-20 显示了可用于修改 feed 中数据的可能操作。

A447395_1_En_11_Fig20_HTML.jpg

图 11-20。

Modifying Data in Feeds

如果你喜欢这个项目,你可以考虑让它运行更长的时间,将采样频率降低到每几个小时一次。您仍然会看到数据,但是如果您环境中的温度发生变化,您可能会看到数据的实际变化,而不是模拟的变化。

至此,您已经完成了另一个真正的 MicroPython IOT 项目。在这种情况下,我们看到了一个在云中收集和显示数据的 IOT 项目。这有多酷?

更进一步

和上一个项目一样,这个项目展示了在其他项目中重用这些技术的良好前景。现在您已经知道如何使用 MQTT,这一点尤其正确。如果你喜欢在互联网上看到你的传感器数据,你应该考虑花时间探索一些修饰。这里有一些你可以考虑的。有些很容易,有些可能是一个挑战或需要更多的研究。

  • 添加更多的传感器,将您的项目扩展到更多的天气观测。
  • 修改仪表板以使用不同的块显示数据。
  • 添加更多的传感器节点,从其他地方收集数据观察,为每个节点创建新的数据源。
  • 设置您自己的 MQTT 代理并连接您的传感器节点。提示:在weather_node.py中修改MQTTClient()调用中的主机名。
  • 使用 MQTT 构建一个家庭自动化项目,允许您通过云打开和关闭灯。
  • 添加另一个可以用来关闭项目的按钮。提示:您可以在run()函数中添加一个检查,以便在开关关闭时终止循环。

摘要

以 WiPy 这样的小型微控制器为例,添加传感器,编写相对较短的 MicroPython 代码,连接到互联网并生成您可以在世界任何地方查看的数据,在一个工作项目中演示了物联网的概念。我们现在已经了解了如何利用我们对 MicroPython 和小型 MicroPython 板的新知识来实现真正的 IOT 解决方案。

在本章中,我们通过构建一个天气传感器项目来演示这一点,该项目将我们的评估板连接到 Adafruit IO 云服务,并使用该服务来存储和可视化数据。虽然回想起来,这个项目似乎比以前的项目更容易,但我们在那些项目中获得了专业知识,使这个项目更容易实施。也就是说,如果没有如何构建 IOT 项目的基础知识,跳入 IOT 项目的深水区将是愚蠢的。

幸运的是,在 Adafruit 的好心人的帮助下,我们现在可以建立 IOT 项目,将数据发送到云端,并与世界共享。不仅如此,我们还可以构建解决方案,让我们能够从互联网上控制我们的项目。一切都做得很快,很少的努力,不需要学习复杂的 API。这有多酷?

凭借本书中获得的经验,您现在已经准备好构建复杂的 IOT 解决方案,从您为了好玩而运行的简单项目到完整的基于云的解决方案。现在是时候发挥你自己的想象力,将你在本书中学到的工具和技术用于构建你自己的 MicroPython IOT 解决方案了。

在下一章中,我们将看到当你计划更多的 MicroPython 项目时,如何进一步利用你在本书中学到的知识。

我希望你喜欢这次旅行,并且阅读和从事本书中的项目对你来说就像我写它们一样有趣。

Footnotes 1

一些精明的 HTML 程序员可能不敢苟同。

2

这是许多计算机科学和编程老师最讨厌的事情。完全合法使用,但被许多人认为是不良形式。

3

我用的是一罐空气,比周围环境干燥多了。

十二、何去何从

现在,您已经对 MicroPython、MicroPython 硬件、物联网以及您可以创建的项目类型和教程以及示例有了全面的介绍,是时候考虑您可以在本书页面之外做些什么了。

在这一章中,我们将探讨你能做些什么来继续你构建 IOT 解决方案的手艺。大多数人只是想继续为自己开发项目,要么是为了娱乐,要么是为了解决家里或办公室的问题。然而,有些人会想让他们的技能更上一层楼。

不管是哪种情况,有几件事你应该考虑。在接下来的章节中,我们将查看更多示例项目的来源,如何通过社交媒体和其他互联网资源加入 IOT 爱好者社区,以及如何成为日益壮大的创客群体中的一员。

更多项目有待探索

如果您想从事更多的 MicroPython IOT 项目,您会很高兴地得知有许多示例可供您探索。大多数示例或者在各种文档站点中,或者是来自社区的贡献,从高层次的概述到如何完成项目的详细说明。可悲的是,大多数例子很少或根本没有说明。然而,现在您已经有了关于使用 MicroPython IOT 项目和各种 MicroPython 板的详细说明 1 ,您应该能够在很少或没有文档的情况下完成示例(但是文档总是有帮助的)。

MicroPython IOT 示例项目有几个存储库。大多数都是在专门讨论 MicroPython 或 MicroPython board 的论坛上(例如 Pyboard),但也有一个名为 Hackster.io 的很酷的网站,这是一个通用硬件社区论坛( www.hackster.io )。我们一会儿将看到如何浏览该站点。首先,让我们看看 MicroPython 示例项目的一些参考资料。

MicroPython 项目示例

有几个网站提供了 MicroPython 示例。有三种基本类型的网站:人们可以张贴他们的项目类别的论坛,有示例项目的文档站点,以及人们可以上传他们的项目的存储库。让我们来看看每一个。

Tip

查找 MicroPython 示例的最佳方式是在 google 上搜索“MicroPython 示例”或“MicroPython 示例”。你会找到大量的点击,包括本章列出的那些资源。

论坛

第一种类型是论坛,提供了一系列类别,包括公告、问题和答案、常见问题、技术支持等等。幸运的是,有几个项目也有一个类别。有时该类别包括许多条目(主题),大多数是关于如何实现某个项目的问题。这有时会让寻找项目想法变得乏味。

MicroPython 论坛有这样一个分类( https://forum.micropython.org/viewforum.php?f=5 )。虽然有超过 100 个主题,大多数是问题,但也有一些你可以探索的瑰宝。Pycom 也有一个 WiPy 论坛,有一个项目类别( https://forum.pycom.io/category/28/projects )。

使用论坛查找示例项目或样本可能需要一些工作,因为它们旨在供社区使用,因此包含许多问题和评论,但我觉得它们仍然是最好的资源之一。但是,请记住,它们是针对特定电路板(Pyboard 或 WiPy)的。使用它们的最佳方式是导航到论坛,滚动浏览主题或搜索关键词。图 12-1 显示了 Pycom MicroPython 项目类别的摘录。

A447395_1_En_12_Fig1_HTML.jpg

图 12-1。

Excerpt from the Pycom MicroPython Project Category (courtesy of pycom.io)

文件

MicroPython 文档也是查找示例代码(但不是整个项目)的好资源。他们通常比论坛更好,因为他们组织得更好,有时写得更好。然而,与论坛一样,MicroPython 文档站点,尤其是示例,是特定于硬件的。也就是说,Pyboard 有一套文档,WiPy 和其他板有一套文档。

然而,文档中隐藏了一些示例项目。不幸的是,有些没有被很好地记录或者可能被部分记录。无论如何,这些示例中的代码通常比您在论坛中找到的要好得多。

使用文档侧的最佳方式是简单地导航到它们并浏览目录(仅此一点就使它们比论坛更好)。例如,图 12-2 显示了 Pycom MicroPython 文档中关于 PySense shield 的一个示例项目。

A447395_1_En_12_Fig2_HTML.jpg

图 12-2。

PySense Sample Project (courtesy of pycom.io)

仓库

MicroPython 项目的最佳网站是资源库。这些通常托管在 GitHub 之类的源代码控制服务中。这些站点最有用的地方在于,您可以直接导航到源文件,并在浏览器中查看代码,跳过文档或演示页面。如果您只想了解如何实现某些功能,而不是浏览一长页文本,这就很好了。当然,使用示例的最佳方式是下载整个示例集。只需访问 GitHub 主站点并下载示例项目。这有多酷?

主要的 MicroPython 站点( https://github.com/micropython/micropython )是 MicroPython 示例项目库的最佳范例之一。这个资源库有很多示例,虽然有些是针对 Pyboard 的,但是您可以使用它们作为模板,来学习如何为各种各样的项目编写代码。图 12-3 显示了存储库主页的摘录。

A447395_1_En_12_Fig3_HTML.jpg

图 12-3。

MicroPython GitHub Repository Sample Projects

大多数(如果不是全部的话——我还没有检查全部)示例都是在开源许可下许可的,比如 MIT 许可,这使得每个人都非常方便,因为 MIT 许可允许您使用甚至发布代码。(参见 https://opensource.org/licenses/MIT 获取麻省理工学院许可证的样本。)这很棒,因为我曾多次想使用一个项目的样本或演示,却发现许可证不允许这样做。

社区项目站点:Hackster.io

Hackster 网站是一个致力于学习硬件的社区。您可以找到各种各样的硬件示例项目,包括许多针对 MicroPython、Python、Raspberry Pi、Arduino 等的项目!MicroPython 项目的数量不多,但在不断增加。当你访问母网站( www.hackster.io )时,在搜索框中输入“MicroPython”即可搜索项目。一旦您输入搜索条件,搜索结果页面,您会看到所有您可以探索的项目。每一个都标有相对的难度等级,从简单到高级。您还可以看到示例项目被查看的次数以及该项目从社区中的其他人那里获得的好评数(您必须加入 Hackster.io 才能对项目进行评级)。最重要的是,有一个评论区,你可以用来鼓励设计师或向设计师寻求帮助。

Tip

在网上论坛发表评论或问题时,使用黄金法则。抵制发布观点、煽动异议或嘲笑的诱惑,只坚持事实。

我最喜欢 Hackster 站点的一点是,这些样本通常都有很好的文档记录,并且经常包括项目的几张照片。由于站点的独特结构,这些示例被组织成易于理解的部分。例如,“I2C 上的字符 LCD”(www.hackster.io/dzerycz/character-lcd-over-i2c-ba8ee9)的网页包含一个概述部分,它给出了项目的简短描述、相关标签、难度等级、出版日期,甚至是许可证。这使得审查项目变得非常容易。

例如,有一个中级项目的优秀例子,它不仅有据可查,而且写得很好。它就是罗宾·科尔的“带 Adafruit 和家庭助手的 MicroPython 检漏仪”。图 12-4 显示了项目概述的摘录。如果你想看另一个优秀项目,请访问 https://www.hackster.io/robin-cole/micropython-leak-detector-with-adafruit-and-home-assistant-a2fa9e

A447395_1_En_12_Fig4_HTML.jpg

图 12-4。

Hackster.io sample project

如果您从概述向下滚动,您会发现演示如何连接硬件的部分(像我在本书中介绍的项目),代码的简短演练,以及如何使用项目的描述和演示。

一些样本包括演示或解释项目的短视频。在页面的末尾,您可以找到评论部分,您可以使用它来阅读其他人对该项目的评论,以及其他人对该项目的疑问。如果你被一个样本卡住了,一定要阅读所有的评论——很有可能有人已经提出了这个问题或者解决了这个问题。

What About Hackaday.io?

另一个和 Hackster.io 类似的网站是 Hackaday.io,它很像 Hackster.io,因为你可以搜索 MicroPython 项目。然而,我不像 Hackster.io 那样喜欢它,但不要让这阻止你探索它——试试吧!

知识库:learn.adafruit.io

关于 MicroPython 的另一个极好的信息源是 learn.adafruit.com。这个网站包含了各种主题的文章、博客和教程。与用户论坛非常相似,内容会频繁地添加到这个站点。所以,你应该定期访问网站,搜索你的主题。例如,要查找 MicroPython 内容,只需搜索 MicroPython。或者,可以用这个链接: https://learn.adafruit.com/search?q=micropython &。

我经常使用这个网站来寻找项目的想法。即使文章、博客或教程不完全是您想要的,或者可能与您的硬件不匹配,它也是值得一读的。例如,如果您计划使用 Charlieplex LED ( https://www.adafruit.com/?q=charlie )构建一个项目,您可以在 https://learn.adafruit.com/micropython-hardware-charlieplex-led-matrix/software?view=all 找到教程。虽然本教程以 Feather 和 ESP8266 板为特色,但您仍然可以学习使用 Charlieplex LED 的一些知识,包括驱动程序(您可以修改)、提示以及如何使其与其他 MicroPython 板配合使用的见解。

图 12-5 显示了来自 learn.adafruit.com 的摘录,展示了一些关于 MicroPython 的有趣内容。请务必查看该网站最近的新增内容。

A447395_1_En_12_Fig5_HTML.jpg

图 12-5。

MicroPython Content on learn.adafruit.com

现在您已经看到了更多示例项目的参考资料,让我们讨论一下如何加入社区并为 IOT 不断增长的所有 MicroPython 资源库做出贡献。

加入社区

一旦您掌握了本书中的示例项目以及其他资源中的一些项目,是时候通过加入 IOT 开发者和爱好者社区来进一步发展您的爱好了。

在这一节中,我将讨论一些你可能想要分享你的知识的原因,分享和贡献的礼仪,以及一些你可能想要加入或至少监控的示例社区。正如您将看到的,这些并不完全是专门针对 MicroPython 的,但是可以成为很好的思想来源。让我们从为什么我们想要分享开始。

为什么要投稿?

随着越来越多的自由思想者推动像 MicroPython 和 IOT 这样的爱好,分享的概念变得越来越流行。这不是偶然的。Python、MicroPython 和 IOT 的许多创始人和先驱都是开放硬件和开源的倡导者。这不仅适用于硬件和软件,也适用于其他智力产品,如已发布项目的源代码和文档。

许多人认为他们的代码应该是免费的,任何人都可以使用和修改。例如,如果你修改了别人的设计或代码,你不仅应该分享改进的设计,还应该表扬原创者。在某些情况下,这就像列出原作者一样简单,但其他时候这可能意味着给原作者你的修改。只要你遵循许可证的指导方针,共享是公平合理的。

然而,根据示例代码的编写方式(许可方式),共享内容可能会有一些限制。例如,可能无法共享专有库中的代码。虽然您可能是使用该库的代码的创建者,但您并不拥有该库,也不能共享它。您很可能可以与其他人共享您的代码,但是发布可能会受到限制。

共享您的项目也意味着将它们放在其他人可以找到的地方。您可能希望将它们免费提供给任何人,或者您可能希望限制人们可以对您的项目做什么。幸运的是,有些网站可以很好地处理这两种情况。

那么,为什么要贡献你的项目呢?有很多原因,包括看到你的一个项目被其他人喜欢、使用和制作是一种很好的感觉。也许做出贡献的最重要的原因是帮助他人了解你所拥有的,或者更好地,了解如何避免陷阱或问题。这样,我们都可以通过学习最佳实践或更好的方法来实现我们的想法。最后,你自己的项目和经验,当被分享时,将会激励其他人去创造其他的项目,或者改进你的项目。

我在自己的项目中遇到过这种情况。人们把我做的东西拿来改进。因为他们反过来分享了他们的项目,所以我可以在我的项目中加入他们的很多改进,使它们比我想象的还要好。

哪个执照,在哪里?

那么,你怎么知道哪个许可证在起作用呢?所有拥有任何形式的源代码、文档、示例等的网站。,将有一个明确标明的许可证。它可能出现在屏幕底部或其他不连续的位置,甚至只出现在标有“许可证”或类似标签的特殊页面上。

例如,micropython.org 网站上的许可证位于标题“完全免费的开源软件”下,并引用了 MIT 对 MicroPython 核心的许可证,该许可证解释如下。您可以在个人、教育和商业产品中自由使用和改编 MicroPython。

类似地,Hackster.io 网站有一个部分,用于在有人上传项目时指定许可证。图 12-6 展示了他们网站上的一个 MicroPython 项目的例子。如您所见,许可证清楚地列在项目信息摘要中。事实上,您可以点击链接并阅读许可证。幸运的是,Hackster.io 和其他网站上的大多数项目都是开源的,在您的项目中使用它们应该是安全的(但还是要检查一下)。

A447395_1_En_12_Fig6_HTML.jpg

图 12-6。

Example Project Information from Hackster.io Tip

在发布之前,请务必检查您使用的任何示例的许可,以确保您不仅符合创作者的意愿,还符合所分配许可的法律限制。

现在,让我们来关注如何分享你的项目。

我们如何分享

你可能想知道为什么有人愿意免费提供他们工作了几个小时的东西。虽然期望是你应该与他人分享你的酷项目,但这并不是一个硬性规定。事实上,已经有一些人付费提供他们的项目,作为在商业渠道上销售 IOT 解决方案的前奏。然而,显然大多数爱好者免费分享他们的想法和项目。

有几个社区可以让你分享你的项目,我们将在下一节看到其中的一些。但是首先,有一些关于共享对象的事情你必须了解。信不信由你,如果你决定加入 IOT 社区或任何类似的社区,有一套你应该遵守的规则——有些是书面的,有些不是。下面列出了一些准则(规则),当你与社区分享你的想法、项目和评论时,你应该注意。

保持你的设计原创

没有人喜欢模仿者。当你 5 岁的时候,你不喜欢它,当你看到你设计并免费分享的东西被评为“本月最佳设计”时,你不会欣赏它。

因此,你必须做足功课,以确保你的设计是独一无二的。你不需要有目的地改变你的设计,使它不像其他人的,但你应该做尽职调查,至少搜索类似的项目。请记住,如果您开发一个类似的项目,这是可以的,但是简单地复制别人已经发表的东西通常是不好的形式(或者可能违反了许可)。

在极少数情况下,当你的项目与另一个项目几乎相同时,只要你的工作是你自己的,就不应该有问题。事实上,我也曾遇到过这种情况。另一位设计师和我的回应是,“很酷的项目。比如思想,嗯?”再说一次,这没有什么不对,只要你们双方都承认相似之处,并且没有许可问题。

如果像你一样的另一个项目确实是相同的设计,但授权不同,你可能必须与其他设计师谈判。当项目获得所有权许可(例如,商业地产)时,这种情况可能会发生,但考虑到大多数 IOT 样本库是人们免费共享其项目的站点,这种情况很少发生。

让我们看另一个非源代码的例子。十几个不同的覆盆子 Pi 盒子尺寸相似,有相同的端口开口,甚至以相同的方式组装(扣在一起)的可能性有多大?很有可能,是吗?这是否意味着有一个原件和 11 个副本?不,当然不是。我说的不是这个。

我所说的独特是指在这 12 个案例中,你应该能够找出它们之间的一些差异。它们是如何打印的(例如,在构建平台上的方向),它们是否由几个部分组成,它们是否设计有通风等。即使 12 个设计师都同时开始,也会有一些微小的差异。更重要的是,每个都是自己的作品。也就是说,没有人用别人的设计冒充自己的设计。

在软件项目的情况下,源代码很可能会在例子中有一点不同。虽然有多大的差异使源代码被认为是不同的是律师们要解决的事情,但可以说,如果你的代码和另一个代码几乎相同,但在不知道对方的情况下创建,只要没有许可冲突,共享你的代码是可以的。

最后,当您共享基于他人作品的项目时,您必须在代码、文档和项目网站上注明原设计者。也就是说,你明确地声明你的项目是原作的衍生。包含原始设计的链接以及修改列表也是很好的形式。同样,这是假设许可允许的。

检查执照

我已经在下载和使用示例项目方面提到了许可。回想一下,大多数存储库会要求您为您的项目指定一个许可证。这允许存储库托管您的项目,并向每个人传达您关于所有权、使用许可等的意图。

正如我之前所说的,在使用任何设计之前,您需要检查许可。如果打算修改,需要密切关注牌照。很明显,大多数许可证都允许你使用这个设计,并且允许你修改它。

然而,一些许可的不同之处在于修改的所有权。一些开源许可证,比如 GPL,允许修改,但是如果你打算发布这些修改,要求你将这些修改交给原始所有者(创建和许可它的个人或组织)。也就是说,您可以随意修改它以供个人使用,但是一旦您分发了这些更改,您必须将它们交给许可证的所有者。

我只遇到过几次,但在那些情况下,设计师是在为商业产品设计原型。许可证和实际上该项目的文本清楚地表明,她正在寻求设计方面的帮助,但该设计不会公开。小心这一点,小心行事。你做的任何工作都可能是为了所有者的利益,而不是你的利益。

Tip

如果对许可证有疑问,请联系创建者并直接询问他们。

由于大多数示例 IOT 项目都获得了共享和自由修改的许可,所以您通常不必太担心。但是,我建议您在使用任何项目之前检查一下许可证,尤其是如果您打算共享或发布您的派生。

保持适当

信不信由你,有些业余爱好者和爱好者有着令人印象深刻的生动想象力,他们提出了一些可能被一些人认为不合适甚至淫秽的 IOT 项目。不管你自己的观点是什么,你都应该努力包容别人的观点。这并不意味着你必须妥协自己的观点;只是要意识到你的可能会冒犯别人,并努力把冒犯降到最低。

更具体地说,不要把主题不合适的项目上传到任何人都可以看到的网站上。上传一些宣传某个主题、理念等的项目(可能)是可以的。(前提是没有侵犯版权);只是不要上传明显具有攻击性或意图造成伤害的项目或评论。

例如,如果你考虑到 IOT 项目在学校中被用来教授孩子们使用硬件和设计软件的技术和技巧,你就不应该上传带有家长可能认为不合适的主题的项目。最明显的当然是攻击性的语言、成人主题和诽谤性的图像。

作为 post-no-post 决策的一部分,您应该检查托管所选存储库的站点的使用情况和用户协议。确保你阅读了关于什么是合适的,什么是不合适的部分,并坚持这样做。

还有一个角度要考虑。您应该避免上传非法或可能非法的示例项目。考虑到 IOT 社区包括全球,这可能很难辨别。然而,大多数网站会有语言提示什么是允许的,什么是不允许的。有些网站在协议中规定他们(网站)有权删除他们认为不合适的内容。

例如,我曾经看到一个射频识别(RFID)阅读器的项目,可以用来从远处读取 RFID。这听起来无害,但是想想使用 RFID 的东西的数量,比如安全徽章、身份证明,甚至信用卡。显然,从你拥有的东西上读取 RFID 是没问题的(事实上,这就是该项目所展示的),但是该项目可能(并且很可能已经)被用于邪恶。幸运的是,其他人注意到了这一点,项目网站已经被删除(URL 导致 401 错误)。

因此,在你上传一个设计或样本项目之前,确保你理解并同意用户协议中关于什么是合适的,什么是不合适的条款。大多数情况下,误解不会给你带来麻烦,但是如果你不止一次地误解,网站上的人可能会想和你谈谈或者限制你的访问。这让我想起了这一部分的开头——一定要尊重他人的观点,尤其是网站的目标受众。如果你不同意,找另一个网站。

注释你的作品

我可以判断一个样本 IOT 项目是好的还是高质量的方法之一是它是如何被注释和记录的。也就是说,设计师在网站上对项目的描述有多好。如果我遇到一个看起来很吸引人的项目,却发现设计师没有费心描述如何连接硬件,或者用七个字(或更少)解释代码,没有提供任何说明,或者更糟的是没有提供任何实际实现的照片,我不会使用它。

因此,你应该努力提供尽可能完整的描述。你不必写一部小说、中篇小说或学位论文;但是你应该提供足够的信息来描述预期的用途;它解决什么问题;以及一组关于如何编写、编译和部署源代码的说明。

唯一的例外是,您仍在处理一个项目,或者您计划在最终确定之前进行更改。在这种情况下,你应该用一些关于正在进行的工作、正在实验等等的措辞来标记(注释)这个项目。如果您的存储库具有将项目标记为这样的特性,那么就使用它。这样,其他人就会知道你的项目还没有准备好被普遍采用。这样做的一个原因可能是为了获得他人的反馈。我自己也这样做过,结果喜忧参半。大多数人都乐于评论他们喜欢它,但是不要评论,或者即使他们评论了,不管多么令人鼓舞,也不要建议任何改变或改进。

我还建议您提供一些联系信息,以便其他有问题的人可以联系您。大多数网站让浏览者很容易通过网站与您联系,但您可能希望提供其他联系方式(如电子邮件)。您可能不想提供您的家庭地址和电话号码(不要这样做),但是电子邮件地址是让您向社区开放的一个好方法。

例如,我看到博客、示例项目甚至教程中,人们张贴了他们的 IRC 句柄、电子邮件地址,甚至有一次还张贴了他们的商业电话号码。虽然我可能不会走那么远,但我建议提供一个电子邮件地址,这样你就可以和喜欢这个项目的人交流了。另外,和某人一对一地讨论你的工作也很不错!

做一个好公民

假设您遇到一个样本项目,它不仅质量不高,而且(在您看来)设计或实现也不正确。你应该立即评论并通过轻率地评论他们的代码有多愚蠢来打击设计师的自尊心吗?不,当然不是!

我会做的(最有可能的)是完全忽略这个项目。我是说,为什么指出缺陷会让事情变得更糟?我发现整个社区(有一些例外)可能会做同样的事情而不发表评论。回想一下,决定一个项目是否设计良好的关键之一是有多少人使用它。通常,有一个计数器可以检查这一点。如果没有人喜欢它,甚至没有人下载它,你可以肯定它不会成为任何搜索列表或本月样本项目的榜首。

另一方面,如果你觉得有必要发表评论,一定要私下联系设计师,或者尽可能地表现出建设性。目标应该是帮助设计师改进他的项目,而不是挑战他们的智力(或骄傲)。

当我评论那些我觉得奇怪或者有缺陷的项目时(这种情况很少发生),我通常会以问题的形式表达我的意见。一个问题通常不会让某人处于守势,如果措辞恰当,也不会冒犯他人。

例如,我可能会问,“你有没有发现如果用户多次按下按钮,代码可能会挂起?”这是一个询问设计师是否在你认为会失败的条件下测试了他的项目的好方法。这是很好的、建设性的批评,是最理智的形式。我敢肯定,如果你考虑一下你要说的话,你可以找到其他更好的方式来帮助人们改进他们的项目。

既然我们已经了解了为什么要分享以及如何以负责任的方式分享,那么让我们来发现一些你可能想要加入或者至少想要监督的社区。

建议的社区

有相当多的通用 Python 和 MicroPython 网站和在线社区可以访问,甚至可以加入。大多数在线社区都有存储库,您可以在其中搜索示例、提示、技术,甚至可以探索完整的项目。大多数网站都有一个或多个区域,会员可以在这里发表评论、提出问题或与论坛上的其他人进行交流。您通常必须加入才能发布回复或提问,但通常任何人都可以查看。

使用这些资源的最好方法是定期访问它们。更具体地说,你应该定期阅读(你感兴趣的)文章和论坛帖子。这可以让你了解最新的时事、新技术,甚至是挑战性问题的新解决方案。表 12-1 给出了一个简短的在线资源列表,你应该考虑访问这些资源来了解关于 Python、MicroPython、硬件和 IOT 的最新消息。我列出了一般的主题、URL 和对每个主题的简要描述。然而,你既不是必须加入这些社区,也不是只有这些网站你可以或应该加入。在某些情况下,您可能只想定期监控站点。

表 12-1。

Online Resources for Python, MicroPython, IOT, and Hardware

| 主题 | 统一资源定位器 | 描述 | | --- | --- | --- | | MicroPython、Python 等等 | `hackster.io` | 各种项目的通用硬件和项目现场。在这里可以找到关于搜索 Python 或 MicroPython 的项目的详细解释 | | Pyboard,MicroPython | [`http://docs.micropython.org/en/latest/pyboard/`](http://docs.micropython.org/en/latest/pyboard/) | Pyboard 上的 MicroPython 文档 | | [`https://forum.micropython.org/`](https://forum.micropython.org/) | MicroPython 和 Pyboard 论坛 | | 威皮,MicroPython | [`https://docs.pycom.io/pycom_esp32/index.html`](https://docs.pycom.io/pycom_esp32/index.html) | WiPy 和其他 Pycom 板上的 MicroPython 文档 | | [`https://forum.pycom.io/`](https://forum.pycom.io/) | MicroPython 和 Pycom 板论坛 | | BBC micro:bit | [`https://microbit-micropython.readthedocs.io/en/latest/`](https://microbit-micropython.readthedocs.io/en/latest/) | BBC micro:bit 上的 MicroPython 文档 | | CircuitPython | [T0](https://learn.adafruit.com/search?q=circuitpython) | Adafruit 关于 CircuitPython 的教程 | | [`https://blog.adafruit.com/?s=circuitpython`](https://blog.adafruit.com/?s=circuitpython) | Adafruit 在 CircuitPython 上的博客 | | 计算机编程语言 | [`https://www.python.org/`](https://www.python.org/) | 常规 Python 信息 | | [`https://www.python.org/doc/`](https://www.python.org/doc/) | Python 文档 | | [`https://www.python.org/community/`](https://www.python.org/community/) | Python 论坛 |

请注意,这里有专门针对 Python、Raspberry Pi、通用硬件和类似资源的网站。虽然许多人并没有专门讨论或介绍 MicroPython,但是大多数人都有一个不断增长的知识库,即使是使用 MicroPython,这个知识库通常也非常有用。

例如,我尝试连接到 Raspberry Pi 网站,以了解那里正在发生的事情。通过查看为 Raspberry Pi 及其本机操作系统实现的项目,我经常可以获得 IOT 项目的想法或简单的功能想法。我发现经常有这样的情况,虽然源代码可能有很大的不同,但大多数硬件(连接等。)无需修改即可应用,这是有意义的,因为硬件与设备上运行的操作系统无关(但驱动硬件的库相关)。

另外,不要低估使用你最喜欢的在线搜索工具进行关键词搜索的能力。我经常发现一些晦涩难懂的信息,它们不会发布在更受欢迎的网站上。在大多数情况下,它们是写得很好的博客。在访问上面列出的网站之前,我通常会先进行关键字搜索。如果没有别的,它证实了我正在研究的东西是独特的还是普遍存在的。您还应该考虑使用错误消息中的关键短语作为搜索词来获得特定错误的帮助。

也有一些你应该考虑获得的优秀期刊。我排除了典型的 Windows、PC 和通用编程期刊,不是因为它们没有帮助,而是因为它们对于 IOT 相关的研究来说过于笼统。相反,以下是我发现对我的 IOT 研究或任何电子或业余爱好项目非常有帮助的期刊。

  • MagPi 杂志:致力于所有树莓派事物的月刊。包括许多关于示例项目的文章、关于 Raspberry Pi 的新闻、外围设备和一般硬件评论。(raspberrypi.org/magpi)
  • Make:一个致力于创客社区广阔领域的杂志,展示样本项目、教程、硬件、工具、无人机、机器人等等!这是一本真正的一站式期刊,涵盖了黑客、调整和一般 DIY 爱好者、爱好者和专业人士。( http://makezine.com/

现在我们已经看到了几个在线社区(但不是一个完整的列表,因为更多的社区似乎每周都在增加)以及一些你可以购买的期刊,让我们讨论一下一旦你成为 IOT 和 IOT 相关在线社区的一名有生产力的贡献成员,你可以采取的下一步行动——成为一名创造者。

成为一名创造者

从新手成长为爱好者(甚至是专业人士)的下一步是定期练习你的手艺,并与世界分享你的知识。实现这一愿望的一个绝佳途径是成为一名创造者。创客被广泛认为是他们感兴趣领域的专家。最棒的是,一个制造者的兴趣可能会因人而异。也就是说,成为一名创客并不是学习一套特定的技术(学术或其他)。这是一门手艺的练习。

什么是创客?

可悲的是,对于制造者是什么或者应该是什么,并没有单一的定义。这是因为创客是一个拥有高度创造性技能的人,他们渴望修补和从事各种项目,从喷火的机械雕塑到电子 gismos,再到回收材料以廉价建造物品的新方法。

事实上,一个制造者仅仅是一个渴望创造东西的工匠、手艺人、爱好者或爱好者。因此,制造商。因此,有许多种制造商。然而,将他们团结在一起的是愿意与他人分享他们的技术和技能。因此,要真正成为一名创客,你必须加入创客社区。

分享你的想法

你可以成为一名创造者,但对在线社区毫无贡献,尽管这很好,但如果你想帮助社区变得更强大,你应该更多地参与进来。最好的方法是加入一个或多个在线论坛并开始投稿。

这并不意味着你必须从发布一些非常酷和成功的 MicroPython 或 IOT 项目开始,这些项目配有获奖的文档和防弹代码,计算机科学教授有一天会用它们来教导年轻人模仿你的才华。当然,你可能会对此嗤之以鼻(甚至大笑),但我遇到过一些人,他们害怕发表任何东西,以免出错或被嘲笑不准确。不要担心这个-最好的贡献者是那些帮助你成长的人,你通过学习更好的技术来成长!

避免这种情况的最好方法是慢慢开始,先问自己的问题。你也可以从对你喜欢的想法和项目发表一些积极的评论开始。随着你越来越多地参与到主题和技巧中,你的知识会扩展到你可以开始回答别人的问题的程度。

如果您想更多地参与 MicroPython,我鼓励您考虑这种程度的参与。我相信与社区互动并获得乐于助人和分享想法的声誉是业余爱好者和爱好者的区别之一。

因此,如果你想成为一名创客,你应该与他人分享你的想法。

参加活动

另一个让你更多参与创客社区的方法是参加创客集会(makerfaire.com/)。这些活动在世界各地举行。这些活动允许制造者展示他们的创造,教导他人,并庆祝所有的创造者。查看 Maker Faire 网站,了解您附近的活动。

如果你住在大城市或附近,你可能会发现有 Python、Raspberry Pi、MySQL、Arduino 的本地用户组,甚至还有 maker 用户组。尝试搜索您所在地区的事件和团体,并找出它们在何时何地相遇。大多数团体都有一个开放的政策,邀请所有人参加他们的会议。然而,有些确实对特许会员收费,但这通常伴随着额外的好处,如使用工具、实验室和批量购买折扣。

你可能想知道创客活动如何帮助你完成 MicroPython 或 IOT 项目。我很高兴地告诉大家,出席人数非常多,很多与会者都对 IOT 甚至 Python 感兴趣!正如我们从本书其他章节中了解到的,我们在 Python 中可以学到的很多东西都转移到了 MicroPython 上。硬件和 IOT 也是如此。当然,你可能只会看到使用其他板(如 Raspberry Pi 或 Arduino)的 IOT 项目的演示,但是大多数硬件都可以在 MicroPython 中使用。如果没有别的,你将获得什么是可能的知识,你可以在你的下一个项目想法中利用它。

一旦你参加了一个活动——即使是一个与 MicroPython 无关的活动,比如 Raspberry Pi meetup,你也会被吸引住。通过这种方式,你可以从别人那里学到很多东西,谁知道呢,也许有一天你会在某个活动上展示自己。从我的经验来看,在一个开放的论坛上与他人分享你的知识是非常令人满意的,比如会议、用户组、meetup 或 Maker Faire。这时你知道你已经获得了一个典型的制造者所拥有的声誉、技能和知识。

Tip

许多 Raspberry Pi 用户在他们的项目中使用 Python。不要犹豫去寻找一个 Raspberry Pi meetup,因为你可以从 Raspberry Pi Python 开发者那里学到很多东西。

这并不意味着当你读到这一章的时候,你已经是 IOT 所有方面的专家,甚至自称是专家,但是你仍然应该在你的道路上走得很好。

摘要

将你的 IOT 技能提升到一个新的水平是大多数人最终会被激励去实现的。然而,即使你不想成为一个教授世界的旅行制造者,你也可以通过加入提供 Python、IOT 和开放硬件的在线社区学到很多东西。

在这一章中,我就如何最好地与在线社区互动,在哪里加入在线社区,甚至如何将你的技能发挥到最高热情水平并成为一名创造者提出了建议。因此,本章结束了我们在 MicroPython for 之路上的旅程,我希望它能激励您继续练习您的技能,并最终加入志同道合的爱好者社区。

我真诚地希望这本书已经为你打开了许多关于 Python、MicroPython、IOT 和任何开放硬件的大门。祝你的 IOT 项目一切顺利,如果不顺利,你会在这个过程中学到一些东西。只要记得与世界分享你的经历——好的或坏的——并从别人的努力中回报一点你能得到的。

Footnotes 1

这是我写这本书的主要动机之一。

posted @ 2024-08-09 17:40  绝不原创的飞龙  阅读(1)  评论(0编辑  收藏  举报