365일 구동할 라즈베리파이 서버 + 여러가지 센서 데이터를 입력받아서 작업을해줄 서버를 만들어 보자. 아직 wol 기능만 넣을거라서 매우 쉽다. 순서는 다음과 같다.
1. go http 서버 구축
2. http의 "/turnon" 요청을 받으면 매직패킷 전송
* IR센서를 통해서 리모콘, 에어컨 제어하려고 했는데 IR수신기 센서가 문제인지 irrecord로 신호를 잡지 못한다.. 에어컨 리모콘, tv리모콘 전부 수신 못하는거보니 센서 문제 같기도해서 센서를 다시 사야겠다.
우리집 네트워크는 대충
이렇게 구성되어 있다. 처음에는 구글 cloud function으로 구글홈미니에서 wol을 실행하려고 했는데..
1. 나는 유동 ip를 쓰고있다..
고정 ip를 쓰려고 찾아보니 살펴보니 겁나 비싸다.. 그래서 다른 방법이 없을까 했는데..
2. ddns
ddns를 설정하면 되긴한다. 근데 ddns를 쓰려면 네트워크 유형을 bridge 모드로 해야된다. 그런데 bridge + 관리자 페이지 접속하려면 외부 접속 허용해줘야해서 보안에 안좋을 수 있다는 말도 있고 그리고 한 번 시도할 때 모든설정 (포트포워딩, ip 확인 등등)을 전부하고 bridge모드로 바꿔줘야한다. 만약 실수하면 다시 초기화하고 설정해야되서 귀찮았다. 그래서 그냥 라즈베리파이 서버에서 매직패킷을 전송해서 wol을 구현하쟈
1. go http 서버 구축
(라즈베리파이 설치 시, 라즈베리파이는 32bit니깐 주의해서 다운받자.. 처음에 64 bit 받아서 삽질했다 ㅠ.ㅠ)
파이썬으로 짤까 고민했는데 go 공부도 할겸 go로 작성했당
package main
import (
"fmt"
"net/http"
)
func hello(w http.ResponseWriter, r *http.Request) {
fmt.Println("Hello world!")
}
func main() {
server := http.Server{
Addr: "<ip-addr>:<port-number>",
Handler: nil,
}
http.HandleFunc("/hello", hello)
err := server.ListenAndServe()
if err != nil {
fmt.Println(err)
}
}
기본적인 go 서버를 이렇게 만들어서 테스트 해보자. 서버를 키고 라즈베리파이를 ssh에 접속해서..
curl <ip-addr>:<port-number>/hello
curl로 요청해주면
서버가 잘 동작하는것을 확인할 수 있땅.
2. http의 "/turnon" 요청을 받으면 매직패킷 전송
Wake On LAN 표준에 정의된 magic packet은 다음과 같은 구조를 같는 패킷이다. 다음과 같은 패킷을 만들고, 원하는 mac 주소를 네트워크에 전달만하면 된다. golang으로 다음과 같은 패킷을 만들어보자.
var prefix = []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
type MagicPacket struct {
Target net.HardwareAddr
Password []byte
}
func (p *MagicPacket) MarshalBinary() ([]byte, error) {
if len(p.Target) != 6 {
return nil, errors.New("invalid mac addr")
}
if pl := len(p.Password); pl != 0 && pl != 4 && pl != 6 {
return nil, errors.New("invalid password")
}
b := make([]byte, 6+(len(p.Target)*16)+len(p.Password))
copy(b[0:6], prefix)
hl := len(p.Target)
for i := 0; i < 16; i++ {
copy(b[6+(hl*i):6+(hl*i)+hl], p.Target)
}
copy(b[len(b)-len(p.Password):], p.Password)
return b, nil
}
func SendWakePacket(addr string, mac net.HardwareAddr) error {
target := net.UDPAddr{
IP: net.ParseIP(addr),
Port: 9,
}
udpConn, _ := net.ListenUDP("udp", nil)
mp := &MagicPacket{
Target: mac,
Password: nil,
}
mpb, err := mp.MarshalBinary()
if err != nil {
return err
}
_, err = udpConn.WriteToUDP(mpb, &target)
return err
}
나는 간단하게 udp로 매직패킷을 전송했다. 사실상 매직 패킷은 2계층으로 전송되가지고 핸드셰이크 과정이 있는(tcp)프로토콜만 아니면 크게 상관이 없다. go언어가 udp, tcp, packet 통신 빌트인 패키지를 잘 지원해주고 기능도 많아서 구현하는건 정말 쉬웠다. MagicPacket 스트럭쳐를 만들고, 표준대로 패킷을 생성해주고, mac 주소와 컴퓨터 주소를 전달 받으면 UDP 패킷을 전송하는 SendWakePacket 함수를 만들었다.
package main
import (
"fmt"
"MyServer/wol"
"net"
"net/http"
)
func hello(w http.ResponseWriter, r *http.Request) {
fmt.Println("Hello world!")
}
func sendMagicPacket(w http.ResponseWriter, r *http.Request) {
mac, err := net.ParseMAC(<mac-addr>) // string
if err != nil {
fmt.Println(err)
return
}
wol.SendWakePacket(<ip-addr>, mac)
}
func main() {
server := http.Server{
Addr: "<ip-addr>:<port-number>",
Handler: nil,
}
http.HandleFunc("/hello", hello)
http.HandleFunc("/turnon", sendMagicPacket)
err := server.ListenAndServe()
if err != nil {
fmt.Println(err)
}
}
이제 main.go 함수에 sendMagicPacket 핸들러 함수를 만들어서 전달해주면 끝이다!
라즈베리파이에서 실행해주고,
노트북에서 다음과 같이 요청을 해주면..!
wireshark를 통해 매직패킷이 전달된 것을 확인할 수 있다. (실제로 켜지는 것도 확인했다.)
* 아마 매직 패킷사용할 떄에는 BIOS설정이랑 네트워크 어댑터 설정이 필요하다.
- BIOS 설정 관련 블로그 : 202psj.tistory.com/1241
- 랜 설정 관련 블로그 : post.naver.com/viewer/postView.nhn?volumeNo=9851246&memberNo=15864833
* password는 wol on secure 의 표준인데 써볼라했는데 다음에 구현하기로 했디
cloud function을 쓰고 막 거창하게 하려고 했는데 사실상 좀 어려워서 스탭바이스탭으로 천천히 해야겠다.. 우선 다음 목표는 IR 리시버 센서 재구매, google assistant api를 라즈베리파이에 적용하는 것 이다. 조금 찾아보니 라즈베리파이는 USB마이크만 지원한다하고... 그냥 아날로그 입력 받는 게 없어서 회로를 따로 또 구성해야한다고 해서 조만간 usb마이크 구매해야겠다.
reference
udp 과련 패키지 : golang.org/pkg/net/
'라즈베리파이' 카테고리의 다른 글
[라즈베리파이] 우리집 iot 허브 만들기 - 1 (os 설치, 와이파이 및 고정ip, ssh 설정) (1) | 2020.09.25 |
---|