안드로이드 앱에서 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; } } } |
'Arduino Uno > WIFI(ESP8266)' 카테고리의 다른 글
Arduino UNO에서 ESP8266 WiFi 모듈을 사용하는 방법 (0) | 2023.10.18 |
---|---|
Arduino Uno에서 ESP8266을 이용하여 MYSQL에 온도값(DB18B20) 넣기 (252) | 2018.10.17 |
시간날때마다 틈틈이 이것저것 해보며 블로그에 글을 남깁니다.
블로그의 문서는 종종 최신 버전으로 업데이트됩니다.
여유 시간이 날때 진행하는 거라 언제 진행될지는 알 수 없습니다.
영화,책, 생각등을 올리는 블로그도 운영하고 있습니다.
https://freewriting2024.tistory.com
제가 쓴 책도 한번 검토해보세요 ^^
그렇게 천천히 걸으면서도 그렇게 빨리 앞으로 나갈 수 있다는 건.
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!