[40호]‘압력센서(RA9P/RA12P)’ 및 ‘아두이노용 센서 측정 쉴드’ 제품 출시
압력센서 RA9P | 압력센서 RA12P | 아두이노 센서 측정 쉴드 |
㈜마블덱스
‘압력센서(RA9P/RA12P)’ 및
‘아두이노용 센서 측정 쉴드’ 제품 출시
FSR 센서 전문 회사인 (주)마블덱스는 ‘압력센서(RA9P/RA12P)’ 및 ‘아두이노용 센서 측정 쉴드’ 제품을 새롭게 출시했다. 기존에 출시되었던 센서 제품군의 선택 폭이 더욱 넓어졌다.
특히 센서 측정 쉴드는 사용자의 편의성을 크게 높인 제품이다. 기존에 센서를 측정하기 위해선 압력 센서 1개당 전선 4개를 연결하는 번거로움이 있었다.(센서, 풀다운 저항, ADC연결) 하지만 새로 출시된 측정 쉴드를 이용하면 전선 연결은 2개로 줄어들어 연결의 노고가 줄어든다. 특히 터미널 블록을 눌러서 간단히 끼우기만 하면 되기 때문에 시간이 크게 줄어든다.
우노 보드에 소스코드(제공됨)를 다운로드하고 압력 센서를 누르면 쉴드의 LED에 불이 들어오는 것도 확인해 볼 수 있다. 압력 센서 1개의 출력을 확인하는 데에 5분이면 충분하다. 여기에 한가지 부가 기능이 더 있는데, 센서의 편차를 영점 조정해주는 ‘가변 저항 조절’ 기능이다. 편차의 원인으로는 센서 커버의 기구물 공차라든가 하드웨어 노이즈를 들 수 있다. 이를 가변 저항으로 영점 조정하면 측정 정확도 향상에 도움이 된다. 이는 미세한 작업을 진행할 때에 해당되는 것이므로, 일반 측정 시에는 가변 저항을 조정하지 않아도 문제가 없다.
상품 소개 페이지를 방문하면 회로도와 소스코드, 전선 연결 예시를 제공하고 있어 학생들도 편리하게 사용 가능하다.
제품 사양
압력센서 RA9P
˙ Size : 9mm
˙ Sensing range : 5gf ~ 4Kgf
압력센서 RA12P
˙ Size : 12mm
˙ Sensing range : 5gf ~ 4Kgf
아두이노 센서 측정 쉴드
˙ 아두이노 우노 보드에 장착하는 쉴드
˙ 압력 센서 최대 측정 개수 : 6개
˙ 편차 보정용 가변 저항 6개 장착
[40호]장애물 방지 드론
2016 ICT 융합 프로젝트 공모전 참가상
장애물 방지 드론
글 | 단국대학교 김건희, 김혜원
심사평
JK전자 드론을 이용한 무인택배, 사람을 쫒아다니면서 촬영해 주는 드론 카메라, 위험지역탐사, 화재 진화 등 최근에 드론을 이용한 여러가지 서비스에 대한 아이디어들이 봇물처럼 나오고 있다. 이러한 무인으로 동작하는 서비스가 가능하기 위해서 가장 기초적으로 필요한 부분이 장애물을 감지하고 회피하는 능력일 것이다. 본 작품에서는 기초적인 방법으로 적외선 센서를 이용해서 아주 근접한 물체에 대한 판별만 하고 있지만, 이 부분을 조금더 수정한다면 다양한 분야에 이용이 가능할 것이다. 또한 최신의 32비트 프로세서인 Cortex-M4를 이용하였고 직접 PID 제어 코드를 모두 구현한 것으로 판단하건데 본 작품 구현을 위해서 많은 노력을 기울인 것이 엿보인다.
뉴티씨 드론 관련 내용 중에서도 장애물을 회피하는 내용을 다루고 있는 데, 실질적으로 충돌을 막아내는 효과가 있는 작품이다. 실제 드론에 적용한다면 좋을 것 같다. 자체로는 상품이 되기는 어렵지만, 이런 아이템을 하나하나 쌓아 나간다면 좋은 작품이 될 것으로 생각한다. 실용성과 창의성 및 작품 완성면에서 좋은 점수를 받았다.
칩센 주제는 식상하다, 하지만 많은 학생들이 관심을 가지는 분야에 자세한 설명이 제공되는 부분에 높은 평가를 한다.
위드로봇 트라이콥터 형식의 기체를 자체적으로 제작한 점은 훌륭하나, 작품의 특성인 장애물을 회피하는 기동을 보이는데 해당 드론이 장점이 있다는 점이 잘 부각되지 않았습니다. 또한 장애물을 회피하기 위해 사용한 센서가 적외선인데 이게 실외에서 잘 동작할 수 있을지 의문입니다. 이러한 설명이 부족한 점이 아쉬웠습니다.
1. 작품개요
제작동기
요즘 무인항공기인 드론이 대세입니다. 하지만 사건, 사고가 많이 일어나고 있습니다. 이탈리아 밀나노의 명소인 두오모 성당에 한국인들이 조종하던 드론이 충돌하는 사고가 일어다는 뉴스도 접할 수 있었습니다.
드론은 사회에 급속히 보급되고 있지만 그만큼 드론과 관련한 충돌 사고도 자주 발생하고 있습니다. 그래서 충돌사고를 줄이는데 도움이 되고자 장애물 피하는 드론을 만들게 되었습니다.
제작과정
stm32f4 보드에 모터, 변속기, 센서 등을 모두 각각 구매하여 자체적으로 제작하였습니다. 처음 드론을 선정할 때 프로펠러 수가 적을수록 기동성이 빠르기 때문에 프로펠러가 3개인 트라이 콥터로 선정하고 제작하였습니다. 트라이콥터를 제작하여 호버링(헬리콥터가 공중에 정지해 있는 상태) 테스트 후 적외선 센서를 통하여 장애물과 가까워지면 드론이 자동적으로 피하게끔 제작하였습니다.
작품내용
트라이콥터를 설명드리면, 일반적으로 쿼드콥터와 달리 3개의 BLDC모터와 1개의 서보모터를 가지고 비행하는 것이 특징입니다. 3개의 BLDC모터로 IMU 센서값에서 얻는 Roll축 값과 Pitch축 값을 가지고 제어하고 나머지 서보모터로 Yaw축 값을 받아 Yaw축을 제어하게 됩니다.
하드웨어를 만들고 소스 구현을 한 다음 적외선 센서를 부착하기 전에 조종기로 드론이 호버링이 될 수 있도록 제작하였습니다. 조종기로 호버링이 된 후 적외선 센서를 9개를 달아 벽이나 장애물에 가까이 다가갔을 때 장애물 반대 방향으로 제어하게끔 소스를 구현하였습니다. 고도도 ms5611센서(기압센서)에서 추출한 값으로 일정 고도를 유지하게 제작하였습니다.
제작 결과
조종기를 이용하여 현재 호버링은 쉽게 되고 있습니다. 이후에 적외선 센서를 통하여 장애물이 가까이 갔을 때 센서 값을 확인하여 보았습니다. 50cm부터 센서값이 추출되어 50cm 이상 거리가 떨어지면 인식을 하지 못하지만 50cm 이내로 들어오면 드론이 장애물의 반대로 움직이려는 현상을 보게 되었습니다. 따라서 현재 시중에 판매할 정도로 완벽하지는 않지만 어느 정도 장애물에 가까이 갔을 때 장애물을 인식하여 반대로 움직이려는 현상을 확인하였고 제가 원하는 제어가 가능하다는 것을 확인하였습니다.
3. 작품 설명
3.1. 주요 동작 및 특징
트라이콥터에는 3개의 bldc 모터와 1개의 서보모터가 들어가 있습니다. 먼저 드론을 전체적으로 제어할 stm32f4 보드가 있고, 조종기와 통신하기 위한 블루투스 모듈, 고도제어를 위한 기압센서, 드론 제어를 위해 관성 측정장치인 imu가 들어가 있습니다, 또한 장애물을 피하기 위한 9개의 적외선 센서가 들어가 있습니다.
먼저 블루투스 통신기는 JMOD-BT-1 모듈을 이용하였습니다. 조종기와 통신하기 위하여 sci 통신을 하였고 tx와 rx를 연결하고 조종기를 움직이게 되면 움직임에 따라 값을 보내게 되고, 제어에 맞는 값이 블루투스 모듈을 통하여 stm32f4 보드에 값을 전달하며, 이 값에 따라 조종을 하게 됩니다.
둘째로 고도제어는 ms5611이라는 기압센서를 통하여 제어하게 됩니다. 다만 일정 고도 이상일 때 값이 일정하게 나오게 되어, 그때부터는 조종기를 통하여 제어하지 않고 드론 스스로 일정 고도를 유지하게끔 모터제어를 하였습니다.
또한 관성 측정장치인 imu(EBIMU-9DOFV2)를 이용하였습니다. imu에는 자이로 센서, 가속도 센서, 지자기 센서가 들어있어 roll, pitch, yaw축을 제어하게 됩니다. 공중에서 드론이 기울어지게 되면 기울어지는 반대방향으로 움직이게끔 모터제어를 하기위해 imu를 사용하게 되었습니다.
마지막으로 적외선 센서가 들어가 있습니다. 한 개의 프로펠러에 3개의 적외선 센서를 달았습니다. 그래서 총 9개의 적외선 센서가 들어있습니다. 처음에는 초음파 센서로 i2c 통신을 시도해 보았으나, 3개의 초음파 센서를 다니까 반응 속도가 느려지는 현상이 발생하게 되었습니다. 그래서 초음파 센서 대신 9개의 적외선 센서를 부착하였습니다. 테스트 해보니 적외선 센서는 장애물 50cm 이내에 들어왔을 때 센서값이 증가하게 됩니다. 이 증가하는 값을 이용하여 모터를 제어를 하게 됩니다. 이에 장애물을 피할 수 있게 소스를 구현하였습니다.
모터 제어는 bldc 제어를 하기 위해서 하비킹의 30A 변속기를 사용하였습니다. 변속기는 일반적으로 모터 드라이브라고 생각하면 쉽게 이해가 됩니다. 3개의 bldc모터에 각각 변속기가 붙어있어 모터를 제어하게 됩니다. 그리고 변속기에 pwm 파형을 주면 변속기를 통하여 모터를 제어하게 됩니다.
서보모터는 imu값에 따라 stm32f4에서 pwm파 형이 나오게 됩니다. 이 파형을 서보모터의 pwm핀에 물리면 서보모터는 파형에 따라 각도 회전을 하기 때문에 이를 이용하여 서보모터를 제어하게 됩니다.
bldc 모터와 서보모터를 제어하기 위해서 pid 제어를 하였습니다. pid 제어는 비례, 적분, 미분을 합쳐 목표값과의 차이를 이용하여 원하는 제어값을 얻는 제어법 중 하나입니다.
3.2. 전체 시스템 구성
3.3. 개발환경(개발 언어, tool, 사용시스템 등)
stm32f407 보드를 사용하기 위해서는 많은 프로그램이 있습니다. 저는 그중에서 IAR Embedded Workbench를 이용하였습니다. IAR은 전세계적으로 임베디드 시스템 개발자들이 많이 사용하는 C/C++ Compiler 및 Debugging Tool 입니다. 그리고 Tera Term이라는 모니터를 이용하여 PC상에서 m4 보드에 접근할 수 있었습니다. 또한 드론을 제작하면서 printf나 에러 처리 등을 확인할 수 있었습니다.
4. 단계별 제작 과정
4.1. 하드웨어 부품 조립
4.2. 소프트웨어 소스 구현
4.2.1. Tera Term과 stm32f4보드 터미널 연결
PB6▶tx, PB7▶rx핀을 연결후 시리얼 포트인 rx232와 연결을 하였습니다. 연결시 vcc, ground, tx, rx핀을 연결 후 sci 인터럽트를 통해 보드에서 printf();값이 나오는지 확인하였습니다.
4.2.2. timer interrupt
timer interrupt가 일정 시간마다 되는지 확인하였습니다. 왜냐하면 bldc 모터나 서보모터를 제어하기 위해서는 일정 시간만큼 계속 제어를 해주어야 하기 때문에 timer interrupt가 있어야 합니다.
4.2.3. pwm
모터 제어를 하기 위해서는 pwm 파형이 나오는지를 확인해야 됩니다. pwm이란 ‘펄스 폭 변조’, 펄스 폭을 원하는 값에 따라 조절할 수 있습니다. 이 조절된 펄스를 가지고 모터를 제어하게 됩니다. 서보모터는 펄스 폭에 따라 회전 각도가 정해져 있어 회전 각도에 따라 펄스 폭 조절을 해야 됩니다.
4.2.4. I2C를 이용하여 고도센서 통신
통신 방법에는 대표적으로 SCI, CAN, I2C, SPI 4가지 통신 방법이 있습니다. 그 중에 기압센서를 제어하기 위해서는 I2C통신이 필요합니다. 그래서 I2C통신이 되는지 확인하였습니다.
4.2.5. ADC
적외선 센서 값을 받기위해서는 ADC가 필요합니다. ADC는 아날로그 신호를 디지털 신호로 바꿔주는 변환기입니다. 적외선 센서 값에 따라 값이 나오는지 확인했습니다. ADC를 통하여 센서 값을 받아도 백열등이나 밖에서 다른 환경일 때 적외선 센서가 값을 인식하는 경우가 발생하게 됩니다. 그래서 하드웨어적으로는 HPF(HIGH PASS FILTER)로 땜을 하여 필터링을 하고 소프트웨어적으로 IIR필터를 설계하였습니다.
IIR필터란 디지털 필터로 적외선 센서인 경우 노이즈를 많이 타기 때문에 노이즈 제거 또는 감소를 시키기 위해 필터를 설계하였습니다.
사진은 센서값을 printf하여 확인한 값입니다.
4.2.6. IMU 센서값 확인
imu 센서에서 sci 통신으로 값을 받게되면 상수 값으로 넘어오는게 아니라 아스키 코드값으로 문자열이 넘어오게 됩니다. 3개의 값이 *123, 456, 789\n처럼 값이 하나의 문자열로 넘어오기 때문에 하나의 문자열을 3개의 변수로 잡아 모터 제어에 필요하게 3개의 변수 값을 확인하였습니다. 밑의 사진은 3개의 변수 중 pitch값을 확인하였습니다.
4.2.7. IMU 센서에서 받은 값과 기압센서에서 받은 값을 이용하여 모터 pid 제어소스 구현을 했습니다.
4.3. 실제 기동 테스트
pid 제어소스를 구현하여 실제 기동 테스트를 해보았습니다.
4.3.1. 시소테스트
pid 제어 소스를 구현해도 pid의 상수값을 맞춰주어야 합니다. P값의 경우에는 한쪽으로 기울어 졌을 때 받아치려는 힘이 느껴질 때 그때의 P값으로 지정하고 D값을 맞추었습니다. D값을 맞추다보면 한쪽으로 기울었을 때 평형을 유지하려고 합니다. 그때가 시소테스트가 완벽하게 된 것입니다.
4.3.2. 실내 테스트
처음에는 서보모터를 제어하지 않고 프로펠러 3개를 이용하여 드론을 날려보니 시소테스트를 제대로 하면 프로펠러가 역방향 2개와 정방향 1개이기 때문에 역방향으로 회전을 하면서 올랐다가 내려오는 모습을 보게 됩니다. 이게 제대로 된 후 imu값의 회전축인 yaw축 값을 받아 서보모터를 제어하여 회전을 안하게 했습니다.
4.3.3. 실외 테스트
4.4. 장애물 인식후 제어 테스트
장애물을 인식하기 위해서 트라이콥터 프로펠러 부분에 3개의 적외선 센서를 달아 총 9개의 적외선 센서로 근처에 장애물이 있는지 감지할 수 있도록 하였습니다. 다음 사진은 한 개의 프로펠러에 3개의 적외선 센서를 달아놓은 상태입니다.
아래는 벽에 가까이 다가갈때의 모습입니다.
벽 반대편으로 움직이려 스스로 제어하려 하는 모습을 볼 수 있습니다.
5. 기타(회로도, 소스코드, 참고문헌등)
5.1. 적외선 센서 I2C 필터 소스
// SENSOR VARIABLE FILTERING
g_sen[ gUint16SensorSelect ].LPFOutDataYet = g_sen[ gUint16SensorSelect ].LPFOutData;
g_sen[ gUint16SensorSelect ].LPFOutData =
mpy( SENSOR_Kb, 30, g_sen[ gUint16SensorSelect ].LPFInData
+ QUP( g_sen[ gUint16SensorSelect ].Uint16Value, 17 ), 17 )
- mpy( SENSOR_Ka, 30, g_sen[ gUint16SensorSelect ].LPFOutData, 17 );
g_sen[ gUint16SensorSelect ].LPFInData = QUP( g_sen[ gUint16SensorSelect ].Uint16Value, 17 );
5.2. IMU값을 3개의 변수로 바꾸는 소스
void IMUchar(void)
{
if(sci_buf == ‘\n’)
{
g_int32_sciflag = OFF;
g_int32_strcnt = 0;
g_int32_IMUnum = 0;
g_f32_imu_flag = 1;
}
if(g_int32_sciflag == ON)
{
if(g_int32_IMUnum == 0)
{
if(sci_buf == ‘,’)
{
g_str_roll[g_int32_strcnt++] = ‘’;
g_int32_IMUnum++;
g_int32_strcnt = 0;
}
else
{
g_str_roll[g_int32_strcnt++] = sci_buf;
}
}
else if(g_int32_IMUnum == 1)
{
if(sci_buf == ‘,’)
{
g_str_pitch[g_int32_strcnt++] = ‘’;
g_int32_IMUnum++;
g_int32_strcnt = 0;
}
else
{
g_str_pitch[g_int32_strcnt++] = sci_buf;
}
}
else if(g_int32_IMUnum == 2)
{
if(sci_buf == 13)//imu 맨마지막 숫자는 13이다.
{
g_str_yaw[g_int32_strcnt++] = ‘’;
g_int32_strcnt = 0;
}
else
{
g_str_yaw[g_int32_strcnt++] = sci_buf;
}
}
}
if(sci_buf == ‘*’)
{
g_int32_sciflag = ON;
}
}
void IMUdecimal(void)
{
int16_t length_roll = 0;
int16_t length_pitch = 0;
int16_t length_yaw = 0;
length_roll = strlen((char *)g_str_roll);
length_pitch = strlen((char *)g_str_pitch);
length_yaw = strlen((char *)g_str_yaw);
//roll
if(length_roll == 4)
{
sci_buf2roll[0]=g_str_roll[0]-48;
sci_buf2roll[2]=(g_str_roll[2]-48)*0.1;
sci_buf2roll[3]=(g_str_roll[3]-48)*0.01;
g_fp32roll = sci_buf2roll[0]+sci_buf2roll[2]+sci_buf2roll[3];
}
else if(length_roll == 5)
{
if(g_str_roll[0]==’-’)
{
sci_buf2roll[1]=g_str_roll[1]-48;
sci_buf2roll[3]=(g_str_roll[3]-48)*0.1;
sci_buf2roll[4]=(g_str_roll[4]-48)*0.01;
g_fp32roll = (sci_buf2roll[1]+sci_buf2roll[3]+sci_buf2roll[4])*(-1);
}
else
{
sci_buf2roll[0]=(g_str_roll[0]-48)*10;
sci_buf2roll[1]=(g_str_roll[1]-48);
sci_buf2roll[3]=(g_str_roll[3]-48)*0.1;
sci_buf2roll[3]=(g_str_roll[4]-48)*0.01;
g_fp32roll = sci_buf2roll[0]+sci_buf2roll[1]+sci_buf2roll
[3]+sci_buf2roll[4];
}
}
else if(length_roll == 6)
{
if(g_str_roll[0]==’-’)
{
sci_buf2roll[1]=(g_str_roll[1]-48)*10;
sci_buf2roll[2]=(g_str_roll[2]-48);
sci_buf2roll[4]=(g_str_roll[4]-48)*0.1;
sci_buf2roll[5]=(g_str_roll[5]-48)*0.01;
g_fp32roll = (sci_buf2roll[1]+sci_buf2roll[2]+sci_buf2roll
[4]+sci_buf2roll[5])*(-1);
}
else
{
sci_buf2roll[0]=(g_str_roll[0]-48)*100;
sci_buf2roll[1]=(g_str_roll[1]-48)*10;
sci_buf2roll[2]=(g_str_roll[2]-48);
sci_buf2roll[4]=(g_str_roll[4]-48)*0.1;
sci_buf2roll[5]=(g_str_roll[5]-48)*0.01;
g_fp32roll = sci_buf2roll[0]+sci_buf2roll[1]+sci_buf2roll[2]+sci_
buf2roll[4]+sci_buf2roll[5];
}
}
else if(length_roll == 7)
{
sci_buf2roll[1]=(g_str_roll[1]-48)*100;
sci_buf2roll[2]=(g_str_roll[2]-48)*10;
sci_buf2roll[3]=(g_str_roll[3]-48);
sci_buf2roll[5]=(g_str_roll[5]-48)*0.1;
g_fp32roll=(sci_buf2roll[1]+sci_buf2roll[2]+sci_buf2roll[3]
+sci_buf2roll[5])*(-1);
}
//pitch
if(length_pitch == 4)
{
sci_buf2pitch[0]=g_str_pitch[0]-48;
sci_buf2pitch[2]=(g_str_pitch[2]-48)*0.1;
sci_buf2pitch[3]=(g_str_pitch[3]-48)*0.01;
g_fp32pitch = sci_buf2pitch[0]+sci_buf2pitch[2]+sci_buf2pitch[3];
}
else if(length_pitch == 5)
{
if(g_str_pitch[0]==’-’)
{
sci_buf2pitch[1]=g_str_pitch[1]-48;
sci_buf2pitch[3]=(g_str_pitch[3]-48)*0.1;
sci_buf2pitch[4]=(g_str_pitch[4]-48)*0.01;
g_fp32pitch = (sci_buf2pitch[1]+sci_buf2pitch[3]
+sci_buf2pitch[4])*(-1);
}
else
{
sci_buf2pitch[0]=(g_str_pitch[0]-48)*10;
sci_buf2pitch[1]=(g_str_pitch[1]-48);
sci_buf2pitch[3]=(g_str_pitch[3]-48)*0.1;
sci_buf2pitch[3]=(g_str_pitch[4]-48)*0.01;
g_fp32pitch = sci_buf2pitch[0]+sci_buf2pitch[1]+sci_buf2pitch
[3]+sci_buf2pitch[4];
}
}
else if(length_pitch == 6)
{
if(g_str_pitch[0]==’-’)
{
sci_buf2pitch[1]=(g_str_pitch[1]-48)*10;
sci_buf2pitch[2]=(g_str_pitch[2]-48);
sci_buf2pitch[4]=(g_str_pitch[4]-48)*0.1;
sci_buf2pitch[5]=(g_str_pitch[5]-48)*0.01;
g_fp32pitch = (sci_buf2pitch[1]+sci_buf2pitch[2]
+sci_buf2pitch[4]+sci_buf2pitch[5])*(-1);
}
else
{
sci_buf2pitch[0]=(g_str_pitch[0]-48)*100;
sci_buf2pitch[1]=(g_str_pitch[1]-48)*10;
sci_buf2pitch[2]=(g_str_pitch[2]-48);
sci_buf2pitch[4]=(g_str_pitch[4]-48)*0.1;
sci_buf2pitch[5]=(g_str_pitch[5]-48)*0.01;
g_fp32pitch = sci_buf2pitch[0]+sci_buf2pitch[1]+sci_buf2pitch[2]
+sci_buf2pitch[4]+sci_buf2pitch[5];
}
}
else if(length_pitch == 7)
{
sci_buf2pitch[1]=(g_str_pitch[1]-48)*100;
sci_buf2pitch[2]=(g_str_pitch[2]-48)*10;
sci_buf2pitch[3]=(g_str_pitch[3]-48);
sci_buf2pitch[5]=(g_str_pitch[5]-48)*0.1;
g_fp32pitch=(sci_buf2pitch[1]+sci_buf2pitch[2]+sci_buf2pitch[3]+
sci_buf2pitch[5])*(-1);
}
//yaw
if(length_yaw == 4)
{
sci_buf2yaw[0]=g_str_yaw[0]-48;
sci_buf2yaw[2]=(g_str_yaw[2]-48)*0.1;
sci_buf2yaw[3]=(g_str_yaw[3]-48)*0.01;
g_fp32yaw = sci_buf2yaw[0]+sci_buf2yaw[2]+sci_buf2yaw[3];
}
else if(length_yaw == 5)
{
if(g_str_yaw[0]==’-’)
{
sci_buf2yaw[1]=g_str_yaw[1]-48;
sci_buf2yaw[3]=(g_str_yaw[3]-48)*0.1;
sci_buf2yaw[4]=(g_str_yaw[4]-48)*0.01;
g_fp32yaw = (sci_buf2yaw[1]+sci_buf2yaw[3]+sci_buf2yaw[4])*(-1);
}
else
{
sci_buf2yaw[0]=(g_str_yaw[0]-48)*10;
sci_buf2yaw[1]=(g_str_yaw[1]-48);
sci_buf2yaw[3]=(g_str_yaw[3]-48)*0.1;
sci_buf2yaw[3]=(g_str_yaw[4]-48)*0.01;
g_fp32yaw = sci_buf2yaw[0]+sci_buf2yaw[1]+sci_buf2yaw[3]+sci_buf2yaw[4];
}
}
else if(length_yaw == 6)
{
if(g_str_yaw[0]==’-’)
{
sci_buf2yaw[1]=(g_str_yaw[1]-48)*10;
sci_buf2yaw[2]=(g_str_yaw[2]-48);
sci_buf2yaw[4]=(g_str_yaw[4]-48)*0.1;
sci_buf2yaw[5]=(g_str_yaw[5]-48)*0.01;
g_fp32yaw = (sci_buf2yaw[1]+sci_buf2yaw[2]+sci_buf2yaw[4]
+sci_buf2yaw[5])*(-1);
}
else
{
sci_buf2yaw[0]=(g_str_yaw[0]-48)*100;
sci_buf2yaw[1]=(g_str_yaw[1]-48)*10;
sci_buf2yaw[2]=(g_str_yaw[2]-48);
sci_buf2yaw[4]=(g_str_yaw[4]-48)*0.1;
sci_buf2yaw[5]=(g_str_yaw[5]-48)*0.01;
g_fp32yaw = sci_buf2yaw[0]+sci_buf2yaw[1]+sci_buf2yaw[2]
+sci_buf2yaw[4]+sci_buf2yaw[5];
}
}
else if(length_yaw == 7)
{
sci_buf2yaw[1]=(g_str_yaw[1]-48)*100;
sci_buf2yaw[2]=(g_str_yaw[2]-48)*10;
sci_buf2yaw[3]=(g_str_yaw[3]-48);
sci_buf2yaw[5]=(g_str_yaw[5]-48)*0.1;
g_fp32yaw=(sci_buf2yaw[1]+sci_buf2yaw[2]+sci_buf2yaw[3]
+sci_buf2yaw[5])*(-1);
}
}
5.3. roll축 pid 제어 소스
roll_motor.g_fp32err_vel[9] = roll_motor.g_fp32err_vel[8];
roll_motor.g_fp32err_vel[8] = roll_motor.g_fp32err_vel[7];
roll_motor.g_fp32err_vel[7] = roll_motor.g_fp32err_vel[6];
roll_motor.g_fp32err_vel[6] = roll_motor.g_fp32err_vel[5];
roll_motor.g_fp32err_vel[5] = roll_motor.g_fp32err_vel[4];
roll_motor.g_fp32err_vel[4] = roll_motor.g_fp32err_vel[3];
roll_motor.g_fp32err_vel[3] = roll_motor.g_fp32err_vel[2];
roll_motor.g_fp32err_vel[2] = roll_motor.g_fp32err_vel[1];
roll_motor.g_fp32err_vel[1] = roll_motor.g_fp32err_vel[0];
roll_motor.g_fp32err_vel[0] = g_fp32roll + g_fp32rollmoveval ;
roll_motor.g_fp32err_vel_sum += roll_motor.g_fp32err_vel[0]*0.001;
//백의 자리 까지 값 변함
if( roll_motor.g_fp32err_vel_sum > 100.0 )
roll_motor.g_fp32err_vel_sum = 100.0 ;
else if( roll_motor.g_fp32err_vel_sum < -100.0 )
roll_motor.g_fp32err_vel_sum = -100.0 ;
g_fp32_rolldiffvelyet =
((roll_motor.g_fp32err_vel[0] – roll_motor.g_fp32err_vel[1])
+(roll_motor.g_fp32err_vel[1] – roll_motor.g_fp32err_vel[2])
+(roll_motor.g_fp32err_vel[2] – roll_motor.g_fp32err_vel[3])+
(roll_motor.g_fp32err_vel[3] – roll_motor.g_fp32err_vel[4])
+(roll_motor.g_fp32err_vel[4] – roll_motor.g_fp32err_vel[5])
+(roll_motor.g_fp32err_vel[5] – roll_motor.g_fp32err_vel[6])+
(roll_motor.g_fp32err_vel[6] – roll_motor.g_fp32err_vel[7])) ;
if(g_fp32_rolldiffvelyet > 0.8) g_fp32_rolldiffvelyet = 0.8;
else if(g_fp32_rolldiffvelyet < -0.8) g_fp32_rolldiffvelyet = -0.8;
if(g_fp32_rolldiffvelyet)
{
g_fp32_rolldiffvelyet2 = g_fp32_rolldiffvelyet * g_fp32_rolldiffvelyet *
g_fp32_rolldiffvelyet * g_fp32_rolldiffvelyet * g_fp32_rolldiffvelyet ;
}
else if(g_fp32_rolldiffvelyet == 0)
{
g_fp32_rolldiffvelyet = g_fp32_rolldiffvelyet2 ;
}
roll_motor.g_fp32proportion_val = roll_motor.g_fp32kp *
(roll_motor.g_fp32err_vel[0] );
roll_motor.g_fp32differential_val = roll_motor.g_fp32kd *
(g_fp32_rolldiffvelyet * 13);
roll_motor.g_fp32integral_val = roll_motor.g_fp32ki *
roll_motor.g_fp32err_vel_sum ;
roll_motor.g_fp32PID_output = roll_motor.g_fp32proportion_val
+ roll_motor.g_fp32differential_val + roll_motor.g_fp32integral_val ;
if( roll_motor.g_fp32PID_output > 300 ) roll_motor.g_fp32PID_output = 300;
else if( roll_motor.g_fp32PID_output < -300 ) roll_motor.g_fp32PID_output = -300;
roll_motor.g_fp32L_Handle = 1 + (roll_motor.g_fp32PID_output /
g_fp32_rollhandleval);
roll_motor.g_fp32R_Handle = 1 – (roll_motor.g_fp32PID_output /
g_fp32_rollhandleval);
g_fp32_roll1motorval = g_fp32pwm_val * roll_motor.g_fp32L_Handle;
g_fp32_roll2motorval = g_fp32pwm_val * roll_motor.g_fp32R_Handle;
5.4. yaw축 서보모터 pid 제어 소스
g_fp32_yawerr[9] = g_fp32_yawerr[8];
g_fp32_yawerr[8] = g_fp32_yawerr[7];
g_fp32_yawerr[7] = g_fp32_yawerr[6];
g_fp32_yawerr[6] = g_fp32_yawerr[5];
g_fp32_yawerr[5] = g_fp32_yawerr[4];
g_fp32_yawerr[4] = g_fp32_yawerr[3];
g_fp32_yawerr[3] = g_fp32_yawerr[2];
g_fp32_yawerr[2] = g_fp32_yawerr[1];
g_fp32_yawerr[1] = g_fp32_yawerr[0];
g_fp32_yawerr[0] = yaw_motor.g_fp32err_vel[9];
yaw_motor.g_fp32err_vel[9] = yaw_motor.g_fp32err_vel[8];
yaw_motor.g_fp32err_vel[8] = yaw_motor.g_fp32err_vel[7];
yaw_motor.g_fp32err_vel[7] = yaw_motor.g_fp32err_vel[6];
yaw_motor.g_fp32err_vel[6] = yaw_motor.g_fp32err_vel[5];
yaw_motor.g_fp32err_vel[5] = yaw_motor.g_fp32err_vel[4];
yaw_motor.g_fp32err_vel[4] = yaw_motor.g_fp32err_vel[3];
yaw_motor.g_fp32err_vel[3] = yaw_motor.g_fp32err_vel[2];
yaw_motor.g_fp32err_vel[2] = yaw_motor.g_fp32err_vel[1];
yaw_motor.g_fp32err_vel[1] = yaw_motor.g_fp32err_vel[0];
yaw_motor.g_fp32err_vel[0] = g_fp32yaw – g_fp32_yawOffset
+ g_fp32yawmoveval ;
if( yaw_motor.g_fp32err_vel_sum > 150.0 )
yaw_motor.g_fp32err_vel_sum = 150.0 ;
if( yaw_motor.g_fp32err_vel_sum < -150.0 )
yaw_motor.g_fp32err_vel_sum = -150.0 ;
g_fp32_yawdiffvelyet = (yaw_motor.g_fp32err_vel[0] -
g_fp32_yawerr[9]) * 1.6;/
if(g_fp32_yawdiffvelyet > 0)
{
g_int32_yawdiffint = g_fp32_yawdiffvelyet ;
}
else if(g_fp32_yawdiffvelyet < -0)
{
g_int32_yawdiffint = g_fp32_yawdiffvelyet ;
}
else
{
g_int32_yawdiffint = 0;
g_fp32_yawdiffvelyet = 0;
}
g_fp32_yawaverage[4] = g_fp32_yawaverage[3];
g_fp32_yawaverage[3] = g_fp32_yawaverage[2];
g_fp32_yawaverage[2] = g_fp32_yawaverage[1];
g_fp32_yawaverage[1] = g_fp32_yawaverage[0];
g_fp32_yawaverage[0] = g_int32_yawdiffint;
g_int32_yawdiffvelyet3 =
(g_fp32_yawaverage[0]+g_fp32_yawaverage[1]+g_fp32_yawaverage[2]+
g_fp32_yawaverage[3]+g_fp32_yawaverage[4])*0.25 ;
yaw_motor.g_fp32proportion_val = yaw_motor.g_fp32kp *
yaw_motor.g_fp32err_vel[0] ;
yaw_motor.g_fp32differential_val = yaw_motor.g_fp32kd *
g_int32_yawdiffvelyet3;
yaw_motor.g_fp32PID_output =
yaw_motor.g_fp32proportion_val + yaw_motor.g_fp32differential_val ;
if( yaw_motor.g_fp32PID_output > 100 ) yaw_motor.g_fp32PID_output = 100;
else if( yaw_motor.g_fp32PID_output < -100 ) yaw_motor.g_fp32PID_
output = -100;
g_int32_yawint = (yaw_motor.g_fp32PID_output / g_fp32_yawhandleval );
g_fp32_yawHandle = g_int32_yawint * 0.5 ;
g_fp32_yawmotorval = 27.5 – g_fp32_yawHandle ;
if((g_fp32_yawmotorval > 17)&&(g_fp32_yawmotorval < 37))
{
TIM3->CCR1 = g_fp32_yawmotorval;
}
6. 참고문헌
· Cortex-M3/M4 완벽가이드(높이깊이) – 임종수
· ARM Cortex-M3 시스템 프로그래밍 완전정복1(D&W WAVE) – 박선호, 오영환
· 최신제어시스템 제 11판(PEARSON) – Richard C. Dorf
[40호]HiFi 오디오 메인 앰프 실장형 “고 감도 음성 모니터 앰프” 출시
케이 벨 ㈜
HiFi 오디오 메인 앰프 실장형 “고 감도 음성 모니터 앰프” 출시
케이 벨 ㈜는 Analogue 제품 개발의 노하우를 기반으로 1995년 창업 이후 소비자가 편리하게 사용할 수 있게 각종 Analogue 제품을 개발해 생산하고 있다. 이번에 신제품으로 출시하는 “MIC1000W”는 미세한 음성 신호를 1000배 증폭해, 스피커를 통해 증폭된 음성 신호를 모니터하여 사용할 수 있게 설계됐다, 또한 10Watt 오디오 출력 앰프가 내장되어 별도로 오디오 앰프 설치가 요구되지 않으며, 음량 조절이 가능하도록 SVR이 실장된 음성 모니터 앰프이다. 모니터 될 음성 신호는 1000배 증폭되어 확장된 스피커 선을 통해 최대 1Km 떨어진 거리까지 전송할 수 있으며, 먼 거리에 위치한 소비자는 미세한 음성 신호를 큰 음량으로 스피커를 통하여 듣고 모니터 할 수 있다.
제품 특징
· 음성 증폭 율 : 1000 배 ( 60dB )
· 오디오 입력 : 마이크 입력(실장된 Microphone 사용)
· 음성 모니터 출력 : 최대 10 Watts (스피커 선의 종류와 거리에 따라 차이 발생)
· 전원 전압 : DC 12 Volts / 2 A
· 스피커 규격 : 4 옴, 8옴, 16 옴 / 10 Watts
· 제품 크기 : 길이 80mm x 직경 38mm
www.avmart.co.kr
TEL. 02-6443-4703
고 출력 음성 마이크 증폭기(MIC1000W) 상세 구경하러 가기
[40호]Wi-Fi를 품은 한국형 아두이노 오렌지보드WiFi
Wi-Fi를 품은 한국형 아두이노
오렌지보드WiFi
글 | 금강초롱 blog.naver.com/crucian2k3
아두이노 제품을 조금이라도 다뤄본 독자님들이라면 언젠간 꼭 한번 도전해 보고 싶은 영역이 바로 네트워크일 것입니다. 그러나 막상 시도해 보려하면 어디서부터 어떻게 접근해야 할지 너무 막막하기도 하고 어렵사리 쉴드 보드를 장만하여 테스트 해보려 해도 그리 생각처럼 쉽지 않은 것이 현실입니다.
오렌지보드 시리즈로 유명한 우리나라의 코코아팹에서 따끈한 WiFi 기반의 아두이노 우노 계열의 보드를 내놓았습니다. 이 보드에는 ATmega328P와 USB FTDI 칩이 내장되어 있으므로 여느 아두이노 시리즈와 별반 다를 것은 없으나, 위즈네트에서 출시한 WiZ250 무선통신모듈이 장착되어 있어 좀 더 손쉽게 네트워크에 발을 들여 놓도록 배려해 주고 있습니다. 배려라기보다도 더 정확히는 이 보드의 핵심이라고 볼 수 있겠습니다.
다만 위즈네트 무선랜 모듈이 SPI 채널을 활용하고 있어 일부 핀들이 여기에 할당토록 살짝 제약이 따르긴 합니다만, 아두이노 보드에 쉴드 등을 얹는 경우에도 이와 크게 다르지 않기에 그리 민감하게 반응할 사안은 아닌 듯합니다.
SPI 채널을 매개체로 하여 동작되는 위즈네트 무선랜 모듈은 내부적으로는 AT 커맨드를 사용해 코어와 통신하도록 설계되어 있습니다.
우선 보드가 어떠한 특징들을 포함하고 있는지 쭉~ 살펴본 후에, 코코아팹에서 준비해놓고 있는 튜토리얼과 필자가 랜 통신을 테스트할 때 약방의 감초처럼 여기는 NTP 클라이언트 모듈을 실행해 가며 이 보드를 어떻게 응용할 수 있을지 확인해 보고자 합니다. 이글은 (주)엔티렉스의 지원을 받아 작성하게 되었습니다.
이번 글은 아무래도 난이도가 다소 있을 수밖에 없는 불가피성이 있으며, 적어도 아두이노 우노로 LED나 Serial 모니터, 라이브러리 등은 자유롭게 사용할 줄 안다는 전제하게 글을 써 보고자 합니다.
또 한 가지는 TCP/IP 네트워크, 웹기반 프로그래밍이 등장하게 되는데 이런 부분들까지 일일이 설명하는 것은, 제 지식이 얕기도 하거니와 범위가 너무 넓어질 수도 있으므로, 튜토리얼을 동작하고 이해해 보는 선에서 간략히 정리하고자 합니다.
이더넷통신, IOT 체계 등에 대해서 궁금하신 사항에 대해서는 인터넷상에 충분히 제공되어 있는 정보를 습득해 보는 것도 좋은 방법이 될 것으로 봅니다.
1. 오렌지보드 HW 구성
1.1. 오렌지보드WiFi 상세스펙
오렌지보드에 적용된 MCU가 아두이노 우노(Uno)에도 적용된 ATmeag328P 칩셋이며 원래 제조사는 Atmel사 입니다만, 최근 PIC라는 제품으로 유명한 마이크로칩스에 인수·합병 되었습니다. 따라서 보드의 구성은 위즈네트의 Wi-Fi 모듈을 제외하면 아두이노 우노와 거의 동일하다고 볼 수 있겠습니다. 이런 경우를 일반적으로 동일한 폼펙터를 사용하고 있다고도 이야기하곤 합니다.
필자는 금번에 소개하는 오렌지보드 시리즈를 사용하여 옐로우캣이라는 귀여운 로봇을 아이들과 만들어본 바가 있어 코코아팹 시리즈는 그리 낯설지 않은 편입니다.
보드에 대한 더 상세한 해부는 아래에서 다뤄보기로 하고 WizFi250 Wi-Fi 모듈의 스펙에 대해서 상세히 살펴보면 아래와 같습니다.
1.2. 위즈넷의 대표적인 Wi-Fi 모듈
위즈네트는 사물인터넷(Internet Of Things)을 위한 인터넷 프로세서를 개발·보급하는 팹리스 벤처기업이라고 자신들을 소개하고 있습니다.
필자는 수년 전부터 이더넷에 관심이 있어 마이크로칩사의 PIC18F97J60으로 인터넷 라디오 등 몇 가지 프로젝트를 진행한 바 있었으며 그때 위즈네트 초창기 발매 칩들을 검토해본 바 있습니다. 위즈네트사에서 시판 중인 임베디드형 와이파이 모듈은 현재 5종(WizFi310, WizFi250, WizFi210, WizFi220, WizFi630A)이 있으며 좌측에 이중 3종을 정리해 보았습니다. 개별칩을 구입해서 제작하기에는 이런저런 부담이 있는 경우에, 이러한 모듈형은 상당한 간편함을 제공해줄 수 있다고 봅니다.
WizFi250이 특별히 눈에 띄는 부분이 있다면 제어명령어가 AT 커맨드를 사용한다는 점과 SPI 인터페이스가 구비되어 있다는 점을 들 수 있을 것 같습니다.
이는 사용하기 쉬운 AT 명령어와 시리얼통신에 비해, 보다 고속의 데이터처리가 가능한 SPI를 지원하기에 ‘오렌지보드WiFi’에서 이 모듈을 채용한 것으로 짐작이 됩니다.
데이터 전송 속도로는 801.11n 모드에서는 HT20 MCS0에서 6.5Mbps ~ HT20 MCS7에서 최대 65Mbps로 속도가 나올 수 있다고 하니 나쁘진 않다고 봅니다.
1.3. 제품 핀 구성
다음으로 핀 구성을 살펴보면 USB Type-B로 구성된 아두이노 우노에 비해 MicroUSB 잭을 사용해 보드 공간을 좀 더 효과적으로 사용하려 노력한 점이 돋보입니다.
그 다음으로 눈에 띄는 부분이 있다면 WizFi250과의 연동을 위해 SPI통신을 위한 핀들이 결선되어 있다는 점을 들 수 있을 것 같습니다.
이는 ATmega328이 갖고 있는 한정된 핀 개수에 비춰볼 때 선택의 여지가 없는 사안으로 보이며, 원래 SPI 포트는 보드 하단에 ISP 커넥터와도 연결되어 있어 AVR MK-II 등 프로그래머를 통해 직접 프로그래밍을 하는 용도로도 사용되던 핀이었습니다.
만일 핀 때문이라면 I2C등도 고려해 볼 수도 있을 듯 합니다만, 위즈네트 임베디드 모듈은 I2C를 지원하지 않기에 선택의 여지는 없어 보입니다.
SCK, MISO, MOSI 이 3개의 핀은 다른 SPI 디바이스들과 병렬로 사용하게 되나 SS, DATA_Ready, WIFI_RESET 핀 등 3개는 WizFi250을 사용하는 조건에서는 꼼짝없이 여기에 연결되어야 하는 상황에 놓이게 됩니다. 또한 흔히 On Board LED가 연결되는 D13 핀이 SPI_CLK 핀에 연결되어 있기에 간단한 테스트를 해보고자 해도, 별도의 LED를 연결해야 하는 점이 유의점이라면 유의점에 해당됩니다.
1.4. 회로설계
1.4.1. USB to Serial 부문
PC와 연결되는 마이크로USB 접속부분이 아두이노 우노 R3 와는 다소 다릅니다.
그냥 널리 사용되는 FT232RL로 간단히 USB to Serial을 구현한 것을 알 수 있습니다. 이 칩은 너무나도 유명한 칩으로서 어지간한 USB2SERIAL에는 거의 다 사용되고 있다고 봅니다.
1.4.2. SPI 인터페이스 부문
ATmega328은 5V로 동작되나 WizFi250은 3.3V에서 동작되므로 레벨시프터가 필요합니다. 총 6개 핀이 WizFi250에 연결되어야 하기에 TXB0106PWR을 사용해 정확히 맞춰주도록 설계가 되어 있습니다.
D4, D2, D3, D11, D13, D12 이렇게 핀 6개는 WiZFI250에 연결되어야 하기에, 적어도 오렌지보드WiFi에서는 다른 용도로 사용하지 않는 것이 좋을 것으로 봅니다.
■ SPI 채널 핀 할당
1.4.3. WizFi250 주변부
WiFi Upgrade를 위한 핀은 별도의 커넥터 없이 랜드 4개가 일렬로 되어 있는 부분으로, 이곳이 회로도상 J9에 해당됩니다.
MCU와 인터페이스를 위한 핀 중 UART2는 미사용 상태이며 SPI 측만 연결되도록 설계되어 있음을 알 수 있습니다.
■ 레벨시프터 칩 주변 부
■ WizFi250 칩 주변부
WizFi250 모듈은 필자의 주관적인 생각이긴 합니다만, ARM M3 계열을 코어프로세서로 사용한 것처럼 보입니다.
느낌에 따라서는 ATmega328P와는 주객이 전도된 것 같기도 합니다.
1.4.4. 기타 특수용도 버튼 및 스위치
SW3 Dip 스위치는 오렌지보드 내 WiFi 기능을 꺼버리게 하거나 펌웨어 문제시 초기화하는 기능을 수행 할 수 있도록 설계되어 있습니다.
■ DIP 스위치 기능 정리
오렌지보드WiFi의 HW 부문은 이 정도로 마무리하고 다음은 SW 부문을 살펴보도록 하겠습니다.
2. 오렌지보드WiFi에 WizFi250 라이브러리 설치하기
■ 오렌지보드WiFi HW모델 : 아두이노 IDE > 도구 > 보드 > Arduino/Genuino Uno
■ 라이브러리 확보 : 아두이노 IDE 실행 > 스케치 > 라이브러리추가 > 라이브러리 관리
라이브러리관리자> 검색조건설정 > wizfi250를 입력하면 다음과 같이 WizFi250 by DongEun Koak 가 나옵니다.
설치를 누르면 자동으로 설치가 진행되며 아래와 같은 INSTALLED가 나오는 화면을 볼 수 있습니다.
필자의 경우 라이브러리가 설치된 이후 화면은 아래와 같습니다.
참고로 아두이노 IDE의 환경설정은 아래와 같습니다.
사실 오렌지보드WiFi는 HW적으로나 SW적으로나 워낙 깔끔하고 군더더기가 없어 접근하는데 큰 어려움은 없다고 봅니다.
다만 가장 큰 즐길 거리이자 난관은 SW 부문에서 기다리고 있으며 TCP/IP라고 하는 커다란 언덕을 올라야 뭐라도 하나 해볼 수 있을 것으로 봅니다. 이왕 손에 오렌지보드WiFi를 쥔 만큼 과감히 첫발을 내디뎌보려 합니다.
3. 코코아팹 튜토리얼 따라해 보기
3.1. WebClient
3.1.1. 개요
아두이노 보드를 통해 이제 막 컴퓨터를 배우는 용도 등으로 사용할 것을 생각해 본다면 아두이노 우노나 메가 시리즈 정도는 큰 무리가 없으리라 봅니다.
IDE상에서 digitalWrite() 함수 정도만 다뤄도 뭔가 그럴싸한 일을 해볼 수 있기 때문이고, 이점이 아두이노의 가장 큰 매력이라는 점에 대해서도 인정 안 할 수가 없습니다.
한편 아두이노의 이면에는 이렇게 쉽게 사용할 수 있도록 배려해 놓은 정말 주도면밀한 기술적 완성도가 곳곳에 숨겨져 있습니다.
다시 우리가 다루고 있는 오렌지보드WiFi 보드를 살펴보면, 아두이노 우노의 ATmega328P와는 비교가 불가능한 막강한 성능을 가진 WizFi250 와이파이 모듈이 보드상에 탑재되어 있고, 이 모듈을 통해 인터넷상의 구성원이 되도록 해주고 있는 것입니다.
자 그렇다면 구성원으로서 오렌지보드WiFi가 되기 위해서는 소정의 약속을 지킬 필요가 있고, 이를 코코팹사에서 라이브러리 형태로 배포를 하고 있으며 그 라이브러리 사용법을 예제를 통해 설명하고 있습니다.
우선은 예제대로 실행을 해본 후 어떤 결과가 나오는지를 먼저 보도록 하겠습니다.
우리의 아두이노는 예제부터 일단 돌려보는 것이 최곱니다.
이 예제는 WebClient로 오렌지보드WiFi가 Client가 되어 웹에 있는 서버에서 데이터를 받아 아두이노의 시리얼 모니터창에 뿌리는 것입니다. 처음부터 생소한 용어가 살짝 등장하긴 합니다만, 일단 아래 위치로 가서 WebClient를 내려받아 실행합니다.
이때 SSID와 PASSWORD는 이글을 읽는 독자님들의 무선공유기(AP)에 접근이 될 수 있는 이름으로 올바르게 넣어줍니다. 그러면 3.1.3의 그림과 같은 화면을 볼 수 있게 될 것입니다.
코드를 업로드하기 전에 아래 소스코드에 빨간 색깔로 표시된 부분을 반드시 제대로 입력해야 에러없이 동작될 것입니다. SSID에는 접속할 AP의 ID를 작성하고 PASSWORD부분에는 WPA/PSK 패스워드를 작성하시면 됩니다.
3.1.2. webclient 소스코드
#include <SPI.h>
#include “WizFi250.h”
char ssid[] = “myLivngRoom(2.4GHz)”; // 접속코자하는 무선공유기의 SSID
char pass[] = “??????????”; // 공유기에 설정된 암호
int status = WL_IDLE_STATUS; // 와이파이 상태변수의 초기 값
char targetServer[] = “arduino.cc”;
// 이더넷 클라이언트 오브젝트 초기화
WiFiClient client;
void printWifiStatus();
void setup()
{
Serial.begin(115200);
WiFi.init();
// WizFi250 와이파이모듈이 잘 동작될 수 있는지 검사해 본다.
if (WiFi.status() == WL_NO_SHIELD) {
Serial.println(“WiFi shield not present”);
// 만일 문제가 있다면 여기서 홀딩한다.
while (true);
}
// 와이파이 망으로 연결을 시도한다.
// 연결이 성공적으로 되어야 루프를 벗어날 수 있다.
while ( status != WL_CONNECTED) {
Serial.print(“Attempting to connect to WPA SSID: “);
Serial.println(ssid);
// 암호방식이 WPA/WPA2인 망에 연결이 진행중인 상태값
status = WiFi.begin(ssid, pass);
}
// 와이파이망에 성공적으로 연결이 됨
Serial.println(“You’re connected to the network”);
printWifiStatus();
Serial.println();
Serial.println(“Starting connection to server…”);
// 목적지 서버인 arduino.cc에 80번 포트로 연결을 시도하고 결과를
// 시리얼모니터로 출력한다.
if (client.connect(targetServer, 80)) {
Serial.println(“Connected to server”);
// 나에게 asciilogo.txt를 보내 달라고 요청한다.
client.println(“GET /asciilogo.txt HTTP/1.1″);
client.println(“Host: arduino.cc”);
client.println(“Connection: close”);
client.println();
}
}
void loop()
{
// 타겟서버와 연결이 이뤄졌고 asciilogo.txt을 보내달라고 요청을 setup()에서
// 하였으므로 보내오는 값을 시리얼모니터창에 뿌리는 작업을 한다.
while (client.available()) {
char c = client.read();
Serial.write(c);
}
// if the server’s disconnected, stop the client
// 타켓서버와 연결이 끊어진 상태라면 Disconnecting from server…라는 메시지를 뿌리고 홀딩한다.
if (!client.connected()) {
Serial.println();
Serial.println(“Disconnecting from server…”);
client.stop();
// 여기서 부터는 아무것도 할 수 없는 상태에 접어든다.
while (true);
}
}
void printWifiStatus()
{
// 방금 내가 연결한 와이파이망의 SSID를 읽어와 시리얼 모니터로 출력 한다.
Serial.print(“SSID: “);
Serial.println(WiFi.SSID());
// 방금 내가 연결한 공유기로부터 할당 받은 IP주소를 시리얼 모니터로 출력한다.
IPAddress ip = WiFi.localIP();
Serial.print(“IP Address: “);
Serial.println(ip);
// 나와 공유간의 무선전송구간 수신강도를 표출 한다.
long rssi = WiFi.RSSI();
Serial.print(“Signal strength (RSSI):”);
Serial.print(rssi);
Serial.println(” dBm”);
}
3.1.3. 시리얼모니터창에 출력된 결과 값
3.1.4. 예제 설명
사실 우리는 그냥 예제코드를 복사하여 붙여넣은 후 업로드 시키면 동작되는 정말 편리한 상황에서 이 제품을 사용 중에 있기는 합니다만, 보다 심도있는 학습을 해보고자 한다면 아주 기초적인 것 몇 가지 정도는 짚어볼 필요가 있다고 봅니다.
우선적으로 네트워크가 어떻게 돌아가는지 간단하게 살펴보도록 하겠습니다. 그냥 넘어가려니 너무도 막막해 여기저기 글들을 참고하여 꼭 필요한 정보만 서술해보려 합니다.
■ tcp/ip의 동작과정
아래 그림은 네트웍 관련 서적이나 인터넷상에서 자주 언급되는 흔한 내용입니다.
앞의 예제에서 우리는 「char targetServer[] = “arduino.cc”;」라는 라인을 서두에 적었습니다. 호스트A는 오렌지보드WiFi가 되고 호스트B는 arduino.cc가 된 것입니다.
응용계층에 해당되는 것은 정확히는 「client.println(“GET /asciilogo.txt HTTP/1.1″);」라인에 해당되는 것으로써 호스트B인 arduino.cc라는 서버에게 asciilogo.txt라는 파일을 열어 보내라는 응용프로그램(응용계층 = 소프트웨어)을 돌린 것이 됩니다.
arduino.cc는 잘 아시는 바와 같이 이탈리아에서 운영 중인 서버고 우리는 대한민국에 있으므로, 매우 많은 라우터를 거치고 또 거쳐서 그곳까지 가는 것이며 인터넷 세상이 그러한 연결을 도맡아 해주고 있는 것입니다.
그런데 응용계층 말고도 전송계층이라는 것이 위 그림에 적시되어 있습다. 영어로는 Transport Layer라고 합니다만 TCP 몇 번 포트, UDP 몇 번 포트 등으로 우리는 일상적으로 부르곤 합니다.
위 예제에서 「if (client.connect(targetServer, 80))」라는 라인에서 arduino.cc라는 서버 주소로 연결을 하되 80번 포트번호를 사용하는 의미가 되며, 이 80번 포트를 HTTP(HyperText Transfer Protocol) 서비스라고 부릅니다. 아주 원론적으로 이를 명기한다면 「http://www.arduino.cc:80」이 되는 것이며, 실제로 인터넷익플로러 등 웹브라우저의 주소에 이렇게 쳐도 아무런 에러 없이 페이지가 잘 열리게 됩니다.
네트워크계층은 IP주소와 연관되어 있는 부분입니다.
우리는 이따금씩 언론에서 “중국의 해커, 북한 등으로부터 공격이 있었다.” 라는 말을 듣곤 합니다. 이는 전세계의 IP주소를 할당하고 관리하는 기관이 있으며 이 기관을 통해 각 국가가 사용하는 IP주소가 명확히 설정이 되어 있으며 각 국가는 정부에서 관리하는 자국의 IP주소관리 정책에 따라 사용할 수 있는 IP주소가 제한되게 됩니다.
우선 인터넷 통신이 되게 하기 위해서는 기본적으로 내 IP주소와 상대방의 IP주소가 필요합니다.
내 IP주소를 받는 방법은 몇 가지가 있으나 우리가 테스트하고 있는 WizFi250은 Wi-Fi를 기반으로 동작되므로 접속되는 AP 장치 즉, 공유기로부터 받아야만 합니다. 이때 IP를 받는 방법 중 AP가 일정한 룰에 의해 자동으로 부여해 주는 방식을 DHCP(Dynamic Host Configuration Protocol)라하며, 이렇게 받은 IP주소를 우리의 예제코드에서는 아래와 같이 보여주고 있습니다.
■ IP 주소를 출력하는 소스코드
IPAddress IP = WiFi.localIP(); // IPAddress라는 타입으로 된 변수 IP에 주소값을 담아라
Serial.print(“IP Address: “); // 시리얼모니터 창에 IP Address:를 출력하라
Serial.println(ip); // 시리얼모니터 창에 IP 값을 출력하고 줄바꿈을 하라
■ 위 소스에 대한 실제 출력 값
SSID: myLivngRoom(2.4GHz)
IP Address: 172.30.1.41
Signal strength (RSSI):-32 dBm
□ 웹서버와 통신
위 예제는 Arduino사이트에서 asciilogo.txt라는 데이터를 읽어오는 예제로, 코드를 보시면 Server에 arduino.cc가 작성되어 있는 것을 볼 수 있습니다.
Arduino.cc에 접속한 다음 GET 명령어로 asciilogo.txt라는 데이터를 아두이노로 가져오게 됩니다. 정말로 이런 데이터를 가져오는지 확인하고 싶다면 웹브라우저를 연 후에 http://Arduino.cc/asciilogo.txt를 주소창에 입력하면 똑같은 데이터가 웹브라우저에 출력되는 것을 확인할 수 있습니다.
이렇게 웹서버와 통신하는 가장 간단한 방법이 GET이라는 방법이 있으며 보안성 측면에서는 매우 취약하나 어떻게 동작이 일어나는지 학습해 보는 것이 목적이므로 크게 문제되지는 않는다고 봅니다. 유사한 방법으로 POST라는 방식으로도 원하는 명령을 보낼 수 있습니다.
■ 웹서버와 통신하는 GET 방식과 POST방식의 차이점
어떠신가요? 아마도 이글을 읽는 독자님들은 인터넷에 접속하여 데이터 한 번 받아오기가 이리도 힘든가라고 여길 수도 있을 것입니다. 순전히 제 개인적인 생각입니다만, 오늘날의 인터넷서비스는 그간 인류가 쌓아온 지식, 지혜의 꽃이라고 저는 생각합니다. 그러니 어찌 단순하고 쉽게 돌아가겠습니까?
사실 그런 깊숙한 상황을 잘 몰라도 우리의 WizFi250 제작자들은 매우 쉽게 접할 수 있도록 배려해 놓았으므로 별로 길지 않은 코드로 인터넷에 접속해 데이터를 받아와 시리얼 모니터에 뿌리는 것 까지를 쉽게 해볼 수 있었습니다.
다음으로는 오렌지보드WiFi에 내장된 A/D 변환기능을 활용하여 조도값을 웹상에서 읽어보는 기능을 실험해 보고자 합니다.
이번 예제에서는 WiFi보드를 Client로 사용해봤으니 다음 예제에서는 반대로 WiFi보드를 Server로 사용해보는 예제를 돌려 보겠습니다.
※ 참고 : http://kocoafab.cc/tutorial/view/649
3.2. 조도센서 값을 내PC 웹 브라우저로 출력하기
3.2.1. 개요
코코아팹에서 2번째 튜토리얼로 오렌지보드WiFi에 조도센서를 장착한 후 PC에서 보드에 접근하여 조도 값을 보여주는 예제를 제시하고 있습니다.
첫 번째 예제가 오렌지보드WiFi의 WiFi 기능이 동작되는 것을 간단히 테스트 해보는 것이 목적이라면, 이번 예제는 실제로 아주 간단한 응용을 통해 웹서버의 개념에 대해 이해하는 과정이라고 보면 되겠습니다.
■ 회로도
회로도는 매우 단순해 아두이노의 A0에 CdS와 10K 저항으로 분압 된 결과 값이 들어가도록 구성하면 됩니다.
ATmega328P는 5V에서 동작되는 칩이므로 전원도 10KR의 반대편은 3.3V가 아니라 5V에 꽂아 주어야 합니다.
■ 필요한 부품명 수량 상세 설명
1 오렌지보드WiFi x 1 (WizFi250을 사용한 WiFi보드)
2 조도 센서 x 1 (CdS)
3 저항 10Kohm x 1
3.2.2. 시리얼 모니터 실행
우선 소스코드를 http://kocoafab.cc/tutorial/view/650에서 받아 업로드해 봅니다. 위와 같은 회로를 꾸미지 않아도 동작 되긴 합니다만, 이 경우 제대로 된 조도값을 얻어 올 수는 없게 됩니다. 제대로 업로드가 되었다면 시리얼 모니터를 실행 시켰을 때 아래와 같은 화면을 볼 수가 있을 것입니다.
Opening port
Port open
[WizFi250] Initializing WizFi250
Attempting to connect to WPA SSID: myLivngRoom(2.4GHz)
[WizFi250] Connected to myLivngRoom(2.4GHz)
You’re connected to the network
SSID: myLivngRoom(2.4GHz)
IP Address: 172.30.1.22
To see this page in action, open a browser to http://172.30.1.22
[WizFi250] Server started on port 80[WizFi250] New client
New client Start!—–>
Sending response
<———-Client disconnected
[WizFi250] New client
New client Start!—–>
HTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Encoding: gzip, deflate, sdch
Accept-Language: ko-KR,ko;q=0.8,en-US;q=0.6,en;q=0.4
Sending response
<———-Client disconnected
[WizFi250] New client
New client Start!—–>
Sending response
<———-Client disconnected
[WizFi250] New client
New client Start!—–>
hrome/55.0.2883.87 Safari/537.36
Accept: image/webp,image/*,*/*;q=0.8
Referer: http://172.30.1.22/
Accept-Encoding: gzip, deflate, sdch
Accept-Language: ko-KR,ko;q=0.8,en-US;q=0.6,en;q=0.4
Sending response
<———-Client disconnected
시리얼 모니터를 통해 웹서버의 IP주소가 172.30.1.22란 것을 알 수 있습니다.
크롬 웹브라우저 등을 활용하여 주소창에 HTTP://172.30.1.22를 치면 아래와 같은 결과 화면을 볼 수 있을 것입니다.
3.2.3. 소스코드
#include <SPI.h>
#include “WizFi250.h”
char ssid[] = “myLivngRoom(2.4GHz)”; // 접속코자하는 무선공유기의 SSID
char pass[] = “???????????”; // 공유기에 설정된 암호
int status = WL_IDLE_STATUS; // 와이파이 상태변수의 초기 값
int reqCount = 0; // 수신 재전송 횟수용 카운터
WiFiServer server(80);
void printWifiStatus();
void setup()
{
// 디버깅을 위한 시리얼 모니터포트 초기화
Serial.begin(115200);
// 와이파이모듈 초기화
WiFi.init();
// 와이파이모듈과 통신이 가능한 상태인지 검사
if (WiFi.status() == WL_NO_SHIELD) {
Serial.println(“WiFi shield not present”);
// 통신이 가능한 상태가 아니라면 여기서 홀딩하라
while (true);
}
// 와이파이망에 연결을 시도한다.
while (status != WL_CONNECTED) {
Serial.print(“Attempting to connect to WPA SSID: “);
Serial.println(ssid);
// 연결에 대한 상태값을 받아 온다.
status = WiFi.begin(ssid, pass);
}
Serial.println(“You’re connected to the network”);
printWifiStatus();
// 80번 포트로 리슨하는 웹서버를 구동하라.
server.begin(); // Serial Monitor out: [WizFi250] Server started on port 80
}
void loop()
{
// 클라이언트에서 연결을 해 오는 것에 대해 수신대기를 합니다.
WiFiClient client = server.available();
// Serial Monitor out : [WizFi250] New client
if (client) {
Serial.println(“New client Start!—–>”);
// http 요청은 빈 줄로 끝납니다.
boolean currentLineIsBlank = true;
while (client.connected())
{
if (client.available()) {
// 클라이언트로부터 1문자를 수신합니다.
char c = client.read();
Serial.write(c);
// 줄 바꿈 문자 (줄 바꿈 문자 수신)가 있고 줄이 비어 있으면
//http 요청이 끝났기 때문에 회신을 보낼 수가 있습니다.
if (c == ‘\n’ && currentLineIsBlank) {
Serial.println(“Sending response”);
// 표준 http 응답 헤더를 보내려면 많은 println 문 대신
//\r\n을 사용하여 데이터 전송 속도를 높입니다.
client.print(
“HTTP/1.1 200 OK\r\n”
“Content-Type: text/html\r\n”
“Connection: close\r\n” // 응답이 완료되면 연결이 닫힙니다.
“Refresh: 20\r\n” // 20초마다 자동으로 페이지를 새로 고칩니다.
“\r\n”);
client.print(“<!DOCTYPE HTML>\r\n”);
client.print(“<html>\r\n”);
client.print(“<h1>Hello World!</h1>\r\n”);
client.print(“Requests received: “);
client.print(++reqCount);
client.print(“<br>\r\n”);
client.print(“Analog input A0: “);
client.print(analogRead(0));
client.print(“<br>\r\n”);
client.print(“</html>\r\n”);
break;
}
if (c == ‘\n’) {
// 새라인을 시작한다.
currentLineIsBlank = true;
} else if (c != ‘\r’) {
// 현재 라인에 어떤 문자가 들어 옴
currentLineIsBlank = false;
}
}
} // while
// 웹 브라우저가 데이터를 받을 시간을 줍니다.
delay(10);
// 연결을 종료한다:
client.stop();
Serial.println(“<———-Client disconnected”);
}
}
void printWifiStatus()
{
// 방금 내가 연결한 와이파이망의 SSID를 읽어와 시리얼 모니터로 출력 한다.
Serial.print(“SSID: “);
Serial.println(WiFi.SSID());
// 방금 내가 연결한 공유기로부터 할당 받은 IP주소를 시리얼 모니터로 출력한다.
IPAddress ip = WiFi.localIP();
Serial.print(“IP Address: “);
Serial.println(ip);
// 브라우져에 어디서부터 접속을 시도 했는지를 표출한다.
Serial.println();
Serial.print(“To see this page in action, open a browser to http://”);
Serial.println(ip);
Serial.println();
}
이 부분이 WizFi250 모듈을 웹서버로 만들어주는 부분이 됩니다.
// 80번 포트로 리슨하는 웹서버를 구동하라.
server.begin(); // Serial Monitor out: [WizFi250] Server started on port 80
// 클라이언트에서 연결을 해 오는 것에 대해 수신대기를 합니다.
WiFiClient client = server.available();
// Serial Monitor out : [WizFi250] New client
이 부분은 웹브라우저로부터 접속을 시도해 오면 시리얼 모니터에 [WizFi250] New client를 뿌린 후 실려오는 데이터를 실제로 받을 준비를 합니다.
3.3. OpenWeatherMap API를 사용하여 날씨 데이터 받아오기
3.3.1. 개요
이번에는 난이도를 좀 더 올려서 API를 사용해 WiFi 쉴드로 날씨정보를 가져오는 예제를 실행해봅니다.
이 튜토리얼 역시 코코아팹 웹사이트에 올려져 있는 내용을 참고삼아 진행해 보도록 하겠습니다. 이 튜토리얼을 성공적으로 따라하시면 인터넷 세상이 돌아가는 것에 대해 어렴풋하게나마 이해가 되시리라 봅니다.
코코아팹 웹싸이트에서도 소개하고 있듯이 API란 Application Programming Interface의 약자로 프로그램이나 어플리케이션이 정보처리를 위해 운영체제에 호출하는 함수나 서브루틴의 집합을 말합니다. WebServer는 API를 통해 사용하기 쉬운 인터페이스를 Client에게 제공하고, Client는 API를 가져와서 사용함으로써 손쉽게 원하는 정보에 접근할 수 있습니다.
이번에 실행할 OpenWaetherMap 또한 날씨정보 API를 제공하는 사이트로서, API를 가져온다면 아두이노에서도 WiFi로 날씨정보를 받아올 수 있습니다.
3.3.2. OpenWeatherMap에서 API key받아오기
먼저 http://openweathermap.org/에 접속해 아이디가 없을 경우 회원가입을 하여 로그인을 합니다.
API keys 탭에 들어가면 자신만이 사용가능한 고유한 API key 값을 확인할 수 있습니다. API key 값은 API에서 정보를 가져오기 위한 필수 정보이니 따로 복사를 합니다.
3.3.3. 아두이노 코드 업로드 하기
다시 아두이노로 돌아와서, 아래 코드를 복사 후 오렌지보드WiFi에 업로드시킵니다.
#include <SPI.h>
#include <WizFi250.h>
int getInt(String input);
#define VARID “??????????????????????????????????”
char ssid[] = “myLivngRoom(2.4GHz)”; // 접속코자하는 무선공유기의 SSID
char pass[] = “??????????”; // 공유기에 설정된 암호
int status = WL_IDLE_STATUS; // 와이파이 상태변수의 초기 값
char server[] = “api.openweathermap.org”;
unsigned long lastConnectionTime = 0; // 마지막으로 서버에 연결한 시간
const unsigned long postingInterval = 1000L; // 업데이트 간격 (밀리 초).
boolean readingVal;
boolean getIsConnected = false;
//String valString;
int val, temp;
float tempVal;
String rcvbuf;
// 이더넷 클라이언트 오브젝트를 초기화 한다.
WiFiClient client;
void httpRequest();
void printWifiStatus();
void setup()
{
// 디버깅을 위한 시리얼 포트 초기화
Serial.begin(115200);
Serial.println(F(“\r\nSerial Init”));
WiFi.init();
// 와이파이모듈이 정상인지 확인
if (WiFi.status() == WL_NO_SHIELD) {
Serial.println(“WiFi shield not present”);
// don’t continue
while (true);
}
// 와이파이망에 연결 시도
while (status != WL_CONNECTED) {
Serial.print(“Attempting to connect to WPA SSID: “);
Serial.println(ssid);
// Connect to WPA/WPA2 network
status = WiFi.begin(ssid, pass);
}
Serial.println(“You’re connected to the network”);
printWifiStatus();
}
void loop()
{
// 인터넷 연결에서 들어오는 데이터에 대해 파싱작업을 한다.
String valString;
while (client.available())
{
if (rcvbuf.endsWith(“\”temp\”:”)) { //서버로 부터 보내져 오는 문자열에 temp:을 찾는다.
readingVal = true;
valString = “”;
}
char c = client.read();
if (c != NULL) {
if (rcvbuf.length() > 30)
rcvbuf = “”;
rcvbuf += c;
Serial.write(c);// DEBUG
}
if (readingVal) {
if (c != ‘,’) {
valString += c; // ‘temp:’ 다음에 콤마(,)가 나타날때 까지 valstring에 담는다.
Serial.write(c); // DEBUG
}
else {
readingVal = false;
//Serial.println(valString); // DEBUG
// 절대온도를 상대온도로 변환시키기 위해 273을 뺌
tempVal = valString.toFloat() – 273.0;
//Serial.println(tempVal); // DEBUG
}
}
}
if (millis() – lastConnectionTime > (postingInterval * 5))
{
if (getIsConnected) {
Serial.println(valString);
Serial.println(F(“===================”));
Serial.print(F(“Temperature : “));
Serial.println(tempVal);
Serial.println(F(“===================”));
}
httpRequest();
}
rcvbuf = “”;
}
// 이 메서드는 서버로 HTTP 연결을 진행합니다.
void httpRequest() {
Serial.println();
// 새로운 요청을 하기전에 연결을 일단 닫는다.
// 이 작업으로 와이파이모듈의 소켓이 해제된다.
client.stop();
// 성공적으로 날씨정보 서버에 접속이 되는지 점검해 본다.
if (client.connect(server, 80)) {
Serial.println(“Connecting…”);
// 날씨정보를 얻고 싶은 도시명, 국가 정보를 웹서버로 보낸다.
client.print(“GET /data/2.5/weather?q=Anyang,kr&appid=”);
client.print(VARID);
client.println(” HTTP/1.1″);
client.println(“Host: api.openweathermap.org”);
client.println(“Connection: close”);
client.println();
// 연결이 만들어진 마지막 시각을 기록해 둔다.
lastConnectionTime = millis();
getIsConnected = true;
}
else {
// 연결이 성공적이지 못할 경우 에러처리를 한다.
Serial.println(“Connection failed”);
getIsConnected = false;
}
}
void printWifiStatus() {
// 연결된 SSID를 표출한다.
Serial.print(“SSID: “);
Serial.println(WiFi.SSID());
// IP주소를 표출한다.
IPAddress ip = WiFi.localIP();
Serial.print(“IP Address: “);
Serial.println(ip);
// 수신강도 인쇄
long rssi = WiFi.RSSI();
Serial.print(“Signal strength (RSSI):”);
Serial.print(rssi);
Serial.println(” dBm”);
}
// 스트링을 정수형으로 변환 시킨다.
int getInt(String input) {
char carray[20];
//Serial.println(input);
input.toCharArray(carray, sizeof(carray));
//Serial.println(carray);
temp = atoi(carray);
return temp;
}
3.3.4. 결과 화면
위 코드를 실행시킨 결과는 아래와 같이 주기적으로 필자의 거주지인 경기도 안양시의 현재 온도값이 시리얼 모니터상에 출력됩니다.
지역을 변경하고 싶다면?! 코드에서는 기본적으로 Anyang으로 되어 있지만 지역을 변경할 경우 다른 지역의 온도값을 출력해볼 수 있습니다.
Opening port
Port open
Attempting to connect to WPA SSID: myLivngRoom(2.4GHz)
[WizFi250] Connected to myLivngRoom(2.4GHz)
You’re connected to the network
SSID: myLivngRoom(2.4GHz)
IP Address: 172.30.1.22
Signal strength (RSSI):-42 dBm
Connecting…
HTTP/1.1 200 OK
Server: openresty
Date: Mon, 02 Jan 2017 13:34:27 GMT
Content-Type: application/json; charset=utf-8
Content-Length: 552
Connection: close
X-Cache-Key: /data/2.5/weather?q=anyang,kr
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: GET, POST
{“coord”:{“lon”:126.93,”lat”:37.39},”weather”:[{"id":701,"main":"Mist","description":"mist","icon":"50n"},{"id":721,"main":"Haze","description":"haze","icon":"50n"},{"id":741,"main":"Fog","description":"fog","icon":"50n"}],”base”:”stations”,”main”:{“temp”:227755..1166,”pressure”:1022,”humidity”:83,”temp_min”:273.15,”temp_max”:277.15},
“visibility”:1200,”wind”:{“speed”:1,”deg”:180},”clouds”:{“all”:75},”dt”:1483363200,”sys”:{“type”:1,”id”:8519,”message”:0.0089,”country”:”KR”,”sunrise”:1483310810,
“sunset”:1483345579},”id”:1846898,”name”:”Anyang”,”cod”:200}
[DISCONNECT 0]ÿ
===================
Temperature : 2.16
===================
날씨에 관한 다른 정보를 추출하고 싶다면?! 현재는 코드상에서 온도 값만을 가져와서 출력해주고 있습니다. 다른 정보를 출력하기 위해서 API에서 전달해주는 데이터를 먼저 분석해 볼 필요가 있습니다. 데이터를 요청하면 XML 형식으로 전달해주며, 우리는 코드상에서 파싱을 통해 원하는 정보만 추출하게 됩니다.
온도의 경우 “temp”라는 글 뒤에 절대온도로 306.15와 같이 보여집니다.
위에 보여드린 코드가 API에서 요청받은 XML 데이터에서 온도 값을 추출하는 코드입니다. 데이터를 차근차근 읽어오면서 “temp”: 라는 값이 오면 그 뒤에 값을 읽어서 온도로 저장하는 형식입니다. 만일 다른 데이터를 가지고 오고 싶다면 temp 대신에 pressure나 humidity를 넣어 파싱을 한다면 다른 데이터를 읽어올 수 있겠죠? 물론 온도의 경우 절대온도로 받아왔기 때문에 -273을 해주었지만, 다른 데이터는 그 데이터 타입에 맞게 값을 조절해 주어야 합니다.
3.4. 인터넷시간 동기화(NTP) 서비스
3.4.1. 개요
임베디드 장치를 활용해서 정확한 시간을 얻을 수만 있다면 그것 하나만으로도 모든 것이 용서가 되고도 남음이 있다고 봅니다. 오렌지보드WiFi도 당연히 인터넷접속이 가능한 기기이므로 가능하리라 생각되기에, 마지막 테스트로 인터넷시간 동기화에 대해서 다뤄보고자 합니다.
우선 아래와 같은 코드를 업로드합니다. 이 코드는 필자가 상당부분을 직접 작성한 것으로 NTP를 활용해 실제 어떻게 이용할 수 있는가를 가늠해 볼 수 있는 중요한 예제에 해당된다고 봅니다.
3.4.2. 시리얼모니터 실행 결과
프로그램이 복잡해지고 분량이 많아지면 아두이노 IDE에서는 아무래도 작업하기가 힘들어집니다.
■ 시리얼 모니터 창
필자는 간단한 프로그래밍이나 예제코드 등은 아두이노 IDE를 활용하나 본격적인 프로그래밍을 할 때는 Microsoft Visual Studio상에서 작업을 하곤 합니다.
위 시리얼 모니터 창은 비주얼 스튜디오상에서 뿌려지고 있는 모습입니다. 여기서 주목해야하는 부분이 아래의 문자열이며 NMEA형식으로 epoch타임값이 출력되고 있음을 알 수 있습니다.
■ Microsoft Visual Studio상 개발환경
■ Sample 전문
$Wi-Fi,1483423819,myLivngRoom(2.4GHz),pppppppp,203.248.240.103*33
3.4.3. NTP 서버(zero.bora.net) 접속용 소스코드
********************************************************************** 코코아팹 오렌지보드WiFi를 활용한 NTP(Network Time Protocol) 프로그램
***********************************************************************
* ProjectName: myUDPNTPclient2_OrangeWiFi_ATmega328P
* Dependencies: <SPI.h>,”WizFi250.h”,”WizFi250Udp.h”, <TimeLib.h>
* Processor: ATmega328P(28pin QFP)
* Complier: arduino.cc-1.6.9
* Microsoft Visual Studio vMicro
* Company:
* Author: 금강초롱(blog.naver.com/crucian2k3)
* History
* Date Version Description
* 2017-01-03 v1.0 최초작성
* 이크로칩사의 NTP 클라이언트 동작방식과 ESP8266기반의
* NTP클라이언트 동작방식을 참고하여 오렌지보드WiFi에 적합토록
* 전면 재작성 됨
***********************************************************************
***********************************************************************
* 동작내용:
* SSID, PASSWORD 등이 제대로 설정된 상황이라면 오렌지보드WiFi가 인터넷에 접속하여 일정한 간격으로 표준시간을 얻어와 자신이 사용하거나 다른 기계장치로 NMEA 전문규격에 부합토록 보내줄 수 있음
* 활용방법:
*전문을 수신하는 측에서는 ‘$Wi-Fi’ 문자열이 오면 버퍼에 저장을 시작하여 crlf가 나타나면 저장을 종료한다.
* *가 나타난후 2개의 문자는 2의 보수형으로 되어있는 체크섬이며, 계산하여 문제가 없다면 해당 전문에 실려 있는 epoc값을 바탕으로 9시간을 더해 한국시간에 맞게 활용하면 된다.
* Notes: 사용포트
* – D8 : LED0 Anode 연결
***********************************************************************
#include <SPI.h> // WizFi250 모듈과 ATmega328간 통신방식이 SPI
#include “WizFi250.h”
#include “WizFi250Udp.h”
#include <TimeLib.h> // EPOCH타임을 처리하기 위한 라이브러리
#define KOREA_TIME_ZONE 9 // 기준시에 비해 우리나라가 9시간 빠름
#define KOREA_TIME_ZONE_SEC (3600ul * KOREA_TIME_ZONE)
#define STATUSLED 8 // 동작상태 표출용 LED 출력포트(1초 간격 점멸시 정상 동작 중)
#define NTP_REFRESH_SECS 10
#define TICKS_PER_SECOND (1000L)
#define TICK_SECOND ((TICK)TICKS_PER_SECOND)
#define TICK_1000mS ((TICK)TICK_SECOND) // 1000ms 간격으로 실행 시
#define TICK_100mS ((TICK)TICK_SECOND / 10UL) // 100ms 간격으로 실행 시
#define TICK_10mS ((TICK)TICK_SECOND / 100UL) // 10ms 간격으로 실행 시
#define TICK_1mS ((TICK)TICK_SECOND / 1000UL) // 1ms 간격으로 실행 시
typedef unsigned long TICK; // TICK타입을 unsigned long으로 설정
char ssid[] = “myLivngRoom(2.4GHz)”; // 접속코자하는 AP의 SSID
char pass[] = “??????????”; // 패스워드
int status = WL_IDLE_STATUS; // AP의 상태값을 나타내는 변수
char wi_fiHeader[] = “$Wi-Fi”; // NTP 시각 외부 전송용 헤더
char timeServer[] = “zero.bora.net”; // NTP(Network Time Protocol) 서버주소
unsigned int localPort = 2390; // UDP 패킷 수신을 위한 로컬포트 지정
IPAddress timeServerIP; // 원격 NTP 서버의 IP주소
const int NTP_PACKET_SIZE = 48; // 메시지의 첫 48바이트에 NTP 정보가 담겨짐
uint8_t packetBuffer[NTP_PACKET_SIZE]; // 입출력이 일어나는 데이터에 대한 버퍼
String stringOne;
char LineBuffer[100];
int ledState = LOW;
int hours = 12; // 아날로그 모양의 시계를 움직이기 위한 초기 시간값
int minutes = 0;
int seconds = 0;
// UDP 인스턴스를 통해 udp데이터 수수가 일어난다.
WiFiUDP Udp;
unsigned long sendNTPpacket(char *ntpSrv);
unsigned long TickGet(); // 마이크로칩 라이브러리 스타일 명칭으로 호출
void setup()
{
Serial.begin(115200); // 디버깅을 위한 시리얼포트 초기화
pinMode(STATUSLED, OUTPUT); // 아두이노:D8
WiFi.init();
// 와이파이모듈과 오렌지보드간 SPI 통신이 가능한지 검사
if (WiFi.status() == WL_NO_SHIELD) {
Serial.println(“WiFi shield not present”);
// don’t continue
while (true);
}
}
void loop()
{
static enum
{
SM_HOME = 0,
SM_ATTEMPT_CONNECTION,
SM_WAIT_WL,
SM_WL_CONNECTED,
SM_SEND_NTP,
SM_WAIT_NTP_RECV,
SM_RCVCHECK,
SM_READPACKET,
SM_WAIT10SEC,
SM_NEXT,
SM_WAIT
} TASKState = SM_HOME;
static TICK myTimer0 = 0;
static int cycleCounter = 0;
int cb;
// 1900년부터 시작하는 NTP 타임체계를 1970년부터 시작하는 epoc 타임체계로 변환하기 위한 상수값
const unsigned long seventyYears = 2208988800UL;
unsigned long highWord;
unsigned long lowWord;
unsigned long secsSince1900;
unsigned long epoch;
// NTP 전문 생성용 변수
unsigned char calcchksum = 0;
unsigned char sl = 0;
unsigned char ct = 0;
unsigned char pl = 0;
int i;
switch (TASKState)
{
case SM_HOME:
// Wi-Fi망에 연결을 위한 사전 준비작업을 한다.
Serial.print(“Attempting to connect to WPA SSID: “);
Serial.println(ssid);
cycleCounter = 0;
myTimer0 = TickGet();
TASKState = SM_ATTEMPT_CONNECTION;
break;
case SM_ATTEMPT_CONNECTION:
// WPA/WPA2 망에 연결을 개시 한다.
status = WiFi.begin(ssid, pass);
if (status == WL_CONNECTED) {
// 연결에 성공되었음이 획인되면 다음 작업을 속행한다.
TASKState = SM_WL_CONNECTED;
}
else {
// 일단 연결에 실패하면….
// STATUSLED가 0.1초마다 한번씩 빠르게 점멸이 된다면 wi-fi를
// 찾지 못해 헤매는 중. 단, 오렌지보드wifi에서는 WizFi250모듈 내부
// 적으로 재전송 체계가 돌고 있는 것 같음 이로 인해 상태정보 응답
// 에 지연이 발생함
// ESP8266 등에서는 문제가 없는 코드이며, 오렌지보드에서도 큰 문
// 제는 없이 동작된다.
if (TickGet() – myTimer0 >= TICK_10mS * 10) {
myTimer0 = TickGet();
if (ledState == LOW)
ledState = HIGH; // LED on data
else
ledState = LOW; // LED off data
digitalWrite(STATUSLED, ledState);// LED action
// 만일 AP에 5초동안 연결을 반복 시도하였음에도 실패하면 에러처리 로직으로
cycleCounter++;
if (cycleCounter > 50) TASKState = SM_WAIT_WL;
}
}
break;
case SM_WAIT_WL:
// 5초 경과 후에도 여전히 연결이 되지 않은 경우 에러처리
if (WiFi.status() != WL_CONNECTED) {
// 5초가 경과되어도 여전히 접속되지 않으면 .을 한 개 출력한 후 처음부터 다시
Serial.print(“.”);
TASKState = SM_ATTEMPT_CONNECTION;
}
else {
// 만일 연결에 성공하면 retry counter를 찍은 후 다음 작업을 속행한다.
Serial.println(cycleCounter);
TASKState = SM_WL_CONNECTED;
}
break;
case SM_WL_CONNECTED:
// 성공적으로 wi-fi망에 연결되면 ip주소와 포트 번호를 표출한다.
Serial.println(“”);
Serial.println(“WiFi connected”);
Serial.print(“IP address: “);
Serial.println(WiFi.localIP());
Serial.println(“Starting UDP”);
Udp.begin(localPort);
Serial.print(“Local port: “);
Serial.println(localPort);
cycleCounter = 0;
TASKState = SM_SEND_NTP; // TIME 서버로 SNTP 패킷을 날린 후 응답을 기다린다.
break;
case SM_SEND_NTP:
// TIME 서버로 NTP 패킷을 전송한다.
//WiFi.hostByName(timeServer, timeServerIP);// ESP8266일 때 사용
Serial.println(“”); // 새로운 전문의 시작이 개시됨을 알리는 개행 작업
sendNTPpacket(timeServer); // time server로 NTP 패킷을 전송한다.
Serial.print(“Send NTP: “);
Serial.println(timeServer);
TASKState = SM_WAIT_NTP_RECV;
break;
case SM_WAIT_NTP_RECV:
// 타임서버로부터 응답이 왔는지를 검사하여 0.2초 후에도 수신이 되지
//않으면 한 번 더 요청을 해보고 그래도 응답이 1초 동안 없으면 wi-fi망에
//처음부터 다시 접속
cb = Udp.parsePacket();
if (!cb) {
// 정상적인 응답이 아닌 상태에 대한 처리
Serial.print(“*”);
cycleCounter++;
delay(100);
if (cycleCounter == 2) {
// TIME 서버로 NTP 패킷이 보내졌으나 0.2초를 기다려도
// 답신이 없으면 한 번 더 보내본다.
Serial.println(cycleCounter);
TASKState = SM_SEND_NTP;
}
else if (cycleCounter >= 10) {
// 답신이 없어 한 번 더 보내고도 1초가 또 경과되면 wi-fi망에
//이상이 있을 가능성이 높으므로 wi-fi 재접속을 시도해본다.
Serial.println(cycleCounter);
cycleCounter = 0;
TASKState = SM_WAIT_NTP_RECV;
}
}
else {
// 제대로 된 값이 온 것 같으면 데이터 파싱 단계를 진행한다.
Serial.println(cycleCounter);
cycleCounter = 0;
TASKState = SM_READPACKET;
}
break;
case SM_READPACKET:
// TIME 서버로 부터 NTP 패킷이 들어왔으므로 파싱 작업을 한다.
// Serial.print(“packet received, length=”);
// Serial.println(cb);
Udp.read(packetBuffer, NTP_PACKET_SIZE); // 일단 버퍼로 패킷을 옮긴다.
// 타임 스탬프는 수신된 패킷의 바이트 40에서 시작해 4바이트 또는
// 2 워드 길이이다. 먼저 두 워드구간을 추출해낸다.
highWord = word(packetBuffer[40], packetBuffer[41]);
lowWord = word(packetBuffer[42], packetBuffer[43]);
// 4 바이트 (두 워드)를 롱타입 정수로 머지합니다.
// 이것은 NTP 시간이 됩니다. (1900.1.1일 이후부터 현재까지의 초 값).
secsSince1900 = highWord << 16 | lowWord;
// Serial.print(“Seconds since Jan 1 1900 = ” );
// Serial.println(secsSince1900);
// 이제 NTP 시간을 우리가 일상적으로 사용하는 매일 시간형식(Unix Time)으로 변환합니다.
// Unix Time은 1970.1.1부터 시작합니다. 70년간을 초로 환산하면 2208988800:
// 즉, Unix Time을 얻기 위해 70년을 감해줍니다.
// 또한 이 시간을 epoch라고도 합니다.
epoch = secsSince1900 – seventyYears;
//Serial.print(“Unix time = “);
//Serial.println(epoch);
// stringOne에는 타임서버로부터 받은 unix epoch 타임 값이 들어가있다.
stringOne = String(epoch, DEC); // using a long and a base
************************************************************************
# NTP 타임 전문 생성 루틴:
1. 전문 생성목적
타임서버로부터 받아온 시간을 외부장치(전자시계, 서버 등등)에게 시리얼라인
으로 전송해주기 위해 GPS 수신기 등에서 사용하는 NMEA 전문 형태로 생상
하는 부분이다.
2. 전문의 형식 예(GPS신호와 매우 유사함)
$Wi-Fi,1470050489,myKT_WLAN,password,203.248.240.103*3A
[step1] [step2] [step3] [step4] [step5] [step6]
************************************************************************
// STEP1: 시리얼 버퍼에 “$Wi-Fi,” 를 시작 문자열로 발신하여 파싱이 일어나도록 한다.
sl = sizeof(wi_fiHeader) – 1; // null문자 위치 제거
pl = 0;
for (i = 0; i< sl; i++) {
LineBuffer[pl] = wi_fiHeader[i]; // copy to buffer : “$Wi-Fi,”
pl++;
}
LineBuffer[pl] = ‘,’; // 구분자를 추가한다.
pl++;
// STEP2: 시리얼버퍼에 “unix epoch 타임”을 어펜드한다.
sl = stringOne.length();
ct = 0;
while (sl) {
LineBuffer[pl] = stringOne[ct];
pl++; ct++;
sl–;
}
LineBuffer[pl] = ‘,’; // 구분자를 추가한다.
pl++;
// STEP3: 시리얼 버퍼에 SSID를 어펜드한다.
sl = sizeof(ssid) – 1; // null문자 위치 제거
for (i = 0; i< sl; i++) {
LineBuffer[pl] = ssid[i]; // copy to buffer : “ssid,”
pl++;
}
LineBuffer[pl] = ‘,’; // 구분자를 추가한다.
pl++;
// STEP4: 시리얼 버퍼에 wi-fi PASSWORD를 어펜드한다.
sl = sizeof(pass) – 1; // null문자 위치 제거
for (i = 0; i< sl; i++) {
LineBuffer[pl] = pass[i]; // copy to buffer : “pass,”
pl++;
}
LineBuffer[pl] = ‘,’; // 구분자를 추가한다.
pl++;
// STEP5: NTP 서버 IP주소를 파싱하여 어펜드한다.
timeServerIP = Udp.remoteIP();
for (i = 0; i<4; i++) {
//stringOne = String(timeServerIP[i], DEC);
stringOne = timeServerIP[i];
sl = stringOne.length();
ct = 0;
while (sl) {
LineBuffer[pl] = stringOne[ct];
pl++; ct++; sl–;
}
LineBuffer[pl] = ‘.’; pl++;
}
pl–; // IP주소의 맨 끝에 붙어있는 .은 제거 시켜 준다.
// STEP6: 체크섬을 계산한다.
calcchksum = 0;
for (i = 1; i < pl; i++) {
calcchksum = calcchksum ^ LineBuffer[i]; // USART 수신데이터로 직접 체크섬 계산
}
LineBuffer[pl] = ‘*’; pl++;
stringOne = String(calcchksum, HEX);
stringOne.toUpperCase();
LineBuffer[pl] = stringOne[0]; pl++;
LineBuffer[pl] = stringOne[1]; pl++;
// STEP7: 외부로 실제 전송을 단행한다.
for (i = 0; i< pl; i++) {
Serial.print(LineBuffer[i]);
}
Serial.println(“”);
//////////////////////////////////////////////////////////////////////////////////////
// DEBUG Code
// 아래는 이렇게 만들어진 시간값이 정말로 제대로 만들어 졌는지를 검증해보는 코드
// UTC시각은 그리니치천문대(Greenwich Meridian) 기준 시각이다. (GMT)
Serial.print(“The Local(Seoul, +9Hr) time is “);
setTime(epoch);
adjustTime(KOREA_TIME_ZONE_SEC); // 대한민국 시간대로 보정
Serial.print(year()); Serial.print(“/”);
Serial.print(monthStr(month())); Serial.print(“/”);
Serial.print(day());
Serial.print(“[");
Serial.print(hour()); Serial.print(":");
Serial.print(minute()); Serial.print(":");
Serial.print(second());
Serial.println("]“);
hours = hour();
minutes = minute();
seconds = second();
//////////////////////////////////////////////////////////////////////////////////////
cycleCounter = 0;
myTimer0 = TickGet();
TASKState = SM_NEXT;
break;
case SM_NEXT:
// 10초 간격으로 한번씩 동기화를 시도한다.
// STATUSLED가 1초마다 한번씩 점멸이 된다면 여기까지의 루프가 잘 돌고 있음을 의미
if (TickGet() – myTimer0 > TICK_100mS * 5UL) {
myTimer0 = TickGet();
if (ledState == LOW)
ledState = HIGH; // Note that this switches the LED *off*
else
ledState = LOW; // Note that this switches the LED *on*
digitalWrite(STATUSLED, ledState);
cycleCounter++;
// 타임서버로 부터 시간을 받아오는 주기를 설정한다.
// *2를 한것은 cycleCounter가 0.5초 단위로 증가하기 때문이다.
if (cycleCounter > NTP_REFRESH_SECS * 2) {
cycleCounter = 0;
// 연결이 성공적으로 잘되어 있는지 재검사하여 이상없으면 다음 작업을 속행
if (WiFi.status() == WL_CONNECTED) {
TASKState = SM_SEND_NTP;
}else {
// 만일 연결에 문제가 생기면 재접속 프로세스 진행
TASKState = SM_HOME;
}
}
}
break;
}
}
// send an NTP request to the time server at the given address
unsigned long sendNTPpacket(char *ntpSrv)
{
// set all bytes in the buffer to 0
memset(packetBuffer, 0, NTP_PACKET_SIZE);
// Initialize values needed to form NTP request
// (see URL above for details on the packets)
packetBuffer[0] = 0b11100011; // LI, Version, Mode
packetBuffer[1] = 0; // Stratum, or type of clock
packetBuffer[2] = 6; // Polling Interval
packetBuffer[3] = 0xEC; // Peer Clock Precision
// 8 bytes of zero for Root Delay & Root Dispersion
packetBuffer[12] = 49;
packetBuffer[13] = 0x4E;
packetBuffer[14] = 49;
packetBuffer[15] = 52;
// all NTP fields have been given values, now
// you can send a packet requesting a timestamp:
Udp.beginPacket(ntpSrv, 123); //NTP requests are to port 123
Udp.write(packetBuffer, NTP_PACKET_SIZE);
Udp.endPacket();
return 1;
}
unsigned long TickGet()
{
return millis();
}
3.4.4. 소스설명
이 프로그램은 NTP 프로토콜로 타임서버에 접속하기 위해 UDP를 사용한 점이 이전의 튜토리얼과는 다소 차이가 있는 점에 해당됩니다. 저는 제대로 동작되는 프로그램을 만들기 위해서는 에러처리가 중요한 부분에 해당된다고 봅니다.
이 프로그램에서는 AP에 접속하고자 하였으나 이미 에러가 난 경우 재접속하는 방안과 AP에 접속하여 한참 통신을 하는 과정에서 갑자기 AP가 사라지는 경우에 대한 대비가 되어 있습니다. 또한 다른 기기에게 타임 값을 넘겨줄 때 상대방에서 에러에 대한 부분을 검증해 볼 수 있도록 체크섬이 부가되어 있습니다.
비교적 상세히 한글 주석을 달아 놓았으므로 추가적인 설명은 생략하겠습니다.
거의 1주일 가량을 연속 가동해본 결과 짜여진 각본대로 잘 동작함을 확인했습니다.
이 기능을 활용해 실제로 디지털시계를 구현한다면 오렌지보드WiFi 만으로도 충분히 가치있는 프로젝트가 되지 않을까 조심스럽게 예상해 봅니다.
4. 총평
이번에는 한국형 아두이노 보드인 오렌지보드에 WiFi 쉴드를 온보드화 시킨 오렌지보드WiFi에 대해 살펴봤습니다. 필자가 가장 관심을 두었던 부분은 WizFi250 와이파이모듈과 ATmega328이 안정적으로 통신을 수행하며 동작이 잘 될 수 있을까 하는 점이었습니다. 위에서 언급된 바와 같이 여러 형태를 실험하는 내내 단 한 번도 양단간의 통신문제는 발생되지 않았습니다.
일단 안정적으로 동작이 된다는 것에 대해서는 이론의 여지가 없을 듯합니다. 이정도의 안정성을 보여주기 위해서는 무척 다양한 종류의 테스트를 수행했고 무난히 통과된 결과가 아닐까 예상해 봅니다.
다음으로 관심을 두었던 분야가 과연 이 보드로 무엇을 할 수 있을지에 대한 가이드와 관련된 부분이었습니다. 코코아팹 웹 싸이트에는 다음과 같은 예제가 기본적으로 10종 제시되어 있으며, 자세한 설명과 동작하는 장면에 대한 동영상까지 제시하고 있음을 보았을 때 기우였다는 것을 금방 알 수 있었습니다.
사실 LED가 동작되는 원리 하나만 갖고도 매우 어려운 학습에 해당 될 수 있으며, 아두이노 학습의 기초 중에 기초인 Blink만 배우는데도 어려워서 쩔쩔맬 수도 있습니다. 여기에다 PWM, ADC, UART, SPI, I2C 등으로 넘어가면 지레 질려버릴 수도 있을 듯합니다.
오렌지보드WiFi 에 장착된 WizFi250 모듈은 정확히는 무선랜 모듈이며 이더넷 통신을 지원하는 첨단기술이 집약된 제품이자 통신장치에 해당된다고 봅니다. 이 모듈을 제대로 활용하기 위해서는 TCP/IP 등 네트워크에 관련된 지식을 필요로 하며 웹이나 모바일을 기반으로 영역을 확대코자 한다면 CGI(Common Gateway Interface)에 대한 깊이있는 지식도 필요하다고 봅니다. 덧붙여 C, C++ 등 프로그래밍 언어에 대한 스킬도 상당한 수준으로 요구될 수 있습니다.
이 보드는 WiFi 통신기능과 아두이노 우노 보드의 접근 용이성이 결합되어 대학생 이상의 교육현장 등에서 인터넷, TCP/IP, IOT, 원격제어 등 이론과 실습과정에 매우 유용한 교재로 쓰일 수 있을 것으로 보이며 일렉트로닉스 분야에 취미를 가진 전 세계인들에게는 보다 저렴한 가격으로, 보다 완성도 있는 IOT 세계를 열어주는데 부족함이 없을 것으로 봅니다. 감사합니다.
자료참고
1. 위즈넷 홈페이지 제품소개 페이지
http://www.wiznet.co.kr/product/wifi-module/
2. 오렌지보드 튜토리얼
http://www.kocoafab.cc/product/orangewifi
3. 옐로우캣 소개
http://www.kocoafab.cc/product/yellowcat
4. 코코아팹 오렌지보드 핀아웃
http://kocoafab.cc/data/docs/orangeboard-wifi/orangeboard_wifi_pinout.pdf
[40호]자이로, 가속도, 지자기, 압력 센서 7종 출시
㈜제이케이이엠씨
자이로, 가속도, 지자기, 압력 센서 7종 출시
산업용, 교육용 Embedded 하드웨어, 소프트웨어 솔루션 및 교육을 제공하는 Embedded 전문 기업 ㈜제이케이이엠씨에서 전원 레귤레이터와 I2C 레벨쉬프터를 내장해 아두이노, 라즈베리파이에서 쉽게 사용이 가능하도록 3.3V ~ 5.0V 전원을 모두 사용할 수 있는 센서 7종을 새로 출시했다.
대표적으로 ‘MPU9250 9축 자이로, 가속도, 지자기 센서 GY-9250 3.3/5V 레귤레이터 I2C 레벨쉬프터 내장형’ 제품의 메인 칩셋은 MPU6050 센서의 후속인 MPU9250 칩셋으로, 가속도와 자이로센서, AK8963 지자기센서가 모두 통합된 9DOF(Degrees of Freedom) 센서 칩셋이다. 특히 MPU9250은 I2C (Inter Integrated Circuit) 통신 프로토콜을 통해 데이터를 추출할 수 있어 높은 활용도를 자랑하는 제품이다. 또한 ‘MPU6050 6축 자이로, 가속도 센서 GY-521 3.3/5V I2C 레벨쉬프터 내장형’ 제품은 가속도와 자이로센서가 통합된 6DOF(Degrees of Freedom)센서인 MPU6050 칩셋을 기반으로 하며, 특히 MPU9250과 같이 I2C (Inter Integrated Circuit) 통신 프로토콜을 통해서 데이터를 추출할 수 있는 제품이라고 업체는 설명한다.
이 외에도 BMP180 고도/압력 Barometer 센서와 ADXL335 3축 가속도센서, ADXL345 3축 가속도센서, L3G4200D 3축 자이로센서, HMC5883L 지자기센서 등 다양한 센서 제품군을 출시해 많은 사용자들의 수요를 충족할 수 있는 라인업을 갖췄다. 위 제품들은 제조사 홈페이지 및 디바이스마트 홈페이지에서 구매할 수 있다.
www.jkelec.co.kr
TEL. 070-7510-4840