Mercurial > public > ostc_companion
view OSTC2cOperations.cpp @ 3:4ace58a7c03c
Send disconnect command before closing the connection
The old BT module transmitted a notification in case a connection were
closed which cause the ostc to exit the uart loop. The new one doesn't
do this => send disconnect command to avoid waiting in the installation
loop till timeout or button press.
| author | Ideenmodellierer |
|---|---|
| date | Fri, 28 Nov 2025 20:00:02 +0100 |
| parents | 0b3630a29ad8 |
| children |
line wrap: on
line source
////////////////////////////////////////////////////////////////////////////// /// \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; } //////////////////////////////////////////////////////////////////////////////
