본문 바로가기

Arduino

안드로이드 앱을 통해 아두이노 led 제어하기

반응형

처음으로 실습관련 이야기를 작성하게 되는 거 같습니다.

현재 전 직장 동료인 친구와 side project 하나를 준비중에 있습니다.

그런데 제가 아두이노를 손을 안댄지도 오래되었고 살짝 야매로 배워서 개념을 다시 잡고 
side project에 필수 원리가 될 code를 연습용으로 만들어 보았고, 이를 소개해보고자 합니다.

Android studio에서 Arduino NANO 33 IOT 제어하기

1. arduino 회로 설계

우선 가볍게 led만 제어 할 생각이므로 가볍게 설계하면 됩니다. 

아두이노 회로 설계

이런 식으로 led만 연결해줍니다.

 

2. arduino code

#include <SPI.h>
#include <WiFiNINA.h> //라이브러리 다운받아야 함
#include <WiFiClient.h>

const char* ssid = "wifi 이름";
const char* password = "wifi 비밀번호";

int ledPin = 2; // arduino nano 33 iot의 d2에 연결
WiFiServer server(80);

void setup() {
  Serial.begin(115200);
  delay(10);
  if(WiFi.status() == WL_NO_MODULE){
    Serial.println("Communication with WiFi module failed");
  }

  //LED 세팅
  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, LOW);

  //WiFi 연결
  delay(1000);
  Serial.print("Connecting to ");
  Serial.print(ssid);
 
  WiFi.begin(ssid, password);
  while(WiFi.status() != WL_CONNECTED){
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP Address: ");
  Serial.println(WiFi.localIP());

  //Server 시작
  server.begin();
  Serial.println("Server started");
  Serial.print("Use this URL to connect: ");
  Serial.print("http://");
  Serial.print(WiFi.localIP());
  Serial.print("/");
}

void loop() {
  // put your main code here, to run repeatedly:
  //client 접속 확인
  WiFiClient client = server.available();
  if(!client){
    return;
  }

  //client가 보내는 데이터를 기다린다.
  Serial.println("new client");
  while(!client.available()){
    delay(1);
  }

 //요청을 읽는다.
 String request = client.readStringUntil('\r');
 Serial.println(request);
 client.flush();

 //요청에 따라 LED를 ON/Off
 if (request.indexOf("/ledOn") > 0){
  digitalWrite(ledPin, HIGH);
  Serial.println("real on"); //just test code
 }
 if (request.indexOf("/ledOff") > 0){
  digitalWrite(ledPin, LOW);
  Serial.println("real off"); //just test code
 }
  delay(1);
}
 
* 설명은 주석으로 대체합니다.
 

3. android studio code

1) build gradle(module)

viewBinding {
    enabled = true
}

우선 저는 binding을 좋아하니깐 추가해줍시다.

 

implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'com.squareup.retrofit2:retrofit:2.9.0'

저는 전에도 많은 글을 썼지만 retrofit2를 이용한 rest 구현을 좋아하므로 추가해줍니다.

 

2) manifast.xml

<uses-permission android:name="android.permission.INTERNET" />

internet을 이용한 통신을 해야하므로 permission을 추가해줍니다.

 

만약 최종적으로 debuging할 때 오류가 생긴다면 

android:usesCleartextTraffic="true"

해당 코드를 추가해줍시다.

 

3) activity_main.xml

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="LED 제어하기"
            android:textSize="30dp"
            android:textColor="@color/black"
            android:layout_margin="10dp" />

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

            <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:id="@+id/led_on"
                android:layout_margin="5dp"
                android:text="on" />

            <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:id="@+id/led_off"
                android:layout_margin="5dp"
                android:text="off" />

        </LinearLayout>

    </LinearLayout>



</androidx.constraintlayout.widget.ConstraintLayout>

어차피 test용이기 때문에 최대한 간단하고 빠르게 작성해줍니다.

 

4)  data model

package com.example.basil

import com.google.gson.annotations.SerializedName

data class BasilModel(

    @SerializedName("ledOnOff")
    val ledControll: String,
)

어차피 arduino에 전달해 줘야 할 값은 text 1개이므로 string 변수 1개를 만들어 줍니다.
* data는 1개만 있으므로 @serializedName은 생략을 추천드립니다.

 

5) api 설계

package com.example.basil

import retrofit2.Call
import retrofit2.Response
import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.POST

interface BasilAPI {

    @POST("post")
    suspend fun controllLed(@Body basilModel: BasilModel):Response<Void>

}

retrofit2를 이용하여 기존에 정의한 string 변수를 arduino로 post합니다.

 

6) object 설계

package com.example.basil

import com.example.basil.BasilObject.BASE_URL
import com.google.gson.GsonBuilder
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory

object BasilObject {
    private const val BASE_URL = "arduino nano 33 iot의 ip주소"

    private val getRetrofitBasil by lazy {
        Retrofit.Builder()
            .baseUrl(BasilObject.BASE_URL)
            .addConverterFactory(GsonConverterFactory.create(GsonBuilder().setLenient().create()))
            .build()
    }
    val getRetrofitBasilService : BasilAPI by lazy { getRetrofitBasil.create(BasilAPI::class.java) }


    fun getInstance(): BasilAPI? {
        return getRetrofitBasilService
    }
}

사실 object 설계의 의의는 객체지향을 추구하고자 중복된 코딩을 최소화 하기 위해서입니다.

 

7) MainActivity.kt

package com.example.basil

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import com.example.basil.databinding.ActivityMainBinding
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import retrofit2.Response

class MainActivity : AppCompatActivity() {

    lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        binding.ledOn.setOnClickListener {
            val text = "/ledOn"
            sendText(text)
        }

        binding.ledOff.setOnClickListener {
            val text = "/ledOff"
            sendText(text)
        }
    }
    private fun sendText(text: String) {
        val model = BasilModel(text)
        GlobalScope.launch(Dispatchers.IO) {
            val response = BasilObject.getRetrofitBasilService.controllLed(BasilModel(text.toString()))
            if (response.isSuccessful){
                Log.d("success","data sent successfully")
            } else {
                Log.e("error","failed to send data")
            }
        }

    }


}

arduino code에서 client가 text로 ledon or ledoff를 보내면 조건이 성립되게 정의 했으므로

button 선택에 따라 text값을 다르게 보내면 성공입니다.

 

4. 최종 완성본

KakaoTalk_20230415_131409669.mp4
4.93MB

해당 영상을 보시면 android studio에서 on/off button을 누르면 arduino의 led가 상황에 맞춰 on/off됩니다.

 

5. 이 글을 마무리하며

앞으로 해당 code는 side project를 진행하며 조금씩 수정하며 사용할 생각입니다.
또한 side project를 진행하며 생기는 과정에서 유익한 부분은 계속해서 블로그에 올릴 생각입니다.

 

이번에 실습 내용을 처음으로 작성해보았는데 분량이 정말 길게 되더라고요 

긴 글 읽어 주셔서 감사합니다.

반응형