반응형

안드로이드 앱에서  ESP8266 WiFi 모듈을 연결한 Arduino Uno에 명령을 내려서 LED를 제어하는 과정을 설명합니다. 



최초 작성 2015.11.14

최종 작성 2023. 3. 20 - 아두이노 코드 오류 수정정




ESP8266 WiFi 모듈을 Arduino Uno에 연결하는 방법과 모듈을 Arduino Uno에서 사용하기 위해 필요한 WeeESP8266 라이브러리 설치 방법은 아래 포스팅을 참고하세요.

 

Arduino Uno에 연결한 ESP8266 WiFi 모듈을 사용하여 컴퓨터에 구성한 TCP 서버와 통신 테스트한 과정을 설명하고 있어서 먼저 해보는 것도 좋을 듯합니다. 



Arduino Uno에 ESP8266 WiFi 모듈을 연결하여 사용하는 방법

https://webnautes.tistory.com/2071  




이전 포스팅과 달리 Arduino Uno가 서버 역활을 하고 안드로이드 앱이 클라이언트입니다.

안드로이드와 ESP8266 WiFi 모듈을 연결한 Arduino Uno가 같은 네트워크(공유기)인 상황에서 테스트했습니다. 



WeeESP8266 라이브러리에 포함되어 있는 TCPServer 예제 코드를 수정하여 사용했습니다.

원본 코드는 Arduino IDE 메뉴에서  파일 > 예제 > ITEADLIB_Arduino_WeeESP8266 >  TCPServer 를 선택하면 불러올 수 있습니다.



포스팅 마지막에 있는 아두이노 코드를 Arduino IDE에 복사해줍니다.

상단에 있는 다음 2줄을 환경에 맞게 수정한 후,  Arduino Uno에 업로드시켜줍니다.

안드로이드 폰에서도 사용중인 공유기 접속 정보를 입력해줘야 합니다. 

#define SSID        "공유기의 SSID" 
#define PASSWORD    "공유기의 비밀번호"  





접속 과정을 확인하기 위해 Arduino IDE의 메뉴에서 툴 > 시리얼 모니터를 선택합니다. 

 

공유기로부터 IP를 할당 받았다면 Join AP success 문자열과 함께 할당받은 IP를 출력해줍니다.

안드로이드 앱에서 접속시 사용해야 하므로 IP: 항목에 출력된 IP 중 두번째 아이피를 안드로이드 코드에 넣어 주어야 합니다. 

 

        new Thread(new ConnectThread("192.168.25.20", 8090)).start();



아두이노 코드에서 설정한 포트로 접속하는 클라이언트를 위한 준비가 성공하면 start tcp server ok  문자열을 보여줍니다. 

 




안드로이드앱에서 명령을(Me:)  전송하여 Arduino Uno 13번핀에 연결되어 있는 내장 LED를 제어할 수 있습니다.

명령을 받은 아두이노에서 수행한 작업 내용을(/192.168.0.7:8090) 다시 안드로이드 앱으로 전송해줍니다.

최근에 내린 명령이나 아두이노의 응답이 위로 올라오도록 되어 있습니다.

 




 Arduino IDE의 시리얼 모니터에서는 안드로이드앱이 보낸 명령(Received from :)이 출력됩니다.

 




아두이노에서 지원하지 않는 명령을 내리면 사용 가능한 명령들을 보내줍니다.

 



현재 해결되지 않은 문제점은 테스트 하다 보면 안드로이드앱에서 명령을 전송했는데 아두이노로부터 응답이 안오는 경우가 있습니다.

대부분 다시 명령을 전송하면 응답이 오지만  가끔 제대로 동작안해서 다시 앱을 실행해야 합니다.

아직 원인을 못찾았습니다. 

 

아두이노 코드에서 사용한 WeeESP8266 라이브러리에서  클라이언트 접속시 감지할 수 있는 방법이 없는 듯합니다.

그래서 아두이노에서 접속 관련 메시지를 클라이언트로 먼저 보내주지 못하고 있습니다. 

대신 안드로이드앱에서 서버에 접속되면 connected to라는 메시지를 상단에 보여줍니다.




안드로이드 코드

AndroidManifest.xml

앱에서 인터넷을 사용하기 위해 필요한 퍼미션을 추가해줍니다.



<?xml version="1.0" encoding="utf-8"?>

    package="com.tistory.webnautes.tcp_client">
   
   <uses-permission android:name="android.permission.INTERNET" />
   
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"





activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:paddingBottom="10dp"
    android:paddingTop="10dp"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="connection state : "/>

        <TextView
            android:id="@+id/connection_status_textview"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text=""/>

    </LinearLayout>

    <LinearLayout
        android:weightSum="1"
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <EditText
            android:id="@+id/input_string_edittext"
            android:hint="input text here"
            android:layout_weight="0.8"
            android:layout_width="0dp"
            android:layout_height="wrap_content"/>

        <Button
            android:id="@+id/send_button"
            android:layout_weight="0.2"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:text="Send" />
    </LinearLayout>

    <ListView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/message_listview"/>

</LinearLayout>




MainActivity.java

package com.tistory.webnautes.tcp_client;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;

import android.content.DialogInterface;
import android.os.Bundle;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;


public class MainActivity extends AppCompatActivity
{

  private TextView mConnectionStatus;
  private EditText mInputEditText;
  private ArrayAdapter<String> mConversationArrayAdapter;

  private static final String TAG = "TcpClient";
  private boolean isConnected = false;

  private String mServerIP = null;
  private Socket mSocket = null;
  private PrintWriter mOut;
  private BufferedReader mIn;
  private Thread mReceiverThread = null;


  @Override
  public void onCreate(Bundle savedInstanceState)
  {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);


      mConnectionStatus = (TextView)findViewById(R.id.connection_status_textview);
      mInputEditText = (EditText)findViewById(R.id.input_string_edittext);
      ListView mMessageListview = (ListView) findViewById(R.id.message_listview);
      Button sendButton = (Button)findViewById(R.id.send_button);
      sendButton.setOnClickListener(new View.OnClickListener(){
          public void onClick(View v){

              String sendMessage = mInputEditText.getText().toString();
              if ( sendMessage.length() > 0 ) {

                  if (!isConnected) showErrorDialog("서버로 접속된후 다시 해보세요.");
                  else {
                      new Thread(new SenderThread(sendMessage)).start();
                      mInputEditText.setText(" ");
                  }
              }
          }
      });


      mConversationArrayAdapter = new ArrayAdapter<>( this,
              android.R.layout.simple_list_item_1 );
      mMessageListview.setAdapter(mConversationArrayAdapter);

      new Thread(new ConnectThread("192.168.0.7", 8090)).start();
  }

  @Override
  protected void onDestroy() {
      super.onDestroy();

      isConnected = false;
  }


  private static long back_pressed;
  @Override
  public void onBackPressed(){

      if (back_pressed + 2000 > System.currentTimeMillis()){
          super.onBackPressed();

          Log.d(TAG, "onBackPressed:");
          isConnected = false;

          finish();
      }
      else{
          Toast.makeText(getBaseContext(), "한번 더 뒤로가기를 누르면 종료됩니다.", Toast.LENGTH_SHORT).show();
          back_pressed = System.currentTimeMillis();
      }

  }


  private class ConnectThread implements Runnable {

      private String serverIP;
      private int serverPort;

      ConnectThread(String ip, int port) {
          serverIP = ip;
          serverPort = port;

          mConnectionStatus.setText("connecting to " + serverIP + ".......");
      }

      @Override
      public void run() {

          try {

              mSocket = new Socket(serverIP, serverPort);
              //ReceiverThread: java.net.SocketTimeoutException: Read timed out 때문에 주석처리
              //mSocket.setSoTimeout(3000);

              mServerIP = mSocket.getRemoteSocketAddress().toString();

          } catch( UnknownHostException e )
          {
              Log.d(TAG,  "ConnectThread: can't find host");
          }
          catch( SocketTimeoutException e )
          {
              Log.d(TAG, "ConnectThread: timeout");
          }
          catch (Exception e) {

              Log.e(TAG, ("ConnectThread:" + e.getMessage()));
          }


          if (mSocket != null) {

              try {

                  mOut = new PrintWriter(new BufferedWriter(new OutputStreamWriter(mSocket.getOutputStream(), "UTF-8")), true);
                  mIn = new BufferedReader(new InputStreamReader(mSocket.getInputStream(), "UTF-8"));

                  isConnected = true;
              } catch (IOException e) {

                  Log.e(TAG, ("ConnectThread:" + e.getMessage()));
              }
          }


          runOnUiThread(new Runnable() {

              @Override
              public void run() {

                  if (isConnected) {

                      Log.d(TAG, "connected to " + serverIP);
                      mConnectionStatus.setText("connected to " + serverIP);

                      mReceiverThread = new Thread(new ReceiverThread());
                      mReceiverThread.start();
                  }else{

                      Log.d(TAG, "failed to connect to server " + serverIP);
                      mConnectionStatus.setText("failed to connect to server "  + serverIP);
                  }

              }
          });
      }
  }


  private class SenderThread implements Runnable {

      private String msg;

      SenderThread(String msg) {
          this.msg = msg;
      }

      @Override
      public void run() {

          mOut.println(this.msg);
          mOut.flush();

          runOnUiThread(new Runnable() {
              @Override
              public void run() {
                  Log.d(TAG, "send message: " + msg);
                  mConversationArrayAdapter.insert("Me - " + msg, 0);
              }
          });
      }
  }


  private class ReceiverThread implements Runnable {

      @Override
      public void run() {

          try {

              while (isConnected) {

                  if ( mIn ==  null ) {

                      Log.d(TAG, "ReceiverThread: mIn is null");
                      break;
                  }

                  final String recvMessage =  mIn.readLine();

                  if (recvMessage != null) {

                      runOnUiThread(new Runnable() {

                          @Override
                          public void run() {

                              Log.d(TAG, "recv message: "+recvMessage);
                              mConversationArrayAdapter.insert(mServerIP + " - " + recvMessage, 0);
                          }
                      });
                  }
              }

              Log.d(TAG, "ReceiverThread: thread has exited");
              if (mOut != null) {
                  mOut.flush();
                  mOut.close();
              }

              mIn = null;
              mOut = null;

              if (mSocket != null) {
                  try {
                      mSocket.close();
                  } catch (IOException e) {
                      e.printStackTrace();
                  }
              }
          }
          catch (IOException e) {

              Log.e(TAG, "ReceiverThread: "+ e);
          }
      }

  }


  public void showErrorDialog(String message)
  {
      AlertDialog.Builder builder = new AlertDialog.Builder(this);
      builder.setTitle("Error");
      builder.setCancelable(false);
      builder.setMessage(message);
      builder.setPositiveButton("OK"new DialogInterface.OnClickListener() {
          @Override
          public void onClick(DialogInterface dialog, int which) {
              dialog.dismiss();
              finish();
          }
      });
      builder.create().show();
  }


}




 

아두이노 코드

#include "ESP8266.h"
#include <SoftwareSerial.h>

#define SSID        "공유기의 SSID" 
#define PASSWORD    "공유기의 비밀번호"


SoftwareSerial mySerial(10, 9); /* RX:D10, TX:D9 */
ESP8266 wifi(mySerial);


void printUsage(uint8_t mux_id)
{
    char buf[]="사용가능한 명령어\n1 : LED 켜기\n2 : LED 끄기\nS : LED 상태정보\n\n";
    wifi.send(mux_id, buf, strlen(buf));
}


void setup(void)
{
    Serial.begin(9600);
    Serial.print("setup begin\r\n");
   
    Serial.print("FW Version:");
    Serial.println(wifi.getVersion().c_str());
     
    if (wifi.setOprToStationSoftAP()) {
        Serial.print("to station + softap ok\r\n");
    } else {
        Serial.print("to station + softap err\r\n");
    }

    if (wifi.joinAP(SSID, PASSWORD)) {
        Serial.print("Join AP success\r\n");
        Serial.print("IP: ");
        Serial.println(wifi.getLocalIP().c_str());   
    } else {
        Serial.print("Join AP failure\r\n");
    }
   
    if (wifi.enableMUX()) {
        Serial.print("multiple ok\r\n");
    } else {
        Serial.print("multiple err\r\n");
    }
   
    if (wifi.startTCPServer(8090)) {
        Serial.print("start tcp server ok\r\n");
    } else {
        Serial.print("start tcp server err\r\n");
    }
   
    if (wifi.setTCPServerTimeout(360)) {
        Serial.print("set tcp server timout 360 seconds\r\n");
    } else {
        Serial.print("set tcp server timout err\r\n");
    }
   
    Serial.print("setup end\r\n");

    pinMode(LED_BUILTIN, OUTPUT);
}

void loop(void)
{
    uint8_t buffer[128] = {0};
    uint8_t mux_id;
    uint32_t len = wifi.recv(&mux_id, buffer, sizeof(buffer), 100);
   
    if (len > 0) {
        Serial.print("Status:[");
        Serial.print(wifi.getIPStatus().c_str());
        Serial.println("]");
       
        Serial.print("Received from :");
        Serial.print(mux_id);
        Serial.print("[");


        Serial.print("Received:[");
        for(uint32_t i = 0; i < len; i++) {
            Serial.print((char)buffer[i]);
        }
        Serial.print("]\r\n");


        char command = buffer[0];
        int ledStatus = digitalRead(LED_BUILTIN);
       

        switch (command){
       
            case '1':
             
              if (ledStatus == LOW){
                digitalWrite(LED_BUILTIN, HIGH);
                sprintf(buffer, "LED가 켜졌습니다.\n");
                wifi.send(mux_id, buffer, strlen(buffer));
              }
              else{
                sprintf(buffer, "이미 LED가 켜져있습니다.\n");
                wifi.send(mux_id, buffer, strlen(buffer));
              }
              break;
       
            case '2':
           
              if (ledStatus == HIGH){
                digitalWrite(LED_BUILTIN, LOW);
                sprintf(buffer, "LED가 꺼졌습니다.\n");
                wifi.send(mux_id, buffer, strlen(buffer));
              }
              else{
                sprintf(buffer, "이미 LED가 꺼져있습니다.\n");
                wifi.send(mux_id, buffer, strlen(buffer));
              }
              break;
       
            case 'S':
            case 's':
               
              if (ledStatus == LOW){
                sprintf(buffer, "LED 상태: 꺼짐\n");
                wifi.send(mux_id, buffer, strlen(buffer));
              }
              else {
                sprintf(buffer, "LED 상태: 켜짐\n");
                wifi.send(mux_id, buffer, strlen(buffer));
              }
              break;
             
            default:
                printUsage(mux_id);
              break;
             
        }     
    }
       
}




     




반응형

문제 발생시 지나치지 마시고 댓글 남겨주시면 가능한 빨리 답장드립니다.

도움이 되셨다면 토스아이디로 후원해주세요.
https://toss.me/momo2024


제가 쓴 책도 한번 검토해보세요 ^^

+ Recent posts