实验简介
简要做一个小实验,通过检测光线亮暗与烟雾是否存在继而通过短信模块来发送到指定电话号码上
似乎当时想要模拟检测气体泄漏和光线状况的这种情况(如工厂、楼层之类的场景),后来老师提醒光敏不适合这些场景,推荐用红外传感,然后没做成不了了之
实验目的
- 使用 GPIO 控制引脚来与传感器通信和控制外部设备
- 配置 GSM 模块以发送短信,包括设置短信模式和字符集
准备材料
- DragonBoard 410c x1
- 4脚光敏电阻传感器模块(MH-Sensor Flying-Fish)x1
- MQ-2 烟雾传感器 x1
- SIM800A GMS模块 x1
- 杜邦线若干
- 面包板 x1
- 显示屏 x1
- HDMI转换器、电源适配器
GPIO 引脚相关知识
96Boards 规范要求有高速扩展连接口与低速扩展连接口
实验中仅用到低速扩展连接口,低速扩展连接口 GPIO 与 物理引脚对应图如下
低速扩展连接口
410c Signals | 96Boards Signals | PIN | PIN | 96Boards Signals | 410c Signals |
---|---|---|---|---|---|
GND | GND | 1 | 2 | GND | GND |
UART0_CTS_N (APQ GPIO_2) | UART0_CTS | 3 | 4 | PWR_BTN_N | PHONE_ON_N |
UART0_TX (APQ GPIO_0) | UART0_TxD | 5 | 6 | RST_BTN_N | PM_RESIN_N |
UART0_RX (APQ GPIO_1) | UART0_RxD | 7 | 8 | SPI0_SCLK | SPI0_CLK (APQ GPIO_19) |
UART0_RTS_N (APQ GPIO_3) | UART0_RTS | 9 | 10 | SPI0_DIN | SPI0_MISO (APQ GPIO_17) |
UART1_TX (APQ GPIO_4) | UART1_TxD | 11 | 12 | SPI0_CS | SPI0_CS_N (APQ GPIO_18) |
UART1_RX (APQ GPIO_5) | UART1_RxD | 13 | 14 | SPI0_DOUT | SPI0_MOSI (APQ GPIO_16) |
I2C0_SCL (APQ GPIO_7) | I2C0_SCL | 15 | 16 | PCM_FS | LS_EXP_MI2S_WS (APQ GPIO_110) |
I2C0_SDA (APQ GPIO_6) | I2C0_SDA | 17 | 18 | PCM_CLK | LS_EXP_MI2S_SCK(APQ GPIO_113)(ALPS_INT) |
I2C1_SCL (APQ GPIO_23) | I2C1_SCL | 19 | 20 | PCM_DO | LS_EXP_MI2S_DATA0 (APQ GPIO_114) |
I2C1_SDA (APQ GPIO_22) | I2C1_SDA | 21 | 22 | PCM_DI | N.C. |
LS_EXP_GPIO_A (APQ GPIO_36) (APQ INT) | GPIO-A | 23 | 24 | GPIO-B | LS_EXP_GPIO_B (APQ GPIO_12) (TS_RST_N) |
LS_EXP_GPIO_C (APQ GPIO_13) (TS_INT_N) | GPIO-C | 25 | 26 | GPIO-D | LS_EXP_GPIO_D (APQ GPIO_69) (MAG_INT) |
LS_EXP_GPIO_E (APQ GPIO_115) (GYRO_ACCL_INT_N) | GPIO-E | 27 | 28 | GPIO-F | LS_EXP_GPIO_F (PM_MPP_4) (DSI_BLCTRL) |
LS_EXP_GPIO_G (APQ GPIO_24) (DSI_VSYNC) | GPIO-G | 29 | 30 | GPIO-H | LS_EXP_GPIO_H (APQ GPIO_25) (DSI_RST) |
LS_EXP_GPIO_I (APQ GPIO_35) (CSI0_RST) | GPIO-I | 31 | 32 | GPIO-J | LS_EXP_GPIO_J (APQ GPIO_34) (CSI0_PWDN) |
LS_EXP_GPIO_K (APQ GPIO_28) (CSI1_RST) | GPIO-K | 33 | 34 | GPIO-L | LS_EXP_GPIO_L (APQ GPIO_33) (CSI1_PWDN) |
LS_EXP_1P8 | +1V8 | 35 | 36 | SYS_DCIN | SYS_DCIN |
SYS_5P0 | +5V | 37 | 38 | SYC_DCIN | SYS_DCIN |
GND | GND | 39 | 40 | GND | GND |
Linux 操作系统上控制 GPIO 引脚
#/sys/class/gpio
#get root permission (linaro -> root)
sudo -i #sudo su
#export
echo ${GPIO_NUM} > /sys/class/gpio/export
#unexport
echo ${GPIO_NUM} > /sys/class/gpio/unexport
#direction
echo in > sys/class/gpio/gpio${GPIO_NUM}/direction
echo out > sys/class/gpio/gpio${GPIO_NUM}/direction
电路连接
光敏电阻模块的 VCC 引脚接到 DragonBoard 410c 的 5V 引脚上,将 GND 引脚接到 DragonBoard 410c 的 GND 引脚上,将 DO 引脚接到 DragonBoard 410c 的 GPIO 物理引脚 34 上
将烟雾传感器模块的 VCC 引脚接到 DragonBoard 410c 的 5V 引脚上,将 GND 引脚接到 DragonBoard 410c 的 GND 引脚上,将 DO 引脚接到 DragonBoard 410c 的 GPIO 物理引脚 30 上
物理引脚 40 接 GND
SIM800A 在实验开始前通过 USB 接口连接到开发板上,并开启
电路简图被我损坏,暂无
编写代码
需要准备好 pyserial 库以及下面微调的 GPIOLibrary 库,代码部分可在参考资料栏中的 GitHub 链接处点击浏览
pyserial 负责串口通信,GPIOLibrary 库包括 GPIOProcessor 类 和 GPIO 类,注释有描述作用
GPIOLibrary.py
微调部分指的是 getPin()
部分,获取 DragonBoard 410c 引脚值需要在基础上加上 390
物理引脚 30 对应 GPIO 25,加上 390 后即为 415
物理引脚 34 对应 GPIO 33,加上 390 即为 423
class GPIOProcessor:
'''This is the GPIO Processor class. IT is used to create GPIO objects and
keep track of them. Pins 23-34 have the their corresponding GPIO number
stored so that they may easily called with the appropriate getPin method.
'''
def __init__(self):
self.GPIOList = []
def getPin(self, pin_number):
'''The getPin method is used to create a GPIO object. After a GPIO is
created it is added to a list to keep track of all GPIO's being used.
'''
pin = GPIO(pin_number)
pin.openPin()
self.GPIOList.append(pin)
return pin
def getPin23(self):
return self.getPin(36)
def getPin24(self):
return self.getPin(12)
def getPin25(self):
return self.getPin(13)
def getPin26(self):
return self.getPin(69)
def getPin27(self):
return self.getPin(115)
def getPin28(self):
return self.getPin(901)
def getPin29(self):
return self.getPin(24)
def getPin30(self):
return self.getPin(415)
def getPin31(self):
return self.getPin(35)
def getPin32(self):
return self.getPin(424)
def getPin33(self):
return self.getPin(28+390)
def getPin34(self):
return self.getPin(423)
def cleanup(self):
'''The cleanup method should be called at the end of every program that
uses GPIO's. It changes the GPIO's back to inputs(which is safer when
not in use) and then unexports all GPIO's that were used.
'''
for pin in self.GPIOList:
pin.input()
pin.closePin()
self.GPIOList = []
class GPIO:
'''This is the GPIO class. This class contains methods that can be used to
control the GPIO's. It is used by the GPIOProcessor class. These methods
open the appropriate file to export GPIOs or change direction and value.
'''
global PATH
PATH = "/sys/class/gpio/"
def __init__(self, pin_number):
self.pin_number = pin_number
def openPin(self):
file = open(PATH + "export", 'w')
file.write(str(self.pin_number))
file.close()
def closePin(self):
file = open(PATH + "unexport", 'w')
file.write(str(self.pin_number))
file.close()
def setDirection(self, direction):
file = open(PATH + "gpio" + str(self.pin_number) + "/direction", 'w')
file.write(str(direction))
file.close()
def setValue(self, value):
file = open(PATH + "gpio" + str(self.pin_number) + "/value", 'w')
file.write(str(value))
file.close()
def getDirection(self):
file = open(PATH + "gpio" + str(self.pin_number) + "/direction", 'r')
direction = file.read()
file.close()
return direction
def getValue(self):
file = open(PATH + "gpio" + str(self.pin_number) + "/value", 'r')
value = file.read()
file.close()
return int(value)
def high(self):
self.setValue(1)
def low(self):
self.setValue(0)
def input(self):
self.setDirection("in")
def out(self):
self.setDirection("out")
main.py
phone_num = ''
处可以指定电话号码
from GPIOLibrary import GPIOProcessor
from GPIOLibrary import GPIO
import os
import serial
from time import sleep
class GasLight:
def __init__(self):
self.gpio = GPIOProcessor()
self.port = serial.Serial(port='/dev/ttyUSB0', baudrate=115200, bytesize=8, stopbits=1, timeout=2)
self.gas_message_sent = False
self.light_message_sent = False
if self.port.isOpen():
print("Serial is opened!")
print(self.port)
print(self.port.name)
else:
print("Serial do not open!")
def gpio_close(self, pin):
self.export_path = f"/sys/class/gpio/gpio{pin}"
if os.path.exists(self.export_path):
self.gpioControl = GPIO(pin)
self.gpioControl.closePin()
def gpio_input(self):
self.gpio_30 = self.gpio.getPin30()
self.gpio_30.setDirection("in")
self.gpio_34 = self.gpio.getPin34()
self.gpio_34.setDirection("in")
def get_gpio_value(self):
self.gas = self.gpio_30.getValue()
self.light = self.gpio_34.getValue()
return self.gas, self.light
def dbg_sim800a(self, data):
self.res_ser = ""
if data != "":
self.port.write(data.encode())
sleep(1)
print("send to sim800a: " + data)
if "AT" == str(data):
sleep(3)
if self.port.inWaiting() > 0:
while True:
self.res_ser = self.port.readall()
if self.res_ser == "":
continue
else:
break
if ">" in str(self.res_ser):
print(data + "successfully!")
print(self.res_ser)
print("you can send message!")
self.res_ser = ""
if "OK" in str(self.res_ser):
print(data + "successfully!\n")
print(self.res_ser)
self.res_ser = ""
else:
print(data + "unsuccessfully!\n")
print(self.res_ser)
self.res_ser = ""
def send_gas_message(self):
if not self.gas_message_sent:
self.port.write(f"AT+CMGS=\"{phone_num}\"\r\n".encode())
sleep(1)
self.port.write("Gas leakage.".encode())
sleep(1)
self.port.write("\x1A".encode())
sleep(5)
self.gas_message_sent = True
def send_light_message(self):
if not self.light_message_sent:
self.port.write(f"AT+CMGS=\"{phone_num}\"\r\n".encode())
sleep(1)
self.port.write("It's dark now!!".encode())
sleep(1)
self.port.write("\x1A".encode())
sleep(5)
self.light_message_sent = True
if __name__ == '__main__':
phone_num = ''
my_board = GasLight()
my_board.dbg_sim800a("AT\r\n")
my_board.dbg_sim800a("AT+CMGF=1\r\n")
my_board.dbg_sim800a("AT+CSCS=\"GSM\"\r\n")
my_board.dbg_sim800a("AT+CSMP=17,167,0,0\r\n")
my_board.gpio_close(415)
my_board.gpio_close(423)
sleep(1)
my_board.gpio_input()
sleep(2)
while True:
gas_value, light_value = my_board.get_gpio_value()
if gas_value == 0:
print(f"gas: {gas_value}, message will be sent!")
my_board.send_gas_message()
my_board.gas_message_sent = False
else:
print(f"gas: {gas_value}, status fine.")
sleep(5)
if light_value == 1:
print(f"light: {light_value}, message will be sent.")
my_board.send_light_message()
my_board.light_message_sent = False
else:
print(f"light: {light_value}, status fine.")
sleep(5)
实际操作
- 使用 SD 卡为 DragonBoard 开发板安装 Linux 操作系统
- 设置相应的 Python 环境
- 电源处于关闭状态下连线至开发板上
- 开机连接至显示器并通过终端测试运行代码
- 处于亮暗条件下测试光敏电阻传感器是否正常,释放出一小撮烟雾测试烟雾传感器是否正常
- 在 4 的条件下,根据 value 来向 SIM800A 发出指令,发送短信至指定电话号码
参考资料
DragonBoard 410c Hardware User Manual - 96Boards
DragonBoard 410c - Tutorials - Qualcomm Developer Network
Dragonboard Pin Mappings - Windows IoT | Microsoft Learn
DragonBoard: How to Access GPIOs Using Python : 8 Steps - Instructables
GPIO export on linux - Invalid argument - Products Support / DragonBoard410c - 96Boards Forum