개발자/Arduino

아두이노에서 멀티태스킹 구현하기 4 - Multi-tasking the arduino

지구빵집 2017. 4. 21. 16:06
반응형

 

 

 

아두이노에서 멀티태스킹 구현하기 4 - Multi-tasking the arduino

 

모든 원문 내용과 코드 이미지 출처는 https://learn.adafruit.com/multi-tasking-the-arduino-part-3/ 에 있고, 구글 번역기와 편집을 실행함.

 

 

전체 포스팅한 자료는 아래와 같다.

아두이노에서 멀티태스킹 구현하기 1 - Multi-tasking the arduino : Blink without delay

아두이노에서 멀티태스킹 구현하기 2 - Multi-tasking the arduino : Blink without delay

아두이노에서 멀티태스킹 구현하기 3 - Multi-tasking the arduino : Blink without delay

아두이노에서 멀티태스킹 구현하기 3.5 - Multi-tasking the arduino 라이브러리 링크 페이지

아두이노에서 멀티태스킹 구현하기 4 - Multi-tasking the arduino

 

 

 

 

 

Neopixel과 같은 디지털 RGB LED는 놀라운 디스플레이 및 조명 효과를 생성하는 데 아주 좋습니다. 그러나 그것들을 대화식 프로젝트에 통합하는 것은 어려울 수 있습니다. Arduino는 한 번에 한 가지만하는 것을 좋아하는 단발성 작은 프로세서입니다. 그렇다면 외부 입력에주의를 기울여 픽셀 픽셀 패턴을 생성하는 방법은 무엇입니까?

 

Adafruit 포럼에서 가장 일반적인 Neopixel 질문 중 일부는 다음과 같습니다.

Neopixel 프로젝트가 버튼 프레스에 안정적으로 반응하도록하려면 어떻게해야합니까?

어떻게 두 개 (또는 그 이상)의 서로 다른 Neopixel 패턴을 동시에 실행할 수 있습니까?

 

내 Neopixel 패턴이 실행되는 동안 내 Arduino가 다른 작업을 수행하도록하려면 어떻게해야합니까?

이 가이드에서는 Neopixel 코드를 구조화하여 응답 성을 유지하고 멀티 태스킹에보다 적합하도록하는 몇 가지 방법을 살펴 보겠습니다.

 

문제는 루프 와 지연

 

사실상 모든 예제 코드는 픽셀 애니메이션의 다양한 단계를 거치는 루프로 구성됩니다. 코드는 메인 루프가 스위치를 확인할 기회가 결코없는 픽셀을 업데이트하는 데 너무 바쁩니다.

그러나 정말로 바쁜가? 실제로이 코드는 대부분 시간을 보내고 아무 것도하지 않습니다! 이것은 타이밍이 모두 delay ()에 대한 호출로 완료되기 때문입니다. 이 시리즈의 일부 (http://adafru.it/mEd)에서 보았 듯이 지연은 말 그대로 총 시간 낭비입니다. 우리는 확실히 지연을 버리고 싶습니다.

 

그러나 그것은 여전히 우리에게 고리를 남긴다. 동시에 두 개의 애니메이션 패턴을 수행하는 하나의 루프를 작성하려고 시도하면 코드가 실제로 너무 못 생겨납니다. 루프도 잃어 버릴 필요가 있습니다.

 

루프 해체

 

이유없이 중단하는 사람이 되지 마라.

반응성 문제에 대해 일반적으로 제안되는 해결책 중 하나는 루프 내부의 스위치를 확인하고 스위치를 누르면 루프를 종료하는 것입니다. 스위치 점검은 지연과 잘 결합 될 수 있습니다.

 

그리고 루프는 일찍 종료하도록 다시 작성할 수 있다.

이것은 당신의 프로그램을 좀 더 적극적으로 만들지만, 접근법에 몇 가지 근본적인 문제가 있습니다 :

여전히 delay () (http://adafru.it/mEd)를 사용하고 있으므로 한 번에 두 개 (또는 그 이상)의 패턴을 수행 할 수 없습니다. 그리고 정말로 그만두기를 원하지 않는다면? 우리는 패턴의 실행을 반드시 방해하지 않고 응답 성을 유지하는 방법을 원합니다.

방해가 도움이 될까요?

 

우리는이 시리즈의 Part 2 (http://adafru.it/mEe)에서 인터럽트 사용법을 배웠다. 그러나 그것들은이 문제들에 대한 완전한 답이 아닙니다. 인터럽트를 사용하면 스위치를 폴링 할 필요가 없습니다. 따라서 코드가 조금 더 깔끔하게되지만 위에 나열된 나머지 문제는 실제로 해결되지 않습니다.

 

루프 밖에서 생각하기 :

 

이러한 모든 문제를 해결할 수 있습니다. 그러나 우리는 지연을 버리고 루프를 잃을 필요가있다. 좋은 소식은 resuting 코드가 놀라 울 정도로 간단하다는 것입니다.

 

Neopixel 루프 및 지연 문제는 Part 1 (http://adafru.it/mEf)에서 리니어 엔지니어링 한 서보 스윕 예제와 매우 유사합니다. Neopixel 패턴에도 동일한 상태 시스템 접근법을 적용 할 수 있습니다.

기본 개념은 패턴을 C ++ 클래스로 캡슐화하는 것입니다. 모든 상태 변수를 멤버 변수로 캡처하고 루프 논리의 핵심을 millis ()를 사용하여 타이밍을 관리하는 Update () 함수로 이동합니다.

Update 함수는 loop () 또는 타이머 인터럽트에서 호출 할 수 있으며 동시에 사용자 상호 작용을 모니터링하면서 각 패스에서 많은 패턴을 동시에 업데이트 할 수 있습니다.

 

 

 

공통 코드

 

이 자습서에서는 다음과 같은 인기있는 픽셀 패턴을 다시 엔지니어링합니다.

- 레인보우 사이클

- 색상 닦아 내기

- 극장 체이스

- 스캐너

- 페이더

 

이 패턴을 Adafruit_NeoPixel 클래스에서 파생 된 하나의 "NeoPattern"클래스로 캡슐화하므로 동일한 스트립의 패턴을 자유롭게 변경할 수 있습니다.

그리고 NeoPattern은 Adafruit_NeoPixel에서 파생되므로 NeoPixel 라이브러리의 모든 일반 기능을 사용할 수 있습니다! 패턴 코드 구현에 들어가기 전에. 이 패턴들이 공통적으로 가지고있는 것을 살펴보고, 그 패턴을 관리하기 위해 알아야 할 것이 무엇인지 살펴 보겠습니다. 그로부터 우리는 그들 모두를 다룰 수있는 클래스 구조를 만들 수 있습니다 :

 

멤버 변수 :

아래 클래스 정의는 사용중인 패턴, 실행중인 다이 렉트 및 패턴 상태 시스템의 현재 상태를 포함하는 멤버 변수를 포함합니다.

 

Pro-Tip : 

방향 옵션은 반 시계 방향과 시계 방향의 픽셀 순서를 갖는 네오 픽셀 고리를 다룰 때 편리합니다.

이러한 변수는 속도와 크기 간의 균형을 나타냅니다. 픽셀 당 오버 헤드는 Adafruit_Neopixel 기본 클래스의 오버 헤드와 동일합니다. 대부분의 패턴 상태 변수는 대부분의 패턴에 공통입니다.

Arduino에서 사용할 수있는 제한된 SRAM이 주어지면 일부 계산은 Update () 함수에서 'on-the-fly'로 수행되어 더 많은 픽셀을위한 추가 공간을 절약 할 수 있습니다!

 

완료시 콜백 :

초기화 된 경우 패턴의 각 반복이 완료되면 "OnComplete ()"콜백 함수가 호출됩니다. 패턴 및 / 또는 작동 상태를 변경하는 것을 포함하여 모든 작업을 수행하기 위해 콜백을 정의 할 수 있습니다.

나중에 어떻게 사용하는지 몇 가지 예를 보여 드리겠습니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <Adafruit_NeoPixel.h>
 
// Pattern types supported:
enum  pattern { NONE, RAINBOW_CYCLE, THEATER_CHASE, COLOR_WIPE, SCANNER, FADE };
// Patern directions supported:
enum  direction { FORWARD, REVERSE };
 
// NeoPattern Class - derived from the Adafruit_NeoPixel class
class NeoPatterns : public Adafruit_NeoPixel
{
    public:
 
    // Member Variables:  
    pattern  ActivePattern;  // which pattern is running
    direction Direction;     // direction to run the pattern
    
    unsigned long Interval;   // milliseconds between updates
    unsigned long lastUpdate; // last update of position
    
    uint32_t Color1, Color2;  // What colors are in use
    uint16_t TotalSteps;  // total number of steps in the pattern
    uint16_t Index;  // current step within the pattern
    
    void (*OnComplete)();  // Callback on completion of pattern
cs

 

생성자:

생성자는 기본 클래스와 콜백 함수에 대한 포인터 (선택적)를 초기화합니다.

 

 

1
2
3
4
5
6
    // Constructor - calls base-class constructor to initialize strip
    NeoPatterns(uint16_t pixels, uint8_t pin, uint8_t type, void (*callback)())
    :Adafruit_NeoPixel(pixels, pin, type)
    {
        OnComplete = callback;
    }
cs

 

 

업데이터 :

Update () 함수는 파트 1 (http://adafru.it/mEf)의 Update () 함수와 매우 유사하게 작동합니다. 마지막 업데이트 이후 경과 된 시간을 확인하고 아직 아무것도 할 시간이 없다면 즉시 반환합니다.

실제로 패턴을 업데이트하는 것이 시간이라고 판단하면 ActivePattern 멤버 변수를보고 호출 할 패턴 특정 업데이트 함수를 결정합니다.

나열된 패턴 각각에 대해 초기화 함수와 Update () 함수를 작성해야합니다. 다음 몇 페이지에서이 작업을 수행하는 방법을 보여줍니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// Update the pattern
    void Update()
    {
        if((millis() - lastUpdate) > Interval) // time to update
        {
            lastUpdate = millis();
            switch(ActivePattern)
            {
                case RAINBOW_CYCLE:
                    RainbowCycleUpdate();
                    break;
                case THEATER_CHASE:
                    TheaterChaseUpdate();
                    break;
                case COLOR_WIPE:
                    ColorWipeUpdate();
                    break;
                case SCANNER:
                    ScannerUpdate();
                    break;
                case FADE:
                    FadeUpdate();
                    break;
                default:
                    break;
            }
        }
    }
cs

 

패턴 증가

Increment () 함수는 Direction의 현재 상태를보고 그에 따라 Index를 증가 시키거나 감소시킵니다. 인덱스가 패턴의 끝에 도달하면 패턴을 다시 시작하기 위해 줄 바꿈됩니다.

OnComplete () 콜백 함수가 null이 아닌 경우 호출됩니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
    // Increment the Index and reset at the end
    void Increment()
    {
        if (Direction == FORWARD)
        {
           Index++;
           if (Index >= TotalSteps)
            {
                Index = 0;
                if (OnComplete != NULL)
                {
                    OnComplete(); // call the comlpetion callback
                }
            }
        }
        else // Direction == REVERSE
        {
            --Index;
            if (Index <= 0)
            {
                Index = TotalSteps-1;
                if (OnComplete != NULL)
                {
                    OnComplete(); // call the comlpetion callback
                }
            }
        }
    }
cs

 

 

RainbowCycle

레인보우 사이클은 색상환을 사용하여 스트립의 길이를 순환하는 무지개 효과를 만듭니다. 이것은 라이브러리에서 StrandTest 예제 스케치에있는 RainbowCycle 패턴의 직접적인 재구성입니다.

 

초기화 :

RainbowCycle () 함수는 Rainbow Cycle 패턴을 실행하기 위해 NeoPatterns 클래스를 초기화합니다. ActivePattern은 RAINBOW_CYCLE로 설정됩니다.

"간격"매개 변수는 패턴 간격을 결정하는 업데이트 간격 (밀리 초)을 지정합니다.

"dir"매개 변수는 선택 사항입니다. 기본값은 FORWARD 조작이지만, REVERSE를 지정할 수 있습니다.

TotalSteps는 색상환의 색상 수인 255로 설정됩니다.

인덱스는 0으로 설정되어 있으므로 휠의 첫 번째 색상부터 시작합니다.

 

1
2
3
4
5
6
7
8
    // Initialize for a RainbowCycle    void RainbowCycle(uint8_t interval, direction dir = FORWARD)
    {
        ActivePattern = RAINBOW_CYCLE;
        Interval = interval;
        TotalSteps = 255;
        Index = 0;
        Direction = dir;
    }
cs

 

 

업데이트:

RainbowCycleUpdate () 함수는 스트립의 각 픽셀을 휠의 색상으로 설정합니다. 휠의 시작점은 인덱스에서 계산됩니다.

show ()를 호출하여 스트립에 기록한 후 Increment ()를 호출하여 색인을 업데이트합니다.

 

1
2
3
4
5
6
7
8
9
10
    // Update the Rainbow Cycle Pattern
    void RainbowCycleUpdate()
    {
        for(int i=0; i< numPixels(); i++)
        {
            setPixelColor(i, Wheel(((i * 256 / numPixels()) + Index) & 255));
        }
        show();
        Increment();
    }
cs

 

 

ColorWipe by Bill Earl

 

ColorWipe 패턴은 한 번에 한 픽셀 씩 스트립의 길이에 걸쳐 색상을 칠합니다.

 

초기화 :

ColorWipe () 함수는 NeoPattern 객체를 초기화하여 ColorWipe 패턴을 실행합니다.

color 매개 변수는 스트립에서 '닦아 낼'색상을 지정합니다.

interval 매개 변수는 지우기에서 연속 픽셀 사이의 밀리 초 수를 지정합니다.

direction 매개 변수는 선택 사항이며 지우기 방향을 지정합니다. 기본 방향이 지정되지 않은 경우 FORWARD입니다. TotalSteps는 스트립의 픽셀 수로 설정됩니다.

 

1
2
3
4
5
6
7
8
9
10
// Initialize for a ColorWipe
    void ColorWipe(uint32_t color, uint8_t interval, direction dir = FORWARD)
    {
        ActivePattern = COLOR_WIPE;
        Interval = interval;
        TotalSteps = numPixels();
        Color1 = color;
        Index = 0;
        Direction = dir;
    }
cs

 

업데이트:

  ColorWipeUpdate () 함수는 스트립의 다음 픽셀을 설정 한 다음 스트립에 쓰기 위해 show ()를 호출하고 인덱스를 업데이트하기 위해 Increment ()를 호출합니다.

 

1
2
3
4
5
6
7
    // Update the Color Wipe Pattern
    void ColorWipeUpdate()
    {
        setPixelColor(Index, Color1);
        show();
        Increment();
    }
cs

 

 

극장 체이스

 

TheaterChase 패턴은 50 년대 시대 극장의 윤곽에서 고전 추적 패턴을 에뮬레이트합니다. 이 변형에서는 쫓는 등화에 대한 전경색과 배경색을 지정할 수 있습니다.

 

초기화 :

TheaterChase () 함수는 TheaterChase 패턴을 실행하기 위해 NeoPattern 객체를 초기화합니다.

color1 및 color2 매개 변수는 패턴의 전경색 및 배경색을 지정합니다.

increment 매개 변수는 패턴 업데이트 사이의 밀리 초 수를 지정하고 추적 효과의 속도를 제어합니다.

direction 매개 변수는 선택적이며 FORWARD (기본값) 또는 REVERSE에서 패턴을 실행할 수 있습니다.

TotalSteps는 스트립의 픽셀 수로 설정됩니다.

 

1
2
3
4
5
6
7
8
9
10
11
    // Initialize for a Theater Chase
    void TheaterChase(uint32_t color1, uint32_t color2, uint8_t interval, direction dir = FORWARD)
    {
        ActivePattern = THEATER_CHASE;
        Interval = interval;
        TotalSteps = numPixels();
        Color1 = color1;
        Color2 = color2;
        Index = 0;
        Direction = dir;
   }
cs

 

업데이트:

TheaterChaseUpdate ()는 추적 패턴을 한 픽셀 씩 진행합니다. 모든 3 픽셀은 전경색 (color1)으로 그려집니다. 나머지는 모두 color2로 그려져 있습니다.

 

show ()를 통해 픽셀 색상을 스트립에 쓴 후 Increment를 호출하여 Index를 업데이트합니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
    // Update the Theater Chase Pattern
    void TheaterChaseUpdate()
    {
        for(int i=0; i< numPixels(); i++)
        {
            if ((i + Index) % 3 == 0)
            {
                setPixelColor(i, Color1);
            }
            else
            {
                setPixelColor(i, Color2);
            }
        }
        show();
        Increment();
    }
cs

 

 

스캐너

스캐너 패턴은 앞뒤로 스캔 한 밝고 선명한 단일 스캔으로 이루어져 있습니다.

초기화 :

Scanner ()는 스캐너 패턴을 실행하기 위해 NeoPattern 객체를 초기화합니다.

색상 매개 변수는 스캔 픽셀의 색상입니다.

interval 매개 변수는 업데이트 사이의 밀리 초 수를 지정하고 검색 속도를 제어합니다.

스캔은 스트립의 한쪽 끝에서 다른 쪽 끝으로의 완전한 라운드 트립으로 간주되고 끝 픽셀에 추가 체류 시간이 없습니다. 따라서 TotalSteps는 픽셀 수에서 2를 뺀 값의 두 배로 설정됩니다.

// SC 초기화

 

1
2
3
4
5
6
7
8
9
    // Initialize for a SCANNNER
    void Scanner(uint32_t color1, uint8_t interval)
    {
        ActivePattern = SCANNER;
        Interval = interval;
        TotalSteps = (Strip->numPixels() - 1* 2;
        Color1 = color1;
        Index = 0;
    }
cs

 

업데이트:

ScannerUpdate ()는 스트립의 픽셀을 반복합니다. 픽셀이 Index 또는 TotalSteps - Index의 현재 값과 일치하면 픽셀을 color1로 설정합니다.

그렇지 않으면 페이드 트레일 효과를 내기 위해 픽셀을 어둡게합니다.

 

다른 모든 패턴과 마찬가지로 ScannerUpdate는 스트립에 쓰기 위해 show ()를 호출하고 상태 시스템을 진행시키기 위해 Increment ()를 호출합니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
    // Update the Scanner Pattern
    void ScannerUpdate()
    { 
        for (int i = 0; i < numPixels(); i++)
        {
            if (i == Index) // first half of the scan
            {
                Serial.print(i);
                setPixelColor(i, Color1);
            }
            else if (i == TotalSteps - Index) // The return trip.
            {
                Serial.print(i);
                setPixelColor(i, Color1);
            }
            else  // fade to black
            {
                setPixelColor(i, DimColor(getPixelColor(i)));
            }
        }
        show();
        Increment();
    }
cs

 

페이더

 

Fader 패턴은 포럼에서 가장 일반적으로 요청되는 Neopixel 효과 중 하나입니다. 이 패턴은 한 색상에서 다른 색상으로 부드러운 선형 페이드를 만듭니다.

초기화 :

Fade ()는 NeoPattern 객체를 초기화하여 Fade 패턴을 실행합니다.

color1 매개 변수는 시작 색상을 지정합니다.

color2는 끝 색상을 지정합니다.

단계는 color1에서 color2로 이동하는 데 걸리는 단계 수를 지정합니다.

간격은 단계 사이의 밀리 초 수를 지정하고 페이드 속도를 제어합니다.

방향은 페이드를 뒤집어서 color2에서 color1로 간다.

 

1
2
3
4
5
6
7
8
9
10
11
    // Initialize for a Fade
    void Fade(uint32_t color1, uint32_t color2, uint16_t steps, uint8_t interval, direction dir = FORWARD)
    {
        ActivePattern = FADE;
        Interval = interval;
        TotalSteps = steps;
        Color1 = color1;
        Color2 = color2;
        Index = 0;
        Direction = dir;
    }
cs

 

업데이트:

FadeUpdate ()는 스트립의 모든 픽셀을 다음 단계에 해당하는 색상으로 설정합니다.

색상은 color1과 color2의 빨강, 녹색 및 파랑 구성 요소 사이의 선형 보간법으로 계산됩니다. 빠르게 유지하기 위해 모든 정수 수학을 사용합니다. 절단 오류를 최소화하기 위해 분할이 마지막으로 수행됩니다.

 

다른 모든 패턴과 마찬가지로 ScannerUpdate는 스트립에 쓰기 위해 show ()를 호출하고 상태 시스템을 진행시키기 위해 Increment ()를 호출합니다.

 

1
2
3
4
5
6
7
8
9
10
// Update the Fade Pattern
    void FadeUpdate()
    {
        uint8_t red = ((Red(Color1) * (TotalSteps - Index)) + (Red(Color2) * Index)) / TotalSteps;
        uint8_t green = ((Green(Color1) * (TotalSteps - Index)) + (Green(Color2) * Index)) / TotalSteps;
        uint8_t blue = ((Blue(Color1) * (TotalSteps - Index)) + (Blue(Color2) * Index)) / TotalSteps;
        ColorSet(Color(red, green, blue));
        show();
        Increment();
    }
cs

 

 

일반적인 유틸리티 함수 - Bill Earl

 

이제 우리는 패턴들 중 어느 것에 의해서라도 사용될 수있는 일반적인 유틸리티 함수를 추가 할 것입니다 :

먼저 Red (), Blue () 및 Green () 함수를 사용합니다. 이것들은 Neopixel Color () 함수의 반대입니다. 픽셀 색상의 빨강, 파랑 및 녹색 구성 요소를 추출 할 수 있습니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
    // Returns the Red component of a 32-bit color
    uint8_t Red(uint32_t color)
    {
        return (color >> 16& 0xFF;
    }
 
    // Returns the Green component of a 32-bit color
    uint8_t Green(uint32_t color)
    {
        return (color >> 8& 0xFF;
    }
 
    // Returns the Blue component of a 32-bit color
    uint8_t Blue(uint32_t color)
    {
        return color & 0xFF;
    }
cs

 

DimColor () 함수는 Red (), Green () 및 Blue ()를 사용하여 색상을 분리하고 흐리게 처리 한 버전을 다시 구성합니다. 픽셀 색상의 빨강, 녹색 및 파랑 구성 요소를 오른쪽으로 1 비트 씩 이동시켜 효과적으로 2로 나눕니다. 빨간색, 녹색 애너 파란색은 8 비트 값이므로 늦어도 8 번째 호출 후에 검정색으로 희미 해집니다.

이 기능은 스캐너 패턴에 페이딩 테일 효과를 생성하는 데 사용됩니다.

 

1
2
3
4
5
6
// Return color, dimmed by 75% (used by scanner)
uint32_t DimColor(uint32_t color)
{
    uint32_t dimColor = Color(Red(color) >> 1, Green(color) >> 1, Blue(color) >> 1);
    return dimColor;
}
cs

 

다음으로 RainbowCycle 패턴에서 사용되는 Wheel () 함수가 있습니다. 이것은 StrandTest 예제에서 그대로 빌린 것입니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Input a value 0 to 255 to get a color value.
    // The colours are a transition r - g - b - back to r.
    uint32_t Wheel(byte WheelPos)
    {
        WheelPos = 255 - WheelPos;
        if(WheelPos < 85)
        {
            return Color(255 - WheelPos * 30, WheelPos * 3);
        }
        else if(WheelPos < 170)
        {
            WheelPos -= 85;
            return Color(0, WheelPos * 3255 - WheelPos * 3);
        }
        else
        {
            WheelPos -= 170;
            return Color(WheelPos * 3255 - WheelPos * 30);
        }
    }
cs

 

Reverse () 함수는 패턴 실행 방향을 바꿉니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
    // Reverse direction of the pattern
    void Reverse()
    {
        if (Direction == FORWARD)
        {
            Direction = REVERSE;
            Index = TotalSteps-1;
        }
        else
        {
            Direction = FORWARD;
            Index = 0;
        }
    }
cs

 

 

그리고 마지막으로 모든 픽셀을 같은 색으로 동 기적으로 설정하는 ColorSet () 함수를 사용합니다. 페이더 (Fader) 패턴에 사용됩니다.

 

1
2
3
4
5
6
7
8
9
    // Set all pixels to a color (synchronously)
    void ColorSet(uint32_t color)
    {
        for (int i = 0; i < numPixels(); i++)
        {
            setPixelColor(i, color);
        }
        show();
    }
cs

 

 

배선

 

이 가이드의 예제 코드는 16 및 24 픽셀 링과 아래 그림과 같이 3 개의 개별 Arduino 핀에 연결된 16 픽셀 스트립 (실제로 한 쌍의 Neopixel 스틱)을 사용하여 개발되었습니다.

 

푸시 버튼은 사용자 입력에 대한 응답 성을 나타 내기 위해 핀 8과 9에 연결됩니다.

 

 

 

NeoPatterns 사용하기

 

네오 패턴 선언하기

시작하기 전에 NeoPattern 객체를 선언하여 픽셀을 제어해야합니다.

이전 페이지의 배선도를 사용하여 16 및 24 픽셀 링과 2 개의 8 픽셀 스틱으로 구성된 16 픽셀 스트립을 초기화합니다.

 

1
2
3
4
5
// Define some NeoPatterns for the two rings and the stick
//  and pass the adderess of the associated completion routines
NeoPatterns Ring1(245, NEO_GRB + NEO_KHZ800, &Ring1Complete);
NeoPatterns Ring2(166, NEO_GRB + NEO_KHZ800, &Ring2Complete);
NeoPatterns Stick(167, NEO_GRB + NEO_KHZ800, &StickComplete);
cs

 

액션 시작하기

 그리고 일반적인 NeoPixel 스트립과 마찬가지로 begin ()을 호출하여 스트립을 사용하기 전에 모든 것을 초기화해야합니다. 또한 푸시 버튼에 대한 입력 핀을 설정하고 각 스트립에 대해 초기 패턴을 시작합니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Initialize everything and prepare to start
void setup()
{
    // Initialize all the pixelStrips
    Ring1.begin();
    Ring2.begin();
    Stick.begin();
    
    // Enable internal pullups on the switch inputs
    pinMode(8, INPUT_PULLUP);
    pinMode(9, INPUT_PULLUP);
    
    // Kick off a pattern
    Ring1.TheaterChase(Ring1.Color(255,255,0), Ring1.Color(0,0,50), 100);
    Ring2.RainbowCycle(3);
    Ring2.Color1 = Ring1.Color1;
    Stick.Scanner(Ring1.Color(255,0,0), 55);
}
cs

 

패턴 업데이트

모든 것을 원활하게 실행하려면 정기적으로 각 NeoPattern에서 Update () 함수를 호출하면됩니다. 패턴으로부터 내부 루프와 지연을 제거 했으므로 여기에 표시된 loop () 함수 나이 시리즈의 파트 2에 표시된 타이머 인터럽트 핸들러에서 간단하게 수행 할 수 있습니다.

간단한 경우는 정의한 각각의 NeoPattern에 대해 업데이트 함수를 호출하는 것입니다.

 

1
2
3
4
5
6
7
8
// Main loop
void loop()
{
    // Update the rings.
    Ring1.Update();
    Ring2.Update();    
    Stick.Update();  
}
cs

 

사용자 상호 작용

내부 루프가 없으면 코드는 사용자 상호 작용이나 외부 이벤트에 응답 할 수 있습니다. 그리고 이러한 이벤트를 기반으로 패턴이나 색상을 즉시 변경할 수 있습니다.

더 재미있는 일을하기 위해이 기능을 활용하고 루프를 확장하여 두 푸시 버튼의 버튼 누르기에 반응합니다.

버튼 # 1 (8 번 핀)을 누르면 :

Ring1의 패턴을 THEATER_CHASE에서 FADE로 변경하십시오.

Ring2에서 RainbowCycle의 속도 변경하기

막대기를 단색으로 돌리십시오 RED

버튼 # 2 (9 번 핀)을 누르면 :

Ring1의 패턴을 THEATER_CHASE에서 COLOR_WIPE (으)로 변경하십시오.

Ring2의 패턴을 THEATER_CHASE에서 COLOR_WIPE (으)로 변경하십시오.

아무 버튼도 누르지 않으면 모든 패턴이 정상 작동을 재개합니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// Main loop
void loop()
{
    // Update the rings.
    Ring1.Update();
    Ring2.Update();    
    
    // Switch patterns on a button press:
    if (digitalRead(8== LOW) // Button #1 pressed
    {
        // Switch Ring1 to FASE pattern
        Ring1.ActivePattern = FADE;
        Ring1.Interval = 20;
        // Speed up the rainbow on Ring2
        Ring2.Interval = 0;
        // Set stick to all red
        Stick.ColorSet(Stick.Color(25500));
    }
    else if (digitalRead(9== LOW) // Button #2 pressed
    {
        // Switch to alternating color wipes on Rings1 and 2
        Ring1.ActivePattern = COLOR_WIPE;
        Ring2.ActivePattern = COLOR_WIPE;
        Ring2.TotalSteps = Ring2.numPixels();
        // And update tbe stick
        Stick.Update();
    }
    else // Back to normal operation
    {
        // Restore all pattern parameters to normal values
        Ring1.ActivePattern = THEATER_CHASE;
        Ring1.Interval = 100;
        Ring2.ActivePattern = RAINBOW_CYCLE;
        Ring2.TotalSteps = 255;
        Ring2.Interval = min(10, Ring2.Interval);
        // And update tbe stick
        Stick.Update();
    }    
}
cs

 

완료 콜백

혼자 남겨두면 각 패턴이 계속 반복됩니다. 선택적 완료 콜백은 각 패턴 사이클이 완료 될 때 어떤 작업을 수행 할 수있는 기능을 제공합니다.

완료 콜백에서 수행되는 작업은 패턴의 일부 측면을 변경하거나 다른 외부 이벤트를 트리거하는 것일 수 있습니다.

Ring1 완료 콜백

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Ring1 Completion Callback
void Ring1Complete()
{
    if (digitalRead(9== LOW)  // Button #2 pressed
    {
        // Alternate color-wipe patterns with Ring2
        Ring2.Interval = 40;
        Ring1.Color1 = Ring1.Wheel(random(255));
        Ring1.Interval = 20000;
    }
    else  // Retrn to normal
    {
      Ring1.Reverse();
    }
}
cs

 

Ring1 완료 콜백은 Ring1과 Ring2의 작동에 영향을줍니다. Button # 2의 상태를 봅니다. 이 키를 누르면 다음과 같이됩니다.

간격을 줄임으로써 Ring2 속도를 높입니다. 이것은 효과적으로 Ring2를 깨 웁니다.

Interval을 증가시켜 Ring1을 천천히하십시오. 이것은 효과적으로 Ring1을 잠자기 상태로 만듭니다.

Ring1 패턴의 다음 반복을 위해 휠에서 radom 색상을 선택합니다.

 아무 버튼도 누르지 않으면, 단순히 정상 체이스 패턴의 방향을 바꾼다.

 

Ring2 완료 콜백

 

 Ring2 완료 콜백은 Ring1 완료 콜백을 보완합니다.

버튼 # 2를 누르면 다음과 같이됩니다.

간격을 줄임으로써 Ring1 속도를 높입니다. 이것은 효과적으로 Ring1을 깨 웁니다.

간격을 늘림으로써 Ring2를 천천히하십시오. 이것은 효과적으로 Ring2를 잠자기 상태로 만듭니다.

Ring2 패턴의 다음 반복을 위해 휠에서 임의의 색상을 선택합니다.

버튼을 누르지 않으면, 정상적인 Ring2 Rainbow Cycle 패턴의 다음 반복을위한 임의의 속도가 선택됩니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Ring 2 Completion Callback
void Ring2Complete()
{
    if (digitalRead(9== LOW)  // Button #2 pressed
    {
        // Alternate color-wipe patterns with Ring1
        Ring1.Interval = 20;
        Ring2.Color1 = Ring2.Wheel(random(255));
        Ring2.Interval = 20000;
    }
    else  // Retrn to normal
    {
        Ring2.RainbowCycle(random(0,10));
    }
}
cs

 

스틱 완료 콜백

 

Stick completon 콜백은 스캐너 패턴의 다음 패스를 위해 휠에서 임의의 색상을 선택합니다.

 

1
2
3
4
5
6
// Stick Completion Callback
void StickComplete()
{
    // Random color change for next scan
    Stick.Color1 = Stick.Wheel(random(255));
}
cs

 

 

모든 코드를 하나로 합치면

 

아래의 코드는 완벽한 NeoPattern 클래스와 함께 Arduino 스케치 형태의 '테스트 드라이브'코드를 포함합니다. 이 코드를 복사하여 IDE에 붙여넣고 배선도에 표시된대로 픽셀을 연결합니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
#include <Adafruit_NeoPixel.h>
 
// Pattern types supported:
enum  pattern { NONE, RAINBOW_CYCLE, THEATER_CHASE, COLOR_WIPE, SCANNER, FADE };
// Patern directions supported:
enum  direction { FORWARD, REVERSE };
 
// NeoPattern Class - derived from the Adafruit_NeoPixel class
class NeoPatterns : public Adafruit_NeoPixel
{
    public:
 
    // Member Variables:  
    pattern  ActivePattern;  // which pattern is running
    direction Direction;     // direction to run the pattern
    
    unsigned long Interval;   // milliseconds between updates
    unsigned long lastUpdate; // last update of position
    
    uint32_t Color1, Color2;  // What colors are in use
    uint16_t TotalSteps;  // total number of steps in the pattern
    uint16_t Index;  // current step within the pattern
    
    void (*OnComplete)();  // Callback on completion of pattern
    
    // Constructor - calls base-class constructor to initialize strip
    NeoPatterns(uint16_t pixels, uint8_t pin, uint8_t type, void (*callback)())
    :Adafruit_NeoPixel(pixels, pin, type)
    {
        OnComplete = callback;
    }
    
    // Update the pattern
    void Update()
    {
        if((millis() - lastUpdate) > Interval) // time to update
        {
            lastUpdate = millis();
            switch(ActivePattern)
            {
                case RAINBOW_CYCLE:
                    RainbowCycleUpdate();
                    break;
                case THEATER_CHASE:
                    TheaterChaseUpdate();
                    break;
                case COLOR_WIPE:
                    ColorWipeUpdate();
                    break;
                case SCANNER:
                    ScannerUpdate();
                    break;
                case FADE:
                    FadeUpdate();
                    break;
                default:
                    break;
            }
        }
    }
  
    // Increment the Index and reset at the end
    void Increment()
    {
        if (Direction == FORWARD)
        {
           Index++;
           if (Index >= TotalSteps)
            {
                Index = 0;
                if (OnComplete != NULL)
                {
                    OnComplete(); // call the comlpetion callback
                }
            }
        }
        else // Direction == REVERSE
        {
            --Index;
            if (Index <= 0)
            {
                Index = TotalSteps-1;
                if (OnComplete != NULL)
                {
                    OnComplete(); // call the comlpetion callback
                }
            }
        }
    }
    
    // Reverse pattern direction
    void Reverse()
    {
        if (Direction == FORWARD)
        {
            Direction = REVERSE;
            Index = TotalSteps-1;
        }
        else
        {
            Direction = FORWARD;
            Index = 0;
        }
    }
    
    // Initialize for a RainbowCycle
    void RainbowCycle(uint8_t interval, direction dir = FORWARD)
    {
        ActivePattern = RAINBOW_CYCLE;
        Interval = interval;
        TotalSteps = 255;
        Index = 0;
        Direction = dir;
    }
    
    // Update the Rainbow Cycle Pattern
    void RainbowCycleUpdate()
    {
        for(int i=0; i< numPixels(); i++)
        {
            setPixelColor(i, Wheel(((i * 256 / numPixels()) + Index) & 255));
        }
        show();
        Increment();
    }
 
    // Initialize for a Theater Chase
    void TheaterChase(uint32_t color1, uint32_t color2, uint8_t interval, direction dir = FORWARD)
    {
        ActivePattern = THEATER_CHASE;
        Interval = interval;
        TotalSteps = numPixels();
        Color1 = color1;
        Color2 = color2;
        Index = 0;
        Direction = dir;
   }
    
    // Update the Theater Chase Pattern
    void TheaterChaseUpdate()
    {
        for(int i=0; i< numPixels(); i++)
        {
            if ((i + Index) % 3 == 0)
            {
                setPixelColor(i, Color1);
            }
            else
            {
                setPixelColor(i, Color2);
            }
        }
        show();
        Increment();
    }
 
    // Initialize for a ColorWipe
    void ColorWipe(uint32_t color, uint8_t interval, direction dir = FORWARD)
    {
        ActivePattern = COLOR_WIPE;
        Interval = interval;
        TotalSteps = numPixels();
        Color1 = color;
        Index = 0;
        Direction = dir;
    }
    
    // Update the Color Wipe Pattern
    void ColorWipeUpdate()
    {
        setPixelColor(Index, Color1);
        show();
        Increment();
    }
    
    // Initialize for a SCANNNER
    void Scanner(uint32_t color1, uint8_t interval)
    {
        ActivePattern = SCANNER;
        Interval = interval;
        TotalSteps = (numPixels() - 1* 2;
        Color1 = color1;
        Index = 0;
    }
 
    // Update the Scanner Pattern
    void ScannerUpdate()
    { 
        for (int i = 0; i < numPixels(); i++)
        {
            if (i == Index)  // Scan Pixel to the right
            {
                 setPixelColor(i, Color1);
            }
            else if (i == TotalSteps - Index) // Scan Pixel to the left
            {
                 setPixelColor(i, Color1);
            }
            else // Fading tail
            {
                 setPixelColor(i, DimColor(getPixelColor(i)));
            }
        }
        show();
        Increment();
    }
    
    // Initialize for a Fade
    void Fade(uint32_t color1, uint32_t color2, uint16_t steps, uint8_t interval, direction dir = FORWARD)
    {
        ActivePattern = FADE;
        Interval = interval;
        TotalSteps = steps;
        Color1 = color1;
        Color2 = color2;
        Index = 0;
        Direction = dir;
    }
    
    // Update the Fade Pattern
    void FadeUpdate()
    {
        // Calculate linear interpolation between Color1 and Color2
        // Optimise order of operations to minimize truncation error
        uint8_t red = ((Red(Color1) * (TotalSteps - Index)) + (Red(Color2) * Index)) / TotalSteps;
        uint8_t green = ((Green(Color1) * (TotalSteps - Index)) + (Green(Color2) * Index)) / TotalSteps;
        uint8_t blue = ((Blue(Color1) * (TotalSteps - Index)) + (Blue(Color2) * Index)) / TotalSteps;
        
        ColorSet(Color(red, green, blue));
        show();
        Increment();
    }
   
    // Calculate 50% dimmed version of a color (used by ScannerUpdate)
    uint32_t DimColor(uint32_t color)
    {
        // Shift R, G and B components one bit to the right
        uint32_t dimColor = Color(Red(color) >> 1, Green(color) >> 1, Blue(color) >> 1);
        return dimColor;
    }
 
    // Set all pixels to a color (synchronously)
    void ColorSet(uint32_t color)
    {
        for (int i = 0; i < numPixels(); i++)
        {
            setPixelColor(i, color);
        }
        show();
    }
 
    // Returns the Red component of a 32-bit color
    uint8_t Red(uint32_t color)
    {
        return (color >> 16& 0xFF;
    }
 
    // Returns the Green component of a 32-bit color
    uint8_t Green(uint32_t color)
    {
        return (color >> 8& 0xFF;
    }
 
    // Returns the Blue component of a 32-bit color
    uint8_t Blue(uint32_t color)
    {
        return color & 0xFF;
    }
    
    // Input a value 0 to 255 to get a color value.
    // The colours are a transition r - g - b - back to r.
    uint32_t Wheel(byte WheelPos)
    {
        WheelPos = 255 - WheelPos;
        if(WheelPos < 85)
        {
            return Color(255 - WheelPos * 30, WheelPos * 3);
        }
        else if(WheelPos < 170)
        {
            WheelPos -= 85;
            return Color(0, WheelPos * 3255 - WheelPos * 3);
        }
        else
        {
            WheelPos -= 170;
            return Color(WheelPos * 3255 - WheelPos * 30);
        }
    }
};
 
void Ring1Complete();
void Ring2Complete();
void StickComplete();
 
// Define some NeoPatterns for the two rings and the stick
//  as well as some completion routines
NeoPatterns Ring1(245, NEO_GRB + NEO_KHZ800, &Ring1Complete);
NeoPatterns Ring2(166, NEO_GRB + NEO_KHZ800, &Ring2Complete);
NeoPatterns Stick(167, NEO_GRB + NEO_KHZ800, &StickComplete);
 
// Initialize everything and prepare to start
void setup()
{
  Serial.begin(115200);
 
   pinMode(8, INPUT_PULLUP);
   pinMode(9, INPUT_PULLUP);
    
    // Initialize all the pixelStrips
    Ring1.begin();
    Ring2.begin();
    Stick.begin();
    
    // Kick off a pattern
    Ring1.TheaterChase(Ring1.Color(255,255,0), Ring1.Color(0,0,50), 100);
    Ring2.RainbowCycle(3);
    Ring2.Color1 = Ring1.Color1;
    Stick.Scanner(Ring1.Color(255,0,0), 55);
}
 
// Main loop
void loop()
{
    // Update the rings.
    Ring1.Update();
    Ring2.Update();    
    
    // Switch patterns on a button press:
    if (digitalRead(8== LOW) // Button #1 pressed
    {
        // Switch Ring1 to FASE pattern
        Ring1.ActivePattern = FADE;
        Ring1.Interval = 20;
        // Speed up the rainbow on Ring2
        Ring2.Interval = 0;
        // Set stick to all red
        Stick.ColorSet(Stick.Color(25500));
    }
    else if (digitalRead(9== LOW) // Button #2 pressed
    {
        // Switch to alternating color wipes on Rings1 and 2
        Ring1.ActivePattern = COLOR_WIPE;
        Ring2.ActivePattern = COLOR_WIPE;
        Ring2.TotalSteps = Ring2.numPixels();
        // And update tbe stick
        Stick.Update();
    }
    else // Back to normal operation
    {
        // Restore all pattern parameters to normal values
        Ring1.ActivePattern = THEATER_CHASE;
        Ring1.Interval = 100;
        Ring2.ActivePattern = RAINBOW_CYCLE;
        Ring2.TotalSteps = 255;
        Ring2.Interval = min(10, Ring2.Interval);
        // And update tbe stick
        Stick.Update();
    }    
}
 
//------------------------------------------------------------
//Completion Routines - get called on completion of a pattern
//------------------------------------------------------------
 
// Ring1 Completion Callback
void Ring1Complete()
{
    if (digitalRead(9== LOW)  // Button #2 pressed
    {
        // Alternate color-wipe patterns with Ring2
        Ring2.Interval = 40;
        Ring1.Color1 = Ring1.Wheel(random(255));
        Ring1.Interval = 20000;
    }
    else  // Retrn to normal
    {
      Ring1.Reverse();
    }
}
 
// Ring 2 Completion Callback
void Ring2Complete()
{
    if (digitalRead(9== LOW)  // Button #2 pressed
    {
        // Alternate color-wipe patterns with Ring1
        Ring1.Interval = 20;
        Ring2.Color1 = Ring2.Wheel(random(255));
        Ring2.Interval = 20000;
    }
    else  // Retrn to normal
    {
        Ring2.RainbowCycle(random(0,10));
    }
}
 
// Stick Completion Callback
void StickComplete()
{
    // Random color change for next scan
    Stick.Color1 = Stick.Wheel(random(255));
}
cs

 

 

 

반응형