REMOTE PLAYING-1

우선 (CSS 대충디자인 하고 기능위주로)클라이언트-웹서버 구조만 구현해보았다.

웹앱인데, 사이트에 접속하고 악기버튼을 누르면 서버가 접속해있는 다른 모든 클라이언트들에게도 전송을 해주어서 한 클라이언트가 누른 음이 같이 울린다.(임시로 드럼음으로 넣었다.) 즉, 여러명이서 서로 떨어져있어도 같이 연주할 수 있다. 아직 프로토타입이라서 많이 고쳐야한다.

– node.js- express라이브러리, socket.io 라이브러리를 이용하였다.

콘솔-사이트에 접속한 클라이언트 중 한명이 버튼을 누르면 서버가 모두에게 값을 전달한다.
접속한 클라이언트와 누른 버튼을 출력한 화면

– 동영상에서 볼 수 있듯이, 서버가 동시에 모두 값을 전달해주지만 기기의 반응속도가 다르다. 노트북이 제일 빠르다. 이에 대해 보완해야 한다.

-여러 기기의 화면에 맞게 나타나도록 viewport를 html에 넣었지만 작은 폰에서 보면 한눈에 들어오지 않아 불편하다.

– 이제 오디오나 이미지파일을 데이터베이스로 관리해야한다.

-외부에서도 접속가능하게만들게 해야한다.(포트포워딩)/조사해본바에 따르면 주소창에 기본게이트웨이를 치면 공유기설정사이트가 나와야하는데 여기기숙사에선 보안문제인지 사이트자체가 뜨지 않는다. 더 알아봐야겠다.

-웹사이트 디자인에 대한 계획을 세워야하고 피아노음으로 바꿔야한다

Remote multi-player service 구현계획

웹브라우저나 모바일브라우저를 통해 접속하여 원하는 소리버튼(건반모양? 아님 드럼모양?미정)을 누르면 서버에 보내게 되고 이 서버는 모든 클라이언트들에게 값을 전달하여 접속하고 있는 사람들이 서로 얼굴도 모르지만 같이 연주하게 됩니다. 이 서버는 이에 대한 값들은 78계단쪽 서버에게도 값을 보내고, 계단에서 그 연주곡이 연주되게 하는 것까지가 최종단계입니다. 78쪽 서버와의 통신 전까지에 대한 전체적인 구현계획=>

리듬

https://touchpianist.com/

이건 키보드를 마음대로 아무키나 누르면 누른 리듬대로 클래식음악이 재생되는 웹입니다. 여기서 인풋을 계단 올라가는 모션으로 해서 만들면 좋을 것 같습니다!

http://qiao.github.io/euphony/#17

이건 웹기반 midi player/visulization입니다. 리듬게임에 활용할 수 있을 것같습니다.

https://www.smashingmagazine.com/2018/03/web-midi-api/ => 자바스크립트 midi api 관련 글 (여러 악기 소리로 음악을 만들 수 있는 등 다양한 응용을 다룬 글)

추가적인 interactive performance 구현에 대해

소리와 불빛이 나는 계단 프로토타입

  1. 프로토타입의 사진/영상 및 코드

– LED는 14번 핀이 HIGH 일 때 3.3V를 공급받는다.
– Sharp IR 센서가 출력하는 데이터(전압)는 ADC를 통해 디지털화 되어 파이에 전달된다. 이 과정에서 spidev 라이브러리에 있는 spi 객체가 사용된다.

import time

# 적외선 센서를 사용하기 위한 사전 작업
import spidev 
spi = spidev.SpiDev()
spi.open(0, 0)
spi.max_speed_hz = 100

# 음악을 재생하기 위한 부분
import pygame 
pygame.init()
pygame.mixer.init()

sound_channel = pygame.mixer.Channel(0)
sound = "piano-C4.wav" # 음에 따라 다른 파일 필요(C4, D4)
sound = pygame.mixer.Sound(sound); 

# LED 재생을 위한 부분
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BCM) # GPIO 핀 세팅 방식 설정
GPIO.setwarnings(False)
GPIO.setup(14, GPIO.OUT, initial = GPIO.LOW) # 14번 핀이 HIGH로 설정될 때, LED가 켜진다

def readChannel(channel) :
  val = spi.xfer2([1, (8 + channel) << 4, 0])
  data = ((val[1] & 3) << 8) + val[2]
  return data

# 거리 측정 함수
def measure_distance() :
  v = (readChannel(0) / 1023.0) * 3.3
  dist = 16.2537*v**4 - 129.893*v**3 + 382.268*v**2 - 512.611*v + 301.439
  return dist

# 측정하고자 하는 위치에 발이 존재하는지를 알려줌
def should_play() :
    distance = measure_distance()
    if distance < 60 :
        return True
    return False
    
# sound channel을 통해 소리 재생
def sound_on() :
    sound_channel.play(sound)

# 소리 재생 중지
def sound_off() :
    sound_channel.stop()
    
try:
    music_on = False
    was_on = False
    
    while True:
        time.sleep(0.1) # 0.1초에 한 번 씩 재생
        music_on = should_play() # 음악이 켜져야 하는가?

        if music_on == was_on : # 이전 상태와 지금 상태가 같다면, 변화가 필요없다.
            pass
        elif music_on : # 음악이 재생되어야 한다면,
            sound_on() # 소리 재생
            GPIO.output(14, GPIO.HIGH) # LED on
        else : # 음악이 꺼져야 한다면,
            sound_off() # 소리 재생 중지
            GPIO.output(14, GPIO.LOW) # LED off
            
        was_on = music_on # 현 상태 정보 저장
                    
except KeyboardInterrupt :
    pass

2. 적외선 센서의 원리

  • 적외선 센서가 적외선을 방출하면 적외선이 물체에 반사되어 다시 센서로 돌아온다. 이때 적외선이 반사된 위치에 따라 센서에 입력되는 적외선의 위치가 다르고, 이에 따라 센서가 발신하는 데이터, 즉 전압의 크기가 달라진다. 거리에 따른 전압의 그래프는 위와 같이 나타난다.
  • 이 때, 0~15cm에서 출력하는 값과, 15cm 이후로 출력하는 전압이 일부 겹친다. (실제로 둘 중 더 큰 거리를 측정값으로 보여준다.) 따라서 0~15cm에서 유의미한 값을 출력한다고 보기 어렵다.

3. 시행착오들

  • 센서 측정하는 과정에서 소리가 씹히는 현상이 발생함.

현재 코드에 따르면, 센서의 측정 위치에 사물이 있느냐 없느냐에 따라 소리가 재생되거나 종료한다. SPI 버스의 데이터 전송 주기를 1000,000 hz로 설정하고, main 함수의 while문을 반복적으로 재생하였을 경우, 한 번에 연속적으로 소리가 재생되는 것이 아닌, 불연속적으로 여러번 소리가 났다. 정확한 발생 원인은 모르지만, 같은 값을 너무 자주 SPI bus를 통해 전송함으로써 오류가 생기지 않았나 추측한다.

SPI 버스의 데이터 전송 주기를 낮추고, while문 재생에 간격을 둠으로써 음이 한번에 연속적으로 재생될 수 있게 하였다.

  • 휴대폰으로 오디오를 출력하려는 노력

오디오를 이어폰으로 출력하면 영상을 촬영할 수 없고, HDMI로 출력하면 모니터 간의 간격이 너무 넓어서 라즈베리 파이 두 개를 나란히 두기 어려웠다. 그래서 휴대폰 또는 모니터와 bluetooth pairing을 하여 휴대폰 또는 모니터로 오디오를 출력하고자 하였다.

라즈베리파이와 휴대폰을 bluetooth pairing하는 것까지 성공했으나, 오디오 출력을 휴대폰으로 바꾸는 방법을 찾지 못했다. 블루투스 스피커를 연결할 경우, 라즈베리파이의 상태표시줄 중 볼륨 아이콘을 오른쪽 마우스로 클릭하면 블루투스 스피커가 표시된다고 하는데, 휴대폰은 표시되지 않는 것으로 보아 파이의 오디로를 휴대폰에서 출력하는 기능이 존재하지 않는 것 같다.

결국 HDMI 모니터 두 개를 사용하기로 했고, 그래서 전선의 길이를 길게 할 필요가 있었다.

  • 전선의 길이가 짧음

LED strip에 전선을 연결할 때에는 납땜을 했고, 전선의 반대쪽 끝은 절연테이프를 활용해 이었다. 적외선 센서에 연결된 전선을 길게 하기 위해서 양끝의 모양이 다른 점프선들을 여러 개 연결하였다.

  • 적외선 센서 측정에 오류가 생김

적외선 센서를 바닥에 그대로 부착했을 때 거리 측정값이 엉망으로 출력되었다. 원인은 적외선 센서의 각도가 약간 아래를 향하고 있던 것이었다. 각도를 바닥과 나란하게 조정하자 올바른 출력값을 얻을 수 있었다.


Written by. 하은&혜정

IR 센서 및 mysql 사용, 음악 재생을 위한 준비!!

Sharp IR 센서
1. SPI 라이브러리 설치.
2. cmd에 sudo raspi-config 을 친 다음, 5번, P4, Yes을 차례대로 선택하고, PI을 reboot 한다.
3. etc/modules-load.d에 있는 modules.conf에 spi-bcm2807을 추가한다.

<주의 사항>
– 파이의 방향과 회선의 방향을 확인한다!
– 코드 작성 시, SPI object의 max_speed_hz를 설정하는 것을 잊으시면 안됩니다! 만약 설정하지 않을 경우, max_speed_hz = 0으로 설정되어 측정이 되지 않습니다.

Pygame 라이브러리
1. sudo apt-get install python-pygame // pygame 설치

구체적인 사용 방식은
https://bit.ly/2GCKpCN
https://bit.ly/2Z3r0kN
을 참고하여 코드를 작성해 주세요!

Pymysql
78project는 파이에 있는 센서를 통해 값을 측정하고, 적절한 처리를 하여 해당 정보를 서버에 업로드 합니다. 그리고 서버에서는 값을 저장하기 위해 mysql 데이터베이스를 사용합니다. 그렇다면 과연 파이에 mysql을 다운 받아야할까요? 그렇지 않습니다. 파이에서는 mysql client 역할을 해주는 pymysql 라이브러리를 단순히 import 하여 사용하면 됩니다.

구체적인 사용 방식은
https://bit.ly/2rUUu8i
https://bit.ly/2Z3r0kN
을 참고하여 작성하여 주세요!

mysql에서 원격 접근(pymysql) 허용하기
pymysql이 mysql client 역할을 하긴 하지만, mysql에서 특정 계정에 대해 미리 원격 접근을 허용하지 않는다면 원격으로 접속이 불가능합니다. 우선 root 계정은 원격으로 접속이 불가능합니다. 따라서 새로운 계정을 만들어, 특정 데이터 베이스 또는 모든 데이터 베이스에 대해 원격 접속 권한을 부여해야 합니다.

– ‘1.2.3.4’ 라는 IP에서 fooUser를 통해 fooDatabase에 접속을 허용하는 경우 :
GRANT ALL ON fooDatabase.* TO fooUser@’1.2.3.4′ IDENTIFIED BY ‘my_password’;

– 모든 IP에서 fooUser를 통해 fooDatabase에 접속을 허용하는 경우 :
GRANT ALL ON fooDatabase.* TO fooUser@’%’ IDENTIFIED BY ‘my_password’;

다른 포스트에서도 볼 수 있듯이, 현재는 lee 유저를 통해 서버의 78project 데이터베이스를 모든 IP에서 원격 접속이 가능합니다.

해당 설치 및 사용 방법은 raspberry pi에 해당 library들을 반복적으로 설치하며 보충하겠습니다!

서버 구축 및 DB 다루기

서버는 https://jimnong.tistory.com/612 을 참조하여 구축하였습니다. 78 project의 경우, 웹사이트가 필요하지 않기 때문에 SSL 인증서는 발급 받지 않았습니다.

WIFI는 여러 대의 기기가 연결될 수 있도록 여러 port가 존재합니다. 하지만 port의 수 역시 유한하기 때문에, WIFI에 연결 시 공유기가 유동적으로 port를 할당합니다.

이러한 특성 때문에, WIFI에 새로 연결 될 때마다 새로운 내부 IP 주소가 할당됩니다. 78 project의 경우, 실시간으로 DB에 값을 업로드하고 다운받아야하기 때문에, 서버가 유동적 IP 주소를 가진다는 것은 문제가 됩니다. 이러한 문제를 해결하기 위해, 서버 구축 시 port folding을 통해 서버가 일정한 내부 IP 주소를 가지도록 하였습니다.

포트 포워딩 및 방화벽 해제는 해당 사이트를 참고하여 진행하였습니다.
https://www.lifewire.com/how-to-port-forward-4163829
https://bit.ly/33zjBgz

서버 IP 주소 : 192.168.31.114
Apache Port : 80
mysql Port : 3306

설치 된 mysql을 사용하여 데이터베이스 및 테이블을 구축할 수 있습니다. 해당 mysql 강의를 참고하였습니다.
https://opentutorials.org/course/3161/19532

Piano 음 재생 및 DB 속도 측정하기

Sharp GP2Y0A02YK0F IR 센서를 사용하였습니다. 해당 센서에 관한 정보는 아래 그림을 클릭하면 확인 할 수 있습니다.

import pymysql
import pygame
import spidev
import time

spi = spidev.SpiDev() 
spi.open(0, 0)
spi.max_speed_hz = 1000000

pygame.init()
pygame.mixer.init()

DISTANCE = [20, 30, 40]

sound_channel = pygame.mixer.Channel(0)
sound_list = ["piano-C4.wav", "piano-D4.wav", "piano-E4.wav", "piano-F4.wav"]
for i in range(4) :
    sound_list[i] = pygame.mixer.Sound(sound_list[i]);

cnx = pymysql.connect(host = '192.168.31.114',
                      user = 'lee',
                      password = 'cdt',
                      database = '78project',
                      autocommit = True
                      )

def readChannel(channel) :
  val = spi.xfer2([1, (8 + channel) << 4, 0])
  data = ((val[1] & 3) << 8) + val[2]
  return data

def measure_distance() :
  v = (readChannel(0) / 1023.0) * 3.3
  dist = 16.2537*v**4 - 129.893*v**3 + 382.268*v**2 - 512.611*v + 301.439
  return dist

def which() :
    distance = measure_distance()
    if distance > 130 :
        note = -1
    elif distance < DISTANCE[0] :
        note = 0
    elif distance < DISTANCE[1] :
        note = 1
    elif distance < DISTANCE[2] :
        note = 2
    else :
        note = 3
    return (note, distance)
    
def sound_on(idx) :
    sound_channel.play(sound_list[idx])

def sound_off(idx) :
    sound_channel.stop()

def replay(idx) :
    sound_channel.stop()
    sound_channel.play(sound_list[idx])
    
try:
    playing = -1
    note = -1
    start_time = time.time()
    
    while True:
        playing = note
        data = which()
        note = data[0]
        dist = data[1]
        
        with cnx.cursor() as cursor :
            # PUSH data to database
            sql = 'UPDATE changed_note SET note_id = ' + str(note) + ';'
            cursor.execute(sql)
            sql = 'SELECT * FROM changed_note;'
            cursor.execute(sql)
            rows = cursor.fetchone()

            # PULL data from database
            idx = int(rows[0])
            
            # Error if push & pull value don't match
            if note != idx :
                print("Database Error")
            #else :
                #print(idx)

            # Play & Replay & Stop playing
            if note == -1 :
                sound_off(note)
                
                if playing != note :
                    print("off")
                    print("distance: %dcm" %dist)
                
            elif playing == note :
                if sound_channel.get_busy() :
                    if time.time() - start_time > 0.5 :
                        replay(note)
                        start_time = time.time()

                        print("replay")
                        print("distance: %dcm" %dist)    
                        
                    else :
                        pass
            else :
                sound_off(playing)
                sound_on(note)
                start_time = time.time()

                print("change")
                print("distance: %dcm" %dist)
                    

except KeyboardInterrupt :
    pass

finally :
    cnx.close()
  • 음원은 freesound.org에서 무료로 다운 받았습니다.
  • 음 재생을 시현해 보기 위해 서버에 있는 78project 데이터베이스에 changed_note라는 table을 생성하였습니다. changed_note 데이터베이스에는 현재 재생되어야하는 음의 id를 저장합니다. (C4 : 0, D4 : 1, E4 : 2, F4 : 3, 아무 음 재생 안하는 경우 : -1)
  • 데이터베이스에 정보를 push 하고 pull 하는 유일한 이유는 통신 속도를 측정하기 위함입니다. push와 pull은 전체적인 기능에 다른 영향을 주지 않습니다.
  • 음원의 일부를 반복 재생하기 위해 0.5초를 재생한 뒤, 다시 처음부터 재생하도록 하였습니다. 하지만 stop 한 뒤 play 하는 데까지 약간의 지연 시간이 있기 때문에, 음이 연속적으로 재생되지 않는 문제를 보입니다.

Ultrasonic 및 Sharp IR 센서로 거리 측정하기

Ultrasonic 센서 이용하기

https://bit.ly/2GCKpCN를 참고하여 Ultrasonic 센서로 거리를 측정하였습니다. https://tutorials-raspberrypi.com/raspberry-pi-ultrasonic-sensor-hc-sr04/ 를 참고하시면 됩니다.

import RPi.GPIO as GPIO 
import time
instrument = 0

GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)

trig = 20
echo = 21
GPIO.setup(trig, GPIO.OUT) # for light shooting
GPIO.setup(echo, GPIO.IN) # for light receiving

DISTANCE = 10 # 10cm
before = 0
now = 0

def measure_distance():
    time.sleep(1)
    GPIO.output(trig, GPIO.HIGH)
    time.sleep(0.001)
    GPIO.output(trig, GPIO.LOW)
    ## shoot for 0.001 second(output)
 
    start = 0
    end = 0
 
    # until there is no input
    while GPIO.input(echo) == GPIO.LOW:
        start = time.time()
 
    # if there is input
    while GPIO.input(echo) == GPIO.HIGH:
        end = time.time()
 
    duration = end - start
    distance = (duration * 340 * 100) / 2
    print("distance: %dcm" %distance)
 
    return distance
 
def state(distance):
    global now
    if distance <= DISTANCE:
        now = 1
    else:
        now = 0
 
def change():
    distance = measure_distance()
    before = now
    state(distance)
    
    if before != now:
        return 1
    else:
        return 0
     
def sense():
    if change() == 0:
        print("stay")
        return 0    #stay
    elif now == 1:
        print("on")
        return 1    #on
    else:
        print("off")
        return -1   #off
    
 #---
     
try:
    while True:
        sns = sense()
        if sns == 0:
            pass
        elif sns == 1:
            print("on")
        else:
            print("off")

except KeyboardInterrupt:
    GPIO.cleanup()

Sharp IR 센서 이용하기

Sharp IR 센서를 사용하기 위해
https://tutorials-raspberrypi.com/infrared-distance-measurement-with-the-raspberry-pi-sharp-gp2y0a02yk0f/
사이트를 참고하였습니다. 주어진 회로와 같이 연결하고, 아래와 같이 코드를 작성하였습니다.

아래 코드는 SPI 객체를 사용하기 위해 spidev library를 import 합니다. SPI는 Serial Peripheral Interface의 약자로, 두 장치간 양방향 통신을 위해 사용되는 프로토콜입니다. Raspberry pi의 경우, 아날로그 신호를 디지털 신호로 변환하는 GPIO 핀이 없기 때문에 별도의 ADC(analog to digital converter)를 사용해야 합니다. spidev library는 adc를 통해 생성된 digital 값을 raspberry pi 내부로 받아오기 위해 사용됩니다. SPI 객체는 raspberry pi에 있는 값과 adc를 통해 변환된 digital 값 3 byte를 교환합니다. raspberry pi에서 전송되는 3 byte는 adc에게 의미 없는 값이지만, adc를 통해 받아온 거리 값은 raspberry pi에게 의미 있는 값이므로 이를 받아 사용합니다.
# 해당 경우, ADC는 MCP8008입니다.

import spidev
import time

spi = spidev.SpiDev() # create spi object
spi.open(0,0) # open spi port 0, device (CS) 0, for the MCP8008
spi.max_speed_hz = 1000000 # set transfer speed

DISTANCE = 10 # 10cm
before = 0
now = 0

def readChannel(channel):
  val = spi.xfer2([1,(8+channel)<<4,0]) 
# 3 바이트의 데이터 1, (8+channel)<<4, 0을 순차적으로 보내고 3바이트를 받습니다.
  data = ((val[1]&3) << 8) + val[2]
  return data

def measure_distance():
  v = (readChannel(0)/1023.0)*3.3
  dist = 16.2537 * v**4 - 129.893 * v**3 + 382.268 * v**2 - 512.611 * v + 301.439
  print("distance: %dcm" %dist)
  return dist
 
def state(distance):
    global now
    if distance <= DISTANCE:
        now = 1
    else:
        now = 0
 
def change():
    global before
    distance = measure_distance()
    before = now
    state(distance)
    
    if before != now:
        return 1
    else:
        return 0
     
def sense():
    if change() == 0:
        print("stay")
        return 0    #stay
    elif now == 1:
        print("on")
        return 1    #on
    else:
        print("off")
        return -1   #off
    
 #---
     
try:
    while True:
        time.sleep(0.5)
        sns = sense()
        if sns == 0:
            pass
        elif sns == 1:
            print("on")
        else:
            print("off")

except KeyboardInterrupt:
    pass

참고 문헌

adafruit 아닌 GPIO 로 DHT11

import RPi.GPIO as GPIO
import time

def bin2dec(string_num): # define a function to convert a binary number to a decimal.
return str(int(string_num, 2)) # return the string representing the integer value of the string passed to this function in base 2 (binary)

data = [] # define a data array

GPIO.setmode(GPIO.BCM) # use the Broadcom numbers instead of the WiringPi numbers

GPIO.setup(4,GPIO.OUT) # Set it as an output so that we can:
GPIO.output(4,GPIO.HIGH) # write a 1
time.sleep(0.025) # for 25 ms
GPIO.output(4,GPIO.LOW) # then write a 0
time.sleep(0.02) # for 20 ms.

I assume that the preceding sequence is the method to get the DHT sensor to respond with the current data.

GPIO.setup(4, GPIO.IN, pull_up_down=GPIO.PUD_UP) # Change the pin to read mode, with a pullup resistor

for i in range(0,500): # 501 times
data.append(GPIO.input(4)) # read a bit from the GPIO, as fast as possible (no wait)

bit_count = 0
tmp = 0
count = 0
HumidityBit = “”
TemperatureBit = “”
crc = “”

try: # do this unless there’s an error. If there’s an error jump to “Except:”
while data[count] == 1: # as long as you read a 1
tmp = 1
count = count + 1 # count how many 1s have been read

for i in range(0, 32): # do this 33 times
    bit_count = 0 # reset the bit count each time

    while data[count] == 0: # as long as a 0 is read
        tmp = 1
        count = count + 1 # move on to the next bit

    while data[count] == 1: # as long as a 1 is read
        bit_count = bit_count + 1 # count how many 1s in a row
        count = count + 1 # move on to the next bit

    if bit_count > 3: # if there were mote than 3 * 1-bits in a row
        if i>=0 and i<8: # if we're in the first byte
            HumidityBit = HumidityBit + "1" # append a 1 to the humidity bitstring
        if i>=16 and i<24: # if we're in the 3rd byte
            TemperatureBit = TemperatureBit + "1" # add a 1 to the temperature bitstring
    else: # if there weren't at least 3 * 1-bits
        if i>=0 and i<8: # if we're in the first byte
            HumidityBit = HumidityBit + "0" # append a 0 to the humidity bitstring
        if i>=16 and i<24: # if we're in the 3rd byte
            TemperatureBit = TemperatureBit + "0" # append a 0 to the temperature bitstring

except: # if there was an error in the “try:” block
print “ERR_RANGE” # report it
exit(0) # end the program

try: # do this unless there’s an error. If there’s an error jump to “Except:”
for i in range(0, 8): # do this 9 times
bit_count = 0 # reset the bit counter

    while data[count] == 0: # as long as a 0 was read
        tmp = 1
        count = count + 1 # move on to the next bit

    while data[count] == 1: # as long as a 1 was read
        bit_count = bit_count + 1 # count how many 1s
        count = count + 1 # move on to the next bit

    if bit_count > 3: # if there were at least 3 * 1-bits
        crc = crc + "1" # add a 1 to the crc (Cyclic redundancy check) bitstring
    else: # if there were less than 3* 1-bits
        crc = crc + "0" # add a 0 to the crc bitstring

except: # if the “try:” block failed
print “ERR_RANGE” # report it
exit(0) # end program

Humidity = bin2dec(HumidityBit) # convert the binary bitstring to a decimal variable for humidity
Temperature = bin2dec(TemperatureBit) # convert the binary bitstring to a decimal variable for temperature

if int(Humidity) + int(Temperature) – int(bin2dec(crc)) == 0: # test whether the CRC indicates that the reading was good
print Humidity # duh
print Temperature # duh
else: # if the CRC check was bad
print “ERR_CRC” # report it

looks like there should be an “exit (0)” here that’s missing. The program ends either way though. Usually the exit value indicates whether the program completed successfully.