【嵌入式——QT】QT集成Ymodem协议使用UDP进行传输

【嵌入式——QT】QT集成Ymodem协议使用UDP进行传输

  • Ymodem协议
    • 帧的数据格式
      • 帧头
      • 包号
      • 校验
    • 通讯过程
      • 握手信号
      • 起始帧
      • 数据帧
      • 结束帧
      • 代码块
    • Ymodem命令
  • QT实现
    • YmodemFileTransmit.h
    • YmodemFileTransmit.cpp
    • BootLoader.h
    • BootLoader.cpp
    • Ymodem协议源码

Ymodem协议

帧的数据格式

帧头、包号、包号反码、数据、校验。

帧头包号包号反码数据校验高位校验低位
Soh/Stx0x000xFFDATACRC_HCRC_L

帧头

以Soh(0x01)开始的数据包,信息块是128字节,该帧类型总长度为133字节。
以Stx(0x02)开始的数据包,信息块是1024字节,该帧类型总长度为1029字节。

包号

包号是为数据块的编号,将要传送的数据进行分块编号,只有一个字节,范围为0~255。大于255的则归零重复计算。

校验

Ymodem采用的是CRC16校验算法,校验值为2字节。

uint16_t Ymodem::crc16(uint8_t *buff, uint32_t len)
{uint16_t crc = 0;while(len--){crc ^= (uint16_t)(*(buff++)) << 8;for(int i = 0; i < 8; i++){if(crc & 0x8000){crc = (crc << 1) ^ 0x1021;}else{crc = crc << 1;}}}return crc;
}

通讯过程

握手信号

发送方收到接收方发送的CodeC(0x43)命令后,才可以开始发送起始帧。

起始帧

帧头包号包号反码文件名称文件大小填充区校验高位校验低位
CodeSoh0x000xFFFileName+0x00FileSize+0x00NULL(0x00)CRC_HCRC_L

文件名称后必须添加0x00作为结束,文件大小值后必须加0x00作为结束,余下的位置以0x00填充。

数据帧

帧头包号包号反码有效数据校验高位校验低位
CodeSoh/CodeStx0x000xFFDATACRC_HCRC_L

对于SOH帧,若余下数据小于128字节,则以0x1A填充,该帧长度仍为133字节;
对于STX帧需考虑几种情况:

  • 余下数据等于1024字节,以1029长度帧发送;
  • 余下数据小于1024字节,但大于128字节,以1029字节帧长度发送,无效数据以0x1A填充;
  • 余下数据等于128字节,以133字节帧长度发送;
  • 余下数据小于128字节,以133字节帧长度发送,无效数据以0x1A填充;

结束帧

帧头包号包号反码数据区校验高位校验低位
CodeSoh0x000xFFDATACRC_HCRC_L
数据区,校验都以0x00填充。

代码块

void Ymodem::transmit()
{switch(stage){case StageNone:{transmitStageNone();break;}case StageEstablishing:{transmitStageEstablishing();break;}case StageEstablished:{transmitStageEstablished();break;}case StageTransmitting:{transmitStageTransmitting();break;}case StageFinishing:{transmitStageFinishing();break;}default:{transmitStageFinished();}}
}

Ymodem命令

CodeEot、CodeCan由发送端发送;
CodeAck、CodeNak、CodeC由接收端发送;

命令命令码备注
CodeNone0x00
CodeSoh0x01133字节长度
CodeStx0x021024字节长度
CodeEot0x04文件传输结束指令
CodeAck0x06接收正确指令
CodeNak0x15重传当前数据包请求指令
CodeCan0x18取消传输指令,连续发送5个该命令,终止传输
CodeC0x43字符C
CodeA10x41
CodeA20x61

QT实现

YmodemFileTransmit.h

#ifndef YMODEMFILETRANSMIT_H
#define YMODEMFILETRANSMIT_H#include <QFile>
#include <QTimer>
#include <QObject>
#include "Ymodem.h"
#include <QUdpSocket>class YmodemFileTransmit : public QObject, public Ymodem
{Q_OBJECTpublic:explicit YmodemFileTransmit(QObject* parent = nullptr);~YmodemFileTransmit();void setFileName(const QString& name);void setIpAddress(const QString& ip);void setPortNumber(quint16 port);bool startTransmit();void stopTransmit();int getTransmitProgress();Status getTransmitStatus();signals:void transmitProgress(int progress);void transmitStatus(YmodemFileTransmit::Status status);public slots:void readTimeOut();void writeTimeOut();private:Code callback(Status status, uint8_t* buff, uint32_t* len);uint32_t read(uint8_t* buff, uint32_t len);uint32_t write(uint8_t* buff, uint32_t len);QFile*       file;QTimer*      readTimer;QTimer*      writeTimer;QUdpSocket* udpClient;int      progress;Status   status;uint64_t fileSize;uint64_t fileCount;QString  serverIp;uint16_t serverPort;
};#endif // YMODEMFILETRANSMIT_H

YmodemFileTransmit.cpp

#include "YmodemFileTransmit.h"
#include <QFileInfo>
#include <QNetworkDatagram>
#include <QThread>#define READ_TIME_OUT   (10)
#define WRITE_TIME_OUT  (1000)YmodemFileTransmit::YmodemFileTransmit(QObject* parent) :QObject(parent),file(new QFile),readTimer(new QTimer),writeTimer(new QTimer),udpClient(new QUdpSocket)
{setTimeDivide(499);setTimeMax(5);setErrorMax(999);connect(readTimer, SIGNAL(timeout()), this, SLOT(readTimeOut()));connect(writeTimer, SIGNAL(timeout()), this, SLOT(writeTimeOut()));
}YmodemFileTransmit::~YmodemFileTransmit()
{delete file;delete readTimer;delete writeTimer;delete udpClient;
}void YmodemFileTransmit::setFileName(const QString& name)
{file->setFileName(name);
}void YmodemFileTransmit::setIpAddress(const QString& ip)
{serverIp = ip;
}void YmodemFileTransmit::setPortNumber(quint16 port)
{serverPort = port;
}bool YmodemFileTransmit::startTransmit()
{progress = 0;status   = StatusEstablish;QByteArray array;array.append(0x02);array.append(0x01);array.append(0xFF);array.append(0x07);array.append(0x01);array.append(0x09);array.append(0x03);QHostAddress targetAddr(serverIp);int ret = udpClient->writeDatagram(array, targetAddr, serverPort);if(ret > 0) {QThread::msleep(50);readTimer->start(READ_TIME_OUT);return true;} else {return false;}
}void YmodemFileTransmit::stopTransmit()
{file->close();abort();status = StatusAbort;writeTimer->start(WRITE_TIME_OUT);
}int YmodemFileTransmit::getTransmitProgress()
{return progress;
}Ymodem::Status YmodemFileTransmit::getTransmitStatus()
{return status;
}void YmodemFileTransmit::readTimeOut()
{readTimer->stop();transmit();if((status == StatusEstablish) || (status == StatusTransmit)) {readTimer->start(READ_TIME_OUT);}
}void YmodemFileTransmit::writeTimeOut()
{writeTimer->stop();transmitStatus(status);
}Ymodem::Code YmodemFileTransmit::callback(Status status, uint8_t* buff, uint32_t* len)
{switch(status) {case StatusEstablish:if(file->open(QFile::ReadOnly) == true) {QFileInfo fileInfo(*file);fileSize  = fileInfo.size();fileCount = 0;strcpy((char*)buff, fileInfo.fileName().toLocal8Bit().data());strcpy((char*)buff + fileInfo.fileName().toLocal8Bit().size() + 1, QByteArray::number(fileInfo.size()).data());*len = YMODEM_PACKET_SIZE;YmodemFileTransmit::status = StatusEstablish;transmitStatus(StatusEstablish);return CodeAck;} else {YmodemFileTransmit::status = StatusError;writeTimer->start(WRITE_TIME_OUT);return CodeCan;}case StatusTransmit:if(fileSize != fileCount) {if((fileSize - fileCount) > YMODEM_PACKET_SIZE) {fileCount += file->read((char*)buff, YMODEM_PACKET_1K_SIZE);*len = YMODEM_PACKET_1K_SIZE;} else {fileCount += file->read((char*)buff, YMODEM_PACKET_SIZE);*len = YMODEM_PACKET_SIZE;}progress = (int)(fileCount * 100 / fileSize);YmodemFileTransmit::status = StatusTransmit;transmitProgress(progress);transmitStatus(StatusTransmit);return CodeAck;} else {YmodemFileTransmit::status = StatusTransmit;transmitStatus(StatusTransmit);return CodeEot;}case StatusFinish:file->close();YmodemFileTransmit::status = StatusFinish;writeTimer->start(WRITE_TIME_OUT);return CodeAck;case StatusAbort:file->close();YmodemFileTransmit::status = StatusAbort;writeTimer->start(WRITE_TIME_OUT);return CodeCan;case StatusTimeout:YmodemFileTransmit::status = StatusTimeout;writeTimer->start(WRITE_TIME_OUT);return CodeCan;default:file->close();YmodemFileTransmit::status = StatusError;writeTimer->start(WRITE_TIME_OUT);return CodeCan;}
}uint32_t YmodemFileTransmit::read(uint8_t* buff, uint32_t len)
{QNetworkDatagram datagram =udpClient->receiveDatagram(len);QByteArray array = datagram.data();uint32_t lenArray = array.size();uint32_t lenBuff  = len;uint32_t length = qMin(lenArray, lenBuff);memcpy(buff, array, length);return length;
}uint32_t YmodemFileTransmit::write(uint8_t* buff, uint32_t len)
{QHostAddress targetAddr(serverIp);int ret = udpClient->writeDatagram((char*)buff, len, targetAddr, serverPort);return ret;
}

BootLoader.h

#ifndef BOOTLOADER_H
#define BOOTLOADER_H#include <QWidget>
#include <QUdpSocket>
#include <YmodemFileTransmit.h>QT_BEGIN_NAMESPACE
namespace Ui
{class BootLoader;
}
QT_END_NAMESPACEclass BootLoader : public QWidget
{Q_OBJECTpublic:BootLoader(QWidget* parent = nullptr);~BootLoader();YmodemFileTransmit* ymodemFileTransmit;public slots:void on_pushButtonConnect_clicked();void on_pushButtonBrowse_clicked();void on_pushButtonSend_clicked();void readData();void transmitProgress(int progress);void transmitStatus(YmodemFileTransmit::Status status);private:Ui::BootLoader* ui;bool firemwareTransmitStatus;QUdpSocket* udpClient;
};
#endif // BOOTLOADER_H

BootLoader.cpp

#include "BootLoader.h"
#include "ui_BootLoader.h"
#include <QByteArray>
#include <QDebug>
#include <QNetworkDatagram>
#include <QFileDialog>
#include <QMessageBox>#define SERVER_ADDR "192.168.xxx.xxx"
#define SERVER_PORT 4002BootLoader::BootLoader(QWidget* parent): QWidget(parent), ui(new Ui::BootLoader)
{ui->setupUi(this);this->setWindowTitle(tr("EthernetYmodem"));this->setWindowIcon(QIcon(":/images/main.ico"));ymodemFileTransmit = new YmodemFileTransmit();connect(ymodemFileTransmit, SIGNAL(transmitProgress(int)), this, SLOT(transmitProgress(int)));connect(ymodemFileTransmit, SIGNAL(transmitStatus(YmodemFileTransmit::Status)), this, SLOT(transmitStatus(YmodemFileTransmit::Status)));udpClient = new QUdpSocket(this);connect(udpClient, &QUdpSocket::readyRead, this, &BootLoader::readData);firemwareTransmitStatus = false;ui->pushButtonSend->setEnabled(false);ui->pushButtonConnect->setEnabled(true);
}BootLoader::~BootLoader()
{delete ui;
}void BootLoader::on_pushButtonConnect_clicked()
{QByteArray array;array.append(0x02);array.append(0x01);array.append(0xFF);array.append(0x07);array.append(0x01);array.append(0x09);array.append(0x03);QHostAddress targetAddr(SERVER_ADDR);int ret = udpClient->writeDatagram(array, targetAddr, SERVER_PORT);qDebug()<<"ret"<<ret;
}void BootLoader::on_pushButtonBrowse_clicked()
{QString curPath = QDir::currentPath();ui->lineEditFilePath->setText(QFileDialog::getOpenFileName(this, u8"打开文件", curPath, u8"任意文件 (*.*)"));
}void BootLoader::on_pushButtonSend_clicked()
{udpClient->abort();if(ui->lineEditFilePath->text().isEmpty()) {QMessageBox::warning(this, tr("!!!"), tr("Please select a file!"));return;}if(firemwareTransmitStatus == false) {ymodemFileTransmit->setFileName(ui->lineEditFilePath->text());ymodemFileTransmit->setIpAddress(SERVER_ADDR);ymodemFileTransmit->setPortNumber(SERVER_PORT);if(ymodemFileTransmit->startTransmit() == true) {firemwareTransmitStatus = true;ui->progressBar->setValue(0);ui->pushButtonSend->setText(tr("Cancel"));ui->pushButtonConnect->setEnabled(false);} else {QMessageBox::warning(this, tr("Failure"), tr("File failed to send!"), tr("Closed"));ui->pushButtonSend->setText(tr("Send"));ui->pushButtonConnect->setEnabled(true);}} else {ymodemFileTransmit->stopTransmit();ui->pushButtonSend->setText(tr("Send"));ui->pushButtonConnect->setEnabled(true);}
}void BootLoader::readData()
{while(udpClient->hasPendingDatagrams()) {QNetworkDatagram datagram = udpClient->receiveDatagram();QByteArray receivedData = datagram.data();qDebug() << "Received data:" << receivedData;if(receivedData.size() > 0 && receivedData[0] == (char)0x43) {ui->pushButtonSend->setEnabled(true);ui->pushButtonConnect->setEnabled(false);}}
}void BootLoader::transmitProgress(int progress)
{ui->progressBar->setValue(progress);
}void BootLoader::transmitStatus(Ymodem::Status status)
{switch(status) {case YmodemFileTransmit::StatusEstablish:break;case YmodemFileTransmit::StatusTransmit:break;case YmodemFileTransmit::StatusFinish:firemwareTransmitStatus = false;QMessageBox::information(this, tr("OK"), tr("Upgrade successed!"), QMessageBox::Yes);ui->pushButtonSend->setText(tr("Send"));ui->pushButtonSend->setEnabled(false);ui->pushButtonConnect->setEnabled(true);break;case YmodemFileTransmit::StatusAbort:firemwareTransmitStatus = false;QMessageBox::warning(this, tr("failure"), tr("File failed to send!"), tr("Closed"));break;case YmodemFileTransmit::StatusTimeout:firemwareTransmitStatus = false;QMessageBox::warning(this, tr("failure"), tr("File failed to send!"), tr("Closed"));break;default:firemwareTransmitStatus = false;QMessageBox::warning(this, tr("failure"), tr("File failed to send!"), tr("Closed"));}
}

Ymodem协议源码

源码链接

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/8175.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Stable Diffusion Ai绘画模型推荐:二次元Coriander_Mix v1大模型推荐

负tag嵌入式:EasyNegative,badhandv4 此模型经测试是写实偏3D的效果 画质灰暗的话请加&#xff1a;VAE840000 或者负tag&#xff1a;(watermark:2),(blurry:2),fat,paintings,sketches,(worst quality:2),(low quality:2),(normal quality:2),((monochrome)), ((grayscale))…

将 Vue、React、Angular、HTML 等一键打包成 macOS 和 Windows 平台客户端应用

应用简介 PPX 基于 pywebview 和 PyInstaller 框架&#xff0c;构建 macOS 和 Windows 平台的客户端。本应用的视图层支持 Vue、React、Angular、HTML 中的任意一种&#xff0c;业务层支持 Python 脚本。考虑到某些生物计算场景数据量大&#xff0c;数据私密&#xff0c;因此将…

数据可视化准备:动态识别echarts的横纵坐标数据字段

前言 继上一篇文章 自动选择图表类型&#xff1a;基于数据特征智能决策 分析了如何根据sql和数据结果判断应该自动使用哪种图表类型&#xff0c;本文继续将图表的x轴和y轴横纵坐标识别出来&#xff0c;基本一个二维数据类普通图表就可以直接输出为echarts参数了。 在数据可视…

NTP卫星授时服务器(GPS北斗授时设备)让自控系统更精准

NTP卫星授时服务器&#xff08;GPS北斗授时设备&#xff09;让自控系统更精准 NTP卫星授时服务器&#xff08;GPS北斗授时设备&#xff09;让自控系统更精准 工业自动化控制是工业生产基础设施的关键组成部分。 通过计算机和自动化技术在工业生产中的广泛应用&#xff0c;实现工…

leetcode-5. 最长回文子串

题目描述 给你一个字符串 s&#xff0c;找到 s 中最长的回文子串。 如果字符串的反序与原始字符串相同&#xff0c;则该字符串称为回文字符串。 示例 1&#xff1a; 输入&#xff1a;s "babad" 输出&#xff1a;"bab" 解释&#xff1a;"aba"…

计算机发展史故事【4】

继往与开来 巴贝奇巨星陨落后&#xff0c;世人已逐渐将他淡忘&#xff0c;20 世纪已经来临。计算机的历史等待着&#xff0c;等待着巴贝奇式的人物再世&#xff0c;等待着人类划时代的壮举。 大约在1936 年&#xff0c;美国青年霍德华艾肯&#xff08;H.Aiken&#xff09;来哈佛…

javacv实时解析pcm音频流

javacv实时解析pcm音频流 解析代码 try (ByteArrayInputStream inputStream new ByteArrayInputStream(bytes);){FFmpegFrameGrabber grabber new FFmpegFrameGrabber(inputStream);// PCM S16LE 格式grabber.setFormat("s16le");// 采样率grabber.setSampleRate(1…

【PHP】查询Mysql根据GREATEST函数对两个字段的较大值对结果进行排序

目录 一、GREATEST函数简介 二、示例 三、应用场景 四、注意事项 在数据库查询中&#xff0c;我们经常需要根据某个字段的值对结果进行排序。然而&#xff0c;有时我们需要根据两个或多个字段的值进行排序&#xff0c;这时我们可以使用MySQL的GREATEST()函数。GREATEST()函…

phpize +Visual Studio + MSYS2 + bison 草稿记录并未正常完成

phpize Visual Studio MSYS2 bison 先安装 Visual Studio https://visualstudio.microsoft.com/zh-hans/vs/ 在安装过程中&#xff0c;选择安装工作负载。确保选择了 C 工作负载以及适用于 C 开发的相关组件&#xff0c;例如 MSVC v142 - VS 2022 C x64/x86 build tools。 …

QT设计模式:工厂模式

基本概念 工厂模式是一种创建型设计模式&#xff0c;用于将对象的创建逻辑与使用者分离&#xff0c;以实现对象的创建和使用的解耦。工厂模式提供了一个统一的接口来创建对象&#xff0c;而客户端代码只需通过该接口来请求所需的对象&#xff0c;而不需要知道具体的对象创建细…

多个glibc库存在时如何查看ldd调用的哪个

但是发现存在多个版本的glibc版本&#xff0c;需要查看具体的库的信息&#xff0c;和相应的关键函数的信息&#xff0c;但是并不知道具体的libc.so.6的路径信息 rootalg-dev04:~/xingqiao# ldd --version ldd (GNU libc) 2.29 rootalg-dev04:/opt# which ldd /usr/local/bin/…

Exness外汇大陆投资者开户详细流程!

Exness是一家全球领先的外汇和差价合约交易平台&#xff0c;因其高效稳定的服务和多样化的交易工具&#xff0c;受到广大投资者的青睐。然而&#xff0c;由于中国大陆用户无法直接访问Exness官网&#xff0c;想要开户的大陆投资者需要通过特定方式才能完成注册。本文将详细介绍…

[Linux使用] apt dpkg 常用包管理命令

查找头文件信息&#xff1a; sudo apt install apt-file sudo apt-file update apt-file search gst.h 查找库文件位置&#xff1a; whereis gstreamer 查找已安装的库&#xff1a; dpkg -l |grep gstreamer

Rust:文件 launch.json 有什么用?

launch.json 是 Visual Studio Code&#xff08;VSCode&#xff09;中的一个配置文件&#xff0c;主要用于配置调试器。当你在 VSCode 中进行代码调试时&#xff0c;launch.json 文件告诉调试器如何启动和配置你的程序。 具体来说&#xff0c;launch.json 文件包含了以下信息&…

PDF批量编辑技巧:高效PDF转txt批量处理,轻松管理大量文档

随着信息技术的飞速发展&#xff0c;文档管理已成为日常工作中不可或缺的一部分。特别是当我们需要处理大量的PDF文件时&#xff0c;如何高效地进行编辑、转换和管理成为了一个重要的问题。本文将介绍一些PDF批量编辑的技巧&#xff0c;特别是如何将PDF批量转换为txt格式&#…

FLIR LEPTON3.5 热像仪wifi 科研实验测温采集仪

点击查看详情!点击查看详情点击查看详情点击查看详情点击查看详情点击查看详情点击查看详情点击查看详情点击查看详情点击查看详情点击查看详情点击查看详情点击查看详情点击查看详情点击查看详情 1、描述 这是一款桌面科研实验测温热成像多功能热像记录仪&#xff0c;小巧轻便…

linux命令——软硬链接

ln可以创建软硬链接&#xff0c;类似于windows系统里的快捷方式 同时还可以创建软链接的软链接 这时cat第二个软链接&#xff0c;依然能查看原文件内容 当使用ls -l查看文件属性时能看到&#xff0c;软链接后面的指向性 同时 &#xff0c;我们可以使用readlink来查看软链接所…

Android 蓝牙实战——蓝牙音乐播放/暂停调用(二十一)

通过前面的学习我们了解了蓝牙开发中的各个协议,同时也知道蓝牙音乐的开发需要使用的是蓝牙的 a2dp 和 avrcp,而对蓝牙音乐的控制使用的是 avrcp,这里我们就梳理一下蓝牙音乐播放的调用流程。 一、调用流程 在前面的 Avrcp 协议中,我们了解 App 可以通过 MediaSession 框架…

【牛客】排列计算

原题链接&#xff1a;登录—专业IT笔试面试备考平台_牛客网 目录 1. 题目描述 2. 思路分析 3. 代码实现 1. 题目描述 2. 思路分析 如果直接涂色来计算单点权重&#xff0c;2e5*2e5必然超时。 所以用差分进行优化。 3. 代码实现 #include<bits/stdc.h> using name…

java的选择题

题目 来自牛客网 1.下列哪个是合法的 Java 标识符?() A.Tree&Glasses B.FirstJavaApplet C.FirstApplet D.273.5 正确答案是 B. FirstJavaApplet 和 C. FirstApplet。 分析&#xff1a; Java 标识符必须遵循以下规则&#xff1a; 1. 必须以字母&#xff08;A-Z 或 a-…