ultrasonic demo

This commit is contained in:
sityliu 2024-04-20 02:24:47 +08:00
parent 3f16c2940c
commit 3405769c14
23 changed files with 422 additions and 82 deletions

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.9" project-jdk-type="Python SDK" />
<component name="ProjectRootManager" version="2" project-jdk-name="C:\ruanjian\anaconda\envs\test_env1" project-jdk-type="Python SDK" />
<component name="PyCharmProfessionalAdvertiser">
<option name="shown" value="true" />
</component>

View File

@ -2,7 +2,7 @@
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="jdk" jdkName="C:\ruanjian\anaconda\envs\test_env1" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

6
.idea/vcs.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

110
README.md
View File

@ -1,71 +1,63 @@
## Python项目结构示例
## Python 获取超声波传感器数据
### 典型的Python项目的项目结构
### 简介
```py
myproject/
├── myproject/
│ ├── __init__.py
│ ├── module1.py
│ ├── module2.py
│ └── ...
├── tests/
│ ├── __init__.py
│ ├── test_module1.py
│ ├── test_module2.py
│ └── ...
├── docs/
├── README.md
├── requirements.txt
└── setup.py
该仓库为超声波传感器数据的获取TTL串口通信数据该超声波有 6 种工作模式代码中只使用模式1/2 ,两种模式足以
运行方式:
```shell
# 安装依赖
pip install -r requirements.txt
# 直接运行 run.py 为默认模式模式1
python run.py
# 带参数说明:
python run.py -m 1
# usage: run.py [-h] [-m {1,2}]
# optional arguments:
# -h, --help show this help message and exit
# -m {1,2}, --mode {1,2} 选择模式1或2默认为1
```
- `myproject/`项目的根目录也是Python包的根目录。
- `myproject/__init__.py`:一个空的`__init__.py`文件,用于将`myproject`目录标记为一个Python包。
- `myproject/module1.py`、`myproject/module2.py`等:项目的模块文件,包含项目的核心代码。
- `tests/`:测试目录,包含用于测试项目代码的测试文件。
- `docs/`:文档目录,包含项目的文档文件。
- `README.md`项目的说明文档通常使用Markdown格式编写。
- `requirements.txt`:项目的依赖文件,列出了项目所需的所有依赖包及其版本号。
- `setup.py`项目的安装文件用于将项目打包为可安装的Python包。
这只是一个基本的项目结构示例,实际项目的结构可能会根据具体需求有所不同。
### 示例一个典型的flask项目目录结构
### 项目结构:
```py
myflaskproject/
├── app/
│ ├── __init__.py
│ ├── models.py
│ ├── views.py
│ ├── templates/
│ │ ├── base.html
│ │ ├── home.html
│ │ └── ...
│ └── static/
│ ├── css/
│ ├── js/
│ └── ...
├── config.py
├── requirements.txt
├── run.py
└── README.md
ultrasonic_ttl> tree /f
│ README.md
│ requirements.txt
│ run.py
├─.idea
│ │ .gitignore
│ │ misc.xml
│ │ modules.xml
│ │ python_code.iml
│ │ vcs.xml
├─docs
│ JSN-SR04T-V3.0带线款说明书中性.pdf
│ JSN超声波Demo2.0.rar
└─tests
test.py
```
- `app/`:应用程序目录,包含应用程序的核心代码。
- `app/__init__.py`应用程序的初始化文件创建Flask应用对象并配置应用程序。
- `app/models.py`:应用程序的模型文件,包含数据库模型定义。
- `app/views.py`:应用程序的视图文件,包含路由和视图函数的定义。
- `app/templates/`模板目录包含应用程序的HTML模板文件。
- `app/static/`静态文件目录包含应用程序的静态资源文件如CSS、JavaScript等。
- `config.py`:配置文件,包含应用程序的配置信息。
- `requirements.txt`:项目的依赖文件,列出了项目所需的所有依赖包及其版本号。
- `run.py`:应用程序的入口文件,用于启动应用程序。
- `README.md`项目的说明文档通常使用Markdown格式编写。
### 使用设备
https://item.taobao.com/item.htm?id=42087449148
### 说明和上位机
├─docs
│ JSN-SR04T-V3.0带线款说明书中性.pdf
│ JSN超声波Demo2.0.rar

Binary file not shown.

Binary file not shown.

View File

View File

View File

View File

66
app/ultrasonic_ttl.py Normal file
View File

@ -0,0 +1,66 @@
# -*- coding: utf-8 -*-
import serial
import time
import modbus_tk
print("Hello,world!")
def Init(port, baudrate, timeout):
try:
# 读取数据慢可以设置 timeout 参数来增加缓冲区大小
ser = serial.Serial(port=port, baudrate=baudrate, bytesize=8,parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_ONE, timeout=timeout)
if ser.isOpen():
print("Port is open")
except:
print("请检查串口是否被占用;或者串口出现异常!")
exit()
return ser
# 模式1自动串口输出
def Mode_M1(ser):
while True:
try:
com_input = ser.read(4) # 每次读10字节的数据可以指定decodeser.read().decode()
data = int(com_input.hex()[2:6], 16)
dis = round(data / 10, 1)
# print(com_input.hex()[2:6])
# print('{}CM'.format(round(data / 10, 1)))
if dis >= 21:
print('{}CM'.format(dis))
else:
# 注: 模块在盲区内输出最近的距离值约 21cm
print('无效测量范围')
except KeyboardInterrupt:
ser.close()
except:
print("无效的十六进制字符串")
# 模式2串口受控输出发指令 0X55 信号)
def Mode_M2(ser):
data = bytearray([0x55])
while True:
ser.write(bytes(data))
# print("发送帧:", data)
time.sleep(0.1)
recbytes = ser.readline()
try:
hex_list = [hex(b)[2:] for b in recbytes]
hex_data = hex_list[1] + hex_list[2]
dis = round(int(hex_data, 16) / 10, 1)
if dis >= 21:
print('{}CM'.format(dis))
else:
# 注: 模块在盲区内输出最近的距离值约 21cm
print('无效测量范围')
except KeyboardInterrupt:
ser.close()
except:
print("获取列表索引超出范围")

View File

View File

@ -1,3 +0,0 @@
class Config:
SQLALCHEMY_DATABASE_URI = 'sqlite:///app.db'
SQLALCHEMY_TRACK_MODIFICATIONS = False

Binary file not shown.

Binary file not shown.

View File

View File

@ -1,3 +1,3 @@
-i https://pypi.tuna.tsinghua.edu.cn/simple
Flask
Flask-RESTful
pyserial
modbus-tk

17
run.py
View File

@ -1,4 +1,17 @@
from app import app
import argparse
from app import ultrasonic_ttl
if __name__ == '__main__':
app.run()
def main(mode):
ser = ultrasonic_ttl.Init('COM6', 9600, 0.1)
if mode == 1:
# 模式1自动串口输出
ultrasonic_ttl.Mode_M1(ser)
elif mode == 2:
# 模式2串口受控输出发指令 0X55 信号)
ultrasonic_ttl.Mode_M2(ser)
parser = argparse.ArgumentParser(description="切换两种模式默认模式1")
parser.add_argument("-m", "--mode", type=int, choices=[1, 2], default=1, help="选择模式1或2默认为1")
args = parser.parse_args()
main(args.mode)

View File

280
tests/test.py Normal file
View File

@ -0,0 +1,280 @@
# -*- coding: utf-8 -*-
import struct
import serial
import sys
import time
import modbus_tk
from modbus_tk import modbus_rtu
print("Hello,world!")
'''
在Python中读取485数据的过程可以分为以下几个步骤
创建Serial对象使用Python的serial库创建一个Serial对象用于与485设备进行通信
打开串口使用Serial对象的open()方法打开串口建立与485设备的连接
配置串口参数设置串口的波特率数据位停止位校验位等参数
读取数据使用Serial对象的read()方法读取485设备发送的数据
解析数据根据实际需求对读取到的数据进行解析和处理
关闭串口使用Serial对象的close()方法关闭串口连接'''
# 02:06:44.552→发 01 06 00 00 00 01 48 0A
# 02:06:51.794→发 01 06 00 00 00 00 89 CA
# 02:06:56.833→发 01 06 00 01 00 01 19 CA
# 02:06:58.360→发 01 06 00 01 00 00 D8 0A
# 01 04 00 00 00 01 31 CA
# 打开串口
# port = "COM7"
# baud = 38400
# timeout = 1
# ser = serial.Serial(port=port, baudrate=baud, bytesize=8,parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_ONE, timeout=1)
# if ser.isOpen():
# print("Port is open")
# print(ser)
# # # 构建串口数据(发布和接收)
# # msg = "01 04 00 00 00 01 31 CA"
# # 发送帧 01 04 00 00 00 03 B0 OB
# # 站号 01
# # 功能码 04
# # 起始地址: 00 00=0X0000起始地址=0
# # 寄存器数: 00 03=0X0003寄存器数=3
# # CRC校验 B0 OB低字节在前高字节在后
# def crc16(data: bytes):
# crc = 0xFFFF
# for pos in data:
# crc ^= pos
# for i in range(8):
# if ((crc & 1) != 0):
# crc >>= 1
# crc ^= 0xA001
# else:
# crc >>= 1
# return ((crc & 0xff) << 8) + (crc >> 8)
# # 示例
# data = bytearray([0x01, 0x03, 0x00, 0x00, 0x00, 0x02])
# print(data)
# crc = crc16(data)
# crc = "{:04x}".format(crc)
# print(crc)
# # 010400000003B0OB
# msg = "010300000002"
# msg1 = msg + crc
# sendbytes = bytes.fromhex(msg1)
# data_to_send = [0x01, 0x06, 0x00, 0x00, 0x00, 0x01, 0x48, 0x0A]
# data_to_send2 = [0x01, 0x06, 0x00, 0x00, 0x00, 0x00, 0x89, 0xCA]
# read_aisle1 = [0x01, 0x04, 0x00, 0x00, 0x00, 0x01, 0x31, 0xCA]
# ser.write(bytes(data_to_send))
# time.sleep(0.2)
# buffer_data = ser.in_waiting
# return_data = ser.read(buffer_data)
# # address = ser.read(1) # 站号
# # function_code = ser.read(1) # 功能码
# # data_length = ser.read(2) # 起始地址
# # humidity = ser.read(2) # 读取数据2字节
# # crc1 = ser.read(2) # 读取CRC校验2字节
# # humidity_value = int.from_bytes(temperature, byteorder='big')
# print(return_data)
# # print(address, function_code, data_length, humidity, crc1)
# # print(crc1)
# return_data2 = ser.read(ser.in_waiting)
# print(return_data2)
# time.sleep(1)
# ser.write(bytes(data_to_send2))
# 98--27
CRC_HI = [
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40
]
CRC_LO = [
0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7, 0x05, 0xC5, 0xC4,0x04,
0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, 0x08, 0xC8,
0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC,
0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3, 0x11, 0xD1, 0xD0, 0x10,
0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4,
0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A, 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38,
0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED, 0xEC, 0x2C,
0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26, 0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0,
0xA0, 0x60, 0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4,
0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, 0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68,
0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C,
0xB4, 0x74, 0x75, 0xB5, 0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0,
0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54,
0x9C, 0x5C, 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98,
0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83, 0x41, 0x81, 0x80,0x40
]
def CRC_16(str, usDataLen):
uchCRCHi = 0xFF # high byte of CRC initialized
uchCRCLo = 0xFF # low byte of CRC initialized
while usDataLen > 0: # pass through message buffer
uIndex = uchCRCHi ^ str[0] # calculate the CRC
uchCRCHi = uchCRCLo ^ CRC_HI[uIndex]
uchCRCLo = CRC_LO[uIndex]
str = str[1:]
usDataLen -= 1
return (uchCRCHi << 8) | uchCRCLo
def calc_crc16(string):
data = bytearray.fromhex(string)
# logging.info(type(data))
crc = 0xFFFF
for pos in data:
crc ^= pos
for i in range(8):
if ((crc & 1) != 0):
crc >>= 1
crc ^= 0xA001
else:
crc >>= 1
return ((crc & 0xff) << 8) + (crc >> 8)
# 485温度
'''
温度读取指令
01 03 00 00 00 01 84 0A
'''
# mode = sys.argv[1]
#
# print("参数1:", sys.argv[1])
#
# print(mode)
port = "COM6"
baud = 9600
timeout = 1
ser = serial.Serial(port=port, baudrate=baud, bytesize=8,parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_ONE, timeout=1)
if ser.isOpen():
print("Port is open")
print(ser)
def crc16(data: bytes) -> int:
crc = 0xFFFF
for b in data:
cur_byte = 0xFF & b
for _ in range(0, 8):
if (crc & 0x0001) ^ (cur_byte & 0x0001):
crc = (crc >> 1) ^ 0xA001
else:
crc >>= 1
cur_byte >>= 1
return ((crc & 0xFF) << 8) | (crc >> 8)
# 示例
# 01 03 00 00 00 01 84 0A
data = bytearray([0x55])
print(data)
crc = crc16(data)
crc = "{:04x}".format(crc)
print(crc)
# 010400000003B0OB
msg = "010300000001"
msg1 = msg + crc
sendbytes = bytes.fromhex(msg1)
data_to_send = [0x01, 0x03, 0x00, 0x00, 0x00, 0x01, 0x84, 0x0A]
# while(1):
# # ser.write(bytes(data))
# # time.sleep(0.1)
#
#
# recbytes = ser.readline()
# # return_data2 = ser.read(ser.in_waiting)
# print(recbytes)
#
# templist = list(recbytes)
# # mode = templist[2]
# # light = int(templist[3])
# # left_time_low = float(templist[5])/16
# print(templist)
# # time.sleep(1)
while True:
com_input = ser.read(4) # 每次读10字节的数据可以指定decodeser.read().decode()
if com_input: # 如果读取结果非空,则输出
print(com_input) # b'ABC123'
templist = list(com_input)
print(templist)
temp_data = templist[1] + templist[2]
print(type(temp_data))
print(temp_data)
print(com_input.hex())
data = int(com_input.hex()[2:6],16)
print(com_input.hex()[2:6])
print('{}CM'.format(round(data / 10, 1)))
# print(com_input.decode('utf-8')) # ABC123
else:
break
# unsigned int CRC_16(unsigned char *str,unsigned int usDataLen)
# {
# unsigned char uchCRCHi = 0xFF ; /* high byte of CRC initialized */
# unsigned char uchCRCLo = 0xFF ; /* low byte of CRC initialized */
# unsigned uIndex ; /* will index into CRC lookup table */
# while (usDataLen--)/* pass through message buffer */
# {
# uIndex = uchCRCHi ^ *str++ ; /* calculate the CRC */
# uchCRCHi = uchCRCLo ^ auchCRCHi[uIndex];
# uchCRCLo = auchCRCLo[uIndex] ;
# }
# return (uchCRCHi << 8 | uchCRCLo) ;
# }
# def CRC_16(str, usDataLen):
# uchCRCHi = 0xFF # high byte of CRC initialized
# uchCRCLo = 0xFF # low byte of CRC initialized
# while usDataLen > 0: # pass through message buffer
# uIndex = uchCRCHi ^ str[0] # calculate the CRC
# uchCRCHi = uchCRCLo ^ auchCRCHi[uIndex]
# uchCRCLo = auchCRCLo[uIndex]
# str = str[1:]
# usDataLen -= 1
# return (uchCRCHi << 8) | uchCRCLo
# def crc_16(data: bytes) -> int:
# crc_hi = 0xFF
# crc_lo = 0xFF
# for byte in data:
# index = crc_hi ^ byte
# crc_hi = crc_lo ^ CRC_HI[index]
# crc_lo = CRC_LO[index]
# return (crc_hi << 8) | crc_lo

View File

@ -1,13 +0,0 @@
import unittest
from app import app
class UserTestCase(unittest.TestCase):
def setUp(self):
self.app = app.test_client()
def test_get_user(self):
response = self.app.get('/users/1')
data = response.get_json()
self.assertEqual(response.status_code, 200)
self.assertEqual(data['username'], 'john')
self.assertEqual(data['email'], 'john@example.com')

View File

@ -1 +0,0 @@
print("Hello World!")