스마트폰으로 간단하게 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 기능을 쓸 수 있도록 구현되어 있다.
그럼, 이제 본격적인 스마트폰을 이용한 데모작업을 해야 하는데, 소스코드에 구현된 내용은 아래와 같다.
- NeoPixel LED를 이용하여 스마트폰에서 받은 RGB 값을 이용하여 제어되는 간단한 코드를 작성해 보았다.
- 시리얼 터미널이 연결되어 있으면,
- 스마트폰 앱에서 보내는 텍스트를 시리얼 터미널로 출력하도록 해 두었다.
- 시리얼 터미널에 원하는 텍스트를 엔터를 치면 그 문장이 저장되었다가 스마트폰 앱이 "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개의 텍스트를 맘껏 활용해 보세요 ^^
앱은 그냥 쉽게 사용할 수 있지만, 혹시나해서 간단하게 사용화면을 몇개 올려둔다.
순서대로 사용하면 소스코드를 돌려보는데 아무런 문제가 없다.
'IT > IoT | Hardware' 카테고리의 다른 글
10 Netstat Command Examples (0) | 2019.01.09 |
---|---|
Cannot compile blink sketch for Arduino MKR boards (0) | 2019.01.04 |
스마트폰으로 간단하게 Arduino 제어하기 (2) - 어플 소개 (0) | 2018.12.14 |
Network 엔지니어 기초 이론 교육 자료집 (0) | 2018.10.30 |
Office Hours - Mbed OS (0) | 2018.08.23 |