본문 바로가기

스마트폰으로 간단하게 Arduino 제어하기


Arduino Ethernet Shield 가 있다면, 아주 쉽게 해 볼 수 있다.

일단, 앱은 아래 링크에서 다운로드.


안드로이드 앱 : https://play.google.com/store/apps/details?id=kr.co.wiznet.app_tcp


아주 쉬운 메시지 구조로 만들어, 아주 간단하게 동작을 테스트 해 볼 수 있도록 하는 것이 이 앱의 목적이다.


앱의 소개는 아래 링크를 참고하세요.


스마트폰으로 간단하게 Arduino 제어하기 - 어플 소개https://ts.devbj.com/570 )


일단 설치 했다면,


이제 아두이노 코딩을 조금......일단 아두이노 개발 환경이 있다면 


Ethernet Library 를 2.0 으로 업그레이드 하기 바란다. 이 버전 부터 자동으로 이더넷쉴드를 인지하여 있다면 그 쉴드에 맞게 Ethernet 기능을 쓸 수 있도록 구현되어 있다.


그럼, 이제 본격적인 스마트폰을 이용한 데모작업을 해야 하는데, 소스코드에 구현된 내용은 아래와 같다.


  1. NeoPixel LED를 이용하여 스마트폰에서 받은 RGB 값을 이용하여 제어되는 간단한 코드를 작성해 보았다.
  2. 시리얼 터미널이 연결되어 있으면, 
    1. 스마트폰 앱에서 보내는 텍스트를 시리얼 터미널로 출력하도록 해 두었다.
    2. 시리얼 터미널에 원하는 텍스트를 엔터를 치면 그 문장이 저장되었다가 스마트폰 앱이 "Refresh All" 버튼을 누르면 데이터를 보내 앱에 표시가 된다.

실험 보드 사진을 간단히 허접 폰카로 찍어 업로드 해본다.

그래도 나름 신종 아두이노 보드인 MKR Zero + MKR Ethernet Shield 제품으로 구성했다. ^-----^v

아래 gist를 참고해서 각자 다운 받아 돌려보면 된다.


각각 I/O 포트에 간단한 led나 버튼 같은 것을 원하는 대로 붙여서 소스를 조금 수정하면 왠만한 간단한 스마트폰 제어 응용은 마음대로 만들 수 있을........................껄

/*
WIZnet IoT Tool Example
A simple example supports remote contorlling and monitoring function with smartphone application.
To use, download WIZnet IoT Tool from Google play store, Apple Apps Store.
https://play.google.com/store/apps/details?id=kr.co.wiznet.app_tcp
You can see the client's input in the serial monitor as well.
Using an Arduino Wiznet Ethernet shield.
THis version attempts to get an IP address using DHCP
Circuit:
Ethernet shield attached to pins 10, 11, 12, 13
created 20 Nov 2018 by Bongjun Hur(bjnhur@gmail.com)
Based on DhcpChatServer example
*/
#include <SPI.h>
#include <Ethernet.h>
// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network.
// gateway and subnet are optional:
byte mac[] = {
0x00, 0xAA, 0xBB, 0xCC, 0xDE, 0x02
};
IPAddress ip(192, 168, 1, 177);
IPAddress myDns(192, 168, 1, 1);
IPAddress gateway(192, 168, 1, 1);
IPAddress subnet(255, 255, 255, 0);
unsigned int localPortUDP = 5000;
unsigned int localPortTCP = 5001;
#define BUFFER_SIZE 100
char receivePacket[BUFFER_SIZE];
char sendPacket[BUFFER_SIZE];
char UDPpacketBuffer[BUFFER_SIZE]; //buffer to hold incoming packet,
EthernetUDP Udp;
EthernetServer server(localPortTCP);
// Current version of WizAPP can support 6 unit (byte value) and 1 string unit.
byte ByteUnitVal[6];
char SerialBuf[80];
String strFromSerial; // saving 1 line string
byte rCmd = 0;
byte rID = 0;
byte rVal = 0;
// Use NeoPixel Library & example
#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
#include <avr/power.h>
#endif
// Which pin on the Arduino is connected to the NeoPixels?
// On a Trinket or Gemma we suggest changing this to 1
#define PIN 6
// How many NeoPixels are attached to the Arduino?
#define NUMPIXELS 2
// When we setup the NeoPixel library, we tell it how many pixels, and which pin to use to send signals.
// Note that for older NeoPixel strips you might need to change the third parameter--see the strandtest
// example for more information on possible values.
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);
void setup()
{
// You can use Ethernet.init(pin) to configure the CS pin
//Ethernet.init(10); // Most Arduino shields
Ethernet.init(5); // MKR ETH shield
//Ethernet.init(0); // Teensy 2.0
//Ethernet.init(20); // Teensy++ 2.0
//Ethernet.init(15); // ESP8266 with Adafruit Featherwing Ethernet
//Ethernet.init(33); // ESP32 with Adafruit Featherwing Ethernet
// Open serial communications and wait for port to open:
Serial.begin(9600);
// while (!Serial) {
// ; // wait for serial port to connect. Needed for native USB port only
// }
// start the Ethernet connection:
Serial.println("Trying to get an IP address using DHCP");
if (Ethernet.begin(mac) == 0) {
Serial.println("Failed to configure Ethernet using DHCP");
// Check for Ethernet hardware present
if (Ethernet.hardwareStatus() == EthernetNoHardware) {
Serial.println("Ethernet shield was not found. Sorry, can't run without hardware. :(");
while (true) {
delay(1); // do nothing, no point running without Ethernet hardware
}
}
if (Ethernet.linkStatus() == LinkOFF) {
Serial.println("Ethernet cable is not connected.");
}
// initialize the Ethernet device not using DHCP:
Ethernet.begin(mac, ip, myDns, gateway, subnet);
}
Ethernet.MACAddress(mac); // fill the MAC buffer
Serial.print("The MAC address is: ");
for (byte octet = 0; octet < 6; octet++) {
Serial.print(mac[octet], HEX);
if (octet < 5) {
Serial.print('-');
}
}
Serial.println();
// print your local IP address:
ip = Ethernet.localIP(); // update the IP address buffer
Serial.print("My IP address: ");
Serial.println(Ethernet.localIP());
Serial.print("My TCP listen port number: ");
Serial.println(localPortTCP);
// start listening for clients
Udp.begin(localPortUDP);
server.begin();
// This initializes the NeoPixel library.
// This is for Trinket 5V 16MHz, you can remove these three lines if you are not using a Trinket
#if defined (__AVR_ATtiny85__)
if (F_CPU == 16000000) clock_prescale_set(clock_div_1);
#endif
// End of trinket special code
pixels.begin(); // This initializes the NeoPixel library.
}
void loop()
{
Network_search_run();
WizAPP_control_run();
User_application_run();
}
void User_application_run()
{
// application for RGB remote control
RGB_NeoPixel(ByteUnitVal[0], ByteUnitVal[1], ByteUnitVal[2]);
// application for simple text chat
if (readline(Serial.read(), SerialBuf, 80) > 0) {
strFromSerial = String(SerialBuf);
Serial.print("Your message saved >>");
Serial.print(strFromSerial);
Serial.println("");
}
}
void Network_search_run()
{
// if there's data available, read a packet
int packetSizeUDP = Udp.parsePacket();
if (packetSizeUDP) {
Serial.print("UDP Received packet of size : ");
Serial.println(packetSizeUDP);
Serial.print("From ");
IPAddress remote = Udp.remoteIP();
for (int i = 0; i < 4; i++) {
Serial.print(remote[i], DEC);
if (i < 3) {
Serial.print(".");
}
}
Serial.print(", port ");
Serial.println(Udp.remotePort());
// read the packet into packetBufffer
Udp.read(UDPpacketBuffer, BUFFER_SIZE);
switch (UDPpacketBuffer[0]) {
case 'S':
Serial.println("Getting device search message");
Serial.print("Reply...");
sendPacket[0] = 'N';
sendPacket[1] = 0x0C;
for (byte octet = 0; octet < 6; octet++) {
sendPacket[octet + 2] = mac[octet];
}
for (byte octet = 0; octet < 4; octet++) {
sendPacket[octet + 8] = ip[octet];
}
sendPacket[12] = highByte(localPortTCP);
sendPacket[13] = lowByte(localPortTCP);
Udp.beginPacket(Udp.remoteIP(), Udp.remotePort());
Udp.write(sendPacket, 14);
Udp.endPacket();
Serial.println("Done");
break;
default:
break;
}
}
}
void WizAPP_control_run()
{
int packetSizeTcp = 0;
//int loop_cnt;
// wait for a new client:
EthernetClient client = server.available();
if (client) {
packetSizeTcp = client.available();
if (packetSizeTcp) {
//Serial.print("TCP Received packet of size : ");
//Serial.println(packetSizeTcp);
if (packetSizeTcp < 2) {
// Get the wrong message, clear received data
client.read();
} else {
// min size : 2 bytes (CMD + ID)
rCmd = client.read();
packetSizeTcp--;
rID = client.read();
packetSizeTcp--;
switch (rCmd) {
case 'W': {
if (packetSizeTcp) {
rVal = client.read();
packetSizeTcp--;
WriteDataWizAPP(rID, rVal);
}
break;
}
case 'R': {
sendPacket[0] = 'W'; // Write operation
sendPacket[1] = rID; // id
sendPacket[2] = ReadDataWizAPP(rID); // Value
client.write(sendPacket, 3);
break;
}
case 'G': { // Get string, Simple message sending
if (strFromSerial.length()) { // if any,
// Send PutString message to WIZnet SmartAPP
sendPacket[0] = 'P'; // Put String
sendPacket[1] = (byte)strFromSerial.length(); // len
strFromSerial.toCharArray( &(sendPacket[2]), (sendPacket[1] + 1) ); // because of null, we use (len+1). Otherwise, last character is missing.
//strcpy(&(sendPacket[2]), "No message");
client.write(sendPacket, (sendPacket[1] + 2) );
strFromSerial = ""; // clear saved message
}
else {
// Send empty message to WIZnet IoT Tool
sendPacket[0] = 'P'; // Put String
sendPacket[1] = 0x00; // zero
client.write(sendPacket, 2 );
}
break;
}
case 'P': { // Put string
Serial.print("Message from APP >> ");
// in case of rCMD == 'P', rID is "String Length information"
for (int i = 0; (i < rID && packetSizeTcp); i++) {
Serial.write(client.read());
packetSizeTcp--;
}
Serial.println();
break;
}
default :
// Get the wrong message, clear received data
while (packetSizeTcp) {
client.read();
packetSizeTcp--;
}
break;
}
}
Ethernet.maintain();
}
}
}
void WriteDataWizAPP(byte uid, byte uval)
{
// This example can control RGB value with WIZnet IoT Tool
// So, Red = ByteUnitVal[0], Green = ByteUnitVal[1], Blue = ByteUnitVal[2]
// You can modify this fuction as your application.
if (uid < 6 ) {
ByteUnitVal[uid] = uval;
}
}
byte ReadDataWizAPP(byte uid)
{
// You can modify this fuction as your application.
if (uid < 6 ) {
return ByteUnitVal[uid];
}
return 0;
}
void RGB_NeoPixel(byte red, byte green, byte blue) {
int delayval = 50; // delay for 50ms
// For a set of NeoPixels the first NeoPixel is 0, second is 1, all the way up to the count of pixels minus one.
for (int i = 0; i < NUMPIXELS; i++) {
// pixels.Color takes RGB values, from 0,0,0 up to 255,255,255
pixels.setPixelColor(i, pixels.Color(red, green, blue)); // Moderately bright green color.
pixels.show(); // This sends the updated pixel color to the hardware.
delay(delayval); // Delay for a period of time (in milliseconds).
}
}
// readline function from https://hackingmajenkoblog.wordpress.com/2016/02/01/reading-serial-on-the-arduino/
int readline(int readch, char *buffer, int len) {
static int pos = 0;
int rpos;
if (readch > 0) {
switch (readch) {
case '\r': // Ignore CR
break;
case '\n': // Return on new-line
rpos = pos;
pos = 0; // Reset position index ready for next time
return rpos;
default:
if (pos < len - 1) {
buffer[pos++] = readch;
buffer[pos] = 0;
}
}
}
return 0;
}


그 간단한 메시지 구조는 아래와 같다. 누구든 아래 메시지만 맞춰 디바이스 코딩하면 이 안드로이드/애플 앱을 활용할 수 있다. 6개의 I/O와 1개의 텍스트를 맘껏 활용해 보세요 ^^


앱은 그냥 쉽게 사용할 수 있지만, 혹시나해서 간단하게 사용화면을 몇개 올려둔다. 

순서대로 사용하면 소스코드를 돌려보는데 아무런 문제가 없다.



네이버밴드네이버블로그핀터레스트텔레그램링크드인포켓레딧이메일

B로그0간

개발 관련 글과 유용한 정보를 공유하는 공간입니다.