안경잡이개발자

728x90
반응형

  USB 장치(device)는 태생적으로 하나의 장치가 여러 개의 기능(function)을 제공할 수 있도록 만들어졌다. 참고로 여기에서 기능(function)이란 USB 프로토콜 상에서 인터페이스(interface)를 의미한다. 예를 들어 마우스(mouse), 키보드(keyboard), HID 장치, 대용량 저장소(mass storage) 등은 각각 하나의 기능(function)이다. 이후에 각 기능은 한 개 이상의 엔드포인트(endpoint)를 가질 수 있으며, 이를 통해 호스트(host) PC와 USB 프로토콜에 맞게 데이터를 주고받는다.

 

출처: Texas Instruments 교육 자료

 

  이번 포스팅에서는 Raspberry Pi Zero W를 이용해 멀티 가젯(multi-gadget) USB 장치를 만드는 방법을 소개하고자 한다. 우리의 라즈베리 파이를 일종의 USB 장치로 쓰면서, 동시에 대용량 저장소나 마우스 등의 다양한 기능을 제공하는 기기가 될 수 있도록 하는 것이다.

 

※ 기초 환경 설정 ※

 

  가장 먼저 USB OTG 기능을 제공하기 위해 라즈베리 파이의 /boot/config.txt 파일을 열어서 제일 마지막 줄에 다음의 코드를 추가한다. dwc2 USB 드라이버를 사용할 수 있도록 하는 것이다.

 

dtoverlay=dwc2

 

  Raspberry Pi Zero W를 기준으로 다음과 같다.

 

 

  이어서 /etc/modules의 마지막 줄에 다음 두 개의 커널 모듈을 넣어주자. 부팅 과정에서 아래의 두 모듈을 자동으로 로드하여 사용할 수 있도록 한다. 만약 부팅과 동시에 멀티 가젯 기능을 제공하지는 않고 수동적으로 필요할 때마다 기능을 제공하고 싶다면 libcomposite는 빼고 넣어주도록 하자.

dwc2 
libcomposite 

 

 

  이때 libcomposite 커널 모듈이 핵심적인 역할을 수행한다. 이 모듈은 여러 개의 가젯(gadget)을 한꺼번에 활성화 할 수 있도록 해준다. 일반적으로 USB 가젯(gadget) 모드란, 리눅스 시스템을 호스트 컴퓨터의 입장에서 보았을 때 USB 장치(device)인 것처럼 보이게 하는 방법을 의미한다. 우리의 Raspberry Pi를 HID 장치나 Mass Storage Class (MSC)처럼 보이게 만들 수 있다.

 

  이러한 배경에서 USB 가젯 기능을 제공하되, 한 번에 여러 개의 USB 디바이스 기능을 제공하려면 libcomposite 모듈을 사용할 수 있다. 이를 이용하면 나의 Raspberry Pi Zero W가 동시에 3개의 USB 대용량 저장소이면서, 그와 동시에 마우스와 키보드의 역할을 수행하도록 만들 수 있다. 이처럼 간단하게 여러 개의 USB 장치 역할을 수행하도록 만들 수 있다.

 

※ 멀티 가젯(Multi-gadget) 모드 확인 ※

 

  이제 /sys/kernel/config/usb_gadget/ 폴더를 활성화해야 한다. 이 폴더 안에서 규칙에 맞게 생성된 파일들을 통해 실제로 multi-gadget 역할을 수행할 수 있다. 이 폴더는 libcomposite 모듈을 커널에 적재함으로써 활성화할 수 있다. 앞서 우리는 재부팅을 했을 때 리눅스 커널 모듈 중에서 libcomposite이 불러와지도록 만들었다. 자동으로 모듈을 불러오도록 설정하지 않았다면 modprobe 명령을 이용해 수동으로 적재하자.

 

  modprobe 명령어커널 모듈(kernel module)을 리눅스 커널에 추가하거나 커널로부터 제거할 때 사용할 수 있다. 원래 모듈을 추가하거나 제거할 때는 insmod나 rmmod를 사용할 수 있는데, modprobe는 더욱 다양한 기능을 제공한다. modprobe는 모듈을 올릴 때 여러 개의 모듈을 한꺼번에 올릴 수 있으며, 의존성이 있는 모듈도 같이 적재할 수 있다.

 

  재부팅하지 않고 바로 libcomposite을 적재하기 위해서는 다음과 같은 명령어를 입력하면 된다. (/etc/modules에 libcomposite을 추가했다면 sudo reboot 명령을 통해 재부팅 하자.)

 

sudo modprobe libcomposite

 

  이후에 /sys/kernel/config/usb_gadget 경로가 생긴 것을 확인할 수 있다. 이제 이 경로에 각종 환경 설정 파일을 생성하여 우리의 라즈베리 파이가 USB 가젯으로 동작하게 만들 수 있다.

 

 

※ 환경 설정 스크립트 만들기 ※

 

  Texas Instruments 교육 자료를 보면 USB 멀티 가젯(multi-gadget) 구조를 쉽게 확인할 수 있다. 바로 다음과 같은 형태로 파일을 배치하면 그에 맞게 다양한 기능(function)이 동작한다.

 

출처: Texas Instruments 교육 자료

 

  따라서 이러한 구조에 맞게 파일을 생성하는 하나의 환경 설정 스크립트를 작성하는 것이 좋다.

 

sudo touch /usr/bin/my_usb # 파일 생성
sudo chmod +x /usr/bin/my_usb # 실행할 수 있도록 만들기

 

 

  이제 이러한 환경 설정 스크립트를 작성해보자.

 

sudo nano /usr/bin/my_usb # 스크립트 작성하기

 

가장 먼저 global configuration에 대하여 작성할 필요가 있다. 앞서 말했듯이 하나의 configuration 안에 여러 개의 기능(function)이 들어가 있는 형태로 동작한다. 따라서 일단 기능(function) 부분은 제외하고 configuration 관련 내용을 작성하여 전체적인 틀을 잡도록 하자.

 

#!/bin/bash
cd /sys/kernel/config/usb_gadget/
mkdir -p my_usb
cd my_usb

# 기본적인 USB 클래스 명시
echo 0x1d6b > idVendor # Linux Foundation
echo 0x0104 > idProduct # Multifunction Composite Gadget
echo 0x0100 > bcdDevice # v1.0.0
echo 0x0200 > bcdUSB # USB2

# 내가 만들 USB 장치의 기본적인 이름
mkdir -p strings/0x409
echo "0123456789abcdef" > strings/0x409/serialnumber
echo "Dongbin Na" > strings/0x409/manufacturer
echo "My USB" > strings/0x409/product

# 하나의 Configuration 정보 작성
mkdir -p configs/c.1/strings/0x409
echo "My USB Config 1" > configs/c.1/strings/0x409/configuration
echo 250 > configs/c.1/MaxPower

# 실질적으로 USB 기능(function)들이 들어갈 공간

# UDC (Usb Device Controller)
ls /sys/class/udc > UDC

 

※ 대용량 저장소(Mass Storage) 기능 추가하기 ※

 

  필자는 개인적으로 Mass Storage Class (MSC) 기능이 필요한 상황이다. 따라서 Mass Storage 기능을 추가하는 방법을 소개한다. 일단 가장 먼저 블록 장치(block device)를 만들 필요가 있다. 라즈베리파이를 USB 대용량 저장소로 보이도록 만들고 싶다면, 호스트(host) PC 입장에서는 블록 단위로 접근할 필요가 있기 때문이다.

 

  필자는 다음과 같이 2개의 이미지를 만들었다. 이미지 내에서 하나의 블록 크기(block size)는 1,048,576 바이트(1Mb)로 설정했다. 이후에 mkdosfs 명령어를 이용해 파일 시스템 형태로 포맷(format)할 수 있도록 한다.

 

mkdir -p /home/pi/images

# 1Gb 이미지 생성
dd if=/dev/zero of=/home/pi/images/usbdisk1.img bs=1048576 count=1024
mkdosfs /home/pi/images/usbdisk1.img

# 500Mb 이미지 생성
dd if=/dev/zero of=/home/pi/images/usbdisk2.img bs=1048576 count=512
mkdosfs /home/pi/images/usbdisk2.img

 

 

  이제 기능(function) 부분에 다음과 같은 코드를 추가하자. 필자는 다음과 같이 총 2개의 Mass Storage를 제공할 수 있도록 2개의 기능을 명시했다.

 

################# Mass Storage 1 #################
# 첫 번째 이미지 지정하기
FILE1=/home/pi/images/usbdisk1.img

# 해당 이미지에 마운트(mount) 진행하기
mkdir -p ${FILE1/img/d}
mount -o loop,ro, -t vfat $FILE1 ${FILE1/img/d} # 라즈베리 파이 입장에서는 읽기 전용(read-only)

# USB 기능(function) 작성하기 (kernel.org reference 참고하기)
mkdir -p functions/mass_storage.usb0
echo 1 > functions/mass_storage.usb0/stall
echo 0 > functions/mass_storage.usb0/lun.0/cdrom
echo 0 > functions/mass_storage.usb0/lun.0/ro # 호스트 입장에서는 쓰기 가능
echo 0 > functions/mass_storage.usb0/lun.0/nofua
echo $FILE1 > functions/mass_storage.usb0/lun.0/file # 이미지 파일 명시하기

# 만들어진 기능(function) 사용할 수 있도록 설정
ln -s functions/mass_storage.usb0 configs/c.1/

################# Mass Storage 2 #################
# 두 번째 이미지 지정하기
FILE2=/home/pi/images/usbdisk2.img

# 해당 이미지에 마운트(mount) 진행하기
mkdir -p ${FILE2/img/d}
mount -o loop,ro, -t vfat $FILE2 ${FILE2/img/d} # 라즈베리 파이 입장에서는 읽기 전용(read-only)

# USB 기능(function) 작성하기 (kernel.org reference 참고하기)
mkdir -p functions/mass_storage.usb1
echo 1 > functions/mass_storage.usb1/stall
echo 0 > functions/mass_storage.usb1/lun.0/cdrom
echo 0 > functions/mass_storage.usb1/lun.0/ro # 호스트 입장에서는 쓰기 가능
echo 0 > functions/mass_storage.usb1/lun.0/nofua
echo $FILE2 > functions/mass_storage.usb1/lun.0/file # 이미지 파일 명시하기

# 만들어진 기능(function) 사용할 수 있도록 설정
ln -s functions/mass_storage.usb1 configs/c.1/

 

※ 두 개의 Mass Storage 장치 전체 스크립트 ※

 

  두 개의 Mass Storage를 이용하는 예제의 전체 /usr/bin/my_usb 스크립트 최종본은 다음과 같다.

 

#!/bin/bash
cd /sys/kernel/config/usb_gadget/
mkdir -p my_usb
cd my_usb

# 기본적인 USB 클래스 명시
echo 0x1d6b > idVendor # Linux Foundation
echo 0x0104 > idProduct # Multifunction Composite Gadget
echo 0x0100 > bcdDevice # v1.0.0
echo 0x0200 > bcdUSB # USB2

# 내가 만들 USB 장치의 기본적인 이름
mkdir -p strings/0x409
echo "0123456789abcdef" > strings/0x409/serialnumber
echo "Dongbin Na" > strings/0x409/manufacturer
echo "My USB" > strings/0x409/product

# 하나의 Configuration 정보 작성
mkdir -p configs/c.1/strings/0x409
echo "My USB Config 1" > configs/c.1/strings/0x409/configuration
echo 250 > configs/c.1/MaxPower

################# Mass Storage 1 #################
# 첫 번째 이미지 지정하기
FILE1=/home/pi/images/usbdisk1.img

# 해당 이미지에 마운트(mount) 진행하기
mkdir -p ${FILE1/img/d}
mount -o loop,ro, -t vfat $FILE1 ${FILE1/img/d} # 라즈베리 파이 입장에서는 읽기 전용(read-only)

# USB 기능(function) 작성하기 (kernel.org reference 참고하기)
mkdir -p functions/mass_storage.usb0
echo 1 > functions/mass_storage.usb0/stall
echo 0 > functions/mass_storage.usb0/lun.0/cdrom
echo 0 > functions/mass_storage.usb0/lun.0/ro # 호스트 입장에서는 쓰기 가능
echo 0 > functions/mass_storage.usb0/lun.0/nofua
echo $FILE1 > functions/mass_storage.usb0/lun.0/file # 이미지 파일 명시하기

# 만들어진 기능(function) 사용할 수 있도록 설정
ln -s functions/mass_storage.usb0 configs/c.1/

################# Mass Storage 2 #################
# 두 번째 이미지 지정하기
FILE2=/home/pi/images/usbdisk2.img

# 해당 이미지에 마운트(mount) 진행하기
mkdir -p ${FILE2/img/d}
mount -o loop,ro, -t vfat $FILE2 ${FILE2/img/d} # 라즈베리 파이 입장에서는 읽기 전용(read-only)

# USB 기능(function) 작성하기 (kernel.org reference 참고하기)
mkdir -p functions/mass_storage.usb1
echo 1 > functions/mass_storage.usb1/stall
echo 0 > functions/mass_storage.usb1/lun.0/cdrom
echo 0 > functions/mass_storage.usb1/lun.0/ro # 호스트 입장에서는 쓰기 가능
echo 0 > functions/mass_storage.usb1/lun.0/nofua
echo $FILE2 > functions/mass_storage.usb1/lun.0/file # 이미지 파일 명시하기

# 만들어진 기능(function) 사용할 수 있도록 설정
ln -s functions/mass_storage.usb1 configs/c.1/

# UDC (Usb Device Controller)
ls /sys/class/udc > UDC

 

  최종적으로 대략 다음과 같이 작성된다. (후반부 생략)

 

 

  이제 이러한 스크립트를 sudo 권한으로 실행하면 곧바로 USB 멀티 가젯 기능이 제공된다.

 

 

  이후에 호스트(host) PC에 연결하면 다음과 같이 정상적으로 대용량 저장소로 인식된다. 윈도우(Windows)와 우분투(Ubuntu) 모두에서 정상적으로 인식되는데, 윈도우 장치 관리자로 보았을 때는 다음과 같다.

 

 

  앞서 만들었던 두 개의 이미지(image)가 정상적으로 인식된다.

 


※ 내 스크립트가 자동으로 실행될 수 있도록 하기 ※

 

  또한 라즈베리파이가 재부팅되면 곧바로 스크립트가 실행되도록 설정할 수 있다. (단, 이게 정상적으로 동작하려면 앞서 /etc/modules에 libcomposite을 추가한 상태여야 한다.) /etc/rc.local을 수정하자. 리눅스는 부팅을 진행하는 과정에서 /etc/rc.local 스크립트를 자동으로 실행한다.

 

sudo nano /etc/rc.local

 

  이후에 exit 0 위쪽 라인에 /usr/bin/my_usb를 추가하자. 라즈베리파이 구동 이후에 자동으로 우리가 작성했던 스크립트를 구동하도록 만드는 것이다.

 

 

  이제 재부팅(reboot)을 진행한다.

 

 

  그러면 재부팅 이후에는 라즈베리파이를 연결만 하면 자동으로 Mass Storage 형태로 인식이 된다.

 

 

  다만 간혹 두 개의 Mass Storage를 이용하도록 했으나 하나만 인식되는 문제가 발생하거나, 연결이 되었다가 안 되었다가 하는 문제가 발생할 수 있다. 이럴 때는 데이터 공급선(USB OTG)이 정상적으로 연결되어 있는지를 다시 한 번 체크해보도록 하자. 혹은 호스트(host) PC의 다른 포트로 연결하면 잘 동작하는 경우가 있다.

 

※ 동적으로 Mass Storage 이미지 변경하기 ※

 

  Mass Storage 목적으로 리눅스 가젯(gadget) 드라이버를 활용한다면, 다양한 기능에 대하여 추가적으로 알아 두면 좋다. 참고로 Mass Storage의 경우 동적으로 블록 장치(block device)를 변경할 수 있다. 동적으로 블록 장치를 변경해 보기 위하여 하나의 블록 장치를 만들어 보자.

 

# 500Mb 이미지 생성
dd if=/dev/zero of=/home/pi/images/usbdisk3.img bs=1048576 count=512
mkdosfs /home/pi/images/usbdisk3.img

 

  이후에 Mass Storage 기능이 제공되고 있는 상황에서 다음과 같은 명령어를 입력하여 동적으로 블록 장치 이미지를 변경할 수 있다.

 

sudo bash -c 'echo "/home/pi/usbdisk3.img" > /sys/kernel/config/usb_gadget/my_usb/functions/mass_storage.usb0/lun.0/file'

 

※ 동적으로 멀티 가젯(Gadget) 기능 올리거나 내리기 ※

 

  라즈베리파이를 호스트(host) PC에 연결한 상태에서 원할 때 멀티 가젯 기능을 열거나 닫는 작업도 가능하다. 다음과 같이 예시로 2개의 Mass Storage 기능을 제공하는 라즈베리파이가 있다고 해보자.

 

 

  가젯(gadget) 기능을 enabling 하기 위해서는 UDC(USB Device Controller)에 바운딩을 시킬 필요가 있다. 같은 원리로 disabling을 할 때는 바운딩을 해제하면 되는 것이다. 따라서 다음과 같은 명령을 실행하면 전원은 공급된 상태에서 가젯 기능만 뺄 수 있다.

 

sudo bash -c 'echo "" > /sys/kernel/config/usb_gadget/my_usb/UDC'

 

 

  다음의 명령어를 입력하면 다시 가젯 기능이 활성화된다.

 

sudo bash -c 'ls /sys/class/udc > /sys/kernel/config/usb_gadget/my_usb/UDC'

 

※ 동적으로 특정 가젯(Gadget) 올리거나 내리기 ※

 

  1) 동적으로 특정한 가젯을 새롭게 올리기

 

  그렇다면 라즈베리파이호스트(host) PC에 연결한 상태에서 원할 때 특정한 가젯 기능만을 열거나 닫고자 한다면 어떻게 할 수 있을까? 사실 우리가 Mass Storage USB를 이용할 때는, 그냥 물리적으로 USB를 PC에서 빼면 연결이 해제된다. 하지만 라즈베리파이에서 여러 개의 가젯(gadget) 기능을 제공하고 있을 때는 어떻게 하나의 기능만 제거할 수 있을지 알아보자. 현재의 라즈베리파이가 다음과 같이 2개의 MSC를 제공한다고 해보자.

 

 

  이제 간단히 하나의 블록 장치를 만들어 보자.

 

# 500Mb 이미지 생성
dd if=/dev/zero of=/home/pi/images/usbdisk4.img bs=1048576 count=512
mkdosfs /home/pi/images/usbdisk4.img

 

  이후에 라즈베리파이에서 다음의 코드를 실행해보자.

 

# 특정 이미지에 마운트(mount) 진행하기
sudo mkdir -p /home/pi/images/usbdisk4.d
sudo mount -o loop,ro, -t vfat /home/pi/images/usbdisk4.img /home/pi/images/usbdisk4.d # 라즈베리 파이 입장에서는 읽기 전용(read-only)

# USB 기능(function) 작성하기 (kernel.org reference 참고하기)
sudo mkdir -p /sys/kernel/config/usb_gadget/my_usb/functions/mass_storage.usb2
sudo bash -c 'echo 1 > /sys/kernel/config/usb_gadget/my_usb/functions/mass_storage.usb2/stall'
sudo bash -c 'echo 0 > /sys/kernel/config/usb_gadget/my_usb/functions/mass_storage.usb2/lun.0/cdrom'
sudo bash -c 'echo 0 > /sys/kernel/config/usb_gadget/my_usb/functions/mass_storage.usb2/lun.0/ro' # 호스트 입장에서는 쓰기 가능
sudo bash -c 'echo 0 > /sys/kernel/config/usb_gadget/my_usb/functions/mass_storage.usb2/lun.0/nofua'
sudo bash -c 'echo /home/pi/images/usbdisk4.img > /sys/kernel/config/usb_gadget/my_usb/functions/mass_storage.usb2/lun.0/file' # 이미지 파일 명시하기

# 만들어진 기능(function) 사용할 수 있도록 설정
sudo ln -s /sys/kernel/config/usb_gadget/my_usb/functions/mass_storage.usb2 /sys/kernel/config/usb_gadget/my_usb/configs/c.1/

 

  단순히 위 코드를 실행하는 것으로는 동적으로 반영되지 않는 것을 알 수 있다. 다음의 두 코드를 추가적으로 실행하여 UDC를 재동작 시켰을 때 비로소 정상적으로 동작한다. 참고로 단순히 호스트(Host) PC와 라즈베리파이 사이의 데이터 케이블을 연결을 해제하고 다시 연결하는 작업만으로는 정상적으로 새 기능이 추가되지 않는다.

 

  반면에 /sys/kernel/config/usb_gadget/my_usb/UDC의 값을 공백으로 바꾸었다가 다시 정상적인 UDC 값을 기입하면 새 기능이 추가되어 재연결 된 것을 확인할 수 있다.

 

sudo bash -c 'echo "" > /sys/kernel/config/usb_gadget/my_usb/UDC'
sudo bash -c 'ls /sys/class/udc > /sys/kernel/config/usb_gadget/my_usb/UDC'

 

  아무튼 케이블을 뺐다가 다시 끼지 않더라도 기능을 변경할 수 있다는 점은 확실하다.

 

 

  2) 동적으로 특정한 가젯을 내리기

 

  특정한 가젯을 내릴 때에도 비슷하다. configuration에서 바운딩 된 mass_storage.usb2 기능(function)을 다음과 같은 명령어로 동적으로 제거할 수 있다.

 

sudo rm /sys/kernel/config/usb_gadget/my_usb/configs/c.1/mass_storage.usb2

 

  물론 이 명령을 실행하면 configuration 정보가 바뀌기 때문에 UDC의 값이 자동으로 공백으로 바뀌어 호스트(Host) PC와의 연결이 해제될 수 있다. 따라서 가젯을 제거한 뒤에는 다음과 같이 UDC 값을 갱신할 필요가 있다.

 

sudo bash -c 'ls /sys/class/udc > /sys/kernel/config/usb_gadget/my_usb/UDC'

 

  결과적으로 호스트 PC는 다시 USB를 인식하고, 하나의 Mass Storage 기능이 정상적으로 제거된 것을 알 수 있다.

 

 

※ (추가) Serial로 터미널 접속하기 ※

 

  Serial을 열어서 터미널로 접속할 수도 있다. (사실 굳이 사용할 이유가 없는 경우가 많다.) 이를 위해 다음과 같은 내용을 환경 설정 파일에 추가한다. 그리고 재부팅을 하면 라즈베리파이에 /dev/ttyGS0 디바이스가 생성된다. 이 디바이스를 이용하여 통신이 가능하다.

 

mkdir -p functions/acm.usb0
ln -s functions/acm.usb0 configs/c.1/

 

  이어서 라즈베리파이에서 USB 시리얼 콘솔 접속을 활성화 해주자.

 

sudo systemctl enable getty@ttyGS0.service

 

  호스트(host) PC 입장에서는 다음과 같이 PuTTY를 이용하여 접속할 수 있다. 윈도우(Windows)의 경우 장치 관리자에서 COM 포트 번호를 확인한 뒤에 115200 레이트로 접속하면 된다.

 

 

  그러면 다음과 같이 성공적으로 접속된다.

 

 

※ (추가) Serial로 통신하기 ※

 

  Serial을 터미널 접속용이 아니라 데이터 주고 받기용으로 사용할 수도 있다. 설정 파일에 다음의 내용을 추가하고 재부팅 해보자.

 

mkdir -p functions/acm.usb1
ln -s functions/acm.usb1 configs/c.1/

 

  그러면 라즈베리파이에서는 /dev/ttyGS1이 추가적으로 생성된다. 이제 이걸로 데이터를 주고 받아보도록 하자.

 

  1) 호스트가 데이터를 보낼 때

 

  호스트의 파이썬 3.7 프로그램은 다음과 같다.

 

import serial

values = [1, 2, 3, 4, 5, 6, 7, 8, 9]

s = serial.Serial("COM7", 115200)
s.write(b'Hello\n')
s.close()

 

  라즈베리파이 쪽에서는 다음과 같이 /dev/ttyGS1에 적힌 내용을 확인하면 된다.

 

 

  2) 호스트가 데이터를 받을 때

 

  호스트의 파이썬 3.7 프로그램은 다음과 같다.

 

import serial

s = serial.Serial("COM7", 115200)

while True:
    data = s.readline()
    print(data)

s.close()

 

  라즈베리파이 쪽에서는 다음과 같이 /dev/ttyGS1를 통해 데이터를 전송하면 된다.

 

 

  그러면 다음과 같이 호스트 쪽에서는 데이터를 받을 수 있게 된다.

 

 

  이어서 라즈베리파이가 보내는 데이터를 있는 대로 계속 받아서 파일에 바이트 스트림(byte stream)으로 저장하려면 다음과 같이 하면 된다.

 

import serial

s = serial.Serial("COM7", 115200)
total = 0

while True:
    len = s.inWaiting()
    if len == 0:
        continue
    f = open("log.dat", "ab")
    data = s.read(len)
    f.write(data)
    f.close()
    total += len
    print(f"total length:", total)

s.close()

 

  그러면 파이썬 프로그램이 계속 실행되고, 라즈베리파이로부터 받은 데이터를 그대로 log.dat에 계속 쓴다.

 

 

  (참고) Serial을 데이터 송수신 목적으로 사용하는 경우에는 raw 모드로 데이터를 전송할 필요가 있다. 그렇지 않으면 \b10을 보냈을 때 \b13\b10의 형태로 보내지는 문제가 발생할 수 있다.

 

※ Mass Storage Class를 Boot Device로 사용할 때 ※

 

  이 케이스는 흔한 케이스는 아니지만, 필자는 연구 목적으로 Raspberry Pi의 Mass Storage Class를 Boot Device로 사용해 본 경험이 있다. 만약 Raspberry Pi Zero W를 부팅 디스크로 사용한다고 하면, 호스트(host) PC의 UEFI 단계에서 Raspberry Pi를 부팅 디스크로 잡아 OS를 부팅하게 된다. 다만 이때 Multi Composite Gadget을 이용하면 OS 부팅 단계에서 오류가 발생할 수 있다.

 

  구체적으로 필자는 g_mass_storage를 올릴 때만 동작하고, Multi Composite Gadget을 사용했을 때 Booting이 안 되는 문제가 발생했었다. g_mass_storage 모듈을 이용했을 때 USB 장치의 기본 Vendor ID는 0x0525, Product ID는 0xA4A5이다. 반면에 Composite Gadget은 일반적으로 Vendor ID 0x1D6B, Product ID 0x0104를 사용한다.

 

  100% 확신하는 것은 아니지만, OS 부팅 과정에서 Well-known이 아닌 Identifier를 사용하면 OS를 올리는 과정에서 USB Reconnection이 이루어지며, 이 과정에서 Mass Storage와의 연결이 일시적으로 해제되어 부팅이 안 되었던 것으로 보인다. 그래서 기존의 Raspberry Pi의 config 파일에서 Vendor ID와 Product ID를 g_mass_storage와 동일하게 맞추어 실행했더니 문제가 해결된 적이 있었다. 이렇게 해도 Multi Composite Gadget의 기능을 가지며 정상 동작하는 것을 확인했다.

728x90
반응형

728x90
반응형

  라즈베리파이(Raspberry Pi) Zero W가성비가 매우 뛰어난 소형 보드로 유명합니다. 저는 현재 참여하고 있는 프로젝트에서 라즈베리파이를 사용해야 해서 이 모델을 골라 사용하고 있습니다. Raspberry Pi Zero W는 다음과 같이 생겼습니다. SD 카드에 라즈베리파이 운영체제(OS)를 설치한 뒤에 라즈베리파이에 꽂아서 부팅하면 됩니다.

 

 

※ 스펙(Specification) 소개 ※

  ▶ 802.11 b/g/n wireless LAN: 와이파이(Wi-Fi)를 사용할 수 있습니다.
  ▶ 블루투스(Bluetooth)
  ▶ 1GHz, single-core CPU: 다른 소형 보드와 비교했을 때, 가격 대비 괜찮은 성능의 CPU가 탑재되어 있습니다.
  ▶ 512MB RAM: 간단한 웹 서버를 올릴 수도 있는 크기의 메모리(RAM)입니다. 
  ▶ USB OTG: USB 디바이스 기능을 지원합니다. (라즈베리파이를 이용한 마우스, 키보드 개발 등이 가능)
  ▶ Micro USB Power: 충전 목적의 포트입니다. 다만 전원용 USB의 전압(voltage)은 반드시 5-5.25V 사이가 되어야 합니다.  일반적인 USB 충전기의 경우 5V 전압이므로, 그냥 편의점에서 파는 마이크로 USB를 꽂아 쓰면 됩니다.
  ▶ CSI Camera Connector: CSI 포트가 존재하므로 라즈베리파이 카메라 모듈을 사용할 수 있습니다. IoT 사물 인식 기기, CCTV 기기 등을 개발할 때 효과적으로 사용됩니다.

 

※ 라즈베리파이의 일반적인 사용 형태 ※

 

  참고로 Raspberry Pi Zero W의 경우 헤드리스(headless) 서버 설정이 가능하기 때문에, 사실 Mini HDMI 케이블을 구매하지 않아도 간단히 서버로 사용이 가능합니다. 아마 단순히 라즈베리파이를 간단한 서버용으로 사용하시는 분들은 다음과 같은 형태로 사용하실 겁니다. SD카드에 우분투(Ubuntu)를 구워 넣고, 마이크로 USB로 전원을 공급하고 있는 모습입니다. 라즈베리파이 OS의 경우에 SSH 접속을 지원하기 때문에, SSH를 열어 놓고 외부에서 접속해 사용하는 방식으로 편하게 사용할 수 있습니다.

 

 

※ Raspberry Pi Zero W 구매 방법 ※

 

  Raspberry Pi Zero W는 Amazon이나 Canakit과 같은 사이트를 통해 구매할 수 있습니다. 또한 일반적인 경우 보드(Board)만 구매하는 경우에는 1개씩만 구매할 수 있도록 하는 경우가 많습니다. 사실 10달러($)에 이 정도의 보드를 살 수 있다는 것 자체가 매우 좋은 조건이긴 합니다.

 

 

  참고로 스타터 키트(Starter Kit)의 경우 다음과 같은 구성을 가지고 있습니다. 확인해 보시면 Mini HDMI 어댑터(adapter)와 USB OTG 케이블(cable)을 가지고 있습니다. 이걸로 모니터와 마우스를 연결할 수 있기 때문에, 헤드리스(headless) 서버가 아니라 하나의 고유한 컴퓨터처럼 라즈베리 파이를 이용하고 싶으신 분들은 스타터 키트를 구매하시는 것이 편할 수 있습니다.

 

 

※ 라즈비안(Raspbian) OS 설치하기 ※

 

  라즈베리파이는 기본적으로 SD 카드를 꽂을 공간을 가지고 있습니다. 따라서 SD 카드에 운영체제(OS)를 설치하면 됩니다. 가장 대표적인 운영체제로 라즈비안(Raspbian) OS를 설치할 수 있습니다.

 

▶ 라즈비안(Raspbian) 운영체제 설치 경로https://www.raspberrypi.org/downloads/raspbian/

 

Download Raspberry Pi OS for Raspberry Pi

Raspberry Pi OS (previously called Raspbian) is the Foundation's official supported operating system. Install it with NOOBS or download the image below.

www.raspberrypi.org

 

  다음과 같이 최소한의 정보만 담은 Lite 버전으로 OS를 설치하여 사용할 수 있습니다. [Download ZIP] 버튼을 눌러  다운로드를 진행합니다. 대개 3분~5분 정도면 다운로드가 완료되며, 압축된 파일은 500MB가 안 되지만 실제 이미지(image) 파일은 2GB 정도입니다.

 

 

※ SD 카드에 라즈비안(Raspbian) OS 굽기 ※

 

  라즈비안(Raspbian)을 OS에 굽기 위해서는 플래시(Flash) 프로그램을 사용할 수 있습니다.

 

  ▶ Etcher 설치 경로: www.balena.io/etcher/

 

  자신의 운영체제에 맞는 설치 프로그램을 받아서 설치를 진행하시면 됩니다. 저는 다음과 같이 윈도우(Windows) 운영체제에 맞는 프로그램을 설치했습니다.

 

 

  이어서 설치를 진행하시면 됩니다.

 

 

  이후에 Etcher 프로그램을 이용하여 라즈베리파이 이미지 파일을 선택하고, 플래시(flash)를 진행하면 됩니다. 이를 위해 SD 카드 어댑터(adapter)를 이용하여 PC에 SD 카드를 연결해야 합니다. 일반적인 컴퓨터나 노트북은 SD 카드 어댑터를 이용해 SD 카드를 연결하게 되어 있습니다.

 

 

  Etcher 프로그램을 실행하여 [Flash from file]을 눌러 라즈베리파이 이미지를 선택합니다.

 

 

  그리고 [Selected target] 버튼을 눌러 SD 카드를 선택하고 플래시(flash) 버튼을 누릅니다.

 

 

  플래시(flash) 작업이 완료되면 SD 카드에 라즈베리파이 OS가 설치가 완료됩니다.

 

 

  이제 완료되면 부트 폴더를 확인할 수 있습니다. 윈도우(Windows) 운영체제에서는 부트 폴더가 자동으로 마운트 되며, 용량이 256MB라고 나오네요. 이제 이러한 부트 폴더에 환경 설정 파일을 넣어서 라즈베리파이가 특정 환경에 맞게 부팅되도록 만들 수 있습니다.

 

 

※ 헤드리스(headless) 서버로 사용하기 ※

 

  Mini HDMI 케이블과 USB OTG 케이블을 가지고 있으신 분들은 직접 모니터와 키보드를 연결하여 사용할 수 있습니다. 다만 그러한 케이블이 없다면 SSH로 라즈베리파이에 접속하여 사용할 수 있습니다. 라즈베리파이에 SSH로 접속하기 위해서는 가장 먼저 boot 폴더에 ssh라는 이름의 파일을 만들어 주어야 합니다. 이로써 라즈베리파이가 부팅될 때 SSH 포트가 열리게 됩니다.

 

 

  또한 라즈베리파이 Zero W가 와이파이(Wi-Fi)를 사용할 수 있도록 하기 위해 와이파이 정보를 기재할 필요가 있습니다. 이를 위해 wpa_supplicant.conf 파일을 생성한 뒤에 메모장으로 열어 내용을 기입합시다. 윈도우(Windows) 운영체제의 경우 단순히 워드패드메모장으로 열어 쓰시면 됩니다.

 

 

  바로 다음의 형식에 맞게 와이파이(Wi-Fi) 정보를 기입하시면 됩니다. 참고로 netsh 명령어를 이용하면 현재 내 컴퓨터나 노트북에 저장되어 있는 와이파이 비밀번호 정보를 알아낼 수 있습니다.

 

country=US
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1

network={
    ssid="SSID"
    psk="비밀번호"
    key_mgmt=WPA-PSK
}

 

  결과적으로 컴퓨터에서 SD 카드를 제거한 뒤에 라즈베리파이에 꽂아서 부팅하시면 됩니다. 파워(power) 케이블을 연결하면 자동으로 부팅이 진행됩니다. 이후에 SSH로 접속하여 사용하면 됩니다. 접속하고자 하는 컴퓨터와 라즈베리파이를 동일 아이피 대역에 둔 뒤에 Advanced IP Scanner와 같은 프로그램을 이용해 라즈베리파이의 IP 주소를 찾아서 접속하면 됩니다. Putty나 XShell 같은 프로그램을 이용해 접속하면 됩니다. 예를 들어 라즈베리파이의 IP 주소가 192.168.43.78로 나온다면 다음과 같이 입력하여 접속할 수 있습니다.

 

 

  기본 설정 그대로 이용할 때 SSH 접속 시 아이디pi이며, 비밀번호raspberry입니다.

 

 

  참고로 WiFi 관련 설정 파일은 SSH 접속 이후에 /etc/wpa_supplicant/wpa_supplicant.conf 파일에서 확인할 수 있습니다. 또한 실제 IP 주소는 ifconfig wlan0 커맨드를 입력해 확인하실 수 있습니다.

 

※ 로컬 SSH로 Raspberry Pi 접속하기 ※

 

  기본적으로 마찬가지로 boot 폴더에 ssh라는 이름의 파일을 생성해 준 뒤에, Raspberry Pi에 SD 카드를 꽂습니다. 그리고 Raspberry Pi 데이터 케이블을 PC에 연결합니다. putty와 같은 유틸리티를 활용하여 pi@raspberrypi.local에 접속합니다.

 

 

  접속이 된다면 성공입니다.

 

※ Serial Gadget을 이용해 Raspberry Pi 접속하기 ※

 

  소정의 이유로 현재 와이파이를 사용할 수 없는 상황일 수 있습니다. 혹은 무선 네트워크의 속도가 너무 느려서 접속이 어려울 수도 있습니다. 이럴 때는 USB OTG 기능을 이용하여 Raspberry Pi에 접속할 수 있습니다. 마찬가지로 SD 카드를 호스트 PC에 연결합니다. 그리고 /boot/config.txt 파일의 마지막 줄에 다음을 추가합니다.

 

dtoverlay=dwc2

 

  이후에 cmdline.txt를 열어서 rootwait 다음 부분에 한 칸(space bar)을 띄고 다음과 같이 작성합니다. g_serial 모듈을 로드(load) 함으로써 Serial 가젯(gadget)을 활성화할 수 있습니다.

 

modules-load=dwc2,g_serial

 

  cmdline.txt의 예시는 다음과 같습니다. 대략 다음과 같이 작성되었는지 확인합니다. 참고로 아래 커맨드는 사용하고 있는 장치마다 조금씩 다를 수 있으며, 필자의 경우 단순히 rootwait 다음에 모듈을 로드하는 내용만 추가했습니다.

 

dwc_otg.lpm_enable=0 console=serial0,115200 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait modules-load=dwc2,g_serial quiet init=/usr/lib/raspi-config/init_resize.sh

 

  이제 라즈베리파이를 실행하면 호스트(host) PC에 연결했을 때 정상적으로 COM 포트로 인식하는 것을 알 수 있습니다. 이후에 systemctl 명령어를 이용해 Serial 포트 기능을 활성화해 준 뒤에, 재부팅하고 호스트(host) PC에서 Raspberry Pi에 USB 시리얼로 접속하면 됩니다.

 

※ Raspberry Pi 고정 IP 이용하기 ※

 

  초기 세팅이 끝나고 나면 고정 IP를 이용하는 것이 편합니다. 이를 위해 Raspbery Pi에 접속하여 가장 먼저 DHCPCD가 동작하고 있는지 서비스(service) 상태를 확인합니다.

 

sudo service dhcpcd status

 

  정상적으로 동작하고 있다면, /etc/dhcpcd.conf 파일을 수정합니다. 가장 아랫줄에 다음과 같은 내용을 추가하면 됩니다. 필자의 경우 무선 네트워크를 사용하므로 wlan0으로 설정했습니다.

 

interface wlan0

static ip_address=192.168.0.177/24
static routers=192.168.0.1
static domain_name_servers=8.8.8.8

 

  이때 네트워크 환경에 따라서 domain_name_server를 자신의 회사/학교에서 제공하는 IP로 설정해야 할 필요가 있습니다. 필자의 경우 8.8.8.8만 설정했을 때 네트워크가 정상적으로 동작하지 않았습니다. 아무튼 설정 이후에는 재부팅하여 ifconfig를 입력하여 정상적으로 IP가 변경되었는지 확인합니다.

728x90
반응형