반응형

지난 글 (http://deneb21.tistory.com/546) 에서는 433Mhz 통신 모듈을 테스트 해 보았는데 통신거리가 별로 신통하지 않았었다. 일단 통신거리가 너무 짧았고 양방향 통신도 지원하지 않았다. 딱 한 가지 장점은 가격이 매우 싸다는 것.  무선 통신 모듈이 통신거리가 짧으면 아무짝에도 쓸모가 없다. 이러한 단점을 어느 정도 보완해 줄 수 있는 모듈이 NRF24L01 이다. 이 모듈은 여러가지 버전이 있는데 내가 구입한 것은 1km 정도까지 통신이 가능하다고 광고하는 안테나 달린 버전이다. (1km? 과연?) 무선 와이파이 공유기와 같은 2.4Ghz 대역으로 통신을 하고 SPI 통신을 하므로 양방향 송수신이 가능하다.  또한 1:n 통신도 가능하다. 


하나의 마스터가 슬레이브들에게 데이터를 보내거나 여러개의 슬레이브가 하나의 마스터에게 데이터를 보내는 것도 가능한 것이다. 여러모로 433Mhz 모듈 보다는 많이 발전된 칩이고 모듈이다.


모듈은 위와 같은 구성으로 되어 있다. 공유기 같은데 들어가는 안테나이다. 이런 외장안테나 버전 이외에도 기판에 안테나가 프린트되어 있는 버전도 있다.


안테나가 내장된 NRF24L01 모듈 아마도 외장 안테나 버전보다는 통신거리가 짧을 것이다.


모듈 기판의 모습니다. NRF24L01 칩이 보인다.


연결 단자의 모습이다. MISO, MOSI 가 있어서 마스터 슬레이브간 양방향 통신이 가능하다. 전원은 3.3V 를 이용한다.


반대편에서 본 연결 단자


일렬로 된 납땜 단자의 연결 배열이다.

 

nRF24L01+ 데이터시트


nRF24L01P_Product_Specification_1_0.pdf


 

■ 연결


두 개의 모듈로 테스트를 했으며 하나는 Receiver 역할로 아두이노 우노에 하나는 Sender 역할로 아두이노 나노에 연결했다. 전원은 아두이노 자체전원 3.3V에 연결했다. 전원 연결시 주의할 점이 있는데 모듈 전원 VCC 와 GND 사이에 1~10uF 의 콘덴서(보통 10uF 전해콘덴서)를 연결해서 노이즈를 제거해주어야 한다. 그러지 않으면 통신이 아예 되지 않을 수도 있다. 나의 경우도 그냥 연결하니 통신이 안됨. 그리고 데이터는 TMP36 온도센서를 이용해서 측정된 온도를 Receiver 로 보내 보았다.


 

nrf24l01_send.fzz

Sender 역할


 


nrf24l01_recv.fzz

Receiver 역할


각각의 연결은 위와 같다. 보다시피 Sender 와 Receiver 의 연결이 온도센서를 제외하면 같다. CE, CSN 은 변경이 가능하다.


 

■ 스케치


RF24 라는 라이브러리를 사용한다. 라이브러리 다운로드는 아래 링크에서 받아서 설치한다.

- RF24 라이브러리 : https://github.com/nRF24/RF24/tree/master

RF24-master.zip


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
//Libraries for NRF24L01+ module.
#include <SPI.h>
#include <RF24.h>
 
//RF24 object with two pins defined with arguments. CE: 9, CSN: 10
RF24 radio(910);
 
//Address of the pipe. 40 bit long, you can choose this freely.
//Remember to use different address in different projects.
long long address = 0x1234ABCDEFLL;
int sensorPin = 0;
float temperature;
 
//Variable that selects whether the circuit is sender (1) or receiver (0)
bool sender = 0;
 
void setup() {
  //Start the radio
  Serial.begin(9600);
  radio.begin();
 
  if (sender) { //If sender
     radio.openWritingPipe(address);
    //RF24_PA_MIN, RF24_PA_LOW, RF24_PA_HIGH and RF24_PA_MAX
    //NRF24L01: -18dBm, -12dBm,-6dBM, and 0dBm
    radio.setPALevel(RF24_PA_LOW);
    radio.stopListening();
  } //If sender
  else { //If receiver
    //Open reading pipe with given address and start listening for incoming data
    radio.openReadingPipe(1, address);
    radio.startListening();
  } //If receiver
}
 
void loop() {
  if (sender) { //If sender
    //Get temperature from the sensor
    int reading = analogRead(sensorPin);
    float voltage = reading * 5.0;
    voltage /= 1024.0
    temperature = (voltage - 0.5* 100 ;
    
    //Send the temperature wirelessly, print error if failed
    if (!radio.write(&temperature, sizeof(temperature))) {
      Serial.println(F("Sending failed"));
    }
    delay(1000);
  } //If sender
  else { //If receiver
    //If data is available in radio's buffer
    if(radio.available()) {
      //While the data is available...
      while (radio.available()) {
        //Update temperature -variable with data from the radio module
        radio.read(&temperature, sizeof(temperature));
      }
      Serial.println(temperature);
    }
  } //If receiver
}
cs


하나의 소스로 Sender 와 Receiver 역할을 수행한다. 15행에 sender 변수값 1을 설정하면 Sender이고 0을 설정하면 Receiver 이다. 그러므로 두 개의 아두이노에 각각 1과 0 값으로 업로드 해야 한다. 


10행을 보면 파이프 방식 통신을 하므로 마스터와 슬레이브 간에 같은 어드레스를 사용해야 한다. 


26행을 보면 송신 전파 강도를 설정하는 부분이 있다. RF24_PA_MIN, RF24_PA_LOW, RF24_PA_HIGH and RF24_PA_MAX 으로 설정할 수 있으며 좌에서 우로 갈수록 전파가 강해진다. 공급 전류가 허용된다면 MAX로 그렇지 않다면 한 단계씩 줄여서 통신 상황을 관찰한다. 나는 아두이노 자체전원을 이용 했으므로 제일 작은 MIN으로 설정했다.



위와 같이 RF 통신으로 온도센서의 측정값이 잘 전해졌다. 전파의 강도를 약하게 해서인지 둘 사이의 거리가 5m 정도를 넘으면 통신이 되지 않았다. 추후 전원을 보강해서 MAX 까지 추가 테스트를 해 볼 필요성을 느낀다.


- NRF24L01 참고 사이트

http://arduino-info.wikispaces.com/Nrf24L01-2.4GHz-HowTo

http://www.mapescn.com/xe/ARDUINO/44799



반응형
반응형

아두이노는 자체로도 쓰임새가 많지만 와이파이, 블루투스, RF 등의 무선 모듈과 합쳐지면 그 쓰임새가 더욱 늘어난다. 여기저기 흩어져 있는 센서의 측정값 들을 모아 통합 저장할 수도 있으며 무선으로 원격 컨트롤을 가능하게 하기도 한다. 블루투스, 와이파이 등...  본 블로그에서는 무선 모듈에 대해서 많이 알아봤었는데 RF 모듈은 처음인 것 같다. 알리익스프레스를 뒤져보니 433Mhz 송수신 모듈이 5쌍에 3000원 정도 밖에 하지 않는다. 매우 저렴하다. 게다가 어떤 유튜버에 따르면 간단한 안테나를 만들어 달면 100여 미터 이상 까지도 통신이 된다고 한다. 저렴한 가격과 괜찮은 성능이다. 바로 구입해서 테스트 해 보았다.


433Mhz RF 무선 송수신 모듈이다. 좌측이 송신모듈, 우측이 수신모듈이다. 크기는 2~3CM 정도로 매우 작고 가벼워서 RC 비행기 같은데 넣어도 될 것 같다.


송신모듈이다. 핀의 구성은 위와 같으며 안테나 연결부도 위와 같다. VCC 에는 3.5 ~ 12V 까지 연결이 가능하다.


수신 모듈이다. 핀의 구성은 위와 같으며 안테나 연결부도 위와 같다. 가운데 두 개의 핀이 DATA 인데  어느것을 사용해도 상관이 없다.


■ 433Mhz RF 송수신 모듈 스펙


Transmitter module parameters

1.Product Model: MX-FS-03V

2.Launch distance :20-200 meters (different voltage, different results)

3.Operating voltage :3.5-12V

4.Dimensions: 19 * 19mm

5.Operating mode: AM

6.Transfer rate: 4KB / S

7.Transmitting power: 10mW

8.Transmitting frequency: 433M

9.An external antenna: 25cm ordinary multi-core or single-core line

10.Pinout from left to right: (DATA; VCC; GND)


Receiver module parameters

1.Product Model: MX-05V

2.Operating voltage: DC5V

3.Quiescent Current: 4MA

4.Receiving frequency: 433.92MHZ

5.Receiver sensitivity:-105DB

6.Size: 30 * 14 * 7mm

7.External antenna: 32CM single core wire, wound into a spiral


 

■ 연결


연결은 간단하다. 나의 경우 송신측은 아두이노 나노를 이용했고 수신측은 우노를 이용했다. 각각 아두이노 자체전원 5V 와 GND 에 전원을 연결했으며 송수신 모듈 모두 DATA 는 하나 밖에 없기 때문에 다음과 같이 연결하면 된다.


- 송신측 DATA : 아두이노 D12 에 연결

- 수신측 DATA : 아두이노 D11 에 연결


연결된 모습은 위와 같다. 송신측은 5V 보조 배터리 이용, 수신측은 PC 연결


 

■ 소스


소스는 송신측에서 문자열을 보내면 수신측에서 문자열을 받아서 시리얼 모니터에 출력하는 것을 목표로 했다. 먼저 RF 모듈을 쉽게 사용할 수 있게 해 주는 VirtuialWire 라이브러리를 다운로드 받아서 설치 해야 한다. 


- 다운로드 : http://www.resistorpark.com/arduino-virtualwire-library-download/

VirtualWire.zip


- 송신측 소스

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <VirtualWire.h>
 
const int TX_DIO_Pin = 12// default 12
 
 
void setup()
{
  vw_set_tx_pin(TX_DIO_Pin); // Initialize TX pin
  vw_setup(2000); // Transfer speed : 2000 bits per sec
}
 
void loop()
{
  send("DOIYYOURSELF");
  delay(1000);
}
 
void send (char *message)
{
  vw_send((uint8_t *)message, strlen(message));
  vw_wait_tx(); // Wait until the whole message is gone
}
cs


- 수신측 소스

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <VirtualWire.h>
 
byte message[VW_MAX_MESSAGE_LEN]; // a buffer to store the incoming messages
byte messageLength = VW_MAX_MESSAGE_LEN; // the size of the message
 
const int RX_DIO_Pin = 11// default 11
 
void setup()
{
  Serial.begin(9600);
  Serial.println("Ready to receive:");
  vw_set_rx_pin(RX_DIO_Pin); // Initialize RX pin
  vw_setup(2000); // Transfer speed : 2000 bits per sec
  vw_rx_start(); // Start the receiver
}
 
void loop()
{
  if (vw_get_message(message, &messageLength)) // Non-blocking
  {
    Serial.print("Received: ");
    for (int i = 0; i < messageLength; i++)
    {
      Serial.write(message[i]);
    }
    Serial.println();
  }
}
cs


- 실행결과


위와 같이 무선 RF 통신으로 'DOIYYOURSELF (DOITYOURSELF 의 오타 ^^; )' 라는 문자열이 제대로 전송되는 것을 알 수 있다. 하지만 이 모듈에는 문제점이 있다. 통신거리가 극히 짧은 것이다. 나의 경우 10CM 정도 떨어지면 송수신이 되지 않았다.


맨 위에서도 언급했듯이 안테나를 달면 송수신 거리를 길게 할 수 있다고 한다. 그래서 랜선을 잘라서 안테나를 만들어 주었다.


안테나 길이 구하는 공식은 '300/주파수' 인데 300/433=0.693m 이다. 이를 cm 으로 하면 69.3cm 이다. 그런데 이를 다시 4로 나누어 안테나 길이를 구하는데 17.3cm가 나온다. 안테나에 대한 구체적인 지식은 모르겠고 이 공식에 따라서 17.3cm로 랜선을 잘라서 모듈에 납땜해 주었다.


그런데 위의 모듈 상세스펙의 마지막 부분을 보면 송신측은 25cm, 수신측은 32cm의 권장 안테나 길이가 쓰여 있다. 뭐가 맞는지 모르겠지만 일단 인터넷의 의견을 따랐다. 나중에 스펙에 있는 다른 안테나 길이도 추가 테스트를 해 봐야 겠다.


안테나를 만들어 달고 결과적으로 아주 약간의 향상이 있었다. 10cm 통신거리에서 30cm 정도로 통신거리가 눈꼽만큼 늘어난 것이다. 그런데 이 정도로는 무선 RF 라는 장점은 하나도 없다. 적어도 10미터는 넘게 통신거리가 나와야 활용할 수 있을 듯 하다. 


안테나 다음으로 생각되는 것이 송신측 전압이다. 송신모듈의 경우 3.5V-12V 가 작동 전압인데 나는 아두이노 자체전원을 사용했다. 이것을 외부전원을 따로 주어 조금 더 강력한 전압과 전류가 되도록 하면 어떨까? 생각해 보았다. 9V 건전지가 적당할 것 같은데 일단 없어서 글을 접고 나중에 추가로 테스트를 해 보아야 겠다.


안테나 길이 관련 참조 자료


비슷한 모듈 같은데 165m 까지 통신이 된다.




과연 싸고 좋은 것은 없는 것인가? 오늘 실험은 실망이지만 다음 추가 테스트가 기대가 된다. 적당한 거리에서 통신이 잘 된다면 이 보다 더 저렴하고 쓸만한 아두이노 무선 통신은 없을 것 같다. ^^



■ 추가내용 (2017.10.30) : 각각 9V 알카라인 배터리를 이용해서 전원을 공급해 보았다. 결론은 위의 비디오 처럼 165m 까지 절대로 안나온다는 것이다. 그래도 기존보다 많이 늘어난 5~10m 까지 통신거리가 늘어났다. 집안 근거리 통신에는 그럭저럭 쓸 수 있을 듯 하다. 또는 분실방지 장치 같은데도 쓰일 수 있을 것 같다. 지갑과 가방에 각각 모듈을 넣고 신호가 끊기면 경보를 울리게 하는... 아무튼 비추 모듈이다. 통신거리가 너무 짧다. 


작동전압을 고려해서 송신측은 9V 배터리 전압을 직접 공급했으며 수신측은 아두이노의 5V 전원을 이용했다.



■ 추가내용 (2018.04.22) : 혹시 이 모듈의 통신거리가 잘 안나오는 분들은 아래 '아두이노초보

' 님의 댓글을 참고하면 좋은 결과가 있을지도 모르겠다.


반응형
반응형

아두이노 우노 기준으로 3.3V 와 5V 전원을 자체에서 공급할 수 있게 되어 있다. 하지만 아두이노의 최대 출력 전류는 500mA 밖에 되지 않는다.  만약 서보모터 2개 이상을 제어하는 프로젝트를 아두이노 자체 전력을 이용해서 만든다면 작동하지 않거나 작동하더라도 아두이노가 고장이 날 수 있다. 보통 서보모터의 소비전류는 400mA 가 넘기 때문이다.  이럴 경우에는 아두이노 자체 전력을 이용하지 않고 외부전원을 이용해야 안정적으로 동작한다. 이 글에서는 아두이노 외부전원 연결에 대해서 알아보려고 한다.


아두이노 우노의 구조 : 자체적으로 3.3V 및 5V 출력을 지원하지만 레귤레이터의 한계 때문에 500mA 정도의 전력 밖에 지원하지 않는다 (이미지 : http://jameco.com)


아두이노 자체의 전원으로는 LED 몇 개, 전력소모가 낮은 센서 1~2개 정도야 문제 없이 구동이 가능하지만 전력소모가 큰 센서나 모터 같은 경우는 제대로된 동작이 어렵게 된다.



아두이노 자체 전원으로 3개의 서보모터를 가동하는 경우이다. 서보모터 1개 정도는 자체전원으로도 문제가 없지만 3개가 동시에 가동된다면 제대로 움직이지 않을 것이다. 그러므로 전원연결의 나쁜 케이스라고 할 수 있다.


위의 나쁜 케이스를 외부전원을 이용해서 개선해 보았다.  외부전원(배터리)가 추가 되었고 배터리의 마이너스(-) 는 아두이노의 GND에 연결해 주었다. 위와 같이 하면 아두이노 자체 전원이 아닌 외부전원으로 서보모터가 구동이 되므로 안정적으로 동작이 가능하다. (외부 공급 전원은 작동시키려는 부품의 허용 동작전압을 넘지 않도록 한다)

 

덧붙여서 아두이노 가동을 위한 전원의 경우 외부전원의 전력이 충분하다는 전제하에 위의 그림과 같이 외부전원의 플러스(+)를 아두이노의 VIN 단자에 연결하면 아두이노 작동을 위한 별도의 전원을 연결하지 않아도 된다.

반응형
반응형

본 블로그에서는 이제까지 LM35, DHT11, DHT22 등의 온도 또는 온습도 센서를 이용해 온도 및 습도를 측정하는 방법을 알아봤었다. 하지만 넓은 범위의 온도를 측정하기에는 부족함이 있었다. 물론 -40~80도 까지 측정이 가능한 DHT22 가 있지만 너무 비싸다. 저렴한 LM35라는 온도센서도 있지만 0~100도 까지만 측정이 가능하여 영하는 측정이 아예 불가능하다. 


이미지 출처 : sparkfun.com


그래서 찾아보니 TMP36 이라는 센서가 있다. 겉 모습은 LM35와 같은 트랜지스터 모양이다. 온도에 따른 전압의 변화(1°C 당 10mV)를 가지고 온도를 측정한다. 알리익스프레스에서 개 당 1천원 정도의 가격으로 구입이 가능하며 −40°C ~ +125°C 까지 측정이 가능하니 165도의 넓은 범위의 온도 측정이 가능하다. 동작전압은 2.7V~5.5V 이다. 저렴하고 온도 측정의 범위가 넓은 괜찮은 센서인 것 같다.


다만 정밀도는 조금 떨어져서 상온 25°C 근처에서는 ±1°C 정도의 괜찮은 오차를 보이지만 그 범위를 벗어나면 ±2°C 의 오차가 있다고 생각하면 된다. 그러므로 아주 정밀한 온도의 측정이 필요한 곳에는 사용하지 않는 것이 좋으며 그런 곳에는 DHT22 나 다른 정밀한 센서를 사용하면 된다.


■ TMP36 데이터시트

데이터시트에 적혀 있는 TMP36의 특징은 다음과 같으며 데이터시트 pdf 파일도 첨부한다.


FEATURES

  • Low voltage operation (2.7 V to 5.5 V)

  • Calibrated directly in °C

  • 10 mV/°C scale factor (20 mV/°C on TMP37)

  • ±2°C accuracy over temperature (typ)

  • ±0.5°C linearity (typ)

  • Stable with large capacitive loads

  • Specified −40°C to +125°C, operation to +150°C

  • Less than 50 µA quiescent current

  • Shutdown current 0.5 µA max

  • Low self-heating

  • Qualified for automotive applications


TMP35_36_37.pdf



이제 실제로 TMP36을 아두이노와 연결해서 온도를 측정해 보자


■ TMP36과 아두이노와의 연결


먼저 TMP36의 Pin 에 대해서 알아야 한다.


3개의 핀이 있는데 각 핀의 구성은 위와 같다. 전원공급(VCC)는 2.7V~5.5V 까지 다양한 전압이 연결이 되지만 여기서는 아두이노 5V 에 연결 하였으며 Signal 은 아두이노 Analog 0 번(A0)에 연결 하였다.


TMP36.fzz



 



■ 소스


소스에 대한 설명은 주석에 상세하게 적었으므로 참고하면 된다. 간단하게 TMP36의 Signal 핀의 온도에 따른 전압의 변화를 읽어 섭씨온도를 구하고 이를 화씨로도 변환하여 시리얼모니터에 표시해 주는 소스이다. 섭씨 온도 구하는 공식은 아래와 같다.


온도(℃) = (((TMP36출력값 x 5.0) / 1024.0) - 0.5) x 100


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
 
int sensorPin = 0//Analog 0 번에 센서핀 연결
 
void setup()
{
  Serial.begin(9600);  //PC와의 시리얼 통신 속도 설정
}
 
void loop()
{
 //전압의 변화값을 센서핀으로 부터 읽음
 int reading = analogRead(sensorPin);  
 
 //전압값을 읽음, 센서에 공급되는 전압이 5V면 읽은값x5.0 이고 3.3V 이면 3.3을 곱해준다
 float voltage = reading * 5.0;
 voltage /= 1024.0
 
 //시리얼 모니터에 전압값 출력
 Serial.print(voltage); Serial.println(" volts");
 
 //전압값(mV)으로 온도 구함 (섭씨) , 시리얼 모니터 출력
 float temperatureC = (voltage - 0.5* 100 ;
 Serial.print(temperatureC); Serial.println(" degrees C");
 
 //섭씨를 화씨로 변환해서 시리얼 모니터 출력
 float temperatureF = (temperatureC * 9.0 / 5.0+ 32.0;
 Serial.print(temperatureF); Serial.println(" degrees F");
 
 delay(1000);   //1초 간격으로 측정
}
cs


위와 같이 A0 를 통해서 측정된 전압과 전압에 따라 계산된 온도가 표시된다.



TMP36은 저렴하게 범위가 넓은 온도를 측정하기에 좋은 센서 같다. 

반응형
반응형

연휴기간에 심심해서 아이와 만들어서 놀아볼 생각으로 아두이노 무선조종 보트를 만들어 보았다. 뭐 결과적으로 실패지만 적절한 추진 프로펠러(스크류)와 모터만 있다면 충분히 가능성이 있다고 생각한다. (결과적으로 내가 만든 보트는 앞으로 잘 나아가지 않았다) 앞으로 나아가는데는 실패지만 아두이노 코드와 블루투스 모듈 서보모터 그리고 PWM 으로 DC모터 제어하는 것은 성공했으니 그래도 절반은 성공이 아닐까? ㅎㅎ


뭐... 작동원리는 간단하다. 아두이노 나노에 블루투스 모듈(HC-06)을 달아서 휴대폰과 통신을 해서 보트의 제어를 하게 된다. 아두이노에는 PWM 으로 DC모터의 속도를 조절할 수 있게 해서 보트의 추진력을 조절하고 서보모터(SG90) 을 달아서 보트의 방향을 전환하도록 한다. 이에 대한 참고는 본 블로그의 과거 글을 참고 했다. 각 모듈에 대해서 궁금하면 아래의 링크를 참고하도록 한다.


 

대략적인 부품들이다. 6V 배터리팩 (나중에 휴대폰 충전용 보조배터리로 교체), PWM용 저항, 다이오드, 세라믹 커패시터, 트랜지스터, 서보 모터, 블루투스 모듈 그리고 실패의 원인 추진용 수중모터 이다. 수중모터는 물을 빨아들여서 뒤로 쏴주면 앞으로 나아가지 않을까 해서 프로펠러 대신 달아 주었는데 너무 출력이 약해서 추진이 제대로 되지 않았다. 이는 나중에 더 높은 전압과 전류를 공급하거나 프로펠러 방식으로 교체하면 제대로된 추진력이 생기지 않을까? 생각한다.


모터 PWM용 부품들 저항 270옴, 다이오드 1N4001, 트랜지스터 KTN2222A 그리고 모터 노이즈 제거용 세라믹 커패시터


간만에 꺼내보는 블루투스 모듈 HC-06


방향전환 용도 서보 모터 SG90


수중 모터. 화살표 방향으로 물이 들어가고 나온다. 충분한 전력이 공급되지 않으면 제대로된 출력이 나오지 않는다. 1W ~ 1.5W 정도는 되어야 한다.


부품들을 모두 연결한 모습


브레드보드에 연결을 했다.


배의 본체는 스티로폼을 보트 모양으로 잘라서 날림으로 만들었다. ^^;


서보 모터와 연결된 보트의 방향타인데 적당한 아이디어가 생각나지 않아서 나무 귀후비개를 잘라서 글루건으로 붙였다. 그런데 이게 너무 약해서 물에 띄우니 너무 흔들 거린다. 이 부분도 제대로 하려면 보강이 필요하다.


Bluetooth_Boat.fzz


■ 소스

소스는 별거 없다. 위의 참고글의 소스를 조합하면 된다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include <SoftwareSerial.h> //시리얼 통신 라이브러리 호출
#include "Servo.h" //서보 라이브러리
 
Servo myservo; //서보객체
int blueTx=2;   //Tx (블투 보내는핀 설정)
int blueRx=3;   //Rx (블투 받는핀 설정)
int motorPin=4//motor pin
 
SoftwareSerial mySerial(blueTx, blueRx);  //시리얼 통신을 위한 객체선언
String myString=""//받는 문자열
 
void setup() {
  myservo.attach(12);   //서보 시그널 핀설정
  myservo.write(45);     //서보 초기각도 0도 설정
  mySerial.begin(9600); //블루투스 시리얼 개방
  pinMode(motorPin, OUTPUT); //motor pin output setup
}
 
void loop() {
  while(mySerial.available())  //mySerial 값이 있으면
  {
    char myChar = (char)mySerial.read();  //mySerial int형식의 값을 char형식으로 변환
    myString+=myChar;   //수신되는 문자열을 myString에 모두 붙임 (1바이트씩 전송되는 것을 모두 붙임)
    delay(5);           //수신 문자열 끊김 방지
  }
  
  if(!myString.equals(""))  //myString 값이 있다면
  {
    Serial.println("input value: "+myString); //시리얼모니터에 myString값 출력
 
      if(myString=="l")  //Turn Left
      {
        myservo.write(90);     
      } else if(myString=="r") {  //Turn Right
        myservo.write(0);
      } else if(myString=="st") {  //Straight
        myservo.write(45);
      } else if(myString=="0") {  //stop
        analogWrite(motorPin, 0);
      } else if(myString=="1") {  //speed 1
        analogWrite(motorPin, 100);
      } else if(myString=="2") {  //speed 2
        analogWrite(motorPin, 180);
      } else if(myString=="3") {  //speed 3 (PWM 최고속도)
        analogWrite(motorPin, 255);
      } else {        
      }
    myString="";  //myString 변수값 초기화
  }
}
cs



소스를 업로드 하고 스마트폰에 앱을 하나 설치하고 실행한다. 블루투스 모듈과 연결하고 신호를 보낼 앱인데 검색하면 많은 앱들이 있다. 나는 그 중에서 'Bluetooth Serial Controller'  라는 앱을 애용한다. 간단하고 사용하기 쉽다.


바로 이 앱이다. 검색하면 바로 나온다.


나는 위와 같이 설정을 했다. 속도는 정지인 0단, 1,2,3단으로 PWM을 이용해서 조절한다. 그리고 서보 모터 (방향타)를 직전, 좌, 우로 설정해서 방향을 조절할 수 있도록 한다. 조금만 살펴 보면 누구나 쉽게 설정할 수 있다. 여기서 잊지 말아야 할 것이 있는데 블루투스 모듈이 앱에 CONNECT 되어 있어야 한다. 블루투스 모듈 설정 및 CONNECT 방법은 맨 위의 참고 글을 참고한다.


스마트폰으로 동작해 보는 영상이다.


아무튼 수중 모터의 추진력이 약해서 결론적으로 실패지만 조금 더 보강하면 충분히 가능성이 있는 DIY 이다. 추진력 보강은 프로펠러 방식으로 바꾸던가 수중모터에 외부전원을 보강해서 더 높은 전력을 공급하면 잘 움직일 것으로 생각된다. 그리고 방향타 방식도 좀 더 튼튼한 방식으로 바꿔야 한다.

반응형
반응형

예전 글(http://deneb21.tistory.com/508) 에서 노키아 5110 LCD를 아두이노로 컨트롤 하는 방법에 대해서 알아보았다. 이번에는 그래픽 라이브러리를 사용해서 간단한 그래픽을 나타내 보려고 한다. 라이브러리는 Adafruit 의 GFX 라이브러리와 PCD8544(5110 LCD 정식 품명) 라이브러리를 이용했으며 다운 받은 라이브러리에 포함된 예제를 핀배열과 백라이트 부분을 살짝 수정해서 그래픽을 표시해 보았다. 5110 LCD에 대한 기본적인 사항은 이 글의 첫 줄에 있는 예전 글을 참고하면 된다.


연결은 예전과 동일하다. 만약 라이브러리 파일의 예제 그대로 연결하고 싶다면 아래의 연결을 따르지 않아도 된다. 물론 아래의 소스도 수정이 되어야 할 것이다.


■ Nokia 5110 LCD 와 아두이노 우노의 연결




 Arduino UNO

 Nokia 5110 LCD

 7

 CE

 6

 RST

 5

 DC

 4

 DIN

 3

 CLK

 2

 LIGHT

 3.3V

 VCC

 GND

 GND




■ 라이브러리 다운로드


Adafruit GFX 라이브러리 : https://github.com/adafruit/Adafruit-GFX-Library


Adafruit PCD8544 Nokia 5110 LCD library : https://github.com/adafruit/Adafruit-PCD8544-Nokia-5110-LCD-library


위의 두 가지 라이브러리를 다운로드 받아서 압축을 풀고 아두이노 설치폴더 하위의 Libraries 폴더에 복사해 넣으면 된다. 참고로 GFX 라이브러리는 예전 OLED LCD 사용에 대한 글에서도 사용한 라이브러리이다.


Adafruit-GFX-Library-master.zip

Adafruit-PCD8544-Nokia-5110-LCD-library-master.zip




■ 소스

아래 소스는 라이브러리 설치 시 Example 로 제공되는 소스(pcdtest.ino)이며 핀 연결 부분을 나의 연결에 맞게 수정했으며 백라이트 제어 부분을 추가한 소스이다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
/*********************************************************************
This is an example sketch for our Monochrome Nokia 5110 LCD Displays
  Pick one up today in the adafruit shop!
  ------> http://www.adafruit.com/products/338
These displays use SPI to communicate, 4 or 5 pins are required to
interface
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
Written by Limor Fried/Ladyada  for Adafruit Industries.
BSD license, check license.txt for more information
All text above, and the splash screen must be included in any redistribution
*********************************************************************/
 
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_PCD8544.h>
 
#define PIN_BL    2   //백라이트 제어를 위한 핀연결
 
// Software SPI (slower updates, more flexible pin options):
// pin 7 - Serial clock out (SCLK)
// pin 6 - Serial data out (DIN)
// pin 5 - Data/Command select (D/C)
// pin 4 - LCD chip select (CS)
// pin 3 - LCD reset (RST)
//Adafruit_PCD8544 display = Adafruit_PCD8544(7, 6, 5, 4, 3); //원래 핀연결
Adafruit_PCD8544 display = Adafruit_PCD8544(34576);   //나의 연결에 맞게 수정한 핀연결
 
// Hardware SPI (faster, but must use certain hardware pins):
// SCK is LCD serial clock (SCLK) - this is pin 13 on Arduino Uno
// MOSI is LCD DIN - this is pin 11 on an Arduino Uno
// pin 5 - Data/Command select (D/C)
// pin 4 - LCD chip select (CS)
// pin 3 - LCD reset (RST)
// Adafruit_PCD8544 display = Adafruit_PCD8544(5, 4, 3);
// Note with hardware SPI MISO and SS pins aren't used but will still be read
// and written to during SPI transfer.  Be careful sharing these pins!
 
#define NUMFLAKES 10
#define XPOS 0
#define YPOS 1
#define DELTAY 2
 
 
#define LOGO16_GLCD_HEIGHT 16
#define LOGO16_GLCD_WIDTH  16
 
static const unsigned char PROGMEM logo16_glcd_bmp[] =
{ B00000000, B11000000,
  B00000001, B11000000,
  B00000001, B11000000,
  B00000011, B11100000,
  B11110011, B11100000,
  B11111110, B11111000,
  B01111110, B11111111,
  B00110011, B10011111,
  B00011111, B11111100,
  B00001101, B01110000,
  B00011011, B10100000,
  B00111111, B11100000,
  B00111111, B11110000,
  B01111100, B11110000,
  B01110000, B01110000,
  B00000000, B00110000 };
 
void setup()   {
  //백라이트를 ON 함
  pinMode(PIN_BL,OUTPUT); 
  digitalWrite(PIN_BL,LOW); //HIGH = Turn Backlight OFF, LOW = Turn Backlight ON
 
  Serial.begin(9600);
 
  display.begin();
  // init done
 
  // you can change the contrast around to adapt the display
  // for the best viewing!
  display.setContrast(50);
 
  display.display(); // show splashscreen
  delay(2000);
  display.clearDisplay();   // clears the screen and buffer
 
  // draw a single pixel
  display.drawPixel(1010, BLACK);
  display.display();
  delay(2000);
  display.clearDisplay();
 
  // draw many lines
  testdrawline();
  display.display();
  delay(2000);
  display.clearDisplay();
 
  // draw rectangles
  testdrawrect();
  display.display();
  delay(2000);
  display.clearDisplay();
 
  // draw multiple rectangles
  testfillrect();
  display.display();
  delay(2000);
  display.clearDisplay();
 
  // draw mulitple circles
  testdrawcircle();
  display.display();
  delay(2000);
  display.clearDisplay();
 
  // draw a circle, 10 pixel radius
  display.fillCircle(display.width()/2, display.height()/210, BLACK);
  display.display();
  delay(2000);
  display.clearDisplay();
 
  testdrawroundrect();
  delay(2000);
  display.clearDisplay();
 
  testfillroundrect();
  delay(2000);
  display.clearDisplay();
 
  testdrawtriangle();
  delay(2000);
  display.clearDisplay();
   
  testfilltriangle();
  delay(2000);
  display.clearDisplay();
 
  // draw the first ~12 characters in the font
  testdrawchar();
  display.display();
  delay(2000);
  display.clearDisplay();
 
  // text display texts
  display.setTextSize(1);
  display.setTextColor(BLACK);
  display.setCursor(0,0);
  display.println("Hello, world!");
  display.setTextColor(WHITE, BLACK); // 'inverted' text
  display.println(3.141592);
  display.setTextSize(2);
  display.setTextColor(BLACK);
  display.print("0x"); display.println(0xDEADBEEF, HEX);
  display.display();
  delay(2000);
 
  // rotation example
  display.clearDisplay();
  display.setRotation(1);  // rotate 90 degrees counter clockwise, can also use values of 2 and 3 to go further.
  display.setTextSize(1);
  display.setTextColor(BLACK);
  display.setCursor(0,0);
  display.println("Rotation");
  display.setTextSize(2);
  display.println("Example!");
  display.display();
  delay(2000);
 
  // revert back to no rotation
  display.setRotation(0);
 
  // miniature bitmap display
  display.clearDisplay();
  display.drawBitmap(3016,  logo16_glcd_bmp, 16161);
  display.display();
 
  // invert the display
  display.invertDisplay(true);
  delay(1000); 
  display.invertDisplay(false);
  delay(1000); 
 
  // draw a bitmap icon and 'animate' movement
  testdrawbitmap(logo16_glcd_bmp, LOGO16_GLCD_WIDTH, LOGO16_GLCD_HEIGHT);
}
 
 
void loop() {
  
}
 
 
void testdrawbitmap(const uint8_t *bitmap, uint8_t w, uint8_t h) {
  uint8_t icons[NUMFLAKES][3];
  randomSeed(666);     // whatever seed
 
  // initialize
  for (uint8_t f=0; f< NUMFLAKES; f++) {
    icons[f][XPOS] = random(display.width());
    icons[f][YPOS] = 0;
    icons[f][DELTAY] = random(5+ 1;
    
    Serial.print("x: ");
    Serial.print(icons[f][XPOS], DEC);
    Serial.print(" y: ");
    Serial.print(icons[f][YPOS], DEC);
    Serial.print(" dy: ");
    Serial.println(icons[f][DELTAY], DEC);
  }
 
  while (1) {
    // draw each icon
    for (uint8_t f=0; f< NUMFLAKES; f++) {
      display.drawBitmap(icons[f][XPOS], icons[f][YPOS], logo16_glcd_bmp, w, h, BLACK);
    }
    display.display();
    delay(200);
    
    // then erase it + move it
    for (uint8_t f=0; f< NUMFLAKES; f++) {
      display.drawBitmap(icons[f][XPOS], icons[f][YPOS],  logo16_glcd_bmp, w, h, WHITE);
      // move it
      icons[f][YPOS] += icons[f][DELTAY];
      // if its gone, reinit
      if (icons[f][YPOS] > display.height()) {
  icons[f][XPOS] = random(display.width());
  icons[f][YPOS] = 0;
  icons[f][DELTAY] = random(5+ 1;
      }
    }
   }
}
 
 
void testdrawchar(void) {
  display.setTextSize(1);
  display.setTextColor(BLACK);
  display.setCursor(0,0);
 
  for (uint8_t i=0; i < 168; i++) {
    if (i == '\n'continue;
    display.write(i);
    //if ((i > 0) && (i % 14 == 0))
      //display.println();
  }    
  display.display();
}
 
void testdrawcircle(void) {
  for (int16_t i=0; i<display.height(); i+=2) {
    display.drawCircle(display.width()/2, display.height()/2, i, BLACK);
    display.display();
  }
}
 
void testfillrect(void) {
  uint8_t color = 1;
  for (int16_t i=0; i<display.height()/2; i+=3) {
    // alternate colors
    display.fillRect(i, i, display.width()-i*2, display.height()-i*2, color%2);
    display.display();
    color++;
  }
}
 
void testdrawtriangle(void) {
  for (int16_t i=0; i<min(display.width(),display.height())/2; i+=5) {
    display.drawTriangle(display.width()/2, display.height()/2-i,
                     display.width()/2-i, display.height()/2+i,
                     display.width()/2+i, display.height()/2+i, BLACK);
    display.display();
  }
}
 
void testfilltriangle(void) {
  uint8_t color = BLACK;
  for (int16_t i=min(display.width(),display.height())/2; i>0; i-=5) {
    display.fillTriangle(display.width()/2, display.height()/2-i,
                     display.width()/2-i, display.height()/2+i,
                     display.width()/2+i, display.height()/2+i, color);
    if (color == WHITE) color = BLACK;
    else color = WHITE;
    display.display();
  }
}
 
void testdrawroundrect(void) {
  for (int16_t i=0; i<display.height()/2-2; i+=2) {
    display.drawRoundRect(i, i, display.width()-2*i, display.height()-2*i, display.height()/4, BLACK);
    display.display();
  }
}
 
void testfillroundrect(void) {
  uint8_t color = BLACK;
  for (int16_t i=0; i<display.height()/2-2; i+=2) {
    display.fillRoundRect(i, i, display.width()-2*i, display.height()-2*i, display.height()/4, color);
    if (color == WHITE) color = BLACK;
    else color = WHITE;
    display.display();
  }
}
   
void testdrawrect(void) {
  for (int16_t i=0; i<display.height()/2; i+=2) {
    display.drawRect(i, i, display.width()-2*i, display.height()-2*i, BLACK);
    display.display();
  }
}
 
void testdrawline() {  
  for (int16_t i=0; i<display.width(); i+=4) {
    display.drawLine(00, i, display.height()-1, BLACK);
    display.display();
  }
  for (int16_t i=0; i<display.height(); i+=4) {
    display.drawLine(00, display.width()-1, i, BLACK);
    display.display();
  }
  delay(250);
  
  display.clearDisplay();
  for (int16_t i=0; i<display.width(); i+=4) {
    display.drawLine(0, display.height()-1, i, 0, BLACK);
    display.display();
  }
  for (int8_t i=display.height()-1; i>=0; i-=4) {
    display.drawLine(0, display.height()-1, display.width()-1, i, BLACK);
    display.display();
  }
  delay(250);
  
  display.clearDisplay();
  for (int16_t i=display.width()-1; i>=0; i-=4) {
    display.drawLine(display.width()-1, display.height()-1, i, 0, BLACK);
    display.display();
  }
  for (int16_t i=display.height()-1; i>=0; i-=4) {
    display.drawLine(display.width()-1, display.height()-10, i, BLACK);
    display.display();
  }
  delay(250);
 
  display.clearDisplay();
  for (int16_t i=0; i<display.height(); i+=4) {
    display.drawLine(display.width()-100, i, BLACK);
    display.display();
  }
  for (int16_t i=0; i<display.width(); i+=4) {
    display.drawLine(display.width()-10, i, display.height()-1, BLACK); 
    display.display();
  }
  delay(250);
}
cs


소스는 setup() 에서 여러가지 도형 및 문자를 출력하는 function을 순차적으로 실행하고 있다. 각 function 들을 자세히 보면 어떻게 도형과 문자 그리고 애니메이션을 만드는지 알 수 있을 것이다. 복잡한 부분은 라이브러리와 되어 있어서 자세히 살펴보면 별로 어려운 부분은 없다. 대부분의 기능들이 예를 들어 display.clearDisplay(); 같이 간단하게 제어가 가능하게 되어있다.





■ 추가사항 (2017.04.20) : 위의 소스에서 텍스트 표시하는 부분만 잘라서 정리해 보았다. 아무래도 이 디스플레이를 가지고 그래픽 보다는 텍스트 표시 용도로 사용할 경우가 많을 것으로 보여서 정리해 보았다. 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_PCD8544.h>
#define PIN_BL 2   //백라이트 제어를 위한 핀연결
 
//SCLK, DIN, DC, CE, RST 핀연결
Adafruit_PCD8544 display = Adafruit_PCD8544(34576);
 
void setup()
{
  //백라이트를 ON 함, HIGH = Turn Backlight OFF, LOW = Turn Backlight ON
  pinMode(PIN_BL,OUTPUT); 
  digitalWrite(PIN_BL,LOW); 
 
  //디스플레이 초기화
  display.begin();
 
  //디스플레이 Contrast 조절 (값이 올라가면 진해짐)
  display.setContrast(60);
 
  //텍스트 디스플레이
  display.clearDisplay();             //디스플레이 지우기
  display.setTextSize(1);             //텍스트 사이즈 조절
  display.setTextColor(BLACK);        //텍스트 색
  display.setCursor(0,0);             //커서 좌표
  display.println("Hello, world!");   //표시 텍스트
  display.setTextColor(WHITE, BLACK); // 'inverted' text (배경 까맣게 글자 하얗게)
  display.println(3.141592);          //숫자 표시
  display.setTextSize(2);             //텍스트 사이즈 조절
  display.setTextColor(BLACK);        //텍스트 색
  display.print("0x");                //표시 텍스트
  display.println(0xDEADBEEF, HEX);   //HEX 쓰기
  display.display();                  //위의 설정내용 표시
}
 
void loop()
{
 
}
cs

주석을 나름대로 상세하게 달아 보았다.


실행결과

반응형
반응형

아두이노용이라고 딱 정해진 것은 아니지만 아두이노에 많이 쓰이는 디스플레이 출력장치에는 7 Segment LED 부터 LCD, OLED 등 꽤 많은 부품들이 쓰이고 있다. 이 중에서 과거 노키아 피처폰에 사용되었던 LCD 인 Nokia 5110 LCD 라는 디스플레이를 아두이노에 연결해보고 출력하는 방법을 알아보겠다. 노키아는 망했지만 과거의 영광을 간직한 LCD는 아직도 남아서 아두이노의 부품으로 쓰일 수 있다는 점이... 생각할수록 기분이 참 묘하다.


Nokia 5110 핸드폰


위의 핸드폰이 노키아 5110 핸드폰이다. 이 핸드폰에 쓰인 LCD만 사용해서 문자를 표시하거나 간단한 그래픽을 표시할 수 있는 것이다.


아두이노용으로 팔고 있는 Nokia 5110 LCD 이다. 아래 위에 8개의 단자가 있으며 2개의 8핀 커넥터가 제공된다. 아래나 위나 단자의 역할은 같다. 양 쪽 중에서 원하는 곳에 아두이노와 연결하면 된다.


LCD 모듈의 뒷면이다. 1번 부터 RST(RESET), CE(Chip Enable), DC(Data/Command Selection), DIN(Serial Input), SCLK(Clock Input), VCC(3.3V), LIGHT(Back Light), GND 가 있다.


나는 그냥 양 쪽 모두 납땜해 버렸다.


■ 노키아 5110 LCD 데이터시트

Nokia5110_Datasheet.pdf


LCD 모듈은 모노크롬이며 48 x 84 픽셀의 해상도를 가진다. 문자나 간단한 그래픽을 나타내기에 적당한 해상도이다. LCD의 컨트롤러로는 PCD8544 라는 컨트롤러를 사용한다. 자세한 내용은 위의 데이터시트에 나와 있다. 개발 시 참고하면 되겠다. 작동은 3.3V 권장인데 5V 에서도 작동하긴 한다. 하지만 LCD 화면이 너무 진하게 나오거나 약간의 조정이 필요했다. 화면의 크기는 직접 자로 재보니 가로 3.6 x 세로 2.6 cm 였다.


■ 아두이노와의 연결


Nokia_5110_LCD.fzz


 Arduino UNO

 Nokia 5110 LCD

 7

 CE

 6

 RST

 5

 DC

 4

 DIN

 3

 CLK

 2

 LIGHT

 3.3V

 VCC

 GND

 GND





■ 소스

Nokia 5110 LCD 의 경우 라이브러리가 많이 개발되어 있다. 아래의 소스는 라이브러리를 사용하지 않은 소스이다. 기존의 라이브러리를 사용한다면 좀 더 간결하게 표현할 수 있을 것이다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
#define PIN_SCE   7
#define PIN_RESET 6
#define PIN_DC    5
#define PIN_SDIN  4
#define PIN_SCLK  3
#define PIN_BL    2 //BackLight
 
#define LCD_C     LOW
#define LCD_D     HIGH
 
#define LCD_X     84
#define LCD_Y     48
#define LCD_CMD   0
 
int a = 0;
 
static const byte ASCII[][5=
{
 {0x000x000x000x000x00// 20  
,{0x000x000x5f0x000x00// 21 !
,{0x000x070x000x070x00// 22 "
,{0x140x7f0x140x7f0x14// 23 #
,{0x240x2a0x7f0x2a0x12// 24 $
,{0x230x130x080x640x62// 25 %
,{0x360x490x550x220x50// 26 &
,{0x000x050x030x000x00// 27 '
,{0x000x1c0x220x410x00// 28 (
,{0x000x410x220x1c0x00// 29 )
,{0x140x080x3e0x080x14// 2a *
,{0x080x080x3e0x080x08// 2b +
,{0x000x500x300x000x00// 2c ,
,{0x080x080x080x080x08// 2d -
,{0x000x600x600x000x00// 2e .
,{0x200x100x080x040x02// 2f /
,{0x3e0x510x490x450x3e// 30 0
,{0x000x420x7f0x400x00// 31 1
,{0x420x610x510x490x46// 32 2
,{0x210x410x450x4b0x31// 33 3
,{0x180x140x120x7f0x10// 34 4
,{0x270x450x450x450x39// 35 5
,{0x3c0x4a0x490x490x30// 36 6
,{0x010x710x090x050x03// 37 7
,{0x360x490x490x490x36// 38 8
,{0x060x490x490x290x1e// 39 9
,{0x000x360x360x000x00// 3a :
,{0x000x560x360x000x00// 3b ;
,{0x080x140x220x410x00// 3c <
,{0x140x140x140x140x14// 3d =
,{0x000x410x220x140x08// 3e >
,{0x020x010x510x090x06// 3f ?
,{0x320x490x790x410x3e// 40 @
,{0x7e0x110x110x110x7e// 41 A
,{0x7f0x490x490x490x36// 42 B
,{0x3e0x410x410x410x22// 43 C
,{0x7f0x410x410x220x1c// 44 D
,{0x7f0x490x490x490x41// 45 E
,{0x7f0x090x090x090x01// 46 F
,{0x3e0x410x490x490x7a// 47 G
,{0x7f0x080x080x080x7f// 48 H
,{0x000x410x7f0x410x00// 49 I
,{0x200x400x410x3f0x01// 4a J
,{0x7f0x080x140x220x41// 4b K
,{0x7f0x400x400x400x40// 4c L
,{0x7f0x020x0c0x020x7f// 4d M
,{0x7f0x040x080x100x7f// 4e N
,{0x3e0x410x410x410x3e// 4f O
,{0x7f0x090x090x090x06// 50 P
,{0x3e0x410x510x210x5e// 51 Q
,{0x7f0x090x190x290x46// 52 R
,{0x460x490x490x490x31// 53 S
,{0x010x010x7f0x010x01// 54 T
,{0x3f0x400x400x400x3f// 55 U
,{0x1f0x200x400x200x1f// 56 V
,{0x3f0x400x380x400x3f// 57 W
,{0x630x140x080x140x63// 58 X
,{0x070x080x700x080x07// 59 Y
,{0x610x510x490x450x43// 5a Z
,{0x000x7f0x410x410x00// 5b [
,{0x020x040x080x100x20// 5c ¥
,{0x000x410x410x7f0x00// 5d ]
,{0x040x020x010x020x04// 5e ^
,{0x400x400x400x400x40// 5f _
,{0x000x010x020x040x00// 60 `
,{0x200x540x540x540x78// 61 a
,{0x7f0x480x440x440x38// 62 b
,{0x380x440x440x440x20// 63 c
,{0x380x440x440x480x7f// 64 d
,{0x380x540x540x540x18// 65 e
,{0x080x7e0x090x010x02// 66 f
,{0x0c0x520x520x520x3e// 67 g
,{0x7f0x080x040x040x78// 68 h
,{0x000x440x7d0x400x00// 69 i
,{0x200x400x440x3d0x00// 6a j 
,{0x7f0x100x280x440x00// 6b k
,{0x000x410x7f0x400x00// 6c l
,{0x7c0x040x180x040x78// 6d m
,{0x7c0x080x040x040x78// 6e n
,{0x380x440x440x440x38// 6f o
,{0x7c0x140x140x140x08// 70 p
,{0x080x140x140x180x7c// 71 q
,{0x7c0x080x040x040x08// 72 r
,{0x480x540x540x540x20// 73 s
,{0x040x3f0x440x400x20// 74 t
,{0x3c0x400x400x200x7c// 75 u
,{0x1c0x200x400x200x1c// 76 v
,{0x3c0x400x300x400x3c// 77 w
,{0x440x280x100x280x44// 78 x
,{0x0c0x500x500x500x3c// 79 y
,{0x440x640x540x4c0x44// 7a z
,{0x000x080x360x410x00// 7b {
,{0x000x000x7f0x000x00// 7c |
,{0x000x410x360x080x00// 7d }
,{0x100x080x080x100x08// 7e ←
,{0x000x060x090x090x06// 7f →
};
 
 
 
 
void LcdCharacter(char character)
{
  LcdWrite(LCD_D, 0x00);
  for (int index = 0; index < 5; index++)
  {
    LcdWrite(LCD_D, ASCII[character - 0x20][index]);
  }
  LcdWrite(LCD_D, 0x00);
}
 
void LcdClear(void)
{
  for (int index = 0; index < LCD_X * LCD_Y / 8; index++)
  {
    LcdWrite(LCD_D, 0x00);
  }
}
 
void LcdInitialise(void)
{
  pinMode(PIN_SCE,   OUTPUT);
  pinMode(PIN_RESET, OUTPUT);
  pinMode(PIN_DC,    OUTPUT);
  pinMode(PIN_SDIN,  OUTPUT);
  pinMode(PIN_SCLK,  OUTPUT);
 
  digitalWrite(PIN_RESET, LOW);
  delay(1);
  digitalWrite(PIN_RESET, HIGH);
 
  LcdWrite( LCD_CMD, 0x21 );  // LCD Extended Commands.
  LcdWrite( LCD_CMD, 0xB9 );  // Set LCD Vop (Contrast). //B0~B9
  LcdWrite( LCD_CMD, 0x04 );  // Set Temp coefficent. //0x04
  LcdWrite( LCD_CMD, 0x14 );  // LCD bias mode 1:48. //0x13
  LcdWrite( LCD_CMD, 0x0C );  // LCD in normal mode. 0x0d for inverse
  LcdWrite(LCD_C, 0x20);
  LcdWrite(LCD_C, 0x0C);
}
 
void LcdString(const char *characters)
{
  while (*characters)
  {
    LcdCharacter(*characters++);
  }
}
 
void LcdWrite(byte dc, byte data)
{
  digitalWrite(PIN_DC, dc);
  digitalWrite(PIN_SCE, LOW);
  shiftOut(PIN_SDIN, PIN_SCLK, MSBFIRST, data);
  digitalWrite(PIN_SCE, HIGH);
}
 
// gotoXY routine to position cursor 
// x - range: 0 to 84
// y - range: 0 to 5
 
void gotoXY(int x, int y)
{
  LcdWrite( 00x80 | x);  // Column.
  LcdWrite( 00x40 | y);  // Row.  
 
}
 
 
 
void drawLine(void)
{
  unsigned char  j;  
   for(j=0; j<84; j++// top
    {
          gotoXY (j,0);
      LcdWrite (1,0x01);
  }     
  for(j=0; j<84; j++//Bottom
    {
          gotoXY (j,5);
      LcdWrite (1,0x80);
  }     
 
  for(j=0; j<6; j++// Right
    {
          gotoXY (83,j);
      LcdWrite (1,0xff);
  }     
    for(j=0; j<6; j++// Left
    {
          gotoXY (0,j);
      LcdWrite (1,0xff);
  }
 
}
 
 
void setup(void)
{
 LcdInitialise();
 LcdClear();
 pinMode(PIN_BL,OUTPUT); 
 digitalWrite(PIN_BL,HIGH); //HIGH = Turn Backlight OFF, LOW = Turn Backlight ON
}
 
void loop(void)
{
  int a,b;
  char Str[15];
  // Draw a Box
  for(b=1000; b>0; b--){
  drawLine();
  for(a=0; a<=5 ; a++){
  gotoXY(4,1);
  // Put text in Box
  digitalWrite(PIN_BL,LOW);
  LcdString ("TestDisplay");
  gotoXY(24,3);
  LcdCharacter('H');
  LcdCharacter('E');
  LcdCharacter('L');
  LcdCharacter('L');
  LcdCharacter('O');
  LcdCharacter(' ');
  LcdCharacter('=');
  // Draw + at this position
  gotoXY(10,3);
  LcdCharacter('=');
  delay(2000);
  gotoXY(24,3);
  LcdCharacter('W');
  LcdCharacter('O');
  LcdCharacter('R');
  LcdCharacter('L');
  LcdCharacter('D');
  LcdCharacter(' ');
  LcdCharacter('-');
  // Draw - at this position
  gotoXY(10,3);
  LcdCharacter('-');
  delay(2000);
  digitalWrite(PIN_BL,HIGH);
gotoXY(24,3);
  LcdCharacter('D');
  LcdCharacter('I');
  LcdCharacter('Y');
  LcdCharacter('!');
  LcdCharacter('@');
  LcdCharacter('$');
  LcdCharacter('&');
  // Draw - at this position
  gotoXY(10,3);
  LcdCharacter('#');
  delay(2000);  
  }
  }
}
 
 
 
cs


만약 LCD의 콘트라스트가 잘 맞지 않는다면 151행의 값을 적절하게 조절하면 된다. 나의 경우 처음 테스트 시 LCD의 문자가 잘 보이지 않다가 (초기값 0xB0) 값을 0xB9 로 조절하니 진하게 잘 표현이 되었다. 


LCD의 백라이트의 경우 LIGHT 단자를 GND에 연결해 버리면 코드에서 제어할 필요 없이 상시 백라이트가 ON 상태로 유지된다. 백라이트 컨트롤을 원하면 위와 같이 아두이노 디지털 핀에 연결하고 digitalWrite 를 HIGH 하면 OFF, LOW 하면 ON 된다.




이 LCD는 OLED LCD보다 저렴하고 크기도 커서 잘 사용하면 저렴하고 좋은 아두이노의 출력장치로 사용할 수 있을 것 같다.

반응형
반응형

예전에 작성했던 글 http://deneb21.tistory.com/222 에서는 전자석의 원리를 이용한 접점식 릴레이를 아두이노에서 제어하는 방법을 알아보았다이번에는 기계식 접점이 없는 반도체식 릴레이인 SSR (Solid State Relay)  아두이노에 연결해서 제어해 보려고 한다. 사실 아두이노에서의 사용방법은 접점식 릴레이와 거의 같다. 기존 릴레이 대비 SSR의 장점으로는 기계적으로 움직이는 부품이 없으므로 수명이 반영구적이다. (접점식 릴레이는 접점이 떨어졌다 붙었다 하면서 발생되는 아크 채터링에 의한 열화로 고장이 생길 수 있다.) 작동 시 소음이 전혀 없다. 전류의 스위칭이 매우 빠르다. 등이 있겠다. 단점으로는 반도체 스위칭 시 발열이 발생할 수 있다는 점이다


위의 그림이 내가 가지고 있는 1채널 SSR 모듈이다. SSR을 사용하기 쉽도록 단자를 달아서 모듈 형태로 만든 것이다. 기존 릴레이 메이커인 일본 Omron 사의 SSR을 이용하고 있다. INPUT 5V DC 이며 LOAD 240VAC 2A 이므로 아두이노의 5V 전원과 디지털 단자로 충분히 제어가 가능하며 우리나라의 가정용 전원인 220VAC 의 제어가 가능하다. 가격은 기존 접점식 릴레이 모듈 대비 3배 정도 비싸다. (1채널 알리익스프레스 기준, 접점식 릴레이 모듈 0.5달러, SSR 모듈 1.5 달러)
 


연결은 위와 같이 된다. 제어 입력부에 3개의 단자가 있는데 CH1 에는 아두이노의 디지털 단자를 DC- DC+ 에는 아두이노의 GND 5V를 연결하면 된다. 스위칭 부분에는 아래와 같은 전원 콘센트를 만들어 사용했다.


220V용 콘센트, 플러그, 전선을 가지고 위와 같은 선을 만들고 SSR의 스위칭 제어부에 연결하였다. 위험한 220V 전원을 사용하므로 매우 주의해서 연결해야 한다.

※ 만약 이 글을 보고 따라하려는 사람이 있다면 220V의 높은 전압을 다루게 되므로 자칫 잘못하면 감전되어 다칠 수 있으므로 주의를 기울여야 하며 사용하려는 릴레이의 허용전압/전류를 꼭 확인하고 사용해야 한다.


SSR 모듈의 뒷면.

 

1
2
3
4
5
6
7
8
9
10
11
12
int relay = 8//릴레이에 5V 신호를 보낼 핀설정
void setup ()
{
  pinMode (relay, OUTPUT); // relay output으로 설정한다.
}
void loop ()
{
  digitalWrite (relay, HIGH); // 릴레이 ON
  delay (10000);              //10 delay
  digitalWrite (relay, LOW); // 릴레이 OFF
  delay (5000);               //5 delay
}
cs


아두이노 우노를 사용하였고 소스는 기존의 릴레이 제어 소스와 동일하다. 위의 소스에서는 제어핀으로 D8을 릴레이의 CH1에 연결하였다10초 동안 전원이 켜지고 (SSR ON) 5초 동안 전원이 꺼지는 것이 반복된다. (SSR OFF)


소스대로 SSR 동작하는 모습이다. 220V 전원에 헤어드라이기를 연결하였다. 우선 기존의 접점식 릴레이와 틀린점이 보인다. 접점이 붙었다 떨어지는 소리인 '딸깍' 하는 소리가 전혀 나지 않는다

조용해야 하는 , 긴 수명을 가진 릴레이가 필요한 곳, 빠른 전류의 단속이 필요한 곳 등에서는 접점식 릴레이 보다는 SSR (Solid State Relay) 를 사용하는 것이 더욱 현명한 선택일 것이다. , 접점식에 비해 비싸므로 가성비도 따져보아야 할 것이다.

반응형
반응형

알코올을 측정할 수 있는 센서가 있다. MQ-3 라는 센서로 예전에 한 번 다뤘던 가연성 가스 탐지 센서 MQ-2(http://deneb21.tistory.com/279)와 비슷하게 생긴 센서이다. 말 그대로 알코올을 측정할 수 있으니 아두이노와 연결하여 간이 음주측정기로 활용이 가능하다. 단, TV에서 나오듯이 혈중알콜농도 몇? 하는 수치는 계산식에 의해서 구할 수 있을 것이다. 센서는 그냥 측정값을 보내주는 역할만 하고 아두이노는 이를 읽어서 표시만 해주기  때문에 이 수치를 신뢰할 수 있는 상용 알콜측정기와 비교하면 혈중알코올 농도 계산식을 만들 수 있을 것이다. 그렇게 되면 MQ-3 센서로도 혈중알코올 농도를 표시해 줄 수 있는 음주측정기를 만드는 것이 가능해진다. 아무튼 오늘은 MQ-3 센서로 알코올이 감지가 되는지 감지가 되면 어떻게 수치가 변화되는지 관찰해 보려고 한다.


MQ-3 센서모듈의 모습이다. 주황색의 센서가 MQ-3 센서이다.



센서에 MQ-3 라고 쓰여 있다.



센서의 뒷 부분이다. 저항, 증폭기, 감도 조절용 가변저항 등의 부품들이 달려 있어 아두이노에 바로 연결해서 사용이 가능하다. 가변저항은 오른쪽으로 돌리면 감도가 증가한다. 핀은 그림의 왼쪽부터 Analog Out, Digital Out, GND, VCC 이다. 전원은 3~5V 까지 사용이 가능하다. 디지털핀은 알코올의 감지여부를 알려주고 아날로그 핀은 감지된 알코올의 측정치를 보내준다. 아래는 데이터시트에서 발췌한 MQ-3의 스펙이다.

■ MQ-3 데이터시트
MQ3.pdf


■ 연결 및 소스

 MQ-3 센서 모듈

 아두이노 우노

 VCC

 5V

 GND

 GND

 AO

 A0

위와 같이 연결하고 아두이노의 13번 핀에 LED를 하나 연결해 주었다. A0 에서 읽은 값이 500이 넘을 경우 켜지도록 하였다.

/* MQ-3 Alcohol Sensor Circuit with Arduino */

const int AOUTpin=0;//the AOUT pin of the alcohol sensor goes into analog pin A0 of the arduino
const int ledPin=13;//the anode of the LED connects to digital pin D13 of the arduino

int value;

void setup() {
  Serial.begin(9600);//sets the baud rate
  pinMode(ledPin, OUTPUT);//sets the pin as an output of the arduino
}

void loop()
{
  value= analogRead(AOUTpin);//reads the analaog value from the alcohol sensor's AOUT pin
  Serial.print("Alcohol value: ");
  Serial.println(value);//prints the alcohol value
  delay(1000);
  
  if (value > 500){
    digitalWrite(ledPin, HIGH);
  } else {
    digitalWrite(ledPin, LOW);
  }
}



감지가 되지 않은 경우의 값이다. 150 정도의 값을 보여준다.



소독용 알코올을 휴지에 뭍혀서 센서에 가까이 가져갔다. 수치가 급속도로 상승하면서 750정도를 가르켰다. 


수치가 500을 넘었으므로 LED에 불이 들어왔다.

Digital Out 도 따로 테스트를 하였는데 가변저항 맞추기가 쉽지 않았다. 알코올이 검출되는지 안되는지만 HIGH/LOW로 판단하는데 별로 쓰임새는 없어보인다.

센서의 값이 처음에는 이상하게 높게 나오는 증상이 있다. 이 센서는 워밍업이 필요하다. 센서가 따뜻하게 달궈져야 제 성능을 발휘하게 된다. 보통 전원을 인가하고 10~15분 정도의 워밍업 시간을 가지면 센서의 전면부가 따뜻해지면서 제대로된 값이 나온다.

경찰들이 쓰는 휴대용 음주측정기가 있다면 MQ-3와 비교 후 개량해서 혈중알코올 농도도 측정할 수 있는 기기도 만들 수 있을 것 같은데 그런 기회가 올지 모르겠다.

반응형
반응형

해외 Maker 사이트인 Instructables 를 보다보니 3색 LED를 이용한 무드등 만들기가 올라와 있었다. 3색 LED가 서서히 색상이 변하고 섞이면서 신기한 색상의 변화를 즐길수 있는 무드등이다. 적당한 음악과 함께 켜 놓으면 연말연시 더욱 좋은 분위기를 낼 수 있을 것 같아서 따라해 보았다. 포토레지스터를 이용해서 주변이 밝으면 등이 꺼지고 어두워지면 등이 자동으로 켜지도록 되어 있다. 

아두이노 우노, 포토레지스터, 3색 RGB LED(또는 Red, Green, Blue 1개씩), 저항 1개(120킬로옴), A4용지 1장 정도 필요하다. 3색 LED에도 각 색상을 나타내는 단자마다 권장사항은 220옴 정도의 저항을 달아주는 것이 권장사항이긴 한데 이렇게하면 LED가 조금 어두워지는 경향이 있다. 내가 가진 LED를 저항 없이 연결해보니 12시간 이상 동작시켜도 이상이 없어서 처음에 저항을 연결했다가 나중에 빼버렸다. 그러나 자신이 가지고 있는 RGB LED 의 규격에 따라 아두이노나 LED가 맛이 갈 수 있으므로 저항의 연결은 개인의 선택에 맡긴다.


3색 RGB LED 는 위와 같은 구조를 가지고 있다. 각 4개의 핀이 있으며 각 핀의 길이에 따라 색상이 구분된다. 가장 긴 핀은 GND 단자이다. 연결시 참고해야 한다. 3색 LED 에 대해서 더 알고 싶다면 http://deneb21.tistory.com/210
포토레지스터에 대해서 알고 싶다면 http://deneb21.tistory.com/217


■ 연결

Mood_Lamp_RGB_LED.fzz

위의 연결은 3색 LED 하나를 이용할 경우의 연결이다.


Mood_Lamp_3_RGB_LED.fzz

위의 연결은 Red, Green, Blue 3개의 LED를 이용할 경우의 연결이다.


모두 연결한 모습이다. LED는 빛의 확산을 위해 A4용지로 감싸 주었다. 확산을 위한 다른 물체가 있다면 다른 것을 사용해도 된다. 하지만 A4용지로도 괜찮은 빛의 확산을 보여 주었다.

당연한 얘기지만 외부 빛의 감지를 위한 포토레지스터는 밖에 노출되어 있어야 한다. 그래야 어두우면 무드등이 켜지고 밝아지면 무드등이 켜지게 된다.


■ 소스

출처: http://www.instructables.com/id/Arduino-Mood-Lamp-1/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
// ---------------------------------------------------
// The brightness of the leds follows these equations:
// Red = sin(x)
// Green = sin(x + PI/3)
// Blue = sin(x + 2PI/3)
// for x from 0 to PI
// ---------------------------------------------------
 
float RGB[3];
int ldrPin = 0;     // LDR in Analog Input 0 to read the ambient light
int ambientLight;   // variable to store the value of the ambient light
int redLed   = 11;  // red LED in Digital Pin 11 (PWM)
int greenLed = 10;  // green LED in Digital Pin 10 (PWM)
int blueLed  = 9;   // blue LED in Digital Pin 9 (PWM)
 
void setup(){
  pinMode(redLed,OUTPUT);  // tell arduino it's an output
  pinMode(greenLed,OUTPUT);// tell arduino it's an output
  pinMode(blueLed,OUTPUT); // tell arduino it's an output
 
  // set all the outputs to low
  digitalWrite(redLed,LOW);
  digitalWrite(greenLed,LOW);
  digitalWrite(blueLed,LOW);
}
 
void loop(){
  for (float x=0;x<PI;x=x+0.00001){
    RGB[0]=255*abs(sin(x*(180/PI)));           // calculate the brightness for the red led
    RGB[1]=255*abs(sin((x+PI/3)*(180/PI)));    // calculate the brightness for the green led
    RGB[2]=255*abs(sin((x+(2*PI)/3)*(180/PI)));// calculate the brightness for the blue led
    ambientLight=analogRead(ldrPin); // read an store the ambient light
    if(ambientLight>600){ // start only if the ambient light is very low
      //  write the brightness on the leds
      analogWrite(redLed,RGB[0]);
      analogWrite(greenLed,RGB[1]);
      analogWrite(blueLed,RGB[2]);
    }
    else{
      digitalWrite(redLed,LOW);
      digitalWrite(greenLed,LOW);
      digitalWrite(blueLed,LOW);
    }
    for(int i=0;i<3;i++){
      if(RGB[i]<1){
        delay(100);
      }
      if(RGB[i]<5){
        delay(50);
      }
      if(RGB[i]<10){
        delay(10);
      }
      if(RGB[i]<100){
        delay(5);
      }
    }
    delay(1);
  }
}
 
cs

등이 어두워지면 켜지게 되어 있는데 어느정도 어두워졌을때 켜지는지의 설정은 소스 중 'if(ambientLight>600)' 의 600을 적절히 조절하면 된다. 값을 크게 할 수록 더욱 어두워져야 등이 켜진다.


위의 동영상과 같이 환상적인 색상의 변화를 볼 수 있는 무드등이 완성되었다.

반응형
반응형

http://deneb21.tistory.com/465 글에서 ATtiny85를 이용해서 자동으로 금붕어의 사료를 줄 수 있는 장치를 만들어 보았다. 이번에는 아두이노 버전으로 만들어 보았다. 아두이노는 프로 미니 5V 16Mhz 제품을 이용하였다. 아두이노 버전이라고 특별히 다른 것은 없다. 핀의 연결이 다르고 소스 상에서 SoftwareServo 라이브러리를 이용했다면 이를 아두이노의 기본 라이브러리인 Servo 라이브러리를 이용한 것이 다르다. 그 이외에 약간의 변경사항이 있다. 

동작은 12시간(조절가능)마다 사료를 주게 되어 있으며 1분 마다 작동확인을 위한 LED가 깜빡이게 하였고 사료를 주기 전에 피에조 부저가 3번 Beep 음을 내도록 만들었다.

거의 대부분의 부분은 ATtiny 버전과 다름이 없으니 이 글에서는 달라진 연결과 소스에 대해서만 다루겠다. 다른 부분은 ATtiny 버전의 글을 참고하면 된다.


■ 연결

Fish_Feeder.fzz



■ 소스

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include <Servo.h> 
 
Servo myservo;  //create servo object to control a servo 
 
int pos = 0;    //variable to store the servo position 
//int term = 960; // 16hours
//int term = 1440; // 24hours
int term = 720//Feeding term
int servo = 9;  //Servo Pin
int led = 12;  //LED Pin
int speaker = 11//piezo speaker Pin
 
void setup(){
  Serial.begin(9600);
  Serial.println("Arduino auto Fish Feeder");
  Serial.println("http://deneb21.tistory.com");
  pinMode(led, OUTPUT);
  pinMode(speaker, OUTPUT);
}
 
void loop(){
    for(int i=1;i<=term;i++)
    {
      delay(59500);  // 59 sec
      digitalWrite(led,HIGH);  //working LED
      delay(500);  // 1 sec , 59+1 = 1 minute
      digitalWrite(led,LOW);
    }
    Serial.println("Feeding Fish Now");
    feedFish();
}
 
// FEED THE FISH...
void feedFish()
{
  myservo.attach(servo);  // attaches the servo on pin9 to the servo object 
  myservo.write(0);
  delay(15);
  for(int i=0;i<3;i++)  //feeding alarm
  {
    tone(speaker,4000,1000);
    delay(2000);
  }
  
  for(pos = 0; pos < 180; pos += 1)  // goes from 0 degrees to 180 degrees 
  {                                  // in steps of 1 degree 
    myservo.write(pos);              // tell servo to go to position in variable 'pos' 
    delay(15);                       // waits 15ms for the servo to reach the position 
  } 
  for(pos = 180; pos>=1; pos-=1)     // goes from 180 degrees to 0 degrees 
  {                                
    myservo.write(pos);              // tell servo to go to position in variable 'pos' 
    delay(15);                       // waits 15ms for the servo to reach the position 
  }
  myservo.detach();
}
cs

아두이노 프로 미니는 자체 시리얼 통신칩이 없으므로 FTDI 모듈을 통해서 소스를 업로드 해야 한다. 방법은 다음의 URL 에서 확인이 가능하다. http://deneb21.tistory.com/393


반응형
반응형

2016 크리스마스가 얼마 남지 않았다. 이 즈음 집안에 크리스마스 트리를 장식하거나 각종 크리스마스 장식들을 하곤 한다. 그래서 나도 하나 만들고 싶어서 검색을 해봤다. 그 중에 마음에 드는 것이 있었다. Maker 사이트인 코코아팹에서 공개했던 ATtiny85를 이용한 크리스마스 트리 만들기이다. (http://kocoafab.cc/make/view/309)그걸 그대로 따라해 보았다. 


ATtiny85는 위의 그림과 같이 PB0 ~ PB5 까지 6개의 GPIO 핀이 있다. 그래서 상식적으로는 6개 이상의 LED는 연결하는게 불가능하다고 생각할 수도 있겠다. 하지만 1995년 찰리 알렌이라는 사람이 고안한 찰리플렉싱 (Charlieplexing) 이라는 기술을 적용하므로써 적은 IO핀으로도 많은 수의 LED의 제어가 가능하다. 제어가능한 LED의 갯수는 다음의 수식으로 구할 수 있다. 


※ 찰리플렉싱으로 제어 가능한 LED의 개수 구하기

n : GPIO 핀의 개수

LED개수 = n*(n-1)


즉, 오늘 ATtiny에서는 4개의 핀을 사용할 것이므로 12개의 LED를 제어할 수 있다. 아두이노 우노에 있는 14개의 핀으로는 무려 182개의 LED를 제어할 수 있는 것이다. 간단한 원리는 PIN을 INPUT 모드로 변환할 때 임피던스가 커지게 되고 마치 선이 연결되지 않은 효과를 가져온다. 이런 현상을 이용해서  INPUT/LOW, OUTPUT/LOW , OUTPUT/HIGH 의 3가지 상태를 가지고 각각의 LED를 제어할 수 있는 것이다. 자세한 내용은 검색해보면 많이 나온다. 어떻게 이런 아이디어를 생각을 했을까? 찰리라는 사람은 아이큐가 매우 높은 사람임에 틀림이 없다. ^^


트리만들기는 위에서도 말했지만 코코아팹 (http://kocoafab.cc/make/view/309) 의 내용을 그대로 따라 했다. 그래서 회로도나 코드도 링크의 것을 이용하면 된다. 다만 코코아팹의 글 내용 중에 프릿징 브레드보드 회로도가 나오는데 PB5 에 파란색 선을 연결한 것으로 나오지만 그대로 연결하고 소스를 업로드하면 LED가 켜지지 않는다. PB5 에 연결하지 말고 바로 아래의 PB3에 연결하면 제대로 작동한다. 검토가 제대로 안된 듯 하다. 그래서 그 회로도만 수정해서 여기에 올린다.


수정한 회로도 (원본 : http://kocoafab.cc/make/view/309)



이번에 사용할 LED들과 ATtiny85, 저항(220옴), 브레드보드 이외에도 LED 들을 연결할 전선이 필요하다.


회로도대로 납땜으로 연결한 모습이다. 헷갈리지 않게 극성을 잘 보고 연결해야 하고 전선의 길이를 잘 조절해서 연결해야 한다. 


전원으로는 AA건전지 4개가 들어간 6V 배터리팩을 이용하였다. 스위치가 달려서 켜고 끄기가 편하다.


전원을 ON 해 보았다. 꽤 이쁜 LED 반짝이가 만들어졌다.


녹색 하드보드지를 잘라서 트리모양을 만들었는데... 트리 맞나? 암튼 트리이다. 저기에 군데군데 구멍을 뚫고 LED를 하나씩 꼽을 것이다.


구멍을 뚫고 LED들을 모두 꼽은 다음 테스트


마지막으로 트리 맨위에 달 별모양의 LED를 만들어 주었다. 같은 극성끼리 LED다리를 납땜하고 220옴 저항을 중간에 연결하고 PB4 와 GND에 연결하였다. 딱 보기에 만들기 어려워 보이는데 의외로 별거 아니었다.


완성된 모습이다. 안쓰는 화분에 트리를 꼽고 탈지면으로 눈장식을 해 보았다. 그런데 눈이 너무 많이 온 것 같다.


뒷면의 모습이다. 지저분...


징글벨을 배경음으로 깔고 완성 후 찍어 보았다. 어설프지만 직접 만들었다는데에 의의가 있을 것이다. 그리고 방안에 불끄고 켜두면 꽤 이쁘다. 쓸만하다. 아두이노로도 만들 수 있지만 작은 MCU 인 ATtiny를 이용했다는 것도 의미가 있을 것이다. 한가지 아쉬운 점은 맨 위의 별을 만든 LED의 다리가 너무 길어서 별로 별 같지가 않은 것이다. 다리를 좀 더 짧게 자르는 것이 나을 것 같다.


그냥 의례적으로 말이 아니라 정말로 다사다난했던 한 해가 가고 있다. 가족과 함께하는 따뜻한 연말연시가 되었으면 좋겠다. Merry Christmas & Happy New Year.

반응형
반응형

제목이 좀 거창하지만 심플하게 ATtiny85 MCU 칩과 서보모터를 이용해서 지금 키우고 있는 금붕어의 사료를 자동으로 줄 수 있도록 장치를 만들어 보았다. 당분간은 그럴 일이 없지만 몇 일 동안 집을 비우게 되면 이 장치가 작동해서 금붕어 들의 밥을 챙겨 줄 수 있을 것이다. 금붕어는 현재 4마리를 키우고 있으며 하루에 두 번 12시간 간격으로 12알 정도의 사료를 주고 있다. 이를 자동화 하는 것이 목표이다. 


하지만 정확히 12알을 주는 것은 아래의 방법으로는 불가능하고 12알씩 따로 컨테이너에 담아두고 회전하면서 구멍으로 떨어뜨리는 장치를 만들어야 하는데 좀 복잡해진다. 뭐 금붕어가 10알 떨어진다고 불평하진 않을테니... 아무튼 그래서 이런 저런 생각 끝에 복잡하게 하지 않고 유튜브 동영상을 참고하여 비슷하게 만들어 보았다.


예전의 글에서 말했듯이 이런 간단한 장치에 아두이노를 사용하는 것은 어떻게 보면 낭비(?)일 것이다. 그런 의미에서 ATtiny 라는 싸고 저렴한 칩 하나를 이용해서 기능을 구현한다는 것이 의미가 있다고 생각한다.


사료통으로 집안에 굴러다니는 조그만 플라스틱 약병을 이용하였다. 서보모터와 붙이기 위해 약병 뚜껑의 꼭지 부분을 잘랐다.


글루건으로 서보모터 브라켓하고 약병 뚜껑을 접착해 주었다.


그리고 약통에 사료가 나올 수 있게 구멍을 뚫어주고 약통뚜껑과 접착된 브라켓을 서보모터에 끼워주면 된다. 이러면 사료주는 장치는 다 만든거나 다름이 없다. 엄청 심플하다.


그리고 이제 ATtiny85 칩에 프로그래밍을 하면 된다. 예전의 글에서도 말 했듯이 아두이노의 기본 Servo 라이브러리는 ATtiny 에서 작동하지 않았다. 그래서 SoftwareServo 라는 라이브러리를 이용하였다. 그리고 코딩도 아두이노의 그것과는 미묘하게 다른 면이 있다.


회로의 연결은 아래와 같다. ATtiny 서보모터 제어 글에서 연결했던 것과 같다. 참고로 서보제어 단자는 ATtiny85의 PB1 단자에 연결하였다.




■ 소스

※ ATtiny 프로그래밍 환경 참고 : http://deneb21.tistory.com/455http://deneb21.tistory.com/462

※ ATtiny 서보모터 제어 & 라이브러리 참고 : http://deneb21.tistory.com/463

SoftwareServo.zip

※ 소스 참고 : http://natemcbean.com/bin/FISHFEEDEERMAXIMUMEFFECTIVENESS.pde


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#include <SoftwareServo.h> 
 
SoftwareServo myservo;  // create servo object to control a servo 
 
int pos = 0;    // variable to store the servo position 
 
long FEEDINGDELAY = 43200000// 12 hours
//long FEEDINGDELAY = 57600000; // 16 hours
//long FEEDINGDELAY = 60000;  //for Test
 
long endtime; 
long now;
 
void setup() 
  myservo.attach(1);  // attaches the servo on pin 1 to the servo object 
  myservo.write(0);
  delay(15);
  
  
  // FEED THE FISH..
  for(pos = 0; pos < 180; pos += 1)  // goes from 0 degrees to 180 degrees 
  {                                  // in steps of 1 degree 
    myservo.write(pos);              // tell servo to go to position in variable 'pos' 
    SoftwareServo::refresh();        // must call at least once every 50ms or so to keep your servos updating
    delay(15);                       // waits 15ms for the servo to reach the position 
  } 
  for(pos = 180; pos>=1; pos-=1)     // goes from 180 degrees to 0 degrees 
  {                                
    myservo.write(pos);              // tell servo to go to position in variable 'pos' 
    SoftwareServo::refresh();
    delay(15);                       // waits 15ms for the servo to reach the position 
  } 
 
 
void loop() 
  // process times
  now = millis();
  endtime = now + FEEDINGDELAY;
  
  while(now < endtime) {
   myservo.write(0);
   delay(20000);
   now = millis();   
  }
  
  // FEED THE FISH..
  for(pos = 0; pos < 180; pos += 1)  // goes from 0 degrees to 180 degrees 
  {                                  // in steps of 1 degree 
    myservo.write(pos);              // tell servo to go to position in variable 'pos' 
    SoftwareServo::refresh();        // must call at least once every 50ms or so to keep your servos updating
    delay(15);                       // waits 15ms for the servo to reach the position 
  } 
  for(pos = 180; pos>=1; pos-=1)     // goes from 180 degrees to 0 degrees 
  {                                
    myservo.write(pos);              // tell servo to go to position in variable 'pos' 
    SoftwareServo::refresh();
    delay(15);                       // waits 15ms for the servo to reach the position 
  } 
}
cs


소스는 위와 같다. 자신이 원하는 시간을 FEEDINGDELAY 변수에 적용하면 된다. 나의 경우는 12시간 간격으로 줄 것이기 때문에 위와 같이 선택했다. 일단 칩에 전원이 공급되면 무조건 서보가 한 번 움직여서 사료를 주게 된다. 그 후 loop() 의 while 문에서 설정한 시간만큼 지연시키게 되고 시간이 경과하면 while문을 빠져나오게 되고 for 문이 작동 되면서 서보가 움직이게 되고 사료를 주게 된다. 이것이 무한반복 된다.


어느 정도 각도에서 내가 원하는 만큼의 사료가 나오는지 테스트를 많이 해 보아야 한다. 정확히는 맞지 않더라도 근사치의 사료양이 공급되어야 어항도 깨끗하게 유지되고 금붕어도 굶거나 너무 과식하는 현상이 없어질 것이다.


어항 언저리에 서보모터를 양면테이프로 붙여서 테스트 해 보았다. 적당량보다 조금 많은 양의 사료가 나왔다. 이럴 경우 약병을 약간 돌려서 구멍 있는 쪽이 조금 덜 기울어지게 하면 밑으로 떨어지는 사료가 줄어든다. 제일 알맞은 각도가 나오면 나중에도 그 각도를 맞출 수 있도록 네임펜으로 표시를 해 주었다.


최종적으로 미니 브레드보드에 회로를 구성하고 AA건전지 4개를 이용해서 6V 전원을 공급해 주었다. ATtiny 데이터시트에 보면 5.5V 까지가 권장 공급전압인데 6V 를 인가해 주어도 이상 없이 동작한다.


이제 잘 작동하는지 관찰하면 되겠다. 

이로써 집을 비울 경우 물고기 밥주는 문제는 해결되었다고 생각한다. 단, 개선의 여지는 많이 있어 보인다.



※ 추가내용 (2016.12.17) : 몇 일을 관찰해 보니 아주 잘 작동하지만 현재 작동하고 있는지 아닌지 확인할 방법이 없어서 1분 마다 깜빡이도록 LED 를 하나 추가로 달아주었다. millis() 를 이용한 기존의 소스를 delay() 로 바꿔주고 전원을 기존의 배터리에서 안 쓰는 핸드폰 어댑터 5V, 1A 짜리로 교체해 주었다.


ATtiny_Fish_Feeder.fzz


■ 변경된 소스
- delay() 를 이용하여 12시간(720분) 간격으로 서보가 동작하여 물고기 밥을 주도록 변경하였다. 
- PB0 에 LED 하나를 추가 하여 1분 마다 깜빡이도록 수정 하였다.
- 향후 개선 아이디어 : 먹이 주는 시간에 주변이 어두울 경우 금붕어가 먹이를 잘 먹을 수 있도록 LED 가 10분 정도 수족관을 밝혀주는 것도 괜찮을 것 같다. (CDS 센서, 고휘도 LED 필요) , 먹이 주는 시간이 되면 알람이 울리게 하는 것도 괜찮을 듯 하다. 금붕어의 파블로프의 개化? (피에조 부저 필요)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <SoftwareServo.h> 
 
SoftwareServo myservo;  // create servo object to control a servo 
 
int pos = 0;    // variable to store the servo position 
int term = 720// 12hours = 43200sec = 720minutes
int led = 0;  // LED Pin (PB0)
 
void setup(){
  pinMode(led, OUTPUT);
  myservo.attach(1);  // attaches the servo on PB1 to the servo object 
  myservo.write(0);
  delay(15);
  feedFish();
}
 
void loop(){
    for(unsigned int i=0;i<=term;i++)
    {
      delay(59000);  // 59 sec
      digitalWrite(led,HIGH);
      delay(1000);  // 1 sec , 59+1 = 1 minute
      digitalWrite(led,LOW);
    }
    feedFish();
}
 
// FEED THE FISH..
void feedFish()
{
  for(pos = 0; pos < 180; pos += 1)  // goes from 0 degrees to 180 degrees 
  {                                  // in steps of 1 degree 
    myservo.write(pos);              // tell servo to go to position in variable 'pos' 
    SoftwareServo::refresh();        // must call at least once every 50ms or so to keep your servos updating
    delay(15);                       // waits 15ms for the servo to reach the position 
  } 
  for(pos = 180; pos>=1; pos-=1)     // goes from 180 degrees to 0 degrees 
  {                                
    myservo.write(pos);              // tell servo to go to position in variable 'pos' 
    SoftwareServo::refresh();
    delay(15);                       // waits 15ms for the servo to reach the position 
  } 
}
cs






※ 추가내용 (2016.12.18) : 어떤 이유인지 확실히는 모르겠는데 새로 업로드한 소스에서 12시간에 6분 정도가 빨리 흐른다. 아마도 ATtiny 에 내장된 타이머가 정밀하지 않은 탓은 아닐까? 하는데 term 변수인 720분에 6분을 더해서 오차를 보정해 주었다.


1
2
3
//int term = 720; 
int term = 726;
 
cs




※ 추가내용 (2016.12.19) : 먹이를 주기전 Beep 음이 3번 울리도록 피에조 스피커를 추가하고 소스를 변경해 주었다. Beep 음은 아두이노에 있는 tone() 함수를 이용했는데 이 함수는 순수한 생짜의 ATtiny 에서는 지원되지 않는다. 사용하려면 http://deneb21.tistory.com/455 의 맨 아래 추가내용에 관련 내용(ATtiny Core 적용)을 적어 놓았다.  참고로 tone() 사용 시 음의 주파수를 500을 주었더니 소리가 제대로 나지 않았다. 아래는 이제까지 수정사항을 반영한 연결과 소스이다.


ATtiny_Fish_Feeder2.fzz



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <SoftwareServo.h> 
 
SoftwareServo myservo;  // create servo object to control a servo 
 
int pos = 0;    // variable to store the servo position 
//int term = 720; // 12hours = 43200sec = 720minutes
int term = 726// 12hours = 43200sec = 720minutes
int led = 0;  // LED Pin (PB0)
int speaker = 2//speaker Pin (PB2)
 
void setup(){
  pinMode(led, OUTPUT);
  myservo.attach(1);  // attaches the servo on PB1 to the servo object 
  myservo.write(0);
  delay(15);
  feedFish();
}
 
void loop(){
    for(unsigned int i=0;i<=term;i++)
    {
      delay(59000);  // 59 sec
      digitalWrite(led,HIGH);
      delay(1000);  // 1 sec , 59+1 = 1 minute
      digitalWrite(led,LOW);
    }
    feedFish();
}
 
// FEED THE FISH..
void feedFish()
{
  //feeding alarm
  for(unsigned int i=0;i<3;i++)
  {
    tone(speaker,2000,1000);
    delay(2000);
  }
  
  //feeding (servo move)
  for(pos = 0; pos < 180; pos += 1)  // goes from 0 degrees to 180 degrees 
  {                                  // in steps of 1 degree 
    myservo.write(pos);              // tell servo to go to position in variable 'pos' 
    SoftwareServo::refresh();        // must call at least once every 50ms or so to keep your servos updating
    delay(15);                       // waits 15ms for the servo to reach the position 
  } 
  for(pos = 180; pos>=1; pos-=1)     // goes from 180 degrees to 0 degrees 
  {                                
    myservo.write(pos);              // tell servo to go to position in variable 'pos' 
    SoftwareServo::refresh();
    delay(15);                       // waits 15ms for the servo to reach the position 
  } 
}
cs


반응형
반응형

아두이노의 PWM (Pulse Width Modulation, 펄스 폭 변조) 을 이용해서 DC 모터의 속도를 제어할 수 있다. 예전 글에서는 DC 모터 드라이버 모듈인 L298N 모듈을 이용해서 DC 모터를 제어해 본 적이 있지만 (http://deneb21.tistory.com/277http://deneb21.tistory.com/281) 이번에는 모듈 없이 간단한 회로구성 만으로 DC 모터를 제어해 보려고 한다. 다만 드라이버 모듈과 다른 점은 속도만 제어가 된다는 것이다. 회전방향은 바꿀 수 없다. 회전방향까지 바꾸려면 모터 드라이버 모듈을 사용하거나 브릿지 회로 등을 구성해서 제어해야 할 것이다.

먼저 아두이노 PWM 이란 위의 그림에서 보다시피 0V 와 5V 사이를 빠르게 반복해서 펄스를 만들어서 출력하는 것이다. 5V 유지시간이 짧아지고 0V 시간이 길어지면 출력전압이 떨어지게 된다. 반대로 되면 출력전압은 5V에 가까워지게 된다. 100% 가 될 경우 아두이노의 최대출력전압인 5V가 출력된다. 

아두이노 우노 에서는 물결표시(~)로 PWM 출력이 가능한 단자를 표시하고 있다. 아두이노의 PWM 은 analogWrite 로 0~255 단계로 조절할 수 있다.

필요한 준비물이다. 아두이노, 브레드보드, 케이블 등 기본적인 것 외에 DC모터, 저항(270옴), 다이오드 (1N4001), 트랜지스터 (PN2222, 실제로는 KTN2222A 라는 제품 사용함 비슷한 규격임) 각 부품들은 비슷한 스펙이면 다른 부품을 사용해도 괜찮다. 

DC 모터의 모습이다. 장난감에서 떼어낸 모터인데 세라믹 콘덴서가 3개 붙어 있어서 고주파 노이즈를 잡아주고 있다. 달지 않아도 되지만 저렇게 단자와 단자 사이, 각 단자와 모터 몸통 사이에 0.1uF 정도의 세라믹 콘덴서를 달아주면 고주파 노이즈 해결에 도움이 된다.


연결

DC_Motor_Speed_Control.fzz

위와 같이 아두이노와 모터를 연결하면 된다. 여기서 각 부품의 용도를 알아보면 먼저 저항은 트랜지스터에 가해지는 전류를 줄여서 트랜지스터를 보호한다. 트랜지스터는 모터를 돌리기에는 모자란 아두이노의 전류를 증폭하는 역할을 한다. 다이오드는 모터에서 발전하는 역전류를 막아서 트랜지스터와 아두이노를 보호하는 역할을 한다. 세라믹 콘덴서는 위에서도 말했듯이 모터의 고주파를 차단하는 역할을 한다. 세라믹 콘덴서는 꼭 달지 않아도 된다.


실제 연결한 모습


■ 소스

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
int motorPin = 3;
 
void setup() 
  pinMode(motorPin, OUTPUT);
  Serial.begin(9600);
  while (! Serial);
  Serial.println("Speed 0 to 255");
 
 
void loop() 
  if (Serial.available())
  {
    int speed = Serial.parseInt();
    if (speed >= 0 && speed <= 255)
    {
      analogWrite(motorPin, speed);
    }
  }
cs

위의 소스를 업로드 하고 시리얼 모니터를 열어서 1~255 사이의 값을 입력해보니 50정도의 값에서 모터가 움직이기 시작하고 값을 높일수록 (PWM 5V의 폭을 늘릴수록) 모터의 속도가 증가하는 것을 관찰하였다.


아래의 사진들은 실제로 PWM 값을 증가시켜 보면서 출력전압의 변화를 관찰한 사진이다.

PWM 값을 0 을 줬더니 0.03V 가 출력된다. 거의 출력전압이 없다고 보면 된다.


PWM 값을 100을 주었다. 2.15V 가 나온다. 5/255*100=1.96 이니까 대충 맞는 전압이 출력되는듯 하다.


PWM 값을 최대값인 255를 주었다. 아두이노 우노의 최대출력 전압인 5V가 출력 되었다.


DC모터를 PWM 으로 속도를 제어하는 것은 회전방향이 제어가 되지 않기 때문에 한쪽 방향의 회전만 있으면 되는 프로젝트에 사용이 될 수 있을 것이다. 예를 들어서 1, 2, 3 단만 있는 선풍기가 아니라 255 단계의 부드러운 속도조절이 가능한 선풍기 같은 것도 만들어 볼 수도 있을 것이고 쿨링 시스템을 만들때 온도에 따라 쿨링팬의 속도가 조절되는 로직도 만들 수 있을 것이다. 

쿨링팬 하니 갑자기 생각이 났는데 내가 키우는 금붕어 어항의 온도조절을 위한 냉각팬에도 적용이 가능할 것 같다. 쿨링팬의 속도를 ON/OFF가 아닌 단계별로 조절할 수 있으므로 아마 전력낭비도 없어질 듯 하다.

반응형
반응형

지난번 글 (http://deneb21.tistory.com/453) 에서 S4A (Scratch for Arduino) 프로그램을 설치해보고 간단하게 Blink 예제를 만들어서 실행해 보았다. 디지털 출력은 해 보았으니 이번에는 센서의 값을 어떻게 입력받고 이것의 처리는 어떻게 하는지 알아보도록 하겠다.  준비물은 아두이노 우노, LM35 온도센서, 브레드보드, 케이블, LED 한 개 정도 되겠다. 목표는 온도센서로 온도를 측정해서 표시하고 온도가 일정 값 이상 올라간다면 LED 를 켜주고 아니라면 끄는 동작을 S4A 를 이용해서 만들어 볼 생각이다.


LM35 온도센서는 위와 같이 생겼다. 일반적인 트랜지스터의 크기와 모양과 같다. 5V를 사용하며 가운데 단자가 아날로그 출력으로 온도를 알려준다. 다만 온도는 다음의 공식에 의해서 계산되어 나올 수 있다. 그러므로 S4A로 프로그래밍 시 고려되어야 한다.


■ LM35 온도 구하는 공식

온도 = 센서값 x 500 / 1024

또는

온도 = 센서값 x 0.48828125


■ 연결

아두이노와 온도센서(LM35)의 연결

 아두이노 우노

 LM35

 5V

 5V

 Analog 0 (A0)

 Signal (Output)

 GND

 GND


아두이노와 LED 연결 (원래 LED 의 보호를 위해 220옴 정도의 저항을 연결하는 것이 권장사항 이지만 여기서는 생략함)

 아두이노 우노

 LED

 Digital 13 (D13)

 긴 다리 (+)

 GND

 짧은 다리 (-)


연결 사진



연결을 마쳤으면 아두이노를 PC 에 연결하고 S4A 프로그램을 실행한다. 



그리고 아래와 같이 프로그램 블럭을 만들어 주었다.



1. 일단 온도센서의 값을 변수에 담아주기 위해서 '변수' 에 들어가서 temp 라는 이름으로 변수를 만들어 주었다.

 

2. '동작' 에 들어가서 아날로그 센서의 값을 불러오는 블럭을 가져와서 '연산' 에 있는 사칙연산 블럭을 이용해 LM35의 온도 구하는 공식을 적용하였다. 이렇게 하면 A0 단자에 연결된 온도센서의 값을 불러와서 온도를 temp 라는 변수에 저장한다. 위의 그림에 보면 연산 블럭이 2개가 겹쳐서 사용이 되었는데 맨 위에 있는 것이 먼저 실행되고 그 아래에 있는 것이 나중에 실행된다.


3. LED 가 켜질 것인지 꺼질 것인지의 판단을 위해 '제어' 에 들어가서 '만약 ~ 라면 ~아니면' 블럭을 가져와서 temp 변수의 값과 비교해서 디지털 13번 핀에 연결된 LED 를 켤 것인지 끌 것인지 판단한다. 위의 프로그램에서는 LED가 켜지는 조건을 23도 보다 높을 때로 했는데 이것은 원하는 대로 수정하면 된다.


4. '형태' 의 '~말하기' 블럭을 가져와서 temp 변수를 출력해 준다. (말하기 이지만 실제로 소리가 나오지는 않는다. S4A 화면에 온도가 표시된다.


5. '제어' 블럭에서 'x 초 기다리기' 블럭을 가져와서 1초 마다 온도를 측정하도록 한다. (꼭 하지 않아도 됨)


6. 위의 과정을 무한반복 하게 하기 위해서 '제어' 의 '무한반복' 블럭을 가져와서 위의 과정을 하는 블록을 모두 안에 넣어준다.


7. 프로그램을 시작하기 위해서 '제어' 의 '(깃발이)클릭되었을때' 블럭을 맨 위에 놓아 준다. 



위의 프로그램을 실행한 결과 동영상이다. 처음에 꺼져 있던 LED 가 온도센서에 손가락을 대자 온도가 올라가고 LED 가 켜지는 것을 볼 수 있다. 반대로 손가락을 떼니 곧 온도가 내려가고 LED 가 꺼진다.


일단 S4A를 이용해서 입력과 출력을 모두 해 보았다. 이 외에도 수 많은 센서를 사용이 가능하며 구글링 해 보면 S4A를 가지고도 엄청난 작품(?)을 만든 사례들이 많이 있다. 이런 여러가지 사례들을 연구해보고 교육용으로서 S4A를 활용한다면 컴퓨터 화면만 들여다 보면서 하는 소프트웨어 교육 보다는 덜 따분하고 재미있는 교육이 되지 않을까 생각해 본다.

반응형
반응형

LED에 대해서는 이 블로그에서 많이 다루었는데 아이러니 하게도 LED 밝기 조절은 해보지 않았던 것 같다. 그래서 오늘은 LED 밝기 조절과 여러가지 색의 LED를 이용해서 색의 혼합을 통한 색상 만들기에 대해서 알아보도록 하겠다. 아두이노에 연결된 LED 는 ON/OFF 의 디지털 신호만 있는 것이 아니라 아날로그 신호를 보낼 수 있다. 즉 코드에 따라서 출력전압을 조절 할 수 있는 것이다. LED에 공급되는 전압을 조절할 수 있게되면 LED의 밝기를 조절할 수 있는 것이다. 통상적으로 LED 는 전압이 커지면 밝아지고 낮으면 어두워진다.


아두이노 우노의 경우  3, 5, 6, 9, 10, 11 번 핀(Digital PWM 지원 핀)으로 analogWrite 를 할 수 있다. 먼저 코드를 하나 만들어 보겠다. 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#define REDPIN 5
 
#define FADESPEED 10     // 밝아지고 어두워지는 Speed 조절
 
void setup() {
  pinMode(REDPIN, OUTPUT);
}
 
void loop() {
  int r;
  // 빨간 LED 가 점점 밝아짐
  for (r = 0; r < 256; r++) { 
    analogWrite(REDPIN, r);
    delay(FADESPEED);
  }
  
  // 빨간 LED 가 점점 어두워짐
  for (r = 255; r > 0; r--) { 
    analogWrite(REDPIN, r);
    delay(FADESPEED);
  }   
}
cs

 

위의 코드는 아두이노 5번핀에 연결된(물론 LED 짧은 다리는 GND에 연결) LED를 analogWrite를 이용해서 점진적으로 밝게 그리고 어둡게 하는 코드이다. 코드에 보면 for 문으로 총 255 단계(256 아님, 256일 경우 LED 꺼짐)의 전압을 LED 로 보내고 있다. 255 단계의 전압값은 변수 r 에 저장되어서 analogWrite(연결핀, 밝기값 r) 로 LED 의 밝기를 컨트롤 하고 있음을 알 수 있다. FADESPEED 변수는 밝아지고 어두워지는 시간을 조절한다.


위의 동영상은 위의 코드를 적용한 동영상이다. 우측의 노란, 파란 LED 는 다음 테스트를 위한 LED로서 신경쓰지 말자 영상을 보면 빨간 LED가 DigitalWrite 할 때 와는 다르게 점진적으로 밝아지고 어두워지는 것을 볼 수 있다.


그럼 이번에는 analogWrite를 이용해서 빛의 삼원색인 RGB (Red, Green, Blue) 를 가지고 빛을 혼합하여 새로운 색을 만드는 원리를 알아보자 하지만 이를 제대로 알아보기 위해서는 RGB LED Strip (LED의 빛을 잘 섞어줄 수 있도록 만든 부품) 이라는 것이 필요한데 나에게는 이것이 없다. 만약 가지고 있다면 아래에서 소개하는 코드를 이용해서 보다 완벽하게 구현할 수 있을 것이다.

 

위의 사진이 RGB LED Strip 이라는 부품이다. 보라색을 내고 있는 것을 볼 수 있는데 이것은 Blue 와 Red 를 섞으면 이런 색이 나온다고 한다. R, G, B 색을 내는 LED 중에서 R 과 B 가 적절한 밝기로 섞이는 순간 이런 보라색을 내는 것이다.


위의 그림은 RGB 를 섞으면 대충 어느색으로 변하는지 알려준다. 참고로 모두 다 섞으면 백색이 된다.


연결은 간단하다. 브레드보드를 이용해서 아두이노의 GND 를 각 LED의 짧은 다리를 공통적으로 연결했으며 (음극, Cathode) , Anode쪽의 연결은 Red 는 5번핀, Yellow 는 6번핀, Blue 는 3번핀에 연결하였다. 그리고 녹색 LED가 없어서 Yellow 를 연결하였다. 물론 이렇게하면 당연히 제대로 된 빛의 혼합에 의한 색은 볼 수 없다. 그저 3개의 LED가 밝아졌다 꺼졌다 할 뿐이다. 이 글의 목적은 원리의 파악이므로 그냥 넘어가기로 한다. ;;  제대로 해 보고 싶은 사람은 위에서 말했던 RGB LED Strip 이라는 부품을 연결하면 된다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#define REDPIN 5
#define GREENPIN 6
#define BLUEPIN 3
 
#define FADESPEED 5     // 밝아지고 어두워지는 Speed 조절
 
void setup() {
  pinMode(REDPIN, OUTPUT);
  pinMode(GREENPIN, OUTPUT);
  pinMode(BLUEPIN, OUTPUT);
}
 
void loop() {
  int r, g, b;
 
  // 파란색에서 보라색으로 변화
  for (r = 0; r < 256; r++) { 
    analogWrite(REDPIN, r);
    delay(FADESPEED);
  } 
  // 보라색에서 빨간색으로 변화
  for (b = 255; b > 0; b--) { 
    analogWrite(BLUEPIN, b);
    delay(FADESPEED);
  } 
  // 빨간색에서 노란색으로 변화
  for (g = 0; g < 256; g++) { 
    analogWrite(GREENPIN, g);
    delay(FADESPEED);
  } 
  // 노란색에서 녹색으로 변화
  for (r = 255; r > 0; r--) { 
    analogWrite(REDPIN, r);
    delay(FADESPEED);
  } 
  // 녹색에서 청록색으로 변화
  for (b = 0; b < 256; b++) { 
    analogWrite(BLUEPIN, b);
    delay(FADESPEED);
  } 
  // 청록색에서 파란색으로 변화
  for (g = 255; g > 0; g--) { 
    analogWrite(GREENPIN, g);
    delay(FADESPEED);
  } 
}
cs


위의 코드는 각각의 LED 의 밝기를 조절해서 빛을 혼합해서 새로운 색을 만들어주는 코드이다. RGB 에 없는 보라색, 노란색, 청록색 등을 만들어 주는 것을 알 수 있다. 여기에서 쓰인 변수들도 맨 위의 코드와 마찬가지로 for 문을 이용해서 각각의 LED 에 analogWrite 값을 조절해 밝기를 조절하고 있는 것을 알 수 있다. 길이만 늘어났지 거의 같은 코드이다.


실행결과이다. 예상대로 빛이 섞이지 않고 그냥 따로 따로 논다. 하지만 이런 원리로 빛의 합성으로 새로운 색을 만들 수 있다는 것은 알게되었다. 지금 드는 생각이지만 3개의 LED의 간격을 줄이고 빛의 산란을 일으키는 뿌연 간유리 등을 통해 색을 본다면 혼합될 수도 있지 않을까? 생각해본다. 아무튼 지금은 그런게 없으니 패스.


이런 원리가 요즘 많이 쓰이는 곳이 있다. 바로 공원 산책로 등에 쓰이는 LED Strip 이다. 위의 영상은 우리동네 산책로에 있는 LED Strip이 쓰인 길이다. 길다란 호스모양의 LED Strip이 길 옆 난간에 부착되어 있고 각각의 빛을 발산하면서 새로운 색을 합성하고 변화하면서 역동적인 조명을 만들어낸다. 아마 이런 곳에도 아두이노는 아니겠지만 비슷한 마이크로프로세서가 쓰여서 새로운 색을 만들도록 제어를 하고 있지 않을까 생각해본다.

반응형
반응형

[DF Player Mini 모듈을 이용한 MP3 제어 #1] 글에서 아두이노로 어떻게 DF Player Mini 모듈을 제어하는지 기본적인 사항을 알아보았다. 이번에는 초음파 센서를 추가해서 물체가 감지될 경우 지정한 MP3 파일이 플레이 되도록 해 보겠다. 목표는 집의 현관문에 설치해서 집에 사람이 들어올 경우 좀 유치하지만 "안녕하세요. 우리집에 오신 것을 환영합니다" 라는 멘트를 녹음한 MP3 파일을 재생하는 것이다. 본 글을 따라해보기 전에 다음의 링크를 참고하면 더욱 도움이 될 것이다.



먼저 나의 목소리 보다는 좀 더 나은 목소리를 녹음하기 위해 괜찮은 TTS (Text To Speech) 프로그램을 찾아 보다가 괜찮은 사이트를 발견했다. http://text-to-speech.imtranslator.net/speech.asp?dir=ko 라는 사이트인데 한글을 입력하면 그대로 읽어주는 사이트 이다. 


사이트에 들어가서 읽을 텍스트를 입력하고 Say it 버튼을 클릭하면 그대로 읽어준다. 이것을 oCam 프로그램을 이용하여 mp3 파일로 녹음을 하였다.


녹음한 mp3 파일은 '0099.mp3' 라는 파일명으로 마이크로SD 카드의 'mp3' 폴더에 복사해 주었다.


■ 연결


df_player_mini_sensor.fzz


위와 같이 초음파 센서의 Trig 는 D9에 Echo 는 D8 에 추가 연결해 주었다. 기타 연결에 대한 궁금한  사항은 위의 사전 참고 글 중에서 [DF Player Mini 모듈을 이용한 MP3 제어 #1] 을 참고하면 된다.


 


■ 소스


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <SoftwareSerial.h>
#include <DFPlayer_Mini_Mp3.h>
//초음파 센서 핀설정
int trigPin = 9;
int echoPin = 8;
 
void setup () {
    Serial.begin (9600);
    mp3_set_serial (Serial);     // DFPlayer-mini mp3 module 시리얼 세팅
    delay(1);                     // 볼륨을 Setup 하기 위한 delay
    mp3_set_volume (30);          // 볼륨 0~30
 
    //초음파 센서 핀설정
    pinMode(trigPin, OUTPUT);
    pinMode(echoPin, INPUT);
}
void loop () {
  float duration, distance;
  digitalWrite(trigPin, HIGH);
  delay(10);
  digitalWrite(trigPin, LOW);
 
  // 장애물과의 거리계산
  duration = pulseIn(echoPin, HIGH);
  distance = ((float)(340 * duration) / 10000/ 2;
  delay(100);
  if(distance < 50//장애물이 50cm 이내일때
  {
    mp3_play(99); //안녕하세요... mp3 (0099.mp3) 파일 플레이
    delay(7000);
  }
}
cs


위의 소스는 장애물(사람)이 50cm 이내에 감지될 경우 0099.mp3 파일(안녕하세요. 파일)을 실행한다. 




실행한 결과 영상이다. 손으로 실험해 보았는데 아주 잘 작동한다. 이 모듈을 조금 더 응용을 한다면 시간을 말로 알려주는 시계, 가스누출이나 화재 등 각종 위험요소 들을 센서로 감지하고 경고하는 장치 등을 만드는 것도 가능할 것 같다.

반응형
반응형

먼저 글을 시작하기 전에 이 글 부터는 예전에 글을 쓰던 방식인 반말 포스팅으로 되돌아 가려고 합니다. 존댓말로 포스팅을 하니까 글이 의도하지 않게 늘어지는 경향이 있어서 다시 예전의 방식으로 돌아가려고 합니다. ^^ 


이전 글에서 DF Robot 의 MP3 모듈인 DF Player Mini 에 대해서 알아보았다. 그 때에는 단순히 전원과 스위치를 연결해서 MP3 를 플레이 해 보았는데 사실 이 모듈은 아두이노와의 시리얼 통신을 통해 프로그래밍으로 제어가 가능한 모듈이다. 그러므로 아두이노 등과의 결합을 통하면 더욱 활용범위가 넓어지는 것이다. 이번에는 어떻게 DF Player Mini MP3 모듈을 아두이노로 제어할 수 있는지 알아보고자 한다.


DF Player Mini (이하 MP3 모듈) 이다. 각 핀의 배열은 위와 같다. 


각 핀의 용도는 위와 같다. 핀들 중에서 RX, TX 를 아두이노에 연결하고 시리얼통신으로 MP3 모듈을 제어를 할 수 있는 것이다.


먼저 간단하게 MP3 파일을 제어하는 예제를 실행해 본다. 준비물은 스피커, MP3모듈, 마이크로SD카드, 브레드보드, 연결선, 1KΩ 저항 2개, 아두이노가 필요하다. 연결은 아래와 같다.


■ 연결 #1


df_player_mini_basic_test.fzz


위와 같이 연결을 한다. TX, RX 연결 시 중간에 반드시 1KΩ 저항을 연결해 주어야 한다. 그러지 않으면 아예 아두이노 스케치가 업로드 되지 않고 잡음이 많이 발생한다.


연결이 되었으면 MP3 파일이 담긴 마이크로SD카드를 준비해야 한다. 소스에서 특정 MP3 파일을 지정해서 플레이를 하려면 아래와 같이 파일의 네이밍 규칙을 맞춰서 파일을 복사해야 한다.


위와 같이 SD카드에 mp3 라는 폴더를 만들어주고 위와 같이 0001 ~ 9999 까지 번호를 원래의 MP3 파일명의 앞에 붙여주면 된다. 또는 번호만으로 네이밍을 해도 된다.



■ 소스 #1

소스의 실행을 위해서는 우선 MP3 모듈의 라이브러리가 설치되어 있어야 한다. 

DFPlayer_Mini_mp3.zip


위의 파일을 다운로드 받아서 압축을 푼 후 아두이노 IDE 설치 폴더의 libraries 폴더에 넣어주면 된다. 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <SoftwareSerial.h>
#include <DFPlayer_Mini_Mp3.h>
 
void setup () {
    Serial.begin (9600);
    mp3_set_serial (Serial);      // DFPlayer-mini mp3 module 시리얼 세팅
    delay(1);                     // 볼륨값 적용을 위한 delay
    mp3_set_volume (30);          // 볼륨조절 값 0~30
}
 
void loop () {        
    mp3_play (1);    //0001 파일 플레이
    delay (6000);
    mp3_next ();    //0002 파일 플레이 , next
    delay (6000);
    mp3_prev ();    //0001 파일 플레이 , prev
    delay (6000);
    mp3_play (4);    //0004 파일 플레이
    delay (6000);
}
cs


setup() 에서 시리얼 통신 및 볼륨을 세팅해주고 loop() 에서 mp3_play (플레이할 파일 1~9999) 값을 넣어주면 해당 파일이 플레이 된다. mp3_next() 와 mp3_prev() 각각 다음곡 이전곡을 플레이 한다. 소스를 플레이하면 재생 순서는 다음과 같이 될 것이다.  6초 간격으로 0001 -> 0002 -> 0001 -> 0004 파일이 무한반복 플레이 된다.


실행 영상이다. 위의 mp3 파일을 넣기 전에 플레이한 영상이라서 위의 리스트와 다른 노래가 나오지만 노래만 바뀌면 같은 결과이다.


그럼 그냥 전원이 연결되었을 때 그냥 순차적으로 플레이 하려면 어떻게 해야 할까?


■ 연결 #2


df_player_mini_play_all.fzz


연결 #1 에서 조금 변형을 해야 한다. MP3 모듈의 TX 를 아두이노의 D10에 연결하고 RX 를 D11에 연결하는 것으로 변경한다.  그리고 MP3 모듈의 BUSY 핀을 아두이노의 D3 에 연결을 추가한다.

 

■ 소스 #2

아래의 소스는 MP3 파일을 0001 부터 끝까지 모두 플레이 해 주는 소스이다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <SoftwareSerial.h>
#include <DFPlayer_Mini_Mp3.h>
 
SoftwareSerial mySerial(1011); // RX, TX
 
void setup () {
  Serial.begin (9600);
  mySerial.begin (9600);
  mp3_set_serial (mySerial);    //set softwareSerial for DFPlayer-mini mp3 module 
  delay(1);                     // delay 1ms to set volume
  mp3_set_volume (15);          // value 0~30
}
void loop () {
  boolean play_state = digitalRead(3);// connect Pin3 to BUSY pin of player
  if(play_state == HIGH){
    mp3_next ();
  }
}
cs

 

BUSY 포트는 MP3 파일의 플레이의 플레이가 끝날 경우 아두이노에 HIGH 값을 보내준다. 그 때 mp3_next() 를 실행하여 다음곡을 플레이하는 원리이다. 실행하면 0001 ~ 9999 까지의 파일을 순차적 플레이를 반복한다.


아래는 MP3 모듈 라이브러리의 function 들이다. 아래의 기능만 있어도 거의 모든 mp3 플레이어의 기능을 구현 가능하다. 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
   mp3_play ();     //start play
   mp3_play (5);    //play "mp3/0005.mp3"
   mp3_next ();     //play next 
   mp3_prev ();     //play previous
   mp3_set_volume (uint16_t volume);    //0~30
   mp3_set_EQ ();   //0~5
   mp3_pause ();
   mp3_stop ();
   void mp3_get_state ();   //send get state command
   void mp3_get_volume (); 
   void mp3_get_u_sum (); 
   void mp3_get_tf_sum (); 
   void mp3_get_flash_sum (); 
   void mp3_get_tf_current (); 
   void mp3_get_u_current (); 
   void mp3_get_flash_current (); 
   void mp3_single_loop (boolean state);    //set single loop 
   void mp3_DAC (boolean state); 
   void mp3_random_play (); 
cs


다음글 에서는 센서를 붙여서 테스트를 해 보려고 한다. 센서에서 값이 감지되면 해당되는 음성을 출력해 보려고 한다.

반응형
반응형

4채널 릴레이 모듈을 구입해서 아두이노 나노와 연결해서 테스트 해 보았습니다. 우노가 아니라 나노와 연결해서 릴레이 동작을 위한 전력이 부족할지 우려가 되었는데 결론적으로 나노의 5V 출력과 디지털핀 연결을 통해서도 모든 동작이 가능했습니다. 릴레이에 대해서는 예전의 글인  '[아두이노] 릴레이(Relay)를 이용하여 220V 전원을 제어해보자' 라는 글에서 릴레이의 원리부터 자세히 알아 본 적이 있으므로 참고 하시면 됩니다. 


모듈은 위와 같이 생겼습니다. 4개의 릴레이가 붙어 있고 각각 상태를 나타내는 LED 가 붙어 있습니다. 그리고 제어를 위한 핀 4개와 전원연결 핀 2개, 총 6개의 핀이 있습니다. 사진의 릴레이의 스위칭 가능 전력은 AC250V 10A, DC30V 10A 이므로 AC의 경우 우리나라 가정용 전원인 220V 전원을 컨트롤 가능 합니다. 동작 전원은 5V 를 이용하며 IN1 ~ IN4 에 아두이노의 디지털 핀을 연결해 주면 됩니다.


■ 릴레이 데이터시트

solgle_relay_datasheet.pdf



릴레이의 전원제어부 단자 입니다. 릴레이 1개당 3개의 단자가 제공되며 릴레이에 전원이 공급되지 않을 때 (릴레이 OFF 시) 에는 공통단자(COM)가 사진 상 우측의 단자와 연결이 되고 릴레이에 전원이 공급될 경우는 좌측의 단자와 연결이 됩니다. 곧, 릴레이 ON 시에 전원을 연결할지 OFF 할지는 용도에 따라 결정하면 될 것 입니다.


■ 연결

연결은 아래 표와 같이 했으며 아두이노 우노에서도 연결은 같습니다.

 4 Channel Relay

 Arduino Nano

 GND

 GND

 IN1

 D2

 IN2

 D3

 IN3

 D4

 IN4

 D5

 VCC

 5V




■ 소스

테스트를 위한 소스 이므로 간단 합니다. 각각의 릴레이를 1초 간격으로 ON/OFF 하고 전체 릴레이를 ON/OFF 해 보는 소스 입니다. 참고로 이 모듈의 경우 Digital Wite LOW 시 릴레이에 전원이 공급이 되네요. 뭔가 반대로 된 것 같기도 한데 뭐 상관 없습니다. 적용 시 거꾸로 하면 되니까요.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
int relay1 = 2//릴레이1 핀설정
int relay2 = 3//릴레이2 핀설정
int relay3 = 4//릴레이3 핀설정
int relay4 = 5//릴레이4 핀설정
 
void setup ()
{
  pinMode (relay1, OUTPUT); // relay1를 output으로 설정한다.
  pinMode (relay2, OUTPUT); // relay2를 output으로 설정한다.
  pinMode (relay3, OUTPUT); // relay3를 output으로 설정한다.
  pinMode (relay4, OUTPUT); // relay4를 output으로 설정한다.
 
  //시작시 릴레이를 OFF 로 설정
  digitalWrite (relay1, HIGH);
  digitalWrite (relay2, HIGH);
  digitalWrite (relay3, HIGH);
  digitalWrite (relay4, HIGH);
}
void loop ()
{
  digitalWrite (relay1, LOW); // 릴레이 ON
  delay (1000);              //1초 delay
  digitalWrite (relay1, HIGH); // 릴레이 OFF
  delay (1000);               //1초 delay
 
  digitalWrite (relay2, LOW); // 릴레이 ON
  delay (1000);              //1초 delay
  digitalWrite (relay2, HIGH); // 릴레이 OFF
  delay (1000);               //1초 delay
 
  digitalWrite (relay3, LOW); // 릴레이 ON
  delay (1000);              //1초 delay
  digitalWrite (relay3, HIGH); // 릴레이 OFF
  delay (1000);               //1초 delay
 
  digitalWrite (relay4, LOW); // 릴레이 ON
  delay (1000);              //1초 delay
  digitalWrite (relay4, HIGH); // 릴레이 OFF
  delay (1000);               //1초 delay
 
  digitalWrite (relay1, LOW); // 릴레이 ON
  digitalWrite (relay2, LOW); // 릴레이 ON
  digitalWrite (relay3, LOW); // 릴레이 ON
  digitalWrite (relay4, LOW); // 릴레이 ON
  delay(1000);
  digitalWrite (relay1, HIGH); // 릴레이 OFF
  digitalWrite (relay2, HIGH); // 릴레이 OFF
  digitalWrite (relay3, HIGH); // 릴레이 OFF
  digitalWrite (relay4, HIGH); // 릴레이 OFF
  delay (1000);               //1초 delay
  digitalWrite (relay1, LOW); // 릴레이 ON
  digitalWrite (relay2, LOW); // 릴레이 ON
  digitalWrite (relay3, LOW); // 릴레이 ON
  digitalWrite (relay4, LOW); // 릴레이 ON
  delay(1000);
  digitalWrite (relay1, HIGH); // 릴레이 OFF
  digitalWrite (relay2, HIGH); // 릴레이 OFF
  digitalWrite (relay3, HIGH); // 릴레이 OFF
  digitalWrite (relay4, HIGH); // 릴레이 OFF
  delay(1000);
}
cs

차례대로 릴레이가 잘 작동하는 모습 입니다.


아래 소스는 릴레이가 얼마나 빨리 전류를 단속할 수 있는지 테스트 해 보았습니다. 50ms 즉, 1/20초 마다 동작하도록 해 보았는데 이것도 잘 되네요. 더 이상은 무리 같아서 더 줄이지는 않았습니다. ^^


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
int relay1 = 2//relay1에 5V 신호를 보낼 핀설정
int relay2 = 3//relay2에 5V 신호를 보낼 핀설정
int relay3 = 4//relay3에 5V 신호를 보낼 핀설정
int relay4 = 5//relay4에 5V 신호를 보낼 핀설정
 
void setup ()
{
  pinMode (relay1, OUTPUT); // relay1를 output으로 설정한다.
  pinMode (relay2, OUTPUT); // relay2를 output으로 설정한다.
  pinMode (relay3, OUTPUT); // relay3를 output으로 설정한다.
  pinMode (relay4, OUTPUT); // relay4를 output으로 설정한다.
 
  digitalWrite (relay1, HIGH);
  digitalWrite (relay2, HIGH);
  digitalWrite (relay3, HIGH);
  digitalWrite (relay4, HIGH);
}
 
void loop ()
{
  digitalWrite (relay1, LOW); // 릴레이 ON
  delay (50);              //50ms delay
  digitalWrite (relay1, HIGH); // 릴레이 OFF
  delay (50);               //50ms delay
 
  digitalWrite (relay2, LOW); // 릴레이 ON
  delay (50);              //50ms delay
  digitalWrite (relay2, HIGH); // 릴레이 OFF
  delay (50);               //50ms delay
 
  digitalWrite (relay3, LOW); // 릴레이 ON
  delay (50);              //50ms delay
  digitalWrite (relay3, HIGH); // 릴레이 OFF
  delay (50);               //50ms delay
 
  digitalWrite (relay4, LOW); // 릴레이 ON
  delay (50);              //50ms delay
  digitalWrite (relay4, HIGH); // 릴레이 OFF
  delay (50);               //50ms delay
}
cs


매우 빠르게 스위칭이 됩니다. 따라라라락~



4채널 릴레이는 IoT 등에서도 유용하게 쓰일 수 있을 것 같습니다. 채널이 많으니 그 만큼 제어가 가능한 장치도 늘어날 것이기 때문 입니다. ESP8266 모듈 등과도 조만간 연결해서 인터넷을 통해 제어해 볼 생각 입니다.

반응형
반응형

바로 이전 글에서 알아보았던 'ATTiny85 (Digispark Board) 보드' 글의 마지막에 추가내용으로 스마트폰이나 PC를 연결하면 키보드 입력하는 것과 같이 ATTiny85의 출력값을 읽어올 수 있다고 했었습니다. 이것을 이용해서 스마트폰과 PC의 잠금해제를 하는 키(Key)를 만들어 보겠습니다. 일종의 하드웨어 Key 같은 것이죠. 요즘도 그런지는 모르겠지만 예전 고가의 소프트웨어들은 불법복제를 방지하기 위해서 USB 형태나 시리얼 포트에 꼽는 형태로 하드웨어 키를 제공하기도 했습니다. 하드웨어 키가 PC에 꼽혀 있지 않으면 프로그램이 실행되지 않았던 것이죠. ATTiny85 를 이용하면 이런 것도 구현이 가능 하겠네요. 아무튼 이 글에서는 비밀번호로 잠겨 있는 PC와 스마트폰을 ATTiny85 보드를 연결하면 잠금해제 되도록 구현해 보겠습니다. 스마트폰의 경우는 안드로이드 폰을 이용하였고 아이폰은 잘 모르겠습니다. OTG 케이블을 이용해서 폰에 연결을 해야 하거든요.


먼저 이 글을 이해하려면 먼저 아래의 글을 읽어서 ATTiny85 (Digispark Board) 보드에 대한 설치와 일반적인 내용을 알아야 하고 개발환경의 세팅이 되어 있어야 합니다.


 


■ 연결

1. 스마트폰 : 스마트폰 ↔ OTG 케이블  스마트폰 케이블(마이크로 5핀)  ATTiny85 보드 순으로 연결 합니다.


2. PC : PC  스마트폰 케이블(마이크로 5핀)  ATTiny85 보드 순으로 연결 합니다. 




■ 소스 & 실행결과

먼저 Digispark 의 예제를 하나 보겠습니다.

위와 같이 예제->DigisparkKeyboard->Keyboard 에 들어가서 열어주면 됩니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include "DigiKeyboard.h"
 
void setup() {
  // don't need to set anything up to use DigiKeyboard
}
 
 
void loop() {
  // this is generally not necessary but with some older systems it seems to
  // prevent missing the first character after a delay:
  DigiKeyboard.sendKeyStroke(0);
  
  // Type out this string letter by letter on the computer (assumes US-style
  // keyboard)
  DigiKeyboard.println("Hello Digispark!");
  
  // It's better to use DigiKeyboard.delay() over the regular Arduino delay()
  // if doing keyboard stuff because it keeps talking to the computer to make
  // sure the computer knows the keyboard is alive and connected
  DigiKeyboard.delay(5000);
}
cs

위와 같은 소스 입니다. DigiKeyboard 라이브러리를 이용해서 'Hello Digispark!' 라는 문자열을 매 5초 마다 키보드를 누르듯이 출력해 주는 예제 입니다. 이것을 이용하면 잠금 해제 장치를 만들 수 있습니다. 다만 5초 마다 계속 입력이 되므로 Loop 가 아니라 Setup 부분에 넣어주면 됩니다. 


테스트 삼아 위의 소스가 잘 작동하는지 업로드를 해 봅니다. 업로드는 위의 사전 참조글에도 설명이 되어 있듯이 아두이노 IDE 의 업로드 버튼을 누른 후에 ATTiny85 보드를 60초 이내에 USB 포트에 연결해야 정상적으로 업로드가 됩니다. 업로드 시 장치가 이미 연결되어 있다면 업로드가 되지 않습니다.


실행 결과 입니다. 5초는 너무 많은 것 같아서 1초 간격으로 써지도록 수정을 했습니다. 'DigiKeyboard.delay(1000);' 으로 수정 했습니다.



1
2
3
4
5
6
7
8
9
10
11
#include "DigiKeyboard.h"
 
void setup() {
  DigiKeyboard.sendKeyStroke(0);
  DigiKeyboard.println("");
  DigiKeyboard.delay(1000);
  DigiKeyboard.println("패스워드");
}
 
void loop() {
}
cs

잠금해제 소스 입니다. '패스워드' 부분에 스마트폰이나 PC의 잠금해제 패스워드를 넣어주면 됩니다. setup 에 있으므로 1번 밖에 실행이 되지 않습니다.


PC 에 ATTiny85 보드를 꼽아서 잠금해제 하는 영상 입니다. 장치를 인식하는데 약간의 시간이 걸린 후 암호가 자동으로 입력되고 PC가 잠금해제 됩니다.


스마트폰에 연결해서 잠금해제 하는 영상 입니다. 스마트폰에는 아무런 설정도 필요 없습니다. 그냥 위의 프로그램을 작성해서 업로드 후 OTG 케이블을 이용해서 연결해 주면 됩니다. 단, 패스워드 입력 방식으로 잠금 설정이 되어 있어야 합니다.



이상으로 ATTiny85 보드를 이용한 키보드 입력에 대해서 알아 보았습니다. 이 기능은 통신포트(COM포트)가 잡히지 않아 시리얼 모니터를 사용할 수 없는 ATTiny85 의 출력값을 읽을 때에도 유용하게 사용할 수 있습니다.

반응형

+ Recent posts