| 
29 | 29 | #include "codeloader.h"  | 
30 | 30 | #include "configparam.h"  | 
31 | 31 | #include "utility.h"  | 
 | 32 | +#include "heatshrink/heatshrinkif.h"  | 
32 | 33 | 
 
  | 
33 | 34 | #include <QApplication>  | 
34 | 35 | #include <QStyleFactory>  | 
@@ -101,6 +102,8 @@ static void showHelp()  | 
101 | 102 |     qDebug() << "--eraseLisp : Erase lisp-script.";  | 
102 | 103 |     qDebug() << "--uploadFirmware [path] : Upload firmware-file from path.";  | 
103 | 104 |     qDebug() << "--uploadBootloaderBuiltin : Upload bootloader from generic included bootloaders.";  | 
 | 105 | +    qDebug() << "--createFirmwareForBootloader [fileIn:fileOut] : Generate firmware-file compatible with the bootloader. ";  | 
 | 106 | +    qDebug() << "--writeFileToSdCard [fileLocal:pathSdcard] : Write file to SD-card.";  | 
104 | 107 | }  | 
105 | 108 | 
 
  | 
106 | 109 | #ifdef Q_OS_LINUX  | 
@@ -295,6 +298,10 @@ int main(int argc, char *argv[])  | 
295 | 298 |     bool eraseLisp = false;  | 
296 | 299 |     QString firmwarePath = "";  | 
297 | 300 |     bool uploadBootloaderBuiltin = false;  | 
 | 301 | +    QString fwForBootloaderIn = "";  | 
 | 302 | +    QString fwForBootloaderOut = "";  | 
 | 303 | +    QString fileForSdIn = "";  | 
 | 304 | +    QString fileForSdOut = "";  | 
298 | 305 | 
 
  | 
299 | 306 |     // Arguments can be hard-coded in a build like this:  | 
300 | 307 | //    qmlWindowSize = QSize(400, 800);  | 
@@ -590,6 +597,46 @@ int main(int argc, char *argv[])  | 
590 | 597 |             }  | 
591 | 598 |         }  | 
592 | 599 | 
 
  | 
 | 600 | +        if (str == "--createFirmwareForBootloader") {  | 
 | 601 | +            if ((i + 1) < args.size()) {  | 
 | 602 | +                i++;  | 
 | 603 | +                auto p = args.at(i).split(":");  | 
 | 604 | +                if (p.size() == 2) {  | 
 | 605 | +                    fwForBootloaderIn = p.at(0);  | 
 | 606 | +                    fwForBootloaderOut = p.at(1);  | 
 | 607 | +                } else {  | 
 | 608 | +                    qCritical() << "Invalid paths specified";  | 
 | 609 | +                    return 1;  | 
 | 610 | +                }  | 
 | 611 | + | 
 | 612 | +                found = true;  | 
 | 613 | +            } else {  | 
 | 614 | +                i++;  | 
 | 615 | +                qCritical() << "No paths specified";  | 
 | 616 | +                return 1;  | 
 | 617 | +            }  | 
 | 618 | +        }  | 
 | 619 | + | 
 | 620 | +        if (str == "--writeFileToSdCard") {  | 
 | 621 | +            if ((i + 1) < args.size()) {  | 
 | 622 | +                i++;  | 
 | 623 | +                auto p = args.at(i).split(":");  | 
 | 624 | +                if (p.size() == 2) {  | 
 | 625 | +                    fileForSdIn = p.at(0);  | 
 | 626 | +                    fileForSdOut = p.at(1);  | 
 | 627 | +                } else {  | 
 | 628 | +                    qCritical() << "Invalid paths specified";  | 
 | 629 | +                    return 1;  | 
 | 630 | +                }  | 
 | 631 | + | 
 | 632 | +                found = true;  | 
 | 633 | +            } else {  | 
 | 634 | +                i++;  | 
 | 635 | +                qCritical() << "No paths specified";  | 
 | 636 | +                return 1;  | 
 | 637 | +            }  | 
 | 638 | +        }  | 
 | 639 | + | 
593 | 640 |         if (!found) {  | 
594 | 641 |             if (dash) {  | 
595 | 642 |                 qCritical() << "At least one of the flags is invalid:" << str;  | 
@@ -628,6 +675,68 @@ int main(int argc, char *argv[])  | 
628 | 675 |         return 0;  | 
629 | 676 |     }  | 
630 | 677 | 
 
  | 
 | 678 | +    if (!fwForBootloaderIn.isEmpty()) {  | 
 | 679 | +        QFile fIn(fwForBootloaderIn);  | 
 | 680 | +        if (!fIn.open(QIODevice::ReadOnly)) {  | 
 | 681 | +            qWarning() << QString("Could not open %1 for reading.").arg(fwForBootloaderIn);  | 
 | 682 | +            return 1;  | 
 | 683 | +        }  | 
 | 684 | + | 
 | 685 | +        QFile fOut(fwForBootloaderOut);  | 
 | 686 | +        if (!fOut.open(QIODevice::WriteOnly)) {  | 
 | 687 | +            qWarning() << QString("Could not open %1 for writing.").arg(fwForBootloaderOut);  | 
 | 688 | +            return 1;  | 
 | 689 | +        }  | 
 | 690 | + | 
 | 691 | +        QByteArray newFirmware = fIn.readAll();  | 
 | 692 | +        fIn.close();  | 
 | 693 | + | 
 | 694 | +        int szTot = newFirmware.size();  | 
 | 695 | + | 
 | 696 | +        bool useHeatshrink = false;  | 
 | 697 | +        if (szTot > 393208 && szTot < 700000) { // If fw is much larger it is probably for the esp32  | 
 | 698 | +            useHeatshrink = true;  | 
 | 699 | +            qDebug() << "Firmware is big, using heatshrink compression library";  | 
 | 700 | +            int szOld = szTot;  | 
 | 701 | +            HeatshrinkIf hs;  | 
 | 702 | +            newFirmware = hs.encode(newFirmware);  | 
 | 703 | +            szTot = newFirmware.size();  | 
 | 704 | +            qDebug() << "New size:" << szTot << "(" << 100.0 * (double)szTot / (double)szOld << "%)";  | 
 | 705 | + | 
 | 706 | +            if (szTot > 393208) {  | 
 | 707 | +                qWarning() << "Firmware too big" <<  | 
 | 708 | +                            "The firmware you are trying to upload is too large for the bootloader even after compression.";  | 
 | 709 | +                return -1;  | 
 | 710 | +            }  | 
 | 711 | +        }  | 
 | 712 | + | 
 | 713 | +        if (szTot > 5000000) {  | 
 | 714 | +            qWarning() << "Firmware too big" <<  | 
 | 715 | +                        "The firmware you are trying to upload is unreasonably "  | 
 | 716 | +                        "large, most likely it is an invalid file";  | 
 | 717 | +            return -2;  | 
 | 718 | +        }  | 
 | 719 | + | 
 | 720 | +        quint16 crc = Packet::crc16((const unsigned char*)newFirmware.constData(),  | 
 | 721 | +                                    uint32_t(newFirmware.size()));  | 
 | 722 | +        VByteArray sizeCrc;  | 
 | 723 | +        if (useHeatshrink) {  | 
 | 724 | +            uint32_t szShift = 0xCC;  | 
 | 725 | +            szShift <<= 24;  | 
 | 726 | +            szShift |= szTot;  | 
 | 727 | +            sizeCrc.vbAppendUint32(szShift);  | 
 | 728 | +        } else {  | 
 | 729 | +            sizeCrc.vbAppendUint32(szTot);  | 
 | 730 | +        }  | 
 | 731 | +        sizeCrc.vbAppendUint16(crc);  | 
 | 732 | +        newFirmware.prepend(sizeCrc);  | 
 | 733 | +        fOut.write(newFirmware);  | 
 | 734 | +        fOut.close();  | 
 | 735 | + | 
 | 736 | +        qDebug() << "Done!";  | 
 | 737 | +        return 0;  | 
 | 738 | +    }  | 
 | 739 | + | 
631 | 740 |     if (!pkgArgs.isEmpty()) {  | 
632 | 741 |         if (pkgArgs.size() < 4) {  | 
633 | 742 |             qWarning() << "Invalid arguments";  | 
@@ -795,7 +904,8 @@ int main(int argc, char *argv[])  | 
795 | 904 |     bool isCustomConf = !getCustomConfPath.isEmpty() || !setCustomConfPath.isEmpty();  | 
796 | 905 | 
 
  | 
797 | 906 |     if (isMcConf || isAppConf || isCustomConf || !lispPath.isEmpty() ||  | 
798 |  | -            eraseLisp || !firmwarePath.isEmpty() || uploadBootloaderBuiltin) {  | 
 | 907 | +            eraseLisp || !firmwarePath.isEmpty() || uploadBootloaderBuiltin ||  | 
 | 908 | +            !fileForSdIn.isEmpty()) {  | 
799 | 909 |         qputenv("QT_QPA_PLATFORM", "offscreen");  | 
800 | 910 |         app = new QCoreApplication(argc, argv);  | 
801 | 911 |         vesc = new VescInterface;  | 
@@ -842,6 +952,15 @@ int main(int argc, char *argv[])  | 
842 | 952 |             }  | 
843 | 953 |         });  | 
844 | 954 | 
 
  | 
 | 955 | +        QObject::connect(vesc->commands(), &Commands::fileProgress, []  | 
 | 956 | +                         (int32_t prog, int32_t tot, double percentage, double bytesPerSec) {  | 
 | 957 | +            (void)prog;  | 
 | 958 | +            (void)tot;  | 
 | 959 | + | 
 | 960 | +            fprintf(stderr, "%s", QString("\rUpload progress: %1% (%2 kbps)").  | 
 | 961 | +                    arg(floor(percentage)).arg(bytesPerSec / 1024).toLatin1().data());  | 
 | 962 | +        });  | 
 | 963 | + | 
845 | 964 |         QTimer::singleShot(10, [&]() {  | 
846 | 965 |             int exitCode = 0;  | 
847 | 966 |             bool ok = false;  | 
@@ -910,6 +1029,27 @@ int main(int argc, char *argv[])  | 
910 | 1029 |                     }  | 
911 | 1030 |                 }  | 
912 | 1031 | 
 
  | 
 | 1032 | +                if (!fileForSdIn.isEmpty()) {  | 
 | 1033 | +                    QFile f(fileForSdIn);  | 
 | 1034 | +                    QFileInfo fi(f);  | 
 | 1035 | +                    if (f.open(QIODevice::ReadOnly)) {  | 
 | 1036 | +                        QFileInfo fi(f);  | 
 | 1037 | +                        vesc->commands()->fileBlockMkdir(fileForSdOut);  | 
 | 1038 | +                        QString target = fileForSdOut + "/" + fi.fileName();  | 
 | 1039 | +                        if (vesc->commands()->fileBlockWrite(target.replace("//", "/"), f.readAll())) {  | 
 | 1040 | +                            qDebug() << "Done!";  | 
 | 1041 | +                        } else {  | 
 | 1042 | +                            qWarning() << "Could not write file";  | 
 | 1043 | +                            exitCode = -51;  | 
 | 1044 | +                        }  | 
 | 1045 | + | 
 | 1046 | +                        f.close();  | 
 | 1047 | +                    } else {  | 
 | 1048 | +                        qWarning() << "Could not open file for reading.";  | 
 | 1049 | +                        exitCode = -50;  | 
 | 1050 | +                    }  | 
 | 1051 | +                }  | 
 | 1052 | + | 
913 | 1053 |                 if (isMcConf || isAppConf || isCustomConf) {  | 
914 | 1054 |                     bool res = vesc->customConfigRxDone();  | 
915 | 1055 |                     if (!res) {  | 
 | 
0 commit comments