diff OSTC2cOperations.cpp @ 1:0b3630a29ad8

Initial version based on previous repository. Project was ported to QT6 and in now cmake based.
author Ideenmodellierer <tiefenrauscher@web.de>
date Thu, 27 Nov 2025 18:40:28 +0100
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/OSTC2cOperations.cpp	Thu Nov 27 18:40:28 2025 +0100
@@ -0,0 +1,542 @@
+//////////////////////////////////////////////////////////////////////////////
+/// \file   OSTC2cOperations.h
+/// \brief  Implementing various operations for H&W OSTC2, mk2, 2n, 2c dive computer
+/// \author JD Gascuel.
+/// \sa     ComputerOperations.h
+///
+/// \copyright (c) 2011-2016 JD Gascuel. All rights reserved.
+/// $Id$
+//////////////////////////////////////////////////////////////////////////////
+//
+// BSD 2-Clause License:
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+//    this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation
+//    and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+// THE POSSIBILITY OF SUCH DAMAGE.
+//
+//////////////////////////////////////////////////////////////////////////////
+
+#include "OSTC2cOperations.h"
+
+#include "HexFile.h"
+
+#include "SettingsDialog.h"
+
+#include "Utils/Exception.h"
+#include "Utils/Log.h"
+#include "Utils/ProgressEvent.h"
+
+#include <QDateTime>
+#include <QRegularExpression>
+
+#define FIRMWARE_SIZE       0x17F40
+
+// 64 bytes on Mk.2/2n/2c:
+#define FIRMWARE_BLOCK_SIZE 0x40
+
+//////////////////////////////////////////////////////////////////////////////
+
+OSTC2cOperations::OSTC2cOperations()
+  : HardwareOperations(),
+    _computerFirmware(0),
+    _computerSerial(0)
+{
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+void OSTC2cOperations::readBank0(byte bank0[])
+{
+    _serial.sleep(100);
+    _serial.purge();
+    _serial.writeByte('g');
+    _serial.readBlock(bank0, 256);
+}
+
+void OSTC2cOperations::writeBank0(const byte bank0[])
+{
+    _serial.sleep(100);
+    _serial.purge();
+    _serial.writeByte('d');
+    int reply = _serial.readByte();
+    if( reply != 'd' )
+        LOG_THROW("Write start");
+
+    for(int a=4; a<256; ++a) {
+        _serial.writeByte(bank0[a]);
+        reply = _serial.readByte();
+        if( reply != bank0[a] )
+            LOG_THROW("Write bank0 @ " << a);
+    }
+
+    _serial.sleep(500); // Allow OSTC2c some time to reboot
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+void OSTC2cOperations::readBank1(byte bank1[256])
+{
+    _serial.sleep(100);
+    _serial.purge();
+    _serial.writeByte('j');
+    _serial.readBlock(bank1, 256);
+}
+
+void OSTC2cOperations::getIdentity()
+{
+    byte bank0[256], bank1[256];
+    memset(bank0, 0xFF, sizeof bank0);
+    memset(bank1, 0xFF, sizeof bank1);
+
+    //---- Get a memory dump:
+    try {
+        readBank0(bank0);
+        readBank1(bank1);
+    } catch(ReadTimeout) {
+        LOG_THROW("No reply from OSTC Mk.2, 2n or 2c...");
+    }
+
+    // Sanity check:
+    if( bank0[65] == 0xFF || bank1[1] > 99 || bank1[2] > 99 )
+        LOG_THROW("Not an OSTC Mk.2, 2n or 2c...");
+
+    _computerSerial   = bank0[0] + bank0[1]*256;
+    _computerFirmware = bank1[1] * 100 + bank1[2];
+    _customText       = QString::fromLatin1((char*)bank0+65, 25).section('}', 0,0);
+
+    _description = QString("%1 #%2, v%3.%4, %5")
+        .arg(model())
+        .arg(_computerSerial)
+        .arg(bank1[1]).arg(bank1[2], 2, 10, QChar('0'))
+        .arg( _customText );
+}
+
+int OSTC2cOperations::firmware() const
+{
+    return _computerFirmware;
+}
+
+int OSTC2cOperations::serialNumber() const
+{
+    return _computerSerial;
+}
+
+QString OSTC2cOperations::customText() const
+{
+    return _customText;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+#if 0
+QRegExp OSTC2cOperations::portTemplate() const
+{
+#if defined(Q_OS_MAC)
+    return QRegExp("tty.usbserial-.*", Qt::CaseInsensitive);
+#elif defined(Q_OS_LINUX)
+    // Seems ok for debian, ubuntu, redhat, CentOS and SUSE (google dixit)
+    return QRegExp("ttyUSB.*", Qt::CaseSensitive);
+#elif defined(Q_OS_WIN)
+    return QRegExp("COM.*", Qt::CaseSensitive);
+#endif
+}
+#endif
+QRegularExpression OSTC2cOperations::portTemplate() const
+{
+#if defined(Q_OS_MAC)
+    return QRegularExpression("tty.usbserial-.*",
+                              QRegularExpression::CaseInsensitiveOption);
+#elif defined(Q_OS_LINUX)
+    return QRegularExpression("ttyUSB.*"); // default: case-sensitive
+#elif defined(Q_OS_WIN)
+    return QRegularExpression("COM.*");    // default: case-sensitive
+#endif
+}
+QStringList OSTC2cOperations::listPorts() const
+{
+    return listUSBPorts();
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+QString OSTC2cOperations::model() const
+{
+    return "OSTC2c";
+}
+
+QString OSTC2cOperations::description()
+{
+    return _description;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+HardwareOperations::CompanionFeatures OSTC2cOperations::supported() const
+{
+    // No ICON.
+    return CompanionFeatures(PARAMETERS|DATE|NAME|DUMPSCREEN|FIRMWARE
+                     |HELIUM_DIVE|CCR_DIVE);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+QString OSTC2cOperations::firmwareTemplate() const
+{
+    return "mk2_*.hex";
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+bool OSTC2cOperations::connect()
+{
+    try {
+        LOG_TRACE("Connecting " << Settings::port);
+        _serial.open(Settings::port, "OSTC2c");
+        getIdentity();
+        return true;
+    }
+    catch(const Exception& e){
+        _serial.close();
+        LOG_THROW("Cannot connect " << Settings::port << ": " << e.what());
+    }
+    return false;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+QSize OSTC2cOperations::nameSize() const
+{
+    return QSize(25, 1);
+}
+
+void OSTC2cOperations::writeText(const QString& msg)
+{
+    // No hardware support for that. Just skip it, as it is done when
+    // writting bank1...
+    LOG_TRACE(msg);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+void OSTC2cOperations::connectServiceMode()
+{
+    try {
+        _serial.open(Settings::port, "OSTC2c");
+
+        _serial.writeByte(0xC1);
+        _serial.sleep(500);                    // 0.50 sec for bootloader to start.
+
+        //---- Send C1C1 to start upgrading firmware -----------------------------
+        unsigned char pic = 0;
+        unsigned char ok = '?';
+        for(int retry=0; retry < 10; ++retry) {
+            _serial.writeByte(0xC1);
+            _serial.sleep(5);
+            _serial.writeByte(0xC1);
+            _serial.sleep(5);
+
+            try {
+                pic = _serial.readByte();
+                ok  = _serial.readByte();
+                break;
+            }
+            catch(const ReadTimeout&) {
+                if( retry == 9 )
+                    LOG_THROW("Cannot start firmware upgrade: timeout.");
+
+                LOG_INFO("Connecting OSTC2c (" << (retry+1) << "/10)...");
+            }
+        }
+
+        //---- Check PIC type ----------------------------------------------------
+        LOG_TRACE("Pic = " << int(pic));
+        if( ! pic )
+            LOG_THROW("Cannot sync firmware upgrade. Cannot detect chip");
+
+        if( pic != 0x57
+         || ok  != 'K'
+        )
+            LOG_THROW( "Cannot sync firmware upgrade. Bad chip " << int(pic) << " " << ok);
+
+        _serial.sleep(50);                       // Wait 0.050 sec here.
+        return;
+    }
+    catch(const Exception& e) {
+        _serial.close();
+        LOG_THROW("Cannot connect " << Settings::port << ": " << e.what());
+    }
+    return;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+bool OSTC2cOperations::disconnect(bool /*closing*/)
+{
+    if( ! _serial.isOpen() ) return false;
+
+    LOG_TRACE("Disconnecting.");
+
+    _serial.purge();
+    _serial.close();
+
+    return true;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+void OSTC2cOperations::setDate(const QDateTime &date)
+{
+    unsigned char buffer[6];
+    buffer[0] = date.time().hour();
+    buffer[1] = date.time().minute();
+    buffer[2] = date.time().second();
+    buffer[3] = date.date().month();
+    buffer[4] = date.date().day();
+    buffer[5] = date.date().year() % 100;
+
+    _serial.sleep(100);         // Make sure last command is finished.
+    _serial.purge();
+    _serial.writeByte('b'); // 0x62
+
+    unsigned char reply = _serial.readByte();
+    if( reply != 'b' )
+        LOG_THROW( "sync time" );
+
+    _serial.writeBlock( buffer, sizeof buffer);
+    _serial.sleep(100);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+void OSTC2cOperations::setName(const QString &newName)
+{
+    QByteArray padded = (newName + QString(25, '}'))
+            .left(25)
+            .toLatin1();
+
+    byte bank0[256] = {0};
+    readBank0(bank0);
+    memcpy(bank0+65, padded.constData(), 25);
+    writeBank0(bank0);
+
+    // Then get the new identity:
+    getIdentity();
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+void OSTC2cOperations::setIcons(const QString &/*fileName*/)
+{
+    LOG_THROW("Not supported");
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+QImage OSTC2cOperations::dumpScreen() const
+{
+    QImage image(320, 240, QImage::Format_RGB32);
+
+     //---- Send dump screen command -------------------------------------
+    unsigned char reply;
+    _serial.writeByte('l');
+    reply = _serial.readByte();
+
+    if( reply != 'l' )
+        LOG_THROW( "Dumpscreen command failed: " << (int)reply );
+
+    //---- Read image ----------------------------------------------------
+    int percent = 0;
+    try {
+        for(int x=0; x<320; ++x)
+        {
+            int p = x/16;       // 5% steps
+            if( p != percent )
+            {
+                PROGRESS(p, 320/16);
+                percent = p;
+            }
+            int pix = 0, count = 0;
+
+            for(int y=0; y<240; ++y, --count)
+            {
+                if( count <= 0 )
+                {
+                    count = _serial.readByte();
+
+                    if( (count & 0x80) == 0 )
+                        pix = 0;
+                    else if( (count & 0xC0) == 0xC0 )
+                    {
+                        pix = 0xFFFF;
+                        count &= 0x3F;
+                    }
+                    else
+                    {
+                        unsigned char bpix[2];
+                        _serial.readBlock(bpix, 2);
+                        pix = (bpix[0] << 8) + bpix[1];
+                        count &= 0x3F;
+                    }
+                    count++;
+                }
+                // Bit extension 5bits --> 8bits:
+                //      12345123.45 = (12345 * 0b100001) / 4
+                //      12345612.3456 = (123456 * 0xb1000001) / 16
+                int r = (31 & (pix >> 11)) * 255/31;
+                int g = (63 & (pix >>  5)) * 255/63;
+                int b = (31 &  pix       ) * 255/31;
+                image.setPixel(x, y, qRgb( r, g, b));
+            }
+        }
+    }
+    catch( ReadTimeout ) {
+        LOG_THROW("Missing image data...");
+    }
+
+    //---- Done ----------------------------------------------------------
+    PROGRESS_RESET();
+    LOG_INFO( "Screen dumped." );
+    return image;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+void OSTC2cOperations::loadFirmware(HexFile& hex, const QString& fileName) const
+{
+    hex.allocate(FIRMWARE_SIZE);
+    hex.load(fileName);
+
+    //---- Patch Firmware intialization GOTO ---------------------------------
+    memcpy((void*)(hex.data() + 0x17F38),   // To bootloader vector
+           (void*)(hex.data() + 0x00000),   // From fw reset code
+           8);                              // Up to 8 bytes...
+
+    static unsigned char byteCode[8] = {
+        0xA0, 0xEF, 0xBF, 0xF0,             // goto 0x1F740 (bootloader 19k)
+        0x00, 0x00,                         // nop
+        0x00, 0x00                          // nop
+    };
+    memcpy((void*)(hex.data() + 0x00000),   // To OSTC reset vector
+           (void*)(byteCode),               // From go back to bootloader.
+           8);                              // Up to 8 bytes...
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+static void
+uploadBlock(Serial& serial, const HexFile& hex, size_t addr)
+{
+    const unsigned char count = FIRMWARE_BLOCK_SIZE;
+    assert( 0 < count && count < 255 );
+    assert((addr+count) <= FIRMWARE_SIZE );
+    unsigned char reply = 0;
+
+    unsigned char header[4];
+    header[0] = 0x1F & (addr >> 16);
+    header[1] = 0xFF & (addr >>  8);
+    header[2] = 0xFF & (addr);
+    header[3] = count;
+
+    unsigned char crc = header[0] + header[1] + header[2] + header[3];
+    for(int i=0; i<count; ++i)
+        crc += hex.data()[addr+i];
+    crc = -crc; // Sum should make zero.
+
+    try {
+        serial.writeBlock(header, sizeof header);
+        serial.writeBlock(hex.data()+addr, count);
+        serial.writeByte(crc);
+    } catch(...) {
+        goto WriteFailed;
+    }
+
+    serial.sleep(20);        // 18msec for a FLASH row write, plus VAT.
+
+    reply = serial.readByte();
+    if( reply != 'K' )
+    {
+        serial.close();
+        LOG_THROW( QString("Bad checksum at 0x%1").arg((unsigned int)addr, 0, 16, QChar('0')));
+    }
+    return;
+
+WriteFailed:
+    serial.close();
+
+    LOG_THROW( QString("Write failed") );
+}
+
+void OSTC2cOperations::upgradeFW(const QString& fileName)
+{
+    //---- Load and check firmware ---------------------------------------
+    LOG_TRACE("Loading firmware '" << fileName << "'.");
+    HexFile hex;
+    loadFirmware(hex, fileName);
+
+    //---- Enter uart_115k_bootloader ----------------------------------------
+    connectServiceMode();
+
+    //---- Let's do it -------------------------------------------------------
+    int percent = 0;
+
+    for(size_t addr = FIRMWARE_BLOCK_SIZE; addr < FIRMWARE_SIZE; addr += FIRMWARE_BLOCK_SIZE)
+    {
+        int p = int((addr*200.0f) / float(FIRMWARE_SIZE) + 0.5f);
+        if( p > percent )
+        {
+            PROGRESS(percent, 200);
+            percent = p;
+        }
+        uploadBlock(_serial, hex, addr);
+    }
+    PROGRESS(200, 200);
+    uploadBlock(_serial, hex, 0);
+
+    PROGRESS_RESET();
+    LOG_INFO("Upgrade FW send.");
+
+    // More than 500ms --> receive timeout.
+    _serial.sleep(600);
+}
+
+void OSTC2cOperations::getSignal()
+{
+    return;
+}
+
+void OSTC2cOperations::getAllHeader(unsigned char* pBuffer)
+{
+    return;
+}
+void OSTC2cOperations::writeAllHeader(unsigned char* pBuffer)
+{
+    return;
+}
+
+void OSTC2cOperations::getAllSamples(unsigned char* pBuffer)
+{
+    return;
+}
+void OSTC2cOperations::writeAllSamples(unsigned char* pBuffer)
+{
+    return;
+}
+//////////////////////////////////////////////////////////////////////////////