重庆交通大学信息科学与工程学院
《嵌入式系统基础A》课程
实验报告
班 级: 物联网工程2002
姓名-学号 : 徐权-632007060327
实验项目名称: Modbus编程实践
实验项目性质: 设计性
实验所属课程: 《嵌入式系统基础A》
实验室(中心): 南岸校区语音大楼
指 导 教 师 : 娄路
完成时间: 2022 年 12 月 25 日
一、实验内容和任务
一、网络协议深入了解。练习wireshark抓取网络数据包。在两台的电脑(笔记本电脑win10 主机与ubuntu虚拟机。网卡选择桥接模式,可得到两个子网IPv4地址)上运行 “疯狂聊天室”程序,通过wireshark抓包:
1)分析此程序网络连接采用的是哪种协议(TCP、UDP)和什么端口号?
2)试着在抓取包中找到窃取到的聊天信息 (英文字符和汉字可能经过了某种编码转换,数据包中不是明文)
3)如果是网络连接采取的是TCP,分析其建立连接时的3次握手,断开连接时的4次握手;如果是UDP,解释该程序为何能够在多台电脑之间(只有是同一个聊天室编号)同时传输聊天数据?
二、在消化学习 server.c和client.c 套接字代码、python-modbus-over-tcp.py 代码基础上,试着用C编程完成modbus协议,从云端服务器读取温湿度数据。
三、用stm32最小核心板+AHT20模块,完成一个 modbus接口的温湿度Slave设备,能够让上位机PC通过modbus协议获取温湿度。主程序采用多任务框架,比如RT-thread Nano。
参考网址:
https://blog.csdn.net/weixin_46129506/article/details/121914039
https://blog.csdn.net/qq_47281915/article/details/122328414
https://blog.csdn.net/weixin_56102526/article/details/121952050
二、实验要求
1. 分组要求:每个学生独立完成,即1人1组。
2. 程序及报告文档要求:具有较好的可读性,如叙述准确、标注明确、截图清晰等。
3.项目代码上传github,同时把项目完整打包为zip文件,与实验报告(Markdown源码及PDF文件)、作业博客地址一起提交到学习通。
三. 实验过程介绍 (此处可以填博客内容)
一、网络协议深入了解。练习wireshark抓取网络数据包。在两台的电脑(笔记本电脑win10 主机与ubuntu虚拟机。网卡选择桥接模式,可得到两个子网IPv4地址)上运行 “疯狂聊天室”程序,通过wireshark抓包:
1)疯狂聊天室
2)wireshark分析
输入ip.dst==255.255.255.255筛选
1)分析此程序网络连接采用的是哪种协议(TCP、UDP)和什么端口号?
软件使用的协议是UDP
2)试着在抓取包中找到窃取到的聊天信息 (英文字符和汉字可能经过了某种编码转换,数据包中不是明文)
3)如果是网络连接采取的是TCP,分析其建立连接时的3次握手,断开连接时的4次握手;如果是UDP,解释该程序为何能够在多台电脑之间(只有是同一个聊天室编号)同时传输聊天数据?
能在多台电脑上同时传输聊天数据的原因是发送信息时使用的广播地址,所以可以多方收到消息。
二、在消化学习 server.c和client.c 套接字代码、python-modbus-over-tcp.py 代码基础上,试着用C编程完成modbus协议,从云端服务器读取温湿度数据。二、在消化学习 server.c和client.c 套接字代码、python-modbus-over-tcp.py 代码基础上,试着用C编程完成modbus协议,从云端服务器读取温湿度数据。
实验所需: 小熊猫c++
1.新建项目project1
输入指令格式
uint8_t data[length_8];
printf("具体指令给格式为0+传感器编号(1,2,3,4,5)0300010002");
printf("请输入采集传感器的指令):\r\n");
scanf("%s",data);
生成将输入指令转化为hex格式并生成crc16校验码
uint16_t crc;
unsigned char * cmd;
char crc1[8];
cmd = fromhex(data);
crc = CRC_16(cmd);
uint8_t a = 0xFF;
for(int i=0;i<6;i++){
//TODO
crc1[i] = cmd[i];
}
crc1[6] = a & crc;
crc1[7] = (crc >> 8) & a;
去校验码低位和高位组成2byte的crc16校验位
对应的fromhex函数和crc16校验码生成函数CRC_16会在后面给出
发送,接收数据并使用wireshark对发送数据进行抓包分析
if (send(client_socket, crc1, 8, 0) < 0) {
printf("Failed to send data!\n");
break;
}
int ret = recv(client_socket, recv_data, BUFFER_SIZE, 0);
if (ret < 0) {
printf("Failed to receive data!\n");
break;
}
recv_data[ret]=0; // correctly ends received string
char yb[4],wd[4];
for(int i=0;i<4;i++){
//TODO
yb[i] = recv_data[4+i];
wd[i] = recv_data[8+i];
}
float mic = hexToDec(yb)/100.0;
float strain_temp = hexToDec(wd)/100.0;
printf("应变:%f\r\n",mic);
printf("温度:%f\r\n",strain_temp);
// printf("Receive data from server: \"%x\"\n",recv_data);
if (strcmp(data,kExitFlag)==0) {
printf("Exit!\n");
break;
}
01:地址 03:指令码 0001:起始寄存器 0002:寄存器个数 95cb:crc校检位
添加过滤信息ip.dst ==123.56.90.74 只显示服务器发送回来的包以及发送过去的包
可以看到请求数据和接收数据方式都为TCP
三、用stm32最小核心板+AHT20模块,完成一个 modbus接口的温湿度Slave设备,能够让上位机PC通过modbus协议获取温湿度。主程序采用多任务框架,比如RT-thread Nano。
1、STM32移植RT_THread
CubeMX安装Nano pack
先获取软件包地址:
https://www.rt-thread.org/download/cube/RealThread.RT-Thread.pdsc
打开 CubeMX,从菜单栏 help 进入 Manage embedded software packages 界面,点击 From Url 按钮,进入 User Defined Packs Manager 界面,其次点击 new,填入上述网址,然后点击 check
傻瓜式安装
然后keil MDK安装RT_THread
CubeMX新建文件
选择 Nano 组件
(1)点击 Softwares Packages->Select Components,进入组件配置界面,选择 RealThread, 然后根3.1.5版本的,然后点击 OK 按钮
这时会新增Software Packs展开就可以看见添加的RealThread.RT_Thread,勾选相应内容
找到 RTOS,勾选 kernel,点击 OK
RCC设置:
时钟设置:
usart2设置4800bit/s
在设置:
配置串口一
配置nvic
然后general项目
修改代码:
usart.c
#include <stdio.h>
int fputc(int ch,FILE *f)
{
HAL_UART_Transmit(&huart1,(uint8_t *)&ch,1,0xFFFF);
//等待发送结束
while(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_TC)!=SET){
}
return ch;
}
添加中断处理
stm32f1xx_it.c的串口2中断处理函数
void USART2_IRQHandler(void)
{
/* USER CODE BEGIN USART2_IRQn 0 */
uint32_t tmp = 0;
tmp =__HAL_UART_GET_FLAG(&huart2,UART_FLAG_IDLE); //获取IDLE标志位
if((tmp != RESET))//idle标志被置位
{
//清除标志位
//__HAL_UART_CLEAR_IDLEFLAG(&huart2);
//清除状态寄存器SR,读取SR寄存器可以实现清除SR寄存器的功能
huart2.Instance->SR;
//读取数据寄存器中的数据
huart2.Instance->DR;
HAL_UART_DMAStop(&huart2); //
// 获取DMA中未传输的数据个数
dataLength=hdma_usart2_rx.Instance->CNDTR;
// 接受完成
isDataEnd = 1;
//清除标志位
__HAL_UART_CLEAR_IDLEFLAG(&huart2);
}
/* USER CODE END USART2_IRQn 0 */
HAL_UART_IRQHandler(&huart2);
/* USER CODE BEGIN USART2_IRQn 1 */
/* USER CODE END USART2_IRQn 1 */
}
main.c
添加变量
extern DMA_HandleTypeDef hdma_usart2_rx;
//modbus问询帧
uint8_t commands[3][8]={
{0x01,0x03,0x00,0x00,0x00,0x02,0xC4,0x38},
{0x02,0x03,0x00,0x00,0x00,0x02,0xC4,0x38},
{0x03,0x03,0x00,0x00,0x00,0x02,0xC5,0xE9},
};
//modbus应答帧
uint8_t dataBuff[20];
//中断完成标志
uint8_t isDataEnd=0;
//应答帧长度
uint8_t dataLength;
//温度
uint16_t temperature;
//湿度
uint16_t humidity;
main函数
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART1_UART_Init();
MX_DMA_Init();
MX_USART2_UART_Init();
/* USER CODE BEGIN 2 */
//使能idle中断
__HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE);
//打开串口DMA接收
HAL_UART_Receive_DMA(&huart2,dataBuff,sizeof(dataBuff));
//传感器地址
uint8_t i=0;
while (1)
{
//中断完成,解析应答帧
if(isDataEnd){
//提取湿度
humidity=(dataBuff[3] << 8) + (dataBuff[4]);
//提取温度
temperature=(dataBuff[5] << 8) + (dataBuff[6]);
//串口一发送解析结果
printf("温度: %.1f\t 湿度: %.1f\r\n",temperature/10.0,humidity/10.0);
memset(dataBuff,0,sizeof(dataBuff));//清空接收数组
isDataEnd=0;//清除接收结束标志位
}
//发送第 i 个传感器的 modbus 问询帧
HAL_UART_Transmit(&huart2,commands[i],sizeof(commands),0xFFFF);
//轮询读取三个传感器
i=(i+1)%3;
//打开串口DMA接收
HAL_UART_Receive_DMA(&huart2,dataBuff,sizeof(dataBuff));
//程序运行标志,闪灯
HAL_Delay(1000);
HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_13);
}
实验效果:
由于AHT20在同学那里所以就没有温湿度读取数据 正确的话应该是可以读出来的