메뉴 전체보기

회원메뉴

ATtiny84 동작 감지형 아크릴 LED 플레이트 > 게시판

본문 바로가기

쇼핑몰 검색

NO. 3234
제목 : ATtiny84 동작 감지형 아크릴 LED 플레이트
2026-09-13 10:42

소개: ATtiny84 동작 감지형 아크릴 LED 플레이트


소개: ATtiny84 동작 감지형 아크릴 LED 플레이트


저는 반려견의 사료/물 급식대에 사용할 소형 배터리 구동 동작 표시기로 이것을 만들었습니다. 원래는 나무 플레이트 아래에서 은은하게 빛나는 언더글로를 만들 생각이었지만, 개발 중에 다음과 같이 변경했습니다: 가장자리에서 아크릴을 비추는 4개의 WS2812 LED가 있는 반투명 아크릴 플레이트.

기대했던 것보다 훨씬 잘 작동했습니다. 단순히 아래쪽 영역을 비추는 대신 빛이 아크릴 전체로 퍼져 플레이트 자체가 빛나게 됩니다.

실용적인 목적도 있습니다. Wi-Fi, Home Assistant 또는 외부 로깅 시스템을 사용하지 않고도 낮 동안 반려견이 물그릇이나 사료 그릇을 이용했는지 간단히 확인하고 싶었습니다.




STEP 1. 만드는 방법


만드는 방법


하드웨어:

컨트롤러는 ATtiny84입니다. 이러한 유형의 단일 목적 배터리 장치에는 ESP32가 불필요합니다. ATtiny는 대부분의 시간을 power-down 슬립 모드에서 보내며 PIR 센서에서 직접 깨어날 수 있습니다.

저는 추가 I/O 기능 때문에 주로 ATtiny85 대신 ATtiny84를 선택했습니다. 또한 프로그래밍 중에 애플리케이션 하드웨어를 분리하지 않아도 애플리케이션 연결부와 ISP 프로그래밍 인터페이스를 함께 사용할 수 있었습니다.

동작은 HC-SR505 PIR 센서로 감지하며, WS2812 RGB LED 4개 가 조명을 제공합니다. LED는 반투명 아크릴의 가장자리를 따라 장착되어 있으며, 아크릴이 빛을 분산하고 혼합합니다.

하나의 BC327 PNP 트랜지스터 가 WS2812에 공급되는 5 V 전원을 스위칭합니다. 이는 WS2812 LED가 논리적으로 꺼진 상태에서도 여전히 전력을 소비하기 때문에 중요합니다. 전원을 차단하기 전에 소프트웨어가 LED를 검은색으로 설정하고 데이터 라인을 LOW로 당겨 데이터 입력을 통한 역전원 공급을 방지합니다.

전원은 2700 mAh 18650 리튬 이온 배터리 에서 공급되며, LX-LCBST 충전/부스트 모듈을 거칩니다. 이 모듈은 시스템에 5 V 전원을 공급하고 USB-C를 통해 배터리를 충전할 수 있게 합니다. 보드의 표시등은 충전 중에는 빨간색으로, 충전이 완료되면 파란색으로 켜집니다.

ATtiny84는 Arduino Uno를 ISP로 사용하여프로그래밍합니다. 하드웨어 연결과 ISP 연결은 그대로 유지할 수 있지만, 한 가지 중요한 예외가 있습니다. LX-LCBST에서 나오는 5 V 전원에 점퍼가 있습니다. 프로그래밍 중에는 이 점퍼를 제거하여 Arduino Uno와 LX-LCBST가 모두 5 V 전원 레일을 구동하지 않도록 해야 합니다.

자세한 연결과 핀 할당은 회로도에 나와 있습니다.

소프트웨어 및 활동 카운터

일반적으로 ATtiny84는 power-down 슬립 모드 에 있으며 WS2812 전원은 완전히 차단되어 있습니다.

PIR 센서가 움직임을 감지하면 핀 변경 인터럽트를 발생시켜 ATtiny를 깨웁니다. LED 전원이 켜지고 LED는 다음을 표시하는 데 사용됩니다: 00부터 99까지의 작동 횟수 카운터.

숫자 0~9는 각각 명확하게 구별되는 고유한 RGB 색상을 가집니다. 카운터의 두 숫자는 각각 5초 동안 순차적으로 표시되며, 그 사이에는 짧은 어두운 시간이 들어갑니다. 따라서 4개의 LED는 별도의 화면을 추가하지 않고도 조명과 매우 간단한 2자리 디스플레이 역할을 모두 수행할 수 있습니다.

카운터를 표시한 후 LED는 낮은 밝기로 서서히 어두워집니다. PIR이 활성 상태인 동안에는 희미하게 켜져 있으며, PIR이 LOW로 돌아가면 완전히 꺼집니다. 그러면 ATtiny는 다시 절전 모드로 들어갑니다.

카운터는 의도적으로 RAM에만 저장되므로 장치의 전원을 껐다 켜면 00으로 재설정됩니다. 정확한 기록이 목적이 아니라, 아침과 나중에 낮 동안 카운터를 확인하여 그릇 주변에서 얼마나 많은 활동이 있었는지 알아보는 것이 목적입니다.

저는 Spece Konde의 ATTinyCore와 함께 Arduino IDE를 사용합니다. Arduino Uno를 ISP로 사용할 때의 핀 연결은 다음과 같습니다:

  1. Arduino Uno 5 V → ATtiny84 핀 1 (VCC)
  2. Arduino Uno GND → ATtiny84 핀 14 (GND)
  3. Arduino Uno D10 → ATtiny84 핀 4 (RESET)
  4. Arduino Uno D11 → ATtiny84 핀 7 (MOSI / PA6)
  5. Arduino Uno D12 → ATtiny84 핀 8 (MISO / PA5)
  6. Arduino Uno D13 → ATtiny84 핀 9 (SCK / PA4)

아래 코드를 참조하세요:

#include <avr/sleep.h>
#include <avr/interrupt.h>
#include <Adafruit_NeoPixel.h>

// ------------------------------------------------------------
// Hardware pins
// ------------------------------------------------------------

#define NEO PIN_PA2 // ATtiny84 physical pin 11 - WS2812 data
#define WAKE_PIN PIN_PA3 // ATtiny84 physical pin 10 - PIR output
#define LED_POWER PIN_PA1 // ATtiny84 physical pin 12 - BC327 control

// ------------------------------------------------------------
// LED settings
// ------------------------------------------------------------

#define NUMPIXELS 4

#define BRIGHTNESS 125 // Normal LED brightness
#define LED_ON_TIME 5000UL // Display each digit for 5 seconds
#define LED_OFF_TIME 200 // Blank period between digits

#define FADE_MIN 10 // Lowest stable WS2812 brightness
#define FADE_STEP 5
#define FADE_DELAY 40 // ms between fade steps

Adafruit_NeoPixel pixels(NUMPIXELS, NEO, NEO_GRB + NEO_KHZ400);


// ------------------------------------------------------------
// Digit colors
//
// Each color represents one numerical digit 0-9.
// ------------------------------------------------------------

struct Color
{
uint8_t r;
uint8_t g;
uint8_t b;
};

const Color arrayColors[10] =
{
{255, 0, 0}, // 0 - Red
{ 0, 255, 0}, // 1 - Green
{ 0, 0, 255}, // 2 - Blue
{255, 255, 0}, // 3 - Yellow
{ 0, 255, 255}, // 4 - Cyan
{255, 0, 255}, // 5 - Magenta
{255, 255, 255}, // 6 - White
{255, 128, 0}, // 7 - Orange
{128, 0, 255}, // 8 - Violet
{255, 128, 192} // 9 - Pink
};


// Counts PIR activations from 00 to 99.
// This value is stored only in RAM and therefore resets after power loss.
uint8_t activationCounter = 0;


// ------------------------------------------------------------
// Pin-change interrupt
//
// The interrupt does not need to perform any operation.
// Its only purpose is to wake the ATtiny from power-down sleep.
// ------------------------------------------------------------

ISR(PCINT0_vect)
{
}


// ------------------------------------------------------------
// Wake interrupt control
// ------------------------------------------------------------

void enableWakeInterrupt()
{
PCMSK0 |= _BV(PCINT3);
}


void disableWakeInterrupt()
{
PCMSK0 &= ~_BV(PCINT3);
}


void clearWakeInterrupt()
{
// AVR interrupt flags are cleared by writing a logic 1.
GIFR |= _BV(PCIF0);
}


// ------------------------------------------------------------
// LED functions
// ------------------------------------------------------------

void ledsPowerOn()
{
// BC327 high-side transistor:
// LOW = LEDs powered
digitalWrite(LED_POWER, LOW);

// Allow WS2812 supply to stabilize.
delay(10);
}


void setColor(uint8_t colorNumber)
{
pixels.setBrightness(BRIGHTNESS);

Color c = arrayColors[colorNumber];

for (uint8_t i = 0; i < NUMPIXELS; i++)
{
pixels.setPixelColor(i, pixels.Color(c.r, c.g, c.b));
}

pixels.show();
}


void clearLeds()
{
// LEDs remain powered but display black.
pixels.clear();
pixels.show();
}


void fadeOut()
{
// Reduce brightness gradually from normal brightness
// to FADE_MIN.
//
// After this function returns, the LEDs remain ON
// at FADE_MIN until ledsOff() is called.

for (int b = BRIGHTNESS; b >= FADE_MIN; b -= FADE_STEP)
{
pixels.setBrightness(b);
pixels.show();
delay(FADE_DELAY);
}
}


void ledsOff()
{
// First send black while the WS2812s still have power.
pixels.clear();
pixels.show();

// Hold the WS2812 data line LOW before removing power.
// This prevents possible back-powering through DIN.
digitalWrite(NEO, LOW);
delay(1);

// BC327 OFF.
digitalWrite(LED_POWER, HIGH);

// Prepare normal brightness for next activation.
pixels.setBrightness(BRIGHTNESS);
}


// ------------------------------------------------------------
// Sleep
// ------------------------------------------------------------

void system_sleep()
{
// Make sure LEDs are unpowered before sleeping.
digitalWrite(LED_POWER, HIGH);
digitalWrite(NEO, LOW);

// Do not sleep if the PIR is already active.
if (digitalRead(WAKE_PIN) == HIGH)
{
return;
}

// Temporarily disable global interrupts while configuring sleep.
cli();

clearWakeInterrupt();
enableWakeInterrupt();

set_sleep_mode(SLEEP_MODE_PWR_DOWN);
sleep_enable();

// Check PIR again immediately before sleeping.
// This prevents missing a transition that occurred while
// sleep was being prepared.
if (digitalRead(WAKE_PIN) == LOW)
{
sei();
sleep_cpu();
}
else
{
sleep_disable();
sei();
return;
}

// Execution continues here after PIR wakes the ATtiny.
sleep_disable();
disableWakeInterrupt();

sei();
}


// ------------------------------------------------------------
// Setup
// ------------------------------------------------------------

void setup()
{
pixels.begin();

pinMode(LED_POWER, OUTPUT);
digitalWrite(LED_POWER, HIGH); // LEDs initially unpowered

pinMode(NEO, OUTPUT);
digitalWrite(NEO, LOW);

pinMode(WAKE_PIN, INPUT);

// Enable pin-change interrupt group for PORT A.
GIMSK |= _BV(PCIE0);

// Individual PIR interrupt is enabled only when entering sleep.
disableWakeInterrupt();

ledsOff();

sei();
}


// ------------------------------------------------------------
// Main loop
// ------------------------------------------------------------

void loop()
{
if (digitalRead(WAKE_PIN) == HIGH)
{
// We do not need PIR interrupts while processing an activation.
disableWakeInterrupt();

// --------------------------------------------------------
// Increment activation counter: 00 -> 01 -> ... -> 99 -> 00
// --------------------------------------------------------

activationCounter++;

if (activationCounter > 99)
{
activationCounter = 0;
}

// Split counter into two decimal digits.
uint8_t tens = activationCounter / 10;
uint8_t ones = activationCounter % 10;


// --------------------------------------------------------
// Display activation count
// --------------------------------------------------------

ledsPowerOn();

// Tens digit
setColor(tens);
delay(LED_ON_TIME);

// Brief blank interval so identical digits can still
// be distinguished, e.g. 11, 22, 33...
clearLeds();
delay(LED_OFF_TIME);

// Ones digit
setColor(ones);
delay(LED_ON_TIME);


// --------------------------------------------------------
// Fade down to low brightness
// --------------------------------------------------------

fadeOut();


// --------------------------------------------------------
// Keep LEDs dim while PIR remains active
// --------------------------------------------------------

while (digitalRead(WAKE_PIN) == HIGH)
{
delay(10);
}


// PIR has returned LOW - turn LEDs completely off.
ledsOff();


// Small settling time before allowing another activation.
delay(100);

// Remove any pending pin-change interrupt from the
// previous PIR transition.
cli();
clearWakeInterrupt();
sei();
}

// Sleep until the next PIR activation.
system_sleep();
}

전력 소비

배터리 수명은 설계에서 중요한 부분이었습니다. 테스트 결과 WS2812의 밝기를 높이면 아크릴을 통해 보이는 밝기가 그만큼 크게 향상되지 않는데도 전류가 크게 증가했습니다. 따라서 전력을 절약하기 위해 일반 LED 밝기를 의도적으로 낮췄습니다.

배터리에서 직접 측정한 결과, 전체 시스템의 소비 전류는 대략 다음과 같습니다:

  1. 52 mA 일반 LED 밝기에서
  2. 22 mA 페이드/저밝기 상태에서
  3. 960 µA 대기 상태에서

최악의 경우로 하루 100회 활성화, 한 번 활성화될 때마다 일반 밝기에서 약 10초, 낮은 밝기에서 20초 동안 작동한다고 가정하면 예상 소비량은 약 49 mAh/일입니다.

2700 mAh 배터리를 사용하면 이론상 작동 시간은 대략 다음과 같습니다:

2700 / 49 ≈ 55일

실제 배터리 수명은 물론 PIR 활성화 횟수와 지속 시간, 그리고 사용 가능한 배터리 용량에 따라 달라집니다.

결과

최종 설계는 의도적으로 매우 단순합니다. 즉, ATtiny84, PIR 센서, 4개의 RGB LED, 트랜지스터 및 충전식 배터리 전원으로 구성됩니다.

아크릴판이 시각적 효과를 제공하고, 소프트웨어는 동일한 4개의 LED를 기본적인 활동 카운터로 활용합니다. Wi-Fi, 디스플레이 또는 외부 시스템 없이도 원하는 정보를 제공하면서 설계를 작고 저전력으로 유지할 수 있었습니다.


원문: https://www.instructables.com/ATtiny84-Motion-Activated-Acrylic-LED-Plate/

좋아요 0
게시판

회사명 공방유니언 주소 각 공방 주소 참조
사업자 등록번호 0000 대표 공방유니언 전화 019-60105846 팩스 0000 이메일 gongbangunion@gmail.com
통신판매업신고번호 0000 개인정보관리책임자 공방유니언
Copyright © 2017 공방유니언. All Rights Reserved.