디지털 보드 게임용 아두이노 나노 33 BLE로 나만의 전자 주사위 만들기
전자 주사위를 예전부터 만들고 싶었는데 자료를 제공하는 사이트를 찾았다. 원본 문서 참고하세요.
많은 사람들이 어린 시절에 스네이크 앤 래더스나 루도와 같은 보드게임을 즐기던 즐거움을 기억합니다. 이 간단하면서도 매력적인 게임은 가족과 친구들을 한데 모아 모임을 활기차고 즐겁게 만들었습니다. 주사위를 굴리고 토큰을 움직이며 게임의 기복을 경험하면서 인내심과 예측 불가능성에 대한 교훈을 얻었고, 수많은 웃음과 선의의 경쟁을 불러 일으켰습니다. 좌절감에 빠져 게임 판을 뒤집거나 행운의 주사위를 굴리며 즐거워하던 그 순간은 우리 기억 속에 선명하게 남아 있습니다.
하지만 요즘은 많은 사람들이 현대 게임에 중독되어 이러한 재미있고 즐거운 보드게임을 잊고 살아가고 있습니다. 고전 게임에 현대적인 감각을 더하고 싶다는 생각에 무선 연결 기능을 갖춘 디지털 주사위를 만들어야겠다는 생각이 들었습니다. 약간의 손질을 거쳐 저전력 특성이 이러한 프로젝트에 매우 유용하기 때문에 연결을 위해 BLE를 선택하기로 결정했습니다.
저는 NRF52840과 같은 초저전력 마이크로컨트롤러를 사용하기로 했습니다. 이 제품은 ESP32와 같은 대응 제품보다 전력 효율성이 높을 뿐만 아니라 Arduino IDE와 같은 개발 IDE에 대한 지원도 뛰어납니다. 저는 NRF52840을 탑재한 NINA-B306 모듈을 선택했습니다. 왜냐하면 다루기가 훨씬 쉽고 베어 nRF52840 칩을 사용하는 것에 비해 안테나 설계에 대한 고민이 필요 없기 때문입니다. BluetoothLE 연결, 고급 모션 센서, LED 기반 시각적 피드백을 결합한 Smart LED Dice는 향수 어린 게임과 현대 기술 간의 격차를 메웁니다.
이 프로젝트는 스폰서인 ALLPCB 덕분에 가능했습니다. 이 프로젝트에 사용된 PCB 보드는 allpcb에서 제작했으며, 주문 방법에 대한 자세한 내용은 이 기사의 후반부에서 공유합니다.
저희 Smart Electronic Dice의 특징
- 저전력 nRF52840 저전력, 다중 프로토콜 Bluetooth 5 SoC 기반.
- Bluetooth V5 Low Energy 연결. 스마트폰, 태블릿 또는 사용자 지정 BLE 지원 기기와 페어링합니다.
- Arduino Nano 33 BLE 호환.
- 주사위 방향과 움직임을 감지하여 굴리는 것을 감지하는 통합 가속도계와 자이로스코프가 있는 MPU6050 IMU.
- 과충전 보호 기능이 있는 TP4056 리튬 이온 배터리 충전 IC.
- 충전 및 프로그래밍을 위한 온보드 USB Type C 포트.
- 얼굴 표시용 LED.
- 상태 표시용 RGB LED.
- 모든 구성 요소를 통합한 컴팩트하고 최적화된 레이아웃의 사용자 지정 PCB.
- 주사위 폼 팩터에 맞게 설계되었습니다.
- MPU6050을 사용하여 굴린 후 주사위의 면을 실시간으로 감지합니다.
- 굴린 후 주사위에 위쪽을 향한 숫자가 표시되도록 LED가 켜집니다.
- Android 앱 지원. 주사위 굴림 데이터를 수신하고 결과를 표시하는 동반 앱.
스마트 LED 주사위를 만드는 데 필요한 구성 요소
BLE를 만드는 데 필요한 구성 요소는 다음과 같습니다. 각 구성 요소의 정확한 값은 회로도 또는 BOM에서 찾을 수 있습니다.
- NINA-B306-00B 모듈
- MPU6050 IMU
- MIC5219-3.3 LDO
- TP4056 리튬이온 배터리
- 1615 CA RGB LED
- 0805 LED
- SMD 저항기 및 커패시터
- 커넥터
- 맞춤형 PCB
- 3D 인쇄 부품.
- 기타 도구 및 소모품.
스마트 LED 주사위 회로도
스마트 LED 주사위의 전체 회로도는 아래에 나와 있습니다. 마지막에 제공된 링크에서 PDF 형식으로 다운로드할 수도 있습니다.
스마트 LED 주사위 회로도
더 나은 이해를 위해 회로도를 섹션별로 살펴보겠습니다. 먼저 전원 입력, 배터리 충전 및 전압 조절을 포함하는 전원 섹션이 있습니다. 충전과 프로그래밍 목적으로 C형 USB 포트가 사용됩니다. USB 포트의 전원은 P채널 MOSFET U3와 다이오드 D1을 중심으로 구축된 전원 경로 컨트롤러 회로에 연결됩니다. 이를 통해 USB 입력이나 배터리에서 보드에 전원을 공급해도 문제가 발생하지 않습니다. 배터리 충전 회로는 악명 높은 TP4056 독립형 선형 리튬이온 배터리 충전 컨트롤러 IC를 중심으로 구축되었습니다. USB 포트에서 5V 입력을 받아 내부 배터리를 충전합니다. TP4056은 또한 충전 표시와 완전 충전 표시를 위한 두 개의 표시기를 제공합니다.
또한 충전 상태를 모니터링하는 데 사용할 수 있는 전압 분배기를 이러한 표시기 핀에 연결했습니다. 전원 경로 컨트롤러의 VBUS 전압을 3.3V로 변환하기 위해 MIC5219 초저잡음 저드롭아웃 전압 레귤레이터를 사용했습니다. 매우 최소한의 보조 구성 요소로 MIC5219는 배터리 충전 수준이 낮을 때에도 매우 안정적인 출력 전압을 제공합니다.
스마트 LED 주사위 회로도 전원 섹션
다음으로, 두뇌 역할을 하는 Nina B306-00B 모듈이 있습니다. Nina B306-00B는 Nordic Semiconductor nRF52840 Bluetooth 5 Low Energy SoC를 특징으로 하며, 64MHz에서 작동하는 부동 소수점 유닛이 있는 Arm Cortex-M4 프로세서를 특징으로 합니다. 1MB의 플래시 메모리와 256kB의 RAM을 통합하여 코드와 데이터 저장을 위한 충분한 공간을 제공합니다. 동작 및 방향 감지를 위해 InvenSense의 MPU6050 IMU를 사용했는데, 이는 동일한 실리콘 다이에 3축 자이로스코프와 3축 가속도계를 특징으로 하며, 복잡한 6축 MotionFusion 알고리즘을 처리하는 온보드 디지털 모션 프로세서와 함께 제공됩니다. MPU6050은 I2C 인터페이스를 통해 Nina B306 모듈과 인터페이스됩니다.
스마트 LED 주사위 회로도 SoC 및 IMU 섹션
주사위 면을 표시하기 위해 LED를 사용했습니다. 각 면에는 해당 수의 LED가 있습니다. LED가 하나 있는 면을 제외하고, 다른 모든 면에는 별도의 전류 제한 저항과 병렬로 연결된 0805 LED를 사용하고 있습니다. LED가 하나 있는 면의 경우 RGB LED를 사용했습니다. 이 RGB는 면 표시뿐만 아니라 연결 상태를 표시하는 데에도 사용됩니다. 1615 패키지의 RGB LED는 크기를 손상시키지 않으면서도 이를 위한 최상의 선택이었습니다. 미학적으로 적합할 만큼 작고 회로를 조립하는 동안 다루기 쉽습니다.
스마트 LED 주사위용 PCB
이 프로젝트에서는 맞춤형 PCB를 만들기로 했습니다. 이렇게 하면 최종 제품이 가능한 한 컴팩트하고 조립 및 사용이 간편해집니다. PCB는 KiCad로 설계되었습니다. 모든 설계 파일은 이 기사 아래에 링크된 GitHub 리포에서 다운로드할 수 있습니다. PCB의 각 면의 치수는 약 30mm x 30mm입니다. 총 6개의 PCB가 있습니다. 그러나 제조를 위해 63x96mm 크기의 6개 모두를 포함하는 패널을 만들었으며, 쉽게 분리할 수 있도록 스탬프 구멍이 있습니다.
PCB의 상단 레이어입니다.
스마트 LED 주사위 PCB 상단 레이어
아래 이미지는 PCB의 하단 레이어를 보여줍니다.
스마트 LED 주사위 PCB 하단 레이어
그리고 PCB의 3D 보기입니다.
Smart LED Dice PCB 3D 보기 Top
Smart LED Dice PCB 3D 보기 Bottom
ALLPCB에서 PCB 주문하기
이제 디자인을 마무리한 후 PCB 주문을 진행할 수 있습니다.
1단계: https://www.allpcb.com/?code=PT19에 접속하여 처음이라면 가입하세요. 그런 다음 PCB 견적 탭에서 고급 PCB 견적을 클릭하고 Gerber 파일을 업로드하면 도구가 자동으로 PCB 치수를 채웁니다. 이제 PCB 색상, 실크스크린, PCB 두께, PCB 수 등 필요한 변경 사항을 적용하세요.
PCB 견적 탭 보기
2단계: 모든 필수 매개변수가 설정되면 지금 견적을 클릭하여 견적을 생성합니다. 도구가 다양한 배송 방법의 빌드 시간, 비용 및 배송 비용을 표시합니다. 적절한 배송 방법을 선택하고 장바구니 추가를 클릭하여 주문을 진행합니다.
PCB 배송 비용
3단계: 장바구니에서 방금 추가한 PCB를 선택하고 진행을 클릭합니다. 배송 및 청구 세부 정보를 입력한 다음 결제를 진행합니다. 결제가 완료되면 ALLPCB에서 주문 제작을 시작합니다. 일주일 이내에 완성된 PCB를 받게 됩니다.
Smart Dice PCB 보드
Smart LED Dice 조립
SMART LED Dice를 조립하려면 먼저 패널에 각 PCB를 조립합니다. 각 PCB는 주사위의 한 면을 나타냅니다. 한 면에는 LED가 있고 다른 면에는 다른 모든 구성 요소가 있습니다. 완전히 조립된 PCB는 다음과 같습니다.
Smart LED Dice 조립된 PCB
완전히 조립된 Dice는 다음과 같습니다.
Smart LED Dice 완전히 조립됨
3D 인쇄 부품
PCB 위에 맞는 3D 인쇄 커버 인클로저를 설계했습니다. 이러한 부품은 PCB 주사위의 날카로운 모서리가 움직임에 영향을 미치지 않고 매끄럽게 굴러가도록 합니다. 모든 3D 인쇄 부품의 파일은 Arduino 스케치 및 비트맵 파일과 함께 기사 끝에 제공된 GitHub 링크에서 다운로드할 수 있습니다. 링크를 따라 3D 인쇄에 대해 자세히 알아보고 시작하는 방법을 알아보세요. Thingiverse 또는 프로젝트 GitHub 리포지토리에서 3D 파일을 다운로드할 수 있습니다.
Thingyverse: https://www.thingiverse.com/thing:6855807
스마트 LED 주사위 3D 보기
다음은 3D로 인쇄된 부품입니다.
스마트 LED 주사위 인클로저 3D 보기
다음은 3D 인클로저가 있는 완전히 조립된 스마트 LED 주사위입니다.
스마트 LED 주사위 및 3D 인클로저
완전히 조립된 스마트 LED 주사위
스마트 LED 주사위는 어떻게 작동합니까?
전원이 켜지면 주사위는 대기 모드로 전환되어 BLE 연결을 기다립니다. 이 시간 동안 통합 RGB LED가 빨간색, 녹색, 파란색을 반복해서 깜박이며 연결할 준비가 되었음을 나타냅니다. 스마트폰 또는 이와 유사한 BLE 장치가 성공적으로 연결되면 RGB LED가 꺼지고 연결 신호가 전달되고 주사위가 유휴 상태로 전환되어 사용자 상호 작용이 가능합니다. 주사위에는 주사위 굴림을 감지하는 MPU6050 모션 센서가 장착되어 있습니다. 모션이 감지되면 모든 면 LED가 주사위를 던지는 것을 시뮬레이션하여 굴리는 효과로 순차적으로 켜집니다. 이 동적 패턴은 주사위가 정지할 때까지 계속됩니다. 정지하면 주사위는 모션 센서의 데이터를 사용하여 위를 향한 면을 판별합니다. 위를 향한 면에 해당하는 LED가 깜박이기 시작하여 결과를 나타냅니다.
연결된 BLE 장치에 결과를 전달하기 위해 주사위는 두 가지 BLE 특성을 업데이트합니다. 상태 특성은 새로운 굴림과 유효한 얼굴 감지가 발생했는지 여부를 나타냅니다. 굴림이 감지되면 1로 설정되고 연결된 장치에서 0으로 재설정하여 주사위가 다음 굴림을 준비할 수 있도록 합니다. 면 번호 특성은 위를 향한 면에 해당하는 번호를 전송하고 다음 굴림까지 변경되지 않습니다. 이러한 특성이 업데이트될 때마다 연결된 장치에 알림이 전송되어 값을 읽도록 합니다. 연결된 장치가 페이스 번호를 읽고 상태 특성을 0으로 재설정하면 주사위는 다음 흔들기 또는 굴리기를 감지할 준비가 됩니다. 이렇게 하면 주사위가 연결된 장치가 다음 동작을 위해 준비되었다고 확인할 때까지 추가 굴리기 감지를 잠그는 원활한 상호 작용 주기가 보장됩니다.
BLE Dice Companion APP
Smart LED Dice의 기능을 보여주기 위해 MIT App Inventor를 사용하여 간단한 스마트폰 앱을 만들었습니다. 앱에는 최소한의 구성 요소만 있습니다. 연결 상태를 보여주는 레이블과 결과를 보여주는 레이블이 두 개 있습니다. 결과를 그래픽으로 표시하는 데 사용되는 이미지 요소가 있습니다. UI에 세 개의 버튼을 추가했는데, 그 중 두 개는 BLE 연결을 연결하고 연결 해제하는 데 사용됩니다. 재설정 버튼은 디버깅 목적으로 사용되며, 상태 특성을 수동으로 0으로 재설정합니다.
BLE Dice 앱
다음은 앱의 블록 뷰입니다.
BLE Dice 앱 블록
BLE 장치 이름, 서비스 UUID, 상태 특성 UUID 및 주사위 면 특성 UUID를 저장하기 위해 네 개의 전역 변수를 만들었습니다. 앱이 열리면 필요한 권한이 이미 부여되었는지 확인합니다. 부여되지 않은 경우 해당 권한을 부여하라는 메시지가 표시됩니다. 장치 설정에서 Bluetooth와 위치를 켰는지 확인하세요. 모든 권한이 부여되면 연결 버튼을 클릭하기만 하면 됩니다. 앱은 장치 이름과 서비스 UUID를 사용하여 Smart LED Dice를 자동으로 스캔하고 연결합니다. 장치를 분리하려면 연결 해제 버튼을 클릭하거나 앱을 닫을 수 있습니다.
Smart LED Dice에 연결되면 앱은 Dice 얼굴 특성에 대한 알림을 등록합니다. 이렇게 하면 얼굴 특성 값이 변경되는 즉시 앱에 알림이 전송됩니다. 알림을 받으면 앱은 이 값을 읽고 앱 화면에 표시합니다. 또한 결과를 나타내는 해당 이미지도 표시합니다. 값을 성공적으로 읽으면 앱은 상태 특성을 값 0으로 업데이트하여 주사위가 다음 굴림을 감지할 수 있도록 합니다.
Android 앱 설치 프로그램 파일과 MIT 앱 인벤터 프로젝트 파일은 모두 이 튜토리얼의 마지막에 제공된 프로젝트 GitHub 저장소 링크에서 찾을 수 있습니다. MIT 앱 인벤터를 사용하는 동안 BluetoothLE 확장 프로그램이 아직 설치되지 않았다면 설치하는 것을 잊지 마세요.
스마트 LED 주사위용 Arduino 코드
이제 하드웨어와 기본 작동 원리를 알게 되었으니 펌웨어 부분을 살펴보겠습니다. NINA-B306 모듈을 사용하고 있으므로 Arduino Nano 33 BLE에 있는 것과 동일한 모듈이므로 Arduino IDE로 프로그래밍할 수 있습니다. 시작하려면 Arduino Nano 33 BLE 부트로더를 모듈에 플래시해야 합니다. 그렇게 하려면 프로그래밍 패드(SWDIO, SWCLK, GND, Reset 및 3.3V)를 CMSIS-DAP 또는 JLINK와 같은 ARM 디버거에 연결합니다. 그런 다음 아직 설치되지 않았다면 Arduino IDE에 Arduino MBed OS Nano 보드를 설치합니다. 나중에 필요한 모든 드라이버를 이미 설치했다고 가정하고 디버거를 PC에 연결하고 Arduino IDE에서 보드로 Arduino Nano 33 BLE를 선택하고 도구 메뉴에서 적절한 프로그래머를 선택합니다. 그런 다음 도구 메뉴에서 부트로더 굽기 옵션을 사용합니다. 그러면 부트로더가 자동으로 모듈에 플래시됩니다. 또 다른 옵션은 부트로더 바이너리를 다운로드하여 J-flash 또는 J-link commander와 같은 선호하는 디버그 도구를 사용하여 플래시하는 것입니다.
부트로더가 구워지면 코딩을 진행할 수 있습니다. 이를 위해 GitHub 저장소에서 Arduino 소스 파일을 다운로드하여 열거나 새 스케치를 만들고 아래에 제공된 코드를 복사하여 붙여넣습니다. 보드를 Arduino Nano ## BLE로 설정해야 합니다. 코드에 언급된 모든 필수 라이브러리를 설치합니다. 컴파일하고 업로드 버튼을 사용하여 보드에 플래시합니다. 이제 사용 가능한 Bluetooth 장치를 검색하면 BLE_Dice라는 BluetoothLE 장치를 볼 수 있습니다.
이제 코드 자체를 살펴보겠습니다. 평소처럼 모든 필수 라이브러리를 포함하고 모든 필수 핀을 정의했습니다. 나중에 전역 변수를 정의하고 MPU6050 라이브러리의 인스턴스도 만들었습니다. 이 인스턴스는 MPU6050 IMU와 통신하는 데 사용됩니다. 장치 이름, 서비스 UUID 및 특성 UUID와 같은 몇 가지 Bluetooth 관련 변수를 정의한 것을 볼 수도 있습니다. 이름에서 알 수 있듯이 장치 이름은 BLE 장치의 이름을 지정하는 데 사용됩니다. 이는 Bluetooth 장치에 대해 표시될 이름입니다. 이 이름은 동반 앱에도 하드코딩되어 있으므로 이름을 변경하기로 결정한 경우 앱에서도 변경해야 합니다. 서비스 UUID는 BLE 통신에 사용됩니다. 두 개의 특성 UUID는 주사위와 연결된 장치 간에 데이터를 전송하는 데 사용됩니다. 앞서 언급했듯이 하나는 상태 플래그를 갖고 다른 하나는 결과를 갖습니다.
#include <Wire.h>
#include <Adafruit_MPU6050.h>
#include <Adafruit_Sensor.h>
#include <ArduinoBLE.h>
// Pin definitions
#define rgbRed A7
#define rgbGreen A6
#define rgbBlue 3
#define leftLEDs 4
#define rightLEDs 9
#define backLEDs 10
#define bottomLEDs 6
#define topLEDs 5
Adafruit_MPU6050 mpu;
// Variables for shake and stationary detection
float shakeThreshold = 15.0; // Adjust for shake sensitivity
float stationaryThreshold = 15.0; // Threshold for being stationary
unsigned long stationaryDuration = 1500; // Duration to confirm stationary (in ms)
// BLE Characteristics
BLEService diceService("180A");//0000180A-0000-1000-8000-00805f9b34fb
BLEByteCharacteristic statusCharacteristic("2A57", BLERead | BLEWrite | BLENotify);//00002A58-0000-1000-8000-00805f9b34fb
BLEByteCharacteristic faceCharacteristic("2A58", BLERead | BLENotify);//00002A57-0000-1000-8000-00805f9b34fb
// Other Variables
int currentFace = -1; // Tracks the current face
unsigned long lastStationaryTime = 0;
unsigned long lastRGBBlinkTime = 0;
bool BLEStatus = false;
int LEDPins[7] = {-1,A6, 4, 5, 6, 9,10};
enum DiceState {
WAIT_FOR_SHAKE,
WAIT_FOR_STATIONARY,
UPDATE_FACE,
WAIT_FOR_BLE_RESET
};
DiceState currentState = WAIT_FOR_SHAKE; // Start in the WAIT_FOR_SHAKE state
unsigned long lastFlashTime = 0; // For non-blocking sequential LED flashing
int flashIndex = 0; // Current LED index for sequential flashing
bool shakeDetected = false; // To track shake detection
unsigned long stationaryStartTime = 0; // Track time for stationary detection
DiceState currentState = WAIT_FOR_SHAKE; // WAIT_FOR_SHAKE 상태에서 시작
unsigned long lastFlashTime = 0; // 비차단 순차적 LED 깜박임
int flashIndex = 0; // 순차적 깜박임에 대한 현재 LED 인덱스
bool shakeDetected = false; // 흔들림 감지 추적
unsigned long stationaryStartTime = 0; // 정지 감지 시간 추적
다음으로 setup 함수에서 IMU 인스턴스를 초기화하고 필요한 모든 GPIO도 초기화했습니다. 센서와 GIO가 성공적으로 초기화되면 주사위가 BLE 서비스를 시작하고 광고를 시작합니다.
void setup() {
Serial.begin(115200);
// Initialize MPU6050
if (!mpu.begin()) {
Serial.println("Failed to find MPU6050 chip");
while (1);
}
Serial.println("MPU6050 Found!");
mpu.setAccelerometerRange(MPU6050_RANGE_8_G);
mpu.setFilterBandwidth(MPU6050_BAND_21_HZ);
// Initialize LED pins
pinMode(rgbRed, OUTPUT);
pinMode(rgbGreen, OUTPUT);
pinMode(rgbBlue, OUTPUT);
pinMode(leftLEDs, OUTPUT);
pinMode(rightLEDs, OUTPUT);
pinMode(backLEDs, OUTPUT);
pinMode(bottomLEDs, OUTPUT);
pinMode(topLEDs, OUTPUT);
turnOffAllLEDs(); // Ensure all LEDs are off at boot
digitalWrite(rgbRed, LOW);
// Initialize BLE
if (!BLE.begin()) {
Serial.println("Starting BLE failed!");
while (1);
}
BLE.setLocalName("BLE_Dice");
BLE.setAdvertisedService(diceService);
diceService.addCharacteristic(statusCharacteristic);
diceService.addCharacteristic(faceCharacteristic);
BLE.addService(diceService);
// Set characteristics to 0 at boot
statusCharacteristic.writeValue(0);
faceCharacteristic.writeValue(0);
BLE.advertise();
Serial.println("BLE_Dice is ready!");
}
loop 함수는 연결 관리를 담당합니다. 연결이 활성화되면 loop 함수가 handleDiceLogic 함수를 호출합니다. 연결이 비활성화되면 loop 함수가 연결 상태를 나타내는 RGB LED를 깜박입니다.
void loop() {
// Handle BLE
BLEDevice central = BLE.central();
if (central) {
BLEStatus = true;
Serial.print("Connected to: ");
Serial.println(central.address());
currentState = WAIT_FOR_SHAKE;
statusCharacteristic.writeValue(0); // Dice ready
faceCharacteristic.writeValue(0); // Detected face
turnOffAllLEDs();
while (central.connected()) {
handleDiceLogic();
}
Serial.println("Disconnected from central");
turnOffAllLEDs();
BLEStatus = false;
digitalWrite(rgbRed, LOW);
}
if(BLEStatus == false) {
blinkRGB();
}
}
handleDiceLogic 함수는 여러 하위 함수를 조정하여 주사위 논리를 만드는 데 사용됩니다. 연결이 활성화되면 주사위는 WAIT_FOR_SHAKE 상태가 됩니다. 이 상태에서 주사위는 detectshake 함수를 사용하여 동작을 감지하고 유효한지 여부를 판별합니다. 흔들기나 굴리기가 감지되면 주사위는 WAIT_FOR_STATIONARY 상태로 변경됩니다. 이 상태에서 주사위는 동작이 멈추고 정지할 때까지 기다립니다. detectStationary 함수는 주사위가 굴리는 것을 멈추고 멈췄는지 감지하는 데 사용됩니다. 주사위가 정지하면 UPDATE_FACE 상태로 변경됩니다. 이 경우 주사위 방향은 detectface 함수의 도움으로 결정되고 해당 LED가 깜박이기 시작합니다. 그런 다음 상태 값과 결과를 언급한 BLE 특성에 쓰고 WAIT_FOR_BLE_REST로 이동합니다. BLE 장치가 연결되어 있는 한 BLE 장치가 상태 값을 재설정할 때까지 주사위는 이 상태를 유지합니다. 주사위가 재설정되면 다시 루프를 시작하고 다음 굴림을 기다립니다.
void handleDiceLogic() {
switch (currentState) {
case WAIT_FOR_SHAKE:
// Blink blue LED until shake is detected
blinkBlueLED();
if (detectShake()) {
shakeDetected = true;
digitalWrite(rgbBlue, HIGH); // Turn off blue LED
currentState = WAIT_FOR_STATIONARY; // Move to next state
Serial.println("Shake detected! Moving to WAIT_FOR_STATIONARY.");
}
break;
case WAIT_FOR_STATIONARY:
// Flash LEDs sequentially until stationary
if (millis() - lastFlashTime >= 100) { // Non-blocking delay for LED flashing
flashSequentialLEDs();
lastFlashTime = millis();
}
if (detectStationary()) {
currentState = UPDATE_FACE; // Move to next state
Serial.println("Stationary detected! Moving to UPDATE_FACE.");
}
break;
case UPDATE_FACE:
// Determine face and update LEDs and BLE characteristics
currentFace = determineFace();
if(currentFace < 0)
{
return;
}
updateLEDs(currentFace);
// Set BLE characteristics
statusCharacteristic.writeValue(1); // Dice ready
faceCharacteristic.writeValue(currentFace); // Detected face
Serial.println("Face updated! Waiting for BLE reset.");
currentState = WAIT_FOR_BLE_RESET; // Move to next state
break;
case WAIT_FOR_BLE_RESET:
// Wait for user to set statusCharacteristic to 0
if (statusCharacteristic.value() == 0) {
turnOffAllLEDs(); // Reset LEDs before next cycle
currentState = WAIT_FOR_SHAKE; // Go back to initial state
Serial.println("BLE reset! Returning to WAIT_FOR_SHAKE.");
}
break;
}
}
bool detectShake() {
sensors_event_t a, g, temp;
mpu.getEvent(&a, &g, &temp);
float magnitude = sqrt(a.acceleration.x * a.acceleration.x +
a.acceleration.y * a.acceleration.y +
a.acceleration.z * a.acceleration.z);
if (magnitude > shakeThreshold) {
Serial.println("Shake detected!");
return true;
}
return false;
}
bool detectStationary() {
static float accelSum = 0; // Sum of acceleration magnitudes
static int sampleCount = 0; // Number of samples in the averaging window
sensors_event_t a, g, temp;
mpu.getEvent(&a, &g, &temp);
float magnitude = sqrt(a.acceleration.x * a.acceleration.x +
a.acceleration.y * a.acceleration.y +
a.acceleration.z * a.acceleration.z);
// Add current magnitude to the sum
accelSum += magnitude;
sampleCount++;
// Average the acceleration magnitude over the sample window
float averageMagnitude = accelSum / sampleCount;
if (averageMagnitude < stationaryThreshold) {
if (millis() - lastStationaryTime > stationaryDuration) {
// Reset for the next cycle
accelSum = 0;
sampleCount = 0;
Serial.println("Stationary detected!");
return true;
}
} else {
// Reset stationary timer if movement is above the threshold
lastStationaryTime = millis();
accelSum = 0;
sampleCount = 0;
}
return false;
}
int determineFace() {
sensors_event_t a, g, temp;
mpu.getEvent(&a, &g, &temp);
if (a.acceleration.y > 8.0) return 3; // Top
if (a.acceleration.y < -8.0) return 4; // Bottom
if (a.acceleration.z > 8.0) return 5; // Right
if (a.acceleration.z < -8.0) return 2; // Left
if (a.acceleration.x > 8.0) return 6; // Back
if (a.acceleration.x < -8.0) return 1; // Front
return -1; // Unknown
}
나머지 함수는 LED 제어에 사용되며 이전에 설명한 함수 내에서 호출됩니다. flashSequentialLEDs는 주사위를 굴리는 동안 각 면의 LED를 순서대로 깜박입니다. blnkRGB 함수는 활성 연결이 없을 때 호출되고 updateLEDs는 LED를 깜박여 결과를 표시하는 데 사용됩니다. turnOffAllLEDs 함수는 언급된 대로 모든 LED를 끄는 데 사용됩니다.
void flashSequentialLEDs() {
const int leds[] = {rgbGreen,leftLEDs, rightLEDs, backLEDs, bottomLEDs, topLEDs};
for (int i = 0; i < 6; i++) {
turnOffAllLEDs();
if(i == 0)
{
digitalWrite(leds[i], LOW);
}
else {
digitalWrite(leds[i], HIGH);
}
delay(100);
}
}
void blinkBlueLED() {
static unsigned long lastBlinkTime = 0;
static bool ledState = false;
if (millis() - lastBlinkTime >= 500) {
ledState = !ledState;
digitalWrite(LEDPins[currentFace], ledState ? LOW : HIGH);
lastBlinkTime = millis();
}
}
void blinkRGB() {
if (millis() - lastRGBBlinkTime >= 200) {
if(digitalRead(rgbRed) == 0)
{
digitalWrite(rgbRed, HIGH);
digitalWrite(rgbBlue, HIGH);
digitalWrite(rgbGreen, LOW);
}
else if(digitalRead(rgbGreen) == 0)
{
digitalWrite(rgbRed, HIGH);
digitalWrite(rgbGreen, HIGH);
digitalWrite(rgbBlue, LOW);
}
else if(digitalRead(rgbBlue) == 0)
{
digitalWrite(rgbBlue, HIGH);
digitalWrite(rgbGreen, HIGH);
digitalWrite(rgbRed, LOW);
}
lastRGBBlinkTime = millis();
}
}
void updateLEDs(int face) {
turnOffAllLEDs();
if(face == 1)
{
digitalWrite(LEDPins[face], LOW);
}
else
{
digitalWrite(LEDPins[face], HIGH);
}
}
void turnOffAllLEDs() {
digitalWrite(rgbRed, HIGH); // Fully off for common anode
digitalWrite(rgbGreen, HIGH);
digitalWrite(rgbBlue, HIGH);
digitalWrite(leftLEDs, LOW);
digitalWrite(rightLEDs, LOW);
digitalWrite(backLEDs, LOW);
digitalWrite(bottomLEDs, LOW);
digitalWrite(topLEDs, LOW);
}
지원 파일
다음은 GitHub 리포지토리에 대한 링크입니다. 여기에서 소스 코드, 회로도 및 자체 스마트 LED 주사위를 빌드하는 데 필요한 모든 다른 파일을 찾을 수 있습니다. 자료 압축 zip 파일 다운로드.
'메이커 Maker' 카테고리의 다른 글
PCB Design Arduino Training Kit (0) | 2025.01.06 |
---|---|
영문과 한글 교재로 만들어야 합니다. 어떤 언어로 된 책을 먼저 써야하는 지 조언 (1) | 2024.12.30 |
하루의 남은 시간을 초로 보요주는 기계 Display (1) | 2024.12.25 |
ESP32 MPU6050 Gesture Control with Audio DFPlayer Mini Integration (1) | 2024.12.20 |
지적재산 디자인권 심사기준 (1) | 2024.12.11 |
아두이노 태양 추적기 Arduino Solar Tracker (3) | 2024.12.06 |
2축 태양광 추적기 아두이노 프로젝트 (2) | 2024.12.05 |
태양광 패널 추적 시스템 회로도 (1) | 2024.12.04 |
더욱 좋은 정보를 제공하겠습니다.~ ^^