普惠课程

第一节:开启掌控板

认识掌控板:

1、掌控板功能介绍

掌控板是由创客教育专家委员会、猫友汇、广大一线老师共同提出需求并与创客教育行业优秀企业代表共同参与研发的教具、学具,是一块为教育而生的开源硬件,也是一个公益项目。 麻雀虽小五脏俱全,掌控板非常小巧,同时也搭载了高性能芯片,支持WiFi和蓝牙,配备OLED显示屏、RGB灯等多种传感器,包含触摸开关、金手指外部拓展接口。在不外接设备的情况下也能完成多种创意作品。

../../_images/157.png

2、掌控板能做什么

掌控板内集成了基本的传感器,OLED显示屏可以显示文字、数字、图案等。可以通过图形化编程或代码编程,结合按钮、声音、光线等完成很多有意思的项目。掌控板还可以通过扩展板连接各种传感器,包含100+种玩法,实现创意不是梦!是学校老师开展创新课程的好帮手。 掌控板可用于学习编程、智造万物、学习物联网及人工智能等。

../../_images/210.png

3、什么是百灵鸽

掌控板没有独立电源,需要通过数据线进行供电。百灵鸽是由N+实验室为掌控板研发的一款扩展板。它除了能给掌控板独立供电外,将掌控板的多数引脚引出,方便学习者外接其他传感器,实现自己的新奇想法。另外,加入了效果极好的扬声器、音频输出孔、BME280气象芯片等,使掌控板能实现更多功能。

../../_images/310.png ../../_images/410.png
认识mPython:

掌控板可以用mPython、Mind+、腾讯扣叮、Mixly等软件进行编程,这里我们采用mPython。

mPython是一款掌控板专用、易上手、可图形化编程的软件。mPython界面包含以下区域:菜单栏、模块区、编程区、仿真区、控制台。

../../_images/510.png

【菜单栏】:文件菜单中可以进行新建文件、打开文件、保存本地、保存代码库的操作;教程菜单中有帮助文档及精品课程可以参考;有普通模式和教学模式可自由切换;代码按钮可切换图形化编辑与代码编辑。

【模块区】:包含“输入”,“事件”,“显示”,“音乐”等类别,每个类别里面分为多种功能模块,按照特定逻辑组合这些功能模块,可以编写出各种功能的程序。

【编程区】:编写程序的区域。拖动模块区中的模块到编程区进行拼接即可。

【仿真区】:可以仿真显示代码块的运行结果。

Hello World:

Step1:在“显示”类别拖出两个文本显示文本模块和一个OLED显示生效模块。

../../_images/61.png

Step2:修改显示文本模块的x,y坐标,并编写内容。

../../_images/71.png

【代码】

from mpython import *
oled.DispChar('Hello, world!', 25, 16, 1)
oled.DispChar('你好世界', 34, 32, 1)
oled.show()

Step3: 点击右上角“刷入”,将程序刷入至掌控板运行,观察效果。

../../_images/81.png
点亮一盏灯:

Step1:在“RGB灯”类别拖出一个设置RGB灯颜色模块。

../../_images/91.png

Step2:点击红色可进入颜色选择界面。

../../_images/105.png

Step3:点击右上角“刷入”,将程序刷入至掌控板运行,观察效果。

../../_images/1110.png

Step4:在“循环”类别中找到等待1秒模块,拖出接至设置RGB灯颜色下面,再拖出一个设置RGB灯颜色模块,接至等待1秒模块下面,并修改成绿色。如下图所示。刷入运行,观察效果。

../../_images/1210.png

【代码】

from mpython import *
import time
rgb[0] = (int(255), int(0), int(0))
rgb.write()
time.sleep_ms(1)
time.sleep(1)
rgb[0] = (int(170), int(0), int(0))
rgb.write()
time.sleep_ms(1)

我们会发现RGB灯会先亮红灯,1秒后改亮绿灯。这与我们程序从上往下的顺序是一致的。说明掌控板在执行多行指令时,是按从上往下的顺序执行。

播放一首歌:

Step1:在“音乐”类别拖出一个播放音乐模块。

../../_images/1310.png

【代码】

from mpython import *
import music
music.play(music.DADADADUM, wait=False, loop=False)

Step2:点击音乐名称可进入音乐选择界面。

../../_images/1410.png

Step3: 点击右上角“刷入”,将程序刷入至掌控板运行,观察效果。

练一练:

结合多个播放音乐模块与等待1秒模块,让掌控板按顺序播放多首歌曲。

第二节:奔跑的表情包

项目预览:

开启掌控板之后,屏幕上会显示一个图像,随后会清空,显示第二个图像。这两个图像会反复切换,达到一个动态图像的效果。同时图像还会不知疲倦地进行穿梭。

../../_images/1510.png
项目制作:

【显示内置图像】

Step1:在“显示”类别中拖出显示内置图像模块x1、OLED显示生效模块x1,组合。点击“心形”可选择更改图像。

../../_images/161.png

Step2:点击右上角“刷入”,将程序刷入至掌控板运行,观察效果。

../../_images/171.png

Step3:显示第二个图像,OLED屏幕显示就像我们在黑板上写字一样,有时候要把黑板擦干净,才好继续写字。在“显示”类别中拖出显示清空模块x1、显示内置图像模块x1、OLED显示生效模块x1;在“循环”类别中拖出等待模块x1。我们刚刚的程序上丰富:显示第一个图像,等待1秒,清空,显示第二个图像。 按照逻辑顺序,从上往下拼接模块(图2-4),将程序刷入至掌控板运行,观察效果。

../../_images/182.png

Step4:反复切换显示第一个图像与第二个图像,第二个图像切换回第一个图像也需要等待1秒,所以还需要一个等待模块。 从“循环”类别中拖出一直重复执行模块x1。(注意:切换回第一个图像时,不要忘记“擦黑板”。)

../../_images/192.png

【改变图像位置】

Step1:学习X坐标。

X坐标,也称横坐标,用来表示图像或文字在屏幕上左右方向的位置。就好比电影票上“3排06座”的“06座”。

掌控板屏幕的总宽度是128,X坐标起点是0,从左往右递增,所以X坐标的变化范围是0~127。

../../_images/202.png

Step2:在屏幕其他位置显示图像,通过更改X坐标,改变图像显示的位置。

../../_images/212.png

Step3:图像在屏幕上“滑动”

通过不断更改X坐标,让图像从屏幕左边开始,向右“滑动”。

因为X坐标不断在变化,这次我们用一个变量来表示X坐标。

变量:从字面上理解就是可以变化的量(数据)。坐标是不断变化的量,我们给变量取个名字,例如i,以后需要获取坐标时,通过名字 i 即可找到相对应的数据。(使用变量前要先给变量赋值)

在“变量”类别中点击创建变量,在弹出的窗口中输入变量名字,例如i。

../../_images/222.png

在“变量”类别中拖出变量i模块,放入X坐标中,同时设置好i的初识置,以及变化规律(增大图像往右移动,减小图像往左移动),

../../_images/232.png

Step4:拽回狂奔的图像

掌控板规定,以图像左上角的顶点坐标表示整个图像坐标(一个图像由多个点组成)。掌控板屏幕内X坐标的范围是0~127,。所以每次增加了i的值后,要进行判断,判断i是否还在合适的范围内,如果超出范围,要重新设定i的值。判断模块“如果…执行…”在“逻辑”类别中可以找到。

../../_images/242.png

【代码】

from mpython import *
import time
image_picture = Image()
i = 0
while True:
    i = i + 5
    if i > 127:
        i = 0
    oled.fill(0)
    oled.blit(image_picture.load('face/1.pbm', 0), i, 0)
    oled.show()
    time.sleep(0.2)
    oled.fill(0)
    oled.blit(image_picture.load('face/2.pbm', 0), i, 0)
    oled.show()
    time.sleep(0.2)
项目扩展:

1、更换其他图像。 2、更改等待时间,调整动画速率。 3、更改i的增加量,调整移动速率。 4、你的创意……

第三节:特制贺卡

项目预览:

开启掌控板之后,屏幕上会显示两行文字,并且RGB一边闪烁一边切换颜色。

../../_images/251.png
项目制作:

【闪烁的RGB灯】

Step1:认识RGB灯,掌控板上集成了3盏RGB灯,可通过三原色不同亮度组合,构成五彩斑斓的颜色。

../../_images/262.png

Step2:点亮RGB灯的2种方法,在“RGB灯”类别中拖出设置RGB灯颜色模块。

../../_images/272.png

刷入运行,可观察到掌控板上左边第一个灯亮红色。

点击模块中的红色方块,可更换其他显色。

../../_images/282.png

删除这个模块,在“RGB灯”类别中拖出另一个设置RGB灯颜色模块。

../../_images/292.png

R、G、B指的是红色(Red)、绿色(Green)、蓝色(Blue)。后面的数值代表是三种颜色的亮度,取值范围是0~255,数值越大,亮度越亮。

通过R、G、B三种颜色不同亮度的混合,可以覆盖我们视力所能感知的所有颜色。这就是三原色原理。

../../_images/301.png

修改数值并刷入程序:R=255,G=0,B=0,可以看到红色全亮度;R=255,G=255,B=0,可以看到黄色全亮度;R=0,G=0,B=127,可以看到蓝色一半亮度。

../../_images/312.png

Step3:奇怪的灯序,设置RGB颜色模块的左边,可以选择单独点亮一盏灯或者所有灯。

../../_images/322.png

为什么灯序是0、1、2,而不是1、2、3呢?

计算机内部是使用二进制的数制,用0 和 1 两个数码来表示的数。它的基数为 2 ,进位规则是“逢二进一”。

../../_images/332.png

二进制只有 0 和 1 两个数字,在计算机的角度,计算起来更方便,计算速度更快,而且能与十进制互相转化。所以在计算机中,与排序有关的序号,都是从“0”开始的。

Step4:闪烁的RGB灯,我们已经成功地点亮一盏或多盏RGB灯了,那么怎么关闭RGB灯呢?

在“RGB灯”类别中拖出一个关闭RGB灯模块。

../../_images/342.png

同样地,可以选择单独关闭一盏RGB灯或全部RGB灯。我们加上等待模块和重复执行模块,为电子贺卡做出闪烁RGB灯的效果。

../../_images/352.png

【显示文字祝福语】

Step1:显示文本的2种方法,在“显示”类别中拖出显示文本模块。

../../_images/362.png

双引号内的内容即需要显示的文本,可以自行修改。左边可以选择在第几行显示。搭配显示清空和显示生效即可让屏幕显示预设的文本。

../../_images/371.png

把上面的代码删除,再从在“显示”类别中拖出另一个显示文本模块。

../../_images/382.png

这里的x坐标功能与显示内置图像的x坐标功能一致,用来调整文本显示的水平位置,x坐标的大小与水平位置的关系依旧是“左小右大”。

类似的,y坐标表示文本的垂直坐标,屏幕的高度是68,所以y坐标的取值范围是0~67。y坐标的大小与垂直位置的关系是“上小下大”。

../../_images/392.png

一个汉字的高度是16,所以1、2、3、4行的y坐标分别是0,16,32,48。

Step2:掌控板的文字的尺寸。

每个中文字符占12x16个像素,中文字符指中文输入法下的文字、标点符号等;

每个英文字符占6x16个像素,英文字符指英文输入法下的字母、标点符号等;

数字及数学运算符号(+、-、*、/等)占8x16个像素;

每个字符的坐标值是指组成该字符的左上角第一个像素点位置。

../../_images/402.png

Step3:在屏幕其他位置显示文本。

在“显示”类别中拖出显示清空模块x1,显示文本模块x2,显示生效模块x1,将代码进行拼接,并修改坐标及文本。

../../_images/412.png

这样,我们送给老师的一份精美的电子贺卡就完成了!

【代码】

from mpython import *
import time
oled.fill(0)
oled.DispChar('老师,我想对你说', 16, 16, 1)
oled.DispChar('您辛苦了!', 40, 32, 1)
oled.show()
while True:
    rgb.fill((int(255), int(0), int(0)))
    rgb.write()
    time.sleep_ms(1)
    time.sleep(1)
    rgb.fill( (0, 0, 0) )
    rgb.write()
    time.sleep_ms(1)
    time.sleep(1)
    rgb.fill((int(136), int(0), int(0)))
    rgb.write()
    time.sleep_ms(1)
    time.sleep(1)
    rgb.fill( (0, 0, 0) )
    rgb.write()
    time.sleep_ms(1)
    time.sleep(1)
项目扩展:

1、修改文本内容:XX,我想对你说…… 2、添加多种灯色 3、修改灯闪烁的频率 4、你的创意……

第四节:彩虹灯

项目预览:掌控板一开机,连接百灵鸽的灯带居然能亮起来,而且可以亮各种各样的颜色,还能做成呼吸灯、流水灯等各种花样。
../../_images/422.png
项目制作:

【百灵鸽扩展口介绍】

人生总有遗憾,就像掌控板上的灯数量有限,但是因为有了百灵鸽的扩展口,让掌控板能够连接上灯带。

扩展口的定义是:主板(掌控板)上用于连接各种外部设备的接口。通过这些扩展接口,可以把各种各样的传感器连接上掌控板,实现数据的输入、处理、输出。

(信号的输出、处理、输出的比喻:输出相当于人的眼睛、鼻子、耳朵等感受器官;处理相当于人的大脑,进行思考;输出相当于人的手、脚、嘴巴等)。

百灵鸽扩展口上有多个引脚(P0、P1、P2……),每个引脚接一个设备。(可进行小游戏互动:老师扮演输入,两位学生分别扮演掌控板和输出,体验输入、处理、输出的关系。例如:老师:天气好冷。学生A思考如何保暖,告诉学生B:穿衣服,学生B模拟穿衣服的动作。)

【旋转彩虹灯】

Step1:介绍灯带。

灯带是指把RGB灯用特殊的加工工艺焊接在铜线或者带状柔性线路板上面,再连接上电源发光,因其发光时形状如一条光带而得名。而且每盏灯都能够亮各种的颜色。所以用于装饰的效果特别好。我们可以通过编程,让灯带随心所欲地亮起我们想要的颜色。

接线方式:每个引脚有黑(G)、红(V)、黄(S)三个接口,灯带上也有黑(G)、红(V)、黄(S)三个接口,颜色对应相接即可。

Step2:点亮灯带。打开mPython,新建文件。得到空白作品。电机模块区的扩展——添加。

../../_images/432.png

加载Neopixel扩展

../../_images/442.png

里面包含了使用灯带时需要使用的模块。

../../_images/452.png

拖出第一个模块。

../../_images/462.png

跟掌控板上的灯不一样,外接灯带需要先初始化。确定灯带的名称,引脚和数量。因为接下来是根据灯带的名字进行编程的。

拖出以下模块,选好引脚,尝试刷入运行。

../../_images/472.png

需要设置生效才能让灯带亮起。

试一试:让灯带全亮。

参考程序:

../../_images/482.png

Step3:体验彩虹灯。如果想让灯带每个灯显示不同颜色,但是灯带数量有很多,应该怎么办?

../../_images/491.png

使用以下模块即可。

../../_images/501.png

刷入运行,观察结果。该模块能让灯带呈现出彩虹灯效。以红色灯开头紫色灯结尾。

试一试:将数量从10改为7会怎样?5呢?

Step4:彩虹灯的偏移。试一试:将偏移改为1,运行并观察结果。

../../_images/511.png

改为2呢?

偏移是指灯带从几号灯为起点。如果偏移是1,就是从1号灯开始亮,最后以0号灯结尾。

思考:偏移慢慢从0增大,会呈现什么效果呢?!

Step5:学习for循环,制作旋转彩虹灯。接下来实现将偏移从0增大到灯带数,在软件循环分类中拖出遍历循环模块(for循环)

../../_images/521.png

该模块有起点,终点,和步长三个参数来决定循环执行的次数。变量i由起点增加到终点,增幅就是步长。每增加一次,就要执行循环体一遍,直到i到达终点才会跳出循坏执行之后的程序。刷入运行以下程序,体验遍历循环的机制。

../../_images/531.png

【代码】

from mpython import *
import neopixel
import time
my_rgb = neopixel.NeoPixel(Pin(Pin.P13), n=10, bpp=3, timing=1)
def make_rainbow(_neopixel, _num, _bright, _offset):
    _rgb = ((255,0,0), (255,127,0), (255,255,0), (0,255,0), (0,255,255), (0,0,255), (136,0,255), (255,0,0))
    for i in range(_num):
        t = 7 * i / _num
        t0 = int(t)
        r = round((_rgb[t0][0] + (t-t0)*(_rgb[t0+1][0]-_rgb[t0][0]))*_bright)>>8
        g = round((_rgb[t0][1] + (t-t0)*(_rgb[t0+1][1]-_rgb[t0][1]))*_bright)>>8
        b = round((_rgb[t0][2] + (t-t0)*(_rgb[t0+1][2]-_rgb[t0][2]))*_bright)>>8
        _neopixel[(i + _offset) % _num] = (r, g, b)

for i in range(1, 11):
    make_rainbow(my_rgb, 10, 50, i)
    my_rgb.write()
    time.sleep(0.5)

试一试:(1)将遍历循环的终点由10改为20;(2)将步长由1改成2。

【RGB灯和呼吸灯】

Step1:认识RGB灯。

RGB灯是在一个物理封装里面,有R(红)、G(绿)、B(蓝)三种颜色的LED灯,这三个灯非常靠近,从远处看可以当作一个三色的发光点。在掌控板上有自带的三个RGB彩灯(即板载RGB灯),但是位置固定,不能延伸出来。所以我也可以考虑使用外接的RGB灯。

使用方法与灯带一样,只需要把RGB彩灯看做灯数只有一的灯带。

Step2:点亮RGB灯。

跟灯带一样,点亮RGB灯需要先初始化。先将刚刚的代码保存,然后新建文件。根据刚刚所学的灯带知识,尝试将RGB灯点亮成红色。

../../_images/541.png

Step3:应用for循环制作呼吸灯(红色为例)。

呼吸灯是什么?呼吸灯是指灯光在微电脑的控制之下完成由暗到亮再由亮到暗的逐渐变化,感觉好像是人在呼吸。呼吸灯中,RGB灯是什么在发生改变? 是亮度。

所以制作呼吸灯需要亮度从0(最暗)逐渐增加到255(最亮),再从255减小到0。我们可以把它看成两部分:(1)RGB灯从暗到亮;(2)RGB灯从亮到暗。先来完成RGB灯从暗到亮。即RGB的红色亮度从0逐渐增加到255,绿色和蓝色一直都是0不变。 当一个参数从某个数值过度到另一个数值时,可以考虑使用刚刚所学的for循环。

../../_images/551.png

里面填什么参数?(0,255,1)

刷入运行后发现灯亮的太快,效果不明显,所以增加一个等待模块。

../../_images/561.png

以上可以实现从暗到亮,学生自己尝试从亮到暗的部分。

../../_images/571.png

可以看到,for循环不单单可以从小到大增长,也可以倒过来从大到小增长。

总体效果:

../../_images/581.png

【代码】

from mpython import *
import neopixel
import time
my_rgb = neopixel.NeoPixel(Pin(Pin.P13), n=1, bpp=3, timing=1)
while True:
    for i in range(256):
        my_rgb[0] = (i, 0, 0)
        my_rgb.write()
        time.sleep_ms(5)
    for i in range(255, -1, -1):
        my_rgb[0] = (i, 0, 0)
        my_rgb.write()
        time.sleep_ms(5)
项目扩展:

1、如何改变呼吸的频率 2、你的创意……

第五节:双人抢答器

项目预览:掌控板开机之后,等待主持人念题。然后A或者B同学就要看谁的手速更快,先按下自己的按键,让自己的灯先亮啦。
../../_images/591.png
项目制作:

【IPO和信号类型】

IPO是指一个项目结构中的输入(Input)、加工(Processing)、输出(Output)。在掌控板的作品中,掌控板一般充当着加工(Processing)的角色,因为掌控板需要处理输入角色传来的信号(信息),然后将处理之后的信号传达到输出角色中。通俗地说,将外界信息以信号形式给予掌控板的传感器是输入,受掌控板的信号控制的传感器是输出。

按键将按键的状态传输给掌控板,所以是输入角色。

【掌控板上的AB键】

按钮是一种输入设备。它具有按下和抬起两种状态,按下时处于低电位,抬起时处于高电位。默认是处于抬起状态,也就是高电位。

在掌控板上已经有A和B两个按键了,我们先来学习使用掌控板上的按键,在学习如何使用外接的按键。

我们先实现利用按键控制一个灯。

Step1:A键点亮灯。打开mPython,新建一个文件,在输入分类拖出按键A被按下模块。

../../_images/60.png

该模块能根据A键的状态返回一个布尔值(真或者假),我们可以根据这个值对RGB灯进行点亮或者熄灭的操作。

../../_images/611.png

Step2:B键熄灭灯。加上代码,达到按下A键灯亮,按下B键熄灭的效果。

../../_images/62.png

【外接按键的使用】

Step1:按钮的连接。

复习:使用3PIN杜邦线将按钮和百灵鸽连接起来。引脚分别为P0,P1。(杜邦线颜色要对应:黄色代表输出、红色代表正极、黑色代表负极。)

Step2:获取外接按钮信号。

在高级的引脚分类中,我们可以看到很多模块。由于按键是输入,所以需要读取它的数值,但是这里面有两个读取的模块。

../../_images/63.png

Step3:数字信号和模拟信号。

设备可以按照输入输出设备分类,其产生的信号还可以根据信号类型分为数字信号和模拟信号。

数字信号:只有两种情况,用0和1代表,例子:按钮、单色LED灯;

模拟信号:有多种情况,用0~4095范围内的数字表示,例子:温湿度传感器、火焰传感器。

../../_images/64.png

Step4:按键控制RGB灯。按键的输入信号属于数字信号,我们使用以下模块读取它的信号值。

../../_images/65.png

该模块是读取特定引脚的输入信号(数字值,0(按下)或者1(没按下)),点击P0可以选择其他引脚。本例选择P0。

将刚刚的“按键按下”替换成引脚数字值的判断即可。

../../_images/66.png

【双人抢答器】

Step1:项目解析。

抢答器需要两位选手一人一个按键和一个代表自己的灯。我们使用掌控板上的0号灯和1号灯表示。P0按键控制0号灯,P1按键控制2号灯。我们通过观察谁的灯先亮起判断谁抢答成功。

Step2:P0按键控制0号灯。

因为P0按键只有两种状态:0(按下)和1(松开)。我们可以通过分支结构判断P0的数字值是0还是1来让0号灯亮还是灭。

../../_images/67.png

分支结构:顺序结构的程序虽然能解决计算、输出等问题,但不能做判断再选择。对于要先做判断再选择的问题就要使用分支结构。分支结构的执行是依据一定的条件选择执行路径,而不是严格按照语句出现的物理顺序。

../../_images/68.png

完善模块,刷入运行,观察效果。

../../_images/69.png

Step3:P1按键的控制。在step1的基础上,加上P1按键控制2号灯的程序。

../../_images/70.png

【代码】

from mpython import *
import time
p0 = MPythonPin(0, PinMode.IN)
p1 = MPythonPin(1, PinMode.IN)
while True:
    if p0.read_digital() == 0:
        rgb[0] = (int(255), int(0), int(0))
        rgb.write()
        time.sleep_ms(1)
    else:
        rgb[0] = (0, 0, 0)
        rgb.write()
        time.sleep_ms(1)
    if p1.read_digital() == 0:
        rgb[2] = (int(255), int(0), int(0))
        rgb.write()
        time.sleep_ms(1)
    else:
        rgb[2] = (0, 0, 0)
        rgb.write()
        time.sleep_ms(1)
项目扩展:

1、按下按键的时候能在屏幕上显示某些文字内容吗? 2、将板载RGB灯换成外接的RGB灯 3、那么能不能在一个灯亮起之后,另一个灯就算按下也不会亮呢? 4、你的创意……

第六节:爱心传递

项目预览:A同学和B同学手上各有一块掌控板,A同学掌控板上有一颗爱心,只要按下P键就能把爱心传递给B同学。B同学的掌控板上会慢慢从左侧滑出一颗爱心。
../../_images/711.png
项目制作:

【按键显示爱心图片】

屏幕下方的Python六个金色的字母是干嘛的呢?它们是触摸按键,虽然都是按键,跟AB按键相比,更加灵敏和方便。

P键显示心形,N键清除屏幕。

Step1:触摸按键模块。打开mPython,在输入分类拖出以下模块。

../../_images/72.png

该模块能根据P键的状态返回一个布尔值(真或者假),我们可以根据这个值进行下一步操作。点击“P”可以选择其他触摸按键。

../../_images/73.png

Step2:显示心形。按下P键显示心形,N键清除图片。

../../_images/74.png

发射爱心,触摸Y键,让爱心向右移动,直到超出屏幕。

../../_images/75.png

爱心在中央的X坐标是32,掌控板屏幕最右边X坐标是127,爱心完全超出屏幕需要X坐标达到128。所以爱心X坐标的变化范围是32~128。知道起点和终点,我们可以使用for循环来实现。

../../_images/76.png

其实我们都知道,爱心不可能真的从A掌控板穿越到B掌控板。其中的原理是,当A爱心移动结束后,A掌控板发送消息,告诉B掌控板:该你登场了,B掌控板就控制B爱心开始移动。A掌控板和B掌控板的通讯,利用的是无线广播。掌控板上自带无线广播模块,接下来我们学习如何使用无线广播

【无线广播】

Step1:无线广播介绍。

广播是指通过无线电波或导线传送声音的新闻传播工具。通过无线电波传送节目信号的称无线广播,通过导线传送节目信号的称有线广播。广播诞生于20世纪20年代。广播的优势是对象广泛,传播迅速,功能多样,感染力强;劣势是一瞬即逝,顺序收听,不能选择,语言不通则收听困难。广播存在我们身边的很多地方,例如电台信号,电视节目等等。

Step2:开启无线广播。

掌控板也不是无时无刻打开广播开关的,所以需要先将无线广播功能打开。打开mPython。得到空白作品。在广播分类找到开关模块:

../../_images/77.png

选择频道:无线广播的发送和接受需要选择自己所属的频道,就好像接力赛上的跑道一样,运动员只能在自己的跑道上奔跑,接力棒也只能又同一跑道上的人接收。

但是跟跑道只有4条不一样,掌控板可用的无线广播有13个频道,分别为1~13。所以如果两块掌控板之间如果想要接收消息,必须在一开始选择好相同的频道。(备注:这阶段可以每两个人分一组,自己商量相同的频道。以便接下来的课堂教学的开展)

../../_images/78.png

Step3:消息的发送和接收。观察广播分类,可以看到掌控板既可以消息,也可以接收消息。

../../_images/79.png

发送消息:拖出发送模块,使用P和N触摸键发送开灯和关灯的广播,刷入运行。

../../_images/80.png

消息名称需要区分,自己决定,这里以掌控板1发送开灯1和关灯1,掌控板2发送开灯2和关灯2。

现在掌控板已经能发出消息了,但是我们还没有编写掌控板收到消息的程序。

接收消息:我们需要替掌控板决定收到消息后做什么事情。

在上面程序里,加上两个接收消息模块。

../../_images/811.png

完善程序,刷入运行。

../../_images/82.png

现在使用第二块掌控板,以上面的程序为参考。达到两块掌控板能控制对方开关灯的效果。

../../_images/83.png

可以发现接收广播消息有2个模块,这两个模块有什么区别呢?

左边是接收到特定的消息,才会执行指令;右边是收到消息就会执行指令。

../../_images/84.png

接下来我们使用右边的模块,让掌控板把接收到的指令显示出来,当收到无线广播消息时,会把消息存到_msg这个变量中,直接把这个变量显示出来就可以了。

../../_images/85.png

还可以同时编写好发送和接收的程序,做出两个掌控板聊天的效果。

A掌控板:

../../_images/86.png

B掌控板:

../../_images/87.png

Step4:爱心传递。 一个人扮演发送消息的角色,另一个扮演接收的角色。双方使用相同频道以及约定好的信号名称。

>>>发送方:

先在屏幕上显示心形:

../../_images/88.png

按下P键后,心形向右边滑走,滑出屏幕后发送广播。

../../_images/89.png

X坐标逐渐增大。

../../_images/90.png

【代码】

from mpython import *
import radio
image_picture = Image()
radio.on()
radio.config(channel=13)
oled.fill(0)
oled.blit(image_picture.load('face/1.pbm', 0), 32, 0)
oled.show()
while True:
    if touchPad_P.read() < 400:
        for i in range(32, 129, 3):
            oled.fill(0)
            oled.blit(image_picture.load('face/1.pbm', 0), i, 0)
            oled.show()
        radio.send('走你')

>>>接收方:

接收方不用发送消息,只需要编写接收消息部分。

../../_images/911.png

收到消息后,心形从左边滑向右边直到屏幕中间。

../../_images/92.png

然后心形不断大小切换,就像心在跳动一样。

../../_images/93.png

【代码】

from mpython import *
import radio
from machine import Timer
import time
image_picture = Image()
import ubinascii
_radio_msg_list = []
tim13 = Timer(13)
tim13.init(period=20, mode=Timer.PERIODIC, callback=timer13_tick)

radio.on()
radio.config(channel=13)

_radio_msg_list.append('走你')

def radio_callback(_msg):
    global _radio_msg_list
    try: radio_recv(_msg)
    except: pass
    if _msg in _radio_msg_list:
        eval('radio_recv_' + bytes.decode(ubinascii.hexlify(_msg)) + '()')

def timer13_tick(_):
    _msg = radio.receive()
    if not _msg: return
    radio_callback(_msg)

def radio_recv_e8b5b0e4bda0():
    global i
    for i in range(-64, 33, 3):
        oled.fill(0)
        oled.blit(image_picture.load('face/1.pbm', 0), i, 0)
        oled.show()
        time.sleep_ms(20)
    while True:
        oled.blit(image_picture.load('face/1.pbm', 0), i, 0)
        oled.show()
        time.sleep_ms(200)
        oled.blit(image_picture.load('face/2.pbm', 0), i, 0)
        oled.show()
        time.sleep_ms(200)
项目扩展:

1、发送其他的消息 2、你的创意……

第七节:我是歌手

项目预览:

开启掌控板之后,屏幕上会显示当前歌曲名称并播放,触摸键可以切换不同歌曲。

../../_images/94.png
项目制作:

【掌控板蜂鸣器】

Step1:认识蜂鸣器。

掌控板板载无源蜂鸣器,其声音主要是通过高低不同的脉冲信号来控制而产生。声音频率可控,频率不同,发出的音调就不一样,从而可以发出不同的声音,还可以做出“多来米发索拉西”的效果。

../../_images/95.png

Step2:播放内置音乐。在“音乐”类别中拖出播放音乐模块(含等待、循环)。刷入运行,掌控板会发出一段音符组成的音乐。

../../_images/96.png

>>>等待:只播放音乐,音乐结束后,再往下执行指令。

以下程序的运行效果:音乐结束后,所有RGB灯亮1秒后熄灭。

../../_images/97.png

以下程序的运行效果:音乐开始播放的同时,所有RGB灯亮起,1秒后,灯熄灭。

../../_images/98.png

>>>循环:音乐结束后,重复播放。

以下程序的运行效果:音乐重复播放,同时RGB灯闪烁。(注意:等待和循环不要同时选上,否则程序会在播放音乐中不断循环,无法执行其他指令。)

../../_images/99.png

怎么让音乐停下来呢?从“音乐”类别中拖出“停止播放音乐”模块,加上触发事件即可。

../../_images/100.png

【网络MP3】

Step1:连接互联网。从“WI-FI”类别中拖出连接WI-FI模块,修改好WI-FI名称和密码,测试掌控板连接WI-FI的功能。

../../_images/1012.png

当掌控板成功连接上WI-FI后,会在屏幕上显示其IP地址,IP地址:每一个设备接入互联网的标识,相当于人的身份证号码。

../../_images/1021.png

此外,控制台也会显示WIFI连接信息。

../../_images/1031.png

Step2:让百灵鸽唱起来。“扩展”→“添加”→左侧“应用扩展”→“音频”,选择加载“音频类别”。

../../_images/1041.png

从“音频”类别中拖出“音频初始化”、“设音频音量”、“音频播放”。按图7-12所示编程并刷入,可从百灵鸽听到动听的歌声。

../../_images/1051.png

从“输入”类别中拖出“当触摸键被触摸”模块,可实现音乐的停止与播放。

../../_images/1061.png

运行效果如下图。

../../_images/1071.png

Step3:制作专属MP3。

如何让百灵鸽播放自己喜欢的歌曲呢?并不是任意一个链接都可以播放的。能够成功播放的链接后缀必须是.mp3。在这里介绍一种方法让百灵鸽“唱”你所想。

①获取网络mp3链接:http://www.170mv.com/tool/music/

../../_images/1081.png ../../_images/1091.png

②运用音频模块播放(备注:此网站音乐链接可能会失效,失效后需要重新搜索并复制粘贴mp3链接)

../../_images/1101.png

③利用触摸按键播放多首歌曲。

../../_images/1111.png

以此类推,可用P、Y、T、H、O分别播放不同的歌曲,N键停止。

【代码】

from mpython import *
import network
my_wifi = wifi()
my_wifi.connectWiFi('my_wifi', '1234')
import audio
from machine import Timer

import gc;gc.collect()
audio.player_init(i2c)
audio.set_volume(100)

_status_p = _status_y = _status_t = _status_h = _status_o = _status_n = 0
def on_touchpad_P_pressed():pass
def on_touchpad_P_unpressed():pass
def on_touchpad_Y_pressed():pass
def on_touchpad_Y_unpressed():pass
def on_touchpad_T_pressed():pass
def on_touchpad_T_unpressed():pass
def on_touchpad_H_pressed():pass
def on_touchpad_H_unpressed():pass
def on_touchpad_O_pressed():pass
def on_touchpad_O_unpressed():pass
def on_touchpad_N_pressed():pass
def on_touchpad_N_unpressed():pass

tim12 = Timer(12)
tim12.init(period=100, mode=Timer.PERIODIC, callback=timer12_tick)

def timer12_tick(_):
    global _status_p, _status_y, _status_t, _status_h, _status_o, _status_n
    try:
        touchPad_P.read();pass
    except:
        return
    if touchPad_P.read() < 400:
        if 1 != _status_p:_status_p = 1;on_touchpad_P_pressed()
    elif 0 != _status_p:_status_p = 0;on_touchpad_P_unpressed()
    if touchPad_Y.read() < 400:
        if 1 != _status_y:_status_y = 1;on_touchpad_Y_pressed()
    elif 0 != _status_y:_status_y = 0;on_touchpad_Y_unpressed()
    if touchPad_T.read() < 400:
        if 1 != _status_t:_status_t = 1;on_touchpad_T_pressed()
    elif 0 != _status_t:_status_t = 0;on_touchpad_T_unpressed()
    if touchPad_H.read() < 400:
        if 1 != _status_h:_status_h = 1;on_touchpad_H_pressed()
    elif 0 != _status_h:_status_h = 0;on_touchpad_H_unpressed()
    if touchPad_O.read() < 400:
        if 1 != _status_o:_status_o = 1;on_touchpad_O_pressed()
    elif 0 != _status_o:_status_o = 0;on_touchpad_O_unpressed()
    if touchPad_N.read() < 400:
        if 1 != _status_n:_status_n = 1;on_touchpad_N_pressed()
    elif 0 != _status_n:_status_n = 0;on_touchpad_N_unpressed()

def on_touchpad_Y_pressed():
    global i
    audio.stop()
    audio.play('歌曲2链接')
    oled.fill_rect(0, 0, 128, 16, 0)
    oled.DispChar('歌名2', 32, 32, 1)
    oled.show()

def on_touchpad_P_pressed():
    global i
    audio.stop()
    audio.play('歌曲1链接')
    oled.fill_rect(0, 0, 128, 16, 0)
    oled.DispChar('歌名', 32, 32, 1)
    oled.show()
项目扩展:

1、思考:为什么播放歌曲前要加一个停止? 2、A/B键控制音量增/减。 3、装饰屏幕显示 4、你的创意……

第八节:入门播报器

项目预览:

按下掌控板开关后,。屏幕上显示超声波传感器的数值。当有人走进门口时,掌控板会发出声音:“欢迎光临”,提醒店员有客人来了。

../../_images/1121.png
项目制作:

【超声波传感器】

Step1:超声波传感器简介。 超声波传感器是将超声波信号转换成其他能量信号(通常是电信号)的传感器。超声波传感器是一种输入设备。它具有发射和接收超声波的探头,超声波碰到杂质或分界面会产生显著反射形成反射回波。根据发射和接收的时间差计算出前方物体的距离。

../../_images/1131.png

应用:超声波距离传感器可以广泛应用在物位(液位)监测,机器人防撞,各种超声波接近开关,以及防盗报警等相关领域。

Step2:IIC接口。

观察超声波传感器跟之前学习的传感器的区别:超声波传感器是4PIN的引脚,本款接的是IIC接口。 IIC是集成电路总线的简称,它是一种串行通信总线,I2C串行总线一般有两根信号线,一根是双向的数据线SDA,另一根是时钟线SCL。它具有自动寻址、多主机时钟同步和仲裁等功能的总线。因此,使用I2C总线设计计算机系统十分方便灵活,体积也小,因而在各类实际应用中得到广泛应用。

Step3:超声波传感器与百灵鸽的连接。I2C的接口方式,对应百灵鸽的I2C区域。I2C接口跟之前的3PIN引脚有很大不同。但是在百灵鸽中有防反接的接口,只要对应颜色插即可。

../../_images/1141.png

Step4:超声波传感器的使用。打开mPython软件,新建文件。在左侧模块区点击扩展添加。

../../_images/1151.png

找到N+的库并加载

../../_images/1161.png

在“N+”类别拖出以下模块。

../../_images/1171.png

如何将超声波的距离数值显示在屏幕上?参考代码如下:

../../_images/1181.png

【语音播报】

前面学习了掌控板上内置的音乐,我们也可以在网上找音乐链接播放。但是入门播报器只需要说几个字,可以使用语百度音广播开放平台,就能让掌控板发出特定的语音了。

Step1:打开百度语音广播开放平台。

浏览器输入网址https://developer.baidu.com/vcast,或者搜索百度语音广播开放平台。

../../_images/1191.png

进入文字转语音界面

../../_images/1201.png

Steo2:生成音频链接。输入自己喜欢的文字(标题不能为空)

../../_images/1212.png

选择好后点击生成音频,进入登陆界面。

../../_images/1221.png

登录成功后再生成音频。

../../_images/1231.png

即可得到MP3链接,复制之。它的用法跟上节课播放音乐一样。

【拼搭环节】

使用简单的木板拼搭成一个实物。

../../_images/1241.png

先将超声波和掌控板利用螺丝固定。

../../_images/1251.png

将门架装好

../../_images/1261.png

【完成程序】

思路:入门播报器是以超声波的距离为输入信号,掌控板处理之后决定是否播放语音。

Step1:使用超声波时刻检测前方距离并挑选一个合适的阈值。

阈值如何选择:没人时,超声波数值是距离门口地面的一个相对固定的值。当有人进来时,超声波数值会变小,阈值要能区分开地面和有人时候的两个数值。

../../_images/1271.png

Step2:音频初始化。掌控板播放的是MP3链接,所以需要先进行联网和音频的初始化。

../../_images/1281.png

Step3:判断超声波是否超过阈值,如果是,播放声音。

../../_images/1291.png

【代码】

from mpython import *
import network
import audio
from nplus import *
import time
my_wifi = wifi()
my_wifi.connectWiFi(' ', '  ')
ultrasonic = Ultrasonic()
import gc;gc.collect()
audio.player_init(i2c)
audio.set_volume(100)

while True:
    if ultrasonic.distance() <= 80:
        audio.play('http://vcast-resource.cdn.bcebos.com/vcast-resource/c340caa2-72a1-421f-b5d8-a2b1d0b3233f.mp3')
        time.sleep(2)
项目扩展:

1、调整阈值 2、尝试其他不同的语音 3、你的创意……

第九节:知心天气

项目预览:

开启掌控板之后,屏幕上会所在城市的气象数据,可通过A/B键切换城市。

../../_images/1301.png
项目制作:

【测量当前环境气象数据】

Step1:认识气象传感器。百灵鸽上自带了sht20温湿度传感器,可以获取温度、湿度的数据值。

../../_images/1312.png

Step2:获取实时气象数据值。在扩展的Bluebit类别中拖出“I2C温度”模块。

../../_images/1321.png

点击“温度”,可更改测量选项。

../../_images/1331.png

此模块返回的是一个数字类型的数据,想要让其在掌控板屏幕上显示,要将其转化为文本。

../../_images/1341.png

【代码】

from mpython import *
from bluebit import *
sht20 = SHT20()
while True:
    oled.fill(0)
    oled.DispChar(str('温度:') + str(sht20.temperature()), 0, 0, 1)
    oled.DispChar(str('湿度:') + str(sht20.humidity()), 0, 16, 1)
    oled.show()

【获取其他城市天气数据】

Step1:认识知心天气。

百灵鸽只能测量周围环境的气象数据,如果我们计划一次旅行,想查询其他城市的天气信息及生活指数,又该怎么办呢?

知心天气是一个企业级高精度气象数据服务平台。免费用户亦可简单、快速获取全国城市基础气象信息。

Step2:获取知心天气私钥。

1.注册知心天气账号,登录后点击控制台。

../../_images/1351.png

2.添加产品,获取私钥。

../../_images/1361.png

Step3:添加“天气”扩展包。“扩展”→“添加”→左侧“应用扩展”→“天气”,点击加载。

../../_images/1371.png

Step4:模块编程。

思路:①连接WIFI;②设置城市及目标信息;③显示信息。

../../_images/1381.png ../../_images/1391.png

如果想三个类别都查询的话,要用3个模块分别设置。

../../_images/1401.png

【代码】

from mpython import *
import network
import json
import urequests
my_wifi = wifi()
my_wifi.connectWiFi('名称', '密码')

def get_seni_weather(_url, _location):
    _url = _url + "&location=" + _location.replace(" ", "%20")
    response = urequests.get(_url)
    json = response.json()
    response.close()
    return json

w1 = get_seni_weather("https://api.seniverse.com/v3/weather/now.json?key=your_private_key", "ip")
w2 = get_seni_weather("https://api.seniverse.com/v3/weather/daily.json?key=your_private_key", "ip")
w3 = get_seni_weather("https://api.seniverse.com/v3/life/suggestion.json?key=your_private_key", "ip")
oled.fill(0)
oled.DispChar(str('城市名称') + str(w1["results"][0]["location"]["name"]), 0, 0, 1)
oled.DispChar(str('天气现象:') + str(w1["results"][0]["now"]["text"]), 0, 0, 1)
oled.DispChar(str('今天最高温度:') + str(w2["results"][0]["daily"][0]["high"]), 0, 0, 1)
oled.DispChar(str('感冒指数:') + str(w3["results"][0]["suggestion"]["flu"]["brief"]), 0, 0, 1)
oled.show()

【多个城市天气数据切换】

目标效果:通过A/B键切换不同城市的天气数据。

难点:如何通过A/B键更换地理位置?

思路:既然城市名称要变化,自然要用变量来表示地理位置。我们之前使用的变量大多是数字型、字符串型。现在我们要用一个新的变量类型:列表。

Step1:什么是列表?我们之前使用变量时,都是一个变量对应一个数据,就像一只手拿住一只大苹果一样。如果我们想一只手拿住多个大苹果,应该怎么办呢?很简单,用“袋子”把苹果装起来,我们再提着“袋子”就可以了。列表就相当于“袋子”,它可以存储任意数量数据。

Step2:如何使用列表?

列表也是一种数据类型,我们用变量来表示它时,首先要给它赋值。 1.新建变量city;2.“高级”→“列表”→“初始化列表”,将多个城市添加进列表。位置不够时可以点击“初始化列表”左上角的小齿轮,将item组件拖至右边。

../../_images/1412.png

3.“高级”→“列表”→“列表第0项”。列表是一个有序的数据类型,其索引(序号)从0开始。

../../_images/1421.png

通过更改“列表第0项”中的索引“0”,就能访问到不同的数据。

../../_images/1431.png

Step3:如何通过A/B键切换城市?

思路:我们通过A/B键控制“列表city第0项”中的序号“0”的增减,即可实现切换。所以序号是个会变化的数,我们应该用变量表示。

1.新建变量i并赋值,把变量i放入模块中。

../../_images/1441.png ../../_images/1451.png

2.A/B键控制变量i的增减,并使其不能超过列表的索引范围(此处是0~3)

../../_images/1461.png ../../_images/1471.png

完成,刷入,观察运行效果。

【代码】

from mpython import *
import network
import json
import urequests
import time
my_wifi = wifi()
my_wifi.connectWiFi('名称', '密码')

button_a.irq(trigger=Pin.IRQ_FALLING, handler=on_button_a_down)
button_b.irq(trigger=Pin.IRQ_FALLING, handler=on_button_b_down)
i = 0

def on_button_a_down(_):
    global i, city
    time.sleep_ms(10)
    if button_a.value() == 1: return
    i = i + -1
    if i < 0:
        i = 0
    w1 = get_seni_weather("https://api.seniverse.com/v3/weather/now.json?key=your_private_key", city[i])
    w2 = get_seni_weather("https://api.seniverse.com/v3/weather/daily.json?key=your_private_key", city[i])
    w3 = get_seni_weather("https://api.seniverse.com/v3/life/suggestion.json?key=your_private_key", city[i])
    oled.fill(0)
    oled.DispChar(str('城市名称') + str(w1["results"][0]["location"]["name"]), 0, 0, 1)
    oled.DispChar(str('天气现象:') + str(w1["results"][0]["now"]["text"]), 0, 0, 1)
    oled.DispChar(str('今天最高温度:') + str(w2["results"][0]["daily"][0]["high"]), 0, 0, 1)
    oled.DispChar(str('感冒指数:') + str(w3["results"][0]["suggestion"]["flu"]["brief"]), 0, 0, 1)
    oled.show()

def on_button_b_down(_):
    global i, city
    time.sleep_ms(10)
    if button_b.value() == 1: return
    i = i + 1
    if i > 3:
        i = 3
    w1 = get_seni_weather("https://api.seniverse.com/v3/weather/now.json?key=your_private_key", city[i])
    w2 = get_seni_weather("https://api.seniverse.com/v3/weather/daily.json?key=your_private_key", city[i])
    w3 = get_seni_weather("https://api.seniverse.com/v3/life/suggestion.json?key=your_private_key", city[i])
    oled.fill(0)
    oled.DispChar(str('城市名称') + str(w1["results"][0]["location"]["name"]), 0, 0, 1)
    oled.DispChar(str('天气现象:') + str(w1["results"][0]["now"]["text"]), 0, 0, 1)
    oled.DispChar(str('今天最高温度:') + str(w2["results"][0]["daily"][0]["high"]), 0, 0, 1)
    oled.DispChar(str('感冒指数:') + str(w3["results"][0]["suggestion"]["flu"]["brief"]), 0, 0, 1)
    oled.show()

def get_seni_weather(_url, _location):
    _url = _url + "&location=" + _location.replace(" ", "%20")
    response = urequests.get(_url)
    json = response.json()
    response.close()
    return json

city = ["ip", "guangzhou", "shanghai", "beijing"]
w1 = get_seni_weather("https://api.seniverse.com/v3/weather/now.json?key=your_private_key", city[i])
w2 = get_seni_weather("https://api.seniverse.com/v3/weather/daily.json?key=your_private_key", city[i])
w3 = get_seni_weather("https://api.seniverse.com/v3/life/suggestion.json?key=your_private_key", city[i])
oled.fill(0)
oled.DispChar(str('城市名称') + str(w1["results"][0]["location"]["name"]), 0, 0, 1)
oled.DispChar(str('天气现象:') + str(w1["results"][0]["now"]["text"]), 0, 0, 1)
oled.DispChar(str('今天最高温度:') + str(w2["results"][0]["daily"][0]["high"]), 0, 0, 1)
oled.DispChar(str('感冒指数:') + str(w3["results"][0]["suggestion"]["flu"]["brief"]), 0, 0, 1)
oled.show()
项目扩展:

1、自定义城市 2、丰富屏幕显示 3、你的创意……

第十节:计步器

项目预览:

按下掌控板的开关后,屏幕上显示“当前步数”的字样以及大大的“0”,代表还没开始计步数,当检测到人在走动时,数字会增加。

../../_images/1481.png
项目制作:

【三轴加速度传感器】

Step1:三轴加速度传感器简介。

三轴加速度传感器是基于加速度的基本原理去实现工作的,能感受XYZ三个方向的加速度并转换成可用输出信号,故可以检测空间各个方向的加速度(运动状态)。加速度传感器能够测量由于重力引起的加速度,传感器在加速过程中,通过对质量块所受惯性力的测量,利用牛顿第二定律获得加速度值。

三轴加速度传感器具有体积小和重量轻特点,可以测量空间加速度,能够全面准确反映物体的运动性质,在航空航天、机器人、汽车和医学等领域得到广泛的应用。

掌控板上的加速度计可测量加速度,测量范围为 -2g 到 +2g 之间。可以检测掌控板本身的运动状态,例如向前向后等等。

Step2:加速度传感器的参数。加速度传感器在掌控板的后面大约中央位置。

../../_images/1491.png

掌控板的测量沿3个轴,每个轴的测量值是正数或负数,正轴越趋近重力加速度方向,其数值往正数方向增加,反之往负数方向减小,当读数为 0 时,表示沿着该特定轴“水平”放置。

../../_images/1501.png

【显示不同方向加速度】

Step1: 加速度传感器的模块。打开mPython软件,新建文件。在左侧模块区输入分类找到X轴加速度

../../_images/1511.png

该模块能够获取获取X、Y、Z三轴的加速度并以一个-1到1之间的小数表示。

Step2:显示三个方向的加速度。

如何将三轴加速度的距离数值显示在屏幕上,参考代码如下:

../../_images/1521.png ../../_images/1531.png

可以尝试掌控板按以下放置,观察3轴数据: 平放桌面 –(0,0,-1);翻转平放桌面 –(0,0,1);掌控板下板边直立与桌面 –(1,0,0);掌控板左板边直立与桌面 –(0,1,0)

【计步器】

思路:计步器怎么检测到人走了一步呢?利用三轴加速度传感器。检测到之后屏幕上的数字会增加。

Step1:屏幕上显示步数。将刚刚的程序先保存,新建文件,尝试在屏幕上显示以下字样

../../_images/1541.png

掌控板中,只有一种字体(掌控板只会写一种字),图中的数字0,字体跟其他的不一样,是掌控板作弊了吗?这是因为掌控板使用了“仿数码管”模块。在显示分类中找到“仿数码管”模块:

../../_images/1551.png

顾名思义,这是掌控板在“模仿”数码管的显示方式。什么是数码管呢?电子手表上显示的就是数码管。

../../_images/1561.png

由于数码管是有8个LED灯组成的,所以可以显示数字和小数点(冒号也可以),但是显示不了汉字。

../../_images/1571.png

由于步数是会改变的,所以需要使用一个变量step记录步数。

../../_images/1581.png

程序开始前将变量step设置为0,否则会报错。

../../_images/1591.png

Steo2:增加步数。

上一个例子中我们显示出了三个方向上不同的加速度的数值。但是在计步器使用过程中,每一个方向的运动都是有可能的,要是每动一次就每个方向都判断一次,我们编程就比较累了,所以mPython准备了一个模块。在输入分类中找到。

../../_images/1601.png

该模块能够检测掌控板是否被摇晃,加上如果分支模块,当人走了一步,就被摇晃了一次,这时候就需要将变量增加一。

../../_images/1611.png

思考:这个分支模块应该放哪里?

../../_images/162.png

Step3:改善程序。

发现问题:摇晃一下步数跳了好多步。

分析问题:这是因为在摇晃的过程中重复判断了很多遍,所以步数一直增加。

解决问题:因为摇晃是需要时间的,所以我们让掌控板检测到摇晃之后,等待一段时间,再增加步数。解决方法有两种:

(1)直接在循环体内增加等待模块。

../../_images/163.png

(2)在输入分类中拖出以下模块。

../../_images/164.png

很明显,该模块放不进重复执行里面,它会一直检测掌控板是否被摇晃,如果摇晃了则执行里面的语句。能够取代重复执行中的如果分支模块。

../../_images/165.png

【多线程】

思考:程序在运行时,同一时间只执行一条语句吗?不是的。

Step1:单线程和多线程。

程序在运行时同时只执行一条语句,叫做程序的单线程;同时执行多条语句,这叫做程序的多线程。刚才的方法二就是一个多线程的例子。

单线程程序如下:

../../_images/166.png

多线程程序如下:

../../_images/167.png

【代码】

from mpython import *
from machine import Timer
from mpython_extend import MpythonExtend
import font.digiface_44
tim11 = Timer(11)
tim11.init(period=100, mode=Timer.PERIODIC, callback=timer11_tick)

_is_shaked = _is_thrown = False
_last_x = _last_y = _last_z = _count_shaked = _count_thrown = 0
def on_shaked():pass
def on_thrown():pass

def timer11_tick(_):
    global _is_shaked, _is_thrown, _last_x, _last_y, _last_z, _count_shaked, _count_thrown
    if _is_shaked:
        _count_shaked += 1
        if _count_shaked == 5: _count_shaked = 0
    if _is_thrown:
        _count_thrown += 1
        if _count_thrown == 10: _count_thrown = 0
        if _count_thrown > 0: return
    x=accelerometer.get_x(); y=accelerometer.get_y(); z=accelerometer.get_z()
    _is_thrown = (x * x + y * y + z * z < 0.25)
    if _is_thrown: on_thrown();return
    if _last_x == 0 and _last_y == 0 and _last_z == 0:
        _last_x = x; _last_y = y; _last_z = z; return
    diff_x = x - _last_x; diff_y = y - _last_y; diff_z = z - _last_z
    _last_x = x; _last_y = y; _last_z = z
    if _count_shaked > 0: return
    _is_shaked = (diff_x * diff_x + diff_y * diff_y + diff_z * diff_z > 1)
    if _is_shaked: on_shaked()

def on_shaked():
    global step
    step = step + 1

step = 0
while True:
    oled.fill(0)
    oled.DispChar('当前步数:', 32, 0, 1)
    MpythonExtend.display_font(font.digiface_44, (str(step)), 32, 16, False, 2)
    oled.show()

Step2:多线程的好处。

打个比方:单线程是一张桌子上一个人吃饭,其他人排队,必须等他吃完才能到下一个;而多线程则是同一张桌子,多个人吃饭,就算其中一个人吃得很慢,也不会很大地影响到后面排队的人。

多线程的作用:比起单线程速度快;资源利用率高;“一心多用”,同时处理多条语句。

Step3:寻找多线程的模块。你们还能在输入分类中找到哪些多线程的模块呢?

../../_images/168.png

这些都是,它们都是“自立为王”,不能放进其他模块里面。而且都是外界出现了状况(A键被按、P键被触摸、被摇晃等等)才执行里面的内容。善用多线程的模块能够让程序可读性增加,运行速度也会变快。

项目扩展:

1、给掌控板加上外在的配件,让它能绑在手上或者腿上。

2、使用显示内置图像和以下模块实现以下效果。

../../_images/169.png ../../_images/1701.png ../../_images/1711.png

3、尝试加入声音

4、你的创意……

第十一节:创立方小车

项目预览:
../../_images/172.png
项目制作:

【360舵机】

Step1:认识360舵机。

舵机是位置(角度)伺服的驱动器,按最大旋转角度可分为180舵机和360舵机。本节我们使用的是360舵机。

../../_images/173.png

Step2:360舵机的使用说明。

360舵机可旋转的角度是360°,但不能旋转至某一指定角度作停留,比如从0°转到250°。当给予360舵机某一个角度时,舵机会以一个特定的速度转动,与电机类似。角度与转速、转向的关系如下图。

../../_images/174.png

Step3:转动吧!360舵机。

从“高级”→“引脚” 类别中,拖出设置舵机角度模块,将360舵机与百灵鸽扩展口相接,在mpython上设置好引脚号和角度,刷入即可。将360舵机与百灵鸽P15引脚相接(注意颜色对应)。

../../_images/175.png

设置引脚号及角度,目标效果:360舵机顺时针转2秒,逆时针转2秒,停止。

../../_images/176.png

测试成功后,接下来我们要用360舵机来充当创立方小车的马达。

【创立方小车组装】

Step1:认识创立方。创立方是一款百变的造物盒子,结构采用激光切割技术实现,旨在推进“一起来玩掌控板”。本节我们就用创立方来搭建一个方形小车。

../../_images/177.png

Step2:组装创立方小车。

1.取出1、2号板,把360舵机按图11-7所示安装,用螺丝或热熔胶固定。(注意:舵机齿轮位置朝下)

../../_images/178.png

2.取出5、6号板,结合1、2号板,组装成车子的四周。

../../_images/179.png

3.取出4号板,安装上万向轮。

../../_images/180.png

4.取出3号板,按照孔位固定百灵鸽。

../../_images/1811.png

5.将1号板舵机接入P15引脚,2号板舵机接入P16引脚,注意舵机线的颜顺序与百灵鸽接口颜色顺序对应。

../../_images/1821.png

6.安装顶板、轮子,完成。

../../_images/183.png

【创立方小车编程】

Step1:前进。1号板舵机(P15)逆时针转动,2号板舵机(P16)顺时针转动。这是由两个指令是小车前进时所必须的,我们可以把两者合一,在以后让小车前进时会方便许多。

../../_images/184.png

从“高级”→“函数”类别中,拖出定义函数模块,将图11-13两条指令放入模块中,并修改函数名称为“前进”。

../../_images/185.png

从“高级”→“函数”类别中,可以发现多出一个“前进”模块,如图11-15所示。将其拖出,这时候,一个“前进”模块就等同于定义函数内的所有模块。

../../_images/186.png

Step2:后退。1号板舵机(P15)顺时针转动,2号板舵机(P16)逆时针转动,并封装成“后退”函数,

../../_images/187.png

Step3:停止。1号板舵机(P15)停止,2号板舵机(P16)停止,并封装成“停止”函数。

../../_images/188.png

Step4:左转。1号板舵机(P15)停止,2号板舵机(P16)顺时针转动,并封装成“左转”函数。

../../_images/189.png

【动作编排】

利用封装好的五个动作,结合等待模块、循环模块等,编排一套小车的动作。

../../_images/190.png

【代码】

from mpython import *
from machine import Timer
from servo import Servo
import time
tim12 = Timer(12)
tim12.init(period=100, mode=Timer.PERIODIC, callback=timer12_tick)
servo_15 = Servo(15, min_us=750, max_us=2250, actuation_range=180)
servo_16 = Servo(16, min_us=750, max_us=2250, actuation_range=180)

_status_p = _status_y = _status_t = _status_h = _status_o = _status_n = 0
def on_touchpad_P_pressed():pass
def on_touchpad_P_unpressed():pass
def on_touchpad_Y_pressed():pass
def on_touchpad_Y_unpressed():pass
def on_touchpad_T_pressed():pass
def on_touchpad_T_unpressed():pass
def on_touchpad_H_pressed():pass
def on_touchpad_H_unpressed():pass
def on_touchpad_O_pressed():pass
def on_touchpad_O_unpressed():pass
def on_touchpad_N_pressed():pass
def on_touchpad_N_unpressed():pass

def timer12_tick(_):
    global _status_p, _status_y, _status_t, _status_h, _status_o, _status_n
    try:
        touchPad_P.read();pass
    except:
        return
    if touchPad_P.read() < 400:
        if 1 != _status_p:_status_p = 1;on_touchpad_P_pressed()
    elif 0 != _status_p:_status_p = 0;on_touchpad_P_unpressed()
    if touchPad_Y.read() < 400:
        if 1 != _status_y:_status_y = 1;on_touchpad_Y_pressed()
    elif 0 != _status_y:_status_y = 0;on_touchpad_Y_unpressed()
    if touchPad_T.read() < 400:
        if 1 != _status_t:_status_t = 1;on_touchpad_T_pressed()
    elif 0 != _status_t:_status_t = 0;on_touchpad_T_unpressed()
    if touchPad_H.read() < 400:
        if 1 != _status_h:_status_h = 1;on_touchpad_H_pressed()
    elif 0 != _status_h:_status_h = 0;on_touchpad_H_unpressed()
    if touchPad_O.read() < 400:
        if 1 != _status_o:_status_o = 1;on_touchpad_O_pressed()
    elif 0 != _status_o:_status_o = 0;on_touchpad_O_unpressed()
    if touchPad_N.read() < 400:
        if 1 != _status_n:_status_n = 1;on_touchpad_N_pressed()
    elif 0 != _status_n:_status_n = 0;on_touchpad_N_unpressed()

def on_touchpad_P_pressed():
    global step
    _E5_89_8D_E8_BF_9B()
    time.sleep(2)
    _E5_B7_A6_E8_BD_AC()
    time.sleep(1)
    _E5_8F_B3_E8_BD_AC()
    time.sleep(1)
    _E5_90_8E_E9_80_80()
    time.sleep(2)
    _E5_81_9C_E6_AD_A2()

def _E5_89_8D_E8_BF_9B():
    servo_15.write_angle(180)
    servo_16.write_angle(0)

def _E5_90_8E_E9_80_80():
    servo_15.write_angle(0)
    servo_16.write_angle(180)

def _E5_8F_B3_E8_BD_AC():
    servo_15.write_angle(180)
    servo_16.write_angle(90)

def _E5_B7_A6_E8_BD_AC():
    servo_15.write_angle(90)
    servo_16.write_angle(0)

def _E5_81_9C_E6_AD_A2():
    servo_15.write_angle(90)
    servo_16.write_angle(90)
项目扩展:

1、修改小车速度。

2、编排小车动作,调整等待时间,让小车走一个正方形。

3、你的创意……

第十二节:遥控小车

项目预览:

创立方多了一个遥控器小伙伴,让创立方小车在前进道路上不在彷徨。手上拿着掌控板制作的遥控器,即可控制创立方的运动甚至灯光声音,快和我们一起玩吧。

项目制作:

【程序流程图】

Step1:流程图介绍。

程序流程图又称程序框图,用统一规定的标准符号描述程序运行的具体步骤。流程图的基本结构跟程序一样,分为顺序结构、分支结构和循环结构。

../../_images/1911.png

Step2:流程图怎么阅读。

为便于识别,绘制流程图的习惯做法是:圆角矩形表示“开始”与“结束”;矩形表示行动方案、普通工作环节用;菱形表示问题判断或判定(审核/审批/评审)环节;

../../_images/1921.png

箭头代表工作流方向。阅读的时候从圆角矩形开始,按步骤跟着箭头的方向流动,因为程序在运行的时候也是一步一步来的。当程序有多线程时,流程图可以使用多个表示开始的圆角矩形,当满足矩形内的条件时就触发对应的动作。

../../_images/193.png

Step4:遥控器和小车的流程图。

遥控器需要实现:在平放和向前后左右倾的时候,会通过无线广播发送消息。在运行过程中,一直检测掌控板处于什么运动状态,再发送对应的广播。所以在程序开始之后,程序就在检测运动状态。但是因为掌控板不能同时检测五种不同情况的运动状态,所以需要逐步逐步判断。判断之后有两个结果:“是”或者“否”,掌控板根据情况选择下一步往哪走:

尝试画出小车的流程图。提示,小车先要收到消息才会进入判断状态。

【遥控器】

项目思路:整个项目分为两部分:遥控器和创立方小车,它们通过无线广播进行通讯,遥控器发送消息,小车收到消息之后相应相应的动作。它们都需要一块掌控板,所以本项目需要两块掌控板,推荐分组合作。

Step1: 遥控器需求

本节课实现:遥控上的掌控板平放,车子停止;向前倾,车子前进;向后倾,车子后退;向左倾,车子左转;向右倾,车子右转。

遥控器作为无线广播的发送端,需要实现:在自身平放和向前后左右倾的时候,会通过无线广播发送不同的消息。

我们可以使用流程图整理遥控器的思路,根据流程图就可以很直观的了解要达到的效果和很简单地编写代码。

Step2:遥控器的流程图。

../../_images/194.png

Step3:根据流程图写出程序。

无线广播部分:需要开始无线广播并且选择频道,每一组自己选择适合的频道。

../../_images/195.png

给前进后退等五个情况发送不同名称的消息,然后在“输入”类别中找到“掌控板向前倾斜时执行”模块

../../_images/196.png

执行的内容是发送消息。

../../_images/197.png

点开“向前倾斜”,可以看到它刚好对应了掌控板的5种姿态。

../../_images/198.png

将五种情况和发送的消息填写好,遥控器的程序就写好了。

../../_images/199.png

【代码】

import radio
from mpython import *
from machine import Timer
radio.on()
radio.config(channel=13)
tim14 = Timer(14)
tim14.init(period=200, mode=Timer.PERIODIC, callback=timer14_tick)

_dir = ''
def on_tilt_forward():pass
def on_tilt_back():pass
def on_tilt_right():pass
def on_tilt_left():pass
def on_tilt_none():pass

def timer14_tick(_):
    global _dir
    if accelerometer.get_x() < -0.3:
        if 'F' != _dir:_dir = 'F';on_tilt_forward()
    elif accelerometer.get_x() > 0.3:
        if 'B' != _dir:_dir = 'B';on_tilt_back()
    elif accelerometer.get_y() < -0.3:
        if 'R' != _dir:_dir = 'R';on_tilt_right()
    elif accelerometer.get_y() > 0.3:
        if 'L' != _dir:_dir = 'L';on_tilt_left()
    else:
        if '' != _dir:_dir = '';on_tilt_none()

def on_tilt_left():
    global step
    radio.send('左转')
    oled.fill(0)
    oled.DispChar('左转', 0, 0, 1)
    oled.show()

def on_tilt_forward():
    global step
    radio.send('前进')
    oled.fill(0)
    oled.DispChar('前进', 0, 0, 1)
    oled.show()

def on_tilt_back():
    global step
    radio.send('后退')
    oled.fill(0)
    oled.DispChar('后退', 0, 0, 1)
    oled.show()

def on_tilt_right():
    global step
    radio.send('右转')
    oled.fill(0)
    oled.DispChar('右转', 0, 0, 1)
    oled.show()

def on_tilt_none():
    global step
    radio.send('停止')
    oled.fill(0)
    oled.DispChar('停止', 0, 0, 1)
    oled.show()

【完善小车】

Step1:小车的需求:先开启无线广播模块,选择频道,然后一直等待无线广播的消息。收到无线广播的消息之后,会做出相对应的动作。

Step2:小车的流程图。小车的流程图跟遥控器的很类似,都要先开启无线广播并选择频道。

../../_images/200.png

然后同样使用多线程的模式来表示接收到的消息,不同的是小车是接收端(被遥控),需要让车子执行动作。

../../_images/2011.png

Step3:小车的程序。

根据流程图编写程序,我们可以打开上节课小车运动的程序,里面有小车运动的函数,我们直接在上面改动会比较方便。

../../_images/2021.png

根据流程图可以知道,我们希望收到的消息只有五种,所以在“广播”分类中找到接收特定消息模块。

../../_images/203.png

填入每种情况和对应的函数。

../../_images/204.png

【代码】

import radio
from mpython import *
from servo import Servo
from machine import Timer
import ubinascii

radio.on()
radio.config(channel=13)
tim13 = Timer(13)
tim13.init(period=20, mode=Timer.PERIODIC, callback=timer13_tick)
_radio_msg_list = []

servo_15 = Servo(15, min_us=750, max_us=2250, actuation_range=180)
servo_16 = Servo(16, min_us=750, max_us=2250, actuation_range=180)

_radio_msg_list.append('前进')

def _E5_81_9C_E6_AD_A2():
    servo_15.write_angle(90)
    servo_16.write_angle(90)

def _E5_89_8D_E8_BF_9B():
    servo_15.write_angle(180)
    servo_16.write_angle(0)

def radio_callback(_msg):
    global _radio_msg_list
    try: radio_recv(_msg)
    except: pass
    if _msg in _radio_msg_list:
        eval('radio_recv_' + bytes.decode(ubinascii.hexlify(_msg)) + '()')

def timer13_tick(_):
    _msg = radio.receive()
    if not _msg: return
    radio_callback(_msg)

def _E5_90_8E_E9_80_80():
    servo_15.write_angle(0)
    servo_16.write_angle(180)

def _E5_8F_B3_E8_BD_AC():
    servo_15.write_angle(180)
    servo_16.write_angle(90)

def _E5_B7_A6_E8_BD_AC():
    servo_15.write_angle(90)
    servo_16.write_angle(0)

def radio_recv_e5898de8bf9b():
    global step
    _E5_89_8D_E8_BF_9B()

_radio_msg_list.append('后退')
def radio_recv_e5908ee98080():
    global step
    _E5_90_8E_E9_80_80()

_radio_msg_list.append('左转')
def radio_recv_e5b7a6e8bdac():
    global step
    _E5_B7_A6_E8_BD_AC()

_radio_msg_list.append('右转')
def radio_recv_e58fb3e8bdac():
    global step
    _E5_8F_B3_E8_BD_AC()

_radio_msg_list.append('停止')
def radio_recv_e5819ce6ada2():
    global step
    _E5_81_9C_E6_AD_A2()
项目扩展:

1、除了五个命令外,加上另外的命令让创立方上的掌控板亮灯和关灯 2、尝试让遥控器控制小车发出声音 3、你的创意? 4、……

第十三节:小车调速器

项目预览:

旋钮顺时针旋转,小车加速,转速表指针上升,反之下降。

../../_images/205.png
项目制作:

【小车调速器】

Step1:旋转电位器。

旋转电位器按输入/输出分属于输入类,输入的信号是模拟信号。其作用是:根据旋转角度的不同,可输入连续变化的信号值,信号值范围是0~4095。

../../_images/206.png

之前我们学习了数字信号,那什么是模拟信号?我们来看看两者的区别:

数字信号:只有两种情况,用0和1代表,例子:按钮、单色LED灯;

模拟信号:有多种情况,用0~4095范围内的数字表示,例子:温湿度传感器

../../_images/207.png

Step2:显示旋转电位器模拟值。

“高级”→“引脚”→“读取引脚模拟值”,此模块可以读取指定引脚上的模拟值,范围是0~4095,数据类型为数字(整型或浮点型)。将旋转电位器接入P1引脚,调整旋钮,观察数值。

../../_images/208.png

可以观察到,当顺时针旋转时,模拟值逐渐减小,最小值为0;逆时针旋转时,模拟值逐渐增大,最大值为4095。

Step3:认识180舵机。

之前我们使用过360舵机,类似地,180舵机可旋转角度为0°~180°。用“高级”→“引脚”→“设置舵机角度”模块,可直接设置180舵机旋转至指定角度。将180舵机接入P13引脚,用图13-5所示程序,观察舵机运动效果。(注意:填写的角度不能小于0°或大于180°,否则程序会报错。)

../../_images/209.png

Step4:旋转电位器控制180舵机。将百灵鸽、旋转电位器、180舵机安装到转速板上。

../../_images/2101.png

把从旋转电位器上获取到的数据,进过处理,让180舵机作输出即可。将旋转电位器接入P1引脚,180舵机接入P13引脚,思考一下,以下程序正确吗?

../../_images/2111.png

上述程序运行起来会报错,为什么呢?

因为模拟值的输入范围是0~4095,而180舵机的输出范围是0~180,两者是不完全匹配的,当输入超过180时,程序就会报错了。因此,我们需要稍微加工处理一下,让输入、输出能“愉快相处”。从“数学”类别中拖出“映射”模块

../../_images/2121.png

这里,P1的模拟输入值是我们需要处理的数据,原始区间是旋转电位器的模拟值范围,即0-4095,目标区间是180舵机的输出范围,即0~180。

../../_images/213.png

将旋转电位器向逆时针旋转至尽头,安装好指针。

../../_images/214.png

【调速器控制小车】

Step1:完善遥控器。

1.打开上一节做好的遥控器程序;2.将速度信息通过无线广播发送。

小车停止时角度为90,最大转速分别是0、180。两者与90之差为90,说明小车速度的可调范围是0~90,与180舵机的可动范围0~180不符合,所以需要再次使用映射进行处理。(注意:因为舵机180°时对应0刻度,所以速度的范围要反过来:90~0。)

../../_images/215.png

【代码】

import radio
from mpython import *
from servo import Servo
import math
from machine import Timer

tim14 = Timer(14)
tim14.init(period=200, mode=Timer.PERIODIC, callback=timer14_tick)

servo_13 = Servo(13, min_us=750, max_us=2250, actuation_range=180)
radio.on()
radio.config(channel=13)

p1 = MPythonPin(1, PinMode.ANALOG)

_dir = ''
def on_tilt_forward():pass
def on_tilt_back():pass
def on_tilt_right():pass
def on_tilt_left():pass
def on_tilt_none():pass

def timer14_tick(_):
    global _dir
    if accelerometer.get_x() < -0.3:
        if 'F' != _dir:_dir = 'F';on_tilt_forward()
    elif accelerometer.get_x() > 0.3:
        if 'B' != _dir:_dir = 'B';on_tilt_back()
    elif accelerometer.get_y() < -0.3:
        if 'R' != _dir:_dir = 'R';on_tilt_right()
    elif accelerometer.get_y() > 0.3:
        if 'L' != _dir:_dir = 'L';on_tilt_left()
    else:
        if '' != _dir:_dir = '';on_tilt_none()

def on_tilt_left():
    global i, s
    radio.send('左转')
    oled.fill(0)
    oled.DispChar('左转', 0, 0, 1)
    oled.show()

def on_tilt_forward():
    global i, s
    radio.send('前进')
    oled.fill(0)
    oled.DispChar('前进', 0, 0, 1)
    oled.show()

def on_tilt_right():
    global i, s
    radio.send('右转')
    oled.fill(0)
    oled.DispChar('右转', 0, 0, 1)
    oled.show()

def on_tilt_back():
    global i, s
    radio.send('后退')
    oled.fill(0)
    oled.DispChar('后退', 0, 0, 1)
    oled.show()

def on_tilt_none():
    global i, s
    radio.send('停止')
    oled.fill(0)
    oled.DispChar('停止', 0, 0, 1)
    oled.show()

while True:
    i = ((180 - 0) / (4095 - 0)) * (p1.read_analog() - 0) + 0
    servo_13.write_angle(i)
    s = round(((0 - 90) / (180 - 0)) * (i - 0) + 90)
    radio.send((str(s)))
    oled.fill(0)
    oled.DispChar(str('当前速度:') + str(s), 0, 0, 1)
    oled.show()

Step2:调整小车程序。

1.打开上节课小车程序;2.可接收的速度值有0~90,不可能把每一个速度值都用一个模块来接收。所以我们采用以下方法:先判断接收到的是动作指令(文本)还是速度值(数字),如果是速度值,用变量i保存起来。

../../_images/216.png

3.调整速度。用90+i表示~180;用90-i表示0~90。

../../_images/217.png ../../_images/218.png

【代码】

import radio
from mpython import *
from servo import Servo
from machine import Timer
import ubinascii

radio.on()
radio.config(channel=13)

tim13 = Timer(13)
tim13.init(period=20, mode=Timer.PERIODIC, callback=timer13_tick)
_radio_msg_list = []
servo_15 = Servo(15, min_us=750, max_us=2250, actuation_range=180)
servo_16 = Servo(16, min_us=750, max_us=2250, actuation_range=180)
_radio_msg_list.append('前进')

def _E5_81_9C_E6_AD_A2():
    global _msg, i
    servo_15.write_angle(90)
    servo_16.write_angle(90)

def radio_recv(_msg):
    global i
    if _msg.isdigit():
        i = int(_msg)

def radio_callback(_msg):
    global _radio_msg_list
    try: radio_recv(_msg)
    except: pass
    if _msg in _radio_msg_list:
        eval('radio_recv_' + bytes.decode(ubinascii.hexlify(_msg)) + '()')

def timer13_tick(_):
    _msg = radio.receive()
    if not _msg: return
    radio_callback(_msg)

def _E5_89_8D_E8_BF_9B():
    global _msg, i
    servo_15.write_angle((90 + i))
    servo_16.write_angle((90 - i))

def _E5_90_8E_E9_80_80():
    global _msg, i
    servo_15.write_angle((90 - i))
    servo_16.write_angle((90 + i))

def _E5_8F_B3_E8_BD_AC():
    global _msg, i
    servo_15.write_angle((90 + i))
    servo_16.write_angle(90)

def _E5_B7_A6_E8_BD_AC():
    global _msg, i
    servo_15.write_angle(90)
    servo_16.write_angle((90 - i))

def radio_recv_e5898de8bf9b():
    global i
    _E5_89_8D_E8_BF_9B()

_radio_msg_list.append('后退')
def radio_recv_e5908ee98080():
    global i
    _E5_90_8E_E9_80_80()

_radio_msg_list.append('左转')
def radio_recv_e5b7a6e8bdac():
    global i
    _E5_B7_A6_E8_BD_AC()

_radio_msg_list.append('右转')
def radio_recv_e58fb3e8bdac():
    global i
    _E5_8F_B3_E8_BD_AC()

_radio_msg_list.append('停止')
def radio_recv_e5819ce6ada2():
    global i
    _E5_81_9C_E6_AD_A2()
项目扩展:

1、丰富屏幕显示 2、你的创意……

第十四节:自动闸道

项目预览:

按下掌控板开关后,超声波传感器会检测它前方是否有车辆,如果是,则让舵机控制的停车杆抬起来,过一段时间后停车杆落下。

../../_images/219.png
项目制作:

【组装模型】

1.根据木板和安装图片,完成模型的制作。

../../_images/220.png

2.安装超声波传感器。

../../_images/2211.png

3.利用两个螺丝,固定板子。

../../_images/2221.png

4.将舵机连同所在的木板用螺丝固定。

../../_images/223.png

5.将摇臂与舵机接好。

../../_images/224.png

6.连接停车杆和超声波传感器。

../../_images/225.png

7.加上掌控板,接线,完成。

../../_images/226.png

【程序流程图】

尝试画出流程图。

../../_images/227.png

【编写程序】

思路:掌控板根据超声波输入的值的大小,决定舵机的运动(道闸的起落)。

Step1:显示超声波数值,请在屏幕上重复显示超声波传感器的数值,并在下方加上单位。

../../_images/228.png

参考程序如下:

../../_images/229.png

Step2:选取一个阀值,控制舵机的运动。接下来判断超声波的值是否超过阀值,并让舵机抬起道闸数秒。

../../_images/230.png

Step3:调整舵机角度,使道闸达到预期效果。将舵机的角度调整至平时道闸水平状态,有车的时候道闸抬起到90度左右。

../../_images/2311.png

【代码】

from mpython import *
from servo import Servo
from nplus import *
import time

servo_13 = Servo(13, min_us=750, max_us=2250, actuation_range=180)
ultrasonic = Ultrasonic()
servo_13.write_angle(90)

while True:
    oled.fill(0)
    oled.DispChar((str(ultrasonic.distance())), 0, 0, 1)
    oled.DispChar('mm', 0, 16, 1)
    oled.show()
    if ultrasonic.distance() <= 200:
        servo_13.write_angle(0)
        time.sleep(3)
        servo_13.write_angle(90)
项目扩展:

1、尝试给掌控板加入文字 2、尝试给道闸加入声音播报功能 3、你的创意?

第十五节:停车位检测装置

项目预览:

车辆进入车位后,状态灯改为亮红灯;车辆启动,即将驶离车位时,道闸自动抬起,车辆驶出后,状态灯改为亮绿色。

../../_images/2321.png
项目制作:

【停车位检测装置】

思路:通过灰度传感器识别小车上的黑条,小车靠近时,灰度传感器能识别到黑条,认为车辆进入车位,RGB改为红灯。

Step1:认识灰度传感器。

灰度传感器是模拟传感器,利用不同颜色的检测面对光的反射程度不同,区别黑色与其它颜色。

../../_images/233.png

Step2:确定感灰度传器阈值,在小车上贴上黑胶布。

搭建好停车位检测装置。本文灰度传感器接P0引脚,RGB灯接P13引脚。

../../_images/234.png

读取灰度传感器模拟值,小车在合适距离下,确定灰度传感器的阈值。

../../_images/235.png ../../_images/236.png

【代码】

from mpython import *
import time
p0 = MPythonPin(0, PinMode.ANALOG)

while True:
    oled.fill(0)
    oled.DispChar(str('当前灰度值:') + str(p0.read_analog()), 0, 0, 1)
    oled.show()
    time.sleep_ms(100)

Step3:梳理流程图。

1.默认状态下,车位上无车,RGB绿灯:

../../_images/237.png

2.判断车辆是否进入:

../../_images/238.png

3.车辆进入车位,RGB红灯,直到车辆驶离车位,发送广播,通知道闸抬起:

../../_images/239.png

【代码】

import radio
from mpython import *
import neopixel
import time

my_rgb = neopixel.NeoPixel(Pin(Pin.P13), n=1, bpp=3, timing=1)
p0 = MPythonPin(0, PinMode.ANALOG)
radio.on()
radio.config(channel=10)
while True:
    my_rgb.fill( (0, 50, 0) )
    my_rgb.write()
    if p0.read_analog() > 2000:
        time.sleep(0.5)
        my_rgb.fill( (50, 0, 0) )
        my_rgb.write()
        while p0.read_analog() > 1900:
            pass
        radio.send('起')
        time.sleep(0.5)

Step4:编程。

1.默认状态下,车位上无车,RGB绿灯:

../../_images/240.png

2.判断车辆是否进入:

../../_images/2411.png

3.车辆进入车位,RGB红灯,直到车辆驶离车位,发送广播,通知道闸抬起:

../../_images/2421.png

停车位检测装置完成,接下来调整道闸,让道闸接收到广播后,抬杠放行。

【调整道闸程序】

Step1:打开上一节道闸的程序;

Step2:加入广播接收:

../../_images/243.png

Step3:加上出闸提醒:一个音节

../../_images/244.png

【代码】

from mpython import *
from servo import Servo
from nplus import *
import time
import radio
from machine import Timer
import ubinascii
import music

servo_13 = Servo(13, min_us=750, max_us=2250, actuation_range=180)
ultrasonic = Ultrasonic()
_radio_msg_list.append('起')
_radio_msg_list = []
servo_13.write_angle(90)

tim13 = Timer(13)
tim13.init(period=20, mode=Timer.PERIODIC, callback=timer13_tick)

radio.on()
radio.config(channel=10)

def radio_callback(_msg):
    global _radio_msg_list
    try: radio_recv(_msg)
    except: pass
    if _msg in _radio_msg_list:
        eval('radio_recv_' + bytes.decode(ubinascii.hexlify(_msg)) + '()')

def timer13_tick(_):
    _msg = radio.receive()
    if not _msg: return
    radio_callback(_msg)

def radio_recv_e8b5b7():
    global i
    music.play('C3:1')
    servo_13.write_angle(0)
    time.sleep(4)
    servo_13.write_angle(90)

while True:
    oled.fill(0)
    oled.DispChar((str(ultrasonic.distance())), 0, 0, 1)
    oled.DispChar('mm', 0, 16, 1)
    oled.show()
    if ultrasonic.distance() <= 70:
        servo_13.write_angle(0)
        time.sleep(3)
        servo_13.write_angle(90)
项目扩展:

1、你的创意……

第十六节:避障跟随小车

项目预览:

创立方小车遇到墙壁之后会后退并转向直到前方没有障碍再继续通行。

项目制作:
避障小车:

【给创立方加上眼睛】

在我们已经学习的传感器中,什么传感器作为创立方的眼睛比较好呢?

很明显,超声波传感器能委以重任。那么,怎么样给创立方装上超声波传感器呢?

在创立方的前方,有一个孔,将超声波传感器放至前方用螺丝固定住,然后再将超声波接入到百灵鸽中

【超声波避障原理】

超声波传感器的原理:即利用超声波发送和接收的时间差计算得到前方物体的距离,而超声波避障小车的原理则是,给小车设定一个阈值,如果超声波数值小于该阈值,即代表前方有障碍物。所以需要停下来,后退到一定距离后转向再继续前进。

【程序流程图】

../../_images/245.png

【编写程序】

根据流程图我们可以写出程序

1.显示超声波数值

../../_images/246.png

2.平常处于前进状态

../../_images/247.png

3.与超声波的阈值比较

../../_images/248.png

4.先后退,再转向

../../_images/249.png

5.使用函数整理程序

../../_images/250.png

【代码】

from mpython import *
from nplus import *
import time
from servo import Servo

servo_15 = Servo(15, min_us=750, max_us=2250, actuation_range=180)
servo_16 = Servo(16, min_us=750, max_us=2250, actuation_range=180)
ultrasonic = Ultrasonic()

def _E5_90_8E_E9_80_80():
    servo_15.write_angle(180)
    servo_16.write_angle(0)

def _E5_89_8D_E8_BF_9B():
    servo_15.write_angle(0)
    servo_16.write_angle(180)

def _E5_8F_B3_E8_BD_AC():
    servo_15.write_angle(180)
    servo_16.write_angle(180)

while True:
    _E5_89_8D_E8_BF_9B()
    oled.fill(0)
    oled.DispChar((str(ultrasonic.distance())), 0, 0, 1)
    oled.DispChar('mm', 0, 16, 1)
    oled.show()
    if ultrasonic.distance() <= 100:
        _E5_90_8E_E9_80_80()
        time.sleep(1.5)
        _E5_8F_B3_E8_BD_AC()
        time.sleep(0.6)
跟随小车:

创立方小车是否可以像我们的宠物一样,跟着我们走。利用超声波模块,我们可以实现该效果。

思路:跟随小车跟刚刚的避障小车都是使用超声波传感器进行识别。但是避障小车是遇到障碍之后后退,而跟随小车则是遇到障碍之后,与障碍保持一定的距离。当障碍物太近时小车后退,当障碍物太远时小车前进。

这里小车的运动方式用三种:停止、前进、后退。三种运动的方式是根据距离前方物体的距离而定的。

1)当前方物体太近:后退;

2)当前方物体太远:前进;

3)与前方物体距离适合:停止。

Step1:画出流程图;

../../_images/2511.png

Step2:根据流程图完成程序。

../../_images/252.png

【代码】

from mpython import * from servo import Servo from nplus import *

servo_15 = Servo(15, min_us=750, max_us=2250, actuation_range=180) servo_16 = Servo(16, min_us=750, max_us=2250, actuation_range=180) ultrasonic = Ultrasonic()

def _E5_90_8E_E9_80_80():
servo_15.write_angle(180) servo_16.write_angle(0)
def _E5_89_8D_E8_BF_9B():
servo_15.write_angle(0) servo_16.write_angle(180)
def _E5_8F_B3_E8_BD_AC():
servo_15.write_angle(180) servo_16.write_angle(180)
def _E5_81_9C_E6_AD_A2():
servo_15.write_angle(90) servo_16.write_angle(90)
while True:

oled.fill(0) oled.DispChar((str(ultrasonic.distance())), 0, 0, 1) oled.DispChar(‘mm’, 0, 16, 1) oled.show() if ultrasonic.distance() <= 100:

_E5_90_8E_E9_80_80()
elif ultrasonic.distance() > 150:
_E5_89_8D_E8_BF_9B()
else:
_E5_81_9C_E6_AD_A2()
项目扩展:

1、尝试给掌控板加入音效 2、尝试给小车加上灯光功能 3、你的创意?