반응형

오늘은 간단한 VBA 코드를 하나 소개해 드릴까 합니다.

프로그래밍을 하다보면 16진수를 10진수로 바꾸거나 10진수를 16진수로 바꾸어야 하는 경우가 종종 있습니다.

읭? 16 진수?

라고 하실수도 있겠지만 막상 개발을 하다 보면 16진수가 쓰이는 경우를 생각보다 자주 만나게 됩니다. 

대표적을 RGB 컬러 값을 HTML 에서 사용하는 #FFEACB 이런 값으로 변경하는 작업이 있겠습니다. 또는 반대로 0 ~ FF 가지 255개의 숫자가 16진수로 구성되어 있는 값을 0~255인 자연수로 변경을 해야 하는 경우가 있을 수도 있고요. 16진수는 1개의 자릿수에서 0~15 까지의 값을 표현할 수 있기 때문에 10진수에 비하여 간단한 텍스트로 더 큰 숫자를 표현할 수 있다는 장점도 있습니다.

자 쓸데 없는 말이 길었네요.

VBA를 이용해서 숫자의 형을 변환하는 여러가지 방법이 있지만 오늘 소개해드릴 방법은 그중에서도 단연 쉽고 간단한 방법입니다.

바로 worksheet의 함수를 이용하는 방법인데요. 엑셀 상단에 수식을 입력할 수 있는 칸이 있자나요? 여기서 사용되는 함수가 바로 worksheet function 입니다. 

worksheet function 중에 DEC2HEX() 와 HEX2DEC()  가 바로 오늘의 주인공 입니다.

함수 명칭만 봐도 딱 감이 오시죠?

사용하는 방법은 아래와 같습니다.

먼저 위와 같이 엑셀에 16진수 값이 들어있는 셀이 있다고 가정을 하고요.

vba 에디터를 열어 아래와 같이 코드를 작성해봅니다.

Sub changeNumber()

    Dim rngA As Range
    Set rngA = [A1]
    
    rngA.Offset(0, 1).Value = WorksheetFunction.Hex2Dec(rngA.Value)
    rngA.Offset(0, 2).Value = WorksheetFunction.Dec2Hex(rngA.Offset(0, 1).Value)

End Sub

자 A1 셀에 있는 값을 바로 우측 옆 칸에 10진수로 바꾸는 함수를 적용하여 값을 넣어 줍니다.

그다음 B1 에 위에서 자동으로 입력된 값을 다시 16진수로 옆칸에 자동으로 입력해주는 코드 입니다.

실행하면요

요렇게 B1 에는 10진수로, 다시 C1 에는 16진수로 값이 변경된 것을 볼 수 있죠?

worksheetFunction 에는 물론 엑셀에서 사용할 수 있는 모든 함수를 지원해 주니까 vba 작성중에도 가져다가 사용할 만한 함수가 많지만 오늘 포스트의 주제에 맞게 숫자의 형 변환 관점에서만 보자면 아래와 같은 여러 함수들이 지원됩니다.

물론 Hex 로 부터 출발 하는 경우에도 마찬가지 입니다.

문장이 조금 길어 보여서 어렵거나 복잡하게 생각될 수도 있기는한데 막상 사용해보면 생각보다 단순 합니다. 사용하는데 따로 알아두어야 할 규칙 같은것도 없어서 코딩하는 시간도 많이 절약되는 방식이라 할 수 있겠습니다.

 

만약 아래와 같이 셀에 html 색상 값이 들어있는 경우 

vba를 이용하여 해당 색상으로 셀의 배경 색상을 자동으로 칠해 줄 수 있습니다.

Sub changeNumber()

    Dim rngA As Range
    Set rngA = [A1]
    
    Dim clrR As Integer
    Dim clrG As Integer
    Dim clrB As Integer
    
    clrR = WorksheetFunction.Hex2Dec(Mid(rngA.Value, 2, 2))
    clrG = WorksheetFunction.Hex2Dec(Mid(rngA.Value, 4, 2))
    clrB = WorksheetFunction.Hex2Dec(Mid(rngA.Value, 6, 2))
    
    rngA.Interior.Color = RGB(clrR, clrG, clrB)
    

End Sub

간단하죠? 위에서 소개해 드린 worksheetFunction을 이용하여 2자리씩 잘라낸 16진수를 10진수로 담은뒤에 셀의 배경 색상으로 지정하는 것이죠.

글자를 잘라내는 건 2019.08.12 - [DEV/VBA] - [VBA] 문자열 가지고 놀기 참고하세요~

스크립트를 실행하면 어떻게 될까요?

 

요렇게 html 형식의 색상 값으로 셀의 색상이 지정 됩니다.

뭐 한두칸이야 얼마든지 수작업으로도 적용할수 있기는 합니다만 이런 값이 수천개가 있는데 모두 엑셀 시트에서 지정된 색상으로 보여지기를 원한다면 vba 가 반드시 필요하겠죠?

 

그럼 이만 오늘의 포스팅을 마칩니다.

2022.06.16 - [DEV/VBA] - [VBA] 선택한 셀의 이미지 파일 이미지 뷰어로 열기

 

[VBA] 선택한 셀의 이미지 파일 이미지 뷰어로 열기

오늘 소개해 드릴 내용은 엑셀 시트에 이미지 파일명과 경로가 있는 상태에서 선택한 이미지만 뷰어로 바로 확인하는 방법 입니다. 이미지가 수백개 들어있는 엑셀 시트에 이미지를 모두 붙여

diy-dev-design.tistory.com

2020.09.04 - [DEV/VBA] - [vba] 엑셀 이미지 리스트로 일괄 다운로드 받기

2020.06.22 - [DEV/VBA] - [vba] 초등학교 연산 자동 문제집 - 곱셈 추가

2019.08.12 - [DEV/VBA] - [VBA] 문자열 가지고 놀기

 

[VBA] 문자열 가지고 놀기

안녕하세요. 이번 글에서는 VBA 에서 문자열을 가지고 무엇인가를 하는 것을 알아보겠습니다. 프로그래밍을 하다 보면 조건을 가지고 어떤 액션을 해야 하는 결우가 무척 많은데요. 그중에 대표

diy-dev-design.tistory.com

2020.01.30 - [분류 전체보기] - [vba] 셀 속성 조정하기 (넓이, 높이, 숨기기, 테두리 등)

 

[vba] 셀 속성 조정하기 (넓이, 높이, 숨기기, 테두리 등)

이번 포스트에서는 vba 를 이용하여 셀의 속성을 조정하는 방법을 설명 드리겠습니다. 엑셀은 단순한 표의 형식을 취하고 있지만 셀의 간격이나 테두리 등의 속성을 자유롭게 조정할 수 있어 다

diy-dev-design.tistory.com

2019.08.30 - [DEV/VBA] - [VBA]RGB 색상 값이 들어있는 셀에 셀 색상 지정하기

 

반응형
반응형

오늘 막내 후배 직원이 작은 실수를 했다.

길지 않은 시간이었지만 얼마간 함께 일하며 아쉬운 부분도 있고 해서 쓸데없는 잔소리를 좀 했나보다.

물론 회사생활을 한지 얼마 안되는 사회 초년생이고 아직 조직의 일원으로서의 역할보다는 보다 개인의 감정이 앞서는 어린 친구인데, 감춰왔던 작은 몇몇 실수들이 뭉쳐 커다란 문제로 붉어져 나온 상황이어서 선배로써 더이상 지켜보기만 하기는 어려운 상황이었기에 문제를 함께 해결해가며 이야기를 시작했는데... 결국 뭐 듣는 입장에서는 잔소리처럼 들렸으리라.

얼마간 나의 이야기를 듣던 후배 직원은 결국 눈물을 흘렸다.

말이 길어질 수록 눈물도 늘었다.

눈물을 철철 흘리는 후배를 보며 마음이 좋지 않았다.

 

 

 

 

무거운 마음으로 퇴근하며 생각해보니 우리의 삶이라는 것은 마치 사기구슬 같다는 생각이 들었다.

 

평생을 들여 만드는, 아주 길고긴 시간을 들여 정성스레 만드는 사기구슬.

 

성장기의 우리들은 스스로 만들어지지 못하는 찰흙덩어리 같은 존재라는 생각이 들었다. 오직 부모에 의해 만들어 지는 찰흙 반죽 같은 느낌이랄까? 흙이 어찌 스스로 반죽을 하겠는가. 어린 시절의 우린 그저 부모에 의해 만들어지는 수동적인 존재일 뿐이다.

어떤 부모들은 아주 신경써서 둥글게 둥글게 예쁜 모양으로 반죽을 다듬으며 살을 붙여 나가지만 또 어떤 부모들은 삶의 고된 시간속에 자식이라는 구슬을 만드는데 정성을 다하기 어렵기도 하다. 물론 둥글게 빚는데 전혀 관심도 없는 부모도 있다. 그냥 대충 흙 덩어리를 어딘가에 툭 던져 놓고 마는 그런...

물론 큰 돈을 들여 둥근 반죽 기계를 산 부모는 완벽하게 둥근 구슬을 만들어 낼 것이다. 하지만 기계를 살 돈이 없는 부모도 열심히 주무르고 빚어주면 기계 못지 않은 둥근 찰흙 구슬을 만들 수 있을 것이다. 기계로 만든 것처럼 둥글지는 않겠지만 인간미는 분명 있는 그런 둥근 구슬이 만들어졌겠지.

 

정성들여 만들어진 동그란 찰흙구슬과 대충 얼버무린 모난 흙 덩어리는 이제 성인이 되며 서서히 굳어져 간다. 

동그랗고 예쁘게 잘 만들어진 구슬은 어느곳이든 잘 굴러가고 잘 구를 수록 점점 더 동그래지겠지만 정성스럽게 만들어지지 않은 울퉁불퉁한 구슬은 구르지 못하고 멈추어 있거나 다른 구슬들 사이에서 이상한 구슬로 보이며 그 모양 그대로 점점 더 굳어져 갈 것이다.

하지만 아직 다 마르지 않았기에 모난 흙 덩어리도 열심히 이리로 저리로 굴러다니면 어떻게 될까? 결국 둥글 둥글 해 지겠지? 성인이 된 우리는 스스로를 굴려가며 자신을 둥글게 만들어야 하지만 이게 쉽지 않다. 겉은 이미 말라가고 있기 때문에 갈라지고 지신을 부스러 뜨리며 둥글게 만들어야 된다. 

힘들겠지만 스스로를 굴리고 부딪혀서 모난 부분을 깎아내야 한다. 그래야 둥글게 될테니까.

아직 마르지 않은 모난 구슬이 멈추어 있지 않도록 친구들이, 가족이, 사회가 적극적으로 도와주어야 한다. 그렇지 않으면 그 친구는 영영 동그란 구슬이 될 수 없을 수도 있다. 여기서 더 굳어져 버리면 이제 동그란 구슬이 되는건 사실상 어렵다고 봐야 한다. 물론  둥근 구슬이 예뻐서 모두가 그것이 되어야 한다는 것은 아니다. 그냥 우리 각자의 인생의 성공이라는 목표를 둥근 구슬이라고 하자는 거다.

 

나이가 들며 직장에 와보니 여기는 이게 더이상 구슬을 둥글게 빚는 곳은 아니었다. 아무도 내가 둥근 구슬이 되는 것에 관심은 없었다.

 

 

가마

 

굳은 흙덩어리를 상상도 못할 뜨거운 불꽃으로 강하고 단단하게 만드는 바로 그 가마.

직장은 흙 구슬들에게 가마와 같은 곳이라는 생각이 들었다. 

시련과 고통이 주어지는 곳.

도예공들인 도자기를 연일 만들지만 그렇다고 만들어 지는데로 가마에 들어가는 것이 아니듯이 흙덩어리 구슬이 만들어 졌다고 해서 모두 같은 시기에 가마로 들어가는 것은 아니다. 모두가 취업이나 자신의 일을 찾아 시작하는 시기도 다르고 또 뒤에서 설명하는 것처럼 그 시간이 다 같은 것은 아니다.

 

직장 생활을 하다보면 정말 힘든 시간은 누구에게나 온다.

내가 컨트롤 할 수 없을 것 같은 그런 힘든 시간이 오고 그 시간들이 결국 나를 더욱 단단하게 만들었다는 것을 알게 되고나니 그 시간이 내가 마치 불가마에 들어간 도자기와 같은 시간이었다는 생각이 들었다. 그런 힘든 시간을 견디고 이겨내며 오늘에 왔기 때문이다. 아직 목표한 온도에 오르지 않은 도자기를 급하게 꺼내면 깨지거나 색깔이 안나오거나 결국 버려질 폐기물이 되듯이 우리는 그 힘든 시간을 이겨내며 시나브로 단단한 사기가 된다.

초벌구이 재벌구이를 겪으며 더욱 단단해지고 유약같은 멋진 동료들을 통해 빛나는 영롱한 도자기가 구워진다.

그런 힘든 시간들은 지금까지 열심히 굴러다니며 완전하리만큼 둥글게 만든 나를 진정 빛나는 사기구슬로 다시 태어나게 만드는 시간들이라 생각이 들었다.

 

잘 빚어진 둥글고 예쁜 구슬은 몇차례 가마를 거치며 유약도 발리고 결국 빛나는 사랑받는 구슬이 되어갈 것이다.

 

나는 부모로써 나의 아이들을 예쁘고 아름다운 구슬로 빚고 있는 걸까? 하는 생각을 해본다. 또 걱정도 된다.

나는 평생토록 그 구슬을 계속해서 빚을 수는 없다.

흙은 언젠가는 마를 것이기 때문이고 내 손을 떠난 구슬은 더이상 내 손으로 빚을 수 없기 때문이다.

 

 

 

오늘 눈물을 흘린 그 후배에게 나는 좋은 가마 역할을 해준 것일까 .

아직 완전히 마르지 않은 흙더미를 조금 더 둥글게 다듬은 것일까.

아니면 아직 구워지지 않은  흙 구슬에 괜한 생채기만 남긴 것은 아닐까

 

 

퇴근길

 

반응형
반응형

안녕하세요 오랜만에 3DS MAX SCRIPT 포스팅을 합니다.

제가 올리는 글을 누가 보는가 싶은데.. 간혹 스크립트를 잘 봐주시는 방문객들이 계신듯 하여 포스팅을 하게 되었습니다.

오늘은 3DS MAX SCRIPT의 계층 구조에 대한 이야기 입니다. 

왠만한 개발을 해 보신 분들은 사실 약간 상식적으로 알고 계신 내용일 것 같기는 합니만 그래도 역시 누군가에게는 도움이 될 듯 하여 글을 남겨 봅니다.

 

3DS MAX Scritp 에서 어떤 오브젝트는 개발 관점에서 보면 클래스 입니다. 상위 클래스가 있고 또 하위 클래스가 있는데요. 계층 구조라고 말씀 드린것처럼 클래스 안에 또 작은 클래스가 정의 되어 있고 또 그안에 작은 클래스 또는 구조체가 정의 되어 있다고 보시면 됩니다.

 

클래스라고???

개발이 생소하신 분들은 클래스라고 하면 잘 이해가 안되시죠?

우리 인간을 예를 들어 클래스라는 개념을 설명해 보면 다음과 같습니다. (그냥 예 입니다)

각 단계는 상위 클래스의 하위 클래스가 됩니다.

  • 동물 - 호흡을 한다. 심장이 있다. 
    • 포유류 - 4개의 팔 또는 다리를 가지고 있다. 아기를 낳는다. 젖을 먹인다.
      • 영장류 - 지능이 뛰어나다. 두발 또는 네발로 걸을 수 있다. 시각 능력이 좋다
        • 인간 - 특별히 뇌가 발달했다. 다양한 언어를 구사할 수 있다. 손의 움직임이 뛰어나다
    • 조류 ... 
    • 양서류...
    • 파충류...

뭐 이런식으로 어떤 큰 개념을 정의하고 그 하위에 상위의 개념을 포함하지만 조금 세분화된 개념을 담는 그런 방식으로 한단계 한단계 세분화된 그런 개념을 담고 있는 것이 일반적인 클래스의 개념입니다. (위에 설명한 건 그냥 예를 든 것입니다. 시비 노노)

 

예를 들면 익히 잘 알고 있는 Box 는 

요런 순서의 계층 구조를 가지고 있습니다.

우린 레퍼런스 파일을 통해서 Box() 라는 클래스가 가지고 있는 다양한 프라퍼티에 접근을 할 수 있습니다.

2021.03.24 - [DEV/MAX SCRIPT] - 3DS MAX 스크립트 레퍼런스 (헬프파일) 다운 받기

예를 들면 생성된 Box 개체의 width, Height, length 를 다음과 같이 변경할 수 있습니다.

myBox = box()
myBox.width = 25
myBox.Height = 150
myBox.Length = 50

그럼 아래와 같은 모델이 화면에 자동으로 생성되겠죠.

스크립트로 생성한 box

 스크립트에서 설정한 값이 잘 들어가 있는 것을 알 수 있습니다.

 

GeometryClass

그런데 위에 계층 구조를 보면 Box 위에 GeometryClass 라는 상위 클래스가 보이죠? 레퍼런스에서 해당 위치를 클릭해보면 GeometryClass 라는 녀석에 대한 정보가 나오는데요, 

"The mesh operations underlying the Boolean Compound Object in 3ds Max are accessible in MAXScript." 

라는 글이 해당 페이지의 첫줄에 보이고 

  • <node1> + <node2>
  • <node1>-<node2>
  • <node1>*<node2>

요런 수식들이 소개가 되어있습니다. 내용을 읽어 보면 GeometryClass 는 + - * 와 같은 연산자로 boolean 을 수행할 수 있다는 의미가 되겠습니다. Box 는 GeometryClass 이므로 당연히 Box 와 Box 는 위와 같은 boolean 연산이 된다고 볼 수 있겠습니다.

한번 해볼까요? 

myBoxA = box()
myBoxA.width = 50
myBoxA.Height = 50
myBoxA.Length = 50


myBoxB = box()
myBoxB.width = 150
myBoxB.Height = 25
myBoxB.Length = 25

myBoxB.pos.z = 12.5

요렇게 두개의 박스를 만드는 스크립트를 실행하면 

요런 형태의 두개의 박스가 생성이 됩니다.정육면체가 myBoxA, 길다란 육면체가 myBoxB가 되겠습니다.

자 그럼 myBoxA 에서 myBoxB를 빼 보도록 하겠습니다.

myBoxA = box()
myBoxA.width = 50
myBoxA.Height = 50
myBoxA.Length = 50

myBoxB = box()
myBoxB.width = 150
myBoxB.Height = 25
myBoxB.Length = 25
myBoxB.pos.z = 12.5

myBoxA - myBoxB

delete myBoxB

다른부분은 동일하고 마지막에 myBoxA 에서 myBoxB 를 빼는 수식을 추가한 다음 길다란 녀석을 삭제하는 코드를 추가하였습니다.

결과는요?

네. boolean 같은 복잡한 클래스를 사용하지 않아도 이렇게 쉽게 boolean 동작을 수행하는 군요. 

네 그럼 Box 뿐만 아니라 다른 geometryClass 의 모든 대상이 위와 같이 단순한 연산자를 이용하여 boolean 연산을 할 수 있다는 의미로 보시면 됩니다.

GeometryClass 에는 Box 외에도 Sphere 나 Cylinder, Tube 및 친근한 Teapot 같은 개체도 모두 GeometryClass 이므로 모두 동일하게 위와 같은 연산을 사용할 수 있다는 거죠.

GeometryClass 에는 아래와 같은 명령어도 지원합니다.

설명을 보면 

"Returns an array of two integers - the face count and the vertex count of the TriMesh on top of the modifier stack. "

라고 되어 있는데요, Sphere 를 하나 생성한 뒤 위의 코드를 한번 실행해 볼까요?

mySphere = Sphere()
GetTriMeshFaceCount mySphere

요렇게 작성한 뒤 실행해 보면 

$Sphere:Sphere002 @ [0.000000,0.000000,0.000000]
#(224, 114)

요런 결과가 리턴 됩니다. 첫째 줄은 생성된 sphere 에 대한 정보이고요, 두번째가 바로 작성한 코드의 결과물인데요. 위의 설명에 따르면 face 개수와 vertex 개수를 돌려 준다고 합니다. 생성된 sphere 는 224개의 face 와 114개의 vertex 로 이루어져 있다는 것을 알 수 있습니다. 다시 말하자면 우리는 생성한 geometryclass 가 뭐든간에 해당 개체의 폴리곤수를 바로 알 수 있게 되는 겁니다. mesh 나 poly 같은 개체의 geometry 속성 정보를 알기 위하여 class 구분을 하고 getNumfaces 나 polyop.getNumVertexs 를 사용하는 것처럼 개체의 클래스나 구성 방식 등을 검사를 할 필요가 없는거죠.

 

Node

자 그럼 한칸 더 상위로 올라가서 Node 를 볼까요?

Node 는 3DS MAX Script 에서 아주 중요한 개념으로 해당 클래스에서 사용할 수 있는 프라퍼티나 메소드가 아주 많습니다. 스크립팅의 아주 기본적인 부분도 많이 보이네요.

아주 간단한 예를 들면 우리가 scene 에서 보여지는 모든 개체는 node 라는 클래스라고 보면 됩니다. EditableMesh 건 EditablePoly 건 Camera 건 심지어 helper 나 dummy 같은 개체도 모두 node의 하위 클래스 입니다.

즉 Node 라는 개체에 대한 설명이 적혀있는 레퍼런스 페이지에 있는 모든 하위요소를 사용할 수 있습니다.

 제가 아~~주 자주 사용하는 node 의 대표적인 프라퍼티 또는 메소드를 보면 아래와 같습니다.

  • IsValidNode <var>
  • move <node> <point3> -- mapped
  • scale <node> <point3> -- mapped
  • rotate <node> <eulerangles> -- mapped
  • copy <node> -- mapped
  • reference <node> -- mapped
  • instance <node> -- mapped
  • delete <node> -- mapped
  • classOf <node>

딱 봐도 어떤 일을 하는지 아시겠죠? 우리가 알고있는 대부분의 요소는 node 이며 위와 같은 명령어를 이용하여 컨트롤 할 수 있답니다. 복사하고 이동하고, 회전하고 삭제하고, 해당 개체가 어떤 클래스인지 확인하는 그런 것들이죠.

Node Common Methods

 

라는 항목을 보면 Node 에서 사용할 수 있는 더 많은 상세한 레퍼런스들을 확인할 수 있습니다.

Node 클리스의 상세한 레퍼런스 들

이렇게 말이죠.

물론 Node 자신의 subClass 에는 GeometryClass, Shape, Light, Camera, Helper, SpacewarpObject, System 이러한 요소들이 있고 각기 자신에 해당하는 프라펕와 메소드들을 다룰 수 있지만 역시 node 에서 정의하고 있는 모든 프라퍼티와 메소드를 사용할 수 있는 것 입니다.

 

드디어 최상위 클래스 : Value

그럼 최상위 Value 라는 개체로 가 볼까요?

Value 개체는 3DS MAX 에서 정의하고 있는 class 의 최상위 개체입니다. 말하자면 모든 구조체, 클래스 등은 Value 에 속하므로 Value 에서 정의하고 있는 기능을 수행할 수 있는 것이라 할 수 있겠습니다.

최상위인 value 에는 대표적으로 다음과 같은 항목들이 정의되어 있습니다.

  • <value> == <value>
  • <value> != <value>

익히 잘 알고 있는 bool 연산자죠? if 구분 같은 곳에서 대상과 대상이 같은지 다른지 판단할 수 있고요. 

  • print <value>
  • format <format_string>

와 같은 코드를 통해서 해당 정보를 출력해 볼 수 도 있습니다. 

Working with Values

라는 레퍼런스 페이지에 보면 스크립팅을 하는 동안 Value 를 어떻게 다룰 수 있는지 잘 소개가 되어있습니다.

스크립트는 다른 개발 언어와 다르게 빠르게 작성하고 빠르게 컴파일, 실행을 할 수 있기 때문에 이런 구조체를 알면 디버깅도 빠르고 작성도 손쉽게 할 수 있겠습니다.

Value 에는 node 와 같은 scene 내의 개체 뿐만 아니라 Float ,Integer ,String ,BitArray ,Point3 ,Ray ,Quat ,AngAxis ,EulerAngles ,Matrix3 ,Point2 ,Color ,Arrays 와 같은 클래스나 구조체들도 포함되어 있습니다. Ray 에 대해서 지난 포스팅에 소개해 드렸었죠?

2022.05.31 - [DEV/MAX SCRIPT] - [3ds max script] ray 에 대하여 알아보기

 

[3ds max script] ray 에 대하여 알아보기

가끔 아니 어쩌면 자주 3D 공간안에서 물체의 위치를 지정할때 이 물체가 저쪽에 닿는 면에 딱 위치하고 싶은데... 할때가 있죠? 예를 들면 나무나 돌덩이 이런걸 랜덤하게 막 생성한 다음 굴곡진

diy-dev-design.tistory.com

사실 Value 는 정말 아~~주 아주 기본적인 속성들을 다루고 있다고 보시면 되고 해당 정보들 없이는 스크립팅이 불가능한 정말 개념이 없어도 늘 사용하는 그런 애들이라고 보시면 됩니다.

 

 

마치며

자 오늘 포스팅은 사실 개념적인 부분이어서 조금 어려울 수도 있을 것 같습니다.

하지만 스크립팅을 하다보면 저처럼 자연스럽게 알게 되기도 하고요. 또 이런 개념을 이해하고 스크립팅을 하면 훨씬 쉽고 유연하게 코딩을 하실 수 있을 거에요. 

저 처럼 무작정 코딩을 하며 배워가는 경우에도 "아~~~ 이게 이런거였어?!?!?" 이런 적이 셀 수 없이 많았거든요. 

 

자 다시 정리하자면 

  • 상위 클래스에서 정의되어 있는 프라퍼티나 메소드는 하위 클래스에서도 접근 할 수 있다.
  • 즉 name, rotate, move, delete, copy 같은 개념은 하위의 어떤 클래스에서도 사용할 수 있다.
  • 지금 보고 있는 개체의 상위 클래스가 무었인지 확인해 보자.

 

자 다들 화이팅하시고 스크립팅을 통해 에이스의 진면모를 보여주시기를 바라며 오늘의 포스팅을 마칩니다.

 

2021.02.26 - [DEV/MAX SCRIPT] - [3ds max script] vector 에 대하여

 

[3ds max script] vector 에 대하여

안녕하세요. 오늘은 디자이너라면 조금 생소할 수 있는 vector 라는 개념에 대하여 소개해 드릴까 합니다. 혹시 작년 쯤인가요? 슈퍼밴드라는 밴드 오디션 프로그램에서 F=ma 라는 곡으로 아주 인

diy-dev-design.tistory.com

2021.02.24 - [DEV/MAX SCRIPT] - 3da max script 점과 점 사이의 거리 구하기

 

3da max script 점과 점 사이의 거리 구하기

간단하게 포스팅을 남기려고 합니다. 제목 그대로 버텍스 간 거리 구하기 입니다. 아니 정확히는 두개의 point3 사이의 거리를 구하는 방법을 소개해 드릴까 합니다. 그리고 용용 편으로 두 점사

diy-dev-design.tistory.com

 

반응형
반응형

일러스트로 디자인을 하다 보면 뒤에 사진이나 이미지를 앉혀야 하는 경우가 자주 있는데 보통 한번에 컨펌이 나지 않기 때문에 place 시킬때 link 형식으로 앉히게 됩니다.

배경 이미지의 시안이 변경 될 경우 이미지만 변경하면 자동으로 일러스트에서도 변경이 되기 때문이기도 하고 또 작업하는 파일의 용량이 작아 HDD 디스크에 부담도 적기 때문이지요.

그런데!!

 

이걸 이제 인쇄를 넘기려면 링크된 모든 파일을 다 찾는 것도 일이고.. 보냈을때 하나라도 누락이 되었다면 사고가 나는것이죠. 

그래서 embed 형식으로 변경하면 ai 파일 내에 이미지가 포함이 되서 이런 문제가 일어나지 않는데요.

place 된 이미지가 한 두개면 괜찮은데 그 숫자가 좀 많을 때는 여간 귀찮고 번거로운 일이 아닐 수 없습니다.

이렇게나 많다면 말이죠.

link 형식으로 잔뜪 place 된 이미지들

 

또 파일을 보낸 뒤에 누락된 개체는 없는지 확인할 동안 기다려야 되는 문제도 있겠네요.

 

이럴때 스크립트가 출동한다면!!!

스!

크!

립!

트!

 

네. 바로 스크립트라면 간단히 해결을 할 수 있습니다.

바로 스크립트를 볼까요?

var cDoc = app.activeDocument
var pItems = cDoc.pageItems

for (var k = 0; k < pItems.length; k++)
{
	var cItem = pItems[k] 
	if (cItem.typename == "PlacedItem")
	{
		cItem.embed()	
	}
}

 

엥?

엄청 짧죠?

 

네 그런데 이게 됩니다.

위의 내용을 텍스트 에디터에 붙여 넣고 jsx 형식으로 저장합니다.

jsx 형식으로 저장된 스크립트

잘 모르시겠으면 그냥 .txt 파일로 저장한 뒤에 확장자만 jsx 로 바꾸어 주어도 됩니다.

그런 다음 일러스트에서 이 스크립트를 실행 시키면 되는데요.

간단합니다. 두가지 방법이 있는데

 

1. 그냥 스크립트 파일을 일러스트 창 안으로 drag and drop 한다

 

2. File - Script - Other Script --> 만들어 두었던 스크립트 파일 선택

 

간단하죠? 

요렇게 하면 place 된 개체가 어느정도이냐에 따라 다르지만 많아야 많아야 1~2분이면 될거에요.

 

결과를 볼까요 ? Ctrl + Y 를 해서 보면 link 형식으로 place 된 개체는 X 표시가 되는 것을 알수 있는데요.

상당히 많은 link 형식의 placeItem 이 있는 화면

 

요렇게요.

그럼 스크립트를 실행한 ai 파일을 Ctrl + Y 해서 볼까요?

모든 placeItem 이 embed 형식으로 변경된 화면

네. 단하나의 누락 없이 모두 embed 로 변경이 되었습니다.

 

캬!

역시 배워야 합니다.

오늘은 이만 마치도록 하겠습니다.

다시한번 말씀 드리지만 위에 제가 소개해드린 스크립트를 메모장 등으로 옮기신 후 jsx 파일로 저장, 일러스트에서 실행하시면 됩니다.

 

감사합니다.

2020.12.03 - [DEV/Adobe Script] - [illustrator script] 일러스트도 스크립트가 되나요?

 

[illustrator script] 일러스트도 스크립트가 되나요?

네! 당연히 됩니다. 일러스트레이터에서 스크립트를 사용할 수 있게 되면 진정 놀라운 결과들을 만들어 낼 수 있습니다. 사람이 손으로 하기에는 정말 귀찮은 작업들을 가능하게 만들어 줍니다.

diy-dev-design.tistory.com

 

2019.12.13 - [DEV/Adobe Script] - [포토샵스크립트] PSD 모든 레이어 자동 저장, 하위 폴더 포함 (레이어 크기로 이미지 저장)

 

[포토샵스크립트] PSD 모든 레이어 자동 저장, 하위 폴더 포함 (레이어 크기로 이미지 저장)

안녕하세요. 제가 포토샵 스크립트를 이용하여 PSD 파일내의 모든 레이어를 자동으로 하위폴더 구조를 유지하며 저장할 수 있는 스크립트를 만들어서 올렸었는데요. 바로 아래 링크에서요. https:

diy-dev-design.tistory.com

 

반응형
반응형

유튜브 영상 또는 음악을 mp3 파일로 다운로드 받을 수 있는 것을 아시나요? 

인터넷에 보면 그런 기능을 지원해주는 사이트들이 있습니다.

저도 유튜브에서 주로 음악을 듣는데 데이터 사용량이 많은 것 같아 다운로드를 한번 받아 보았습니다. 그런데 이게 특성상 전체음악이 하나의 파일로 다운로드 되어 다음곡이나 이전곡으로 넘어가지지를 않더라고요.

그래서 사운드 편집 프로그램인 goldwave 를 이용해서 한곡  한곡 웨이브 파형을 보면서 자르다가 너무나 번거롭고 귀찮아서 포기 했었는데요.

 

음.. 

생각해보면 음원의 중간중간 비어있는 공백을 찾아서 잘라주기만 하면 될거 같은데 이런 간단한 기능이 자동으로 안된다고? 하는 의문의 들어 인터넷을 찾아 보았지만 뭐 제 생각처럼 간단하고 좋은 방법은 없었습니다.

(인터넷에서 찾지 못해 직접 만들었습니다. MUSICDOWNTOWN -- 바로가기)

그리고 다시 goldwave 를 열어서 이런 저런 기능들을 살펴 보던중

 

유레카!

 

찾고야 말았습니다.

 

그래서 그 방법을 공유드릴까 합니다.

 

제가 다운로드 받은 곡은 OOO 의 베스트 곡 모음입니다. 무려 한시간 반 정도의 긴 음원이고 여러 곡이 포함되어 있습니다.

1시간 반짜리 여러 곡 모음 mp3

자 그럼 이제 골드 웨이브를 열어 줍니다.

골드웨이브

 잘 아시는 바로 그 골드웨이브 (goldwave) 맞습니다.

 

다운로드 받은 mp3 파일을 열어줍니다.

우측 상단에 보여지는 큐 버튼을 이용한다

그리고 우측 상단에 보여지는 "큐" 버튼을 눌러 줍니다.

 

그럼 아래와 같은 창이 뜨게 됩니다.

큐 포인트 리스트 창

 

우리는 자동으로 음악을 자르는게 목적이므로 Auto Cue 버튼을 누릅니다.

자동으로 큐를 추가하기 위한 설정 창

요런 창이 나오게 되는데요. 좌측 상단을 보면 "Mark Silence" 라는 부분이 눌려있는 모습이죠? 네. 바로 무음 구간을 찾아내는 겁니다.

위에 두개의 슬라이더를 조절하여 무음 구간을 판단하기 위한 속성을 조절합니다. 위쪽 슬라이더는 사운드의 크기가 일정 수준 이하이면 무음 구간으로 판단하는 거고요, 아래쪽 슬라이더는 해당 무음구간이 지정한 시간 이상 지속되면 해당 위치에 Cue 를 추가하는 것이죠.

음원마다 조금찍 차이가 있지만 영상을 만드신 분이 노래와 노래 사이에 무음구간을 잘 넣지 않으면 사실 정확하게 잘리지는 않습니다. 

어쨌든 무음 구간이 정확히 들어있는 곡 모음 파일이라면 정확히 잘라집니다.

이렇게 세팅하고 OK 를 눌러보면 

자동으로 무음을 기준으로 큐 가 추가되었다

이렇게 자동으로 큐가 추가된 것을 볼 수 있습니다. 총 19개의 큐가 추가 되었는데 중간에 18,19 번의 경우 시간이 30초정도 밖에 안되는 것을 보면 정확한 판정이 되지 않은 것으로 보입니다.

어쨌든 이렇게 자동으로 큐가 추가된 리스트를 개별의 음원으로 저장하는 방법은 위에 빨간 네모박스가 표시된 Split File 버튼을 눌러주면 됩니다.

저장 경로 및 네이밍 규칙 설정

그럼 어떤 경로에 어떻게 저장할 것인지 설정하거나 어떤 형식으로, 어떤 이름 규칙을 적용할 것인지 등을 선택할 수 있게 되어 있고 저는 기본 저장 포멧이 mp3 이므로 저장 경로만 선택한뒤 그대로 저장합니다.

OK 버튼을 눌러주면

큐 별로 저장이 진행되는 중

 

요렇게 각각 파일이 저장되는 다이얼로그 창이 보여지게 되고요.

 

저장된 경로를 가보면 

자동으로 잘라진 음악들

짜잔~

네 중간에 6초짜리 잘못 저장된 음원도 있네요. 9분짜리 음원도 있는데 아마 중간에 큐 삽입이 안된것 같습니다.

이런 경우에는 그냥 열어서 하나만 잘라주면 되니까 그정도 고생은 할만 하지 않겠습니까? ㅋ

이제 원곡 이름으로 이름을 쫙 바꾸어 주면 완료.

단 5분도 되지 않는 시간에 모든 곡을 잘라낼 수 있게 되었습니다. 이제 인터넷 없이도 사랑하는 OOO 의 음악을 들을 수 있겠군요 ㅋ

 

물론 이번 포스트를 소개해 드리기 위해 다운 받아서 듣기는 해보았지만 사실 매일 매일 듣고 싶은 곡은 다르기 때문에 대부분은 유튜브에서 직접 듣기는 합니다만... 

어쨌든 인터넷이 안되는 장비나 환경에서 구할 수 없는 음원을 듣고 싶다면 이런 방법 밖에는 없지 않을까요?

그럼 이만 오늘의 포스팅을 마치겠습니다.

 

귀찮게 자르지 마시고 자동으로 다운로드, 잘라주는 프로그램 사용해 보세요~

2023.06.20 - [DEV] - 유튜브 음악, 영상 다운로드 프로그램 - MUSIC DOWNTOWN

 

유튜브 음악, 영상 다운로드 프로그램 - MUSIC DOWNTOWN

데이터는 없는데 유튜브로 음악은 듣고 싶다면? 데이터는 없는데 봐야 할 영상이 수두룩 하다면? 바로 다운로드 받아서 보는 방법이 있죠. 인터넷에 보면 유튜브 주소를 넣으면 음원 파일이나

diy-dev-design.tistory.com

 

반응형
반응형

딸내미랑 만들었던 병아리 부화기!

2022.05.21 - [DIY/micro:bit] - [DIY] micro:bit - 병아리 부화기 만들어 보기 (초딩도 가능)

과연 동작할까?

시골에 있는 처갓집에서 돌아오는 길에 닭장에 들러 방금 낳았는지 아직 온기가 남아있는 청계란을 하나 들고 왔습니다.

딸아이가 두시간가량 집에 오는 차안에서 수건에 감싼 계란을 품에 안고 와서 지난번에 만들었던 마이크로비트를 이용한 계란 부화기에 넣었습니다.

온도는 37도, 습도는 70% 이상을 유지해 주어야 하고 부화기에 넣은 후 18일 이전 까지는 하루에 4~5번 정도 전란을 해주어야 하는 21일 간의 대장정이 시작되었습니다.

온도가 나올때는 T 가 먼저 나오고 숫자를 출력한다.

요렇게 불이켜지면 온도가 서서히 올라갑니다. 전구는 할로겐을 썼으면 좀더 빠르게 열이 올랐을텐데요, 마침 가지고 있는 할로겐 램프가 없어서 자동차 깜박이용 꼬마 전구로 연결을 했습니다. 아마 5W 정도 되는것 같은데요, 열이 서서히 올라가는 것이 어쩌면 계란에 부담이 적을 것 같아 그냥 사용했습니다.

습도가 나올때는 H 가 나오고 숫자를 출력한다.

 

그리고 센서의 값 읽기에 실패를 하는 경우가 있는데 이상하게 연속해서 실패가 나는 경우가 있더라고요. 그럴때 마이크로비트를 재부팅하면 센서 측정이 다시 잘 되는것을 보고 아래와 같이 아두이노와 릴레이 모듈을 이용해서 30분에 한번씩 마이크로비트의 전원을 차단했다가 다시 연결해주어 물리적으로 재부팅을 해주었습니다.

마이크로 비트가 장시간 동작할때 센서 측정이 잘 안되는 듯하여 릴레이를 연결. 주기적으로 재부팅

 

일단 온도와 습도는 위 사진에서 본것과 같이 마이크로 비트가 표시해 주기 때문에 수시로 확인이 가능했으며 전란의 경우 엑셀로 표를 출력해서 전란을 해준 시간을 기록하는 것으로 하기로 하였습니다.

제가 출근하는 날은 딸아이가 시간 날때 전란을 해주고 제가 재택근무를 하는 날은 직접 전란을 해주기로 하였습니다.

그리고 계란을 전란 했을때 방향을 알 수 있도록 연필로 방향을 표시해주고 이름도 붙였습니다.

"청계구리"

ㅋㅋ

청계닭이 낳은 거라서 청계구리라고 하네요.

 

 

그렇게 약 12일 정도가 지난 시점.

 

이제 검란을 해봐야 겠죠?

검란이란 계란 내부에 병아리가 자라고 있는지 확인하는 과정인데요. 저도 처음해보는 것이라서 두근두근 했습니다.

후레쉬를 아래두고 비추어본 사진

핏줄 같은 것들이 보이시나요? 

정말 너무너무 신기합니다.

안에 움직이는 녀석은?

자세히 보고 있으면 안쪽에 무엇인가가 저렇게 꿀렁꿀렁 움직이고 있어요.

불과 10여일 만에 저 작은 알 안에 생명이 싹터서 저렇게 움직이는 것이 너무나 신기하고 아름다웠습니다.

 

검란은 저때 딱 한번 해보고 그 뒤로는 하지 않았습니다.

굳이 계속 봐야 할 이유도 없고요, 자라나는 병아리에게 나쁜 영향이 있지는 않을까 해서 말이죠.

 

그 뒤로 내부에 물을 담은 그릇에 물이 말라 습도가 40% 까지 떨어진적도 하루 있었습니다.

정말 너무너무 걱정되고 후회되고 그랬습니다. 제 자식도 아닌데도 말이죠 ㅎㅎ

 

그렇게 시간이 흘러 흘러 21일 째 날이 되었습니다.

예상대로라면 오늘쯤에는 병아리가 껍질을 깨고 나와야 하는게 아닌가 싶어 왠종일 부화기만 쳐다보고 있었는데요, 

저녁 10시 쯤이었나요.. 

 

아주 아주 작은 소리로 

"삐약~삐약~" 

읭?? 

너무 병아리를 기다린 탓에 환청이 들린걸까? 생각하는데

"삐약~삐약~삐약~" 

 

앗!

병아리가 있다!

병아리가 있다! 

소리를 쳤더니 아이들이 달려와 같이 알 속의 병아리 소리를 들으며 얼른 나오라고 응원을 했습니다.

 

그런데 예상과는 달리 삐약 소리만 간헐 적으로 들릴 뿐 나올 생각을 안하는 겁니다.

흠...

계란을 조금 깨줘 볼까.. 생각을 했지만 스스로 나와야 할 것 같아서 내일 까지 기다려 보기로 하고 잠을 청했습니다.

꿈속에서 병아리가 껍질을 깨고 나오지 못해 죽음을 맞이하는 무시무시한 악몽에 시달리며 아침을 맞이 했습니다.

 

과연 병아리가 나왔을까?

 

...

이제 삐약 소리도 안나는군요.

에라 모르겠다. 조금 씩 껍질을 깨 봐야 겠다 싶어서

부화기에 손을 넣어 계란을 들어올리는 순간

"뻑!"

하며 충격이 오는게 아니겠습니까?

어맛! 깜짝이야!

첫 입질에 금이간 청계구리

정말 심장이 떨어지는 줄 알았습니다.

생각보다 힘차게 쪼았나봅니다.

 

"톡, 톡, 토톡! "

가끔씩 소리가 나면서 이제 나오려나 보다 .. 하고 지켜보았으나.. 

 

 

 

안나와요..

 

 

한시간 정도가 지나서 조그만 구멍이 뚫렸지만 계속 부리만 보이고 속껍질을 핥아 먹는 다고 해야 할까요? 그렇게 진도가 나가지 않아서 또 애간장이 타더군요.

요상태로 몇시간을 .

정말 첫째 아이 출산할때 기다리던 것처럼 애간장이 타고 걱정이 이만 저만이 아니었습니다.

껍질 깨는 것을 도와줘야 하나 마나 하는 생각을 1000번도 더 한것 같아요.

어쨌든 인고의 시간이 지나고... 

 

 

오전 9시 쯤 첫 입질을 하고나서 거의 7시간이 지나서야 본격적으로 껍질을 깨기 시작합니다.

으아.. 드디어.. 

약 30배속으로 찍은 영상
드디어 나왔다!

 

와 정말 감격 스럽더군요.

아이들과 함께 이 장면을 보면서 환호성을 질렀습니다.

갓 태어난 우리 청계구리

 

인터넷으로 본 바로는 태어나면 핏덩어리 같은게 묻어 있기도 하고 엄청 징그럽다는 글을 봤지 적나라하게 갓 태어난 병아리를 자세히 볼 기회는 없었는데요. 

막상 직접 보니 전혀 징그럽지 않고 예뻤습니다. 털이 젖고 몸이 부실해서 덜덜 떨며 휘청거리는 모습이 조금 안쓰럽기는 하지만 징그럽다는 느낌은 전혀 안들고 아주 귀여웠습니다.

 

아직 따뜻한 온도가 유지되는 부화기 안에서 하루 정도 더 두었다가 딸아이와 직접 만든 육추기로 옮겨 주었습니다.

그리고 이름은 '단군이' 로 지었습니다. 태명은 청계구리 였는데요, ㅋㅋ 개천절에 태어났기 때문에 단군이로 부르기로 했습니다.

육추기에 들어간 단군이

육추기 만드는 것도 따로 준비를 했으면 좋았을텐데.. 아쉽지만 없네요.

어렵지 않습니다.

가능한 커다란 박스를 준비해 주시고요.

위에는 할로겐 등과 공기 순환용 fan 을 장착하고 옆쪽에는 온습도 센서를, 이번에는 마이크로 비트 대신 아두이노로 온도 조절을 하였습니다.

뭔가 아두이노가 더 편한것 같아요. ㅎ

 

 

달랑 계란 한개 들고와서 부화가 안되면 어쩌나,,, 전란을 하루 빼먹은 날도 있는데, 문제가 있는건 아닐까.. 습도가 맞지 않았던 그날 어떤 문제가 생기지는 않았을까.. 너무 너무 걱정을 하며 지낸 시간이 무색하도록 정말 건강한 병아리가 되었습니다.

너무나 귀여운 단군이

딸아이가 날때 부터 계속 봐줬더니 엄마인줄 아는건지 무서워 하지를 않네요. ㅎㅎ

 

손에서 잠든 아기 병아리 단군이

첫째 아들 녀석이 손이 뜨거운 편인데요, 첫째 손에만 가면 저렇게 잠이 듭니다 ㅋㅋ

지 어미 품인줄 아는걸까요

 

단군이가 태어난지 10여일이 지났습니다.

어느새 깃털이 자라고 있는 단군이
자태가 제법 늠름한 듯 ㅋ

어느새 꼬리와 날개에 깃털이 자랍니다. 이제 병아리에서 점점 닭으로 바뀌어 가는거겠죠?

재택근무를 하는 동안 저렇게 거실에 내려 놓아도 제 발 주변을 맴돌면서 바닥에 떨어진 먼지 같은걸 쪼거나 할 뿐 다른 곳으로 가지 않아요. 또 제가 주방으로 가거가 하면 쪼르르 저를 따라 쫒아옵니다.

아마 날 때 부터 사람들과 있어와서 저희 가족들이 제 가족인 줄 아는가 봅니다.

 

아파트인 저희 집에서 아마 단군이는 오래 함께 하지는 못하겠지요?

2주일이 되니 이제 날개짓을 하며 공중으로 날아오르려 합니다. 시골로 가야하는 순간이 다가 온 듯 하네요.

제가 손을 내밀면 손바닥으로 뛰어 올라오는 단군이를 보며 마음이 좀 착잡 합니다. 시골에 내려가면 이제 여기에서 처럼 따뜻한 사람의 손길은 없을 거에요. 아마 다른 닭들 처럼 닭장 안의 삶을 살아가겠죠. 어쩌면 다음번에 단군이에게 드리워진 손길은 단군이의 목숨을 가져 갈 손일 수도 있다고 생각하니 너무 불쌍하다는 생각이 듭니다.

 

암튼, 아이들에게도, 저에게도 너무나 소중하고 좋은 경험이었습니다..

안녕 단군아~ ㅋ

 

병아리 부화기를 만들었던 글은 아래 글을 참고하세요~

2022.05.21 - [DIY/micro:bit] - [DIY] micro:bit - 병아리 부화기 만들어 보기 (초딩도 가능)

 

[DIY] micro:bit - 병아리 부화기 만들어 보기 (초딩도 가능)

딸내미 : "아빠! 할머니 집에서 가져오는 계란은 유정란이야?" 나 : "그럼~. 암탉도 있고 수탉도 있으니 유정란이겠지?" 딸내미 : "그럼 내가 저거 품으면 병아리가 나오는 거야?" 나 : "흠... 아니....

diy-dev-design.tistory.com

2021.09.26 - [DIY/micro:bit] - [마이크로비트] 시작해보자. micro bit (마이크로비트)

 

[마이크로비트] 시작해보자. micro bit (마이크로비트)

요즘 학생들도 코딩교육이 한창인지 저희 아들도 학교에서 코딩 교육을 진행하다고 하기에 어떤 걸로 배우는가 물어봤습니다. 아들 : 마이크로비트, BBC 에서 만든거래. 저: ??? 네. 아두이노만 알

diy-dev-design.tistory.com

 

반응형
반응형

아이가 어느덧 중학생이 되었다.

많은 지식보다 따뜻한 마음을, 고리타분한 원리원칙 보다는 위트있는 재치와 지혜를, 범생이 공부벌레 보다는 음악과 예술을 사랑하는 몸고 마음이 건강한 아이로 키우려고 마음먹은지 15년이 지난 지금 아이를 보며 나는 어떤 아빠인지 생각해 본다.

아이에게 영,수 학원보다는 복싱 체육관과 피아노학원을 선택할 수 있는 기회를 주었고 최근에는 실용 음악 학원에서 베이스 기타를 배우며 학교에서는 중학생 밴드 부원으로 활동하고 있다.

나름 내가 생각했던 그런 아이로 자랄 수 있는 환경을 주었다고 생각했고, 그렇게 음악을 사랑하는 아이가 연주하는 피아노 곡이나 기타를 연주하는 모습을 바라보면 흐믓하고 가슴이 따뜻해 진다.

 

아이가 연주하는 음악을 들으며

 

문득 그런생각이 들었다.

 

아.. 내가 이 아이의 팬 인가보다.

내가 아이의 연주하는 모습을 이렇게 좋아하는건, 엉성한 연주도 이렇게 가슴이 따뜻해지는건 아마 내가 이 아이의 팬이기 때문인거라는 생각이 들었다.

육아 이야기 하다가 갑자기 왠 연예인 이야기도 아니고 '팬' 이 나오냐고?

음...

연예인의 이야기를 잠깐 해보자.

[사진 출처 = 'One in an ARMY' 공식 홈페이지 / 빅히트엔터테인먼트]

BTS 가 오늘의 인기를 얻기까지 BTS 의 능력과 외모 못지않게 영향을 미친 것은 바로 BTS 의 팬덤인 아미 때문이 아닐까. 아미야 말로 정말 자신의 연예인을 최고로 만든 일등 공신이라 생각되는데, 다른 극성 팬들과 다르게 그들의 행보를 보면 정말 자기가 사랑하는 연예인이 최고의 가수가 될 수 있도록 모든 방면에서 긍정적인 영향력을 행사하는 것을 보았다.

어쩌면 훌륭한 연예인이 만들어 지기 위하여는 본인의 스타성과 노력도 중요 하겠지만 바로 멋진 팬들이 있어야 하는게 아닌가 생각된다.

부모는 자식의 팬이 되어 자식을 응원하고 자식이 가야할 길을 비추어 주는 열혈 관객이라는 거지.

팬들이 떠나버린 연예인은 어떠한가. 더이상 사랑해 주는 이가 없는 연예인... 어쩌면 그 시점에 이미 연예인이라 할 수 없을 것이다.

아이도 마찬가지다. 어릴때는 그저 예쁘고 귀여워 눈에 넣어도 아프지 않을 것 처럼 아이만을 바라보던 부모가 어느새 커버린 아이에게 관심이 줄어들고 부모의 기대에 미치지 못하는 성적과 사춘기를 맞아 예민해진 아이와의 잦은 의견 충돌로 점점 담을 쌓게 된다면 그 아이는 잃어버린 팬을 찾으러 가정 밖으로 돌겠지. 

팬들이 떠나간 연예인이 밤무대와 이름모를 행사장을 전전하는 것처럼 아이도 어른들의 보호가 닿지 않는 그늘 속으로 그를 사랑하는(?) 친구들, 그리고 인터넷 세상 속으로 점점 숨어드는 거라 생각한다.

집안에 자신의 열혈 팬이 여전히 자신만을 사랑해 주고, 믿어주고, 기댈 곳을 준다면 어두운 그늘이나 가정으로 부터의 도피처를 찾아 떠나갈 아이는 없을 것이다.

아이가 무엇을 좋아하는지 잘 살펴보고 아이의 관심사에 나도 관심을 갖는것, 우리가 연예할때 애인의 마음을 얻기 위해 했던 바로 그것들, 사랑하는 연예인의 팬덤에 들어가 그들과 함께 응원하는 그 마음을 아이에게 준다면, 그렇게 행동하는 것을 아이가 본다면 아마 자신의 오랜 팬이 돌아온것에 고마워 할 것이다.

요즘 밴드활동을 하는 아이를 위해 아빠 세대의 슈퍼스타였던 건스앤로지스나 너바나, 매탈리카를 소개해주고 그들의 이야기를 함께 나누며 즐거운 시간을 보내고 있다. 내가 어릴 적 정보가 없어 몰랐던 유명한 밴드 맴버들의 숨은 이야기 등을 아이 스스로 찾아보고 눈을 반짝이며 이야기한  '너바나'의 베이시스트인 '크리스노보셀릭'의 삶에 대하여 이야기를 할때는 정말 크게 웃지 않을 수 없었다. 

 

라이브로 전설적인 가수들의 음악을 듣는 다는건 음악하는 이에게 얼마나 황홀한 경험인가!

나는 한번도 가보지 못한 락페스티벌에 며칠전에 아이와 친구들을 보내주며 그들이 행복한 시간을 갖기를 기도했다. 다시 안올 열다섯살의 잊지 못할 추억이 되기를 바라며 티켓을 끊어 주었다. (이틀 뒤 가 중간고사 라는건 함정)

돌아오는 아이의 상기된 표정과 재잘대는 친구들의 얼굴이 밝아 흐믓했다.

 

 

자 아이를 키우는 엄마 아빠들이여, 오늘부터 자신의 아이의 열혈 팬이 되어 보자.

아이를 키우는건 훈육도 간섭도, 교육이나 잔소리가 아니고 바로 사랑이다.

 

성인이 되기 까지 이제 5년. 

길고 긴 육아라는 퀘스트가 중간은 넘은것 같다.

물론 둘째 퀘스트도 함께 수행중이긴 하지만 ㅋ

반응형
반응형

나이가 40을 넘어가면서 체력이 뚝뚝 떨어지는 것을 느낀다.

매년 연초에 계획하는 운동 계획은 1월을 넘기지 못하고 흐지부지 되었고, 단 한번도 계획을 세운적은 없지만 매일 같이  어기지 않고 저녁마다 먹는 맥주는 내 몸을 더욱 무겁게 만들고 있다.

이런 저런 이유로 요즘 무기력함이 커지는 가운데 현대자동차에서 주관하는 '롱기스트런'이라는 이벤트가 눈에 들어왔다. 몇가지 달리기 미션을 도전하고 그 보상으로 마일리지를 받아 경품 추첨에 응모할 수 있으며 파이널 런이라는 10km 미터 단축 마라톤 대회를 개최하는 이벤트다. 

이 활동은 현대자동차가 주관하는 사회공헌 활동의 하나로 쉽게 말하자면 이용자가 달린 만큼 현대자동차가 나무를 심는 프로젝트라고 할 수 있겠다.

뭐 솔직히 그런건 기업이 사회공헌을 하는 핑계 정도에 불과할테니 내가 크게 신경쓸 건 아니고 뭔가 운동을 시작하는 핑계정도라고 생각하고 파이널런에 도전하기로 했다.

 

본격적으로 10km 달리기 준비하기!

솔직히 말하면 군대를 제대하고 20년이 넘도록 10km 라는 거리를 달려본적은 없다.

뭔가 뛸수 있을 것 같은 느낌적인 느낌은 있지만 막상내가 달릴 수 있을지... 

황근출 해병님이 뒤에서 쫒아오며 '잡히면 건조장'을 외치지 않으면 달릴 수 없는 몸이 된 것인가.

매년 연초에 운동한다고 나가서 4km 이상을 달린적이 없는 내게 10km 는 약간 높은 벽이었다.

4km 정도를 달리면 항상 종아리가 앞뒤로 많이 땡기고 무릎이 아팠기 때문에 뭔가 변화를 주어야 겠다 생각했고 신발을 구입했다.

참고로 본인은 175cm 에 70kg 인 평범한 몸매에 근육은 1도 없고 배만 뽈록한 전형적인 사무실 차장님 체형이다.

 

그래서 구입한 신발은?

아식스 타사 엣지 

내가 구입한 아식스 타사 엣지

 

소싯적에 10만원이 넘는 러닝화를 보며 와... 나도 이런 신발 살 수 있을까 하며 군침을 흘렸었는데... 

40이 넘어서야 구입을 하게 되었다.ㅜㅜ

가난했던 내 젊은 시절..

 

어쨌든 난 발볼이 넓으므로 4E 로 구입! (발 볼에 맞게 구입할 수 있다니 아식스 짱)

오호.. 신발 구입의 효과는 굉장했다.

뭔가 쿠션은 약한듯한데 뒷굼치 쪽에 통통 튀어오르는 반발력이 있고 창의 좌우 뒤틀림이 거의 없는 느낌이다. 앞쪽의 독특한 그립때문인지 땅바닥에 발바닥이 쫙쫙 붙는 느낌이 든다. 차고 나가는 힘도 덜 드는 느낌이랄까.

 

4~5만원짜리 저렴한 러닝화랑은 땅바닥을 딛는 느낌자체가 달라졌다. 

결정적으로 똑같이 4km 를 뛰었을때 무릎과 종아리가 전~~혀 아프지 않았다. 

이건 놀라운데?

 

 

얼마뒤 바로 10km 미터 달리기에 도전했다.

완주를 목표로 달렸고 최근 몇차례 4km 이상을 달린적이 있었기 때문에 무리하지 않고 완주를 목표로 달렸다.

 

결과는...

처음 도전에 성공한 10km 달리기

이정도면 만족.

이대로면 파이널런에서 1시간을 목표로 완주는 할 수 있겠다 싶었다.

 

하지만 그 이후 10km 도전할 때 마다 번번히 실패했다.

몇 차례 도전해 봤지만 4km, 3km, 6km, 7km, 5km... 번번히 10km 에 실패했다.

어쩌다 자기전에 맥주라도 먹고나면 근육통 때문에 한 2~3일은 달릴수가 없었다. 

뛰다 보면 장트러블이 항상 와서 집으로 향해야 했고, 그렇지 않은 경우엔 1시간을 달려야 하는 그 시간이 너무너무 지루했다. 정신이 육체에게 그만 두라고 발을 내 딛을 때마다 강요하는 느낌에 도저히 지쳐서 달릴 수가 없었다.

그래서 필요한건? 

바로 음악!

그리고 오래 착용해도 불편하지 않고, 오래 달려도 귀에서 빠지지 않는 블루투스 이어폰을 구입했다. 요즘 핫 하다는 

QCY-ailyPods

QCY aliPods bluetooth earphone

적당한 가격에, 적당한 음질, 적당하지 않은 디자인과 사용성 까지 아주 괜찮은 이어폰이다.

블루투스 통화음질은 별로인 듯 

 

그리고 1시간 러닝을 책임져 줄 180bpm workout 음악! (유튜브에 널려있음)

https://www.youtube.com/watch?v=8z6GKCwHZuE&ab_channel=WorkoutMusicSource 

뭐 대충 이런 음악

여기서 180bpm 이 중요하다.

이 180bpm 비트에 맞게 발을 내 딛으면 거의 정확하게 분당 180 걸음으로 달릴수 있어 운동 효과도 극대화 되고 페이스 조절도 문제 없다. 게다가 음악이 주는 힘 때문인지 달려나가는 힘이 훨씬 덜 든다. 뭔가 전장으로 달려나가는 전사가 된 듯한 느낌?

 

효과는?

Really good!

다시 한번 10km 달리기 도전 

신발과 음악이 더해진 기록

 

성공!!

 

또 한번 기록을 단축할 수 있었다.

 

 

그럼 이게 끝이냐 하면,,,

 

인터넷에 각종 운동관련 정보를 보던 중 심박수를 모니터링 하는게 운동에 상당히 중요하다는 것을 알게 되었다. 지방을 효율적으로 태울 수 있는 수준의 심박수를 유지한다거나 자신의 최대 운동 능력과 지속 가능한 운동능력의 수준을 심박수를 통해 모니터링 가능하다는 것이다.

뭐 더이상의 자세한 설명은 집어 치우고 쇼핑모드 진입!

운동 센서하면 가민이지. 가민 제품을 알아봤는데..

너무 비싸다.

나약한 정신상태를 갖고 있는 저질 몸뚱아리가 언제 러닝을 포기할지 모르는 나이기에 좀 저렴한 센서를 찾다가 그나마 평이 좋은 제품을 찾았다.

결국 알리에서 구입하는걸로... 

알리에서 Magene H303 이라는 제품이 눈에 들어와 구입했다. H64 는 구형인듯 해서 H303으로 구입했고 일주일만에 배송되었다.

가성비 HR 센서 Magene H303

 

내가 사용하는 운동 기록 어플은 스포츠트래커 (sportstracker) 인데...  사람들이 많이 사용하지는 않는 어플인것 같다. 보통 스트라바(strava) 나 나이키 러닝 앱 같은 뭔가 이름부터 메이저한 앱을 많이 사용하는 것 같은데 나는 몇년전에 우연히 자전거를 타며 깔았던 스포츠 트래커 앱을 그냥 사용하기로 했다.

결국 스포츠 트래커를 계속 사용하게 된 가장 중요한 이유가 스트라바의 강력한 커뮤니티 기능이 난 싫었기 때문인데 (내가 원하지 않는데 마구 사람들의 기록을 보여주고 내기록을 공유할 것인지 자꾸만 물어봄) 스포츠 트래커는 뭔가 그런 기능이 약했고 강요하는 느낌이 없어 좋았다. 사실 난 SNS를 거의 하지 않는 아싸니까.

암튼 첫번째 걱정은 스포츠트래커 어플에서 구입한 심박센서가 잘 인식 되는가 였는데

 

예스!

잘됨. (걱정말고 구입하세요)

페어링 방법은 먼저 밴드 안쪽에 매끈한 재질로 심전도를 인식하는 부분이 있다. 그 부분에 물을 뭍힌 후 가슴에 적당히 조이도록 부착한다음 H303의 똑딱이 단추에 맞춰 부착하면 된다. (정상적으로 장착이 되었다면 상단에 빨간 LED 가 들어옴)명치 부분근처에 센서가 오도록 밴드를 조절해주면 자동으로 H303의 중간에 빨간 불이 들어온다. 그럼 페어링 준비 끝

블루투스 연결 메뉴로 가지 말고 바로 스포츠트래커 앱을 켠다.

설정으로 들어가서 아래와 같이 심박수센서를 연결해주면 된다.

사진과 같은 순서로 들어가면 센서 연결을 할 수 있다

연결되면 바로 심장 박동이 모니터링 되는데 준비 페이지에 들어가면 자동으로 모니터링을 시작한다.

 

심박센서를 연결하니 스포츠트래커에서 1km 마다 음성으로 알려주는 피드백 정보에 심박수도 알려준다. ㅎ 물론 피드백을 주는 간격을 거리로 정할 수도 있으니 원하는 타이밍에 피드백을 받을 수 있다.

심박수 센서를 동작하며 달려 본 결과 

편안하게 시속 10km/h 정도로 뛰면 145~155 사이, 10 ~ 11km/h 정도로 뛰면 155~165 사이, 11.5km/h 까지 올렸더니 175에 육박하는 엄청난 속도로 심장이 뛴다는 것을 알 수 있었다. 

이번에 초반에 페이스 조절을 잘해서 인지 9km 를 통과했을때 체력이 좀 남아 있는 듯하여 마지막 500m 정도에 페이스를 마구 올렸더니 180이 넘어버렸다.

심장이 터저버렷

 

자. 이제 알게된 것은 내 심박수 기준으로 150 ~160 수준으로 맞추면 10km 를 뛰는데 문제가 없다는 것을 알았다. 

심박수까지 모니터링하면서 뛰었더니 거짓말처럼 기록이 좋아졌다.

 

무려 5분 초반대 페이스를 기록함

대박

 

 

자 그럼 이제 10km 파이널런 참가를 위한 준비는 끝난 것인가?

 

그런데 코로나 시국 아닌가!

정부가 바뀌면서 거리두기도 안하고 뭐 질병관리청이 뭐하는 곳인지 당췌 이해가 안가는 코로나 시국이니 만큼 내건강은 내가 챙겨야지. 

마스크를 쓰고 달려야 하면 이야기가 달라지자나!

주최측에 물어봤다.

이런 제길

 

ㅜㅜ

 

마스크를 쓰면 땀이랑 습기 때문에 2km 만 뛰어도 마스크가 입에 달라붙여 도처히 숨을 쉴 수 없었기에 달릴때 마스크 착용을 권고한다는 주최측의 답변은 나에겐 절망적인 소식이었다.

 

그렇게 내가 포기 할 것 같음? 마스크가 입에 붙지 않게 하면되지.

이너가드를 만들자!

(인터넷 쇼핑몰에서 그냥 사면 된다는 사실을 모르는 나란 남자)

집에 있는 3D 프린터용 필라멘트를 이용해서 마스크 이너가드를 만들었다.

짜잔~

3D 펜으로 대충 떼워서 붙여주었다.
마스크에 장착하면 이런 모양

뭔가 착용해 보면 자꾸 입술에 필라멘트가 닿는다.

뛰면서 혀로 낼름거리며 필라멘트를 빨거나 입술로 만지작하는 재미가 있기는 한데...

 

앞쪽만 남기로 안쪽은 없애야 겠다.

 

-_-

 

자 이제 준비완료.

 

자 진지하게 좀 달려보기 위한 준비물을 정리해보자면

  • 자신에게 잘맞는 러닝화 구입 (좋은걸로)
  • 180bpm 음악 준비, 편안하고 사용하기 좋은 이어폰 필수
  • 운동 전후로 술 먹지 않기
  • 배가 부른 상태에서 달리지 않기
  • 심박센서와 같은 자신의 운동 모니터링 장비 구입
  • 마스크 이너가드 (옵션)

요정도가 초보 러너에게 필요한 10km 도전을 위한 준비물이라 할 수 있겠다. 

경품으로 응모한 가민 포러너가 당첨된다면 ㅎㅎ 너무 좋겠다.

 

 

2022년 10월 15일 여의도.

롱기스트런 파이널런

과연 성공할 수 있을까?

 

 

 

건강한 신체에 건강한 정신이 깃든다는 아식스 창립자의 창립 이념을 난 좋아한다. 아식스라는 브랜드를 좋아하는 이유이기도 하다.

이 글을 보는 모든 분들에게 건강한 신체와 건강한 정신이 깃들기를 바랍니다.

반응형
반응형

만능 리모컨을 만들기로 해놓고 정말 많은 시간이 흘렀습니다.

2019.11.10 - [DIY/Arduino] - [DIY] 아두이노로 만능 (통합) 리모콘 만들기 1/3

2019.11.10 - [DIY/Arduino] - [DIY] 아두이노로 통합(만능) 리모콘 만들기 2/3

2020.06.17 - [DIY/Arduino] - [DIY] 아두이노로 통합(만능) 리모콘 만들기 3/3

결국 이번에도 완성되지 못한채 프로젝트가 끝나는것이 안타까워 마무리를 해보기로 합니다.

(리모컨 신호 따기, 각 기능별 동작 확인하는 과정 등은 위 링크의 글을 참고해 주세요)

사실 로직도 다 만들었고 테스트도 어느정도 완료 했으니 하드웨어만 만들면 되는데요, 항상 그렇지만 적당한 하드웨어를 구성하는 것이 쉬운일은 아닙니다. 3D 프린터가 있긴 하지만 완전한 완성품을 만드는게 쉽지 않더군요.

저처럼 DIY 를 하는 경우에는 개조에 적당한 다른 하드웨어를 가지고 튜닝을 하는게 더 쉽고 완성도도 높다 보니 적당한 녀석을 찾는 것이 일이었습니다.

 

그러던 와중!

완전 적당한 녀석을 발견했지 뭡니까.

바로 알리에서 구입한 18650 배터리를 이용한 파워뱅크 케이스 입니다. 병렬로 2개의 18650 배터리가 들어가게 되는데 그 중 하나를 넣지 않아도 정상 동작하기에 다른 한쪽에 아두이노 나노와 필요한 부품을 넣으면 되겠다 싶더군요.

 

그냥 이전에 만들었던거 집어 치우고 새로 만들겁니다.

 

바로 작업에 들어갑니다.

아래 사진처럼 납작한 택트 스위치를 덮개 위에 얹으면 될 것 같았습니다.

파워뱅크 케이스 위에 택트 스위치를 배열해본 모습

음.. 그럴싸 하군요.

하지만 작업의 편의성을 위해 만능 기판 위에 올리기로 합니다.

만능기판 위에 올린 모습

네.. 요게 더 작업이 수월할 것 같군요.

요렇게 가기로 합니다.

표면에 스위치가 그대로 노출되면 예쁘지 않으니 덮개를 만들어야 겠습니다.

잽싸게 3D 모델링을 한 다음 바로 3D 프린터로 프린트 해 봅니다.

3D 프린터로 덮개를 출력중임

 

만능 기판 위에 덮어본 모습

네.. 잘 맞는 군요. (몇차례 수정이 있었습니다)

 

 

그럼 이제 만능 기판위에 스위치와 인디케이터용 LED 를 납땜해 봅니다.

양면으로 사용가능한 만능 기판이므로 위쪽에 버튼과 LED 를 모두 붙여 준 뒤 아래 쪽에서 실제 배선을 하기로 합니다.

지금 보이는 모든 점퍼 선은 그라운드에 연결할 예정이에요. 

부품 장착 및 윗면 배선을 모두 마친 모습
인디케이터용으로 작은 SMD LED 를 장착했다.

그리고 인디케이터 용으로 LED 를 장착 했는데요, 위쪽에 보이는 4개는 각각 제가 선택한 제품을 표시하도록 하고 아래쪽 한개는 동작 할 때마다 신호가 나가는지 확인하기 위한 용도로 사용하기로 하였습니다.

워낙 SMD 부품이 작아 삐뚤빼뚤 하긴 하지만 뭐 큰 문제는 없습니다.

 

버튼의 위치와 버튼별 기능 목록

실제 버튼별 기능 목록과 각 기능별로 전달될 신호는 위와 같습니다.

 

아래쪽에서 본 버튼별 위치

 

뒷면에서 보면 위와 같은 모양으로 버튼들이 배치되어 있습니다. 헷갈리지 않도록 저렇게 만들어 놓고 배선을 시작하기로 합니다.

 

반응형

 

이제 실제 아두이노와 연결을 해야 겠죠?

파워뱅크 덮개 중앙을 뚫어 배선이 내부로 들어갈 수 있도록 해 주었고요,

제작한 기판이 올라갈 위치에 맞게 구멍을 뚫어 주었다.

안쪽에 아두이노 나노를 넣어서 문제 없이 장착이 될 지 확인해 봅니다.

기판과 덮개를 덮어 본 모습

네.. 아주 딱 맞아 떨어집니다.

이제 살인적인 납땜이 남았군요.

 

 

배선 시작 

..

분노의 땜질 시작!!!!

..

 

 

 

네.

끝냈습니다.

살인적인 케이블 들..
아래쪽에는 나노의 USB 포트를 노출시켰다.

 

.

.

 

 

네.. 아두이노의 거의 모든 포트와 연결이 되어있기 때문에 정말 힘들었습니다.

신호를 전달 하는 케이블은 사용하지 않는 랜선 내부의 케이블을 이용해서 연결해 주었습니다.

그리고 아두이노 나노의 USB 연결 포트를 노출 시켜서 나중에라도 프로그래밍을 손쉽게 변경할 수 있도록 했고요, 구석쪽으로 IR LED 를 꺼내주었습니다.

 

배터리를 넣고 동작이 되는지 테스트 해보았습니다.

일단 불은 들어오는 것 확인

 

네 일단 동작은 되는 것 같군요.

TV 에 와서 동작하는지도 확인해 보았고,, 약간의 코드 수정이 필요해 보이는 것을 제외하면 버튼들은 모두 동작하는 것을 확인했습니다.

 

 

그리고 단순히 리모컨으로써의 기능 뿐만 아니라 간단한 플래쉬와 레이저 포인터 기능도 동작하도록 할 생각입니다.

리모컨은 항상 거실의 중심에 있으니 플래쉬가 필요할 때도 빠르게 찾아 사용할 수 있을 것입니다.

 

최종 마무리가 된 모습은 아래와 같습니다.

좌측 하단에 1W LED 를 넣어주고 아래쪽으로 푸쉬락 기능이 있는 스위치를 하나 달아 주었고요,

좌측 위쪽으로는 레이저 다이오드를 넣은뒤 택트 스위치를 연결해주었습니다.

배터리에서 아두이노로 전원을 공급하기 위해 5V 승압 보드를 연결해서 전원 공급을 해주게 됩니다. 물론 스압 보드와 배터리 사이에 푸쉬락 기능이 있는 스위치를 하나 달아 주었습니다.

 

이렇게 하면 일반 USB 충전기를 이용해서 리모컨을 충전할 수 있으며 만약 배터리가 방전되어 교체가 필요하더라도 18650 배터리만 교체하면 되므로 사용성이나 유지보수도 용이한 구성이 되겠습니다.

 

 

 

UI 디자인 올리기

이제 슬슬 마무리를 해야하는데요,

어떤 버튼이 어떤 동작을 하는지 알아야 하지 않겠습니까? 그래서 전면부 UI 디자인을 해보았습니다.

이래뵈도 디자이너 아뉘겠습니까?

리모컨에 사용될 UI 작업 중

일러스트로 버튼의 위치에 맞게 UI 를 그려보았습니다.

좌측에 뒤집혀 있는 그림은 뭘까요?

조금뒤에 알려드리겠습니다.

일단 프린트해서 크기와 위치, 배치 등이 적당한지 확인해 봅니다.

버튼의 위치와 크기 확인 중

또 실제 버튼의 모양, 위치, 기능이 모두 정상 동작하는지 최종 확인을 해봅니다.

 

그럼 이제 OHP 필름을 꺼내 프린트를 다시 합니다!

네. 바로 OHP 필름입니다.

OHP 필름에 뒤집힌 그래픽이 출력되었다.

 

자 OHP 필름에도 출력이 잘 되었습니다.

그럼 출력된 면에 

영일 락카 등장

띠용~ 락카가 등장했네요.

 

네 출력된 면에 백색 락카를 칠해주면 

OHP 필름 출력면에 락카 칠 하는 중

 

칠해주면~

 

그리고 뒤집으면 ~ 

 

쨔쟌~

 

요렇게 반짝 반짝 하는 예쁜 그래픽을 볼 수 있답니다. 손으로 눌르거나 해도 지워질 염려가 없지요.

요렇게 만들어진 전면부를 제작한 리모컨 위에 똭 붙여 주면

완 to the 성!

완성된 아두이노 만능리모컨

오예~

완성 되었습니다.

 

사실 처음 계획이랑 전원 스위치 와 제품 변경 기능이 뒤바뀌긴 했지만 뭐 괜찮습니다.

 

부가기능들을 볼까요? 

플래쉬 동작!
레이저 포인터 동작!

오~예! 오~ 예!

모두 잘 동작 합니다. ㅋㅋ

 

며칠에 한번씩 배터리 방전을 막기 위해 핸드폰 충전기로 배터리를 충전해 주면 끝

충전 중인 만능 리모컨

 

네.. 이렇게 마무리가 되었습니다.

 

어휴 생각보다 힘들었지만 이제 거실 테이블 위에 널브러져 있는 리모컨들을 치울 수 있다고 생각하니 후련 합니다.

 

끝으로 최종으로 적용한 아두이노 소스코드를 아래에 올립니다.

상당히 긴 코드가 되어 버렸네요. 

더보기
#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;

bool canWorking_01 = true;
bool canWorking_02 = true;
bool canWorking_03 = true;
bool canWorking_04 = true;
bool canWorking_05 = true;
bool canWorking_06 = true;
bool canWorking_07 = true;
bool canWorking_08 = true;
bool canWorking_09 = true;
bool canWorking_10 = true;
bool canWorking_11 = true;
bool canWorking_12 = true;
bool canWorking_13 = true;
bool canWorking_14 = true;
// 
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);
  //EEPROM.write(0, productIdx);  
  productIdx = EEPROM.read(0);

  sw_01 = HIGH;
  sw_02 = HIGH;
  sw_03 = HIGH;
  sw_04 = HIGH;
  sw_05 = HIGH;
  sw_06 = HIGH;
  sw_07 = HIGH;
  sw_08 = HIGH;
  sw_09 = HIGH;
  sw_10 = HIGH;
  sw_11 = HIGH;
  sw_12 = HIGH;
  sw_13 = HIGH;
  sw_14 = HIGH;
  
  canWorking_01 = true;
  canWorking_02 = true;
  canWorking_03 = true;
  canWorking_04 = true;
  canWorking_05 = true;
  canWorking_06 = true;
  canWorking_07 = true;
  canWorking_08 = true;
  canWorking_09 = true;
  canWorking_10 = true;
  canWorking_11 = true;
  canWorking_12 = true;
  canWorking_13 = true;
  canWorking_14 = true;

  Serial.begin(9600);
  Serial.println(productIdx);

  
}

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) {    
    canWorking_01 = true;
  }else{
    digitalWrite(13, HIGH);
    if (canWorking_01)
    {
      press_S01(productIdx);
      canWorking_01 = false;
    }
  }
  if (sw_03 == HIGH) {   
    canWorking_03 = true; 
  }else{
    digitalWrite(13, HIGH);
    if (canWorking_03)
    {
      press_S03(productIdx);
      canWorking_03 = false;
    }
  }
  if (sw_04 == HIGH) {
    canWorking_04 = true;     
  }else{
    digitalWrite(13, HIGH);
    if (canWorking_04)
    {
      press_S04(productIdx);
      canWorking_04 = false;
    }
  }
  if (sw_05 == HIGH) {  
    canWorking_05 = true;    
  }else{
    digitalWrite(13, HIGH);
    if (canWorking_05)
    {
      press_S05(productIdx);
      canWorking_05 = false;
    }
  }
  if (sw_06 == HIGH) {    
    canWorking_06 = true;  
  }else{
    digitalWrite(13, HIGH);
    if (canWorking_06)
    {
      press_S06(productIdx);
      canWorking_06 = false;
    }
  }
  if (sw_07 == HIGH) {  
    canWorking_07 = true;    
  }else{
    digitalWrite(13, HIGH);
    if (canWorking_07)
    {
      press_S07(productIdx);
      canWorking_07 = false;
    }
  }
  if (sw_08 == HIGH) {    
    canWorking_08 = true; 
  }else{
    digitalWrite(13, HIGH);
    if (canWorking_08)
    {
      press_S08(productIdx);
      canWorking_08 = false;
    }
  }
  if (sw_09 == HIGH) {  
    canWorking_09 = true;   
  }else{
    digitalWrite(13, HIGH);
    if (canWorking_09)
    {
      press_S09(productIdx);
      canWorking_09 = false;
    }
  }
  if (sw_10 == HIGH) {    
    canWorking_10 = true;   
  }else{
    digitalWrite(13, HIGH);
    if (canWorking_10)
    {
      press_S10(productIdx);
      canWorking_10 = false;
    }
  }
  if (sw_11 == HIGH) {    
    canWorking_11 = true;   
  }else{
    digitalWrite(13, HIGH);
    if (canWorking_11)
    {
      press_S11(productIdx);
      canWorking_11 = false;
    }
  }
  if (sw_12 == HIGH) {    
    canWorking_12 = true;  
  }else{
    digitalWrite(13, HIGH);
    if (canWorking_12)
    {
      press_S12(productIdx);
      canWorking_12 = false;
    }
  }
  if (sw_13 == HIGH) {    
    canWorking_13 = true;  
  }else{
    digitalWrite(13, HIGH);
    if (canWorking_13)
    {
      press_S13(productIdx);
      canWorking_13 = false;
    }
  }
  if (sw_14 == HIGH) {    
    canWorking_14 = true;  
  }else{
    digitalWrite(13, HIGH);
    if (canWorking_14)
    {
      press_S14(productIdx);
      canWorking_14 = false;
    }
  }

	delay(10); //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(0x807F06F9,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:
    //for (int i = 0; i < 1; i++) irsend.sendNEC(0x2DF10EF,32);   
    break;
  case 2:
    for (int i = 0; i < 1; i++) irsend.sendNEC(0x1FE40BF,32);   // Btv home
    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
{

  irsend.sendNEC(0x2DFD02F,32); // tv 외부입력
  // S03 번은 편의를 위해 항상 외부입력으로 동작한다
  /*
  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
{
  Serial.println(productIdx);
  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;
  }
}

 

 

이상으로 아두이노 만능 리모컨 만들기를 마칩니다. 

 

반응형

+ Recent posts