아두이노프로세싱 프로그래밍

아두이노 NodeMCU HTML+Javascript 계산기 프로그램

coding art 2017. 3. 8. 20:34
728x90


아두이노를 가지고 단지 센서 데이터 모니터링 만 할 게 아니라 계산 지능이 필요해 보이는 Calculator 프로그램을 작성해 보기로 하자.


이 계산기 프로그램으로 말할 것 같으면 전산 전공하는 영역에서는 알고리듬 공부 차 거쳐 가야 하는 필수 예제 중의 하나로 알려져 있다.


인터넷에서 “아두이노 계산기”로  검색을 해보면 4X4 키패드 나 스위치를 이용 배선하여 계산기를 만든 사례들이 있으며 이런 간단한 계산기를 만드는데 들어가는 배선 량이 어마어마하여 배선들이 머리카락 다발처럼 엉켜있는 재미있는 사진을 볼 수 있다.


하지만 아두이노도 컴퓨터로서 꿀릴 것이 전혀 없는 컴퓨터이므로 좀 더 깔끔하면서 지능을 가진 계산기 프로그램을 작성하여 보여 줌으로 인해서 미래에 사물인터넷 분야에서 활약하게될 아두이노의 엄청난 능력을 보여주기로 하자. 


____________________________________________________________________________________________________________________

스팀잇에서 넘어오신 분들은 여기서 부터 계속 읽으세요.


계산기에 필요한 준비물은 아두이노 NodeMCU 보드와 PC와 연결을 위한 USB 케이블만 있으면 된다.

아두이노에서 LED on OFF 사물인터넷 프로그램을 작성할 정도의 수준이라면 아울러 본 블로그에서 소개했던 키보드 프로그램을 충분히 소화할 수 있는 정도라면 계산기 프로그램 작성은 정도는 그다지 문제가 되지는 않을 것이다.


프로그램 구조는 지난번 소개했던 키보드 프로그램에서 계산기에 필요한 키들을 골라 버튼들을 재배치하였다.
계산기 프로그램의 성능을 공학용 계산기가 아닌 대수학적 계산기로 설정하였다. 즉 +,-,*,/ 및 괄호 계산을 수행하는 것으로 하였다.


필요한 버튼 키들은 숫자 0,1,2,⚫⚫⚫,9 와 소수점 표현을 위한 점 “.” 그리고 사칙 연산을 위한 +,-,*,/ 및 괄호 계산을 위해서 “(” 와 “)”가 필수적이다. 아울러 C는 클리어 즉 아무것도 하지 않았거나 앞의 계산 기록을 지우는데 사용된다.


     client.println("<a href=\"/KEY=C\"\" class='button'> C </button></a>");
     client.println("<a href=\"/KEY=0\"\" class='button'> 0</button></a>");
     client.println("<a href=\"/KEY=1\"\" class='button'> 1 </button></a>");
⚫⚫⚫
     client.println("<a href=\"/KEY=/\"\" class='button'> / </button></a>");
     client.println("<a href=\"/KEY=(\"\" class='button'> ( </button></a>");
     client.println("<a href=\"/KEY=)\"\" class='button'> ) </button></a>");


프로그램을 실행하면 상기의 버튼 터치 프로그램에 의해서 입력된 숫자나 연산자들이 스마트폰 웹브라우저 주소창을 통해 아두이노 NodeMCU  프로그램에 request 형태로 입력된다.  입력된 문자를 시리얼 모니터에 출력하여 프로그램 실행 과장을 모니해 보도록 한다.


// Read the first line of the request
   String request = client.readStringUntil('\r');
   Serial.println(request);


아울러 데이터 입력에 의한 request 말고도 favicon.ico에 의한 request도 들어오는 수 가 있으므로 확인되면 배제하고 계산기 자판 입력에 의한 새로운 request를 기다리도록 한다.


// favicon.ico 배제
   if(request.substring(5,16) != "favicon.ico") {
   client.flush();


사실 HTML 및 Javascript로 짠 계산 프로그램을 스마트폰 웹브라우저에 아웃소싱 형태로 넘겨주고 아두이노는 아무런 request도 받지 않는 농땡이 형태의 프로그램을 작성할 수 도 있을 것이나 이런 프로그램은 아무런 도움이 되지 않는다. NodeMCU만 하드라도 80MHz 의 엄청난 클럭 주파수를 가지지기 때문에 최대한 가능한 한 많이 부려먹는 방향의 프로그램을 작성하도록 해야 할 것이다.


그 다음 키 보드 버튼 수 에 해당하는 만큼의 if 문을 사용하여 계산기에 입력된 문자를 체크하도록 하자. 입력된 문자가 체크되면 문자로 정의된 변수 c에 추가시킨다. 이러한 과정에 의하여 일종의 연산 식 표현이 얻어질 수 있다. 현재까지의 입력으로 인해 연산이 가능하면 그때 그때 Answer 에 계산 결과가 나타난다. 하지만 괄호를 사용하는 경우 괄호를 열면 닫아 줄때까지 연산 식 표현만 출력하는 상태가 되며, 괄호를 닫아 주는 순간  Answer에 계산결과를 출력해 주게 된다.


// Match the request: Key board input processing
     if (request.indexOf("/KEY=1") != -1)  {
     c += "1";
     Serial.println(c);}
      if (request.indexOf("/KEY=2") != -1)  {
     c += "2";
     Serial.println(c);}
      if (request.indexOf("/KEY=3") != -1)  {
      c += "3";
⚫⚫⚫
    if (request.indexOf("/KEY=(") != -1)  {
     c += "(";
     Serial.println(c);}
    if (request.indexOf("/KEY=)") != -1)  {
     c += ")";
     Serial.println(c);}


프로그램을 실행해 보면 프로그램에서 입력하지는 않았지만 때때로 Undefined 라는 멘트가 자동적으로 나오는데 유의하자. 사용해 보니 웹브라우저 자체의 지능에 의해서 제공되는 정보로 보인다.
특히 상기의 예제는 두 개의 괄호 연산이 포함되어 있음에 유의하고 필요하다면 몇 개까지의 괄호가 인식될 수 있는지 각자 시험해 보도록 한다.

연이어 나오는 프로그램 내용 중 CSS 관련된 부분은 일종의 디자인 장식이므로 프로그램의 알고리듬 상 그다지 중요도는 없어 보이며 그대로 둬도 무방하다.


계산기 프로그램의 핵심 부분은 역시 HTML에서 좀 더 Javascript로 넘어간 부분에서 나온다.  프로그램을 Javascript로 써 보면 다음과 같이 요약된다. 즉 c 라는 문자열 데이터를 Javascript 함수 즉 eval(c)를 수행하는 것이 계산기 알고리듬의 핵심이다. 아두이노 프로그램을 작성하는 프로그래머라면 함수 eval()에 해당하는 루틴을 충분히 작성할 수 가 있으리라 보지만 개개인들이 워낙 바쁘고 아두이노 또한 메모리 용량이 쥐새끼 꼬리만큼 밖에 안 되기 때문에 Javascript 언어가 제공하는 도구를 그냥 쓰면 될 것이다. 이 eval()이라는 함수는 괄호 안에 문자열 형태로 연산 식을 써 주면 알아서 연산을 해 주는 기능이 있다.


      <script>
      res = eval(c);
      document.write('  Answer '+ res );
      </script> 


아래의 프로그램에서 <p>와 </p> 즉 문단 태그 사이에 Javascript 프로그램이 위치하는데, 스마트 폰 웹브라우저 프로그램을 작성하는 프로그래머는 아두이노 프로그램에서 상기의 자바스크립트가 어떻게 변형되어 들어가는지 유의할 필요가 있을 것이다. 지금까지 블로그에서 제시되었던 여러 가지 용용 프로그램을 살펴보면 아두이노 NodeMCU 측에서 다루었던 변수 값들을 스마트폰 웹 브라우저에 어떻게 간단하게 정확한 형태로 전달하느냐에 달려있었다는 점을 알 수 있을 것이다.

      client.println("<p>");
      client.println(c);
      client.println("<script>");
      client.println("res = eval(");
      client.println(c);
      client.println(");");
      client.println("document.write('  Answer '+ res );");
      client.println("</script>");  
      client.println("</p>");


Webseerver_nodemcu_IOT_keybd_calc_01

 #include <ESP8266WiFi.h>
 
 const char* ssid = "AndroidHotspot1234";//자신의 스마트폰 핫스팟 ID
 const char* password = "00000000";//자신의 스마트폰 핫스팟 비밀번호

 WiFiServer server(80);
 String c;
 String enter;
 void setup() {
   Serial.begin(115200);
   delay(10);

   // Set WiFi to station mode and disconnect from an AP if it was previously connected
   WiFi.mode(WIFI_STA);
   WiFi.disconnect();
   delay(100);

   Serial.println("Setup done");

   // Connect to WiFi network
   Serial.println();
   Serial.println();
   Serial.print("Connecting to ");
   Serial.println(ssid);

   WiFi.begin(ssid, password);

   while (WiFi.status() != WL_CONNECTED) {
     delay(500);
     Serial.print(".");
   }
   Serial.println("");
   Serial.println("WiFi connected");

   // Start the server
   server.begin();
   Serial.println("Server started");

   // Print the IP address
   Serial.print("Use this URL to connect: ");
   Serial.print("http://");
   Serial.print(WiFi.localIP());
   Serial.println("/");

 }

 void loop() {
 
// Check if a client has connected
   WiFiClient client = server.available();
   if (!client) {
     return;
   }

// Wait until the client sends some data
   Serial.println("new client");
   while(!client.available()){
     delay(1);
   }


// Read the first line of the request
   String request = client.readStringUntil('\r');
   Serial.println(request);

// favicon.ico 배제
   if(request.substring(5,16) != "favicon.ico") {
   client.flush();

// Match the request: Key board input processing
     if (request.indexOf("/KEY=1") != -1)  {
     c += "1";
     Serial.println(c);}
      if (request.indexOf("/KEY=2") != -1)  {
     c += "2";
     Serial.println(c);}
      if (request.indexOf("/KEY=3") != -1)  {
      c += "3";
     Serial.println(c);}
      if (request.indexOf("/KEY=4") != -1)  {
      c += "4";
     Serial.println(c);}
   if (request.indexOf("/KEY=5") != -1)  {
    c += "5";
     Serial.println(c);}
   if (request.indexOf("/KEY=6") != -1)  {
    c += "6";
     Serial.println(c);}
   if (request.indexOf("/KEY=7") != -1)  {
    c += "7";
     Serial.println(c);}
   if (request.indexOf("/KEY=8") != -1)  {
    c += "8";
     Serial.println(c);}
   if (request.indexOf("/KEY=9") != -1)  {
    c += "9";
     Serial.println(c);}
   if (request.indexOf("/KEY=0") != -1)  {
    c += "0";
     Serial.println(c);}
   if (request.indexOf("/KEY=.") != -1)  {
    c +=".";
     Serial.println(c);}  
   if (request.indexOf("/KEY=C") != -1)  {
    c += "C";
    c = "";
     Serial.println(c);}  
   if (request.indexOf("/KEY=+") != -1)  {
     c += "+";
     Serial.println(c);}
   if (request.indexOf("/KEY=-") != -1)  {
     c += "-";
     Serial.println(c);}
   if (request.indexOf("/KEY=*") != -1)  {
     c += "*";
     Serial.println(c);}
   if (request.indexOf("/KEY=/") != -1)  {
     c += "/";
     Serial.println(c);}
   if (request.indexOf("/KEY=(") != -1)  {
     c += "(";
     Serial.println(c);}
   if (request.indexOf("/KEY=)") != -1)  {
     c += ")";
     Serial.println(c);}
 
// Return the response
   client.println("HTTP/1.1 200 OK");
   client.println("Content-Type: text/html");
   client.println(""); //  do not forget this one
   client.println("<!DOCTYPE HTML>");
   client.println("<html>");
   client.println("<head>");
//배경 색 문자 색 사이즈 HTML CSS 설정  
   client.println("<style>");
   client.println("body {");
   client.println("background-color: lightblue;");
   client.println("}");
   client.println("p {");
   client.println("color:red;");
   client.println("font-size:280%;");
   client.println("}");  
//버튼 HTML CSS 설정  
    client.println(".button {");
    client.println("background-color: white;");
    client.println("border: solid;");
    client.println("color: black;");
    client.println("padding: 15px 32px;");
    client.println("text-align: center;");
    client.println("text-decoration: none;");
    client.println("display: inline-block;");
    client.println("font-size: 50px;");
    client.println("margin: 2px 2px;");
    client.println("cursor: pointer;");
    client.println("}");   
   client.println("</style>");
   client.println("<title>Page Title</title>");
   client.println("<h1> Arduino AI Calc</h1>"); 
   client.println("</head>");
   client.println("<body>");
      client.println("<p>");
      client.println(c);
      client.println("<script>");
      client.println("res = eval(");
      client.println(c);
      client.println(");");
      client.println("document.write('  Answer '+ res );");
      client.println("</script>");  
     client.println("</p>");
  
// Key board input
    client.println("<p>");
     client.println("<a href=\"/KEY=C\"\" class='button'> C </button></a>");
     client.println("<a href=\"/KEY=0\"\" class='button'> 0</button></a>");
     client.println("<a href=\"/KEY=1\"\" class='button'> 1 </button></a>");
     client.println("<a href=\"/KEY=2\"\" class='button'> 2 </button></a>");
     client.println("<a href=\"/KEY=3\"\" class='button'> 3 </button></a>");
     client.println("<a href=\"/KEY=4\"\" class='button'> 4 </button></a>");  
     client.println("<br>");
     client.println("<a href=\"/KEY=5\"\" class='button'> 5 </button></a>");  
     client.println("<a href=\"/KEY=6\"\" class='button'> 6 </button></a>");
     client.println("<a href=\"/KEY=7\"\" class='button'> 7 </button></a>");
     client.println("<a href=\"/KEY=8\"\" class='button'> 8 </button></a>");
     client.println("<a href=\"/KEY=9\"\" class='button'> 9 </button></a>");
     client.println("<a href=\"/KEY=.\"\" class='button'> . </button></a>");
     client.println("<br>");
     client.println("<a href=\"/KEY=+\"\" class='button'> + </button></a>");
     client.println("<a href=\"/KEY=-\"\" class='button'> - </button></a>");
     client.println("<a href=\"/KEY=*\"\" class='button'> * </button></a>");
     client.println("<a href=\"/KEY=/\"\" class='button'> / </button></a>");
     client.println("<a href=\"/KEY=(\"\" class='button'> ( </button></a>");
     client.println("<a href=\"/KEY=)\"\" class='button'> ) </button></a>");
     client.println("<br>");
    client.println("</p>");
    client.println("</body>");
    client.println("</html>");

   delay(1);
   Serial.println("Client disonnected");
   Serial.println("");
 }// if문 괄호 닫기
 }//프로그램 끝