1 // ----------------------------------------------------------------------------
2 // nanoIO.cxx  --  Interface to Arduino Nano keyer
3 //
4 // Copyright (C) 2018
5 //		Dave Freese, W1HKJ
6 //
7 // This file is part of fldigi.
8 //
9 // Fldigi is free software: you can redistribute it and/or modify
10 // it under the terms of the GNU General Public License as published by
11 // the Free Software Foundation, either version 3 of the License, or
12 // (at your option) any later version.
13 //
14 // Fldigi is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 // GNU General Public License for more details.
18 //
19 // You should have received a copy of the GNU General Public License
20 // along with fldigi.  If not, see <http://www.gnu.org/licenses/>.
21 // ----------------------------------------------------------------------------
22 
23 
24 #include <config.h>
25 
26 #include "fl_digi.h"
27 #include "nanoIO.h"
28 #include "serial.h"
29 #include "morse.h"
30 
31 #include "strutil.h"
32 
33 #define LOG_TINYFSK  LOG_INFO
34 
35 using namespace std;
36 
37 Cserial nano_serial;
38 
39 bool use_nanoIO = false;
40 bool nanoIO_isCW = false;
41 
42 static cMorse *nano_morse = 0;
43 
44 // use during debugging
sent(string s)45 void sent(string s)
46 {
47 	if (!grp_nanoio_debug->visible()) return;
48 
49 	brws_nanoio_sent->add(s.c_str());
50 	brws_nanoio_sent->redraw();
51 }
52 
rcvd(string s)53 void rcvd(string s)
54 {
55 	if (!grp_nanoio_debug->visible()) return;
56 
57 	if (s.empty())
58 		brws_nanoio_rcvd->add("...nil");
59 	else {
60 		size_t ptr = s.find("\ncmd:");
61 		if (ptr != std::string::npos) s.erase(ptr, 1);
62 		if (s[s.length() - 1] == '\n') s.erase(s.length() -1);
63 		ptr = s.find("\n");
64 		while (!s.empty()) {
65 			if (ptr == std::string::npos) {
66 				brws_nanoio_rcvd->add(string("@t").append(s).c_str());
67 				break;
68 			}
69 			brws_nanoio_rcvd->add(string("@t").append(s.substr(0, ptr)).c_str());
70 			brws_nanoio_sent->add("@t-");
71 			s.erase(0, ptr+1);
72 			ptr = s.find("\n");
73 		}
74 	}
75 	brws_nanoio_rcvd->redraw();
76 }
77 
78 // subtract two timeval values and return result in seconds
79 
timeval_subtract(struct timeval & x,struct timeval & y)80 double timeval_subtract (struct timeval &x, struct timeval &y)
81 {
82 	double t1, t2;
83 	t1 = x.tv_sec + x.tv_usec * 1e-6;
84 	t2 = y.tv_sec + y.tv_usec * 1e-6;
85 	return t2 - t1;
86 }
87 
nano_display_io(string s,int style)88 void nano_display_io(string s, int style)
89 {
90 	if (s.empty()) return;
91 	REQ(&FTextBase::addstr, txt_nano_io, s, style);
92 	REQ(&FTextBase::addstr, txt_nano_CW_io, s, style);
93 }
94 
nano_serial_write(char c)95 int nano_serial_write(char c)
96 {
97 	if (!use_nanoIO) {
98 		return 1;
99 	}
100 	string buffer = " ";
101 	buffer[0] = c;
102 	REQ(sent, buffer);
103 	return nano_serial.WriteBuffer((unsigned char *)(buffer.c_str()), 1);
104 }
105 
nano_read_byte(int & msec)106 char nano_read_byte(int &msec)
107 {
108 	unsigned char c = 0;
109 	int ret;
110 	int numtries = 0;
111 	timeval start = tmval();
112 	timeval end = start;
113 	while (timeval_subtract(start, end) < msec) {
114 		ret = nano_serial.ReadByte(c);
115 		end = tmval();
116 		if (ret && c > 0) break;
117 		if (++numtries % 50 == 0) Fl::awake();
118 	}
119 	double dmsec = timeval_subtract(start, end) * 1000;
120 	msec = round(dmsec);
121 	static char szresp[50];
122 	snprintf(szresp, sizeof(szresp), "'%c' [%0.2f]", c, dmsec);
123 std::cout << szresp << std::endl;
124 
125 	REQ(rcvd, szresp);
126 
127 	return c;
128 }
129 
nano_read_string(int msec_wait,string find)130 string nano_read_string(int msec_wait, string find)
131 {
132 	string resp;
133 
134 	timeval start = tmval();
135 	int timer = msec_wait;
136 
137 	if (!find.empty()) {
138 		while (timer > 0 && (resp.find(find) == string::npos)) {
139 			resp.append(nano_serial_read());
140 			MilliSleep(10);
141 			timer -= 10;
142 		}
143 	} else {
144 		while (timer > 0) {
145 			resp.append(nano_serial_read());
146 			MilliSleep(10);
147 			timer -= 10;
148 		}
149 	}
150 	timeval end = tmval();
151 	double diff = timeval_subtract(start, end);
152 	int msec = round(diff * 1000);
153 
154 	static char szresp[1000];
155 	snprintf(szresp, sizeof(szresp), "[%d msec] %s", msec, resp.c_str());
156 
157 	REQ(rcvd, szresp);
158 	return resp;
159 }
160 
161 static bool calibrating = false;
162 
nano_send_cw_char(int c)163 void nano_send_cw_char(int c)
164 {
165 	if (c == GET_TX_CHAR_NODATA || c == 0x0d) {
166 		MilliSleep(50);
167 		return;
168 	}
169 
170 	int   msec = 0;
171 
172 	float tc = 1200.0 / progdefaults.CWspeed;
173 	float ta = 0.0;
174 	float tch = 3 * tc, twd = 4 * tc;
175 
176 	if (progdefaults.CWusefarnsworth && (progdefaults.CWspeed > progdefaults.CWfarnsworth)) {
177 		ta = 60000.0 / progdefaults.CWfarnsworth - 37200 / progdefaults.CWspeed;
178 		tch = 3 * ta / 19;
179 		twd = 4 * ta / 19;
180 	}
181 
182 	if (nano_morse == 0) {
183 		MilliSleep(50);
184 		return;
185 	}
186 	if (c == '^' || c == '|') {
187 		nano_serial_write(c);
188 		msec = 50;
189 		nano_read_byte(msec);
190 		MilliSleep(50);
191 		return;
192 	}
193 	int len = 4;
194 	if (c == 0x0a) c = ' ';
195 	if (c == ' ') {
196 		len = 4;
197 		msec = len * tc + 10;
198 		nano_serial_write(c);
199 		nano_read_byte(msec);
200 		if (!calibrating && progdefaults.CWusefarnsworth && (progdefaults.CWspeed > progdefaults.CWfarnsworth))
201 			MilliSleep(twd);
202 	} else {
203 		len = nano_morse->tx_length(c);
204 		if (len) {
205 		msec = len * tc + 10;
206 			nano_serial_write(c);
207 			nano_read_byte(msec);
208 			if (!calibrating && progdefaults.CWusefarnsworth && (progdefaults.CWspeed > progdefaults.CWfarnsworth))
209 				MilliSleep(tch);
210 		}
211 	}
212 	return;
213 }
214 
nano_send_tty_char(int c)215 void nano_send_tty_char(int c)
216 {
217 	if (c == GET_TX_CHAR_NODATA) {
218 		MilliSleep(50);
219 		return;
220 	}
221 	int msec = 165; // msec for start + 5 data + 1.5 stop bits @ 45.45
222 	if (progdefaults.nanoIO_baud == 50.0) msec = 150;
223 	if (progdefaults.nanoIO_baud == 75.0) msec = 100;
224 	if (progdefaults.nanoIO_baud == 100.0) msec = 75;
225 	nano_serial_write(c);
226 	msec += 20;
227 	c = nano_read_byte(msec);
228 }
229 
nano_send_char(int c)230 void nano_send_char(int c)
231 {
232 	if (nanoIO_isCW)
233 		nano_send_cw_char(c);
234 	else
235 		nano_send_tty_char(c);
236 }
237 
238 static int  setwpm = progdefaults.CWspeed;
239 
save_nano_state()240 void save_nano_state()
241 {
242 	if (!use_nanoIO) return;
243 	setwpm = progdefaults.CWspeed;
244 }
245 
restore_nano_state()246 void restore_nano_state()
247 {
248 	if (!use_nanoIO) return;
249 	progdefaults.CWspeed = setwpm;
250 	sldrCWxmtWPM->value(progdefaults.CWspeed);
251 	cntCW_WPM->value(progdefaults.CWspeed);
252 	calibrating = false;
253 LOG_INFO("%f WPM", progdefaults.CWspeed);
254 }
255 
nanoIO_wpm_cal()256 void nanoIO_wpm_cal()
257 {
258 	save_nano_state();
259 	progdefaults.CWusefarnsworth = false;
260 	progdefaults.CWspeed = cntrWPMtest->value();
261 LOG_INFO("%f WPM", progdefaults.CWspeed);
262 
263 	sldrCWxmtWPM->value(progdefaults.CWspeed);
264 	cntCW_WPM->value(progdefaults.CWspeed);
265 
266 	nanoIO_isCW = true;
267 
268 	set_nanoWPM(progdefaults.CWspeed);
269 
270 	string s;
271 	for (int i = 0; i < progdefaults.CWspeed; i++) s.append("paris ");
272 	s.append("^r");
273 
274 	TransmitText->add_text(s);
275 
276 	calibrating = true;
277 	start_tx();
278 
279 }
280 
nano_sendString(const string & s)281 void nano_sendString (const string &s)
282 {
283 	REQ(sent, s);
284 
285 	nano_serial.WriteBuffer((unsigned char *)(s.c_str()), s.length());
286 
287 	return;
288 }
289 
290 
291 static timeval ptt_start;
292 static double  wpm_err = 0;
293 
dispWPMtiming(void *)294 void dispWPMtiming(void *)
295 {
296 	corr_var_wpm->value(wpm_err + 60);
297 	int nucorr = progdefaults.usec_correc;
298 	nucorr += wpm_err * 1e6 / (cntrWPMtest->value() * 50);
299 	usec_correc->value(nucorr);
300 }
301 
302 
303 static bool nanoIO_busy = false;
304 
nano_PTT(int val)305 void nano_PTT(int val)
306 {
307 	if (use_nanoIO) {
308 		nano_serial_write(val ? '[' : ']');
309 		int msec = 100;
310 		nano_read_byte(msec);
311 	}
312 
313 	if (val) {
314 		ptt_start = tmval();
315 		nanoIO_busy = true;
316 	} else {
317 		timeval ptt_end = tmval();
318 		double tdiff = timeval_subtract(ptt_start, ptt_end);
319 //std::cout << "PTT: [ " << tdiff << " ]" << std::endl;
320 		if (calibrating) {
321 			wpm_err = tdiff - 60;
322 			Fl::awake(dispWPMtiming);
323 			restore_nano_state();
324 		} else {
325 			ptt_start.tv_sec = 0;
326 			ptt_start.tv_usec = 0;
327 			nanoIO_busy = false;
328 		}
329 	}
330 
331 }
332 
nano_cancel_transmit()333 void nano_cancel_transmit()
334 {
335 	nano_serial_write('\\');
336 }
337 
nano_mark_polarity(int v)338 void nano_mark_polarity(int v)
339 {
340 	string cmd = "~";
341 	cmd.append(v == 0 ? "1" : "0");
342 	string resp;
343 	nano_sendString(cmd);
344 	resp = nano_serial_read();
345 	nano_display_io(resp, FTextBase::ALTR);
346 }
347 
nano_MARK_is(int val)348 void nano_MARK_is(int val)
349 {
350 	progdefaults.nanoIO_polarity = val;
351 	chk_nanoIO_polarity->value(val);
352 }
353 
nano_set_baud(int bd)354 void nano_set_baud(int bd)
355 {
356 	string resp;
357 	string cmd = "~";
358 	cmd.append(bd == 3 ? "9" : bd == 2 ? "7" : bd == 1 ? "5" : "4");
359 	nano_sendString(cmd);
360 	resp = nano_serial_read();
361 	nano_display_io(resp, FTextBase::ALTR);
362 }
363 
nano_baud_is(int val)364 void nano_baud_is(int val)
365 {
366 	int index = 2;
367 	if (val == 45) index = 0;
368 	if (val == 50) index = 1;
369 	if (val == 100) index = 2;
370 	progdefaults.nanoIO_baud = index;
371 	sel_nanoIO_baud->index(index);
372 }
373 
374 static int pot_min, pot_rng;
375 static bool nanoIO_has_pot = false;
376 
init_pot_min_max()377 void init_pot_min_max()
378 {
379 	nanoIO_has_pot = true;
380 
381 	btn_nanoIO_pot->activate();
382 	nanoIO_use_pot();
383 
384 	cntr_nanoIO_min_wpm->activate();
385 	cntr_nanoIO_rng_wpm->activate();
386 
387 	cntr_nanoIO_min_wpm->value(pot_min);
388 	cntr_nanoIO_rng_wpm->value(pot_rng);
389 	cntr_nanoIO_min_wpm->redraw();
390 	cntr_nanoIO_rng_wpm->redraw();
391 }
392 
disable_min_max()393 void disable_min_max()
394 {
395 	btn_nanoIO_pot->deactivate();
396 	cntr_nanoIO_min_wpm->deactivate();
397 	cntr_nanoIO_rng_wpm->deactivate();
398 }
399 
400 // this function must be called from within the main UI thread
401 // use REQ(nano_parse_config, s);
402 
nano_parse_config(string s)403 void nano_parse_config(string s)
404 {
405 	nano_display_io(s, FTextBase::ALTR);
406 
407 	size_t p1 = 0;
408 	if (s.find("nanoIO") == string::npos) return;
409 
410 	if (s.find("HIGH") != string::npos)  nano_MARK_is(1);
411 	if (s.find("LOW") != string::npos)   nano_MARK_is(0);
412 	if (s.find("45.45") != string::npos) nano_baud_is(45);
413 	if (s.find("50.0") != string::npos)  nano_baud_is(50);
414 	if (s.find("75.0") != string::npos)  nano_baud_is(75);
415 	if (s.find("100.0") != string::npos) nano_baud_is(100);
416 	if ((p1 = s.find("WPM")) != string::npos) {
417 		p1 += 4;
418 		int wpm = progdefaults.CWspeed;
419 		if (sscanf(s.substr(p1).c_str(), "%d", &wpm)) {
420 			progdefaults.CWspeed = wpm;
421 LOG_INFO("%f WPM", progdefaults.CWspeed);
422 			cntCW_WPM->value(wpm);
423 			cntr_nanoCW_WPM->value(wpm);
424 			sldrCWxmtWPM->value(wpm);
425 		}
426 	}
427 	if ((p1 = s.find("/", p1)) != string::npos) {
428 		p1++;
429 		int wpm = progdefaults.CW_keyspeed;
430 		if (sscanf(s.substr(p1).c_str(), "%d", &wpm)) {
431 			progdefaults.CW_keyspeed = wpm;
432 			cntr_nanoCW_paddle_WPM->value(wpm);
433 		}
434 	} else { // ver 1.1.x
435 		if ((p1 = s.find("WPM", p1 + 4)) != string::npos) {
436 			p1++;
437 			int wpm = progdefaults.CW_keyspeed;
438 			if (sscanf(s.substr(p1).c_str(), "%d", &wpm)) {
439 				progdefaults.CW_keyspeed = wpm;
440 				cntr_nanoCW_paddle_WPM->value(wpm);
441 			}
442 		}
443 	}
444 	if ((p1 = s.find("dash/dot ")) != string::npos) {
445 		p1 += 9;
446 		float val = progdefaults.CWdash2dot;
447 		if (sscanf(s.substr(p1).c_str(), "%f", &val)) {
448 			progdefaults.CWdash2dot = val;
449 			cntCWdash2dot->value(val);
450 			cnt_nanoCWdash2dot->value(val);
451 		}
452 	}
453 	if ((p1 = s.find("PTT")) != string::npos) {
454 		if (s.find("NO", p1 + 4) != string::npos)
455 			progdefaults.disable_CW_PTT = true;
456 		else
457 			progdefaults.disable_CW_PTT = false;
458 	} else
459 		progdefaults.disable_CW_PTT = false;
460 	nanoIO_set_cw_ptt();
461 
462 	if ((p1 = s.find("Speed Pot")) != string::npos) {
463 		size_t p2 = s.find("ON", p1);
464 		int OK = 0;
465 		p2 = s.find("minimum", p1);
466 		if (p2 != string::npos)
467 			OK = sscanf(&s[p2 + 8], "%d", &pot_min);
468 		p2 = s.find("range", p1);
469 		if (p2 != string::npos)
470 			OK = sscanf(&s[p2 + 6], "%d", &pot_rng);
471 		if (OK)
472 			init_pot_min_max();
473 	} else
474 		disable_min_max();
475 
476 	if ((p1 = s.find("corr usec")) != string::npos) {
477 		int corr;
478 		if (sscanf(s.substr(p1+9).c_str(), "%d", &corr)) {
479 			progdefaults.usec_correc = corr;
480 			usec_correc->value(corr);
481 		}
482 	}
483 	return;
484 }
485 
open_port(string PORT)486 int open_port(string PORT)
487 {
488 	if (PORT.empty()) return false;
489 
490 	REQ(sent, "reset");
491 	nano_serial.Device(PORT);
492 	switch (progdefaults.nanoIO_serbaud) {
493 		case 0: nano_serial.Baud(1200); break;
494 		case 1: nano_serial.Baud(4800); break;
495 		case 2: nano_serial.Baud(9600); break;
496 		case 3: nano_serial.Baud(19200); break;
497 		case 4: nano_serial.Baud(38400); break;
498 		case 5: nano_serial.Baud(57600); break;
499 		case 6: nano_serial.Baud(115200); break;
500 		default: nano_serial.Baud(57600);
501 	}
502 	nano_serial.Timeout(10);
503 	nano_serial.Retries(5);
504 	nano_serial.Stopbits(1);
505 
506 	use_nanoIO = false;
507 
508 	if (!nano_serial.OpenPort()) {
509 		nano_display_io("\nCould not open serial port!", FTextBase::ALTR);
510 		LOG_ERROR("Could not open %s", progdefaults.nanoIO_serial_port_name.c_str());
511 		return false;
512 	}
513 
514 	use_nanoIO = true;
515 
516 	nano_read_string(1500, "cmd:");
517 
518 	nano_display_io("Connected to nanoIO\n", FTextBase::RECV);
519 
520 	return true;
521 }
522 
nano_serial_read()523 string nano_serial_read()
524 {
525 	static char buffer[4096];
526 
527 	memset(buffer, '\0',4096);
528 
529 	int rb = nano_serial.ReadBuffer((unsigned char *)buffer, 4095);
530 	if (rb)
531 		return buffer;
532 	return "";
533 }
534 
nano_serial_flush()535 void nano_serial_flush()
536 {
537 	static char buffer[1025];
538 	memset(buffer, 0, 1025);
539 	while (nano_serial.ReadBuffer((unsigned char *)buffer, 1024) ) ;
540 }
541 
no_cmd(void *)542 void no_cmd(void *)
543 {
544 	nano_display_io("Could not read current configuration\n", FTextBase::ALTR);
545 }
546 
close_nanoIO()547 void close_nanoIO()
548 {
549 	nano_serial.ClosePort();
550 	use_nanoIO = false;
551 
552 	nano_display_io("Disconnected from nanoIO\n", FTextBase::RECV);
553 
554 	if (nano_morse) {
555 		delete nano_morse;
556 		nano_morse = 0;
557 	}
558 
559 	progStatus.nanoCW_online = false;
560 	progStatus.nanoFSK_online = false;
561 	nanoIO_isCW = false;
562 
563 	enable_rtty_quickchange();
564 
565 }
566 
open_nano()567 bool open_nano()
568 {
569 	if (use_nanoIO)
570 		return true;
571 	if (!open_port(progdefaults.nanoIO_serial_port_name))
572 		return false;
573 
574 	return true;
575 }
576 
open_nanoIO()577 bool open_nanoIO()
578 {
579 	string rsp;
580 
581 	progStatus.nanoCW_online = false;
582 	progStatus.nanoFSK_online = false;
583 
584 	if (open_nano()) {
585 
586 		set_nanoIO();
587 
588 		nano_sendString("~?");
589 		rsp = nano_read_string(1000, "PTT");
590 
591 		size_t p = rsp.find("~?");
592 		if (p == string::npos) return false;
593 		rsp.erase(0, p + 3);
594 
595 		if (rsp.find("eyer") != string::npos)
596 			REQ(nano_parse_config, rsp);
597 
598 		progStatus.nanoFSK_online = true;
599 		nanoIO_isCW = false;
600 
601 		disable_rtty_quickchange();
602 
603 		return true;
604 	}
605 	return false;
606 }
607 
open_nanoCW()608 bool open_nanoCW()
609 {
610 	if (nano_morse == 0) nano_morse = new cMorse;
611 
612 	progStatus.nanoCW_online = false;
613 	progStatus.nanoFSK_online = false;
614 
615 	string rsp;
616 	if (open_nano()) {
617 
618 		set_nanoCW();
619 
620 		nano_sendString("~?");
621 		rsp = nano_read_string(1000, "PTT");
622 
623 		size_t p = rsp.find("~?");
624 		if (p == string::npos) return false;
625 		rsp.erase(0, p + 3);
626 
627 		if (rsp.find("eyer") != string::npos)
628 			REQ(nano_parse_config, rsp);
629 
630 		progStatus.nanoCW_online = true;
631 
632 		return true;
633 	}
634 	return false;
635 }
636 
set_nanoIO()637 void set_nanoIO()
638 {
639 	if (progStatus.nanoFSK_online) return;
640 
641 	string cmd = "~F";
642 	string rsp;
643 	int count = 5;
644 	while (rsp.empty() && count--) {
645 		nano_sendString(cmd);
646 		rsp = nano_read_string(165*cmd.length(), cmd);
647 	}
648 	progStatus.nanoFSK_online = true;
649 	progStatus.nanoCW_online = false;
650 	nanoIO_isCW = false;
651 }
652 
set_nanoCW()653 void set_nanoCW()
654 {
655 	string cmd = "~C";
656 	string rsp;
657 	int count = 5;
658 
659 	while (rsp.empty() && count--) {
660 		nano_sendString(cmd);
661 		rsp = nano_read_string(165*cmd.length(), cmd);
662 	}
663 
664 	if (rsp.find(cmd) != string::npos) {
665 		nanoIO_isCW = true;
666 		set_nanoWPM(progdefaults.CWspeed);
667 		set_nano_dash2dot(progdefaults.CWdash2dot);
668 	}
669 
670 	setwpm = progdefaults.CWspeed;
671 
672 	progStatus.nanoCW_online = true;
673 	progStatus.nanoFSK_online = false;
674 	nanoIO_isCW = true;
675 }
676 
set_nanoWPM(int wpm)677 void set_nanoWPM(int wpm)
678 {
679 	static char szwpm[10];
680 	if (wpm > 60 || wpm < 5) return;
681 	snprintf(szwpm, sizeof(szwpm), "~S%ds", wpm);
682 	nano_sendString(szwpm);
683 	nano_read_string(165*strlen(szwpm), szwpm);
684 }
685 
nanoIO_correction()686 void nanoIO_correction()
687 {
688 	progdefaults.usec_correc = usec_correc->value();
689 	static char szcorr[15];
690 	snprintf(szcorr, sizeof(szcorr), "~E%de", progdefaults.usec_correc);
691 	nano_sendString(szcorr);
692 	nano_read_string(165*strlen(szcorr), szcorr);
693 }
694 
set_nano_keyerWPM(int wpm)695 void set_nano_keyerWPM(int wpm)
696 {
697 	static char szwpm[10];
698 	if (wpm > 60 || wpm < 5) return;
699 	snprintf(szwpm, sizeof(szwpm), "~U%du", wpm);
700 	nano_sendString(szwpm);
701 	nano_read_string(165*strlen(szwpm), szwpm);
702 }
703 
set_nano_dash2dot(float wt)704 void set_nano_dash2dot(float wt)
705 {
706 	static char szd2d[10];
707 	if (wt < 2.5 || wt > 3.5) return;
708 	snprintf(szd2d, sizeof(szd2d), "~D%3dd", (int)(wt * 100) );
709 	nano_sendString(szd2d);
710 	nano_read_string(165*strlen(szd2d), szd2d);
711 }
712 
nano_CW_query()713 void nano_CW_query()
714 {
715 	nano_serial_flush();
716 	nano_sendString("~?");
717 	string resp = nano_read_string(165*3, "PTT");
718 
719 	nano_display_io(resp, FTextBase::ALTR);
720 	REQ(nano_parse_config, resp);
721 }
722 
nano_help()723 void nano_help()
724 {
725 	nano_serial_flush();
726 	nano_sendString("~~");
727 	string resp = nano_read_string(1000, "cmds");
728 	nano_display_io(resp, FTextBase::ALTR);
729 }
730 
nano_CW_save()731 void nano_CW_save()
732 {
733 	nano_sendString("~W");
734 	nano_read_string(165*2, "~W");
735 }
736 
nanoCW_tune(int val)737 void nanoCW_tune(int val)
738 {
739 	if (val) {
740 		nano_sendString("~T");
741 		nano_read_string(165*2, "~T");
742 	} else {
743 		nano_sendString("]");
744 		nano_read_string(165, "]");
745 	}
746 }
747 
set_nanoIO_incr()748 void set_nanoIO_incr()
749 {
750 	string s_incr = "~I";
751 	s_incr += progdefaults.nanoIO_CW_incr;
752 	nano_sendString(s_incr);
753 	nano_read_string(165*2, s_incr);
754 }
755 
set_nanoIO_keyer(int indx)756 void set_nanoIO_keyer(int indx)
757 {
758 	string s;
759 	if (indx == 0) s = "~A";
760 	if (indx == 1) s = "~B";
761 	if (indx == 2) s = "~K";
762 	nano_sendString(s);
763 	nano_read_string(165*s.length(), s);
764 }
765 
nanoIO_set_cw_ptt()766 void nanoIO_set_cw_ptt()
767 {
768 	string s = "~X";
769 	s += progdefaults.disable_CW_PTT ? '0' : '1';
770 	nano_sendString(s);
771 	nano_read_string(165*s.length(), s);
772 }
773 
nanoIO_read_pot()774 void nanoIO_read_pot()
775 {
776 	if (!use_nanoIO) return;
777 	if (!nanoIO_has_pot) return;
778 	if (!progdefaults.nanoIO_speed_pot) return;
779 	if (nanoIO_busy) return;
780 
781 // reread the current pot setting
782 	static char szval[10];
783 	string rsp;
784 	snprintf(szval, sizeof(szval), "~P?");
785 	nano_sendString(szval);
786 	rsp = nano_read_string(165*strlen(szval), szval);
787 	int val = 0;
788 	size_t p = rsp.find("wpm");
789 	if (p != string::npos) {
790 		rsp.erase(0,p);
791 		if (sscanf(rsp.c_str(), "wpm %d", &val) == 1) {
792 			REQ(set_paddle_WPM, val);
793 		}
794 	}
795 }
796 
nanoIO_use_pot()797 void nanoIO_use_pot()
798 {
799 	string s = "~P";
800 	if (progdefaults.nanoIO_speed_pot) s += '1';
801 	else s += '0';
802 	nano_sendString(s);
803 	nano_read_string(165*s.length(), s);
804 	nanoIO_read_pot();
805 }
806 
set_paddle_WPM(int wpm)807 void set_paddle_WPM (int wpm)
808 {
809 	cntr_nanoCW_paddle_WPM->value(wpm);
810 	cntr_nanoCW_paddle_WPM->redraw();
811 }
812 
set_nanoIO_min_max()813 void set_nanoIO_min_max()
814 {
815 	static char szval[10];
816 // set min value for potentiometer
817 	snprintf(szval, sizeof(szval), "~M%dm", (int)cntr_nanoIO_min_wpm->value());
818 	nano_sendString(szval);
819 	nano_read_string(165*strlen(szval), szval);
820 
821 // set range value of potentiometer
822 	snprintf(szval, sizeof(szval), "~N%dn", (int)cntr_nanoIO_rng_wpm->value());
823 	nano_sendString(szval);
824 	nano_read_string(165*strlen(szval), szval);
825 
826 	nanoIO_read_pot();
827 }
828 
829