Files
CCU4-firmware/dipdisplay.ino
2023-09-11 18:04:36 +02:00

883 lines
22 KiB
C++

//#include "utils.h"
#define DISPDEBUG 0
enum {disp_max_rows = 12, disp_max_buttons = 14};
enum {button_std, button_clr, button_cmd};
struct Disp_Row {
byte fmt; // the row format (see dipdisplay.h, fmt_...)
boolean line; // line to be drawn
char text[18]; // text
char head[18]; // head
byte dirty; // 0: unchanged, 1: text changed, 2: format changed
};
struct Disp_Button {
byte menu; // the menu of the command (0 means no command)
byte cmd; // the command
char text[10]; // the button label
byte chk; // validity code for button commands (0x10 ... 0xF0, increased when button changes)
byte touchCode; // the installed touch code
boolean dirty; // button was changed
boolean used; // button is used
};
struct {
void (*handler)();
ulong lastAct; // time of last action
boolean bright;
boolean wakemeup;
byte actMenu; // actual screen
byte textPos;
byte rowPos;
boolean commdebug;
byte areaCmd; // cmd when screen outside buttons touched
byte areaMenu; // menu for above
byte page;
byte butLim;
boolean clrall;
boolean oldline;
int8_t maxbut;
int8_t maxbut0; // number of buttons used
struct Disp_Row rows[disp_max_rows];
struct Disp_Button buttons[disp_max_buttons];
} disp = {0};
// --- functions for drawing to buffer
void DispBegin(byte menu) {
int8_t nr;
struct Disp_Button *button;
disp.rowPos = 0;
disp.areaMenu = m_none;
disp.areaCmd = 0;
disp.maxbut = -1;
for (nr = 0; nr < disp_max_buttons; nr++) {
ARRAY(disp.buttons, nr).used = false;
}
if (disp.actMenu != menu || disp.clrall) {
disp.actMenu = menu;
disp.clrall = true;
for (nr = 0; nr < disp_max_buttons; nr++) {
button = &ARRAY(disp.buttons, nr);
button->menu = 0;
button->cmd = 0;
button->dirty = false;
}
}
}
void DispClrAll() {
disp.clrall = true;
}
void DispButton(int8_t nr, byte menu, byte cmd, const char *text) {
struct Disp_Button *button = &ARRAY(disp.buttons, nr);
char old[18];
if (menu != button->menu) {
if (DISPDEBUG) {
Serial.print("D mnu>"); Serial.print(button->menu); Serial.print('>'); Serial.print(menu); Serial.print(' ');
}
button->dirty = true;
button->menu = menu;
}
if (cmd != button->cmd) {
if (DISPDEBUG) {
Serial.print("D cmd>"); Serial.print(button->cmd); Serial.print('>'); Serial.print(cmd); Serial.print(' ');
}
button->cmd = cmd;
button->dirty = true;
}
if (DISPDEBUG) textcopy(old, sizeof old, button->text);
if (!textcmpcopy(button->text, sizeof button->text, text)) {
if (DISPDEBUG) {
Serial.print("D txt>"); Serial.print(old); Serial.print('>'); Serial.print(text); Serial.print(F("> "));
}
button->dirty = true;
}
if (DISPDEBUG) {
if (button->dirty) {
Serial.print(nr); Serial.println(F(". button"));
}
}
if (nr > disp.maxbut) disp.maxbut = nr;
button->used = true;
}
void DispLine() {
if (disp.rowPos > 0) {
struct Disp_Row *row = &ARRAY(disp.rows, disp.rowPos - 1);
row->line = true;
} else {
Serial.println(F("no line at top"));
}
}
void DispTextRow(byte fmt, const char *head, const char *text) {
struct Disp_Row *row;
byte l;
char old[18];
if (disp.rowPos > 0) {
row = &ARRAY(disp.rows, disp.rowPos - 1); // last row, if available
if (row->line != disp.oldline) { // line status of last row has changed
row->dirty = 2;
}
}
row = &ARRAY(disp.rows, disp.rowPos);
disp.oldline = row->line;
row->line = false;
if (fmt != row->fmt) {
if (DISPDEBUG) {
Serial.print("D fmt>"); Serial.print(row->fmt); Serial.print('>'); Serial.print(fmt); Serial.print(F("> "));
}
row->dirty = 2;
row->fmt = fmt;
}
if (DISPDEBUG) textcopy(old, sizeof old, row->text);
if (!textcmpcopy(row->text, sizeof row->text, text)) {
if (DISPDEBUG) {
Serial.print("D tex>"); Serial.print(old); Serial.print('>'); Serial.print(text); Serial.print(F("> "));
}
if (row->dirty == 0) row->dirty = 1;
}
if (head == 0) head = "";
if (DISPDEBUG) textcopy(old, sizeof old, row->head);
if (!textcmpcopy(row->head, sizeof row->head, head)) {
if (DISPDEBUG) {
Serial.print("D hd>"); Serial.print(old); Serial.print('>'); Serial.print(head); Serial.print(F("> "));
}
if (row->dirty == 0) row->dirty = 1;
}
if (DISPDEBUG) {
if (row->dirty) {
Serial.print(disp.rowPos); Serial.println(F(". row"));
}
}
disp.rowPos++;
}
void DispText(const char *text) {
DispTextRow(fmt_lsmall, 0, text);
}
void DispText2(const char *head, const char *text) {
DispTextRow(fmt_rsmall, head, text);
}
void DispState(const char *head, byte nr) {
DispTextRow(fmt_lsmall, head, ParFmt(nr));
}
void DispValueArg(byte fmt, const char *head, byte nr, byte stateNr) {
byte state, maxlen;
//if (!disp.freeze) {
if (stateNr) {
state = ParGet(stateNr);
} else {
state = 0;
}
if (state > 0) {
DispTextRow(fmt, head, ParFmt(stateNr));
} else {
if (fmt == fmt_big) {
head = 0;
maxlen = 4;
} else if (fmt == fmt_large) {
maxlen = 4;
} else {
maxlen = 0; // no max length
}
DispTextRow(fmt, head, ParFmt(nr, maxlen));
}
}
void DispBigValue(byte nr, byte stateNr) {
DispValueArg(fmt_big, 0, nr, stateNr);
}
void DispMediumValue(const char *head, byte nr, byte stateNr) {
DispValueArg(fmt_medium, head, nr, stateNr);
}
void DispLargeValue(const char *head, byte nr, byte stateNr) {
DispValueArg(fmt_large, head, nr, stateNr);
}
void DispValue(const char *head, byte nr, byte stateNr) {
DispValueArg(fmt_rsmall, head, nr, stateNr);
}
// --- real drawing
void DispSend(const char *data) {
if (data) SerialD.print(data);
SerialD.write((byte)0);
}
byte DispInit(void (*handler)(), byte homescreen) {
long start;
disp.commdebug = false;
disp.handler = handler;
SerialD.commdebug = disp.commdebug;
// should we accept that there might be no display? - this would end in an infinite loop:
start = millis();
do {
DispPutStr("#DO1,"); // turn Display
if (millis() - start > 3000) { // no display
return 0;
}
} while (SerialD.send() < 0); // check until success as the display needs time to get running
//delay(1000);
DispPutStr("#TA,#DL,#YZ1,#QZ3,#YH100,#AL0,0,#AH0,0,63,127,"); // disable terminal and clear Display, dimmer fast, blink 3 ms, full light, clear buttons, actiavte general button area
DispPutStr("#ZV1,"); // drawing mode 'set'
//DispSend("\033NT\002"); // touch menu mode
while (SerialD.read() >= 0); // clear old requests
disp.wakemeup = false;
disp.bright = true;
disp.actMenu = homescreen;
disp.lastAct = start;
disp.maxbut0 = -1;
if (DISPDEBUG) {
Serial.println(F("D init"));
}
DispClrAll();
return 1;
}
void DispPut(byte chr) {
SerialD.write(chr);
}
void DispPutStr(const char *str) {
SerialD.print(str);
}
void DispPutData(byte *data, byte len) {
for (; len>0; data++,len--) {
SerialD.write(*data);
}
}
int DispCharWidth(const char ch) {
// character width for Font 4 (GENEVA10)
// the table contains the width including 1 pixel for the gap
static byte len[96] = {6,3,5,7,6,9,8,3,4,4,7,6,4,5,3,5,
6,6,6,6,6,6,6,6,6,6,4,4,5,6,5,6,
8,7,6,6,6,5,5,6,6,3,6,6,5,8,6,6,
6,6,6,6,6,6,6,8,6,6,5,4,6,4,4,6,
3,5,5,5,5,5,4,5,5,3,4,5,3,8,5,5,
5,5,5,5,4,5,6,8,6,6,5,4,2,4,6,7};
if ((signed char)ch < 32) { // >= 128 is negative
return 6;
}
return len[ch-32];
}
int DispTextWidth(const char *text) {
// caclulate text with for Font 4 (GENEVA10)
int width = 0;
char ch;
while ((ch = *text) != 0) {
width += DispCharWidth(ch);
text++;
}
return width;
}
int DispRowHeight(byte fmt) {
switch (fmt) {
case fmt_lsmall:
case fmt_rsmall: return 11;
case fmt_medium: return 21;
case fmt_large: return 26;
case fmt_big: return 27;
}
return 0;
}
void DispDrawRow(struct Disp_Row *row, boolean doclear) {
byte fmt = row->fmt;
char *text = row->text;
char *head = row->head;
byte xpos, align, x;
byte zoom = 0;
boolean skip = false;
int len, maxlen;
byte ytop, ybot;
const char *afterHead = 0;
disp.handler();
ytop = disp.textPos;
ybot = ytop + DispRowHeight(fmt) - 1;
if (ybot > disp.butLim) {
if (DISPDEBUG) {
Serial.print(F("D ybot overlap ")); Serial.println(ybot - disp.butLim);
}
if (ytop > disp.butLim) {
if (DISPDEBUG) Serial.println(F("skip"));
skip = true;
ytop = disp.butLim;
}
}
if (doclear) {
// clear text area
DispPutStr("\033RL"); DispPut(0); DispPut(ytop ? ytop-1 : 0); DispPut(63); DispPut(ybot - 1); // clear rect
}
if (skip) return;
len = DispTextWidth(text); // text length in pixels
maxlen = 63;
switch (fmt) {
case fmt_none:
goto drawline;
case fmt_lsmall:
DispPutStr("#ZF4,"); align='L'; xpos = 0;
break;
case fmt_rsmall:
//sameline = true;
DispPutStr("#ZF4,"); align='R'; xpos = 63;
break;
case fmt_medium:
if (head && *head) {
if (len + DispTextWidth(head) / 2 >= 32) {
afterHead = "#ZF4,#ZZ1,2,"; zoom = 1;
} else {
afterHead = "#ZF4,#ZZ2,2,"; zoom = 2;
}
DispPutStr("#ZF4,");
} else if (len >= 32) {
DispPutStr("#ZF4,#ZZ1,2,"); zoom = 1;
} else {
DispPutStr("#ZF4,#ZZ2,2,"); zoom = 2;
}
align='R'; xpos = 63;
break;
case fmt_large:
DispPutStr("#ZF4,");
if (len >= 24) {
afterHead = "#ZF4,#ZZ1,2,"; zoom = 1;
} else {
afterHead = "#ZF6,";
}
align='R'; xpos = 63;
break;
case fmt_big:
head = 0;
if (len >= 24) {
DispPutStr("#ZF4,#ZZ1,2,"); zoom = 1;
} else {
DispPutStr("#ZF6,");
}
align='R'; xpos = 63;
break;
}
if (head && *head) {
if (DISPDEBUG) {
SerialD.send();
Serial.print(F("D head:"));
Serial.println(head);
}
DispPutStr("\033Z");
if (align == 'R') {
DispPut('L'); DispPut(0); DispPut(ytop);
DispSend(head);
maxlen -= DispTextWidth(head) + 1;
if (afterHead) DispPutStr(afterHead); // change format for value
DispPutStr("\033Z"); DispPut(align); DispPut(xpos); DispPut(ytop);
} else {
DispPut(align); DispPut(xpos); DispPut(ytop);
DispPutStr(head);
maxlen -= DispTextWidth(head);
}
} else {
// if (fmt == fmt_large && len < 24) DispPutStr("#ZF6,"); // use Big for number
if (afterHead) DispPutStr(afterHead); // change format for value
DispPutStr("\033Z"); DispPut(align); DispPut(xpos); DispPut(ytop);
}
if (DISPDEBUG) {
SerialD.send();
Serial.print(F("D text:"));
Serial.println(text);
}
if (len > maxlen) {
len = DispCharWidth(*text);
for (; len <= maxlen && *text != 0; text++) {
DispPut(*text);
len += DispCharWidth(*text);
}
DispSend("");
} else {
if (text[0] == 0) {
DispSend(" ");
} else {
DispSend(text);
}
}
if (zoom) {
DispSend("#ZZ1,1,");
}
drawline:
if (row->line) {
if (DISPDEBUG) {
Serial.println(F("D line"));
}
//if (ybot > 0) ybot -= 2;
if (ybot < disp.butLim) {
DispPutStr("\033GD"); DispPut(0); DispPut(ybot); DispPut(63); DispPut(ybot); // draw line
}
}
SerialD.send();
}
byte DispButtonYpos(int8_t nr) {
return 127 - 16 - (nr / 2) * 18;
}
void DispPutButtonCoord(int8_t nr, byte mode) {
byte y1 = DispButtonYpos(nr);
byte y2 = y1 + 16;
if (mode != button_std) {
if (mode == button_cmd) {
if (y1 > 0) y1--;
}
if (y1 > 0) y1--;
}
if (nr % 2) {
DispPut(0);
DispPut(y1);
if (mode == button_clr) {
DispPut(32);
} else {
DispPut(30);
}
} else {
if (mode == button_clr) {
DispPut(31);
} else {
DispPut(33);
}
DispPut(y1);
DispPut(63);
}
DispPut(y2);
}
void DispTouchCmd(byte menu, byte cmd) {
disp.areaMenu = menu;
disp.areaCmd = cmd;
}
byte DispPage() {
return disp.page;
}
byte DispNextPage(byte pagelimit) {
disp.page++;
if (disp.page >= pagelimit) disp.page = 0;
}
byte DispGotoPage(byte page) {
disp.page = page;
}
void DispClrButton(int8_t nr, byte oldCode) {
if (oldCode) {
if (DISPDEBUG) {
Serial.print(F("D clrbut ")); Serial.println(oldCode, HEX);
}
DispPutStr("\033AL"); DispPut(oldCode); DispPut(1);
}
}
void DispClrButtonArea(int8_t nr) {
if (DISPDEBUG) {
Serial.print(F("D clrbar ")); Serial.println(nr, DEC);
}
DispPutStr("\033RL"); DispPutButtonCoord(nr, button_clr);
}
void DispDrawButton(int8_t nr, byte touchCode, char *text) {
byte mode;
disp.handler();
mode = button_std;
if (text[0] == '*') { // top button for command screen
DispPutStr("#AE1,"); // frame thin
text++;
} else if (text[0] == '-') { // appended button for command screen
DispPutStr("#AE1,"); // frame thin
text++;
mode = button_cmd;
} else if (text[0] == ':') { // no frame button
DispPutStr("#AE0,"); // no frame
text++;
} else { // normal button
DispPutStr("#AE1,"); // frame thin
}
DispPutStr("\033RL"); DispPutButtonCoord(nr, button_clr); // clear rect
// AF4 button font Geneva10
if (DISPDEBUG) {
Serial.print(F("D but "));
Serial.print(nr, DEC);
Serial.print(F(" code ")); Serial.print(touchCode, HEX);
Serial.print(" w="); Serial.print(DispTextWidth(text)); Serial.print(" ");
Serial.println(text);
}
if (DispTextWidth(text) < 30) {
DispPutStr("#AF4,");
} else {
DispPutStr("#AF1,");
}
// AT create button
DispPutStr("\033AT"); DispPutButtonCoord(nr, mode);
DispPut(touchCode); DispPut(0); DispPut('C'); DispSend(text);
SerialD.send();
}
void DispClr(byte from, byte to) {
DispPutStr("\033RL"); DispPut(0); DispPut(from); DispPut(63); DispPut(to); // clear rect
if (DISPDEBUG) {
Serial.print(F("D clr ")); Serial.print(from, DEC);
Serial.print(F(" to ")); Serial.println(to, DEC);
}
}
void DispNextChk(struct Disp_Button *button) {
byte chk = button->chk;
if (chk >= 0xf0) {
button->chk = 0x10;
} else {
button->chk = (chk & 0xf0) + 0x10;
}
}
void DispEnd() {
int8_t nr, nr1;
int8_t l;
struct Disp_Row *row;
struct Disp_Button *button;
boolean redraw;
if (disp.clrall) {
for (nr = 0; nr < disp_max_buttons; nr++) {
button = &ARRAY(disp.buttons, nr);
if (button->menu != m_none) {
DispNextChk(button);
}
button->touchCode = 0;
}
disp.maxbut0 = 0;
for (nr = 0; nr < disp_max_rows; nr++) {
row = &ARRAY(disp.rows, nr);
if (row->fmt) {
row->dirty = 1; // redraw without clear
}
}
if (DISPDEBUG) {
Serial.println(F("D clr"));
}
DispSend("#AL0,0,#DL,#AH0,0,63,127,"); // clear all touch buttons, clear screen, general touch area
SerialD.send();
disp.clrall = false;
}
if ((disp.maxbut % 2) == 0) disp.maxbut++; // must always be odd to be sure left button area is cleared
for (nr = 0; nr < disp_max_buttons; nr++) {
button = &ARRAY(disp.buttons, nr);
if (button->menu != m_none && !button->used) { // removed button
button->dirty = true;
button->menu = m_none;
button->cmd = 0;
}
if (button->dirty) { // dirty
if (DISPDEBUG) {
Serial.print(F("D ")); Serial.print(nr);
Serial.print(" cmd "); Serial.print(button->menu); Serial.print('/'); Serial.print(button->cmd);
if (button->used) Serial.print(F(" used"));
if (button->dirty) Serial.print(F(" dirty"));
Serial.print(F(" oldcod ")); Serial.println(button->touchCode, HEX);
}
if (button->touchCode) {
DispClrButton(nr, button->touchCode);
button->touchCode = 0;
}
DispNextChk(button);
if (button->menu && button->used) {
button->touchCode = button->chk | nr;
DispDrawButton(nr, button->touchCode, button->text);
}
if (DISPDEBUG) {
Serial.print(F("D newcod ")); Serial.println(button->touchCode, HEX);
}
button->dirty = false;
} else if (nr > disp.maxbut0 && nr <= disp.maxbut) {
// clear intermediate unused button spaces
DispClrButtonArea(nr);
}
}
if (DISPDEBUG) {
if (disp.maxbut0 != disp.maxbut) {
Serial.print(F("D max ")); Serial.print(disp.maxbut0); Serial.print('>'); Serial.println(disp.maxbut);
}
}
disp.maxbut0 = disp.maxbut;
disp.butLim = 128 - (disp.maxbut + 1) / 2 * 18;
disp.textPos = 0;
redraw = false;
for (nr = 0; nr < disp.rowPos; nr++) {
row = & ARRAY(disp.rows, nr);
if (!redraw && row->dirty == 2) {
DispClr(disp.textPos, disp.butLim);
redraw = true;
}
if (redraw || row->dirty != 0) {
if (DISPDEBUG) {
Serial.print(F("D row y")); Serial.print(disp.textPos);
Serial.print(F(" fmt ")); Serial.print(row->fmt, DEC);
Serial.print(F(" head: ")); Serial.print(row->head); Serial.print(F(" text: ")); Serial.print(row->text); Serial.print(' ');
Serial.print(nr, DEC); Serial.println(F(". row draw"));
}
DispDrawRow(row, !redraw); // clear not needed when redrawing
row->dirty = 0;
}
disp.textPos += DispRowHeight(row->fmt);
if (row->line) disp.textPos++;
}
for (; nr < disp_max_rows; nr++) {
row = & ARRAY(disp.rows, nr);
if (row->fmt != 0) {
if (!redraw) {
DispClr(disp.textPos, disp.butLim);
redraw = true;
}
row->fmt = 0;
}
}
SerialD.send(); // complete send
if (DISPDEBUG) {
if (redraw) {
Serial.println("");
}
}
}
void DispDebugGet(char ch, int8_t i) {
static boolean off = true;
if (!DISPDEBUG) return;
if (off) {
Serial.print(F("D get"));
off = false;
}
if (ch == 0) {
Serial.print('>');
Serial.println(i, DEC);
off = true;
return;
}
Serial.print(' ');
if (ch == '#') {
Serial.print(i, DEC);
} else {
Serial.print(ch);
}
}
int DispGetEvent(byte *code, byte maxlen) {
// read edip event of format: ESC name len [data]
// skip everything before the next ESC, read name and data
// store data in code[maxlen], fill up with zeros
// and return name or a negative value in case of error
int ch, len, arg;
do {
ch = SerialD.read();
if (ch < 0) return ch; // error
} while (ch != 27); // skip chars until esc
ch = SerialD.read();
if (ch < 0) return ch; // error
len = SerialD.read();
if (len < 0) return len; // error
while (len > 0) {
len--;
arg = SerialD.read();
if (arg < 0) return arg; // error
if (maxlen > 0) {
*code = arg;
code++;
maxlen--;
}
}
while (maxlen > 0) {
*code = 0;
code++;
maxlen--;
}
return ch;
}
void DispGet(byte &menu, byte &cmd) {
// return menu and command
int ch;
int result = 0;
byte len;
//boolean menuOn=false;
byte arg;
byte nr;
struct Disp_Button *button;
long s = now;
if (digitalRead(io_disp_input) != LOW) { // nothing touched
if (disp.bright && time_ge(now, disp.lastAct + 3600000)) { // 3600000 = 1 h
if (dispEco > 99) dispEco = 99;
DispPutStr("#YH"); DispPut('0' + dispEco / 10); DispPut('0' + dispEco % 10); DispPut(','); // display light dark
SerialD.send();
disp.bright = false;
disp.wakemeup = false;
//DispTriggerRewrite();
menu = m_home;
cmd = cmd_sleep;
} else {
menu = m_none;
cmd = 0;
}
return;
}
while ((ch = DispGetEvent(&arg, 1)) >= 0) {
disp.wakemeup = true;
switch (ch) {
case 'A':
//case 'N': // menu response
DispDebugGet('#', ch);
if (arg > 0) {
result = arg; // the touch code
//disp.freeze = false;
}
break;
case 'H':
DispDebugGet('.', 0);
// if full screen touch is activated, tapping anywhere on screen triggers command screen
if (arg == 1) { // touch (not drag or release)
if (disp.bright) { // when dark: only wake up
result = cmd_area; // indicate full screen touch
}
}
break;
/*
case 'T':
menuOn = true;
DispDebugGet('m', 0);
disp.freeze = true;
break;
*/
}
}
/*
if (menuOn) {
DispSend("\033NT\002"); SerialD.send();
result = 0;
}
*/
if (result != 0) {
disp.lastAct = now;
}
if (!disp.bright && disp.wakemeup) { // there was any event
disp.wakemeup = false;
DispSend("#YH100"); // full light
SerialD.send();
//DispTriggerRewrite(); // refresh display
//disp.freeze = false;
disp.bright = true;
disp.lastAct = now;
if (result == 0) {
result = cmd_wake; // wake up
}
}
if (result != 0) {
DispDebugGet(0, result);
}
/*
if (disp.commdebug) {
Serial.println("<.");
if (result == 0) {
while (Serial.read() < 0);
}
}
*/
if (result >= 0x10) { // button touch code
nr = result & 0x0f;
button = &ARRAY(disp.buttons, nr);
if (result == button->touchCode) {
menu = button->menu;
cmd = button->cmd;
} else {
//if (DISPDEBUG) {
Serial.print(F("touch fault ")); Serial.print(result, HEX); Serial.print('-'); Serial.println(button->touchCode, HEX);
//}
menu = m_none;
cmd = 0;
}
} else if (result != 0) {
if (result == cmd_area) {
menu = disp.areaMenu;
cmd = disp.areaCmd;
} else {
menu = m_home;
cmd = result;
}
} else {
menu = m_none;
cmd = 0;
}
if (DISPDEBUG && menu != m_none) {
Serial.print("D mnu ");
Serial.print(menu, DEC);
Serial.print(" cmd ");
Serial.println(cmd, DEC);
}
return;
}
/*
int DispActMenu() {
return disp.actMenu;
}
*/
void DispError(const char *head, const char *text) {
Serial.print("\nERROR: ");
Serial.print(head);
Serial.print(' ');
Serial.println(text);
disp.textPos = 0;
DispClrAll();
DispBegin(0);
DispTextRow(fmt_lsmall, 0, "ERROR:");
DispTextRow(fmt_lsmall, head, text);
DispEnd();
delay(999999999);
while (1);
}
void DispDebug(boolean on) {
disp.commdebug = on;
SerialD.commdebug = on;
}