【嵌入式——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_OBJECT

public:
    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_NAMESPACE

class BootLoader : public QWidget
{
    Q_OBJECT

public:
    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 4002

BootLoader::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.mfbz.cn/a/602602.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱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))…

iOS xib布局

1.多次启动发现启动图和截屏的图片不一致,设置launch storyboard 不能到顶部 https://blog.csdn.net/u011960171/article/details/104053696/ 2.multipiler是比例&#xff0c;需要控制顺序1.视图&#xff0c;2父视图&#xff0c;选择宽度比例&#xff0c;默认是1 3.Aspect R…

python爬虫(二) 之 42号网汽车文章爬虫

python爬虫&#xff08;二&#xff09; 之 42号网汽车文章爬虫 今天在咸鱼上有个买家找我一个42号网汽车文章的爬虫&#xff0c;目前需求已经做完了&#xff0c;现在将这部分代码开源&#xff0c;供大家参考。爬虫能够抓取到网站上所有文章的数据&#xff0c;大概一小时左右就…

超越Scratch的梦 用心打造商业系统图形编程体验

在一个阳光明媚的上午&#xff0c;卧龙和凤雏正在公司会议室激烈地讨论着图形化编程产品在商业系统开发中的应用和改进。会议室里摆放着一些电脑和投影仪&#xff0c;方便他们展示和演示相关的内容。 “你知道图形化编程在商业系统开发中没有被广泛应用的原因吗&#xff1f;”卧…

libcity笔记:libcity/evaluator/traj_loc_pred_evaluator.py

1 构造函数 2 _check_config 检查配置是否符合评估器的要求&#xff0c;确保评估过程能够顺利执行 3 collect 4 evaluate 5 save_result & clear

创建禁止操作区域并且添加水印

css 设置 &#xff1a; 引用换成自己就好 .overlay {z-index: 1000;cursor: none; /*设置为不可点击*/user-select: none; /*设置为不可选择*/contenteditable: false; /*设置为不可编辑*/draggable: false; /*设置为不可拖动*/position: absolute;top: 0;left: 0;width: 100…

最新闲鱼小众蓝海虚拟资源,单号日入300+,三天必起店,矩阵放大月入1-2W

详情介绍 本项目售卖的虚拟资源非常小众&#xff0c;宅男的最爱&#xff0c;并且市场一片蓝海&#xff01;只需一步手机&#xff0c;随时随地操作项目&#xff0c;流量巨大&#xff0c;安装教程方法操作三天必起店&#xff0c;消息多到回不过来&#xff0c;一天轻松出个大几十单…

数字孪生涉及到的前沿技术:虚拟现实 人工智能 区块链 边缘计算。

数字孪生是各类技术的综合应用&#xff0c;除了咱们常见的传感器、数据采集、清洗、传输、建模、可视化技术外&#xff0c;还有还有一些前沿技术&#xff0c;会让数字孪生更加强大和智能&#xff0c;本文介绍几个。 虚拟现实&#xff08;Virtual Reality&#xff0c;VR&#x…

搜维尔科技:【案例分享】Xsens用于工业制造艺术创新设计平台

用户名称&#xff1a;北京理工大学 主要产品&#xff1a;Xsens MVN Awinda惯性动作捕捉系统 在设计与艺术学院的某实验室内&#xff0c;通过Xsens惯性动作捕捉&#xff0c;对人体动作进行捕捉&#xff0c;得到人体三维运动数据&#xff0c;将捕到的数据用于后续应用研究。…

【影片欣赏】【指环王】【魔戒:双塔奇谋 The Lord of the Rings: The Two Towers】

2003年发行&#xff0c;Special Extended DVD Edition Part One 1. The Foundations of Stone 2. Elven Rope 3. The Taming of Smeagol 4. The Uruk-hai 5. The Three Hunters 6. The Burning of the Westfold 7. Massacre at the Fords of Isen 8. The Banishment of Eomer …

stable diffusion 之云端部署攻略

本文主要介绍stable diffusion云端产品以及使用步骤 ℹ️整合安装包、模型资源见文末~ megaease cloud&#xff08;强烈推荐&#xff09; 优点&#xff1a; 集成了常用大模型和插件、VAE3080显卡配置&#xff0c;费用大概0.48元/小时&#xff0c;可随时暂停&#xff0c;暂停…

图片如何压缩到500kb以下?3步完成图片压缩

在日常生活和工作中&#xff0c;经常需要处理各种图片&#xff0c;而有时候图片文件过大&#xff0c;不仅占用了大量的存储空间&#xff0c;还可能影响文件的传输速度和加载速度。因此&#xff0c;如何将图片压缩到500kb以内成为了许多人的需求&#xff0c;普通的图片压缩可能没…

如何在没有备份的情况下恢复 Mac 上丢失的数据

如果您因意外删除、错误格式化硬盘或文件损坏而丢失了重要的、感伤的文件、照片或音乐&#xff0c;那么这可能会令人非常痛苦。幸运的是&#xff0c;您有几个选择。 您的 Mac 位于数字宇宙的中心。您可能会在上面留下照片和视频形式的记忆&#xff0c;以及来自您不再见面的朋友…

Ubuntu16.04 离线安装CDH6.2.1

1. 离线包工作 下载Cloudera Manager安装包&#xff0c;地址&#xff1a;https://archive.cloudera.com/cm6/6.2.1/repo-as-tarball/ cm6.2.1-ubuntu1604.tar.gz下载CDH6.2.1安装包&#xff0c;地址&#xff1a;https://archive.cloudera.com/cdh6/6.2.1/parcels/ CDH-6.2.1-1.…

IP 地址追踪工具促进有效的 IP 管理

网络 IP 地址空间的结构、扫描和管理方式因组织的规模和网络需求而异&#xff0c;网络越大&#xff0c;需要管理的 IP 就越多&#xff0c;IP 地址层次结构就越复杂。因此&#xff0c;如果没有 IP 地址管理&#xff08;IPAM&#xff09;解决方案&#xff0c;IP 资源过度使用和地…

Java面试八股文(SpringCloud篇)

****************************************************

数据分析:小红书夏季情绪营销,已经到了“next level”

导语 5月伊始&#xff0c;夏季营销拉开帷幕。盘点品牌近两年出圈案例&#xff0c;夏季营销的立足点正在从提供功能性向渲染情绪转变。 2023年&#xff0c;蕉下《所有的太阳》&#xff0c;将防晒与风景、山水链接&#xff0c;重新定位了「防晒衣」的角色&#xff0c;从躲避炙热…

阿里云VOD视频点播流程(1)

一、开通阿里云VOD 视频点播&#xff08;ApsaraVideo VoD&#xff0c;简称VOD&#xff09;是集视频采集、编辑、上传、媒体资源管理、自动化转码处理、视频审核分析、分发加速于一体的一站式音视频点播解决方案。登录阿里云&#xff0c;在产品找到视频点播VOD &#xff0c;点击…

Davinci工程CAN模块讲解

CAN模块是用来配置CAN Driver的&#xff0c;里面有CanConfigSet是用来配置驱动内容的&#xff0c;CanGeneral配置参数。涉及四个文件Can_Lcfg.c/Can_Lcfg.h/Can_Cfg.c/Can_Cfg.h CanConfigSet CanControllers CAN控制器&#xff0c;我们这里的CAN控制器只有一个&#xff0c;名…

ETCD 简介

ETCD 简介 1-etcd介绍 etcd 是 CoreOS 团队于 2013 年 6 月发起的开源项目&#xff0c;它的目标是构建一个高可用的分布式键值(key-value)数据库。具有以下特点&#xff1a; 简单&#xff1a;安装配置简单&#xff0c;而且提供了 HTTP API 进行交互&#xff0c;使用也很简单键…
最新文章