이 블로그는 Web 환경을 이용한 원격 제어 기술에 필요한 지식을 공유 하기 위한 블로그 입니다.
실제 개발과 프로그램 예를 위하여 Raspberry Pi와 Raspberry Pi Pico, ATmega128 보드, Arduino Mega 보드(ATmega2560), WiFi 모듈을 사용 합니다.

python-i2c-comm

Python i2c communication
Raspberry Pi에서 Python 언어를 이용한 I2C(Inter Integrated Circuit) 통신

    I2C 통신은 동기 방식의 Serial 통신으로 2개의 신호선(SDA, SCL) 만을 사용하고, 여러개의 Master와 Slave를 사용할 수 있는 장점이 있다. 이러한 장점 때문에 비교적 가까운 거리( 특별한 장치를 사용하지 않는 경우 2.7m에서 최대 5m( Clock speed와 통신선의 상태에 따라 변동이 있음) 이내에서 통신이 가능)에 있는 여러개의 장치(센서나 모터 등)를 제어 하여야 하는 경우 유리 하다.




  • 필요한 배경 지식
    • HTML: Web pages 작성에 필요한 Markup language
    • JavaScript: Web pages 작성에 필요한 Programming language
    • Python: HTML과 Web의 Programming language
    • Linux OS: Raspberry Pi의 OS 가 Linux 이다.
    • Atmel 개발 환경: Atmel Studio 7 개발 환경과 ATmega128 보오드를 사용 한다.
    • ATmega128의 i2c 통신에 대한 이해가 필요 하다.
  • Raspberry Pi에서 I2C(Inter Integrated Circuit) 통신을 위한 설정
    • "sudo raspi-config" 명령을 사용하여 I2C 사용을 Enable 하여야 한다.
    • UART 통신을 위한 설정 방법과 같기 때문에 "UART 통신을 위한 설정" 과 아래 사진을 참고 하여 설정 한다.

      아래 창에서 "P5 I2C"을 선택하고 Enable로 설정 한다.

    • 아래 명령으로 user-mode I2C port를 확인 할 수 있디.
    • ls /dev/*i2c*

      Raspberry Pi 3인경우 " /dev/i2c-1 " 메세지가 출력 된다.

    • 아래 명령으로 Raspberry Pi에 연결된 I2C Device를 확인 할 수 있다.
    • sudo i2cdetect -y 1

      Raspberry Pi에 연결된 I2C Device를 보여 주는 터미널 응답

      윗 터미널 응답은 I2C bus address 0x48 번지와 0x48 번지에 I2C Device가 연결된 것을 보여 준다.


  • Raspberry PI I2C 통신 Protocol
    • I2C 통신을 위한 구성(회로 연결)
    • I2C 통신은 Master device와 Slave device로 구성 된다. Master와 Slave device는 1개 이상일 수 있다.

      I2C 통신을 위한 회로 연결 예: Raspberry PI는 내부에 Rp(Pull up resistor)를 내장하고 있기 때문에 회로 구성시 연결 할 필요가 없다.


    • I2C 통신 Protocol
      • Master device에서 Slave device로 2 Byte를 전송하는 Timing 예
        • Master device에서 Start bit를 I2C Bus에 출력 한다.
        • Master device에서 Slave address(7 Bits)와 Write 신호(0) 1 Bit를 출력 한다.
        • Slave address(7 Bits)와 일치하는 Slave device가 선택되고 선택된 Slave device에서 I2C Bus에 Acknowledge(Ack) 신호를 출력 한다.
        • 선택된 Slave device의 Ack 신호를 받은 Master device에서 8 Bits Data를 전송 한다.
        • 8 Bits Data를 전송 받은 Slave device는 다시 Ack 신호를 출력 한다.
        • 위 List에서 4 - 5 번을 N번 반복하면 N Byte의 Data를 Slave device에 전송 할 수 있다.
      • Slave device로 부터 Master device로 1 Byte를 수신하는 Timing 예
        • Master device에서 Start bit를 I2C Bus에 출력 한다.
        • Master device에서 Slave address(7 Bits)와 Read 신호(1) 1 Bit를 출력 한다.
        • Slave address(7 Bits)와 일치하는 Slave device가 선택되고 선택된 Slave device에서 I2C Bus에 Acknowledge(Ack) 신호를 출력 하고,
        • 계속하여 Master device에 8 Bits Data를 전송 한다.
        • Slave device에서 Master device에 Data 전송이 종료되면 Not Ack 상태가 되고 Read 동작이 종료(Stop) 된다.
    • SMBus(System Management Bus) Functions
    • SMBus은 Python 프로그램 언어를 사용하여 I2C 통신을 가능하게 하여 주는 Library 이다. 여기서는 자주 사용하는 SMBus 함수 만 설명 한다.

      • write_byte_data(int addr,char cmd,char val)
        • 이 함수는 Slave device에 1 Byte의 Command(사용하는 시스템에 때라 Address 또는 Data로 사용 할 수 있음)와 1 Byte Data를 전송 한다.

          이 함수가 실행 될 때 I2C Bus 신호 Timing 은 아래와 같다.

        • Master device에서 Start bit를 I2C Bus에 출력 한다.
        • Master device에서 Slave address(7 Bits)와 Write 신호(0) 1 Bit를 출력 한다.
        • Slave address(7 Bits)와 일치하는 Slave device가 선택되고 선택된 Slave device에서 I2C Bus에 Acknowledge(Ack) 신호를 출력 한다.
        • 선택된 Slave device의 Ack 신호를 받은 Master device에서 8 Bits Data(cmd)를 전송 한다.
        • 8 Bits Data를 전송 받은 Slave device는 다시 Ack 신호를 출력 한다.
        • Ack 신호를 받은 Master device에서 8 Bits Data(val)를 한번 더 전송 한다.
        • 8 Bits Data를 전송 받은 Slave device는 다시 Ack 신호를 출력 한다.
      • read_byte_data(int addr,char cmd)
        • 이 함수는 Slave device에 1 Byte의 Command(사용하는 시스템에 때라 Address 또는 Data로 사용 할 수 있음)를 전송하고, 1 Byte Data를 Slave device로 부터 수신 한다.

          이 함수가 실행 될 때 I2C Bus 신호 Timing 은 아래와 같다.

          Master device에서 Slave device로 명령(cmd) 전송 하기

        • Master device에서 Command를 Slave device에 전송하기 위하여 Start bit를 I2C Bus에 출력 한다.
        • Master device에서 Slave address(7 Bits)와 Write 신호(0) 1 Bit를 출력 한다.
        • Slave address(7 Bits)와 일치하는 Slave device가 선택되고 선택된 Slave device에서 I2C Bus에 Acknowledge(Ack) 신호를 출력 한다.
        • 선택된 Slave device의 Ack 신호를 받은 Master device에서 8 Bits Data(cmd)를 전송 한다.
        • 8 Bits Data를 전송 받은 Slave device는 Ack 신호를 출력 한다.
        • Master device가 Slave device로 부터 1 Byte Data 수신 하기

        • Master device에서 Data를 Slave device로 부터 수신 하기 위하여 Start bit를 I2C Bus에 출력 한다.
        • Master device에서 Slave address(7 Bits)와 Read 신호(1) 1 Bit를 출력 한다.
        • Slave address(7 Bits)와 일치하는 Slave device가 선택되고 선택된 Slave device에서 I2C Bus에 Acknowledge(Ack) 신호를 출력 하고,
        • 계속하여 Master device에 8 Bits Data를 전송 한다.
        • Slave device에서 Master device에 Data 전송이 종료되면 Not Ack 상태가 되고 Read 동작이 종료(Stop) 된다.

  • I2C 통신을 이용한 측정 과 제어 예
    • I2C 통신을 이용한 측정 과 제어 시스템 구성 예

    • I2C 통신을 이용한 온도 측정(DS1621) 예
      • 윗 I2C 통신을 이용한 측정 과 제어 시스템 구성 예에서 Raspberry PI를 Master로 온도 센서(DS1621)를 Slave device로 사용 한다.

      • SI2C 통신(디지털 온도계) 기본 프로그램 예: i2c-th-meter-basic.py
      • 학습 포인트
        • I2C 통신을 사용한 Master 와 Slave device(이 예에서는 DS1621 온도센서) 사이의 Data 송수신에 대한 이해
        • DS1621 온도센서에 대한 이해.
        • SMBus 대한 이해

        i2c-th-meter-basic.py 프로그램

        
        #! /usr/bin/env python
        # -*- coding: utf-8 -*-
        
        # DS1621는 3개의 Address Pin(A2, A1, A0)을 갖는다. A2, A1, A0
        # A2A1A0 = 000(모든 Address Pin이 GND) 일때 DS16221의 I2C bus address는 0x48 번지가 된다.  
        # A2A1A0 = 001 (A0 <- Vdd)인 경우 DS16221의 I2C bus address는 0x49 번지가 된다.
        #
        # Room    = 0x48  
        # Remote  = 0x49  
        
        import smbus
        import time, threading
        
        # DS1621 commands
        START           = 0xEE
        STOP            = 0x22
        READ_TEMP       = 0xAA
        
        i2c_1 = smbus.SMBus(1)
        
        # Sensorname Room의 Address를 0x48로 설정
        Room = 0x48
        
        # 2의 보수를 구한다.
        def twos_comp(byte):
            # 2이 보수인 1 Byte data가 Signed integer로 변환되어 Return 된다.
            if len(bin(byte)[2:]) > 8:
                # shouldn't ever get here
                print('\nWarning: input ' + str(hex(byte)) + \
                      ' truncated to least significant byte: ' + \
                      str(hex(0xFF & byte)))
                byte = 0xFF & byte
            return ~(255 - byte) if byte > 127 else byte
        
        # 온도 센서의 온도 Data를 읽는다.  
        def read_degreesC_byte(bus, sensor):       
            bus.read_byte_data(sensor, START)
            degreesC_byte = bus.read_byte_data(sensor, READ_TEMP)
            return degreesC_byte             
                    
        def wake_up(bus, sensor):
            #Device는 항상 Idle mode로 start 하기 때문에 첫 번째 읽은 값은 사용하지 못 한다.
            read_degreesC_byte(bus, sensor)
            time.sleep(0.6)
        
        # 이 함수는 1Sec 주기로 온도를 측정 하여 출력 한다.
        def periodControl():
            degrees = twos_comp(read_degreesC_byte(i2c_1, Room))
            print('Room Temp: ', degrees)
            threading.Timer(1, periodControl).start()
        
        def main():
            # 프로그램 시작 후 첫 번째 reading 사용할 수 없다.
            wake_up(i2c_1, Room)
        
            # 일정 주기로 실행되는 periodControl() 함수를 시작 한다.
            periodControl()
            
        if __name__ == "__main__":
            main()	
              

      • 실험을 위한 준비
        • Raspberry Pi와 온도 센서(DS1621)의 I2C 통신선 연결
        • Raspberry Pi GPIO Header의 Pin 배치도를 참고하여 아래와 같이 Raspberry Pi GPIO Header Pin과 온도 센서(DS1621)를 연결 한다.

          • Raspberry Pi SDA1(Pin3) <-> DS1621 SDA(Pin1) : 2-wire serial 통신을 위한 Data input/output pin.
          • Raspberry Pi SCL1(Pin5) <-> DS1621 SCL(Pin2) : 2-wire serial 통신을 위한 Clock input/output pin.
          • Raspberry Pi 3.3V(Pin1) <-> DS1621 VDD(Pin8) : VDD pin.
          • Raspberry Pi GND(Pin9) <-> DS1621 GND(Pin4) : Ground pin.
        • 온도센서 번호(번지) 설정 하기
          • DS1621의 Pin7(A0), Pin6(A1), Pin5(A2)를 사용하여 온도 센서의 번호(번지)를 설정 한다.
          • 온도 센서의 번호(번지)를 설정 예
            • 0번(이 예에서는 0번 장치를 0x48 번지로 설정 하였다.): A0 <- GND, A1 <- GND, A2 <- GND
      • 실험 방법
        • Putty로 Raspberry Pi에 Login 하고, i2c-th-meter-basic.py가 있는 폴더로 이동 한다.
        • 다음 명령으로 i2c-th-meter-basic.py를 실행 한다.
        • python3 i2c-th-meter-basic.py

        • 프로그램이 정상 실행되면 터미널 창에서 온도 출력을 확인 할 수 있다.
    • I2C 통신을 이용한 Embedded Microcontroller 제어(Master(Raspberry PI) - Slave(Embedded Microcontroller)) 예
      • 윗 I2C 통신을 이용한 측정 과 제어 시스템 구성 예에서 Raspberry PI를 Master로 Embedded Microcontroller(ATmega128)를 Slave device로 사용 한다.

      • I2C 통신을 이용한 Embedded Microcontroller 제어를 위한 Raspberry Pi(Master) 측 Python 프로그램 예: i2c-avr-master-basic.py
      • I2C 통신을 이용한 Embedded Microcontroller 제어를 위한 ATmega128 보드(Slave) 측 c 예: cho_avr_i2c_slave_basic.zip
      • i2c 통신 중요 c 프로그램 예: avr_i2c_slave_basic.c

      • 학습 포인트
        • Raspberry PI(Master)와 ATmega128(Slave) 사이의 I2C 통신 실험(Python 언어와 SMBus Library 사용)
        • I2C 통신을 위한 ATmega128 프로그램(c 언어 사용) 작성에 대한 이해
        • SMBus 대한 이해
      • 실험을 위한 준비
        • Raspberry Pi와 ATmega128의 I2C 통신선 연결
        • Raspberry Pi GPIO Header의 Pin 배치도를 참고하여 아래와 같이 Raspberry Pi GPIO Header Pin과 ATmega128 보드의 TWI(AVR에서는 I2C 통신을 TWI라고 함) 신호선을 연결 한다.

          • Raspberry Pi SDA1(Pin3) <-> ATmega128 보드의 TWI Data 신호선 : 2-wire serial 통신을 위한 Data input/output pin.
          • Raspberry Pi SCL1(Pin5) <-> ATmega128 보드의 TWI Clock 신호선 : 2-wire serial 통신을 위한 Clock input/output pin.
          • Raspberry Pi GND(Pin9) <-> ATmega128 보드의 GND : Ground pin.
          • 주의: Raspberry Pi 와 Atmega128 보드의 전원선(Vdd)은 서로 연결하면 안 된다. Raspberry Pi는 3.3V를 Atmega128 보드는 5V 전원을 사용하는 경우가 많기 때문임.
      • 실험 방법
        • cho_avr_i2c_slave_basic.zip File을 다운로드 하여 압축을 풀고 실행 File(cho_avr_i2c_slave_basic.hex)을 ATmega128 보드에 Up load 하고 실행 한다.
        • 모니터 프로그램을 ATmega128 보드의 uart0에 연결하고 실행 한다.
        • Putty로 Raspberry Pi에 Login 하고, i2c-avr-master-basic.py가 있는 폴더로 이동 한다.
        • 다음 명령으로 i2c-avr-master-basic.py를 실행 한다.
        • python3 i2c-avr-master-basic.py

        • Putty 창의 메세지에 따라 명령을 입력 하면 ATmega128 보드에 연결된 모니터에 Putty에서 입력한 명령(문자열)이 출력 되고, Putty 창에는 ATmega128에서 전송한 "AVR i2c testing" 메세지가 출력 된다.
    • I2C 통신(Master(Raspberry PI) - Slave(Embedded Microcontroller)) 이용한 LED 제어 예
      • 윗 I2C 통신을 이용한 측정 과 제어 시스템 구성 예에서 Raspberry PI를 Master로 Embedded Microcontroller(ATmega128)를 Slave device로 사용 한다.

      • I2C 통신을 이용한 Embedded Microcontroller의 LED 제어를 위한 Raspberry Pi(Master) 측 Python 프로그램 예: i2c-avr-master-led.py
      • I2C 통신을 이용한 Embedded Microcontroller의 LED 제어를 위한 ATmega128 보드(Slave) 측 c 프로그램 예: cho_avr_i2c_slave_led.zip
      • i2c 통신 중요 c 프로그램 예: avr_i2c_slave_led.c

      • 학습 포인트
        • Raspberry PI(Master)와 ATmega128(Slave) 사이의 I2C 통신 실험(Python 언어와 SMBus Library 사용)
        • LED 제어를 위한 Command 설계와 ATmega128 제어 프로그램 작성에 대한 이해
      • 실험을 위한 준비
        • Raspberry Pi와 ATmega128의 I2C 통신선 연결
        • Raspberry Pi GPIO Header의 Pin 배치도를 참고하여 아래와 같이 Raspberry Pi GPIO Header Pin과 ATmega128 보드의 TWI 신호선을 연결 한다.

          • Raspberry Pi SDA1(Pin3) <-> ATmega128 보드의 TWI Data 신호선 : 2-wire serial 통신을 위한 Data input/output pin.
          • Raspberry Pi SCL1(Pin5) <-> ATmega128 보드의 TWI Clock 신호선 : 2-wire serial 통신을 위한 Clock input/output pin.
          • Raspberry Pi GND(Pin9) <-> ATmega128 보드의 GND : Ground pin.
          • 주의: Raspberry Pi 와 Atmega128 보드의 전원선(Vdd)은 서로 연결하면 안 된다. Raspberry Pi는 3.3V를 Atmega128 보드는 5V 전원을 사용하는 경우가 많기 때문임.
        • 8Bit-LED-array-module 제작 예를 참고하여 ATmega128의 PORTF에 LED Array를 연결 한다.
      • 실험 방법
        • cho_avr_i2c_slave_led.zip File을 다운로드 하여 압축을 풀고 실행 File(cho_avr_i2c_slave_led.hex)을 ATmega128 보드에 Up load 하고 실행 한다.
        • Putty로 Raspberry Pi에 Login 하고, i2c-avr-master-led.py가 있는 폴더로 이동 한다.
        • 다음 명령으로 i2c-avr-master-led.py를 실행 한다.
        • python3 i2c-avr-master-led.py

        • Putty 창의 메세지에 따라 명령을 입력 하며 ATmega128 보드에 연결된 LED의 동작이 명령과 일치 하는지 확인 한다.
  • Web 환경과 I2C 통신을 이용한 디지털 온도 측정(DS1621) 예
    • Web 환경에서 I2C 통신을 이용한 측정 과 제어 시스템 구성 예


      Web 환경에서 디지털 온도 측정(DS1621)을 위한 Web Page 예

    • 원격 측정(I2C 통신(디지털 온도계)) 프로그램 예: web-i2c-th-meter.zip
    • 학습 포인트
      • Web 환경을 이용한 원격 측정(제어)에 대한 이해
      • I2C 통신을 사용한 Server 와 Device(이 예에서는 DS1621 온도센서) 사이의 Data 송수신에 대한 이해
      • Timer를 사용하여 일정한 주기 간격으로 측정 하기.
      • DS1621 온도센서에 대한 이해.
    • 실험을 위한 준비
      • Raspberry Pi와 온도 센서(DS1621)의 I2C 통신선 연결
      • Raspberry Pi GPIO Header의 Pin 배치도를 참고하여 아래와 같이 Raspberry Pi GPIO Header Pin과 온도 센서(DS1621)를 연결 한다.

        • Raspberry Pi SDA1(Pin3) <-> DS1621 SDA(Pin1) : 2-wire serial 통신을 위한 Data input/output pin.
        • Raspberry Pi SCL1(Pin5) <-> DS1621 SCL(Pin2) : 2-wire serial 통신을 위한 Clock input/output pin.
        • Raspberry Pi 3.3V(Pin1) <-> DS1621 VDD(Pin8) : VDD pin.
        • Raspberry Pi GND(Pin9) <-> DS1621 GND(Pin4) : Ground pin.
      • 온도센서 번호(번지) 설정 하기(이 예에서는 2개의 온도 센서를 사용 하였음)
        • DS1621의 Pin7(A0), Pin6(A1), Pin5(A2)를 사용하여 온도 센서의 번호(번지)를 설정 한다.
        • 온도 센서의 번호(번지)를 설정 예
          • 0번(0x48 번지): A0 <- GND, A1 <- GND, A2 <- GND
          • 1번(0x49 번지): A0 <- VDD, A1 <- GND, A2 <- GND
    • 실험 방법
      • 적당한 폴더에 Server 측 프로그램(web-i2c-th-meter.py)을 복사하여 web-i2c-th-meter.py File을 만든다.
      • web-i2c-th-meter.py 가 있는 폴더(예: webapp)에 templates 폴더를 만들고, web-i2c-th-meter.htm을 복사 한다.
      • Putty로 Raspberry Pi에 Login 하고, web-i2c-th-meter.py가 있는 폴더로 이동 한다.
      • 다음 명령으로 web-i2c-th-meter.py를 실행 한다.
      • python3 web-i2c-th-meter.py

      • Chrome browser를 실행하고 아래 예를 참고하여 Web server에 연결 하면 web-i2c-th-meter.htm 페이지가 Load 된다.
      • http://Raspberry Pi의 IP 주소:2880/

        2880은 공유기에서 포트포워드로 설정한 포트 번호 이다.

      • web-i2c-th-meter.htm 페이지에서 "실내 온도 읽어 오기" 와 "실외 온도 읽어 오기" 버튼을 이용하여 온도를 측정 한다.
      • 1초 간격으로 온도가 자동으로 갱신되도록 하려면 web-i2c-th-meter.htm 페이지의 onLoadFunc() 함수 내의 clockTime() 함수의 Comment 처리를 해제 한다.
  • Web 환경에서 Raspberry PI와 ATmega128 사이 i2c 통신을 이용한 LED 제어
    • 윗 Web 환경에서 I2C 통신을 이용한 측정 과 제어 시스템 구성 예에서 Raspberry PI를 Master로 ATmega128 보드(LED 연결)를 Slave device로 사용 한다.

      Web 환경에서 LED 제어 위한 Web Page 예

    • Web 환경에서 I2C 통신을 이용한 LED 제어 프로그램(Raspberry PI 에서 실행되는 Server와 Client 프로그램) 예: web-i2c-avr-led.zip
    • 학습 포인트
      • Web 환경을 이용한 원격 제어에 대한 이해
      • I2C 통신을 사용한 Master 와 Slave Device(이 예에서는 ATmega128 보드) 사이의 Data 송수신에 대한 이해
      • Embedded Microcontroller의 LED 제어를 위한 ATmega128 보드(Slave) 측 c 프로그램 작성
      • ATmega128의 Timer interrupt를 사용하여 일정한 주기로 제어 하기.
    • 실험을 위한 준비
      • Raspberry Pi와 ATmega128의 I2C 통신선 연결
      • Raspberry Pi GPIO Header의 Pin 배치도를 참고하여 아래와 같이 Raspberry Pi GPIO Header Pin과 ATmega128 보드의 TWI 신호선을 연결 한다.

        • Raspberry Pi SDA1(Pin3) <-> ATmega128 보드의 TWI Data 신호선 : 2-wire serial 통신을 위한 Data input/output pin.
        • Raspberry Pi SCL1(Pin5) <-> ATmega128 보드의 TWI Clock 신호선 : 2-wire serial 통신을 위한 Clock input/output pin.
        • Raspberry Pi GND(Pin9) <-> ATmega128 보드의 GND : Ground pin.
        • 주의: Raspberry Pi 와 Atmega128 보드의 전원선(Vdd)은 서로 연결하면 안 된다. Raspberry Pi는 3.3V를 Atmega128 보드는 5V 전원을 사용하는 경우가 많기 때문임.
      • 8Bit-LED-array-module 제작 예를 참고하여 ATmega128의 PORTF에 LED Array를 연결 한다.
    • 실험 방법
      • 적당한 폴더에 Server 측 프로그램(web-i2c-avr-led.py.py)을 복사하여 web-i2c-avr-led.py.py File을 만든다.
      • web-i2c-avr-led.py.py 가 있는 폴더(예: webapp)에 templates 폴더를 만들고, web-i2c-avr-led.htm을 복사 한다.
      • Putty로 Raspberry Pi에 Login 하고, web-i2c-th-meter.py가 있는 폴더로 이동 한다.
      • 다음 명령으로 web-i2c-avr-led.py를 실행 한다.
      • python3 web-i2c-avr-led.py

      • Chrome browser를 실행하고 아래 예를 참고하여 Web server에 연결 하면 web-i2c-avr-led.htm 페이지가 Load 된다.
      • http://Raspberry Pi의 IP 주소:2880/

        2880은 공유기에서 포트포워드로 설정한 포트 번호 이다.

      • web-i2c-avr-led.htm 페이지의 LED 제어 버튼을 이용하여 ATmega128 보드의 LED를 제어 한다.