i2cProject_with_Jetson

Jetson Nano와 아두이노 간의 I2C 통신 2)

찬영_00 2024. 11. 3. 10:40

이번 포스팅에서는 2개의 아두이노 메가를 I2C로 제어해 볼 생각이다.

 

이전 포스팅에서 이어서 작성하는 것이니 이전 포스팅을 참고바란다.

 

이전 1에서 아두이노에 업로드한 코드를 새로운 아두이노 메가 2에도 업로드 해주자

업로드 할때 Slave 주소를 0x60으로 바꾸는 것을 잊지마라

이후 밑의 글을 진행하면 된다.

 

젯슨나노에 2개의 아두이노 I2C 배선

 

보기 어려움으로 다음을 다음 회로를 제공하겠다.

 

연결후 잘 연결되었는지 확인한다.

sudo i2cdetect -r -y 1

 

 

만약 주소가 다 안뜨면 배선을 의심하고, 선을 의심하자!

선을 바꾸고, 테스트기로도 정상이면 보드를 의심해라.

 

여튼 이렇게 인식이 완료되면 연결 성공이다.

이제 2개를 제어하는 젯슨나노 코드를 짜보자

 

젯슨에서 코드 짜기

#include <iostream>
#include <fcntl.h>
#include <unistd.h>
#include <linux/i2c-dev.h>
#include <sys/ioctl.h>
#include <signal.h>
#include <cstring>
#include <chrono>
#include <thread>

#define I2C_DEVICE      "/dev/i2c-1"  // Jetson Nano의 I2C 포트
#define SLAVE_ADDRESS_1 0x57          // 첫 번째 아두이노의 I2C 주소
#define SLAVE_ADDRESS_2 0x60          // 두 번째 아두이노의 I2C 주소
#define WRITE_REGISTER  0x01          // LED 제어를 위한 레지스터 주소
#define LED_ON          0xAC          // LED 켜기 명령
#define LED_OFF         0x1F          // LED 끄기 명령

int file;  // I2C 파일 디스크립터

// I2C 장치 열기
int openI2CDevice(int address) {
    int f = open(I2C_DEVICE, O_RDWR);
    if (f < 0) {
        std::cerr << "I2C 장치를 열 수 없습니다." << std::endl;
        return -1;
    }

    // 슬레이브 주소 설정
    if (ioctl(f, I2C_SLAVE, address) < 0) {
        std::cerr << "슬레이브 주소를 설정할 수 없습니다." << std::endl;
        close(f);
        return -1;
    }
    return f;
}

// LED 제어 함수
void controlLED(int file, bool turnOn) {
    unsigned char buffer[2];
    buffer[0] = WRITE_REGISTER;
    buffer[1] = turnOn ? LED_ON : LED_OFF;

    if (write(file, buffer, 2) != 2) {
        std::cerr << "데이터 전송에 실패했습니다." << std::endl;
    } else {
        std::cout << "LED " << (turnOn ? "켜기" : "끄기") << " 명령을 보냈습니다." << std::endl;
    }
}

// 깜빡임 제어 함수
void blinkLED(int file, int delaySeconds) {
    for (int i = 0; i < 2; ++i) {  // 2번 깜빡임
        controlLED(file, true);
        std::this_thread::sleep_for(std::chrono::seconds(delaySeconds));
        controlLED(file, false);
        std::this_thread::sleep_for(std::chrono::seconds(delaySeconds));
    }
}

// 프로그램 종료 시 클린업
void cleanup(int signum) {
    if (file >= 0) {
        close(file);
    }
    std::cout << "\n프로그램을 종료합니다." << std::endl;
    exit(0);
}

// 명령어 파싱 함수
bool parseCommand(const std::string &command, int &arduinoNum, std::string &action, int &delaySeconds) {
    if (command.size() < 4 || command[0] != 'M' || command[2] != '_') return false;

    arduinoNum = command[1] - '0';  // 아두이노 번호 추출
    action = command.substr(3);     // 명령어 추출

    if (action[0] == 'D') {  // 깜빡임 명령어일 경우
        delaySeconds = std::stoi(action.substr(1));
    }
    return true;
}

int main() {
    // 시그널 핸들러 등록
    signal(SIGINT, cleanup);

    std::string command;
    int address;
    int arduinoNum, delaySeconds;
    std::string action;

    while (true) {
        std::cout << "M?_ON, M?_OFF, M?_D??으로 명령을 줄 수 있습니다.\n?는 아두이노 번호이고, ??는 깜빡이 딜레이(초)입니다." << std::endl;
        std::cin >> command;

        // 명령어 파싱
        if (!parseCommand(command, arduinoNum, action, delaySeconds)) {
            std::cerr << "잘못된 명령 형식입니다." << std::endl;
            continue;
        }

        // 아두이노 주소 설정
        address = (arduinoNum == 1) ? SLAVE_ADDRESS_1 : SLAVE_ADDRESS_2;
        file = openI2CDevice(address);
        if (file < 0) continue;

        // 명령에 따라 LED 제어
        if (action == "ON") {
            controlLED(file, true);
        } else if (action == "OFF") {
            controlLED(file, false);
        } else if (action[0] == 'D') {
            blinkLED(file, delaySeconds);
        } else {
            std::cerr << "알 수 없는 명령입니다." << std::endl;
        }

        close(file);  // I2C 장치 닫기
    }

    cleanup(0);  // 프로그램 종료 시 클린업 함수 호출
    return 0;
}

 

이 코드를 컴파일하고 실행시켜주면 된다.

 

 

https://youtu.be/wbs0w73WDeE

 

이렇게 슬레이브에 마스터가 명령어를 보내는 것까진 확인이 되었다.

 

다음 포스터에서는 슬레이브가 마스터에게 데이터를 보내는 것을 해보겠다.