wpa_supplicant.conf
파일 내에 내용을 다음과 같이 수정 후 모든 파일(.) 형식으로 저장한다.ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1
country=US
network={
# 패스워드 없는 경우
ssid=”와이파이 이름1(SSID)”
scan_ssid=1
key_mgmt=NONE
}
network={
# WPA1, WPA2-personal
ssid=”와이파이 이름2(SSID)”
psk=”암호2”
scan_ssid=1
key_mgmt=WPA-PSK
}
해상도는 128x64 pixel이며, color와 grayscale은 표현되지 않는 OLED를 사용한다.
OLED와 라즈베리 파이 Zero와 SPI 통신
으로 연결할것이다.
하나의 마스터에 여러개의 Slave를 연결할 수 있다.
4개의 핀을 통해 통신한다.
1. SCK : Clock 핀
2. MOSI : Master -> Slave 데이터 전송
3. MISO : Slave -> Master 데이터 전송
4. SS : Slave를 구분하기 위한 선
SPI 활성화 : sudo raspi-config
에서 Interface Options에서 SPI를 활성화 시켜준다.
OLED 구동 라이브러리 설치 : pip3 install Adafruit-GPIO==1.0.3
& pip3 install Adafruit-SSD1306
Pillow 라이브러리 설치 : pip3 install pillow
예제 설치 : git clone https://github.com/adafruit/Adafruit_Python_SSD1306.git
&
라이브러리 수정
: cd Adafruit_Python_SSD1306/examples
에서 sudo nano image.py
로 파일 열고 다음 코드 처럼 수정한다.
연결한 핀에 맞게 값 바꿔주고, IC2 통신 주석 처리 및 SPI 통신 주석 처리 해제해주기!
RST = 24
DC = 25
SPI_PORT = 0
SPI_DEVICE = 0
# 128x32 display with hardware I2C:
#disp = Adafruit_SSD1306.SSD1306_128_32(rst=RST)
# 128x64 display with hardware I2C:
# disp = Adafruit_SSD1306.SSD1306_128_64(rst=RST)
# 128x32 display with hardware SPI:
# disp = Adafruit_SSD1306.SSD1306_128_32(rst=RST, dc=DC, spi=SPI.SpiDev(SPI_PORT, SPI$
# 128x64 display with hardware SPI:
disp = Adafruit_SSD1306.SSD1306_128_64(rst=RST, dc=DC, spi=SPI.SpiDev(SPI_PORT, SPI_D$
import Adafruit_GPIO.SPI as SPI
import Adafruit_SSD1306
from PIL import Image
# OLED 설정
RST = 24
DC = 25
SPI_PORT = 0
SPI_DEVICE = 0
oled = Adafruit_SSD1306.SSD1306_128_64(rst=RST, dc=DC, spi=SPI.SpiDev(SPI_PORT, SPI_DEVICE, max_speed_hz=8000000))
oled.begin()
im = Image.open('happycat_oled_64.ppm').convert('1') # 이미지 만들기
oled.clear() # OLED 화면 지우기
oled.display() # OLED에 표시
# 이미지 자르기
box = (32,0,96,64) # (시작 x,y ~ 끝 x,y)로 x축으로는 32~96 y축으로는 0~64인 박스 만들기
region = im.crop(box) # 해당 영역 만큼 이미지 자르기
# 이미지 변형
# region = region.transpose(Image.ROTATE_180) # 90도 회전
region = region.transpose(Image.FLIP_LEFT_RIGHT) # 좌우 반전
im.paste(region, box) # 이미지 붙이기
oled.image(im) # 메모리에 이미지 올리기
oled.display() # OLED에 표시
import Adafruit_GPIO.SPI as SPI
import Adafruit_SSD1306
from PIL import Image
# OLED 설정
RST = 24
DC = 25
SPI_PORT = 0
SPI_DEVICE = 0
oled = Adafruit_SSD1306.SSD1306_128_64(rst=RST, dc=DC, spi=SPI.SpiDev(SPI_PORT, SPI_DEVICE, max_speed_hz=8000000))
oled.begin()
# oled에 하얀색 바탕의 빈 화면 띄우기
# 파라미터 1 : '1'모드는 흑백 모드 , 파라미터 2 : 해상도, 파라미터 3 : 1 => 하얀색
im = Image.new('1', (128,64), 1)
from PIL Import ImageDraw
draw = ImageDraw.Draw(im) # 그림을 그리겠다고 설정
draw.line((0,0,128,64),fill = 1) # 좌상단에서 우하단으로 하얀 선 그리기
draw.line((0,64,128,0), fill = 0) # 우상단에서 좌하단으로 검은 선 그리기
# 네모
box = (32,0,96,64)
draw.rectangle(box,fill = 0)
# 원
draw.ellipse(box,fill = 1)
oled.image(im) # 메모리에 이미지 올리기
oled.display() # OLED에 표시
우선 wget -O malgun.ttf https://bit.ly/3BzjZfq
를 통해 폰트 다운
import Adafruit_GPIO.SPI as SPI
import Adafruit_SSD1306
from PIL import Image
# OLED 설정
RST = 24
DC = 25
SPI_PORT = 0
SPI_DEVICE = 0
oled = Adafruit_SSD1306.SSD1306_128_64(rst=RST, dc=DC, spi=SPI.SpiDev(SPI_PORT, SPI_DEVICE, max_speed_hz=8000000))
oled.begin()
# oled에 검은색 바탕의 빈 화면 띄우기
# 파라미터 1 : '1'모드는 흑백 모드 , 파라미터 2 : 해상도, 파라미터 3 : 0 => 검은색
im = Image.new('1', (128,64), 0)
from PIL Import ImageDraw
draw = ImageDraw.Draw(im) # 그림을 그리겠다고 설정
font = ImageFont.truetype('malgun.ttf', 15) # 다운 받은 15 사이즈 맑음 폰트
draw.text((10,10), "안녕하세요\n반갑습니다", font = font, fill = 1) # 하얀색 폰트인 글씨를 (10,10) 위치에 그리기
oled.image(im) # 메모리에 이미지 올리기
oled.display() # OLED에 표시
참고 문서
우선, 날짜 및 시간을 올바르게 출력하기 위해서는 라즈베리 파이를 내가 사는 지역 시간에 맞춰주어야 한다.
sudo raspi-config
에서 5. Locallisation Options > L2. Timezone > Asia, Seoul을 차례로 션택. > Finish
로 설정해줍니다.
# 특정 날짜 표시
from datetime import date
my_birthday = date(1998,1,16)
print(my_birthday) # 1998-01-16
print(f'나는 {my_birthday.year}년 {my_birthday.month}월 {my_birthday.day}일에 태어났습니다.')
# => 나는 1978년 8월 8일에 태어났습니다.
# 요일 표시
# print(my_birthday.weekday()) 숫자로 출력됨 => 0 : mon ~~ 6 : sun
# day = '월화수목금토일'[my_birthday.weekday()] # 요일로 전환
# print(day)
# 오늘
# print(date.today()) # 2022-04-22
from datetime import time
# 특정 시간 표시
at = time(14,2,0)
print(at) # 14:02:00
print(f'기차는 {at.hour}시 {at.minute}분에 떠납니다') # 기차는 14시 2분에 떠납니다.
# 특정 날짜 & 시간 동시 표시
from datetime import datetime
time = datetime(2022,4,22,14,4,30)
print(time) # 2022-04-22 14:04:30
# 지금 날짜 & 시간 출력
from time import sleep
while True:
now = datetime.now()
print(now) # 2022-04-22 14:06:07.053065
# 오늘에서 원하는 것만 추출
print(f'지금은 {now.month}월 {now.day}일 {now.hour}시 {now.minute}분입니다.')
print(f'{"월화수목금토일" [now.weekday()]}요일입니다.')
# 시간 표현 프레임
go = now.strftime('%Y %h.%d %p%I:%M %S')
print(go)
sleep(1)
OLED에서 보여지는 상을 홀로그램에서 많이 사용되는 perper's ghost
기술을 사용하여 반사가 많이 되는 유리를 통해 OLED의 허상과 실제 보는 상을 동시
에 보게됩니다. 야외버스에 창밖을 보면 버스 내부가 보이는 현상과 같이, 유리에 상을 띄우게 되어도 하나의 문제가 남아있습니다. 그건 바로 초점이 맞지 않는다
입니다. 따라서, 저는 초점을 맞추기 위해 Fresnel lens라는 얇은 볼록렌즈
를 사용하여 초점을 맞추었습니다.
완성된 모습
라즈베리파이 내부 풀다운 저항을 이용하여 버튼을 연결합니다.
import RPi.GPIO as GPIO
import time
GPIO.setmode(GPIO.BCM)
GPIO.setup(20, GPIO.IN, pull_up_down = GPIO.PUD_DOWN)
def myFunction(channel):
clear
# interrupt 함수로, RISING/FALLING/BOTH
GPIO.add_event_detect(20, GPIO.RISING, callback = myFunction)
I2S Microphone : MEMS(반도체 공정)
을 활용한 조그만 마이크
핀 4개
1. CLK : Clock 선, 동기화를 위한 것
2. SD : 데이터 선, 데이터 송수신 선
3. WS : 2-스테레오를 구현을 위해 왼쪽인지 오른쪽인지 구분하기 위한 선
4. SEL : 0V => 왼쪽 마이크, 3.3V => 오른쪽 마이크
장치를 테스트하기 위해 커널을 항상 빌드하는것이 매우 많은 시간이 소요되기 때문에, 커널 모듈
을 통해서 장치를 테스트합니다.
Device Tree
: .dts
텍스트 파일을 dtc
로 컴파일하여 dtb
바이너리로 만들어 어떤 장치를 테스트할지 결정한다.
리눅스 커널 헤더
를 다운받습니다.
sudo apt-get install raspberrypi-kernel-headers
후 sudo reboot
마이크를 동작 시키기 위해 디바이스 드라이버가 커널 모듈 형태로 제작된 코드를 다운받습니다. 우선, mkdir ics43432
파일 생성 후 cd ics43432
내부로 이동한다.
uname -a로
을 확인하여 커널 버전을 확인하고, 버전에 맞게 명령어를 입력해준다.
wget https://raw.githubusercontent.com/raspberrypi/linux/rpi-5.10.y/sound/soc/codecs/ics43432.c
sudo nano Makefile
obj-m := ics43432.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
install:
sudo cp ics43432.ko /lib/modules/$(shell uname -r)
sudo depmod -a
make all install
로 빌드하기
에러 발생시 2번을 다시 다운받고 4번 실행
디바이스 트리 소스파일 다운
wget https://raw.githubusercontent.com/lhdangerous/i2s-mems-mic/main/i2s-soundcard-overlay.dts
dts로 컴파일 후 오버레이를 /boot/overlays에 설치하기
dtc -@ -I dts -O dtb -o i2s-soundcard.dtbo i2s-soundcard-overlay.dts
sudo cp i2s-soundcard.dtbo /boot/overlays
부팅시 디바이스 오버레이 설정
설정 파일 열기 : sudo nano /boot/config.txt
# 아래 내용 uncomment한다.
dtparam=i2s=on
dtparam=audio=on
# 아래 내용 추가한다.
dtoverlay=i2s-soundcard,alsaname=i2sPiSound
재부팅 후 테스트
재부팅 : sudo reboot
테스트 : arecord -c1 -d10 -vv test.wave
파이썬에서 오디오 장치를 다루는 툴 설치
sudo apt-get install portaudio19-dev
pip3 install pyaudio
# with 안에서 파일 사용 : 명시적으로 close를 적어주지 않아도 with 구문을 빠져나올떄는 close가 실행된다.
# with 구문 내에서는 중간에 error 발생시 파일이 닫히는 것이 보장된다.
with open('test.txt', 'r') as file:
print(file.read())
# with 없이 파일 사용
file = open('test.txt', 'r')
print(file.read())
# 하지만 with 없이 사용할 때 중간에 error 발생시 파일이 닫히지 않는다.
file.cloes()
# Python Context Manager
class ContextManager() :
def __init__(self):
print("__init__")
def __enter__(self):
print("__enter__")
def __exit__(self, exc_type, exc_value, exc_traceback):
print("__exit__")
# with 구문과 함께 context Manager을 사용하면 with 구문 실행시 __init__ & __enter__ 실행
# 종료시 __exit__ 실행 보장
with ContextManager() as manager:
print("with in")
print("with after")
끝나지 않는 함수
, 보통 함수를 시작하면 메모리 적재후 return
으로 값을 리턴 후 메모리에서 내려오는데, yield
를 통해 값을 리턴하는데, 이때 메모리에서 내려가는게 아니고 잠시 대기한 후, 추가 명령어를 통해 진행된다.
# 일반 함수
def func():
n = 1
print('first')
return n
n += 1
print('second')
return n
a=func() # first만 출력되고 second는 출력되지 않는다.
# generator function
def my_gen():
n = 2
print('first')
yield n
n += 2
print('second')
yield n
n += 2
print('third')
yield n
a = my_gen() # 시작하자마자 대기
next(a) # first 출력
next(a) # second 출력
next(a) # third 출력
next(a) # 에러 발생
# for루프에서 사용
b = my_gen()
for i in b:
print(i)
from time import sleep
import Adafruit_GPIO.SPI as SPI
import Adafruit_SSD1306
RST = 24
DC = 25
SPI_PORT = 0
SPI_DEVICE = 0
oled = Adafruit_SSD1306.SSD1306_128_64(rst=RST, dc=DC, spi=SPI.SpiDev(SPI_PORT, SPI_DEVICE, max_speed_hz=8000000))
oled.begin()
oled.clear()
oled.display()
# text
from PIL import Image, ImageDraw, ImageFont
im = Image.new('1',(128,64),0)
draw = ImageDraw.Draw(im)
font1 = ImageFont.truetype('malgun.ttf', 15)
font2 = ImageFont.truetype('malgun.ttf', 25)
from datetime import datetime
now = datetime.now()
pm = now.strftime('%p')
time = now.strftime('%I:%M')
sec = now.strftime('%S')
draw.text((0,0), pm, font = font1, fill = 1)
draw.text((0,15), time, font = font2, fill = 1)
draw.text((40,40), sec, font = font1, fill = 1)
im = im.transpose(Image.FLIP_LEFT_RIGHT)
im = im.transpose(Image.ROTATE_90)
box = (0,0,128,64)
go = im.crop(box)
oled.image(go)
oled.display()
화면
한계점
제가 예상한 결과로는 1초마다 시간이 갱신되어 유리에 시간을 띄우게 하려고 하였지만, 코드의 미흡으로 구현하지 못하였습니다.