어렸을적 위대한 발명가, 과학자 하면 단연 손꼽는 일인자가 있었는데 누군가 물어본다면 100 이면 100 다 토마스 에디슨을 떠올릴 것입니다.. 그런데 커보니 에디슨은 발명가나 과학자라기보다는 사업가에 가까운 사람이었고 정반대 편에 니콜라 테슬라라는 진정 위대한 과학자가 존재함을 알게되었습니다.
테슬라의 위대한 발명품중에 단연 인지도 최강 아이템.
바로 테슬라 코일 되시겠습니다.
제가 과학도도 아니고 테슬라코일의 원리나 구조에 대해 이야기할건 아니고요. 알리익스프레스에서 2000원 정도의 저렴한 가격에 구입할 수 있는 킷이 있어 구입을 해 보았습니다.
일단 배송비가 있어 2세트를 주문 했는데 배송비까지 모두 해서 5000원 정도에 구입을 하였습니다.
약 1달여가 지나 도착했고.... 아래와 같이 포장이 되어 있었습니다.
그냥 비닐백에 싸여 왔습니다. 물론 알리 특유의 회색 비닐백안에 들어있었습니다.
음.. 좀 조잡한데?
부품은 별거 없습니다만.... 별도의 조립 설명서 같은건 없군요...
음... 조립 설명서가 없다라...
보드에 뭔가 적혀있는 것을 자세히 봤습니다.
음.. 대략적으로 감은 오는데...
네 어려울 건 없네요.
그냥 생긴데로 끼우면 되는거였습니다.
딱히 잘못 조립할 수있는 유일한 부품은 LED 군요.
다행인건 보드에 표시가 되어 있고 LED 의 극성을 구분하는 것은 쉬운 일이니까요. LED 안쪽을 자세히 보면 핀과 연결된 부분이 보이는데 넓은 면이 - 입니다. 다리 길이로 보면 긴쪽이 + 가 되지요.
그레서 바로 조립해 보았습니다.
조립하는데에는 15분도 걸리지 않습니다. 제가 어렸을 적 라디오 키트 조립 같은게 유행했었는데 말이죠. 그 시절의 저였다면 초등학생이지만 가능한 간단한 조립입니다.
참고로 코일 원통에 선이 위로 한가닥 아래로 한가닥이 나오는데요. 보드에는 T 라고 써있는 부분에 한가닥만 연결하시면 됩니다.
다른 한가닥은 바로 스파크를 내뿜는 곳이죠.
그리고 코일통과 보드는 따로 고정해줄 수 있는 구조가 없기 때문에 실리콘이나 본드로 붙이셔야 합니다.
저는 E7000 본드로 고정을 해주었습니다.
어쨌든 2개를 샀으니 하나는 아들내미에게 기회를 한번 줘 볼까 합니다.
드디어 조립완료
굴러다니는 12V 아답터를 연결해주니 안쪽의 LED가 켜지면서 동작이 되네요. 가운데 보이는 스위치로 껏다 켰다 할수도 있습니다.
자 이제 테슬라코일임을 즐겨야겠지요?
동봉되어 있는 부품중에 할로겐 전구 같은 전구가 하나 있습니다.
그 전구가 바로 테스트용 전구인데요. 테슬라 코일을 동작시키로 가까이 가져가면 불이 켜지는것을 테스트 해보기 위한 재료입니다.
두구두구
드디어 사진이나 영상으로만 보던걸 제 손으로 해보는 군요.
두구두구두구..
쨔쨘~ ㅋㅋ
올~~
진짜 되는군요.
그럼 진짜배기 테스트를 해봐야 겠지요?
형광등을 하나 준비합니다. 굴러다니는 삼파장 등이면 됩니다.
두구두구
올~~~~
ㅋㅋㅋ
정말 재미있네요.
내일은 아이들에게 신기한 장면을 좀 보여주어야 겠습니다.
상단에 코일 끝부분에서 스파크가 나오는데 ... 음.. 사진이 제대로 찍힌게 없어서 올리지는 못하겠네요.
나중에 제대로 나온 사진이 있으면 본 포스트에 추가하도록 하겠습니다.
오늘도 재미있는 놀거리를 하나 만들었습니다. 만들기도 쉽고 가격도 저렴하고요. 아이들과 니콜라 테슬라라는 과학자에 대한 이야기를 나누어 볼수도 있겠네요.
이제는 콤보박스의 선택정보가 변경될 때 시리얼 포트가 열려있지 않다면 포트이름과 설정을 지정해 주고 열도록 하겠습니다.
UI 메뉴에서 콤보 박스를 더블클릭하면 함수가 자동으로 생성되는데요. 만들어진 함수에 아래와 같이 코드를 작성하시면 됩니다.
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
if (mySerial.IsOpen) // 특정 포트가 이미 열려 있다면 설정이 되지 않기 때문에 우선 닫는다.
{
mySerial.Close();
}
if (!mySerial.IsOpen) //시리얼포트가 닫혀있을 때만
{
mySerial.PortName = comboBox1.Text; // 선택된 combobox 의 이름으로 포트명을 지정하자
mySerial.BaudRate = 9600; //아두이노에서 사용할 전송률를 지정하자
mySerial.DataBits = 8;
mySerial.StopBits = StopBits.One;
mySerial.Parity = Parity.None;
mySerial.Open(); //시리얼포트 열기
}
else
{
MessageBox.Show("해당포트가 이미 열려 있습니다.");
}
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO.Ports;
namespace csharp2arduino_serial
{
public partial class Form1 : Form
{
private SerialPort mySerial;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
mySerial = new SerialPort();
comboBox1.DataSource = SerialPort.GetPortNames();
}
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
if (mySerial.IsOpen)
{
mySerial.Close();
}
if (!mySerial.IsOpen) //시리얼포트가 닫혀있을 때만
{
mySerial.PortName = comboBox1.Text; // 선택된 combobox 의 이름으로 포트명을 지정하자
mySerial.BaudRate = 9600; //아두이노에서 사용할 전송률를 지정하자
mySerial.DataBits = 8;
mySerial.StopBits = StopBits.One;
mySerial.Parity = Parity.None;
mySerial.Open(); //시리얼포트 열기
}
else
{
MessageBox.Show("해당포트가 이미 열려 있습니다.");
}
}
private void button1_Click(object sender, EventArgs e)
{
// 텍스트 박스 추가한 버전
string myMsg = textBox1.Text;
if (myMsg != "")
{
myMsg += "\n";
}
else
{
myMsg = "Hello Arduino?\n";
}
byte[] datas = StringToByte(myMsg);
mySerial.Write(datas, 0, datas.Length);
}
private byte[] StringToByte(string _str)
{
byte[] tmpBytes = Encoding.UTF8.GetBytes(_str);
return tmpBytes;
}
}
}
아두이노에서 시리얼 메시지 받기
아두이노에서 시리얼 메시지를 받는것은 사실 매우 쉽습니다. 아두이노 기본 예제 중에 Serial 을 사용하는 것을 많이 보셨죠? 아두이노는 Serial 을 사용하기 위하여 별도로 설정할것이 거의 없습니다.
아두이노를 열어 주시고 아래와 같이 코드를 작성합니다.
String myStr = "";
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
}
void loop() {
if(Serial.available()){//시리얼에 데이터가 있다면
char data = (char)Serial.read();//한 문자씩 읽어 문자형 변수에 담고
if((int)data != 10){ //개행문자 '\n' -> newline 이 아니라면 스트링에 계속 저장
myStr += data;
}else{
// \n 을 만나면 여기로 와서 문자열을 출력하면 된다.
// LCD 에 문자를 여기서 출력하면 됨
//그리고 문자열은 다시 초기화를 해주겠습니다.
myStr = "";
}
}
}
완전 간단하죠? 시리얼 신호가 들어오면 \n 이라는 줄바꿈 문자열이 오기 전까지 계속해서 한글자씩 문자열 변수에 추가를 해주었다가 \n 을 만나면 필요한 결과를 수행하고 원래 문자열 변수를 초기화 하는 것이죠.
저는 개인적으로 해치백이나 왜건 형태의 차량을 선호합니다. 스포티해 보이기도 하고 실용적이기도 하지요. 실제로 트렁크가 달려있는 세단보다 트렁크부분을 잘라낸듯한 해치백이 차량의 운동성능이 훨씬 좋기 때문에 랠리 차량의 경우 대부분 해치백과 같은 형태의 디자인을 하고 있는 것을 알 수 있죠.
유럽의 경우 해치백이나 왜건 차량이 우리나라와 비교하면 압도적으로 많을 정도로 많은 사랑을 받고 있는 차의 형태이기도 합니다. 차종도 아주 다양하고 거의 대부분의 차량 라인업에 아반트, 에스테이트 등의 명칭으로 왜건모델이 함께 만들어지고 있는 것도 운전자들의 선호도를 반영하는 것이기도 합니다.
심지어 아우디의 RS6 와 같은 준 슈퍼카급 모델도 초기에는 왜건형태의 차량으로 유명해 졌으며 벤츠나 BMW, 볼보와 같은 프리미엄급 차량에서도 슈팅브레이크나 크로스컨트리 같은 승용차의 플랫폼에 왜건형태의 차량을 만들고 있는걸 보면 정말 많은 인기를 누리고 있지 않나 생각합니다.
그런데 말입니다. 우리나라는....
우리나라는 작년 유일한 왜건 차량인 i40 가 단종되는 기이한 상황이 도래했습니다. 위에 장황하게 적어놓은 것 과는 너무나 대조적이지요? 왜건의 강국인 유럽 소비자를 타겟으로 심혈을 기울여 만든 만큼 실제로 유럽에서는 i30, i40 시리즈가 많은 인기를 얻었으며 i40 의 경우 첫번째 개발된 플랫폼으로 무려 9년이라는 긴 시간을 자리를 지켜왔으니 정말 잘만든 차량이기는 했던것 같습니다. 하지만 긴 시간동안 신차가 나오지 못해서 였는지 결국 소비자의 외면속에 역사속으로 사라지는 비운의 차량이 되기도 했습니다.
그렇다면 왜 국내 소비자는 i40을 그렇게 멀리했던 걸까요? 정말 대한민국은 왜건의 무덤인걸까요? 정말 SUV 에 비하여 왜건은 상품성이 떨어지는 걸까요?
아파트 주차장에 서있는 볼보 V90 크로스컨트리와 V60, 길가에 흔히 돌아다니는 BMW 3,5시리즈 왜건, 지나가면 사람들의 눈길을 사로잡는 벤츠의 CLS 슈팅브레이크를 보고 있자면 과연 우리나라 사람들이 왜건을 싫어 하는 걸까 하는 의문마져 듭니다. 심지어 그렇게 안팔렸다던 i40 은 고속도로에서 심심찮게 보이기도 합니다.
정말 소비자의 문제인걸까요?
아쉬운 제가 한번 만들어 봤습니다.
그래서 제가 한번 대충 이런 모델이 나오면 어떨까 싶어서 제가 갖고 싶은 그런 차를 그려 보았습니다.
어떻습니까? 괜찮지 않습니까?
이정도로만 나와 준다면 그리고 볼보 V90보다 약 3~4천만원 저렴하다면?
볼보는 그냥 씹어먹지 않겠습니까?
손 덴김에 후측면도 한번 만들어 보았습니다.
어떠신가요?
진심으로 저라면 볼보 V60이나 V90이라면 고민할 것도 없이 위의 모델을 구입하겠습니다. 물론 약간의 옵션이나 상품성이 보장되어야 하겠죠.
기존 i40 처럼 유럽스타일의 탄탄한 하체, 싸구려같지 않은 마감재, 준수한 오디오, 편안한 승차감이 받침된다면 정말 바랄것이 없을 것 같네요.
사실 왜건의 장점은 타본사람만 안다는 이야기가 있습니다. 승차감만 비교하면 비슷한 기술력을 가진 차량 제조사에서 만들어진 차량이라면 왜건이 압도적으로 좋습니다. 코너링도 안정적이며 고속으로 갈수록 비교할 수가 없죠.
결론
국내 소비자를 위하여도 좀더 다양한 차량 선택지가 주어지도록 꾸준히 디자인을 개선하고 상품성 높은 차량을 만들어 간다면 결국 소비자의 사랑을 받게 되지 않을까요?
모두들 왜건의 매력이 빠지는 그날이 오기를 기대해 봅니다.
SUV 와는 다른 매력의 Wagon .. 어떠세요?
<이미지 출처 : 구글검색 / 현대자동차 공식 사이트>
정말 저런 차를 기다리고 기다리는 한명의 현대차 오너입니다. 제말 만들어 주세요. 문제가 되면 삭제하도록 하겠습니다.
아두이노로 통합(만능) 리모콘 만들기 3/3 이라 쓰고 왠지 이번 포스트에서 끝나지 않을 것 같은 느낌이 강하게 듭니다. 지난 포스트에서 아두이노 IR 센서를 이용하여 신호를 해킹하고 또 필요한 신호를 따서 기록하는 것까지 소개해 드렸는데요. 해당 포스트는 아래 링크를 참고해 주세요.
이번 포스트에서는 본격적으로 SW 를 설계하고 HW 를 만들어가는 과정을 소개해 드릴까 합니다.
먼저 조사했던 여러가지 기능을 모두 소화하기에 아두이노의 입력핀은 너무 적기 때문에 기기 전환 버튼같은 기능을 두어서 버튼은 동일한 레이아웃을 가지고 아두이노의 동일한 핀으로 입력 신호가 들어가더라도 실제 출력해주는 IR 신호는 다르게 보내주는 방식으로 구현을 할 계획입니다.
필요한 재료
3cm x 7cm 양면 만능기판 (2.54mm 규격) x 2EA
아두이노 나노
Attiny85
SW-200D 진동/틸트 스위치 양쪽 다리형
Push switch (2pin) x 14 EA
LED (red, SMD) x 4EA
LED (green, SMD) x 1EA
5V DCDC Converter, Step Up Power Supply 보드
TP4056 18650 charge module
0.1F 전해콘덴서 (슈퍼콘덴서??) 꼭 필요한지 모르겠음
대충 33옴 정도되는 저항, 낮아도 별 상관 없음 (SMD LED 연결용)
IR LED (송신용) x 1EA
18650 배터리 1EA
SW 를 대충 설계해보자.
먼저 아두이노의 인풋, 아웃풋 키와 함께 각각의 기능을 매칭하는 테이블을 만들어야겠습니다. 아무래도 여러가지 기능이 동작되어야 하므로 나중에 실수하지 않으려면 각각의 입력핀에 연결될 버튼과 기능을 정의하는게 중요합니다.
위와 같이 각각의 기능을 테이블로 만들어 보았습니다. 가장 우측의 비고에 해당하는 열이 아두이노의 핀번호가 되겠으며 9~12번은 4개의 제품에 해당되는 LED 를 켜주기 위한 출력핀입니다. 18번은 제품전환용 버튼 입력핀이 됩니다.
제일 좌측에 S01~S14까지 번호가 있는데요. 이번호들은 각각 아래와 같은 형태로 배치할 계획입니다.
저는 양면 만능기판을 사용할 계획이고요. 만능기판의 끝부분에 단자용도로 사용할 수 있는 납이 붙어있어 그부분까지 일단 납땜을 하여 연결해 보기로 합니다. 중앙 상단의 네게의 흐릿한 동그라미가 LED 1234 와 연결이 되는 것이죠.
14개의 버튼은 모두 1개의 그라운드핀과 연결되며 나머지 한쪽 다리를 아래쪽 단자에 순차적으로 매칭을 시켜줍니다.
LED 역시 그라운드를 모두 한꺼번에 연결하고 아래쪽 LED 핀은 아두이노 디지털 출력핀과 연결해주어 선택된 제품에 불이 켜지도록 하겠습니다.
만능기판에 버튼을 실장한 사진은 아래와 같습니다.
총 14개의 버튼이 위와 같이 아름답게 배치됩니다. 만능기판의 크기를 생각해보면 아주 아담한 리모컨이 만들어 질 것 같네요.
아두이노 나노와 전원 공급 모듈
전원 입력은 3.7V 리튬이온 18650 배터리를 이용할 계획인데요. 아두이노가 5V 이상의 전압이 필요하기 때문에 5V 로 승압을 해주는 보드를 하나 장착이 필요하고요, 이 제품을 연속으로 사용하지 않을 경우 자동으로 전원 공급이 중단되도록 할 필요가 있을듯 하여 attiny85 를 이용하여 일정 시간이 지나면 전원 공급이 중단되도록 할 계획입니다. atiny는 sleep 모드라는 것을 지원하는데 sleep 모드로 들어가면 거의 전기를 먹지않는 놀라운 기능을 수행하게 됩니다.
다시 동작할때는 reset 핀에 GND 를 입력해주면 다시 동작을 시작하게 되는데 틸트 스위치를 이용하여 움직임을 감지하여 물리적으로 움직임이 발생되면 attiny85의 reset핀으로 신호를 보내줌으로써 attiny 가 전원 공급을 시작하는 것이죠. 이번 제작하는 리모컨 자체가 전류를 거의 사용하지 않기 때문에 문제는 없을 듯 하지만 그래도 attiny 의 디지털 출력핀이 충분한 전기를 공급할 수 있을지는 미지수입니다.
그래서 0.1F 정도의 콘덴서를 전원 공급용으로 하나 달아 보았습니다. ( 사실 전기/전자 지식이 매우 얕아서 저렇게 하면 되는지는 잘 모르겠어요)
그렇게 만들어진 보드가 바로 아래와 같이 만들어졌습니다.
위에서 설명드린 것처럼 우측 상단에 attiny85 가 자리잡고 있고요. (SMD 버전입니다) 중간에 검은 통이 틸트 스위치입니다. 약간 기울여서 달아주어야 평소에 눕혀져 있을때 신호가 끊어지기 때문에 기울여서 부착을 했고요, 아래 검은 네모박스가 0.1F 전해 콘덴서 입니다. 어디서 떼어낸건지는 기억이 안나는데 버리는 가전제품에서 떼어낸 부품입니다.
실제 아두이노 나노로 입력되는 전원은 전해콘덴서와 연결이 되어 있어 attiny 가 sleep 모드로 들어가더라도 일정시간 전원을 공급하여 주는 역할을 하게 됩니다.
우측의 단자부분은 이미 납땜이 마구 되어 있는데요, 위에 소개해드린 버튼부분과 연결이 되게 됩니다.
고난의 시작 ! 인정사정 없이 납땜시작
정말 납땜하다가 정신병 걸리는 줄 알았습니다.
왼쪽은 아두이노 나노를 포함한 메인 컨트롤 부, 오른쪽은 버튼 조작부와 LED 되겠습니다. LED 는 우측 끝에 매달려 있는데 잘 안보이시죠? 작은 회로기판 같은데 신호용으로 아주아주 작은 SMD 타입의 LED가 붙여 있는 것을 보셨을 텐데요, 고 녀석들을 떼어내서 부탁을 해주었습니다. 일부러 구입할일은 없으니까요.
각각의 선들은 위에서 설계한 것처럼 연결을 해주었는데 설계할때는 이게 이렇게나 복잡할거라고는 생각을 못했습니다.
위쪽에 보이는 작은 초록색 기판이 3.7 --> 5V 승압회로 , 그사이에 아주 작게 보이는 파란색 보드가 USB 전원을 이용하여 3.7V 배터리를 충전할 수 있는 충전 보드 입니다.
이렇게 하면 완전한 하나의 제품에 해당되는 부품을 모두 제작한 셈이 되겠네요.
위에서 보았던 두개의 보드는 위 사진처럼 접어서 조립할 계획입니다. 안쪽역시 매우 지저분하네요. 저렇게 만들어진 배선이 과연 잘 붙어있을까 궁금합니다.
두개의 보드 중간에는 절연을 위하여 얇은 실리폰 패드를 하나 넣어줄 계획입니다.
전원이 들어오게 되면 위사진과 같이 불이 켜집니다. 계획에는 LED가 4개였지만 메인 전원이 들어왔는지 여부를 판단하기 위하여 연두색 SMD LED를 하나 추가하였습니다.
코딩 시작!
자 순서가 좀 바뀐것 같지만 이제부터는 지루한 SW 코딩 부분입니다.
SW 구현할때 다음의 것들이 동작해야 하는 것이 중요합니다.
하나의 버튼에 의해 주어진 신호는 4개의 제품에 대한 신호를 보낼 수 있어야 한다.
마지막으로 사용한 제품을 기억했다가 다시 동작할때 세팅해준다.
신호가 한번에 입력이 안될 경우를 대비하여 연속으로 n번 보낼 수 있도록 구성한다.
뭐 별건 없습니다만 두번째에 해당되는 부분은 제가 처음해보는 관계로 저도 공부를 좀 했습죠.
EEPROM 을 사용해보자!
아두이노 나노에 사용되는 Atmega328 칩에는 무려 1kb 라는 비 휘발성 저장공간이 존재합니다. EEPROM 이라고 부르는데요, 프로그램이 저장되는 공간 외에 사용자가 데이터를 저장할 수 있는 공간말입니다.
무려 1kb 요.
-_-;;
1kb 라니.. 장난하냐..
싶겠지만 있는게 어딥니까?
이 저장공간을 이용하여 우리는 마지막 동작했던 제품의 정보를 기억할 건데요. 따지고 보면 차고도 넘치는 양이지요.
자 그럼 EEPROM 에 데이터는 어떻게 저장하고 읽어 들이는지 볼까요?
#include <EEPROM.h>
int sw_02;
int productIdx = 0; //0 : TV / 1: TV BOX / 2: BTV / 3: SPK
bool chkon = false;
//LED
const int L00 = 9; // TV
const int L01 = 10; // TV BOX
const int L02 = 11; // BTV
const int L03 = 12; // LONPOO SPEAKER
void setup()
{
pinMode(s02, INPUT_PULLUP);
productIdx = EEPROM.read(0); // EEPROM 에 저장된 제품의 번호를 불러오는 부분
}
void loop() {
sw_02 = digitalRead(s02);
// change product
if (sw_02 == HIGH) {
chkon = true; // 연속으로 눌리는 것을 방지하기 위하여 on 상태일때만 기기 변경이 되도록 한다.
ledON(productIdx);
}else{
if (chkon)
{
productIdx++;
if (productIdx > 3) productIdx = 0;
EEPROM.write(0, productIdx); // 이부분이 EEPROM 에 현재 제품의 번호를 저장하는 부분
chkon = false;
}
ledON(productIdx);
}
void ledON (int idx) // 선택된 인덱스에 맞게 LED 를 켜주는 함수
{
digitalWrite(L00, LOW);
digitalWrite(L01, LOW);
digitalWrite(L02, LOW);
digitalWrite(L03, LOW);
switch (idx) {
case 0:
digitalWrite(L00, HIGH);
break;
case 1:
digitalWrite(L01, HIGH);
break;
case 2:
digitalWrite(L02, HIGH);
break;
case 3:
digitalWrite(L03, HIGH);
break;
default:
break;
}
}
어렵지 않죠? 제일 위에 해당 라이브러리를 불러들여 주고요,
Setup 시에 저장되어 있는 값을 불러옵니다.
버튼은 눌렀을때 LOW 가 되도록 하고 항상 HIGH 한 상태를 만들어 줍니다. HIGH 한 상태가 되면 chkon 이 true 가되고 버튼이 눌렸을때 기기의 인덱스를 변경한 뒤 EEPROM 에 값을 저장하고 chkon 을 false 로 만들어 다음 신호를 기다리게 됩니다. 이렇게 하면 버튼을 연속으로 누르고 있어도 신호는 한번만 가게 되겠죠. 마지막으로 사용한 제품도 저장이 됩니다.
버튼 하나로 4개의 제품은 어떻게 제어를 할까요?
//setup, 변수 선언 등은 생략합니다.
void loop() {
if (sw_03 == HIGH) { //s03 은 전원 버튼입니다.
}else{
digitalWrite(13, HIGH);
press_S03(productIdx);
}
// 생략
}
void press_S03 (int idx) // power
{
switch (idx) {
case 0:
for (int i = 0; i < 1; i++) irsend.sendNEC(0x2DF10EF,32); // tv power
break;
case 1:
for (int i = 0; i < 1; i++) irsend.sendNEC(0x807F02FD,32); // tv box power
break;
case 2:
for (int i = 0; i < 1; i++) irsend.sendNEC(0x1FE807F,32); // Btv power1
break;
case 3:
for (int i = 0; i < 1; i++) irsend.sendNEC(0x40BF807F,32);
break;
default:
// statements
break;
}
}
바로 Switch 문을 이용하여 입력되는 idx 에 따라 각각 다른 신호를 보내주는 것이지요.
아래 press_S03 버튼이 바로 전원을 켜고 끄는기능을 담당하는 함수가 되는 것이죠.
저런 식으로 버튼 개수만큼 함수를 만들어 주면 됩니다. 좀더 효율적으로 코딩할수도 있겠지만 초보인 저에게는 저렇게 쉽게 코딩하는게 나중에 뜯어 고치지도 좋을 것 같아 이란 저런 방식으로 작성을 하였습니다.
포스트가 점점 길어지네요..
자 이제 전체 소스 갑니다.
중요한 내용은 위에 다 설명 드렸고 이전 포스트를 참고하시면서 내용 확인해 보시면 되고요. 꼭 저와 동일하게 만들 필요는 없으니 참고만 해주시면 됩니다.
길이가 대단하니 조심하세요~ ㅋ
#include <boarddefs.h>
#include <IRremote.h>
#include <IRremoteInt.h>
#include <ir_Lego_PF_BitStreamEncoder.h>
#include <EEPROM.h>
/*
* IRremote: IRsendDemo - demonstrates sending IR codes with IRsend
* An IR LED must be connected to Arduino PWM pin 3.
* Version 0.1 July, 2009
* Copyright 2009 Ken Shirriff
* http://arcfn.com
*/
/*
* tv (NEC)
* power : 0x2DF10EF, 32
* input : 0x2DFD02F, 32
* chnnel up : 0x2DF00FF, 32
* chnnel dn : 0x2DF807F, 32
* volumn up : 0x2DF40BF, 32
* volumn dn : 0x2DFC03F, 32
* OK : 0x2DF22DD, 32
* sw up : 0x2DF02FD, 32
* sw dn : 0x2DF827D, 32
* sw left : 0x2DFE01F, 32
* sw right : 0x2DF609F, 32
*
* android tv (NEC)
* power : 0x807F02FD, 32
* sw up : 0x807F6897, 32
* sw dn : 0x807F58A7, 32
* sw left : 0x807F8A75, 32
* sw right : 0x807F0AF5, 32
* sw OK : 0x807FC837, 32
* back : 0x807F9867, 32
* volumn up : 0x807F18E7, 32
* volumn dn : 0x807F08F7, 32
*
*
* lonpoo speaker
* power : 0x40BF807F, 32
* volumn up : 0x40BF50AF, 32
* volumn dn : 0x40BFD02F, 32
* bt : 0x40BFA05F, 32
* bt esc : 0x40BF906F, 32
* opt : 0x40BF20DF, 32
*
*
* LED
* tv : 9
* tv box : 10
* btv : 11
* spk : 12
*
* input
* s01
*
*
*
*
*/
#include <IRremote.h>
const int s01 = 0;
const int s02 = 18; // select sw
const int s03 = 1;
const int s04 = 2;
const int s05 = 19;
const int s06 = 4;
const int s07 = 5;
const int s08 = 6;
const int s09 = 7;
const int s10 = 8;
const int s11 = 14;
const int s12 = 15;
const int s13 = 16;
const int s14 = 17;
//LED
const int L00 = 9; // TV
const int L01 = 10; // TV BOX
const int L02 = 11; // BTV
const int L03 = 12; // LONPOO SPEAKER
//
// define sw value
int sw_01;
int sw_02;
int sw_03;
int sw_04;
int sw_05;
int sw_06;
int sw_07;
int sw_08;
int sw_09;
int sw_10;
int sw_11;
int sw_12;
int sw_13;
int sw_14;
//
int productIdx = 0; //0 : TV / 1: TV BOX / 2: BTV / 3: SPK
int addr = 0;
bool chkon = false;
IRsend irsend;
void setup()
{
pinMode(s01, INPUT_PULLUP);
pinMode(s02, INPUT_PULLUP);
pinMode(s03, INPUT_PULLUP);
pinMode(s04, INPUT_PULLUP);
pinMode(s05, INPUT_PULLUP);
pinMode(s06, INPUT_PULLUP);
pinMode(s07, INPUT_PULLUP);
pinMode(s08, INPUT_PULLUP);
pinMode(s09, INPUT_PULLUP);
pinMode(s10, INPUT_PULLUP);
pinMode(s11, INPUT_PULLUP);
pinMode(s12, INPUT_PULLUP);
pinMode(s13, INPUT_PULLUP);
pinMode(s14, INPUT_PULLUP);
pinMode(L00, OUTPUT);
pinMode(L01, OUTPUT);
pinMode(L02, OUTPUT);
pinMode(L03, OUTPUT);
pinMode(13, OUTPUT);
productIdx = EEPROM.read(0);
}
void loop() {
digitalWrite(13, LOW);
sw_01 = digitalRead(s01);
sw_02 = digitalRead(s02);
sw_03 = digitalRead(s03);
sw_04 = digitalRead(s04);
sw_05 = digitalRead(s05);
sw_06 = digitalRead(s06);
sw_07 = digitalRead(s07);
sw_08 = digitalRead(s08);
sw_09 = digitalRead(s09);
sw_10 = digitalRead(s10);
sw_11 = digitalRead(s11);
sw_12 = digitalRead(s12);
sw_13 = digitalRead(s13);
sw_14 = digitalRead(s14);
// change product
if (sw_02 == HIGH) {
chkon = true;
ledON(productIdx);
}else{
if (chkon)
{
productIdx++;
if (productIdx > 3) productIdx = 0;
EEPROM.write(0, productIdx);
chkon = false;
}
ledON(productIdx);
}
if (sw_01 == HIGH) {
}else{
digitalWrite(13, HIGH);
press_S01(productIdx);
}
if (sw_03 == HIGH) {
}else{
digitalWrite(13, HIGH);
press_S03(productIdx);
}
if (sw_04 == HIGH) {
}else{
digitalWrite(13, HIGH);
press_S04(productIdx);
}
if (sw_05 == HIGH) {
}else{
digitalWrite(13, HIGH);
press_S05(productIdx);
}
if (sw_06 == HIGH) {
}else{
digitalWrite(13, HIGH);
press_S06(productIdx);
}
if (sw_07 == HIGH) {
}else{
digitalWrite(13, HIGH);
press_S07(productIdx);
}
if (sw_08 == HIGH) {
}else{
digitalWrite(13, HIGH);
press_S08(productIdx);
}
if (sw_09 == HIGH) {
}else{
digitalWrite(13, HIGH);
press_S09(productIdx);
}
if (sw_10 == HIGH) {
}else{
digitalWrite(13, HIGH);
press_S10(productIdx);
}
if (sw_11 == HIGH) {
}else{
digitalWrite(13, HIGH);
press_S11(productIdx);
}
if (sw_12 == HIGH) {
}else{
digitalWrite(13, HIGH);
press_S12(productIdx);
}
if (sw_13 == HIGH) {
}else{
digitalWrite(13, HIGH);
press_S13(productIdx);
}
if (sw_14 == HIGH) {
}else{
digitalWrite(13, HIGH);
press_S14(productIdx);
}
delay(75); //5 second delay between each signal burst
}
void press_S14 (int idx) // vol -
{
switch (idx) {
case 0:
for (int i = 0; i < 1; i++) irsend.sendNEC(0x2DFC03F,32); // tv vol -
break;
case 1:
for (int i = 0; i < 1; i++) irsend.sendNEC(0x807F08F7,32); // tv box vol -
break;
case 2:
for (int i = 0; i < 1; i++) irsend.sendNEC(0x1FE42BD,32); // BTV box vol -
break;
case 3:
for (int i = 0; i < 1; i++) irsend.sendNEC(0x40BFD02F,32); // spk vol -
break;
default:
// statements
break;
}
}
void press_S13 (int idx) // chnl -
{
switch (idx) {
case 0:
for (int i = 0; i < 1; i++) irsend.sendNEC(0x2DF807F,32); // tv chnl -
break;
case 1:
//for (int i = 0; i < 1; i++) irsend.sendNEC(0x807F18E7,32); // tv box vol +
break;
case 2:
for (int i = 0; i < 1; i++) irsend.sendNEC(0x1FE827D,32); // Btv chnl -
break;
case 3:
//for (int i = 0; i < 1; i++) irsend.sendNEC(0x40BF50AF,32); // spk vol +
break;
default:
// statements
break;
}
}
void press_S12 (int idx) //vol +
{
switch (idx) {
case 0:
for (int i = 0; i < 1; i++) irsend.sendNEC(0x2DF40BF,32); // tv vol +
break;
case 1:
for (int i = 0; i < 1; i++) irsend.sendNEC(0x807F18E7,32); // tv box vol +
break;
case 2:
for (int i = 0; i < 1; i++) irsend.sendNEC(0x1FEC23D,32); // Btv vol +
break;
case 3:
for (int i = 0; i < 1; i++) irsend.sendNEC(0x40BF50AF,32); // spk vol +
break;
default:
// statements
break;
}
}
void press_S11 (int idx) // chnl +
{
switch (idx) {
case 0:
for (int i = 0; i < 1; i++) irsend.sendNEC(0x2DF00FF,32); // tv chnl +
break;
case 1:
//for (int i = 0; i < 1; i++) irsend.sendNEC(0x807F58A7,32); // tv box
break;
case 2:
for (int i = 0; i < 1; i++) irsend.sendNEC(0x1FE02FD,32); // Btv chnl +
break;
case 3:
for (int i = 0; i < 1; i++) irsend.sendNEC(0x40BF906F,32); // spk bt esc
break;
default:
// statements
break;
}
}
void press_S10 (int idx) // down
{
switch (idx) {
case 0:
for (int i = 0; i < 1; i++) irsend.sendNEC(0x2DF827D,32); // tv down
break;
case 1:
for (int i = 0; i < 1; i++) irsend.sendNEC(0x807F58A7,32); // tv box down
break;
case 2:
for (int i = 0; i < 1; i++) irsend.sendNEC(0x1FE52AD,32); // Btv down
break;
case 3:
//for (int i = 0; i < 1; i++) irsend.sendNEC(0x40BF20DF,32); //
break;
default:
// statements
break;
}
}
void press_S09 (int idx) // right
{
switch (idx) {
case 0:
for (int i = 0; i < 1; i++) irsend.sendNEC(0x2DF609F,32); // tv right
break;
case 1:
for (int i = 0; i < 1; i++) irsend.sendNEC(0x807F0AF5,32); // tv box right
break;
case 2:
for (int i = 0; i < 1; i++) irsend.sendNEC(0x1FE32CD,32); // Btv right
break;
case 3:
//for (int i = 0; i < 1; i++) irsend.sendNEC(0x40BF20DF,32); // tv box up
break;
default:
// statements
break;
}
}
void press_S08 (int idx) // ok
{
switch (idx) {
case 0:
for (int i = 0; i < 1; i++) irsend.sendNEC(0x2DF22DD,32); // tv ok
break;
case 1:
for (int i = 0; i < 1; i++) irsend.sendNEC(0x807FC837,32); // tv box ok
break;
case 2:
for (int i = 0; i < 1; i++) irsend.sendNEC(0x1FE629D,32); // Btv ok
break;
case 3:
//for (int i = 0; i < 1; i++) irsend.sendNEC(0x40BF20DF,32); // tv box up
break;
default:
// statements
break;
}
}
void press_S07 (int idx) // arrow left
{
switch (idx) {
case 0:
for (int i = 0; i < 1; i++) irsend.sendNEC(0x2DFE01F,32); // tv left
break;
case 1:
for (int i = 0; i < 1; i++) irsend.sendNEC(0x807F8A75,32); // tv box left
break;
case 2:
for (int i = 0; i < 1; i++) irsend.sendNEC(0x1FED22D,32); // Btv left
break;
case 3:
//for (int i = 0; i < 1; i++) irsend.sendNEC(0x40BF20DF,32); // tv box up
break;
default:
// statements
break;
}
}
void press_S06 (int idx)
{
switch (idx) {
case 0:
for (int i = 0; i < 1; i++) irsend.sendNEC(0x2DF02FD,32); // tv back
break;
case 1:
//for (int i = 0; i < 1; i++) irsend.sendNEC(0x807F9867,32); // tv box back
break;
case 2:
for (int i = 0; i < 1; i++) irsend.sendNEC(0x1FE728D,32); // Btv back
break;
case 3:
for (int i = 0; i < 1; i++) irsend.sendNEC(0x40BF20DF,32); // spk OPT
break;
default:
// statements
break;
}
}
void press_S05 (int idx) // arrow up
{
switch (idx) {
case 0:
for (int i = 0; i < 1; i++) irsend.sendNEC(0x2DF02FD,32); // tv up
break;
case 1:
for (int i = 0; i < 1; i++) irsend.sendNEC(0x807F6897,32); // tv box up
break;
case 2:
for (int i = 0; i < 1; i++) irsend.sendNEC(0x1FE926D,32); // Btv up
break;
case 3:
//irsend.sendNEC(0x40BFA05F,32);
break;
default:
// statements
break;
}
}
void press_S04 (int idx)
{
switch (idx) {
case 0:
for (int i = 0; i < 1; i++) irsend.sendNEC(0x2DFD02F,32); //외부입력
break;
case 1:
//irsend.sendNEC(0x2DF10EF,32);
break;
case 2:
//irsend.sendNEC(0x2DF10EF,32);
break;
case 3:
for (int i = 0; i < 1; i++) irsend.sendNEC(0x40BFA05F,32); // blue tooth
break;
default:
// statements
break;
}
}
void press_S03 (int idx) // power
{
switch (idx) {
case 0:
for (int i = 0; i < 1; i++) irsend.sendNEC(0x2DF10EF,32); // tv power
break;
case 1:
for (int i = 0; i < 1; i++) irsend.sendNEC(0x807F02FD,32); // tv box power
break;
case 2:
for (int i = 0; i < 1; i++) irsend.sendNEC(0x1FE807F,32); // Btv power1
break;
case 3:
for (int i = 0; i < 1; i++) irsend.sendNEC(0x40BF807F,32);
break;
default:
// statements
break;
}
}
void press_S01 (int idx) // S01
{
switch (idx) {
case 0:
for (int i = 0; i < 1; i++) irsend.sendNEC(0x2DF10EF,32);
break;
case 1:
for (int i = 0; i < 1; i++) irsend.sendNEC(0x807F02FD,32);
break;
case 2:
for (int i = 0; i < 1; i++) irsend.sendNEC(0x1FE807F,32);
break;
case 3:
for (int i = 0; i < 1; i++) irsend.sendNEC(0x40BF807F,32);
break;
default:
// statements
break;
}
}
void ledON (int idx)
{
digitalWrite(L00, LOW);
digitalWrite(L01, LOW);
digitalWrite(L02, LOW);
digitalWrite(L03, LOW);
switch (idx) {
case 0:
digitalWrite(L00, HIGH);
break;
case 1:
digitalWrite(L01, HIGH);
break;
case 2:
digitalWrite(L02, HIGH);
break;
case 3:
digitalWrite(L03, HIGH);
break;
default:
// statements
break;
}
}
자 이렇게 해서 프로그램까지 마쳤습니다.
원래 3편에 걸쳐 포스팅을 완료하려고 하였으나 너무 길어져서 최종 케이스제작과 동작 테스트는 다음 포스트로 미루도록 하겠습니다. 죄송합니다.
또 얼마가 걸릴지 모르겠네요.
어쨌든 핵심이 되는 내용은 이번 편까지인것 같고 하드웨어 구성은 여러분께서 마음껏 하셔도 될 것 같습니다. 기능도 변경해보시고요, 좀 고민해보면 더 많은 기능을 구성하는 것도 가능할 수도 있습니다.
이번 프로젝트로 무려 4개의 리모컨을 1개로 통합하는 작업을 해보았는데요, 사실 제가 생각했던것과 아주 유사한 제품이 판매되는 제품도 있습니다. 가격이 아주 비싼것도 아니고요. 다만 이런것을 직접 해봄으로 해서 우리는 또 많은 것을 배울 수 있지 않겠습니까?
저는 이번 프로젝트에서 EEPROM 과 IR 신호를 제어하는 것을 배울 수 있는 좋은 기회였던 것 같습니다.
저는 회사에서 디자인팀에 있고 전공도 디자인과 출신이며 심지어는 고등학교도 예체능계열 고등학교를 나왔습니다만 지금 회사에서 하는일의 95% 정도는 개발을 하고 있습니다. 실제로 제가 포토샵, 일러스트 및 기타 디자인툴을 다루는 시간을 다 합쳐도 비주얼 스튜디오를 사용하는 시간의 1/10 도 안될거에요.
요즘은 대부분의 개발을 c# 으로 진행하고 있습니다. 포토샵 스크립트를 이용해서 이미지 컨트롤은 할 수 있겠지만 좀더 복잡하고 다양한, 그리고 시스템 차원에서 뭔가를 하기에는 부족한 부분이 있습니다. 하지만 포토샵에는 레이어 컨트롤이나 layer effect 와 같은 놀라운 기능들이 있기 때문에 버리기는 아까운 부분이 있습니다.
그래서 저는 C# 을 이용하여 개발을 하되 포토샵을 이미지 자동화 편집 툴로 사용할때가 종종 있습니다. javascript 로 개발된 이미지 편집용 스크립트를 C# application 에서 포토샵으로 전달하여 자동화를 하는 것이지요.
??
이게 가능하냐고요?
예전에 제가 엑셀 비주얼 베이직을 이용하여 엑셀과 포토샵이 연동되는 것을 소개해 드린적이 있는데요, 개념적으로는 크게 다르지 않습니다.
개념적으로는 윈도우의 COM 오브젝트를 이용하는 것과 동일한데요. 연결해주는 방법에 약간 차이가 있고 C# 에서 포토샵 스크립트 작성이 쉽지 않으니 개발은 Extend Script Tool kit 으로 하고 실행만 C#이 하는 역할을 하는 겁니다.
이렇게 되면 C#이 다양한 파일 처리, 관리를 하는 동안 이미지 편집이 필요한 순간에 Photoshop 을 호출하여 이미지를 열고 자동화 편집 스크립트를 통해 이미지 편집을 진행하고 그 다음 나머지 파일 처리를 하는 방식 인 겁니다.
C# 에서 COM 오브젝트 선언하는 방법
Type ptsApp = Type.GetTypeFromProgID("Photoshop.Application");
dynamic psApp = Activator.CreateInstance(ptsApp);
psApp.doJavaScript("alert(\"ds\")"); // 여기에 수행되어야 할 포토샵 스크립트를 문자열로 작성하여 전달
dynamic 개체를 이용하여 선언이 가능하며 .NET4.0 이상의 플랫폼에서 지원합니다.
이렇게 했을 때 빌드를 하게 되면 아래와 같은 오류가 나타나는 경우가 있는데요.
Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create' 멤버가 필요한 컴파일러가 없습니다
우측의 솔루션 탐색기 '참조' 부문에서 마우스 우클릭 후 '참조 추가' 로 참조할 개체를 추가해 주어야 하는데요.
Microsoft.CSharp
을 추가해주시면 정상적으로 빌드가 진행이 됩니다.
막상 포토샵 오브젝트를 추가해 주었지만 실제 실행될 때 C# 에서 포토샵 코드를 작성하는 것이 여간 귀찮은 일이 아닙니다. 그런 경우 미리 javascript 로 필요한스크립트를 작성하여 준뒤 아래와 같이 실행시킬 수 있습니다. 이렇게 하면 실행시 미리 변수를 전달 할 수 있기 때문에 상당히 유연한 개발을 진행할 수 있게 됩니다.
변수는 위에 보시는것 과 같이 dynamic 배열 개체를 생성한뒤 필요한 값을 입력해주고 스크립트와 함께 전달하는 방식을 사용하면 됩니다.
포토샵 스크립트 실행 완료 후 반환(리턴) 값이 필요한 경우라면?
무언가 일을 시켰다면 피드백이 있어야 하겠지요? 만들어진 개체의 파일 명이든 무엇이든 간에 어떤 피드백을 받아야 하는 경우 아래와 같이 작성합니다.
Javascript (포토샵에서 실행되어야 할 스크립트)
var value1 = arguments[0]; // C# 으로 부터 전달 받는 인자
var value2 = arguments[1]; // C# 으로 부터 전달 받는 인자
main(value1, value2)
function main (val1, val2)
{
string myString = "";
// 실제 필요한 계산, 동작을 작성한다.
myString = val1 + "," + val2 ;
return myString; // 이 값이 C# 으로 반환된다.
}
이렇게 해주면 myResult 라는 문자열 변수에 입력한 변수들을 합친 문자열이 반환이 되는것이죠.
어떠신가요? 어렵지 않죠?
이렇게 하면 C# 으로 빠르고 편리한 이미지 관리 프로그램을 만들어 포토샵으로 강력한 이미지 편집 기능을 함께 이용할 수 있는 기능을 개발 할 수 있습니다. 물론 ImageMagick 과 같은 강력한 이미지 편집 프로그램이 있긴 하지만 이미 만들어져 있는 PSD 파일 등의 레이어속성을 조회하거나 변경하고, 특정 레이어들을 이용하여 어떤 작업을 해야 한다면 ImageMagick 으로는 한계가 있습니다.
직접 한번 코딩을 해보시면서 테스트 해보시길 바랍니다.
궁금하신 부분은 뎃글로 남겨 주시면 답변 드릴 수 있도록 하겠습니다.
이만 포스팅을 마칩니다.
뎃글,공감은 블로그 작성자에게 큰 힘이 된답니다. 도움이 되었다 생각되시면클릭!! 부탁드려요~
여러분들 집에는 작은 텃밭 하나씩 있으신가요? 저는 없습니다만 처갓집에는 작은 비닐 하우스와 텃밭이 있습니다. 요즘처럼 날씨가 가물거나 하우스 안에 텃밭을 가지고 계신 분들은 매일매일 물주기에 바쁘시죠? 물조리에 물을 담아 뿌리는것도 생각보다 큰 일입니다. 물 한통을 부어 봐야 토양의 표면만 살짝 촉촉해 질 뿐 뿌리있는 곳까지 흠뻑 주려면 몇번, 또는 몇 수십번은 왔다갔다 해야 하는 귀찮은 일이지요.
장인어른의 일손을 좀 덜어보고자 넓은 텃밭에 물을 손쉽게 주는 방법이 있을까 하여 찾아본 결과 적당한 아이템을 발견하였습니다.
알리익스프레스에서 2만원도 안되는 금액에 재료를 구입할 수 있었습니다.
총 25m 의 호스와 수도 연결부, 50 개의 T 형 연결구, 50개의 샤워 토출구가 세트로 준비되어 있습니다.
먼저 물이 나오는걸 테스트 해보았습니다. 음.. 소개페이지에 나온 것처럼 예쁘게 잘 나오는 군요.
이제 설치만 하면 됩니다. 기존 수도 역시 사용해야 하기 때문에 T 형으로 나온 분배기를 하나 구입하였으며 한쪽은 벨브가 달린 타입을 구입하였습니다. 벨브가 달린 쪽에 텃밭에 물주는 호스를 연결하게 되면 벨브만 열어주면 텃밭에 물이 공급이 될 겁니다.
조립하고 설치하는건 간단 합니다.
먼저 수도에 연결하기 위하여 사진에 보이는 것처럼 연결해주고, 사진의 우측 하단에 보이는 T 형 벨브타입 소켓을 연결해 줍니다.
기존의 수도꼭지를 떼어내고 T 벨브를 연결한뒤 다시 수고꼭지를 연결해 줍니다.
벨브를 열면 물이 공급이 되겠지요.
원하는 길이만큼 먼저 호스를 길게 자리잡아 봅니다.
그다음 일정한 간격으로 호스를 잘라 구입한 부품중에 T 자 연결부품의 양쪽을 끼워주고 나머지 한쪽에는 물 공급용 마개를 달아주는 거죠.
쭈그리고 앉아 30분 정도 작업을 했을까요?
일단 완성이 되었습니다. 맨 마지막 부분도 역시 마개를 달아주었습니다.
마개는 빨간 부분을 돌려서 물이 나오는 양을 조절할 수 있게 되어있습니다. 한쪽으로 돌려 열어주면 나오는 물의 양이 많아 지고 다른 쪽으로 돌리면 물이 아얘 잠기는 방식입니다.
음... 그런데
첫번째 토출구만 이렇게 예쁘게 나오는군요. 나머지는 쫄쫄쫄입니다.
제일 끝부분은 거의 나오지도 않는군요.
실제 틀어 보니 물이 처음 한개로 테스트할 때와는 사뭇 다른 형태로 분사가 되었습니다. 거의 분사라고 보기 어려울 정도로 쫄쫄쫄 흘러나오네요.
아마 호스 자체가 너무 얇아 흐를 수 있는 물의 양에 한계가 있고 수압 역시 한계가 있어서 인 듯 합니다.
만약 아주 길게 연결을 해야 한다면 좀더 두꺼운 고무 호스를 연결하고 호스 중간중간 구멍을 뚫어 샤워형 토출구를 직결하면 모든 꼭지에 물이 샤워형태로 분사가 될 것 같습니다.
이번에 작업한 텃밭은 크기도 크지 않거니와 어짜피 물이 바닥에 스며들고 또 스며들면 점차 전체적으로 땅이 젖을 것 이므로 전체적으로 동일한 양이 나올 수 있게 마개를 돌려 수량을 조절해 주었습니다. 맨 끝으로 갈수록 많이 열어주고 수도쪽으로 올수록 마개를 닫아 주어야 겠지요.
어쨌든 결과는 만족스럽습니다. 이렇게 하면 이제 더이상 물조리를 들고 화단 이쪽 저쪽을 옮겨 다니며 시간을 버리는 일은 없을 것입니다. 같은 시간에 화단에 잡초도 뽑아주고 관리하는데 더 신경을 쓸 수 있을거라 생각합니다.
어떠신가요? 2만원도 안되는 저렴한 금액으로 많은 시간과 노동을 절약할 수 있는 방법. 한번 도전 해 보실까요?
나중에 시간이 되면 집안에서 화단 물주기 호스의 벨브를 열었다 닫았다 할 수 있도록 리모컨을 만드는 것도 한번 도전해 볼까 합니다. 아니면 토양 수분센서를 이용하여 수분이 마르게 되면 자동으로 급수를 해주는 시스템도 괜찮겠네요. 이렇게 되면 완전 스마트 팜 아니겠습니까?
지식을 쌓는 다는건 우리의 삶이 좀더 윤택해 질 수 있도록 바꾸어 나가는 것이라 잠시 생각해 봅니다.