Raspberry Pi Zero W를 이용한 USB Gadget Serial 데이터 전송 예제
Raspberry Pi Zero W는 Serial 통신 목적의 USB 디바이스로 사용할 수 있다. 한 번 2개의 Serial 인터페이스를 제공하는 USB 디바이스를 만들어 보았다. 기본적인 환경 설정 파일은 다음과 같다. 경로는 /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 # USB 2.0
# 내가 만들 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
# 하나의 Serial 통신 기능 추가
mkdir -p functions/acm.usb0
ln -s functions/acm.usb0 configs/c.1/
# 하나의 Serial 통신 기능 추가
mkdir -p functions/acm.usb1
ln -s functions/acm.usb1 configs/c.1/
# UDC (Usb Device Controller)
ls /sys/class/udc > UDC
이로써 두 개의 Serial 인터페이스를 호스트에 제공하게 된다. 기본적인 이름은 /dev/ttyGS0과 /dev/ttyGS1에 매핑된다. 추가하는 횟수에 따라서 인덱스가 증가하게 된다.
1. 단순히 Serial (Bulk Transfer)로 "A" X 4096을 반복적으로 보내는 프로그램
그냥 계속해서 데이터를 이어 붙여 보내기 때문에 r+ 옵션을 사용한다.
#include <stdio.h>
int main(void) {
FILE* file;
char buf[4096];
for (int i = 0; i < sizeof(buf); i++) {
buf[i] = 'A';
}
while (1) {
file = fopen("/dev/ttyGS0", "r+");
fwrite(buf, 1, sizeof(buf), file);
fclose(file);
}
return 0;
}
간단히 transfer1라는 이름으로 실행 파일을 만들었다.
sudo gcc 소스코드 -o transfer1
2. 랜덤한 64bits (8 bytes) 데이터를 생성하여 반복적으로 보내는 프로그램
이 경우 파일 내용은 매번 새로 써서 8 bytes 씩 보내는 것이므로 하므로 w+ 옵션을 이용한다. (근데 이렇게 해도 Bulk Transfer의 특성상 가능한 최대한 많은 양의 데이터를 보낸다.)
#include <stdio.h>
#include <stdlib.h> // rand()
#include <time.h> // time()
#include <stdint.h> // uint32_t
uint32_t rand_uint32(void) {
uint32_t r = 0;
for (int i = 0; i < 32; i++) {
r = r * 2 + rand() % 2;
}
return r;
}
// 4바이트를 읽어 버퍼의 특정 인덱스에 삽입
void val32_to_buf(char* buf, uint32_t val, uint32_t index) {
buf[index] = val;
buf[index + 1] = val >> 8;
buf[index + 2] = val >> 16;
buf[index + 3] = val >> 24;
}
int main(void) {
FILE* file;
char buf[8];
srand(time(NULL));
while (1) {
uint32_t r1 = rand_uint32();
uint32_t r2 = rand_uint32();
val32_to_buf(buf, r1, 0);
val32_to_buf(buf, r2, 4);
file = fopen("/dev/ttyGS1", "w+");
fwrite(buf, 1, sizeof(buf), file);
fclose(file);
}
return 0;
}
이어서 transfer2라는 이름으로 실행 파일을 만들었다.
sudo gcc 소스코드 -o transfer2
※ Serial 통신 프로그램 자동 실행 ※
이후에 /etc/rc.local를 수정한다. 두 프로그램을 백그라운드에서 실행할 수 있도록 한다.
stty -F /dev/ttyGS0 raw
stty -F /dev/ttyGS1 raw
/home/pi/transfer1 &
/home/pi/transfer2 &
이후에 sudo reboot을 해서 재부팅하자. 그러면 재부팅 이후에 호스트(host) PC 입장에서는 다음과 같이 나온다.
※ 호스트 쪽 프로그램 작성 ※
호스트 쪽 프로그램은 다음과 같이 pyserial 라이브러리를 이용하여 작성할 수 있다. 그냥 USB 디바이스로부터 데이터를 받아 출력하는 코드다. 윈도우(Windows) OS에서는 Serial USB 장치가 꽂힌 경우 COM 포트로 인식하기 때문에 이를 이용하면 된다.
import time
import serial
s = serial.Serial("COM8", 115200)
total = 0
start_time = time.time()
last_time = 0
while True:
# 가져올 데이터가 존재하는 경우에만 처리
len = s.inWaiting()
if len == 0:
continue
data = s.read(len)
print(data)
time.sleep(1)
s.close()
프로그램을 실행하면 다음과 같이 'A'를 계속해서 받는 것을 알 수 있다.
※ (추가) Guest가 데이터를 받아서 단순히 파일에 기록할 때 ※
USB device가 호스트(host)로부터 데이터를 받아서 저장할 때는 다음과 같이 한다. 그냥 /dev/ttyGS1에서 파일 읽기를 수행하여 특정한 파일에 쓰는 것(이어쓰기)이다.
cat /dev/ttyGS1 >> /home/pi/test.dat
결과적으로 다음과 같이 /etc/rc.local 파일을 수정하면 된다. 기본적으로 cat과 >> 를 이용하여 파일의 뒤쪽에 쭉 이어 붙여 데이터를 쓸 수 있도록 한다.
stty -F /dev/ttyGS0 raw
stty -F /dev/ttyGS1 raw
/home/pi/transfer1 &
cat /dev/ttyGS1 >> /home/pi/test.dat
이제 리부팅 진행한 뒤에 호스트에서 다음과 같은 프로그램을 작성하여 보내보자.
import serial
s = serial.Serial("COM6", 115200)
s.write(b'Hello\n')
s.close()
프로그램을 여러 번 실행하면 라즈베리파이에는 다음과 같이 파일의 내용이 쓰인다.
'기타' 카테고리의 다른 글
Raspberry Pi Zero W를 이용해 2개의 USB Configuration을 재부팅마다 교체하기 (0) | 2020.11.15 |
---|---|
무료 파일 공유 서비스: 미디어파이어(MediaFire) 사용 방법 (0) | 2020.11.15 |
리눅스 C언어 랜덤(무작위) 64 bits 데이터 생성하여 출력하기 (1) | 2020.11.15 |
파워포인트(PowerPoint)에서 글꼴(글씨체) 한꺼번에 변경하는 방법 (2) | 2020.11.09 |
카카오톡 오픈채팅 단체 카톡방 만드는 방법 (0) | 2020.11.09 |