Mercurial > public > ostc_companion
comparison 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 |
comparison
equal
deleted
inserted
replaced
| 0:76ccd6ce50c0 | 1:0b3630a29ad8 |
|---|---|
| 1 ////////////////////////////////////////////////////////////////////////////// | |
| 2 /// \file OSTC2cOperations.h | |
| 3 /// \brief Implementing various operations for H&W OSTC2, mk2, 2n, 2c dive computer | |
| 4 /// \author JD Gascuel. | |
| 5 /// \sa ComputerOperations.h | |
| 6 /// | |
| 7 /// \copyright (c) 2011-2016 JD Gascuel. All rights reserved. | |
| 8 /// $Id$ | |
| 9 ////////////////////////////////////////////////////////////////////////////// | |
| 10 // | |
| 11 // BSD 2-Clause License: | |
| 12 // | |
| 13 // Redistribution and use in source and binary forms, with or without | |
| 14 // modification, are permitted provided that the following conditions | |
| 15 // are met: | |
| 16 // | |
| 17 // 1. Redistributions of source code must retain the above copyright notice, | |
| 18 // this list of conditions and the following disclaimer. | |
| 19 // | |
| 20 // 2. Redistributions in binary form must reproduce the above copyright notice, | |
| 21 // this list of conditions and the following disclaimer in the documentation | |
| 22 // and/or other materials provided with the distribution. | |
| 23 // | |
| 24 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
| 25 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| 26 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
| 27 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE | |
| 28 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
| 29 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
| 30 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
| 31 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
| 32 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
| 33 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF | |
| 34 // THE POSSIBILITY OF SUCH DAMAGE. | |
| 35 // | |
| 36 ////////////////////////////////////////////////////////////////////////////// | |
| 37 | |
| 38 #include "OSTC2cOperations.h" | |
| 39 | |
| 40 #include "HexFile.h" | |
| 41 | |
| 42 #include "SettingsDialog.h" | |
| 43 | |
| 44 #include "Utils/Exception.h" | |
| 45 #include "Utils/Log.h" | |
| 46 #include "Utils/ProgressEvent.h" | |
| 47 | |
| 48 #include <QDateTime> | |
| 49 #include <QRegularExpression> | |
| 50 | |
| 51 #define FIRMWARE_SIZE 0x17F40 | |
| 52 | |
| 53 // 64 bytes on Mk.2/2n/2c: | |
| 54 #define FIRMWARE_BLOCK_SIZE 0x40 | |
| 55 | |
| 56 ////////////////////////////////////////////////////////////////////////////// | |
| 57 | |
| 58 OSTC2cOperations::OSTC2cOperations() | |
| 59 : HardwareOperations(), | |
| 60 _computerFirmware(0), | |
| 61 _computerSerial(0) | |
| 62 { | |
| 63 } | |
| 64 | |
| 65 ////////////////////////////////////////////////////////////////////////////// | |
| 66 | |
| 67 void OSTC2cOperations::readBank0(byte bank0[]) | |
| 68 { | |
| 69 _serial.sleep(100); | |
| 70 _serial.purge(); | |
| 71 _serial.writeByte('g'); | |
| 72 _serial.readBlock(bank0, 256); | |
| 73 } | |
| 74 | |
| 75 void OSTC2cOperations::writeBank0(const byte bank0[]) | |
| 76 { | |
| 77 _serial.sleep(100); | |
| 78 _serial.purge(); | |
| 79 _serial.writeByte('d'); | |
| 80 int reply = _serial.readByte(); | |
| 81 if( reply != 'd' ) | |
| 82 LOG_THROW("Write start"); | |
| 83 | |
| 84 for(int a=4; a<256; ++a) { | |
| 85 _serial.writeByte(bank0[a]); | |
| 86 reply = _serial.readByte(); | |
| 87 if( reply != bank0[a] ) | |
| 88 LOG_THROW("Write bank0 @ " << a); | |
| 89 } | |
| 90 | |
| 91 _serial.sleep(500); // Allow OSTC2c some time to reboot | |
| 92 } | |
| 93 | |
| 94 ////////////////////////////////////////////////////////////////////////////// | |
| 95 | |
| 96 void OSTC2cOperations::readBank1(byte bank1[256]) | |
| 97 { | |
| 98 _serial.sleep(100); | |
| 99 _serial.purge(); | |
| 100 _serial.writeByte('j'); | |
| 101 _serial.readBlock(bank1, 256); | |
| 102 } | |
| 103 | |
| 104 void OSTC2cOperations::getIdentity() | |
| 105 { | |
| 106 byte bank0[256], bank1[256]; | |
| 107 memset(bank0, 0xFF, sizeof bank0); | |
| 108 memset(bank1, 0xFF, sizeof bank1); | |
| 109 | |
| 110 //---- Get a memory dump: | |
| 111 try { | |
| 112 readBank0(bank0); | |
| 113 readBank1(bank1); | |
| 114 } catch(ReadTimeout) { | |
| 115 LOG_THROW("No reply from OSTC Mk.2, 2n or 2c..."); | |
| 116 } | |
| 117 | |
| 118 // Sanity check: | |
| 119 if( bank0[65] == 0xFF || bank1[1] > 99 || bank1[2] > 99 ) | |
| 120 LOG_THROW("Not an OSTC Mk.2, 2n or 2c..."); | |
| 121 | |
| 122 _computerSerial = bank0[0] + bank0[1]*256; | |
| 123 _computerFirmware = bank1[1] * 100 + bank1[2]; | |
| 124 _customText = QString::fromLatin1((char*)bank0+65, 25).section('}', 0,0); | |
| 125 | |
| 126 _description = QString("%1 #%2, v%3.%4, %5") | |
| 127 .arg(model()) | |
| 128 .arg(_computerSerial) | |
| 129 .arg(bank1[1]).arg(bank1[2], 2, 10, QChar('0')) | |
| 130 .arg( _customText ); | |
| 131 } | |
| 132 | |
| 133 int OSTC2cOperations::firmware() const | |
| 134 { | |
| 135 return _computerFirmware; | |
| 136 } | |
| 137 | |
| 138 int OSTC2cOperations::serialNumber() const | |
| 139 { | |
| 140 return _computerSerial; | |
| 141 } | |
| 142 | |
| 143 QString OSTC2cOperations::customText() const | |
| 144 { | |
| 145 return _customText; | |
| 146 } | |
| 147 | |
| 148 ////////////////////////////////////////////////////////////////////////////// | |
| 149 #if 0 | |
| 150 QRegExp OSTC2cOperations::portTemplate() const | |
| 151 { | |
| 152 #if defined(Q_OS_MAC) | |
| 153 return QRegExp("tty.usbserial-.*", Qt::CaseInsensitive); | |
| 154 #elif defined(Q_OS_LINUX) | |
| 155 // Seems ok for debian, ubuntu, redhat, CentOS and SUSE (google dixit) | |
| 156 return QRegExp("ttyUSB.*", Qt::CaseSensitive); | |
| 157 #elif defined(Q_OS_WIN) | |
| 158 return QRegExp("COM.*", Qt::CaseSensitive); | |
| 159 #endif | |
| 160 } | |
| 161 #endif | |
| 162 QRegularExpression OSTC2cOperations::portTemplate() const | |
| 163 { | |
| 164 #if defined(Q_OS_MAC) | |
| 165 return QRegularExpression("tty.usbserial-.*", | |
| 166 QRegularExpression::CaseInsensitiveOption); | |
| 167 #elif defined(Q_OS_LINUX) | |
| 168 return QRegularExpression("ttyUSB.*"); // default: case-sensitive | |
| 169 #elif defined(Q_OS_WIN) | |
| 170 return QRegularExpression("COM.*"); // default: case-sensitive | |
| 171 #endif | |
| 172 } | |
| 173 QStringList OSTC2cOperations::listPorts() const | |
| 174 { | |
| 175 return listUSBPorts(); | |
| 176 } | |
| 177 | |
| 178 ////////////////////////////////////////////////////////////////////////////// | |
| 179 | |
| 180 QString OSTC2cOperations::model() const | |
| 181 { | |
| 182 return "OSTC2c"; | |
| 183 } | |
| 184 | |
| 185 QString OSTC2cOperations::description() | |
| 186 { | |
| 187 return _description; | |
| 188 } | |
| 189 | |
| 190 ////////////////////////////////////////////////////////////////////////////// | |
| 191 | |
| 192 HardwareOperations::CompanionFeatures OSTC2cOperations::supported() const | |
| 193 { | |
| 194 // No ICON. | |
| 195 return CompanionFeatures(PARAMETERS|DATE|NAME|DUMPSCREEN|FIRMWARE | |
| 196 |HELIUM_DIVE|CCR_DIVE); | |
| 197 } | |
| 198 | |
| 199 ////////////////////////////////////////////////////////////////////////////// | |
| 200 | |
| 201 QString OSTC2cOperations::firmwareTemplate() const | |
| 202 { | |
| 203 return "mk2_*.hex"; | |
| 204 } | |
| 205 | |
| 206 ////////////////////////////////////////////////////////////////////////////// | |
| 207 | |
| 208 bool OSTC2cOperations::connect() | |
| 209 { | |
| 210 try { | |
| 211 LOG_TRACE("Connecting " << Settings::port); | |
| 212 _serial.open(Settings::port, "OSTC2c"); | |
| 213 getIdentity(); | |
| 214 return true; | |
| 215 } | |
| 216 catch(const Exception& e){ | |
| 217 _serial.close(); | |
| 218 LOG_THROW("Cannot connect " << Settings::port << ": " << e.what()); | |
| 219 } | |
| 220 return false; | |
| 221 } | |
| 222 | |
| 223 ////////////////////////////////////////////////////////////////////////////// | |
| 224 | |
| 225 QSize OSTC2cOperations::nameSize() const | |
| 226 { | |
| 227 return QSize(25, 1); | |
| 228 } | |
| 229 | |
| 230 void OSTC2cOperations::writeText(const QString& msg) | |
| 231 { | |
| 232 // No hardware support for that. Just skip it, as it is done when | |
| 233 // writting bank1... | |
| 234 LOG_TRACE(msg); | |
| 235 } | |
| 236 | |
| 237 ////////////////////////////////////////////////////////////////////////////// | |
| 238 | |
| 239 void OSTC2cOperations::connectServiceMode() | |
| 240 { | |
| 241 try { | |
| 242 _serial.open(Settings::port, "OSTC2c"); | |
| 243 | |
| 244 _serial.writeByte(0xC1); | |
| 245 _serial.sleep(500); // 0.50 sec for bootloader to start. | |
| 246 | |
| 247 //---- Send C1C1 to start upgrading firmware ----------------------------- | |
| 248 unsigned char pic = 0; | |
| 249 unsigned char ok = '?'; | |
| 250 for(int retry=0; retry < 10; ++retry) { | |
| 251 _serial.writeByte(0xC1); | |
| 252 _serial.sleep(5); | |
| 253 _serial.writeByte(0xC1); | |
| 254 _serial.sleep(5); | |
| 255 | |
| 256 try { | |
| 257 pic = _serial.readByte(); | |
| 258 ok = _serial.readByte(); | |
| 259 break; | |
| 260 } | |
| 261 catch(const ReadTimeout&) { | |
| 262 if( retry == 9 ) | |
| 263 LOG_THROW("Cannot start firmware upgrade: timeout."); | |
| 264 | |
| 265 LOG_INFO("Connecting OSTC2c (" << (retry+1) << "/10)..."); | |
| 266 } | |
| 267 } | |
| 268 | |
| 269 //---- Check PIC type ---------------------------------------------------- | |
| 270 LOG_TRACE("Pic = " << int(pic)); | |
| 271 if( ! pic ) | |
| 272 LOG_THROW("Cannot sync firmware upgrade. Cannot detect chip"); | |
| 273 | |
| 274 if( pic != 0x57 | |
| 275 || ok != 'K' | |
| 276 ) | |
| 277 LOG_THROW( "Cannot sync firmware upgrade. Bad chip " << int(pic) << " " << ok); | |
| 278 | |
| 279 _serial.sleep(50); // Wait 0.050 sec here. | |
| 280 return; | |
| 281 } | |
| 282 catch(const Exception& e) { | |
| 283 _serial.close(); | |
| 284 LOG_THROW("Cannot connect " << Settings::port << ": " << e.what()); | |
| 285 } | |
| 286 return; | |
| 287 } | |
| 288 | |
| 289 ////////////////////////////////////////////////////////////////////////////// | |
| 290 | |
| 291 bool OSTC2cOperations::disconnect(bool /*closing*/) | |
| 292 { | |
| 293 if( ! _serial.isOpen() ) return false; | |
| 294 | |
| 295 LOG_TRACE("Disconnecting."); | |
| 296 | |
| 297 _serial.purge(); | |
| 298 _serial.close(); | |
| 299 | |
| 300 return true; | |
| 301 } | |
| 302 | |
| 303 ////////////////////////////////////////////////////////////////////////////// | |
| 304 | |
| 305 void OSTC2cOperations::setDate(const QDateTime &date) | |
| 306 { | |
| 307 unsigned char buffer[6]; | |
| 308 buffer[0] = date.time().hour(); | |
| 309 buffer[1] = date.time().minute(); | |
| 310 buffer[2] = date.time().second(); | |
| 311 buffer[3] = date.date().month(); | |
| 312 buffer[4] = date.date().day(); | |
| 313 buffer[5] = date.date().year() % 100; | |
| 314 | |
| 315 _serial.sleep(100); // Make sure last command is finished. | |
| 316 _serial.purge(); | |
| 317 _serial.writeByte('b'); // 0x62 | |
| 318 | |
| 319 unsigned char reply = _serial.readByte(); | |
| 320 if( reply != 'b' ) | |
| 321 LOG_THROW( "sync time" ); | |
| 322 | |
| 323 _serial.writeBlock( buffer, sizeof buffer); | |
| 324 _serial.sleep(100); | |
| 325 } | |
| 326 | |
| 327 ////////////////////////////////////////////////////////////////////////////// | |
| 328 | |
| 329 void OSTC2cOperations::setName(const QString &newName) | |
| 330 { | |
| 331 QByteArray padded = (newName + QString(25, '}')) | |
| 332 .left(25) | |
| 333 .toLatin1(); | |
| 334 | |
| 335 byte bank0[256] = {0}; | |
| 336 readBank0(bank0); | |
| 337 memcpy(bank0+65, padded.constData(), 25); | |
| 338 writeBank0(bank0); | |
| 339 | |
| 340 // Then get the new identity: | |
| 341 getIdentity(); | |
| 342 } | |
| 343 | |
| 344 ////////////////////////////////////////////////////////////////////////////// | |
| 345 | |
| 346 void OSTC2cOperations::setIcons(const QString &/*fileName*/) | |
| 347 { | |
| 348 LOG_THROW("Not supported"); | |
| 349 } | |
| 350 | |
| 351 ////////////////////////////////////////////////////////////////////////////// | |
| 352 | |
| 353 QImage OSTC2cOperations::dumpScreen() const | |
| 354 { | |
| 355 QImage image(320, 240, QImage::Format_RGB32); | |
| 356 | |
| 357 //---- Send dump screen command ------------------------------------- | |
| 358 unsigned char reply; | |
| 359 _serial.writeByte('l'); | |
| 360 reply = _serial.readByte(); | |
| 361 | |
| 362 if( reply != 'l' ) | |
| 363 LOG_THROW( "Dumpscreen command failed: " << (int)reply ); | |
| 364 | |
| 365 //---- Read image ---------------------------------------------------- | |
| 366 int percent = 0; | |
| 367 try { | |
| 368 for(int x=0; x<320; ++x) | |
| 369 { | |
| 370 int p = x/16; // 5% steps | |
| 371 if( p != percent ) | |
| 372 { | |
| 373 PROGRESS(p, 320/16); | |
| 374 percent = p; | |
| 375 } | |
| 376 int pix = 0, count = 0; | |
| 377 | |
| 378 for(int y=0; y<240; ++y, --count) | |
| 379 { | |
| 380 if( count <= 0 ) | |
| 381 { | |
| 382 count = _serial.readByte(); | |
| 383 | |
| 384 if( (count & 0x80) == 0 ) | |
| 385 pix = 0; | |
| 386 else if( (count & 0xC0) == 0xC0 ) | |
| 387 { | |
| 388 pix = 0xFFFF; | |
| 389 count &= 0x3F; | |
| 390 } | |
| 391 else | |
| 392 { | |
| 393 unsigned char bpix[2]; | |
| 394 _serial.readBlock(bpix, 2); | |
| 395 pix = (bpix[0] << 8) + bpix[1]; | |
| 396 count &= 0x3F; | |
| 397 } | |
| 398 count++; | |
| 399 } | |
| 400 // Bit extension 5bits --> 8bits: | |
| 401 // 12345123.45 = (12345 * 0b100001) / 4 | |
| 402 // 12345612.3456 = (123456 * 0xb1000001) / 16 | |
| 403 int r = (31 & (pix >> 11)) * 255/31; | |
| 404 int g = (63 & (pix >> 5)) * 255/63; | |
| 405 int b = (31 & pix ) * 255/31; | |
| 406 image.setPixel(x, y, qRgb( r, g, b)); | |
| 407 } | |
| 408 } | |
| 409 } | |
| 410 catch( ReadTimeout ) { | |
| 411 LOG_THROW("Missing image data..."); | |
| 412 } | |
| 413 | |
| 414 //---- Done ---------------------------------------------------------- | |
| 415 PROGRESS_RESET(); | |
| 416 LOG_INFO( "Screen dumped." ); | |
| 417 return image; | |
| 418 } | |
| 419 | |
| 420 ////////////////////////////////////////////////////////////////////////////// | |
| 421 | |
| 422 void OSTC2cOperations::loadFirmware(HexFile& hex, const QString& fileName) const | |
| 423 { | |
| 424 hex.allocate(FIRMWARE_SIZE); | |
| 425 hex.load(fileName); | |
| 426 | |
| 427 //---- Patch Firmware intialization GOTO --------------------------------- | |
| 428 memcpy((void*)(hex.data() + 0x17F38), // To bootloader vector | |
| 429 (void*)(hex.data() + 0x00000), // From fw reset code | |
| 430 8); // Up to 8 bytes... | |
| 431 | |
| 432 static unsigned char byteCode[8] = { | |
| 433 0xA0, 0xEF, 0xBF, 0xF0, // goto 0x1F740 (bootloader 19k) | |
| 434 0x00, 0x00, // nop | |
| 435 0x00, 0x00 // nop | |
| 436 }; | |
| 437 memcpy((void*)(hex.data() + 0x00000), // To OSTC reset vector | |
| 438 (void*)(byteCode), // From go back to bootloader. | |
| 439 8); // Up to 8 bytes... | |
| 440 } | |
| 441 | |
| 442 ////////////////////////////////////////////////////////////////////////////// | |
| 443 | |
| 444 static void | |
| 445 uploadBlock(Serial& serial, const HexFile& hex, size_t addr) | |
| 446 { | |
| 447 const unsigned char count = FIRMWARE_BLOCK_SIZE; | |
| 448 assert( 0 < count && count < 255 ); | |
| 449 assert((addr+count) <= FIRMWARE_SIZE ); | |
| 450 unsigned char reply = 0; | |
| 451 | |
| 452 unsigned char header[4]; | |
| 453 header[0] = 0x1F & (addr >> 16); | |
| 454 header[1] = 0xFF & (addr >> 8); | |
| 455 header[2] = 0xFF & (addr); | |
| 456 header[3] = count; | |
| 457 | |
| 458 unsigned char crc = header[0] + header[1] + header[2] + header[3]; | |
| 459 for(int i=0; i<count; ++i) | |
| 460 crc += hex.data()[addr+i]; | |
| 461 crc = -crc; // Sum should make zero. | |
| 462 | |
| 463 try { | |
| 464 serial.writeBlock(header, sizeof header); | |
| 465 serial.writeBlock(hex.data()+addr, count); | |
| 466 serial.writeByte(crc); | |
| 467 } catch(...) { | |
| 468 goto WriteFailed; | |
| 469 } | |
| 470 | |
| 471 serial.sleep(20); // 18msec for a FLASH row write, plus VAT. | |
| 472 | |
| 473 reply = serial.readByte(); | |
| 474 if( reply != 'K' ) | |
| 475 { | |
| 476 serial.close(); | |
| 477 LOG_THROW( QString("Bad checksum at 0x%1").arg((unsigned int)addr, 0, 16, QChar('0'))); | |
| 478 } | |
| 479 return; | |
| 480 | |
| 481 WriteFailed: | |
| 482 serial.close(); | |
| 483 | |
| 484 LOG_THROW( QString("Write failed") ); | |
| 485 } | |
| 486 | |
| 487 void OSTC2cOperations::upgradeFW(const QString& fileName) | |
| 488 { | |
| 489 //---- Load and check firmware --------------------------------------- | |
| 490 LOG_TRACE("Loading firmware '" << fileName << "'."); | |
| 491 HexFile hex; | |
| 492 loadFirmware(hex, fileName); | |
| 493 | |
| 494 //---- Enter uart_115k_bootloader ---------------------------------------- | |
| 495 connectServiceMode(); | |
| 496 | |
| 497 //---- Let's do it ------------------------------------------------------- | |
| 498 int percent = 0; | |
| 499 | |
| 500 for(size_t addr = FIRMWARE_BLOCK_SIZE; addr < FIRMWARE_SIZE; addr += FIRMWARE_BLOCK_SIZE) | |
| 501 { | |
| 502 int p = int((addr*200.0f) / float(FIRMWARE_SIZE) + 0.5f); | |
| 503 if( p > percent ) | |
| 504 { | |
| 505 PROGRESS(percent, 200); | |
| 506 percent = p; | |
| 507 } | |
| 508 uploadBlock(_serial, hex, addr); | |
| 509 } | |
| 510 PROGRESS(200, 200); | |
| 511 uploadBlock(_serial, hex, 0); | |
| 512 | |
| 513 PROGRESS_RESET(); | |
| 514 LOG_INFO("Upgrade FW send."); | |
| 515 | |
| 516 // More than 500ms --> receive timeout. | |
| 517 _serial.sleep(600); | |
| 518 } | |
| 519 | |
| 520 void OSTC2cOperations::getSignal() | |
| 521 { | |
| 522 return; | |
| 523 } | |
| 524 | |
| 525 void OSTC2cOperations::getAllHeader(unsigned char* pBuffer) | |
| 526 { | |
| 527 return; | |
| 528 } | |
| 529 void OSTC2cOperations::writeAllHeader(unsigned char* pBuffer) | |
| 530 { | |
| 531 return; | |
| 532 } | |
| 533 | |
| 534 void OSTC2cOperations::getAllSamples(unsigned char* pBuffer) | |
| 535 { | |
| 536 return; | |
| 537 } | |
| 538 void OSTC2cOperations::writeAllSamples(unsigned char* pBuffer) | |
| 539 { | |
| 540 return; | |
| 541 } | |
| 542 ////////////////////////////////////////////////////////////////////////////// |
