본문 바로가기

작업

라즈베리파이에서 LED 구동하기

개발환경 & 준비물

 

- 라즈베리파이3B+

- WS2812 Flexible LED (https://www.devicemart.co.kr/goods/view?no=1328618)

- ubuntu-18.04.4-preinstalled-server-armhf+raspi3.img.xz

- ROS melodic

- arduino mega

 

우선 라즈베리파이에 우분투와 ROS를 설치해주세요

https://chackhansaram.tistory.com/2  참고

 

step1. 라즈베리파이에 아두이노 설치

https://www.arduino.cc/en/software 접속 후 linux ARM 32 bits 버전 아두이노IDE zip 파일 다운로드

라즈베리파이 home에서 tool이라는 이름의 새폴더를 생성해주고 방금 받은 zip파일을 압축해제합니다. tool이 아니어도 됩니다.

 

아래 명령어를 통해 압축을 푼 폴더로 이동합니다

$ cd arduino-1.8.13
 

그리고 설치를 진행합니다

$ sudo ./install.sh

 

한글폰트를 설치합니다.

 

$ sudo apt install fonts-nanum-coding

$ sudo apt-get update

//입력 후 아두이노 재실행

 

 

step2. rosserial 설치

터미널창에 다음과 같이 입력해주세요.

sudo apt-get install ros-melodic-rosserial ros-melodic-rosserial-server ros-melodic-rosserial-arduino
sudo apt-get update

아두이노IDE실행 - 툴 - 라이브러리 관리 클릭

Adafruit NeoPixel

rosserial

두가지 라이브러리를 설치해줍니다.

혹시 이미 ros.lib이 설치돼있다면 삭제해주세요.

저는 여기까지 진행했을 때 아두이노에서 rosserial 라이브러리에 있는 아무 예제나 컴파일 하면 다음과 같이 에러가 떴습니다.

In file included from /home/kk/Arduino/libraries/ros_lib/std_msgs/Time.h:7:0,
                 from /home/kk/Arduino/libraries/ros_lib/ros/node_handle.h:40,
                 from /home/kk/Arduino/libraries/ros_lib/ros.h:38,
                 from /home/kk/Arduino/libraries/ros_lib/examples/HelloWorld/HelloWorld.pde:6:
/home/kk/Arduino/libraries/ros_lib/ros/msg.h:40:10: fatal error: cstring: No such file or directory
 #include <cstring>
          ^~~~~~~~~
compilation terminated.

exit status 1
Error compiling for board Arduino Leonardo.

~/Arduino/libraries/ros_lib/ros 이 경로에 있는 msg.h라는 파일을 실행하고

#include <cstring> 을 #include <string.h>로 바꿔주고

std::memcpy() 을 memcpy()로 바꿔주니 아두이노 컴파일 에러가 없어졌습니다

참고 : https://answers.ros.org/question/361930/rosserial-arduino-compilation-error-no-cstring/

 

step3. 아두이노 - LED 연결

led의 gnd선과 아두이노 보드의 gnd

led의 5V선과 아두이노 보드의 5V

led의 data선과 아두이노 보드의 6번 핀을 연결해줍니다.

참고 : https://blog.naver.com/no1_devicemart/221325255307

 

터미널 블럭 연결에 대해 :

더보기

지금 실행할 코드의 경우 LED에 따로 전원을 입력해주지 않아도 아두이노 보드만 pc와 연결돼있으면 작동합니다.

파워서플라이로 LED 터미널 블럭과 연결하여 전원을 주면 밝기가 더 밝아졌습니다. 그런데 보드판에서 연기가 나길래 사용하지 않았습니다. 파워서플라이와 LED 전원선 사이에 저항과 커패시터를 추가하면 괜찮을 것 같기도 합니다.

 

step4. 아두이노 코드 컴파일, 업로드

컴파일하기 전에 아두이노 맨 위 툴바에 tool 에서 보드와 포트 설정을 해주세요.

testtestttestsetstset.ino
0.01MB

#include <Adafruit_NeoPixel.h>
#include <Arduino.h>
#include <ros.h>
#include <std_msgs/UInt16MultiArray.h>

//루프문에 불켜는걸 콜백에다가 넣어보자 안되면
//문자열 데이터 
#include <std_msgs/String.h>



#define rePublishTime 1000 // (ms)
//초기 스위치 값

//버튼핀은 define으로 핀 번호를 설정한 뒤에  boolean newState2 = digitalRead(BUTTON_PIN2);으로 값을 받아오자
//ㄴㄴ 위에처럼 하지 말자
//수동 스위치 개념 버리기 버튼 핀을 실제 버튼으로 입력받을 필요가 없음

// 컨트롤 핀
#define PIN            6

// WS2812B 의 연결된 개수
#define NUMPIXELS     576 //아두이노 uno의 경우 데이터처리 한계 때문에 461까지만 가능
#ifdef AVR
 #include <avr/power.h> // Required for 16 MHz Adafruit Trinket
#endif
// 컨트롤 핀
#define PIN            6
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRBW + NEO_KHZ800);
// pixels.Color(a, b, c, d) = pixels.Color(Red, Green, Blue, White)
// Red(빨강), Green(초록), Blue(파랑), White(하양) 의 색상을 각 255단계로 제어

  const int init_switch = 0;
  boolean newState2 = init_switch;
  boolean newState3 = init_switch;
  boolean newState4 = init_switch;
//  boolean newState5 = init_switch; //를 쓰려면 아마 uint32를 써야할거 같은 느낌

//  int ledPin = 13; 굳이 정의 안해도 아두이노에서 정해져 있는듯.
  
  //인풋 스위치 변수 지정
  boolean oldState2 = HIGH; //풀업 인풋은 HIGH가 끊겨있는거임
  boolean oldState3 = HIGH;
  boolean oldState4 = HIGH;
//  boolean oldState5 = HIGH;
  uint32_t b = pixels.Color(0,0,0,255);
  uint32_t c = pixels.Color(255,0,0,0);
  uint32_t d = pixels.Color(0,0,0,0);
  uint32_t e = pixels.Color(0,255,0,0);
  uint32_t f = pixels.Color(0,0,255,0);
//  int inputcmd[3] = { newState2, newState3, newState4 };


//ros 노드 핸들 
ros::NodeHandle  nh;

/*
 * ROS 콜백 함수 
 * 스위치 데이터 받기 / 이게 문자열로 명령을 못 받을 수도 있음
 * 그리고 서보 블로그 참고 예제에서는 servo함수 자체에 포함돼서 그런지 
 * 위에서 미리 선언하지 않은 변수를 바로 콜백함수에서 사용함
 * cmd를 switch로 바꿔주자
 */

//a_led방식으로 트라이
//void switch_cb( const std_msgs::Byte& switch_msg){
//  int i;
//
//  for (i=0; i<3; i++)
//  {
//    inputcmd[i]   = switch_msg.data[i];
//    digitalWrite(13, HIGH-digitalRead(13)); 
//  } 
//}

void switch_cb( const std_msgs::UInt16MultiArray& switch_msg){

    newState2   = switch_msg.data[0];
    newState3   = switch_msg.data[1];
    newState4   = switch_msg.data[2];
//    newState5   = switch_msg.data[3];
    digitalWrite(13, HIGH-digitalRead(13)); 

}



// Subscriber switch 메시지 입력과 콜백 함수와 연결 
ros::Subscriber<std_msgs::UInt16MultiArray> sub("switch", switch_cb);
//ros::Subscriber<std_msgs::Byte> sub("switch", switch_cb);

// Publisher echo_hello  생성 
std_msgs::String str_msg;
ros::Publisher pubTester("echo_hello", &str_msg);

/*
 * 초기 설정 
 */
void setup() {

  //nh.subscribe(sub);
  pixels.begin(); // This initializes the NeoPixel library.
  pixels.setBrightness(20); // LED 밝기 : 255가 최대 0이 최소 입니다.
  pixels.show();
    // LED 핀 설정
  pinMode(13, OUTPUT);
  // 노드 설정
  nh.initNode();
  
  //Subscriber 설정 
  nh.subscribe(sub);

  //Publisher 설정   
  nh.advertise(pubTester);

}

void loop() {
  //이전 시간 기억(ms)
  static unsigned long prevTime = 0;  
  //현재 시간 기억(ms)
  unsigned long currentTime;
  //ROS 콜백 함수 newstate에 명령이 입력된다.
  nh.spinOnce();
  delay(1);

  
  currentTime = millis();
  //publish 시간 체크
  if((currentTime - prevTime) >= rePublishTime)
  {
    str_msg.data = "Hello, This is Testing for ROS Publisher";
    //'hello' 전송 
    pubTester.publish(&str_msg);

    //현재 시간 이전 시간으로 기억
    prevTime = currentTime;
  }

  // 하양
  if((newState2 == LOW) && (newState3 == LOW) && (newState4 == LOW) ) {

    delay(20);
    pixels.fill(b,0,NUMPIXELS);
    pixels.show(); 
    }
    else if((newState2 == LOW) && (oldState2 == HIGH)){

    delay(20);

//    else if(newState2 == LOW){
//    pixels.fill(d,0,NUMPIXELS);
//    pixels.show(); 
//    }
  }

//  // 검정
//  if((newState2 == LOW) && (newState3 == LOW)&&(newState4 == LOW) && (newState5 == HIGH) ) {
//    
//
//    delay(20);
//
//    
//    pixels.fill(d,0,NUMPIXELS);
//    pixels.show(); 
//
//  }
//    else if((newState5 == LOW) && (oldState5 == HIGH)){
//    //GND(-극)로 연결이 돼있다가 연결이 끊기면 LED가 꺼진다
//    delay(20);
//    if(newState5 == LOW){
//    pixels.fill(d,0,NUMPIXELS);
//
//    }
//  }
  
  // 빨강
  if((newState2 == HIGH) && (newState3 == LOW)&&(newState4 == LOW) ) {
    

    delay(20);

    
    pixels.fill(c,0,NUMPIXELS);
    pixels.show(); 

  }
    else if((newState2 == LOW) && (oldState2 == HIGH)){
    //GND(-극)로 연결이 돼있다가 연결이 끊기면 LED가 꺼진다
    delay(20);
    if(newState2 == LOW){
    pixels.fill(d,0,NUMPIXELS);

    }
  }
  //초록
  if((newState3 == HIGH)&& (newState2 == LOW)&&(newState4 == LOW)) {
    // Short delay to debounce button. HZ to GND
    delay(20);
    pixels.fill(e,0,NUMPIXELS);
    pixels.show(); 

    }
    else if((newState3 == LOW) && (oldState3 == HIGH)){
    //GND(-극)로 연결이 돼있다가 연결이 끊기면 LED가 꺼진다
    delay(20);
    if(newState3 == LOW){
    pixels.fill(d,0,NUMPIXELS);
    pixels.show(); 

    }
  }
  
  //파랑
  if((newState4 == HIGH)&& (newState2 == LOW)&&(newState3 == LOW) ) {
    
  
    delay(20);
    pixels.fill(f,0,NUMPIXELS);
    pixels.show(); 
  
    }
    else if((newState4 == LOW) && (oldState4 == HIGH)){
    delay(20);
    if(newState4 == LOW){
    pixels.fill(d,0,NUMPIXELS);
    pixels.show(); 

    }
  }
  
  // PIN2와 PIN3 동시에 ON
  if((newState2 == HIGH) &&(newState3 == HIGH) &&(newState4 == LOW) )
  {

    delay(20);
    pixels.fill(c,0,NUMPIXELS);
    pixels.show(); 
    delay(500);
    pixels.fill(d,0,NUMPIXELS);
    pixels.show(); 



    }
    
  // PIN2와 PIN4 동시에 ON
  if((newState2 == HIGH) &&(newState3 == LOW) &&(newState4 == HIGH) )
  {
    // Short delay to debounce button. HZ to GND
    delay(20);


    pixels.fill(e,0,NUMPIXELS);
    pixels.show(); 
    delay(500);
    pixels.fill(d,0,NUMPIXELS);
    pixels.show(); 



    }


  // PIN3와 PIN4 동시에 ON
  if((newState2 == LOW) &&(newState3 == HIGH) &&(newState4 == HIGH) )
  {
    // Short delay to debounce button. HZ to GND
    delay(20);


    pixels.fill(f,0,NUMPIXELS);
    pixels.show(); 
    delay(500);
    pixels.fill(d,0,NUMPIXELS);
    pixels.show(); 



    }
  // PIN2와 PIN3 PIN4 동시에 ON
  if((newState2 == HIGH) &&(newState3 == HIGH) &&(newState4 == HIGH) )
  {
    // Short delay to debounce button. HZ to GND
    delay(20);

    theaterChaseRainbow(50); 


    }

  
  // Set the last-read button state to the old state.
  oldState2 = newState2;
  oldState3 = newState3;
  oldState4 = newState4;  
//  oldState5 = newState5; 

  delay(300); // 깜빡이는 주기

}

void theaterChaseRainbow(int wait) {
  int firstPixelHue = 0;     // First pixel starts at red (hue 0)
  for(int a=0; a<30; a++) {  // Repeat 30 times...
    for(int b=0; b<3; b++) { //  'b' counts from 0 to 2...
      pixels.clear();         //   Set all pixels in RAM to 0 (off)
      // 'c' counts up from 'b' to end of strip in increments of 3...
      for(int c=b; c<NUMPIXELS; c += 3) {
        // hue of pixel 'c' is offset by an amount to make one full
        // revolution of the color wheel (range 65536) along the length
        // of the strip (strip.numPixels() steps):
        int      hue   = firstPixelHue + c * 65536L / NUMPIXELS;
        uint32_t color = pixels.gamma32(pixels.ColorHSV(hue)); // hue -> RGB
        pixels.setPixelColor(c, color); // Set pixel 'c' to value 'color'
      }
      pixels.show();                // Update strip with new contents
      delay(wait);                 // Pause for a moment
      firstPixelHue += 65536 / 90; // One cycle of color wheel over 90 frames
    }
  }
}

 

실행 순서

roscore

 -> 아두이노 코드 업로드

sudo chmod 766 /dev/ttyACM0
rosrun rosserial_python serial_node.py __name:=arduino _port:=/dev/ttyACM0 _baud:=57600
rostopic pub /switch std_msgs/UInt16MultiArray "{data: [0, 0, 1]}" --once

 

입력해주는 데이터 행렬에 따라 다음과 같이 동작합니다.

  RED GREEN BLUE RED BLINK GREEN BLINK BLUE BLINK LED SHOW
1 1 0 0 1 1 0 1
2 0 1 0 1 0 1 1
3 0 0 1 0 1 1 1

 

 

<수정 버전>


0 : white
1 : 초록 전진 100
2 : 초록 후진 010
4 : 알맹이가 아니라 첨부터 끝까지 드르륵 001
3 : red blink 110
5 : 블링크 대신 쓸 수 있을거 같은 빨강 왔다갔다 101
6 : black 011
7 : rainbow 111

 

- 기존 코드와 다르게 led show가 복잡해졌다. 제어기가 pixel의 led를 바꾸고 밝히는 코드를 수행할 때는 rosserial 호출을 못함 -> 호출 기능을 여러군데에 넣기 시도

- spinOnce를 여러군데 넣어도 여러군데에서 호출 기능이 작동하지 않았다.
- 함수(absss) 안에 spinOnce 명령을 넣으면 제대로 기능한다. -> 함수 군데군데 absss 많이 넣음

 

  white 초록 전진 초록 후진 알맹이가 아니라 첨부터 끝까지 드르륵 red blink 블링크 대신 쓸 수 있을거 같은 빨강 왔다갔다 black rainbow
1 0 1 0 0 1 1 0 1
2 0 0 1 0 1 0 1 1
3 0 0 0 1 0 1 1 1

13_fix_slow.ino
0.05MB

 

'작업' 카테고리의 다른 글

라즈베리파이에서 ydlidar 구동하기  (0) 2021.09.08