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 //////////////////////////////////////////////////////////////////////////////