ขอบเขตการทำงาน
ออกแบบโปรแกรมคำนวนสมการทางคณิตศาสตร์ ประกอบด้วย + - * / % ^ ( ) = และตัวเลข โดยรับข้อความทาง Serial Monitor และนำมาคำนวณบนบอร์ด Arduino และแสดงผลลัพธ์กลับมายัง Serial Monitor
แนวทางการออกแบบ
-รับข้อความจาก user ในรูปแบบ infix
-แปลง infix ให้อยู่ในรูปของ Postfix โดยใช้ String Array (ทำหน้าที่คล้ายกับ Stack )เข้ามาช่วยในการแปลง
-นำ Postfix ที่ได้มาคำนวณค่าทางคณิตศาสตร์
อธิบายช่วงการทำงานของ Code
-ช่วง State0 : รับค่าจาก Serial Monitor
-ช่วง State1 : แปลง infix เป็น postfix
-ช่วง State2 : นำ postfix ที่ได้มาคำนวน
Code Arduino
#define MAX 20 //Max digit
char ch; //each character in string
int state = 0; //State Cycle
int pr = 0; //Priority Value
String token = ""; //Debug input Value for state 0
/* (10-3/2+5)*2 */ //28
/* ((100+1)*50)/100 */ //50
/* 15-((10-5)-3) */ //13
/* (10^2 + 2^10) % (100^2) */ //1124
/* 2^3-5*2^2-3*2+10 */ //-8
String postfix = ""; //Postfix Statement
String stacker = ""; //Collect to stack
String st = ""; //string temp
String sa[MAX], *sap; //Structure to collect data, use pointer to point the string array
// startup point entry (runs once).
void setup () {
// start serial communication.
Serial.begin (9600); //9600 buadrate
Serial.println("__Arduino Calculator__");
sap = sa; // pointer to String array
}
// loop the main sketch.
void loop () {
if (state == 0) { // Input State
//Comment if u want to use DEBUG STATEMENT//
//>>>> /*
if (Serial.available() > 0) { // available
token = Serial.readString(); // readString from serial monitor
//<<<< */
if (!token.equals("")) { // check it not empty
Serial.println("\n__STATE 0 : Input__");
state = 1; // next state
Serial.println("INPUT: " + token); //show input
}
//>>>> /*
}
//<<<< */
}
else if (state == 1) { // infix->postfix State
Serial.println("\n__STATE 1 : Infix to Postfix__");
// loopfor check each character
for (int index = 0; index < token.length(); index++) {
ch = token.charAt(index); // define each character from input String
if (ch == ' ') { //if found ' ' (space) it can be skip this loop
continue; // skip this loop
}
st += ch; // store in string temp
pr = priority(ch); // check priority of character in ch
if (pr == 0) { // same as isalnum() [it is a num?]
stacker += ch; // collect to stacker
}
else {
if (!stacker.equals("")) { // if stacker is not empty
postfix += stacker + ' '; // collect data to postfix statement
}
stacker = ""; // clear stacker
if (ch == ')') { // end bracket case
// use to pop data from stack until it found "("
while (!top(sap).equals("("))
stacker += pop(sap) + " "; // pop data and collect to stacker
pop(sap); // remove "("
}
// open bracket or stack is empty case
else if (ch == '(' || top(sap).equals("")) {
push(sap, st); // push + to stack
}
// top of stack priority value
// less than priority value of this character
else if (priority(top(sap).charAt(0)) < pr) {
push(sap, st);
}
// top of stack priority value
// more than (and Equal) priority value of this character
else if (priority(top(sap).charAt(0)) >= pr) {
// use for loop until stack is empty
// and priority value of top of stack is more
while (priority(top(sap).charAt(0)) >= pr && !top(sap).equals("")) {
// pop stack data to collect in stacker
stacker += pop(sap) + " ";
}
push(sap, st);
}
}
st = ""; // clear stack temp
}
if (!stacker.equals("")) { // check stacker is not empty
postfix += stacker + " "; // collect stacker date into postfix
}
while (!top(sap).equals("")) { // looping until stack is empty
postfix += pop(sap) + " "; // pop stack data to collect into postfix
}
Serial.print("POSTFIX: ");
Serial.println(postfix);
token = ""; // clear input data
stacker = ""; // clear stacker
state = 2; // next state
}
else if (state == 2) { // Calculate State
Serial.println("\n__STATE 2 : Calculate__");
// looping for each character in postfix statement
for (int index = 0; index < postfix.length(); index++) {
ch = postfix.charAt(index); // get each character to char variable
pr = priority(ch); // priority value of that character
// check character is space and stacker is not empty
if (ch == ' ' && !stacker.equals("")) {
push(sap, stacker); // push stacker string to stack
stacker = ""; // clear stacker
}
// if priority meaning is NUMBER
else if (pr == 0) {
stacker += ch; // collect that character into stacker
}
// check for not NUMBER
else if (pr > 0) {
long second = pop(sap).toInt(); //2nd value in integer
long first = pop(sap).toInt(); //1st value in integer
long answer = 0; // answer in long variable
if (ch == '+') { // plus case
answer = first + second; // summation answer
}
else if (ch == '-') { // minus case
answer = first - second; // minus answer
}
else if (ch == '*') { // multiply case
answer = first * second; // product answer
}
else if (ch == '%') { // modulation case
answer = first % second; // mod answer
}
else if (ch == '/') { // divide case
if (second != 0) { // check 2nd number (answer will be infinite)
answer = first / second; // divide answer
}
else // if 2nd is zero => answer not have a value (infinite)
Serial.println("ERROR"); // Error Statement
}
else if (ch == '^') { // Power case
// power answer (have +1 becuase it calculate in binary)
answer = pow(first, second) + 1;
}
push(sap, (String)answer); // push answer in String type to stack
}
}
if (!stacker.equals("")) // check stacker isn't empty
push(sap, stacker); // push stacker date into stack
Serial.print("ANSWER: ");
Serial.println(pop(sap)); // Final Answer
Serial.println("----- DONE! PLEASE EXIT OR NEXT INPUT -----");
postfix = ""; // clear postfix statement
state = 0; // next state to zero-state
}
}
int priority(char key) { // Function send integer priority value of key
if (key > 47 && key < 58) { // is a number
return 0;
}
else { // is a operator/others
switch (key) {
case '^': // Highest priority
return 4;
case '%':
case '*':
case '/':
return 3;
case '+':
case '-':
return 2;
case '(':
case ')':
return 1; // Lowest priority
default:
return -1; // others
}
}
}
// use to push date to stack
// inputs are String Array in str pointer and Data String in s
void push(String* str, String s) {
// loop for find space that can be collect the data
for (int i = 0; i < sizeof(*str); i++) {
if (str[i].equals("")) { //found space
str[i] = s; // store in that slot
break; // stop loop
}
}
}
// use to find lastest data of stack
// input is String Array in str pointer
String top(String* str) {
if (str[0].equals("")) { // empty data
return ""; // return empty string
}
// loop for find lastest data
for (int i = 0; i < sizeof(*str) - 1; i++) {
if (str[i + 1].equals("")) { // found next slot that is empty space
return str[i]; // return lastest data
}
}
}
// use to get String value in stack
// input is String Array in str pointer
String pop(String* str) {
if (str[0].equals("")) { // empty data
return ""; // return empty string
}
// loop for find lastest data
for (int i = 0; i < sizeof(*str) - 1; i++) {
if (str[i + 1].equals("")) { // found next slot that is empty space
String s = str[i]; // get lastest data into s String variable
str[i] = ""; // clear that slot into empty space
return s; // return data
}
}
}
1.การรับค่าจาก Serial Monitor
-กำหนดค่า bound rate =9600 bps ด้วยคำสั่ง Serial.begin()
void setup () {
// start serial communication.
Serial.begin (9600); //9600 buadrate
//...
}
-ตรวจสอบว่ามีการป้อนข้อมูลเข้ามาหรือไม่ โดยใช้คำสั่ง Serial.available() และใช้คำสั่ง Serial.Read String() ในการอ่านค่า
void loop () {
if (state == 0) { // Input State
if (Serial.available() > 0) { // available
token = Serial.readString(); // readString from serial monitor
//...
}
}
}
-เปลี่ยน State0 -> State1 เมื่อรับข้อมูลเข้ามา โดยเช็คจาก token != Empty (token คือตัวแปลที่เก็บข้อมูลที่รับค่าเข้ามา)
void loop () {
if (state == 0) { // Input State
//......
if (!token.equals("")) { // check it not empty
Serial.println("\n__STATE 0 : Input__");
state = 1; // next state
Serial.println("INPUT: " + token); //show input
}
}
}
3.การคำนวณ
-เมื่อเราได้แปลงค่า infix เป็น postfix แล้ว เราจะทำการคำนวณโดย เมื่อพบเจอเครื่องหมาย ที่ให้ทำ pop ตัวเลขออกมาคำนวณเป็นคู่ๆ ดังนี้
else if (state == 2) { // เข้าสู่ขั้นตอนการคำนวณ
Serial.println("\n__STATE 2 : Calculate__");
// สั่งวนลูปตามความยาวข้อความ
for (int index = 0; index < postfix.length(); index++) {
ch = postfix.charAt(index); // กำหนดตัวแปร ch ให้ชี้แต่ละตำแหน่งในตัวแปร postfix
pr = priority(ch); // กำหนดค่าความสำคัญให้กับตัวแปร ch
// ถ้า ch มีค่า '' และ stack ไม่ว่าง
if (ch == ' ' && !stacker.equals("")) {
push(sap, stacker); // push ค่าลงไปใน stack
stacker = ""; // เคลีย stacker
}
// ถ้า pr มีค่าเป็น 0 (เป็นตัวเลข)
else if (pr == 0) {
stacker += ch; // ให้ stacker บวกเท่ากับ ch
}
// ถ้า pr มีค่ามากกว่า 0 (เป็นเครื่องหมาย)
else if (pr > 0) {
long second = pop(sap).toInt(); //popค่าออกมาเก็บไว้ในตัวแปร second
long first = pop(sap).toInt(); //popค่าออกมาเก็บไว้ในตัวแปร first
long answer = 0;
if (ch == '+') { // เงื่อนไขเป็นบวก
answer = first + second; // นำตัวแปร first บวก second แล้วเก็บไว้ในตัวแปร answer
}
else if (ch == '-') { // เงื่อนไขเป็นลบ
answer = first - second; // นำตัวแปร first ลบ second แล้วเก็บไว้ในตัวแปร answer
}
else if (ch == '*') { // เงื่อนไขเป็นคูณ
answer = first * second; // นำตัวแปร first คูณ second แล้วเก็บไว้ในตัวแปร answer
}
else if (ch == '%') { // เงื่อนไขเป็นหารเอาเศษ
answer = first % second; // นำตัวแปร first หาร second แล้วนำเศษส่วนเก็บไว้ในตัวแปร answer
}
else if (ch == '/') { // เงื่อนไขเป็นหาร
if (second != 0) { // ถ้าส่วนไม่ใช่ 0
answer = first / second; // นำตัวแปร first หาร second แล้วเก็บไว้ในตัวแปร answer
}
else // ถ้าส่วนเป็น 0 ค่าที่ได้จะเยอะมากๆ
Serial.println("ERROR");
}
else if (ch == '^') { // เงื่อนไขเป็นยกกำลัง
// นำตัวแปร first ยกกำลัง second แล้วเก็บไว้ในตัวแปร answer( +1 เพราะเป็นการคำนวณแบบ binary)
answer = pow(first, second) + 1;
}
push(sap, (String)answer); // push answer ลงไปใน stack
}
}
if (!stacker.equals("")) // ถ้า stack ไม่ว่าง
push(sap, stacker); // push stacker ลงไปใน stack
Serial.print("ANSWER: ");
Serial.println(pop(sap)); // แสดงคำตอบที่คำนวณออกมา
Serial.println("----- DONE! PLEASE EXIT OR NEXT INPUT -----");
postfix = ""; // เคลีย postfix
state = 0; // ให้ state เท่ากับ 0
}
}
ฟังก์ชัน
int priority(char key) { // Function ลำดับความสำคัญ
if (key > 47 && key < 58) { // เช็คว่า key เป็นตัวเลข 0-9
return 0; //คืนค่า 0
}
else { // ถ้าไม่ใช่ตัวเลข
switch (key) { //กำหนดเคส
case '^': // ยกกำลัง มีค่าความสำคัญสูงสุด
return 4; // คืนค่า 4
case '%':
case '*':
case '/':
return 3; // คืนค่า 3
case '+':
case '-':
return 2; // คืนค่า 2
case '(':
case ')':
return 1; // คืนค่า 1 มีค่าความสำคัญต่ำสุด
default: // กำหนดให้ค่าเดิมตือ
return -1; // คืนค่า -1
}
}
}