Mercurial > public > ostc_companion
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; +} +//////////////////////////////////////////////////////////////////////////////
