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

784 lines
18 KiB
Plaintext

//#include "utils.h"
#define DISPDEBUG 0
enum {disp_maxbuttons = 14};
struct {
boolean commdebug;
boolean rewrite; // true during a rewrite
boolean forced; // true when display for next element is forced
boolean rewriteLater; // delayed rewrite
//boolean finishMenu; // finish the menu out of DispMenu
ulong lastAct; // time of last action
boolean bright;
boolean wakemeup;
//boolean freeze;
byte actMenu; // actual screen
byte textPos;
byte butLim;
byte button[disp_maxbuttons]; // code for button
byte fullCode; // code when full screen was touched
byte more;
word buttonmask;
void (*handler)();
} disp = {0};
void DispSend(const char *data) {
if (data) SerialD.print(data);
SerialD.write((byte)0);
}
boolean 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,"); // disable terminal and clear Display, dimmer fast, blink 0.3 s, full light
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.rewriteLater = true;
if (DISPDEBUG) {
Serial.println(F("D init"));
}
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);
}
}
// this might not be needed
void DispTriggerRewrite() {
disp.rewriteLater = true;
}
boolean DispShowThis(byte nr) {
return /*!disp.freeze && */ (ParChanged(nr) || disp.rewrite);
}
void DispButtonArea(byte nr, byte coord[4], byte clr[4]) {
byte x1, x2, y1, y2;
x1 = ((nr + 1) % 2) * 33;
y1 = 129 - (nr / 2 + 1) * 18;
x2 = x1 + 30;
y2 = y1 + 16;
coord[0] = x1;
coord[1] = y1;
coord[2] = x2;
coord[3] = y2;
x2+=2;
if (y1 > 0) y1--;
if (x2 > 63) x2 = 63;
if (clr) {
clr[0] = x1;
clr[1] = y1;
clr[2] = x2;
clr[3] = y2;
}
}
void DispClrButton(byte nr, byte newCode) {
byte code = ARRAY(disp.button, nr);
byte i;
if (newCode != 0) {
// clear other buttons with the same code
for (i = 0; i < disp_maxbuttons; i++) {
if (ARRAY(disp.button, i) == newCode) {
if (DISPDEBUG) {
Serial.print("D clr button i");
Serial.print(i);
Serial.print(" cod = ");
Serial.println(newCode);
}
DispPutStr("\033AL"); DispPut(newCode); DispPut(1);
DispSend(0);
ARRAY(disp.button, i) = 0;
}
}
}
if (code != 0) {
// clear button at position nr
if (DISPDEBUG) {
Serial.print("D clr button at nr ");
Serial.print(nr);
Serial.print(" cod = ");
Serial.println(code);
}
DispPutStr("\033AL"); DispPut(code); DispPut(1);
DispSend(0);
}
SerialD.send();
ARRAY(disp.button, nr) = newCode;
}
void DispBegin(byte menu) {
byte coord[4], clr[4];
byte nr;
disp.textPos = 0;
disp.buttonmask = 0;
disp.rewrite = false;
//bitSet(disp.buttonmask, 0); // mark menu button as used
//if (disp.freeze) return;
if (disp.actMenu != menu) {
disp.actMenu = menu;
disp.more = 0;
} else if (!disp.rewriteLater) {
return;
}
disp.rewrite = true;
disp.rewriteLater = false;
ParSetAllChangedDisp();
if (DISPDEBUG) {
Serial.println(F("D begin ---"));
}
DispSend("#AL0,1,"); // clear all touch buttons
for (nr = 0; nr < disp_maxbuttons; nr++) {
disp.button[nr] = 0;
}
SerialD.send();
}
void DispFullScreenTouch(byte code) {
if (disp.rewrite /* && !disp.freeze */) {
DispSend("#AH0,0,63,127,"); // activate general touch area
SerialD.send();
}
disp.fullCode = code;
}
byte DispMore() {
return disp.more;
}
byte DispSetMore(byte value) {
disp.more = value;
}
/*
void DispMenu(byte nr, byte code, char *menutitle) {
byte coord[4], clr[4];
byte touchCode, oldCode, newCode;
bitSet(disp.buttonmask, nr); // mark button as used
if (disp.freeze) return;
if (code >= 16 || nr == 0) {
touchCode = code;
} else {
touchCode = disp.actMenu * 16 + code;
}
newCode = disp_menuoff + nr;
oldCode = ARRAY(disp.button, nr);
if (oldCode == newCode && !disp.rewrite) return;
DispClrButton(nr, newCode);
DispButtonArea(nr, coord, clr);
//DispPutStr("\033RL"); DispPutData(clr, 4); // clear rect
// show menu button and define popup menu
// button font geneva10, frame thin
DispPutStr("#AF4,#AE1,\033AM"); DispPutData(coord, 4);
DispPut(0); DispPut(disp_menuoff+nr); DispPut(touchCode); DispPutStr("OC"); DispPutStr(menutitle);
if (DISPDEBUG) {
Serial.print(F("D menu\n"));
}
disp.finishMenu = true;
return;
}
void DispRedrawMenu(byte nr) {
ARRAY(disp.button, nr) = 0;
}
void DispAppendToMenu(char *text) {
if (disp.finishMenu) {
disp.finishMenu = false;
DispPut('|');
DispPutStr(text);
disp.finishMenu = true;
}
}
void DispEndMenu() {
if (disp.finishMenu) {
DispSend(0);
disp.finishMenu = false;
SerialD.send();
}
}
*/
void DispButton(byte nr, byte code, char *text) {
byte x, y, w;
byte coord[4], clr[4];
byte touchCode, oldCode;
disp.handler();
bitSet(disp.buttonmask, nr); // mark button as used
//if (disp.freeze) return;
if (code >= 16) {
touchCode = code;
} else {
touchCode = disp.actMenu * 16 + code;
}
oldCode = ARRAY(disp.button, nr);
if (oldCode == touchCode && !disp.rewrite && !disp.forced) return;
disp.forced = false;
DispClrButton(nr, touchCode);
DispButtonArea(nr, coord, clr);
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++;
coord[1] -= 2;
} else if (text[0] == ':') { // no frame button
DispPutStr("#AE0,"); // no frame
text++;
} else { // normal button
DispPutStr("#AE1,"); // frame thin
}
DispPutStr("\033RL"); DispPutData(clr, 4); // clear rect
// AF4 button font Geneva10
if (DISPDEBUG) {
Serial.print(F("D len "));
Serial.print(DispTextLength(text));
Serial.println(text);
}
if (DispTextLength(text) < 30) {
DispPutStr("#AF4,");
} else {
DispPutStr("#AF1,");
}
// AT create button
DispPutStr("\033AT"); DispPutData(coord, 4);
DispPut(touchCode); DispPut(0); DispPut('C'); DispSend(text);
SerialD.send();
if (DISPDEBUG) {
Serial.print(F("D button "));
Serial.println(text);
}
}
void DispLine() {
if (disp.textPos > 110) return; // do not draw line in menu button area
if (disp.rewrite /* && !disp.freeze */) {
DispPutStr("\033GD"); DispPut(0); DispPut(disp.textPos - 1); DispPut(63); DispPut(disp.textPos - 1); // draw line
SerialD.send();
}
disp.textPos+=1;
}
void DispEnd() {
byte nr;
int8_t lastbut;
byte coord[4], clr[4];
//if (disp.freeze) return;
lastbut = -1;
for (nr = 0; nr < disp_maxbuttons; nr++) {
if (bitRead(disp.buttonmask, nr)) {
lastbut = nr;
} else if (disp.button[nr]) {
DispClrButton(nr, 0);
bitSet(disp.buttonmask, nr);
}
}
if (!disp.rewrite) return;
if (lastbut % 2 == 0) lastbut++;
if (DISPDEBUG) {
Serial.print("lastbut");
Serial.println(lastbut);
}
for (nr = 0; nr <= lastbut; nr++) {
if (!bitRead(disp.buttonmask, nr)) {
DispButtonArea(nr, coord, clr);
//Serial.print("clear "); Serial.print(clr[1], DEC); Serial.print(" "); Serial.println(clr[3], DEC);
DispPutStr("\033RL"); DispPutData(clr, 4); // clear rect
}
}
// clear gap between text and buttons
if (lastbut < 0) {
clr[1] = 127;
} else {
DispButtonArea(lastbut, coord, clr);
}
if (disp.textPos > 0) disp.textPos--;
if (DISPDEBUG) {
Serial.print("textpos");
Serial.println(disp.textPos);
Serial.print("clrbut");
Serial.println(clr[1]);
}
if (disp.textPos < clr[1]) {
if (DISPDEBUG) {
Serial.print("gap "); Serial.print(disp.textPos, DEC); Serial.print(" "); Serial.println(clr[1], DEC);
}
DispPutStr("\033RL"); DispPut(0); DispPut(disp.textPos); DispPut(63); DispPut(clr[1]-1); // clear rect
}
SerialD.send(); // complete send
if (DISPDEBUG) {
Serial.println(F("D end ---"));
}
}
void DispRewrite() {
disp.rewrite = true;
}
void DispRewriteNext() {
disp.forced = true;
}
void DispTextArg(byte fmt, const char *head, const char *text) {
byte xpos, h, align, x;
int i;
boolean zoom = false;
int len, maxlen;
char txt[2];
disp.handler();
if (DISPDEBUG) {
Serial.print(F("D text "));
Serial.print(fmt, DEC);
Serial.print("|");
if (head) Serial.print(head);
Serial.println(text);
}
len = DispTextLength(text);
maxlen = 127;
switch (fmt) {
case fmt_none:
return;
case fmt_lsmall:
DispPutStr("#ZF4,"); align='L'; xpos = 0; h = 11; break;
case fmt_ckbox:
DispPutStr("#ZF4,"); align='L'; xpos = 18; h = 11; break;
case fmt_rsmall:
//sameline = true;
DispPutStr("#ZF4,"); align='R'; xpos = 63; h = 11; break;
case fmt_medium:
DispPutStr("#ZF4,");
align='R'; xpos = 63; h = 26; break;
case fmt_big:
if (len >= 30) {
DispPutStr("#ZF4,#ZZ1,2,"); zoom = true;
} else {
DispPutStr("#ZF6,"); head = 0;
}
align='R'; xpos = 63; h = 27;
break;
}
// clear text line
DispPutStr("\033RL"); DispPut(0); DispPut(disp.textPos - (disp.textPos > 0)); DispPut(63); DispPut(disp.textPos + h - 2); // clear rect
if (head && head[0]) {
DispPutStr("\033Z");
if (align == 'R') {
DispPut('L'); DispPut(0); DispPut(disp.textPos);
DispSend(head);
maxlen -= DispTextLength(head);
if (fmt == fmt_medium && len < 30) DispPutStr("#ZF6,"); // use Big for number
DispPutStr("\033Z"); DispPut(align); DispPut(xpos); DispPut(disp.textPos);
} else {
DispPut(align); DispPut(xpos); DispPut(disp.textPos);
DispPutStr(head);
maxlen -= DispTextLength(head);
}
} else {
DispPutStr("\033Z"); DispPut(align); DispPut(xpos); DispPut(disp.textPos);
}
if (len > maxlen) {
txt[0] = text[0];
txt[1] = 0;
len = DispTextLength(txt);
for (i = 0; len <= maxlen && text[i] != 0; i++) {
DispPut(text[i]);
txt[0] = text[i+1];
len += DispTextLength(txt);
}
DispSend("");
} else {
if (text[0] == 0) {
DispSend(" ");
} else {
DispSend(text);
}
}
disp.textPos += h;
if (zoom) {
DispSend("#ZZ1,1,");
}
SerialD.send();
}
void DispSkip(byte fmt) {
switch (fmt) {
case fmt_none:
return;
case fmt_medium:
disp.textPos += 26; break;
case fmt_big:
disp.textPos += 27; break;
case fmt_lsmall:
case fmt_rsmall:
case fmt_ckbox:
default:
disp.textPos += 11; break;
}
}
void DispText(const char *text) {
if (disp.rewrite || disp.forced /*&& !disp.freeze*/) {
disp.forced = false;
DispTextArg(fmt_lsmall, 0, text);
} else {
DispSkip(fmt_lsmall);
}
}
void DispText2(const char *head, const char *text) {
if (disp.rewrite || disp.forced) {
disp.forced = false;
DispTextArg(fmt_rsmall, head, text);
} else {
DispSkip(fmt_rsmall);
}
}
int DispTextLength(const char *text) {
// caclulate text length for Font 4 (GENEVA10)
// the table contains the pure width, we have to add one pixel for the gap
static byte len[96] = {5,2,4,6,5,8,7,2,3,3,6,5,3,4,2,4,
5,5,5,5,5,5,5,5,5,5,3,3,4,5,4,5,
7,6,5,5,5,4,4,5,5,2,5,5,4,7,5,5,
5,5,5,5,5,5,5,7,5,5,4,3,5,3,3,5,
2,4,4,4,4,4,3,4,4,2,3,4,2,7,4,4,
4,4,4,4,3,4,5,7,5,5,4,3,1,3,5,6};
int length = 0;
char ch;
while ((ch = *text) != 0) {
if (ch < 32 || ch >= 128) {
length += 5;
} else {
length += len[ch-32] + 1;
}
text++;
}
return length;
}
boolean DispState(char *head, byte nr) {
char text[16];
if (DispShowThis(nr)) {
DispTextArg(fmt_lsmall, head, ParFmt(nr));
return true;
}
DispSkip(fmt_lsmall);
return false;
}
boolean DispValueArg(byte fmt, char *head, byte nr, byte stateNr) {
byte state, maxlen;
char text[16];
//if (!disp.freeze) {
if (stateNr) {
state = ParGet(stateNr);
} else {
state = 0;
}
if (state > 0) {
if (DispShowThis(stateNr)) {
DispTextArg(fmt, head, ParFmt(stateNr));
return true;
}
} else {
if (DispShowThis(nr)) {
if (fmt == fmt_big) {
head = 0;
maxlen = 4;
} else if (fmt == fmt_medium) {
maxlen = 4;
} else {
maxlen = 0; // no max length
}
DispTextArg(fmt, head, ParFmt(nr, maxlen));
return true;
}
}
//}
DispSkip(fmt);
return false;
}
boolean DispBigValue(byte nr, byte stateNr) {
return DispValueArg(fmt_big, 0, nr, stateNr);
}
boolean DispMediumValue(char *head, byte nr, byte stateNr) {
return DispValueArg(fmt_medium, head, nr, stateNr);
}
boolean DispValue(char *head, byte nr, byte stateNr) {
return DispValueArg(fmt_rsmall, head, nr, stateNr);
}
boolean DispCheckbox(byte on, byte off, byte nr, char *text) {
byte value = ParGet(nr);
byte code;
static byte oldPos = 1;
if (disp.textPos == oldPos) { // make a gap between two succesive checkboxes
disp.textPos += 4;
} else {
oldPos = disp.textPos;
}
if (!DispShowThis(nr)) {
DispSkip(fmt_ckbox);
oldPos = disp.textPos;
return false;
}
if (DISPDEBUG) {
Serial.print(F("D checkbox "));
Serial.println(text);
}
if (disp.textPos > oldPos) { // clear gap
DispPutStr("\033RL"); DispPut(0); DispPut(oldPos - 1); DispPut(63); DispPut(disp.textPos - 1);
}
DispTextArg(fmt_ckbox, 0, text);
// button font 4x6, no button frame
DispPutStr("#AF4,#AE0,\033AT"); DispPut(0); DispPut(disp.textPos-14); DispPut(63); DispPut(disp.textPos-1);
if (value) {
code = off;
} else {
code = on;
}
DispPut(0); DispPut(disp.actMenu * 16 + code); DispPut('L');
if (value) {
DispSend("[x]");
} else {
DispSend("[ ]");
}
SerialD.send();
oldPos = disp.textPos;
return true;
}
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) {
int ch, len, arg;
do {
ch = SerialD.read();
if (ch < 0) return ch;
} while (ch != 27); // skip chars until esc
ch = SerialD.read();
if (ch < 0) return ch;
len = SerialD.read();
if (len < 0) return len;
while (len > 0) {
len--;
arg = SerialD.read();
if (arg < 0) return arg;
if (maxlen > 0) {
*code = arg;
code++;
maxlen--;
}
}
while (maxlen > 0) {
*code = 0;
code++;
maxlen--;
}
return ch;
}
void DispGet(byte &menu, byte &code) {
// return menu and code
// menu might have the special values disp_none, disp_wake, disp_sleep
int ch;
int result = 0;
byte len;
//boolean menuOn=false;
byte arg;
long s = now;
if (digitalRead(io_disp_input) != LOW) {
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 = disp_sleep;
code = 0;
} else {
menu = disp_none;
code = 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;
//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 = disp.fullCode;
}
}
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 = 16 * disp_wake; // wake up
}
}
if (result != 0) {
DispDebugGet(0, result);
}
/*
if (disp.commdebug) {
Serial.println("<.");
if (result == 0) {
while (Serial.read() < 0);
}
}
*/
if (result == 0) {
menu = disp_none;
code = 0;
} else {
menu = result / 16;
code = result % 16;
}
if (DISPDEBUG && menu != disp_none) {
Serial.print("mnu ");
Serial.print(menu, DEC);
Serial.print("cod ");
Serial.println(code, DEC);
}
return;
}
/*
int DispActMenu() {
return disp.actMenu;
}
*/
void DispError(char *head, char *text) {
Serial.print("\nERROR: ");
Serial.print(head);
Serial.print(' ');
Serial.println(text);
disp.textPos = 0;
DispTriggerRewrite();
DispBegin(0);
DispSend("#DL,");
DispTextArg(fmt_lsmall, 0, "ERROR:");
DispTextArg(fmt_lsmall, head, text);
delay(999999999);
while (1);
}
void DispDebug(boolean on) {
disp.commdebug = on;
SerialD.commdebug = on;
}