#include #include #include #include #define MINPRESSURE 200 #define MAXPRESSURE 1000 const int XP = 8, XM = A2, YP = A3, YM = 9; const int TS_LEFT = 125, TS_RT = 920, TS_TOP = 948, TS_BOT = 97; TouchScreen ts = TouchScreen(XP, YP, XM, YM, 300); MCUFRIEND_kbv tft; const char* button_labels[5][5] = { {"7", "8", "9", "/", "C"}, {"4", "5", "6", "*", "<-"}, {"1", "2", "3", "-", "("}, {"0", ".", "=", "+", ")"}, {"sqrt", "x^2", "1/x", "%", "+/-"} }; #define ROWS 5 #define COLS 5 int pixel_x, pixel_y; #define STACK_MAX 12 float numStack[STACK_MAX]; char opStack[STACK_MAX]; int ns = 0, os = 0; #define INPUT_MAX 40 // auch für längere serielle Eingaben char input[INPUT_MAX+1] = "0"; bool errorFlag = false; void serialPrintInput() { Serial.print("Display: "); Serial.println(input); } // ---------- Sicherheitsprüfungen ---------- bool isSafeInput(const char* in) { int len = strlen(in); if (len == 0) return false; if (strchr("+-*/%", in[len-1])) return false; if (strchr("+*/%)", in[0])) return false; for (int i = 1; i < len; i++) { if (strchr("+-*/%", in[i]) && strchr("+-*/%", in[i-1])) return false; } int bal = 0; for (int i = 0; i < len; i++) { if (in[i] == '(') bal++; if (in[i] == ')') bal--; if (bal < 0) return false; if (in[i] == '(' && in[i+1] == ')') return false; } if (bal != 0) return false; for (int i = 1; i < len; i++) { if (in[i] == ')' && strchr("+-*/%(", in[i-1])) return false; } for (int i = 1; i < len; i++) { if (in[i] == '.' && (strchr("+-*/%()", in[i-1]) || in[i-1] == 0)) return false; } return true; } // ---------- Parser ---------- int precedence(char op) { if (op == '+' || op == '-') return 1; if (op == '*' || op == '/' || op == '%') return 2; return 0; } bool applyOp() { if (ns < 2) return false; float b = numStack[--ns], a = numStack[--ns]; char op = opStack[--os]; switch(op) { case '+': numStack[ns++] = a + b; break; case '-': numStack[ns++] = a - b; break; case '*': numStack[ns++] = a * b; break; case '/': if (b == 0) return false; numStack[ns++] = a / b; break; case '%': if (b == 0) return false; numStack[ns++] = fmod(a, b); break; } return true; } float evalExpr(const char* expr, bool& error) { ns = os = 0; int i = 0; while (expr[i]) { if (expr[i] == ' ') { i++; continue; } // Sonderfunktionen für serielle Eingabe: sqrt(), sq(), inv() if (!strncmp(&expr[i], "sqrt(", 5)) { i += 5; int start = i; int par = 1; while (expr[i] && par > 0) { if (expr[i] == '(') par++; if (expr[i] == ')') par--; i++; } char inner[INPUT_MAX+1]; strncpy(inner, &expr[start], i-start-1); inner[i-start-1] = 0; bool err = false; float x = evalExpr(inner, err); if (err || x < 0) { error = true; return 0; } if (ns >= STACK_MAX) { error = true; return 0; } numStack[ns++] = sqrt(x); continue; } if (!strncmp(&expr[i], "sq(", 3)) { i += 3; int start = i; int par = 1; while (expr[i] && par > 0) { if (expr[i] == '(') par++; if (expr[i] == ')') par--; i++; } char inner[INPUT_MAX+1]; strncpy(inner, &expr[start], i-start-1); inner[i-start-1] = 0; bool err = false; float x = evalExpr(inner, err); if (err) { error = true; return 0; } if (ns >= STACK_MAX) { error = true; return 0; } numStack[ns++] = x * x; continue; } if (!strncmp(&expr[i], "inv(", 4)) { i += 4; int start = i; int par = 1; while (expr[i] && par > 0) { if (expr[i] == '(') par++; if (expr[i] == ')') par--; i++; } char inner[INPUT_MAX+1]; strncpy(inner, &expr[start], i-start-1); inner[i-start-1] = 0; bool err = false; float x = evalExpr(inner, err); if (err || x == 0) { error = true; return 0; } if (ns >= STACK_MAX) { error = true; return 0; } numStack[ns++] = 1.0 / x; continue; } if (isdigit(expr[i]) || expr[i] == '.' || (expr[i] == '-' && (i == 0 || expr[i-1] == '('))) { char buf[16]; int bi=0; if (expr[i] == '-') buf[bi++] = expr[i++]; while (isdigit(expr[i]) || expr[i] == '.') buf[bi++] = expr[i++]; buf[bi]=0; if (ns >= STACK_MAX) { error = true; return 0; } numStack[ns++] = atof(buf); } else if (expr[i] == '(') { opStack[os++] = '('; i++; } else if (expr[i] == ')') { while (os > 0 && opStack[os-1] != '(') { if (!applyOp()) { error = true; return 0; } } if (os == 0) { error = true; return 0; } os--; i++; } else if (strchr("+-*/%", expr[i])) { char currOp = expr[i]; while (os > 0 && precedence(opStack[os-1]) >= precedence(currOp)) { if (!applyOp()) { error = true; return 0; } } opStack[os++] = currOp; i++; } else { error = true; return 0; } } while (os > 0) { if (!applyOp()) { error = true; return 0; } } if (ns != 1) { error = true; return 0; } return numStack[0]; } // ---------- Anzeige & Eingabe ---------- #define BTN_W 62 #define BTN_H 56 #define BTN_SX 3 #define BTN_SY 8 #define BTN_X0 5 #define BTN_Y0 140 void drawButtons() { tft.setTextSize(2); for (int row = 0; row < ROWS; row++) { for (int col = 0; col < COLS; col++) { int x = BTN_X0 + col * (BTN_W + BTN_SX); int y = BTN_Y0 + row * (BTN_H + BTN_SY); tft.fillRoundRect(x, y, BTN_W, BTN_H, 14, 0xC618); tft.drawRoundRect(x, y, BTN_W, BTN_H, 14, TFT_BLACK); int labelLen = strlen(button_labels[row][col]); int fontW = 12; int fontH = 16; int xOffset = BTN_W/2 - (labelLen*fontW)/2; int yOffset = BTN_H/2 - fontH/2; tft.setTextColor(TFT_BLACK); tft.setCursor(x + xOffset, y + yOffset); tft.print(button_labels[row][col]); } } tft.setTextSize(2); } void handleTouch(int px, int py) { for (int row = 0; row < ROWS; row++) for (int col = 0; col < COLS; col++) { int x = BTN_X0 + col * (BTN_W + BTN_SX); int y = BTN_Y0 + row * (BTN_H + BTN_SY); if (px >= x && px < x + BTN_W && py >= y && py < y + BTN_H) { handleButton(button_labels[row][col]); serialPrintInput(); } } } void drawScreen() { tft.fillScreen(TFT_WHITE); tft.fillRoundRect(10, 40, 300, 65, 8, 0xFFFF); tft.drawRoundRect(10, 40, 300, 65, 8, 0x0000); drawButtons(); } void showInput() { tft.fillRoundRect(15, 50, 280, 55, 6, 0xFFFF); tft.setTextColor(TFT_BLACK); tft.setTextSize(3); tft.setCursor(22, 67); int len = strlen(input); if (len > 12) tft.print("..." + String(&input[len-12])); else tft.print(input); serialPrintInput(); } void resetCalc() { strcpy(input, "0"); errorFlag = false; serialPrintInput(); } void setup() { Serial.begin(115200); wdt_enable(WDTO_2S); uint16_t ID = tft.readID(); tft.begin(ID); tft.setRotation(0); drawScreen(); resetCalc(); showInput(); Serial.println("Touch-Taschenrechner bereit! Eingabe per Touch ODER Serial Monitor."); Serial.println("Beispiele:"); Serial.println("1+2*3 -> 7"); Serial.println("sqrt(9) -> 3"); Serial.println("inv(2) -> 0.5"); Serial.println("sq(5) -> 25"); Serial.println("C -> Reset"); } void handleButton(const char* btn) { int len = strlen(input); if (errorFlag && strcmp(btn,"C") && !(isdigit(btn[0]) && strlen(btn)==1)) return; if (!strcmp(btn,"C")) { resetCalc(); } else if (!strcmp(btn,"<-")) { if (len > 1) { input[len-1]=0; } else strcpy(input, "0"); } else if (!strcmp(btn,"=")) { if (!isSafeInput(input)) { strcpy(input, "ERR"); errorFlag = true; Serial.println("ERR"); return; } bool error = false; float res = evalExpr(input, error); if (error || isinf(res) || isnan(res) || fabs(res) > 999999999) { strcpy(input, "ERR"); errorFlag = true; Serial.println("ERR"); return; } if (fabs(res - (long)res) < 1e-6) sprintf(input, "%ld", (long)res); else { dtostrf(res, 1, 6, input); char* p = input+strlen(input)-1; while (*p == '0' && p > input && *(p-1) != '.') *p-- = 0; if (*p == '.') *p = 0; } Serial.println(input); } else if (!strcmp(btn,"+/-")) { if (strcmp(input, "0") && strcmp(input, "ERR")) { if (input[0]=='-') memmove(input, input+1, strlen(input)); else if (len < INPUT_MAX) { memmove(input+1, input, len+1); input[0]='-'; } } } else if (!strcmp(btn,"sqrt")) { float val = atof(input); if (val<0) { strcpy(input, "ERR"); errorFlag=true; Serial.println("ERR"); return; } val = sqrt(val); if (fabs(val - (long)val) < 1e-6) sprintf(input, "%ld", (long)val); else dtostrf(val, 1, 6, input); Serial.println(input); } else if (!strcmp(btn,"x^2")) { float val = atof(input); val *= val; if (fabs(val - (long)val) < 1e-6) sprintf(input, "%ld", (long)val); else dtostrf(val, 1, 6, input); Serial.println(input); } else if (!strcmp(btn,"1/x")) { float val = atof(input); if (val==0) { strcpy(input, "ERR"); errorFlag=true; Serial.println("ERR"); return; } val = 1.0/val; if (fabs(val - (long)val) < 1e-6) sprintf(input, "%ld", (long)val); else dtostrf(val, 1, 6, input); Serial.println(input); } else if (!strcmp(btn,"%")) { float val = atof(input); val = val / 100.0; if (fabs(val - (long)val) < 1e-6) sprintf(input, "%ld", (long)val); else dtostrf(val, 1, 6, input); Serial.println(input); } else if (!strcmp(btn,".") && !strchr(input, '.') && len < INPUT_MAX-1) { input[len] = '.'; input[len+1]=0; } else if (strlen(btn)==1 && isdigit(btn[0])) { if (!strcmp(input,"0") || !strcmp(input,"ERR")) { input[0]=btn[0]; input[1]=0; } else if (len < INPUT_MAX-1) { input[len]=btn[0]; input[len+1]=0; } } else if (strchr("+-*/()", btn[0]) && len < INPUT_MAX-1) { char last = (len>0) ? input[len-1] : 0; if ((strchr("+-*/%", last) && btn[0] != '(' && btn[0] != ')') || (len == 0 && btn[0]!='(' && btn[0]!='-') || (last == '(' && strchr("*/%)", btn[0]))) { return; } input[len]=btn[0]; input[len+1]=0; } } bool Touch_getXY(void) { TSPoint p = ts.getPoint(); pinMode(YP, OUTPUT); pinMode(XM, OUTPUT); digitalWrite(YP, HIGH); digitalWrite(XM, HIGH); bool pressed = (p.z > MINPRESSURE && p.z < MAXPRESSURE); if (pressed) { pixel_x = map(p.x, TS_LEFT, TS_RT, 0, tft.width()); pixel_y = map(p.y, TS_TOP, TS_BOT, 0, tft.height()); } return pressed; } // ---- Serieller Interpreter ---- void interpretSerial(char* buf) { // Remove newline int l = strlen(buf); while (l > 0 && (buf[l-1]=='\n'||buf[l-1]=='\r')) buf[--l]=0; if (l==0) return; if (!strcmp(buf, "C")) { resetCalc(); showInput(); return; } if (!strcmp(buf, "exit")) { asm volatile ("jmp 0"); return; } if (!isSafeInput(buf)) { strcpy(input, "ERR"); errorFlag = true; showInput(); Serial.println("ERR"); return; } bool error = false; float res = evalExpr(buf, error); if (error || isinf(res) || isnan(res) || fabs(res) > 999999999) { strcpy(input, "ERR"); errorFlag = true; showInput(); Serial.println("ERR"); return; } // Ausgabeformat wie im Taschenrechner if (fabs(res - (long)res) < 1e-6) sprintf(input, "%ld", (long)res); else { dtostrf(res, 1, 6, input); char* p = input+strlen(input)-1; while (*p == '0' && p > input && *(p-1) != '.') *p-- = 0; if (*p == '.') *p = 0; } showInput(); Serial.println(input); } void loop() { wdt_reset(); static bool prevTouch = false; bool currTouch = Touch_getXY(); if (prevTouch && !currTouch) { handleTouch(pixel_x, pixel_y); showInput(); } prevTouch = currTouch; // Serielle Eingabe verarbeiten static char serBuf[INPUT_MAX + 1]; static uint8_t serIdx = 0; while (Serial.available()) { char c = Serial.read(); if (c == '\r' || c == '\n') { serBuf[serIdx] = 0; if (serIdx > 0) { interpretSerial(serBuf); serIdx = 0; } } else if (serIdx < INPUT_MAX && (isprint(c) || c == '\t')) { serBuf[serIdx++] = c; } } }