树莓派如何与 Buzz 控制器通讯,并实现一个简单的游戏

在此树莓派测验游戏项目中,我们将向您展示如何与树莓派一起使用 PlayStation Buzz 控制器来运行简单的测验。

在本教程中,我们将通过使用 Python 的 hidapi 库引导您完成与 Buzz 控制器进行交互的过程。

hidapi 库将使我们能够直接与 Buzz 控制器通信,包括从 Buzz 控制器取回数据并将数据写入 Buzz 控制器以打开和关闭其红色 LED 。

在本教程结束时,您将学习如何读取有关所有 USB 设备的信息。了解了如何读取设备发送回的数据,以及如何编写一个用作处理设备的简单包装的库。

所有这些都将通过编写一个简单的问答游戏而结束,它将使用我们编程到 Buzz Controller 库中的所有功能。

您可以在下面的树莓派上找到有关如何使用 PlayStation Buzz 控制器的完整教程。这是我们更长的树莓派项目之一。

设备:

以下是完成我们有关如何在树莓派上使用 Buzz 控制器并设置问答游戏的教程所需的设备的完整列表。

推荐的:

  • 树莓派

  • Micro SD 卡

  • Ethernet Cord 或 * Wifi dongle (树莓派 3 已内置 WiFi) )

  • 电源

  • 蜂鸣控制器

可选的:

  • 树莓派外壳

  • USB 键盘

  • USB 鼠标

让您的 Pi 准备与 Buzz 控制器互动

1 在开始之前,我们首先需要确保 Raspbian 操作系统完全是最新的。

我们可以通过在树莓派上运行以下两个命令来实现。

sudo apt-get update
sudo apt-get upgrade

2 现在,我们将为 hidapi 软件包安装一些所需的软件包,这些软件包将在本教程的后面部分进行安装。这些软件包包含用于访问 USB 设备的代码。

运行以下命令以安装所需的软件包

sudo apt-get install python-dev libusb-1.0-0-dev libudev-dev

3 在开始安装所需的 Python 软件包之前,请先运行以下命令,以确保我们正在运行最新版本的 Python setuptools。

sudo pip install-升级 setuptools 

4 有了我们最新的树莓派,让我们继续操作并使用 ** pip ** 安装一个名为 Cython 的特定软件包。

对于那些不知道 Cython 是什么的人来说,它是 Python 的一个版本,经过编译可尝试实现类似 C 的性能。我们主要需要用于与 USB 设备接口的软件包。

现在运行以下代码,通过树莓派上的 pip 安装 Cython 。

sudo pip安装 cython 

请注意,Cython 可能需要花费大量时间来安装,因此现在是购买另一杯咖啡的好时机。

5 现在,我们继续安装所需的最终 Python 程序包,即 hidapi 程序包。

hidapi 软件包需要在上一步中安装的 Cython 。 Cython 允许 hidapi 代码获得接近 C 的性能,这意味着 USB 交互可以迅速发生。

通过在树莓派上运行以下命令来安装此软件包。

sudo 点安装 hidapi 

6 现在,我们已将所有必需的软件包安装到系统中,现在我们可以继续进行下一部分。

下一节将介绍如何从 Buzz 控制器读回数据并了解我们可以预期必须处理的值。

如果您想直接编写蜂鸣控制器库和测验游戏逻辑,则可以跳到下一部分的结尾。

通过 Python 查找您的 Buzz 控制器

1 在本节中,我们将探索如何从 Buzz 控制器中检索原始数据。

为此,我们将编写两个小的 Python 脚本,这些脚本利用我们之前安装的 hidapi 软件包。

这些脚本之一将枚举所有已连接的 USB 设备,因此我们可以选择 Buzz 控制器并获取其供应商 ID 和产品 ID 。

我们正在编写的第二个脚本将通过从枚举中检索到的供应商 ID 和产品 ID 来访问 Buzz 控制器中的原始数据。让我们开始在树莓派上运行以下命令来编写枚举脚本。
sudo vim 枚举

2 在该文件中编写以下代码行。我们将解释每条至关重要的代码行,以使您了解所有这一切。

#!/usr/bin/env python

该行告诉命令行解释器它应该使用环境路径来查找并利用 Python 运行以下脚本。

在脚本开始处使用 shebang (#!) 行总是很方便的,因为它使运行变得更容易,而不必指定每次都要运行的程序。

从__future__导入 print _function

隐藏进口

第一行从 Python 3 导入 ** print 函数 ** 到我们当前的 Python 版本中,使我们能够在代码中使用它。

这是因为打印功能仅在 Python 3 中实现,** \ _ \ _ future \ _ \ _ ** 功能使我们能够将其转发到 Python 2

我们的第二个库导入了我们在本教程前面安装的” 隐藏库”,这将使我们能够与 USB 设备进行交互,并且对于此脚本,我们将枚举所有可用的 USB 设备。

对于 hid .enumerate()中的 d :
    键=列表(d.keys())
    keys.sort()
    用于键入密钥:
        print("%s:%s"%(key,d [key]))

此代码块是我们简短脚本的关键部分。它将利用 USB 库检索所有当前可用的 USB 设备的枚举。

然后它将遍历 ** hid ** 库提供给它的所有设备,从而创建每个设备的密钥列表。

然后,我们对这些键进行排序,以便以更好的格式打印出来。

最后,我们遍历提供给我们的所有密钥,然后将它们打印出来。打印输出将使我们能够找到有关设备的一些关键信息。

3 完成编写代码后,它应该看起来像我们在下面显示的内容。

如果您对代码正确感到满意,请依次按 ** CTRL + X ,然后按 Y **,最后按 ENTER ** 保存文件。

#!/usr/bin/env python
从__future__导入 print _function

隐藏进口

对于 hid .enumerate()中的 d :
    键=列表(d.keys())
    keys.sort()
    用于键入密钥:
        print("%s:%s"%(key,d [key]))

4 在运行我们刚编写的 Python 脚本之前,您需要首先将 Buzz 控制器插入树莓派。

我们还建议您取出其他 USB 设备,以便更轻松地找到 Buzz 控制器的详细信息。

将 Buzz Controller 插入树莓派后,您现在可以通过运行以下命令来运行脚本。确保为此使用 ** sudo **,因为它需要访问 USB 接口。

sudo python enumerate.py

5 从该脚本中,您应该看到类似于下面展示的输出,您应该看到制造商显示为”** Logitech “,产品 \ _string 显示为” Logitech Buzz (tm)” 控制器 **”。

您要在此处注意的两个值是 ** product \ _id vendor \ _id **,因为我们将在下一部分中开始使用该设备进行交谈。

interface_number:0
Manufacturer_string:罗技
路径:0001:0004:00
product_id:2
product_string:Logitech Buzz(tm)控制器 V1 
发行编号:4353
序列号 :
用法:0
usage_page:0
vendor_id:1356

6 在下一节中,我们将向您展示如何使用 ** vendor \ _id product \ _id ** 与 Buzz 控制器进行通信。

如果 ** vendor \ _id product \ _id ** 的值不同,则在上面的示例中,请确保您将它们记下来。

通过 Python 从 Buzz 控制器中检索数据

1 现在,由于检索了上一节中的 ** product \ _id ** 和 vendor \ _id **,我们现在可以继续学习如何从 buzz 控制器读取数据。

首先,编写脚本,该脚本将用于从 Buzz 控制器中读取所有数据。在树莓派上输入以下命令。

sudo vim read _controller.py

2 在此文件中,输入以下代码行,我们将解释在进行过程中重要的几行。

#!/usr/bin/env python

该行称为 shebang (特别是 **#!** 字符序列),命令行解释器读取该行并使用指定的应用程序来解释文件。

隐藏进口
导入时间

在这里,我们导入了两个重要的程序包,它们是启动和运行脚本所需的,因此我们可以从 Buzz Controller 中读取。

第一个软件包称为 ** hid ,它的使用使我们可以通过 Python 轻松与 USB 设备进行交互。该软件包将使我们能够从 Buzz 控制器中检索输入数据。除了 hid 包之外,我们还导入 time ** 包。我们利用时间包,因此可以将 Python 脚本休眠半秒钟,以停止在命令行中填充消息。
h = hid.device()

h.open(0x54c,0x002)

h.set_nonblocking(1)

我们首先从我们在本教程前面导入的 ** hid 库中创建一个对象。这将使我们能够访问库并使用我们在本教程前面获得的 vendor \ _id product \ _id ** 打开实际的 USB 设备。

现在使用 ** h.open 函数,我们可以指定 vendor \ _id ** 和 product \ _id **,并且库将尝试使用这些特定值连接到设备。

如果您在我们的示例中注意到,则我们使用的是十六进制版本的数字,其中 ** 0x54c 等于 1356 0x002 等于 2 **。

成功建立连接后,我们将继续为其启用非阻塞模式。此模式对于保持脚本平稳运行且不必暂停读取操作很重要。

非阻塞模式使之不会立即返回的任何读取调用都将返回值 0 ,而不是等待要返回的数据。

而 True :
        d = h.read(5)
        如果 d :
            打印(d)

在此代码块中,我们运行一个 while 循环,该循环无限运行。

在 while 循环的每个循环中,我们从设备读取 5 个字节的数据,如果有数据,我们将其打印出来。否则,我们将继续循环,直到用户终止脚本或检索到数据为止。

从技术上讲,我们应该在循环后运行 ** h.close ()**,但是由于我们的无限循环,它无法达到此目的。

3 输入完所有代码后,其外观应类似于我们在下面显示的内容。

对代码满意后,请依次按 ** CTRL + X ,然后按 Y **,最后按 ENTER * 保存文件。

#!/usr/bin/env python
隐藏进口
导入时间

h = hid.device()

h.open(0x54c,0x002)

h.set_nonblocking(1)

而 True :
        d = h.read(5)
        如果 d :
            打印(d)
        time.sleep(0.5)

4 现在,在将 Buzz 控制器插入树莓派的同时,运行以下命令以运行我们刚刚编写的脚本。确保使用 ** sudo **,因为它需要访问 hid 界面。

sudo python read_controller.py

5 在运行脚本时,您会注意到按下按钮时会看到一个包含 5 个不同值的字节数组。您可以忽略其中的前两个,因为它们永远不会改变,后三个值包含我们所按的按钮。

例如,如果您按下第一个控制器上的红色蜂鸣按钮,释放它,然后按下蓝色按钮,您将收到以下数据。

[127,127,1,0,240]
[127,127,0,0,240]
[127、 127 、 16 、 0 、 240 ]
[127,127,0,0,240]

6 通过按下每个按钮并记下它们的值及其出现的位置,我们可以算出在 Python 中检测按钮时需要注意的值。

如果写下所有值,则应该得到一个下表,如下所示。我们将使用此表来计算检测每个不同控制器的按钮按下情况所需的功能。

对于最后一个位置,我们从任何结果中减去 ** 240 以获得真实值,因为 240 是静止值。例如,如果我们获得值 241 ,我们将从该数字中减去 240 以得到值 1 **。

按钮** 控制器 1 **** 控制器 2 **** 控制器 3 **** 控制器 4 **
红色Pos 3 值 1Pos 3 值 32位置 4 的值 4Pos 4 值 128
黄色Pos 3 值 2Pos 3 值 64位置 4 的值 8Pos 5 值 1
绿色Pos 3 值 4Pos 3 值 128位置 4 的值 16Pos 5 Value 2
橙色Pos 3 值 8位置 4 的值 1Pos 4 值 32Pos 5 值 4
蓝色Pos 3 值 16位置 4 的值 2Pos 4 值 64Pos 5 Value 8

7 现在我们有了这个数据表,我们将进入教程的下一部分。

在下一部分中,我们将开始编写我们的库,该库将为访问 Buzz 控制器按钮的按下提供方便的点,并且还将触摸打开和关闭 Buzz 控制器 LED 的信息。

为 Buzz 控制器编写 Python 库

1 在树莓派问答游戏教程的这一部分中,我们将编写一个库,该库将使用 Pi 与 Buzz 控制器进行交互。

这个库将使我们稍后在本教程中更容易编写小型测验游戏。这将使我们能够轻松地修改和改进与 Buzz 控制器的通话方式。

要开始编写将用作库的 Python 脚本,请在您的 Pi 上运行以下命令。

vim BuzzController .py

2 在此文件中,输入以下代码行。在进行过程中,我们将解释代码的每个重要部分,以尝试使您了解所有工作原理,以便您对其进行修改以供使用。
隐藏进口
导入时间

在本教程中,我们已经多次解释了这些导入,因此我们将深入介绍目前使用它们的基础。

我们导入 ** hid ** 库,以便我们可以访问 USB 接口并依次与 Buzz 控制器进行通讯。

然后,我们导入 ** time ** 库,以便我们可以出于任何原因利用它来暂停脚本,特别是,我们将使用它来实现控制器闪烁功能。

BuzzController类:

该行定义了我们的班级名称,下面的所有代码都被标记为该班级的一部分。通过引用其名称”** BuzzController **”,我们将可以从此类访问变量和函数。

您将在本教程的后面部分看到有关如何访问此类的示例。

    light_array = [0x00、 0x00 、 0x00 、 0x00 、 0x00 、 0x00 、 0x00 、 0x00 ]

我们定义了一个名为 ** light \ _array ** 的变量,该变量包含一个 8 字节数组,均设置为 0x00 。我们对此进行定义,以便可以随时跟踪应打开或关闭哪些 LED 。

    light_blinking =假

这个 ** light \ _blinking 变量用于跟踪我们是否应该继续进行灯光闪烁循环。退出循环的唯一方法是,如果将此变量设置为 False **。

    buttonState = [
        {"red":错误,"blue":错误,"orange":错误,"green":错误,"yellow":错误},
        {"red":错误,"blue":错误,"orange":错误,"green":错误,"yellow":错误},
        {"red":错误,"blue":错误,"orange":错误,"green":错误,"yellow":错误},
        {"红色":错误,"蓝色":错误,"橙色":错误,"绿色":错误,"黄色":错误}
    ]

我们的最后一个主要变量是我们的 ** buttonState ** 数组,该数组包含四组,每组代表一个控制器。

每组都包含一个变量,用于每个可能的按钮,可以为”True **”(按下) 或”False **”(未按下)。

    def __init __():
        #实例化设备类
        self.hid = hid.device()

        #打开设备
        self.hid.open(0x54c,0x02)

        #设置非阻止模式
        self.hid.set_nonblocking(1)

        #清除 Buzz 控制器的 LED 
        self.hid.write(self.light_array)

在这里,我们定义了 ** \ _ \ _ init \ _ \ _ ** 函数。初始化类时,Python 会调用此函数。

在此功能中,我们设置了读取和写入 Buzz 控制器所需的所有内容。我们首先初始化 ** hid ** 类,然后使用之前检索的详细信息建立与 Buzz 控制器的连接。

然后,我们继续设置” 非阻塞” 模式,以便无论是否有要读取的数据都立即返回读取。

最后,我们将一些数据写入设备。这些数据只是我们空白的灯光阵列,以确保关闭所有 Buzz Controllers LED。

    def light_blink(自我,控制器):
        blink_lights_off = [0x00、 0x00 、 0x00 、 0x00 、 0x00 、 0x00 、 0x00 、 0x00 ]
        self.blink_lights_on = [0x00、 0x00 、 0x00 、 0x00 、 0x00 、 0x00 、 0x00 、 0x00 ]

        对于我在控制器中:
            self.blink_lights_on [i + 2] = 0xFF

        如果(不是 self .light_blinking):
            self.light_blinking =真
            眨眼=真
            而 self .light_blinking:
                如果(闪烁):
                    self.hid.write(self.blink_lights_on)
                其他:
                    self.hid.write(blink_lights_off)
                闪烁=不闪烁
                time.sleep(0.5)

            self.hid.write(self.light_array)

在这里,我们定义了 ** light \ _blink ** 函数。此功能将运行一个循环,该循环将持续打开和关闭我们指定控制器上的 LED 。

这段代码的工作方式是:首先遍历传递给它的指定控制器,对于我们指定的每个控制器,我们将 ** blink \ _lights \ _on 数组中的值从 0x00 (关闭) 更改为 0xFF **(开)

接下来,仅在我们尚未运行 ** self.light \ _blinking ** 循环的情况下。接下来,我们切换眨眼变量以知道是否需要将 blink \ _lights \ _on ** 或 blink \ _lights \ _off ** 写入 buzz 控制器。

    def get_button_status(self):
        数据= self.hid.read(5)
        如果数据:
            self.buttonState [0] ["red"] =((data [2]&0x01)!= 0)#red
            self.buttonState [0] ["yellow"] =(((data [2]&0x02)!= 0)#yellow
            self.buttonState [0] ["green"] =((data [2]&0x04)!= 0)#绿色
            self.buttonState [0] ["orange"] =((data [2]&0x08)!= 0)#orange
            self.buttonState [0] ["blue"] =((data [2]&0x10)!= 0)#blueself.buttonState [1] ["red"] =((data [2]&0x20)!= 0)#red self.buttonState [1] ["yellow"] =((data [2]&0x40)!= 0)#黄色
            self.buttonState [1] ["green"] =((data [2]&0x80)!= 0)#绿色
            self.buttonState [1] ["orange"] =((data [3]&0x01)!= 0)#orange
            self.buttonState [1] ["blue"] =((data [3]&0x02)!= 0)#blue

            self.buttonState [2] ["red"] =((data [3]&0x04)!= 0)#red
            self.buttonState [2] ["yellow"] =((data [3]&0x08)!= 0)#yellow
            self.buttonState [2] ["green"] =((data [3]&0x10)!= 0)#绿色
            self.buttonState [2] ["orange"] =((data [3]&0x20)!= 0)#orange
            self.buttonState [2] ["blue"] =((data [3]&0x40)!= 0)#blue

            self.buttonState [3] ["red"] =((data [3]&0x80)!= 0)#red
            self.buttonState [3] ["yellow"] =((data [4]&0x01)!= 0)#yellow
            self.buttonState [3] ["green"] =((data [4]&0x02)!= 0)#绿色
            self.buttonState [3] ["orange"] =((data [4]&0x04)!= 0)#orange
            self.buttonState [3] ["blue"] =((data [4]&0x08)!= 0)#blue
        返回 self .buttonState

这是我们最重要的功能之一,它从 Buzz 控制器读取按钮数据。我们每次从控制器读取的内容均为 5 个字节,因为这是我们知道控制器所有按钮的当前状态所需要的。

然后,我们继续解释刚刚从 Buzz 控制器读取的数据。为了解释这个日期,我们将利用在本教程前面部分的表格中写下的信息。

如果您看一下我们对第一个控制器的处理 (** self.buttonState \ [0] ),您会注意到我们正在使用表中的数组位置 ( data \ [2] * * 是我们在本教程前面打印的数组中的” 第三位置”。每个按钮还使用我们从数组中该位置获得的值 (例如 ** data \ [2 ]&0x01 **)

对于上一节中获取的数据值,我们使用按位和 (&) 运算符。如果数据值和按位运算符的结果不等于 0 ,则将值设置为 True ,但如果设置为 true ,则将该按钮的值设置为 False 。

只需简单地按位进行”“和”“运算,就可以通过比较数字的各个位来确保值实际上在返回的数据之内。当然,这比这要复杂一些,但是现在,我们将坚持简单的解释。

然后,我们继续处理所有四个控制器的读入数据。您应该与每个人一起注意,我们正在使用上一节中检索到的所有数据。

    def get_button_pressed(自己,控制器):
        按钮= self.get_button_status()
        对于键,按钮中的值[controller] .items():
            如果(值):
                返回键

** get \ _button \ _pressed 函数用作解释我们使用先前编写的 get \ _button \ _status ** 函数检索回的数据的助手。

在此函数中,我们从 ** get \ _button \ _status ** 函数中检索结果,并循环遍历指定控制器的值。

一旦找到不为 False 或为空的值,我们将立即返回数据,否则,如果未找到任何内容,我们将不返回任何内容。

    def controller_get_first_pressed(self,buzzButton,controllers = [0,1,2,3]):
        而 True :
            按钮= self.get_button_status()
            对于我在控制器中:
                如果(按钮[i] [buzzButton]):
                    还给我

** controller \ _get \ _first \ _pressed 函数非常类似于 get \ _button \ _pressed ** 函数。

该功能不会报告特定控制器按下的按钮,而是侦听控制器以查看它们是否按下了指定按钮。

它将一直等待直到收到响应,然后返回返回按下指定按钮的控制器的 ID 。

在本树莓派问答游戏教程的下一部分中,当我们编写问答游戏时,您将看到如何使用它。

    def light_blink_stop():
        self.light_blinking =假

light \ _blink \ _stop 函数将 light \ _blinking 变量更改为 False ,这将停止由 light \ _blink ** 函数启动的循环。需要再次运行 light \ _blink ** 才能运行此功能。

    def light_set(自身,控制器,状态):
        如果状态为 0x00 ,则 self .light_array [controller + 2] = 0xFF
        self.hid.write(self.light_array)

** light \ _set 函数采用指定的控制器,并将其值在我们的 light \ _array 中设置为 0xFF 0x00 ,具体取决于 status ** 的值。

在 ** light \ _array 中设置值后,我们便将该数据写回到控制器。3** 编写完所有代码后,您可以通过查看下面显示的代码来验证是否正确输入了所有内容。如果要下载代码,则可以在我们的 GitHub 上找到它。
一旦您对代码正确感到满意,就可以通过依次按 ** CTRL + X Y **,最后按 ENTERENTER 保存文件。

隐藏进口
导入时间

BuzzController类:
   light_array = [0x00、 0x00 、 0x00 、 0x00 、 0x00 、 0x00 、 0x00 、 0x00 ]
   light_blinking =假
   buttonState = [
        {"red":错误,"blue":错误,"orange":错误,"green":错误,"yellow":错误},
        {"red":错误,"blue":错误,"orange":错误,"green":错误,"yellow":错误},
        {"red":错误,"blue":错误,"orange":错误,"green":错误,"yellow":错误},
        {"红色":错误,"蓝色":错误,"橙色":错误,"绿色":错误,"黄色":错误}
    ]

   def __init __():
        #实例化设备类
        self.hid = hid.device()

        #打开设备
        self.hid.open(0x54c,0x02)

        #设置非阻止模式
        self.hid.set_nonblocking(1)

        #清除 Buzz 控制器的 LED 
        self.hid.write(self.light_array)

   def light_blink(自我,控制器):
        blink_lights_off = [0x00、 0x00 、 0x00 、 0x00 、 0x00 、 0x00 、 0x00 、 0x00 ]
        self.blink_lights_on = [0x00、 0x00 、 0x00 、 0x00 、 0x00 、 0x00 、 0x00 、 0x00 ]

        对于我在控制器中:
            self.blink_lights_on [i + 2] = 0xFF

        如果(不是 self .light_blinking):
            self.light_blinking =真
            眨眼=真
            而 self .light_blinking:
                如果(闪烁):
                    self.hid.write(self.blink_lights_on)
                其他:
                    self.hid.write(blink_lights_off)
                闪烁=不闪烁
                time.sleep(0.5)

            self.hid.write(self.light_array)
   def get_button_status(self):
        数据= self.hid.read(5)
        如果数据:
            self.buttonState [0] ["red"] =((data [2]&0x01)!= 0)#red
            self.buttonState [0] ["yellow"] =(((data [2]&0x02)!= 0)#yellow
            self.buttonState [0] ["green"] =((data [2]&0x04)!= 0)#绿色
            self.buttonState [0] ["orange"] =((data [2]&0x08)!= 0)#orange
            self.buttonState [0] ["blue"] =((data [2]&0x10)!= 0)#blue

            self.buttonState [1] ["red"] =((data [2]&0x20)!= 0)#red
            self.buttonState [1] ["yellow"] =((data [2]&0x40)!= 0)#yellow
            self.buttonState [1] ["green"] =((data [2]&0x80)!= 0)#绿色
            self.buttonState [1] ["orange"] =((data [3]&0x01)!= 0)#orange
            self.buttonState [1] ["blue"] =((data [3]&0x02)!= 0)#blue

            self.buttonState [2] ["red"] =((data [3]&0x04)!= 0)#red
            self.buttonState [2] ["yellow"] =((data [3]&0x08)!= 0)#yellow
            self.buttonState [2] ["green"] =((data [3]&0x10)!= 0)#绿色
            self.buttonState [2] ["orange"] =((data [3]&0x20)!= 0)#orange
            self.buttonState [2] ["blue"] =((data [3]&0x40)!= 0)#blue

            self.buttonState [3] ["red"] =((data [3]&0x80)!= 0)#red
            self.buttonState [3] ["yellow"] =((data [4]&0x01)!= 0)#yellow
            self.buttonState [3] ["green"] =((data [4]&0x02)!= 0)#绿色
            self.buttonState [3] ["orange"] =((data [4]&0x04)!= 0)#orange
            self.buttonState [3] ["blue"] =((data [4]&0x08)!= 0)#blue
        返回 self .buttonState
   def get_button_pressed(自己,控制器):
        按钮= self.get_button_status()
        对于键,按钮中的值[controller] .items():
            如果(值):
                返回键
   def controller_get_first_pressed(self,buzzButton,controllers = [0,1,2,3]):
        而 True :
            按钮= self.get_button_status()
            对于我在控制器中:
                如果(按钮[i] [buzzButton]):
                    还给我
   def light_blink_stop():
        self.light_blinking =假
   def light_set(自身,控制器,状态):
        如果状态为 0x00 ,则 self .light_array [controller + 2] = 0xFF
        self.hid.write(self.light_array)

4 现在,我们已经完成了 Buzz Controller 库的编写,接下来我们将向您展示如何在实践中使用该库。

为了展示该库,我们将编写一个简短的问答游戏来实现控制器库。这个小游戏将阅读一组问题,将答案分配给的按钮随机化,并实现一个简单的评分系统。

## 在树莓派问答游戏中利用我们的 Buzz 控制器库
1 在使用 Buzz 控制器教程的树莓派问答游戏的最后一部分中,我们将向您展示如何将我们在过去两个部分中所做的所有工作放在一起,以创建一个简单的问答游戏。

要开始编写测验游戏脚本,请在树莓派上运行以下命令。

vim quiz _game.py

2 在此文件中,您需要输入以下代码行,在每个重要的代码块之后,我们将尝试解释其工作方式。

从__future__导入 print _function

在整个教程中,我们都使用了这种导入方式。尽管它是 Python 3 的功能,但它允许我们在 Python 2 中使用打印功能。

导入 BuzzController 

在这里,我们导入刚刚编写的库。通过此导入,我们可以访问在上一节中创建的 ** buzzcontroller ** 类。

导入时间

导入时间库只是为了使脚本暂停一秒钟。

导入线程

我们利用线程库,这样我们就可以开始闪烁控制器 LED ,而无需通过在单独的线程上启动脚本来保留整个脚本。

从随机导入洗牌

我们最后一次导入脚本的功能是随机播放功能。这将使我们能够摇晃可能的答案,以便正确的答案并不总是分配给同一按钮。

import_questions = [
    {"问题":"澳大利亚的首都是什么","答案":["堪培拉","悉尼","霍巴特","墨尔本"]}},
    {"问题":"日本的首都是什么","答案":["东京","广岛","大阪","京都"]},
]
问题= []
分数= [0,0,0,0]

在这里,我们从定义我们的 ** import \ _questions ** 数组开始,此数组的每个元素将是一个新问题和一组答案。

每个集合都需要定义” 问题 “和” 答案 “数组。对于” 答案 “数组,第一个元素必须是正确的答案,而其他三个答案则是错误的选择。

例如,在第一个问题中,我们将”** question “定义为”** 澳大利亚的首都是什么”,并将”** answers “变量设置为 \ [“ Canberra ,”** 悉尼 “,” 霍巴特 “,” 墨尔本 “] 和” 堪培拉 “是对该问题的正确答案。

然后,我们定义一个名为问题的空数组,在该数组中,我们将附加一个 ** import \ _questions ** 数组的处理后的版本。这些准备好的问题将使游戏逻辑更容易操作。

最后,我们创建一个名为” 得分” 的数组,该数组将跟踪所有用户的正确答案。

在 import _questions中提问:
    按钮= ["蓝色","橙色","绿色","黄色"]
    new_answer = {}

此代码块循环遍历我们导入的问题,并将其转换为更易于管理的程序供我们处理。

我们从定义可能的按钮数组开始,这些按钮分别是蓝色,橙色,绿色,黄色。

然后,我们定义一个空集,其中将包含修改后的问题。

    随机播放(按钮)
    new_answer ['question'] =问题['question']

定义按钮数组之后,我们最终通过利用 shuffle 库来对按钮数组进行随机排序来使用它。

通过对数组进行改组,我们确保可以为任何可能的按钮设置正确的答案,并消除某人为每个答案记忆正确的按钮的能力。

整理数组后,我们便将实际问题复制到 ** new \ _answer ** 变量中。我们的问答游戏逻辑将输出该问题以及可能的答案。

    对于我在范围(4)中:
        如果 i == 0:
            new_answer ["正确"] =按钮[i]
        new_answer [buttons [i]] =问题["answers"] [i]
    questions.append(new_answer)

最后,我们利用 ** for 循环遍历 imported \ _questions answers **’数组。

我们知道数组中的第一个元素是正确的答案,所以我们检查是否在第一个循环中,然后将 ** new \ _answer 的” correct **” 元素设置为按钮数组中” 位置 0 “的值。

由于我们之前进行了改组,因此该值可以是” 蓝色 “,” 橙色 “,” 绿色 “或” 黄色 “。

然后,我们再次将另一个值分配给 ** new \ _answer 集合,这一次我们将答案值设置为该迭代的按钮值。例如, new \ _questions \ [“blue”] ** 将为其设置特定的答案。

我们将继续执行此操作,直到所有四个可能的按钮都设置了答案为止。

最后,我们将格式化后的 ** new \ _answer 值附加到 questions ** 变量中。

buzz = BuzzController.BuzzController()

在这里,我们通过实例化 buzz 变量来启动 Buzz 控制器库。通过执行此操作,我们将调用在库中定义的 ** \ _ \ _ init \ _ \ _ 函数。如果您还记得的话, \ _ \ _ init \ _ \ _ 函数是初始化 hid ** 库并与 Buzz Controller 建立连接的函数。这将使我们能够在游戏逻辑循环中访问 BuzzController 库的各个元素。
有疑问的地方:
question_answered = 错误
available_answers = [“蓝色”,” 橙色”,” 绿色”,” 黄色”]
available_controllers = [0,1,2,3]

在这里,我们开始一个循环,遍历所有可用的问题。这个循环可以运行任意长时间,因为我们只是依靠 Python 的”** for **” 来遍历数组。

对于每个循环,我们设置一些值 ** question \ _answered **,以了解问题是否已正确回答。

接下来,我们设置 ** available \ _answers **,每个循环的值应该是 Buzz 控制器上所有可能的按钮。我们使用此变量来跟踪仍然可以选择哪些答案。

最后,我们设置 ** available \ _controllers ** 数组。该数组包含每个可能允许回答的控制器的 ID 。

    而不是 question _answered:
        打印(question ["question"])

        为我在 available _answers中:
            打印(i +""+问题[i.lower()])

在这里,我们开始另一个 while 循环。该循环将一直运行,直到四个可能的控制器之一获得正确答案为止。

在每个循环中,我们都会向用户打印所要提问的问题,然后继续循环显示可用按钮,并在按钮颜色旁边打印出每个可能的答案。

        thread.start_new_thread(buzz.light_blink,(available_controllers,))
        控制器= buzz.controller_get_first_pressed("红色",available_controllers)
        buzz.light_blinking = False

在此代码块中,我们利用在脚本开头导入的线程库来启动一个新线程,该线程从 Buzz 控制器库运行 ** light \ _blink ** 函数。为此,我们还传入了可用的控制器。

它的作用是只允许能够回答控制器问题的用户闪烁,而不会影响当前的脚本循环。

然后,我们利用 ** controller \ _get \ _first \ _pressed ** 函数检查哪个可用控制器是第一个按下其控制器上闪烁的红色 Buzz 按钮的控制器。然后,我们将返回的控制器 ID 存储在我们的控制器变量中。

一旦用户按下其红色按钮,我们就可以通过将 Buzz Controller 库中的 ** light \ _blinking 变量设置为 False **,来阻止所有控制器连续闪烁。反过来,这也结束了我们旋转的线程。

        buzz.light_set(controller,True)
        time.sleep(0.5)

现在已经选择了控制器,我们将其控制器上的红灯切换为信号,表明该轮到他们回答问题了。

        而 True :
            按钮= buzz.get_button_pressed(控制器)

现在我们进入另一个”while 循环”**。该循环将一直运行,直到该控制器选择正确的答案或错误的答案为止。

在每个循环中,我们都使用 buzz 控制器库 ** get \ _button \ _pressed ** 函数来检索指定控制器所按下的任何按钮。

            如果按钮和按钮!="红色":
                如果按钮==问题["正确"]:
                    print("Controller"+ str(controller)+"是正确的")
                    question_answered =真
                    得分[控制器] + = 1
                    打破

然后,我们继续检查是否返回了一个按钮,并确保它不是阻止用户不小心按下红色按钮的时间过长的红色按钮,并自动触发”** Invalid Answer “(错误回答) 响应。

我们检查用户在控制器上按下的按钮是否与我们的正确答案按钮匹配。

如果用户选择了右键,我们首先会打印一条消息,通知他们,然后将 ** question \ _answered 变量设置为 True ,将控制器的得分提高 1 ,然后利用 break ** 退出当前的 while 循环。

                elif button.capitalize()在 available _answers中:
                    打印("抱歉,答案不正确")
                    available_controllers.remove(控制器)
                    available_answers.remove(button.capitalize())
                    打破

现在,如果用户没有选择正确的答案,我们将查看该按钮是否在我们的” 可用 \ _answers” 数组中。

如果该值在数组中,我们会通知用户他们选择了错误的答案,然后我们从 ** available \ _controllers 数组中删除该控制器,并从 available \ _answers ** 数组中删除此答案。因此其他用户无法选择它。

        buzz.light_set(controller,False)
    time.sleep(1)

打印("最终分数")
打印(分数)用户选择了答案后,我们将继续使用蜂鸣控制器库** light \ _set **函数关闭其灯光。

接下来,我们将脚本休眠一秒钟,然后继续循环浏览所有可用问题。

现在,如果游戏中没有问题要问用户,它将打印出文本” 最终分数 “,后跟我们在脚本前面设置的分数数组。

3 现在编写了我们的问答游戏脚本,您可以验证输入的代码是否正确。您也可以在本 GitHub 上找到本教程的完整代码 (https://github.com/pimylifeup/Raspberry-Pi-Quiz-Game-Buzz-Controllers)。

如果您对代码正确感到满意,则可以按 ** CTRL + X ,然后按 Y **,最后按 ENTER * 保存文件。

从__future__导入 print _function
导入 BuzzController 
导入时间
导入线程
从随机导入洗牌

import_questions = [
    {"问题":"澳大利亚的首都是什么","答案":["堪培拉","悉尼","霍巴特","墨尔本"]}},
    {"问题":"日本的首都是什么","答案":["东京","广岛","大阪","京都"]},
]
问题= []
分数= [0,0,0,0]

在 import _questions中提问:
    按钮= ["蓝色","橙色","绿色","黄色"]
    new_answer = {}
    随机播放(按钮)
    new_answer ['question'] =问题['question']
    对于我在范围(4)中:
        如果 i == 0:
             new_answer ["正确"] =按钮[i]
        new_answer [buttons [i]] =问题["answers"] [i]
    questions.append(new_answer)

buzz = BuzzController.BuzzController()

有疑问的地方:
    question_answered =错误
    available_answers = ["蓝色","橙色","绿色","黄色"]
    available_controllers = [0,1,2,3]

    而不是 question _answered:
        打印(question ["question"])

        为我在 available _answers中:
            打印(i +""+问题[i.lower()])

        thread.start_new_thread(buzz.light_blink,(available_controllers,))
        控制器= buzz.controller_get_first_pressed("红色",available_controllers)
        buzz.light_blinking = False
        buzz.light_set(controller,True)
        time.sleep(0.5)

        而 True :
            按钮= buzz.get_button_pressed(控制器)
            如果按钮和按钮!="红色":
                如果按钮==问题["正确"]:
                    print("Controller"+ str(controller)+"是正确的")
                    question_answered =真
                    得分[控制器] + = 1
                    打破
                elif button.capitalize()在 available _answers中:
                    打印("抱歉,答案不正确")
                    available_controllers.remove(控制器)
                    available_answers.remove(button.capitalize())
                    打破
        buzz.light_set(controller,False)
    time.sleep(1)

打印("最终分数")
打印(分数)

4 现在,我们已经编写了脚本,我们继续在树莓派上运行以下命令来启动它。

在执行此操作之前,请记住先将 Buzz 控制器连接到树莓派,否则会遇到错误。

sudo python quiz_game.py

5 您现在应该可以通过树莓派测验游戏进行游戏。如果您使用的是示例问题,那么您应该得到的结果与下面显示的结果类似。

澳大利亚的首都是什么
蓝色悉尼
橙色墨尔本
格林霍巴特
黄堪培拉
抱歉,答案不正确
澳大利亚的首都是什么
蓝色悉尼
格林霍巴特
黄堪培拉
控制器 3 是正确的
日本的首都是什么
蓝色广岛
橙色京都
绿色大阪
黄色东京
控制器 2 是正确的
最终成绩
[0,0,1,1]

6 到现在,您应该已经使脚本成功运行并且可以与 Buzz Controller 正常对话。希望您现在对一切工作原理有所了解,并能够自己扩展。

您可以通过进一步扩展此脚本来挑战自己。第一个改进将是使问题通过文本文件导入。第二个是对分数和错误答案提供更好的反馈。

我希望到本教程结束时,您将学到一些与 USB 控制器交互的知识。在学习如何解释它们返回的数据以及如何编写充当该控制器接口的库的过程中。

如果您使用 Buzz 控制器教程对树莓派测验游戏有任何反馈,请随时在下面给我们留下评论。

分享到