|
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