Skip to content

EmbeddedSDB/SDB

Repository files navigation

๐Ÿ“– ํ”„๋กœ์ ํŠธ ์†Œ๊ฐœ

๋™์ž‘ ๊ฐ์ง€ ์„ผ์„œ ์ด๋ฏธ์ง€

์Šค๋งˆํŠธ ๋„์–ด๋ฒจ (Smart Doorbell, SDB)์€ IoT ๊ธฐ์ˆ ์„ ํ™œ์šฉํ•˜์—ฌ ๋ฐฉ๋ฌธ์ž๋ฅผ ๊ฐ์ง€ํ•˜๊ณ , ์‚ฌ์šฉ์ž์—๊ฒŒ ์•Œ๋ฆผ์„ ๋ณด๋‚ด๋ฉฐ, ์›๊ฒฉ์œผ๋กœ ๋ฌธ์„ ์ œ์–ดํ•  ์ˆ˜ ์žˆ๋Š” ์‹œ์Šคํ…œ์ด๋‹ค. Raspberry Pi์™€ Flutter ์•ฑ์„ ์ค‘์‹ฌ์œผ๋กœ ์„ค๊ณ„๋˜์–ด, ์‹ค์‹œ๊ฐ„ ์˜์ƒ ์ŠคํŠธ๋ฆฌ๋ฐ, ์–‘๋ฐฉํ–ฅ ์Œ์„ฑ ํ†ตํ™”, ๋…นํ™” ๊ธฐ๋Šฅ, ๋ฐฉ๋ฌธ์ž ๊ฐ์ง€ ์•Œ๋ฆผ ๋“ฑ ๋‹ค์–‘ํ•œ ๊ธฐ๋Šฅ์„ ํ†ตํ•ด ์‚ฌ์šฉ์ž์—๊ฒŒ ํŽธ์˜์„ฑ๊ณผ ์•ˆ์ „์„ฑ์„ ์ œ๊ณตํ•œ๋‹ค.



๐Ÿ’ก ํ”„๋กœ์ ํŠธ ๋ฐฐ๊ฒฝ

์ˆ˜์ƒํ•œ ์‚ฌ๋žŒ

ํ˜„๋Œ€ ๊ฐ€์ •์˜ ๋ณด์•ˆ ์š”๊ตฌ ์ฆ๊ฐ€

  • 1์ธ ๊ฐ€๊ตฌ์˜ ์ฆ๊ฐ€์™€ ํ•จ๊ป˜ ๊ฐ€์ • ๋‚ด ๋ณด์•ˆ ๋ฌธ์ œ ๋Œ€๋‘
  • ๋นˆ์ง‘ํ„ธ์ด, ๊ฐ•๋„ ์‚ฌ๊ฑด ๋“ฑ ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ์œ„ํ—˜ ์š”์†Œ์— ๋Œ€ํ•œ ๋Œ€์ฒด ํ•„์š”
  • ๋ฐฉ๋ฌธ์ž ํ™•์ธ ๋ฐ ๋ณด์•ˆ ๊ฐ•ํ™”๋ฅผ ์œ„ํ•œ ํšจ๊ณผ์ ์ธ ์†”๋ฃจ์…˜ ์š”๊ตฌ

IoT ๊ธฐ์ˆ ์˜ ๋ฐœ์ „๊ณผ ํ™œ์šฉ

  • ์„ผ์„œ, ์นด๋ฉ”๋ผ, ํด๋ผ์šฐ๋“œ ์„œ๋น„์Šค ์—ฐ๋™์„ ํ†ตํ•œ ์Šค๋งˆํŠธ ๋ณด์•ˆ ์‹œ์Šคํ…œ ๊ตฌํ˜„
  • ์ €๋น„์šฉ์œผ๋กœ ๋‹ค์–‘ํ•œ ์žฅ์น˜ ํ†ตํ•ฉ ๊ฐ€๋Šฅ
  • ์Šค๋งˆํŠธ ํ™ˆ ๋ณด์•ˆ ์†”๋ฃจ์…˜์˜ ํ•ต์‹ฌ ๊ธฐ์ˆ ๋กœ ์ž๋ฆฌ ์žก์€ IoT

์•ˆ์ „ํ•œ ๊ฐ€์ • ํ™˜๊ฒฝ ๊ตฌ์ถ•์˜ ํ•„์š”์„ฑ

  • ์›€์ง์ž„ ๊ฐ์ง€, ์‹ค์‹œ๊ฐ„ ์˜์ƒ ์ŠคํŠธ๋ฆฌ๋ฐ, ๋…นํ™” ๋ฐ ์•Œ๋ฆผ์„ ํ†ตํ•œ ์ฆ‰๊ฐ์ ์ธ ์œ„ํ—˜ ๋Œ€์‘
  • SDB ํ”„๋กœ์ ํŠธ์˜ ํ†ตํ•ฉ ๊ธฐ์ˆ ๋กœ ํŽธ๋ฆฌํ•จ๊ณผ ๋ณด์•ˆ ์ œ๊ณต

๐ŸŽฏ ํ”„๋กœ์ ํŠธ ๋ชฉํ‘œ

๋ฐฉ๋ฌธ์ž ๊ด€๋ฆฌ์˜ ํŽธ๋ฆฌ์„ฑ ์ œ๊ณต

  • ์‹ค์‹œ๊ฐ„ ์˜์ƒ ์ŠคํŠธ๋ฆฌ๋ฐ์œผ๋กœ ๋ฌธ ์•ž ์ƒํ™ฉ์„ ์Šค๋งˆํŠธํฐ์—์„œ ๋ฐ”๋กœ ํ™•์ธ
  • ์–‘๋ฐฉํ–ฅ ์Œ์„ฑ ํ†ตํ™”๋ฅผ ํ†ตํ•ด ๋ฐฉ๋ฌธ์ž์™€ ์ง์ ‘ ์†Œํ†ต
  • ๋„์–ด๋ฝ ์›๊ฒฉ ์ œ์–ด๋กœ ๋ฌผ๋ฆฌ์  ๊ฑฐ๋ฆฌ์™€ ์ƒ๊ด€์—†์ด ๋„์–ด๋ฝ ์ œ์–ด ๊ฐ€๋Šฅ

์•ˆ์ „ํ•œ ๊ฐ€์ • ๋ณด์•ˆ ์‹œ์Šคํ…œ ๊ตฌํ˜„

  • ๋ฐฉ๋ฌธ์ž์˜ ์›€์ง์ž„์„ ๊ฐ์ง€ํ•˜๊ณ , ์›€์ง์ž„ ๊ฐ์ง€ ์ด๋ฒคํŠธ ๋ฐœ์ƒ์„ ๊ธฐ๋ก
  • ๋…นํ™”๋œ ์˜์ƒ์„ ์„œ๋ฒ„์— ์ €์žฅํ•ด ์–ธ์ œ๋“ ์ง€ ์ƒํ™ฉ ํ™•์ธ ๊ฐ€๋Šฅ
  • ์•Œ๋ฆผ ์‹œ์Šคํ…œ์„ ํ†ตํ•ด ์œ„ํ—˜ ์ƒํ™ฉ ๋ฐœ์ƒ ์‹œ ๋น ๋ฅด๊ฒŒ ๋Œ€์ฒ˜ ๊ฐ€๋Šฅ

IoT ๊ธฐ์ˆ ์˜ ํ†ตํ•ฉ ํ™œ์šฉ

  • Raspberry Pi์™€ ๋‹ค์–‘ํ•œ ์„ผ์„œ๋ฅผ ์—ฐ๋™ํ•˜์—ฌ ์Šค๋งˆํŠธ ๋ณด์•ˆ ํ™˜๊ฒฝ ์ œ๊ณต
  • ํด๋ผ์šฐ๋“œ ์„œ๋น„์Šค๋ฅผ ํ™œ์šฉํ•ด ์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ ๊ด€๋ฆฌ์™€ ์•Œ๋ฆผ ์ „์†ก
  • ์Šค๋งˆํŠธํฐ ์•ฑ์„ ํ†ตํ•ด ์‹œ์Šคํ…œ ์ „์ฒด๋ฅผ ์›๊ฒฉ์œผ๋กœ ์ œ์–ดํ•˜๊ณ  ๋ชจ๋‹ˆํ„ฐ๋ง ๊ฐ€๋Šฅ

โš™๏ธ ํ•˜๋“œ์›จ์–ด

์ด๋ฏธ์ง€ ํ•˜๋“œ์›จ์–ด ์ด๋ฆ„ ์—ญํ• 
Raspberry Pi 4B ํ”„๋กœ์ ํŠธ์˜ ์ค‘์‹ฌ ์ œ์–ด ์žฅ์น˜
PIR ๋ชจ์…˜ ์„ผ์„œ ๋ฐฉ๋ฌธ์ž์˜ ๋™์ž‘ ๊ฐ์ง€
์ดˆ์ธ์ข… ๋ฒ„ํŠผ ๋ฐฉ๋ฌธ์ž ์ž…๋ ฅ ๊ฐ์ง€
๋ถ€์ € ์ดˆ์ธ์ข… ์•Œ๋ฆผ์Œ ์ถœ๋ ฅ
์„œ๋ณด ๋ชจํ„ฐ ๋„์–ด๋ฝ ์›๊ฒฉ ์ œ์–ด
๋ผ์ฆˆ๋ฒ ๋ฆฌํŒŒ์ด ์นด๋ฉ”๋ผ ๋ชจ๋“ˆ ์‹ค์‹œ๊ฐ„ ์˜์ƒ ์ฒ˜๋ฆฌ
USB ๋งˆ์ดํฌ ๋ผ์ฆˆ๋ฒ ๋ฆฌํŒŒ์ด ์Œ์„ฑ ์ž…๋ ฅ
3.5mm ์žญ ์Šคํ”ผ์ปค ๋ผ์ฆˆ๋ฒ ๋ฆฌํŒŒ์ด ์Œ์„ฑ ์ถœ๋ ฅ

๐Ÿ› ๏ธ ๊ธฐ์ˆ  ์Šคํƒ

์šด์˜ ์ฒด์ œ ๋ฐ ํ™˜๊ฒฝ

  • Raspberry Pi OS (Lite): ํ•˜๋“œ์›จ์–ด ์ œ์–ด์™€ ์„œ๋ฒ„ ์šด์˜
  • Python Virtual Environment: ์ข…์†์„ฑ ๊ด€๋ฆฌ ๋ฐ ์‹คํ–‰ ํ™˜๊ฒฝ ์ œ๊ณต

ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด

  • C: ์ดˆ์ธ์ข… ๋ฐ ๋ฐฉ๋ฌธ์ž ๊ฐ์ง€, ์„œ๋ณด๋ชจํ„ฐ ๋ฐ ํ•˜๋“œ์›จ์–ด ์ œ์–ด
  • Python: Flask ์„œ๋ฒ„ ๊ตฌํ˜„, Firebase ์—ฐ๋™, ๋…นํ™” ๋ฐ ์ŠคํŠธ๋ฆฌ๋ฐ ์ œ์–ด
  • Dart (Flutter): ์‚ฌ์šฉ์ž ์ธํ„ฐํŽ˜์ด์Šค, ์‹ค์‹œ๊ฐ„ ์ŠคํŠธ๋ฆฌ๋ฐ ๋ฐ ๋„์–ด๋ฒจ ์ œ์–ด

๋ฐฑ์—”๋“œ

  • Flask: HTTP ์„œ๋ฒ„, ๋น„๋””์˜ค/์˜ค๋””์˜ค ์ŠคํŠธ๋ฆฌ๋ฐ, ํŒŒ์ผ ์—…๋กœ๋“œ
  • Firebase: Cloud Messaging(์•Œ๋žŒ ์ „์†ก), Storage(๋…นํ™” ํŒŒ์ผ ๊ด€๋ฆฌ), Admin SDK(Python, Flutter ํ†ตํ•ฉ)

๐Ÿ“Š ์‹œ์Šคํ…œ ์•„ํ‚คํ…์ฒ˜

graph TD
    A[PIR ์„ผ์„œ] -->|์›€์ง์ž„ ๊ฐ์ง€| B[Raspberry Pi]
    C[์ดˆ์ธ์ข… ๋ฒ„ํŠผ] -->|๋ฒ„ํŠผ ํด๋ฆญ ์ด๋ฒคํŠธ| B
    D[๋ผ์ฆˆ๋ฒ ๋ฆฌํŒŒ์ด ์นด๋ฉ”๋ผ] -->|์˜์ƒ ์บก์ฒ˜| B
    B -->|๋ฐ์ดํ„ฐ ์ „์†ก| E[Flask ์„œ๋ฒ„]
    E -->|๋…นํ™” ์˜์ƒ ์ €์žฅ| F[Firebase Storage]
    E -->|์ด๋ฒคํŠธ ๊ธฐ๋ก| G[Firebase Firestore]
    E -->|ํ‘ธ์‹œ ์•Œ๋ฆผ ์ „์†ก| H[Firebase Messaging]
    H -->|์•Œ๋ฆผ ์ˆ˜์‹ | I[Flutter ์•ฑ]
    F -->|๋…นํ™” ์˜์ƒ ํ™•์ธ| I
    B -->|์‹ค์‹œ๊ฐ„ ์ŠคํŠธ๋ฆฌ๋ฐ| I
    I -->|์›๊ฒฉ ์ œ์–ด ์š”์ฒญ| B
    B -->|๋„์–ด๋ฝ ์ œ์–ด| J[์„œ๋ณด๋ชจํ„ฐ]
    B -->|์•Œ๋ฆผ์Œ ์ถœ๋ ฅ| K[๋ถ€์ €]

    classDef sensor fill:#ADFF2F,stroke:#8FBC8F,stroke-width:2px;
    classDef raspberrypi fill:#f9b0b4,stroke:#ff0000,stroke-width:2px;
    classDef flask fill:#b3d6f3,stroke:#0066cc,stroke-width:2px;
    classDef firebase fill:#fffacd,stroke:#ffcc00,stroke-width:2px;
    classDef flutter fill:#e6ccff,stroke:#9933ff,stroke-width:2px;
    classDef actuator fill:#FFA500,stroke:#FF7F50,stroke-width:2px;

    class A,C,D sensor;
    class B raspberrypi;
    class E flask;
    class F,G,H firebase;
    class I flutter;
    class J,K actuator;
Loading

๐Ÿ’ป ๋ฉ€ํ‹ฐ ํ”„๋กœ์„ธ์Šค & ๋ฉ€ํ‹ฐ ์Šค๋ ˆ๋“œ

๋ฉ€ํ‹ฐ ํ”„๋กœ์„ธ์Šค ๐Ÿ”—

ํ”„๋กœ์„ธ์Šค ์ด๋ฆ„ ์ƒ์„ฑ ์œ„์น˜ ์ฃผ์š” ์—ญํ• 
Main Process motion_detection.c PIR ์„ผ์„œ ๋ฐ ์ดˆ์ธ์ข… ๋ฒ„ํŠผ ๊ฐ์ง€, Flask ๋ฐ Motor ํ”„๋กœ์„ธ์Šค ์ƒ์„ฑ, ์ด๋ฒคํŠธ ํŠธ๋ฆฌ๊ฑฐ
Flask Process flask_app.py Flask ์„œ๋ฒ„ ์‹คํ–‰, ๋น„๋””์˜ค/์˜ค๋””์˜ค ์ŠคํŠธ๋ฆฌ๋ฐ ์ œ๊ณต, HTTP ์š”์ฒญ ์ฒ˜๋ฆฌ, ๋…นํ™” ๊ด€๋ฆฌ
Recording Controller flask_app.py ๋‚ด๋ถ€ control.txt ๋ชจ๋‹ˆํ„ฐ๋ง, ๋…นํ™” ์ƒํƒœ ๊ด€๋ฆฌ, Firebase ์—…๋กœ๋“œ ์ฒ˜๋ฆฌ
Motor Process motor.c Flask Process์™€ ์†Œ์ผ“ ํ†ต์‹  ๋Œ€๊ธฐ, ๋ช…๋ น ์ˆ˜์‹  ํ›„ ์„œ๋ณด๋ชจํ„ฐ ์ œ์–ด

ํ”„๋กœ์„ธ์Šค ๊ฐ„ ๋ฐ์ดํ„ฐ ์ „๋‹ฌ ๋ฐฉ์‹ ๐Ÿ“‚

๐Ÿ“‚ ๊ณต์œ  ์ž์›์„ ํ†ตํ•œ ์ƒํƒœ ๊ด€๋ฆฌ

  • ํ”„๋กœ์„ธ์Šค ๊ฐ„ ์ƒํƒœ ์ •๋ณด๋ฅผ ๊ธฐ๋กํ•˜๊ณ  ์ฝ๋Š” ๋ฐฉ์‹์œผ๋กœ ๋™๊ธฐํ™” ์ˆ˜ํ–‰
  • ์›€์ง์ž„ ๊ฐ์ง€ ์ƒํƒœ, ์ŠคํŠธ๋ฆฌ๋ฐ ์ƒํƒœ ๊ธฐ๋ก

๐Ÿ“ ์ƒํƒœ ์ „์†ก ์‚ฌ๋ก€์™€ ๊ฐ ํ”„๋กœ์„ธ์Šค์—์„œ์˜ ํ™œ์šฉ ๋ฐฉ์‹

  1. ์›€์ง์ž„ ๊ฐ์ง€ ์‹œ โ†’ ์›€์ง์ž„ ๊ฐ์ง€ "ํ™œ์„ฑ" ์ƒํƒœ ๊ธฐ๋ก

    • ์›€์ง์ž„ ๊ฐ์ง€ ํ”„๋กœ์„ธ์Šค: PIR ์„ผ์„œ๋ฅผ ํ†ตํ•ด ์›€์ง์ž„์ด ๊ฐ์ง€๋˜๋ฉด ์›€์ง์ž„ ๊ฐ์ง€ ์ƒํƒœ ํŒŒ์ผ์— "ํ™œ์„ฑ" ์ƒํƒœ ๊ธฐ๋ก
    • ๋…นํ™” ํ”„๋กœ์„ธ์Šค: ์›€์ง์ž„ ๊ฐ์ง€ ์ƒํƒœ ํŒŒ์ผ์„ ์ฃผ๊ธฐ์ ์œผ๋กœ ํ™•์ธํ•˜๋ฉฐ, "ํ™œ์„ฑ" ์ƒํƒœ๊ฐ€ ๊ฐ์ง€๋˜๋ฉด ๋…นํ™” ์‹œ์ž‘
      • ๋งŒ์•ฝ ์ŠคํŠธ๋ฆฌ๋ฐ ์ƒํƒœ๊ฐ€ "ํ™œ์„ฑ"์ธ ๊ฒฝ์šฐ, ์›€์ง์ž„ ๊ฐ์ง€ ์ƒํƒœ์™€ ๊ด€๊ณ„์—†์ด ๋…นํ™”๋ฅผ ์ˆ˜ํ–‰ํ•˜์ง€ ์•Š์Œ
  2. ์›€์ง์ž„ ์ข…๋ฃŒ ์‹œ โ†’ ์›€์ง์ž„ ๊ฐ์ง€ "๋น„ํ™œ์„ฑ" ์ƒํƒœ ๊ธฐ๋ก

    • ์›€์ง์ž„ ๊ฐ์ง€ ํ”„๋กœ์„ธ์Šค: PIR ์„ผ์„œ๊ฐ€ ์ผ์ • ์‹œ๊ฐ„ ์›€์ง์ž„์„ ๊ฐ์ง€ํ•˜์ง€ ๋ชปํ•˜๋ฉด ์›€์ง์ž„ ๊ฐ์ง€ ์ƒํƒœ ํŒŒ์ผ์— "๋น„ํ™œ์„ฑ" ์ƒํƒœ ๊ธฐ๋ก
    • ๋…นํ™” ํ”„๋กœ์„ธ์Šค: ์›€์ง์ž„ ๊ฐ์ง€ ์ƒํƒœ ํŒŒ์ผ์ด "๋น„ํ™œ์„ฑ" ์ƒํƒœ๋กœ ๋ณ€๊ฒฝ๋˜๋ฉด ๋…นํ™” ์ข…๋ฃŒ
  3. ์ŠคํŠธ๋ฆฌ๋ฐ ํ™œ์„ฑํ™” ์‹œ โ†’ ์ŠคํŠธ๋ฆฌ๋ฐ "ํ™œ์„ฑ" ์ƒํƒœ ๊ธฐ๋ก

    • ์ŠคํŠธ๋ฆฌ๋ฐ ํ”„๋กœ์„ธ์Šค: ์ŠคํŠธ๋ฆฌ๋ฐ ์š”์ฒญ์ด ๋“ค์–ด์˜ค๋ฉด ์ŠคํŠธ๋ฆฌ๋ฐ ์ƒํƒœ์— "ํ™œ์„ฑ" ์ƒํƒœ ๊ธฐ๋ก
    • ๋…นํ™” ํ”„๋กœ์„ธ์Šค: ์ŠคํŠธ๋ฆฌ๋ฐ ์ƒํƒœ๋ฅผ ์ฃผ๊ธฐ์ ์œผ๋กœ ํ™•์ธํ•˜๋ฉฐ, "ํ™œ์„ฑ" ์ƒํƒœ๊ฐ€ ๊ฐ์ง€๋˜๋ฉด ๋…นํ™” ์ค‘์ง€
    • ์›€์ง์ž„ ๊ฐ์ง€ ํ”„๋กœ์„ธ์Šค: ์ŠคํŠธ๋ฆฌ๋ฐ ์ƒํƒœ๋ฅผ ์ฐธ์กฐํ•˜์—ฌ, ์ŠคํŠธ๋ฆฌ๋ฐ์ด ํ™œ์„ฑ ์ƒํƒœ์ธ์ง€ ํ™•์ธํ•˜๋ฉฐ ์ž‘์—… ์ˆ˜ํ–‰
  4. ์ŠคํŠธ๋ฆฌ๋ฐ ์ค‘์ง€ ์‹œ โ†’ ์ŠคํŠธ๋ฆฌ๋ฐ "๋น„ํ™œ์„ฑ" ์ƒํƒœ ๊ธฐ๋ก

    • ์ŠคํŠธ๋ฆฌ๋ฐ ํ”„๋กœ์„ธ์Šค: ์ŠคํŠธ๋ฆฌ๋ฐ ์ค‘์ง€ ์š”์ฒญ์ด ๋“ค์–ด์˜ค๋ฉด ์ŠคํŠธ๋ฆฌ๋ฐ ์ƒํƒœ์— "๋น„ํ™œ์„ฑ" ์ƒํƒœ ๊ธฐ๋ก
    • ์›€์ง์ž„ ๊ฐ์ง€ ํ”„๋กœ์„ธ์Šค: ์ŠคํŠธ๋ฆฌ๋ฐ ์ƒํƒœ๋ฅผ ํ™•์ธํ•˜์—ฌ, "๋น„ํ™œ์„ฑ" ์ƒํƒœ๋กœ ์ž‘์—… ์ข…๋ฃŒ

๐Ÿ’ก ๊ณต์œ  ์ž์›์˜ ์žฅ์ 

  • ๊ฐ„๋‹จํ•˜๊ณ  ๊ฐ€๋ฒผ์šด ๋ฐฉ์‹์œผ๋กœ ํ”„๋กœ์„ธ์Šค ๊ฐ„ ๋ฐ์ดํ„ฐ ๋™๊ธฐํ™” ๊ฐ€๋Šฅ
  • ๋‹ค๋ฅธ ์–ธ์–ด๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ํ”„๋กœ์„ธ์Šค ๊ฐ„ ๋ฐ์ดํ„ฐ ๊ณต์œ ๊ฐ€ ๊ฐ„ํŽธ
  • ๊ฐ•ํ•œ ๊ฒฐํ•ฉ์„ ํ”ผํ•˜๊ณ  ํ™•์žฅ ๊ฐ€๋Šฅํ•œ ๊ตฌ์กฐ ์ œ๊ณต

๋ฉ€ํ‹ฐ ์Šค๋ ˆ๋“œ ๐Ÿ”—

  • Main Process (motion_detection.c)

    ์Šค๋ ˆ๋“œ ์ด๋ฆ„ ์ฃผ์š” ์—ญํ• 
    Motion Detection Thread PIR ์„ผ์„œ๋ฅผ ํ†ตํ•ด ์›€์ง์ž„ ๊ฐ์ง€, Flask ์„œ๋ฒ„ ์‹คํ–‰
    Button Press Thread ์ดˆ์ธ์ข… ๋ฒ„ํŠผ ์ž…๋ ฅ ๊ฐ์ง€, Firebase ์•Œ๋ฆผ ์ „์†ก, ํ›„์† ์ž‘์—… ํŠธ๋ฆฌ๊ฑฐ
    Buzzer Thread ์ดˆ์ธ์ข… ๋ฒ„ํŠผ ์ž…๋ ฅ ๋ฐœ์ƒ ์‹œ ๋ถ€์ €๋ฅผ ์šธ๋ ค ์‚ฌ์šฉ์ž ์•Œ๋ฆผ ์ œ๊ณต
  • Flask Process (flask_app.py)

    ์Šค๋ ˆ๋“œ ์ด๋ฆ„ ์ฃผ์š” ์—ญํ• 
    Flask Request Threads HTTP ์š”์ฒญ ์ฒ˜๋ฆฌ, /stream, /audio, /upload ๋“ฑ ๊ฐ ์š”์ฒญ๋ณ„๋กœ ์Šค๋ ˆ๋“œ ์ƒ์„ฑ
    Recording Thread Picamera2๋กœ ๋น„๋””์˜ค ์ŠคํŠธ๋ฆฌ๋ฐ ์ฒ˜๋ฆฌ
    Audio Streaming Thread PyAudio๋กœ ์‹ค์‹œ๊ฐ„ ์˜ค๋””์˜ค ๋ฐ์ดํ„ฐ ์ŠคํŠธ๋ฆฌ๋ฐ
    Audio Playback Thread Pygame์œผ๋กœ ์—…๋กœ๋“œ๋œ ์˜ค๋””์˜ค ํŒŒ์ผ ์žฌ์ƒ
    Cleanup Thread ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ข…๋ฃŒ ์‹œ ์นด๋ฉ”๋ผ ๋ฐ ์˜ค๋””์˜ค ๋ฆฌ์†Œ์Šค ์ •๋ฆฌ
  • Motor Process (motor.c)

    ์Šค๋ ˆ๋“œ ์ด๋ฆ„ ์ฃผ์š” ์—ญํ• 
    Socket Thread ํด๋ผ์ด์–ธํŠธ๋กœ๋ถ€ํ„ฐ ๋ช…๋ น ์ˆ˜์‹ , ์ž‘์—… ํ์— ์ €์žฅ
    Output Thread ์ž‘์—… ํ์—์„œ ๋ช…๋ น์„ ์ฝ์–ด ์„œ๋ณด๋ชจํ„ฐ ๊ฐ๋„ ์ œ์–ด

๐Ÿ“š ์ฃผ์š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ

C ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ

๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์—ญํ•  ๋ฐ ์„ค๋ช…
WiringPi Raspberry Pi GPIO ํ•€ ์ œ์–ด
Pthread ํ•˜๋“œ์›จ์–ด ์ด๋ฒคํŠธ๋ฅผ ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌํ•˜์—ฌ ์‹ค์‹œ๊ฐ„ ์„ฑ๋Šฅ ์ตœ์ ํ™”

Python ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ

๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์—ญํ•  ๋ฐ ์„ค๋ช…
Picamera2 Raspberry Pi ์นด๋ฉ”๋ผ ๋ชจ๋“ˆ๋กœ ๋น„๋””์˜ค ํ”„๋ ˆ์ž„ ์บก์ฒ˜ ๋ฐ ์ŠคํŠธ๋ฆฌ๋ฐ
OpenCV ์‹ค์‹œ๊ฐ„ ๋น„๋””์˜ค ํ”„๋ ˆ์ž„ ์ฒ˜๋ฆฌ ๋ฐ JPEG ํ˜•์‹ ๋ณ€ํ™˜
FFmpeg h264 ํ˜•์‹์˜ ๋…นํ™” ์˜์ƒ์„ mp4๋กœ ๋ณ€ํ™˜
PyAudio ์˜ค๋””์˜ค ๋ฐ์ดํ„ฐ ์บก์ฒ˜ ๋ฐ ์–‘๋ฐฉํ–ฅ ์˜ค๋””์˜ค ์ŠคํŠธ๋ฆฌ๋ฐ ์ฒ˜๋ฆฌ
Flask HTTP ์„œ๋ฒ„๋กœ ๋น„๋””์˜ค ์ŠคํŠธ๋ฆฌ๋ฐ, ์˜ค๋””์˜ค ์†ก์ˆ˜์‹ , ํŒŒ์ผ ์—…๋กœ๋“œ ๊ด€๋ฆฌ
Firebase Admin SDK ์ด๋ฒคํŠธ ๋ฐ์ดํ„ฐ์™€ ์˜์ƒ ํŒŒ์ผ ์ €์žฅ ๋ฐ ์—ฐ๋™

Flutter ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ

๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์—ญํ•  ๋ฐ ์„ค๋ช…
http Flask ์„œ๋ฒ„์™€์˜ ๋ฐ์ดํ„ฐ ์š”์ฒญ ๋ฐ ์‘๋‹ต ์ฒ˜๋ฆฌ
firebase_messaging Firebase๋ฅผ ํ†ตํ•ด ํ‘ธ์‹œ ์•Œ๋ฆผ ์ˆ˜์‹  ๋ฐ ๊ด€๋ฆฌ
just_audio Flutter ์•ฑ์—์„œ ์˜ค๋””์˜ค ์ŠคํŠธ๋ฆฌ๋ฐ ๋ฐ์ดํ„ฐ๋ฅผ ์žฌ์ƒ

โŒจ๏ธ ์‹œ์Šคํ…œ ๊ตฌํ˜„

1. ์›€์ง์ž„ ๊ฐ์ง€ ๋ฐ ๋…นํ™” ์ œ์–ด

์ฃผ์š” ์—ญํ• 

  • PIR ์„ผ์„œ๋ฅผ ํ†ตํ•ด ์›€์ง์ž„์„ ๊ฐ์ง€ํ•˜๊ณ , ์›€์ง์ž„ ๊ฐ์ง€ ์ƒํƒœ ํŒŒ์ผ์„ ํ†ตํ•ด ๋…นํ™” ํ”„๋กœ์„ธ์Šค์™€ ์ƒํƒœ๋ฅผ ๋™๊ธฐํ™”
  • ์›€์ง์ž„ ๊ฐ์ง€ ์‹œ ๋น„๋””์˜ค๋ฅผ ๋…นํ™”ํ•˜๊ณ , ๋…นํ™”๋œ ํŒŒ์ผ์„ .mp4๋กœ ๋ณ€ํ™˜ํ•œ ํ›„ Firebase์— ์—…๋กœ๋“œ

๋™์ž‘ ๋ฐฉ์‹

  1. ์›€์ง์ž„ ๊ฐ์ง€

    • PIR ์„ผ์„œ๊ฐ€ ์›€์ง์ž„์„ ๊ฐ์ง€ํ•˜๋ฉด ์›€์ง์ž„ ๊ฐ์ง€ ์ƒํƒœ ํŒŒ์ผ์— "ํ™œ์„ฑ" ์ƒํƒœ๋ฅผ ๊ธฐ๋ก
    • ๋…ธ์ด์ฆˆ๋กœ ์ธํ•ด 0์œผ๋กœ ๋ฐ”๋€Œ๊ฑฐ๋‚˜ ์‚ฌ๋žŒ์ด ์ž ๊น ๊ฐ์ง€๋˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ๋ฅผ ๋Œ€๋น„ํ•˜์—ฌ ์›€์ง์ž„์ด 8์ดˆ๊ฐ„ ๊ฐ์ง€๋˜์ง€ ์•Š์„ ๊ฒฝ์šฐ "๋น„ํ™œ์„ฑ" ์ƒํƒœ๋กœ ์ดˆ๊ธฐํ™”
  2. ๋…นํ™” ์‹œ์ž‘ ๋ฐ ์ข…๋ฃŒ

    • Flask ํ”„๋กœ์„ธ์Šค๊ฐ€ ์›€์ง์ž„ ๊ฐ์ง€ ์ƒํƒœ ํŒŒ์ผ์„ ์ฃผ๊ธฐ์ ์œผ๋กœ ์ฝ์–ด ์ƒํƒœ๋ฅผ ํ™•์ธ
    • "ํ™œ์„ฑ" ์ƒํƒœ์ผ ๊ฒฝ์šฐ ๋…นํ™”๋ฅผ ์‹œ์ž‘ํ•˜๊ณ , "๋น„ํ™œ์„ฑ" ์ƒํƒœ๋กœ ์ „ํ™˜๋˜๋ฉด ๋…นํ™”๋ฅผ ์ข…๋ฃŒ
    • ๋…นํ™” ์ทจ์†Œ: ์‚ฌ๋žŒ์ด 13์ดˆ ์ด๋‚ด์— ์‚ฌ๋ผ์ง€๋ฉด ๋…นํ™”๊ฐ€ ์ทจ์†Œ๋˜๋ฉฐ, ํ•ด๋‹น ์˜์ƒ์€ ์ €์žฅ๋˜์ง€ ์•Š์Œ
      • PIR ์„ผ์„œ ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ, ์„ผ์„œ ํ™œ์„ฑํ™” ์ƒํƒœ์—์„œ ๋น„ํ™œ์„ฑํ™” ์ƒํƒœ๋กœ ์ „ํ™˜๋˜๋Š”๋ฐ ์•ฝ 5์ดˆ ๊ฑธ๋ฆผ
      • ์ด๋ฅผ ๊ฐ์•ˆํ•˜์—ฌ 5์ดˆ(ํ™œ์„ฑ -> ๋น„ํ™œ์„ฑ) + 8์ดˆ(๋…ธ์ด์ฆˆ ๋ฐฉ์ง€) = 13์ดˆ๋ผ๋Š” ์‹œ๊ฐ„ ์ ์šฉ
      • ์˜ค๋ฒ„ํ—ค๋“œ ๋ฐœ์ƒ์„ ๊ฐ์•ˆํ•˜์—ฌ ์ฝ”๋“œ์—์„œ๋Š” 13์ดˆ๊ฐ€ ์•„๋‹Œ 10์ดˆ๋กœ ์„ค์ •
  3. Firebase ์—…๋กœ๋“œ

    • 13์ดˆ ์ด๋‚ด์— ๋…นํ™”๊ฐ€ ์ข…๋ฃŒ๋˜์ง€ ์•Š์•˜๋‹ค๋ฉด .h264 ํŒŒ์ผ์„ .mp4๋กœ ๋ณ€ํ™˜
    • ๋ณ€ํ™˜๋œ .mp4 ํŒŒ์ผ์„ Firebase Storage์— ์—…๋กœ๋“œ

ํ•ต์‹ฌ ์ฝ”๋“œ

  • ์›€์ง์ž„ ๊ฐ์ง€ (Motion_Detection.c):

    void *motionDetectionThread(void *arg) {
        int motionDetected = 0;
        unsigned long lastMotionTime = 0;
    
        while (1) {
            motionDetected = digitalRead(PIR_PIN);
            if (motionDetected) {
                lastMotionTime = millis();  // ์›€์ง์ž„ ๊ฐ์ง€ ์‹œ๊ฐ„ ์—…๋ฐ์ดํŠธ
                if (!personDetected) {
                    personRecognition = 1;
                    personDetected = 1;
                    writeSignalToFile("1");  // "ํ™œ์„ฑ" ์ƒํƒœ ๊ธฐ๋ก
                    uploadToFirebase_Sensor();  // Firebase ์•Œ๋ฆผ ์ „์†ก
                }
            } else if (!motionDetected && (millis() - lastMotionTime > 8000) && personRecognition) {
                personRecognition = 0;
                personDetected = 0;
                writeSignalToFile("0");  // "๋น„ํ™œ์„ฑ" ์ƒํƒœ ๊ธฐ๋ก
            }
            delay(200);  // ์งง์€ ๋Œ€๊ธฐ
        }
    }
  • ๋…นํ™” ๋ฐ Firebase ์—…๋กœ๋“œ (flask_app.py):

    def start_recording():
        timestamp = time.strftime("%Y%m%d_%H%M%S")
        video_path = os.path.join(video_dir, f"{timestamp}.h264")
        print("๋…นํ™” ์‹œ์ž‘...")
        command = ["libcamera-vid", "-t", "0", "-o", video_path]
        process = subprocess.Popen(command)
        return process, video_path
    
    def convert_to_mp4(video_path):
        convert_command = [
            "ffmpeg",
            "-i", video_path,
            "-c:v", "copy",
            video_path.replace(".h264", ".mp4")
        ]
        print("MP4 ๋ณ€ํ™˜ ์ค‘...")
        subprocess.run(convert_command)
        print("MP4 ๋ณ€ํ™˜ ์™„๋ฃŒ")
    
    def upload_to_firebase(video_path):
        firebase_video_upload.upload_file_to_firebase(
            video_path, "videos/" + os.path.basename(video_path)
        )
    
    def recording_controller():
        prev_state = None
        recording_process = None
        current_video_path = None
        global capture
    
        while True:
            if not streaming_video.value:
                try:
                    # ์ƒํƒœ ํŒŒ์ผ์—์„œ ์ƒํƒœ ์ฝ๊ธฐ
                    with open(file_path, "r") as f:
                        state = f.read().strip()
    
                    if state == "1" and prev_state != "1":
                        prev_state = state
                        # ๋…นํ™” ์‹œ์ž‘
                        if recording_process is None:
                            capture = True
                            print("๋…นํ™”๊ฐ€ ์‹œ์ž‘๋˜์—ˆ์Šต๋‹ˆ๋‹ค.")
                            time.sleep(2)
                            recording_process, current_video_path = start_recording()
    
                            for _ in range(100):  # 10์ดˆ ๋™์•ˆ ์ƒํƒœ ํ™•์ธ (0.1์ดˆ ๊ฐ„๊ฒฉ)
                                time.sleep(0.1)
                                with open(file_path, "r") as f:
                                    updated_state = f.read().strip()
                                if updated_state == "0":
                                    print("๋…นํ™”๋ฅผ ์ทจ์†Œํ•ฉ๋‹ˆ๋‹ค.")
                                    recording_process.terminate()
                                    recording_process.wait()
                                    recording_process = None
    
                                    if current_video_path:
                                        try:
                                            os.remove(current_video_path)  # ์งง์€ ๋…นํ™”๋ณธ ์‚ญ์ œ
                                            print("์งง์€ ๋…นํ™”๋ณธ ์‚ญ์ œ")
                                        except Exception as e:
                                            print("ํŒŒ์ผ ์‚ญ์ œ ์‹คํŒจ")
                                    current_video_path = None
                                    break
                            else:
                                print("10์ดˆ ์ด์ƒ ์ธ์‹๋˜์–ด ๋…นํ™”๋ฅผ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค.")
                        else:
                            print("์ด๋ฏธ ๋…นํ™” ์ค‘์ž…๋‹ˆ๋‹ค.")
    
                    elif state == "0" and prev_state != "0":
                        prev_state = state
                        # ๋…นํ™” ์ค‘์ง€
                        if recording_process is not None:
                            print("์˜์ƒ ๋…นํ™”๋ฅผ ์ค‘์ง€ํ•ฉ๋‹ˆ๋‹ค...")
                            recording_process.terminate()
                            recording_process.wait()
                            recording_process = None
    
                            if current_video_path:
                                convert_to_mp4(current_video_path)
                                firebase_video_upload.upload_file_to_firebase(
                                    current_video_path.replace('.h264', '.mp4'),
                                    "videos/" + time.strftime("%Y%m%d_%H%M%S") + ".mp4"
                                )
                                current_video_path = None
    
                        else:
                            print("๋…นํ™” ์ค‘์ด ์•„๋‹™๋‹ˆ๋‹ค.")
                        release_camera()
                        capture = False
    
                    time.sleep(0.1)  # ์ƒํƒœ ํŒŒ์ผ์„ ์ฃผ๊ธฐ์ ์œผ๋กœ ํ™•์ธ
                except FileNotFoundError:
                    print(f"{file_path} ํŒŒ์ผ์ด ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋Œ€๊ธฐ ์ค‘...")
                except Exception as e:
                    print(f"์˜ค๋ฅ˜ ๋ฐœ์ƒ: {e}")
            time.sleep(0.1)

2. ์˜์ƒ ์ŠคํŠธ๋ฆฌ๋ฐ

์ฃผ์š” ์—ญํ• 

  • ์ŠคํŠธ๋ฆฌ๋ฐ ํ™œ์„ฑํ™”: /stream ์š”์ฒญ ์‹œ ๋น„๋””์˜ค ์ŠคํŠธ๋ฆผ ํ™œ์„ฑํ™”
  • ์ŠคํŠธ๋ฆฌ๋ฐ ์ค‘์ง€: /stop_stream ์š”์ฒญ ์‹œ ๋น„๋””์˜ค ์ŠคํŠธ๋ฆผ ์ค‘์ง€

๋™์ž‘ ๋ฐฉ์‹

  1. ์ŠคํŠธ๋ฆฌ๋ฐ ํ™œ์„ฑํ™”

    • ์ŠคํŠธ๋ฆฌ๋ฐ ์ƒํƒœ ๋ณ€์ˆ˜(streaming_video.value)๋ฅผ "ํ™œ์„ฑ"์œผ๋กœ ์„ค์ •
    • ์นด๋ฉ”๋ผ ์ดˆ๊ธฐํ™” ๋ฐ ํ”„๋ ˆ์ž„ ์ƒ์„ฑ ์‹œ์ž‘
  2. ์ŠคํŠธ๋ฆฌ๋ฐ ์ค‘์ง€

    • ์ƒํƒœ ๋ณ€์ˆ˜๋ฅผ "๋น„ํ™œ์„ฑ"์œผ๋กœ ์„ค์ •ํ•˜๊ณ , ์นด๋ฉ”๋ผ ๋ฆฌ์†Œ์Šค๋ฅผ ํ•ด์ œ

ํ•ต์‹ฌ ์ฝ”๋“œ

  • ์ŠคํŠธ๋ฆฌ๋ฐ ํ™œ์„ฑํ™” ๋ฐ ์ค‘์ง€ (flask_app.py):

    @app.route('/stream')
    def stream():
        with lock:
            if not streaming_video.value:
                initialize_camera()
                streaming_video.value = True
        return Response(generate_frames(), mimetype='multipart/x-mixed-replace; boundary=frame')
    
    @app.route('/stop_stream')
    def stop_stream():
        with lock:
            streaming_video.value = False
            release_camera()
        return "์ŠคํŠธ๋ฆฌ๋ฐ ์ค‘์ง€"
  • ํ”„๋ ˆ์ž„ ์ƒ์„ฑ (flask_app.py):

    def generate_frames():
      """Picamera2์—์„œ ํ”„๋ ˆ์ž„ ์ƒ์„ฑ"""
      global picam2
      initialize_camera()  # Picamera2 ์ดˆ๊ธฐํ™”
      while streaming_video.value:
          try:
              if picam2 is None:
                  break
              frame = picam2.capture_array()
              _, buffer = cv2.imencode('.jpg', frame)
              frame = buffer.tobytes()
              yield (b'--frame\r\n'
                     b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')
          except Exception as e:
              print(f"Error generating frame: {e}")
              break

3. ์Œ์„ฑ ์ž…๋ ฅ ๋ฐ ์†ก์‹  (๋ผ์ฆˆ๋ฒ ๋ฆฌํŒŒ์ด โ†’ ์Šค๋งˆํŠธํฐ)

์ฃผ์š” ์—ญํ• 

  • ๋ผ์ฆˆ๋ฒ ๋ฆฌํŒŒ์ด ๋งˆ์ดํฌ๋กœ ์ž…๋ ฅ๋œ ์Œ์„ฑ์„ ์‹ค์‹œ๊ฐ„์œผ๋กœ ์บก์ฒ˜ํ•˜๊ณ  Flask ์„œ๋ฒ„๋ฅผ ํ†ตํ•ด ์Šค๋งˆํŠธํฐ์œผ๋กœ ์ „์†ก

๋™์ž‘ ๋ฐฉ์‹

  1. ์˜ค๋””์˜ค ์บก์ฒ˜
    • PyAudio๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ผ์ฆˆ๋ฒ ๋ฆฌํŒŒ์ด์˜ ๋งˆ์ดํฌ ์ž…๋ ฅ์„ ์บก์ฒ˜
    • ์ด๋•Œ ๊ธฐ๋ณธ ์žฅ์น˜ ๋ฒˆํ˜ธ๋ฅผ ์‚ฌ์šฉํ•˜๋ คํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๊ธฐ๊ธฐ ๋ฒˆํ˜ธ๋ฅผ ๋งˆ์ดํฌ ๋ฒˆํ˜ธ์— ๋งž๊ฒŒ ์ง€์ •
  2. ์‹ค์‹œ๊ฐ„ ์ŠคํŠธ๋ฆฌ๋ฐ
    • Flask ์„œ๋ฒ„์˜ /audio ์—”๋“œํฌ์ธํŠธ๋ฅผ ํ†ตํ•ด ์Šค๋งˆํŠธํฐ์œผ๋กœ ์˜ค๋””์˜ค ๋ฐ์ดํ„ฐ๋ฅผ ์ „์†ก

ํ•ต์‹ฌ ์ฝ”๋“œ

  • ์˜ค๋””์˜ค ์ŠคํŠธ๋ฆฌ๋ฐ (flask_app.py):

    def audio_stream():
        CHUNK = 256
        stream = audio1.open(format=FORMAT, channels=CHANNELS, input_device_index=2,
                             rate=RATE, input=True, frames_per_buffer=CHUNK)
        print("์˜ค๋””์˜ค ์ŠคํŠธ๋ฆฌ๋ฐ ์‹œ์ž‘")
        while is_streaming:
            data = stream.read(CHUNK, exception_on_overflow=False)
            yield data
        stream.stop_stream()
        stream.close()
    
    @app.route('/audio')
    def audio():
        global is_streaming
        is_streaming = True
        return Response(audio_stream(), mimetype="audio/wav")

4. ์Œ์„ฑ ์ˆ˜์‹  ๋ฐ ์ถœ๋ ฅ (์Šค๋งˆํŠธํฐ โ†’ ๋ผ์ฆˆ๋ฒ ๋ฆฌํŒŒ์ด)

์ฃผ์š” ์—ญํ• 

  • ์Šค๋งˆํŠธํฐ์—์„œ ์ „์†ก๋œ ์˜ค๋””์˜ค ๋ฐ์ดํ„ฐ๋ฅผ ๋ผ์ฆˆ๋ฒ ๋ฆฌํŒŒ์ด์—์„œ ์ˆ˜์‹ ํ•˜์—ฌ ์Šคํ”ผ์ปค๋กœ ์ถœ๋ ฅ

๋™์ž‘ ๋ฐฉ์‹

  1. ์˜ค๋””์˜ค ์—…๋กœ๋“œ
    • ์Šค๋งˆํŠธํฐ์—์„œ ๋…น์Œ๋œ ํŒŒ์ผ์„ /upload ์—”๋“œํฌ์ธํŠธ๋กœ ์ „์†ก
  2. ์˜ค๋””์˜ค ์žฌ์ƒ
    • ์—…๋กœ๋“œ๋œ ํŒŒ์ผ์„ Pygame์„ ์ด์šฉํ•˜์—ฌ ๋ผ์ฆˆ๋ฒ ๋ฆฌํŒŒ์ด์˜ ์Šคํ”ผ์ปค๋กœ ์ถœ๋ ฅ

ํ•ต์‹ฌ ์ฝ”๋“œ

  • ์˜ค๋””์˜ค ํŒŒ์ผ ์—…๋กœ๋“œ (flask_app.py):

    @app.route('/upload', methods=['POST'])
    def upload_audio():
        if 'file' not in request.files:
            return "No file part", 400
        file = request.files['file']
        if file.filename == '':
            return "No selected file", 400
        path = os.path.join(audio_path, file.filename)
        file.save(path)
        threading.Thread(target=play_audio, args=(path,)).start()
        return f"File saved at {path}", 200
    
    def play_audio(audio_path):
        print("์˜ค๋””์˜ค ์žฌ์ƒ ์ค‘...")
        pygame.mixer.init()
        pygame.mixer.music.load(audio_path)
        pygame.mixer.music.play()
        while pygame.mixer.music.get_busy():
            continue
        pygame.mixer.music.stop()
        pygame.mixer.quit()

5. ์„œ๋ณด๋ชจํ„ฐ๋ฅผ ํ†ตํ•œ ๋„์–ด๋ฝ ์ œ์–ด

์ฃผ์š” ์—ญํ• 

  • ์›๊ฒฉ ๋„์–ด๋ฝ ์ œ์–ด๋ฅผ ์œ„ํ•œ ์„œ๋ณด๋ชจํ„ฐ ๋ช…๋ น ์ฒ˜๋ฆฌ
  • ์†Œ์ผ“ ์„œ๋ฒ„๋ฅผ ํ†ตํ•ด ๋ช…๋ น ์ˆ˜์‹  ๋ฐ ์„œ๋ณด๋ชจํ„ฐ ํšŒ์ „

๋™์ž‘ ๋ฐฉ์‹

  1. ๋ช…๋ น ์ˆ˜์‹ 
    • ์†Œ์ผ“ ์„œ๋ฒ„์—์„œ ๋ช…๋ น์„ ์ˆ˜์‹ ํ•˜์—ฌ ์ž‘์—… ํ์— ์ €์žฅ
  2. ๋ช…๋ น ์‹คํ–‰
    • ์ž‘์—… ํ์—์„œ ๋ช…๋ น์„ ์ฝ์–ด ์„œ๋ณด๋ชจํ„ฐ ํšŒ์ „ ๊ฐ๋„ ์„ค์ •

ํ•ต์‹ฌ ์ฝ”๋“œ

  • ์„œ๋ณด๋ชจํ„ฐ ์ œ์–ด (motor.c):

    void *outputThread(void *arg) {
        while (1) {
            if (!queueIsEmpty()) {
                char *command = dequeue();
                if (strcmp(command, "LOCK") == 0) {
                    setServoAngle(0); // ๋„์–ด๋ฝ ์ž ๊ธˆ
                } else if (strcmp(command, "UNLOCK") == 0) {
                    setServoAngle(90); // ๋„์–ด๋ฝ ํ•ด์ œ
                }
                free(command);
            }
            usleep(50000);
        }
    }

6. ํ‘ธ์‹œ ์•Œ๋ฆผ ์ „์†ก

์ฃผ์š” ์—ญํ• 

  • ์›€์ง์ž„ ๊ฐ์ง€ ์•Œ๋ฆผ: PIR ์„ผ์„œ์—์„œ ์›€์ง์ž„์„ ๊ฐ์ง€ํ•˜๋ฉด Firebase๋ฅผ ํ†ตํ•ด ์•Œ๋ฆผ ์ „์†ก
  • ์ดˆ์ธ์ข… ์•Œ๋ฆผ: ์ดˆ์ธ์ข… ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด Firebase๋ฅผ ํ†ตํ•ด ์•Œ๋ฆผ ์ „์†ก

๋™์ž‘ ๋ฐฉ์‹

  1. ์›€์ง์ž„ ๊ฐ์ง€ ์•Œ๋ฆผ
    • PIR ์„ผ์„œ์—์„œ ์›€์ง์ž„์ด ๊ฐ์ง€๋˜๋ฉด uploadToFirebase_Sensor() ํ˜ธ์ถœํ•˜์—ฌ Firebase๋กœ ์•Œ๋ฆผ ์ „์†ก
  2. ์ดˆ์ธ์ข… ์•Œ๋ฆผ
    • ์ดˆ์ธ์ข… ๋ฒ„ํŠผ์ด ๋ˆŒ๋ฆฌ๋ฉด uploadToFirebase_Bell() ํ˜ธ์ถœํ•˜์—ฌ Firebase๋กœ ์ดˆ์ธ์ข… ์•Œ๋ฆผ ์ „์†ก

ํ•ต์‹ฌ ์ฝ”๋“œ

  1. ์›€์ง์ž„ ๊ฐ์ง€ ์•Œ๋ฆผ (Motion_Detection.c)

    // ์„ผ์„œ firebase ์•Œ๋žŒ ํŠธ๋ฆฌ๊ฑฐ
    void uploadToFirebase_Sensor() {
        int result = system("python3 /home/pi/SDB/firebaseUpload_Sensor.py");
        if (result == 0) {
            printf("Sensor Firebase ์—…๋กœ๋“œ ์„ฑ๊ณต\n");
        } else {
            printf("Sensor Firebase ์‹คํŒจ\n");
        }
    }
    
    // ๋ชจ์…˜ ๊ฐ์ง€ ์Šค๋ ˆ๋“œ
    void *motionDetectionThread(void *arg) {
        int personDetected = 0; // ์‚ฌ๋žŒ ๊ฐ์ง€ ์—ฌ๋ถ€
        int motionDetected = 0; // ์„ผ์„œ ์ถœ๋ ฅ ์ƒํƒœ
        unsigned long lastMotionTime = 0; // ๋งˆ์ง€๋ง‰์œผ๋กœ ์›€์ง์ž„์„ ๊ฐ์ง€ํ•œ ์‹œ๊ฐ„
    
        while (1) {
            motionDetected = digitalRead(PIR_PIN);
            if (motionDetected) {
                lastMotionTime = millis(); // ์›€์ง์ž„ ๊ฐ์ง€ ์‹œ๊ฐ„ ์—…๋ฐ์ดํŠธ
                if (!personDetected) {
                    personRecognition = 1;
                    personDetected = 1;
                    printf("์‚ฌ๋žŒ ๊ฐ์ง€, ๋…นํ™” ํ”„๋กœ๊ทธ๋žจ ์‹œ์ž‘\n");
                    writeSignalToFile("1");
                    uploadToFirebase_Sensor(); // Firebase๋กœ ์•Œ๋ฆผ ์ „์†ก
                }
            }
            else if (!motionDetected && (millis() - lastMotionTime > 8000) && personRecognition) {
                personRecognition = 0;
                personDetected = 0;
                printf("์‚ฌ๋žŒ ์—†์Œ, ๋…นํ™” ํ”„๋กœ๊ทธ๋žจ ์ข…๋ฃŒ\n");
                writeSignalToFile("0");
            }
    
            delay(200); // ์งง์€ ๋Œ€๊ธฐ
        }
        return NULL;
    }
  2. ์ดˆ์ธ์ข… ์•Œ๋ฆผ (Motion_Detection.c)

    // ์ดˆ์ธ์ข… firebase ์•Œ๋žŒ ํŠธ๋ฆฌ๊ฑฐ
    void uploadToFirebase_Bell() {
        int result = system("python3 /home/pi/SDB/firebaseUpload_Bell.py");
        if (result == 0) {
            printf("Bell Firebase ์—…๋กœ๋“œ ์„ฑ๊ณต\n");
        } else {
            printf("Bell Firebase ์‹คํŒจ\n");
        }
    }
    
    // ์ดˆ์ธ์ข… ์Šค๋ ˆ๋“œ
    void *buttonPressThread(void *arg) {
        int buttonState = HIGH; // ๋ฒ„ํŠผ ์ดˆ๊ธฐ ์ƒํƒœ
        int lastButtonState = HIGH; // ๋ฒ„ํŠผ ๋‚˜์ค‘ ์ƒํƒœ
        while (1) {
            buttonState = digitalRead(BUTTON_PIN);
    
            if (buttonState == LOW && lastButtonState == HIGH) {
                doorbellState = 1; // Doorbell was pressed
                printf("์ดˆ์ธ์ข…์ด ๋ˆŒ๋Ÿฌ์ง, ์•Œ๋žŒ ์ „์†ก\n");
                uploadToFirebase_Bell(); // Firebase๋กœ ์ดˆ์ธ์ข… ์•Œ๋ฆผ ์ „์†ก
                printf("ํ™”์ƒํ†ตํ™” ํ”„๋กœ์„ธ์Šค ์‹œ์ž‘\n");
            }
            lastButtonState = buttonState;
            delay(50); // ๋””๋ฐ”์šด์‹ฑ ์ฒ˜๋ฆฌ
        }
        return NULL;
    }

๐Ÿ“ฑ ์•ฑ ๊ตฌํ˜„

Flutter๋กœ ๊ฐœ๋ฐœ๋œ ์Šค๋งˆํŠธ ๋„์–ด๋ฒจ ์•ฑ์€ ๋ผ์ฆˆ๋ฒ ๋ฆฌํŒŒ์ด์™€์˜ ์‹ค์‹œ๊ฐ„ ํ†ต์‹ ์„ ํ†ตํ•ด ์˜์ƒ ์ŠคํŠธ๋ฆฌ๋ฐ, ์Œ์„ฑ ์†ก์ˆ˜์‹ , ๋„์–ด๋ฝ ์ œ์–ด ๋ฐ ๋…นํ™” ๊ด€๋ฆฌ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•œ๋‹ค. ์‚ฌ์šฉ์ž๋Š” ์ง๊ด€์ ์ธ UI๋ฅผ ํ†ตํ•ด ๊ฐ„ํŽธํ•˜๊ฒŒ ์Šค๋งˆํŠธ ๋„์–ด๋ฒจ์˜ ๋‹ค์–‘ํ•œ ๊ธฐ๋Šฅ์„ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

์•ฑ์˜ ์ฃผ์š” ๊ธฐ๋Šฅ

Flutter๋กœ ๊ฐœ๋ฐœ๋œ ์•ฑ์€ ๋‹ค์Œ์˜ ์ฃผ์š” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•œ๋‹ค:

  • IP ์ž…๋ ฅ ๋ฐ ์ €์žฅ: ์‚ฌ์šฉ์ž๊ฐ€ ๋ผ์ฆˆ๋ฒ ๋ฆฌํŒŒ์ด์˜ IP ์ฃผ์†Œ๋ฅผ ์ž…๋ ฅํ•˜๊ณ  ์ €์žฅ
  • ์˜์ƒ ์ŠคํŠธ๋ฆฌ๋ฐ: ์‹ค์‹œ๊ฐ„์œผ๋กœ ๋ผ์ฆˆ๋ฒ ๋ฆฌํŒŒ์ด์—์„œ ์ŠคํŠธ๋ฆฌ๋ฐ๋˜๋Š” ์˜์ƒ์„ ํ‘œ์‹œ
  • ์Œ์„ฑ ์†ก์‹  ๋ฐ ์ˆ˜์‹ : ์‚ฌ์šฉ์ž ์Œ์„ฑ์„ ๋ผ์ฆˆ๋ฒ ๋ฆฌํŒŒ์ด์— ์ „์†กํ•˜๊ฑฐ๋‚˜ ๋ผ์ฆˆ๋ฒ ๋ฆฌํŒŒ์ด์˜ ๋งˆ์ดํฌ ์ž…๋ ฅ์„ ์‹ค์‹œ๊ฐ„์œผ๋กœ ์ฒญ์ทจ
  • ๋„์–ด๋ฝ ์ œ์–ด: ๋ฒ„ํŠผ์„ ํ†ตํ•ด ์›๊ฒฉ์œผ๋กœ ๋„์–ด๋ฝ์„ ์ž ๊ทธ๊ฑฐ๋‚˜ ํ•ด์ œ
  • ๋…นํ™” ๋ชฉ๋ก ๊ด€๋ฆฌ: Firebase Storage์—์„œ ๋…นํ™”๋œ ์˜์ƒ ๋ชฉ๋ก์„ ๊ฐ€์ ธ์™€ ์žฌ์ƒ

1. ์•ฑ์˜ ๊ตฌ์กฐ

  • IpInputScreen: ๋ฉ”์ธ ํ™”๋ฉด์œผ๋กœ, IP ์ž…๋ ฅ, ์˜์ƒ ์ŠคํŠธ๋ฆฌ๋ฐ, ์Œ์„ฑ ์†ก์‹ /์ˆ˜์‹ , ๋„์–ด๋ฝ ์ œ์–ด๋ฅผ ์ œ๊ณต
  • VideoListScreen: Firebase Storage์— ์ €์žฅ๋œ ๋…นํ™” ์˜์ƒ ๋ชฉ๋ก์„ ํ‘œ์‹œ
  • VideoPlayerScreen: ์„ ํƒ๋œ ์˜์ƒ์„ ์žฌ์ƒ

2. IP ์ž…๋ ฅ ๋ฐ ์ €์žฅ

์‚ฌ์šฉ์ž๊ฐ€ ๋ผ์ฆˆ๋ฒ ๋ฆฌํŒŒ์ด์˜ IP ์ฃผ์†Œ๋ฅผ ์ž…๋ ฅํ•˜์—ฌ ์•ฑ์˜ ๋ชจ๋“  ๊ธฐ๋Šฅ์ด ํ•ด๋‹น IP๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๋™์ž‘ํ•˜๋„๋ก ์„ค์ •

ํ•ต์‹ฌ ์ฝ”๋“œ:

void _saveIp() {
  setState(() {
    _savedIp = _ipController.text;
  });
  ScaffoldMessenger.of(context).showSnackBar(
    SnackBar(content: Text("IP ์ฃผ์†Œ๊ฐ€ ์ €์žฅ๋˜์—ˆ์Šต๋‹ˆ๋‹ค: $_savedIp")),
  );
}

3. ์˜์ƒ ์ŠคํŠธ๋ฆฌ๋ฐ

์‚ฌ์šฉ์ž๊ฐ€ ์นด๋ฉ”๋ผ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ๋ผ์ฆˆ๋ฒ ๋ฆฌํŒŒ์ด์—์„œ ์˜์ƒ ์ŠคํŠธ๋ฆฌ๋ฐ์„ ์‹œ์ž‘ํ•˜๊ฑฐ๋‚˜ ์ค‘์ง€

ํ•ต์‹ฌ ์ฝ”๋“œ:

void _toggleWebcam() {
  if (_savedIp.isEmpty) {
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text("๋จผ์ € IP ์ฃผ์†Œ๋ฅผ ์ €์žฅํ•ด์ฃผ์„ธ์š”.")),
    );
    return;
  }

  setState(() {
    if (!_showWebcam) {
      _showWebcam = true;
      _webViewController.loadRequest(
          Uri.parse('http://$_savedIp:5000/stream'));
    } else {
      _showWebcam = false;
      _webViewController.loadRequest(
          Uri.parse('http://$_savedIp:5000/stop_stream'));
    }
  });
}

4. ์Œ์„ฑ ์†ก์‹  (์•ฑ โ†’ ๋ผ์ฆˆ๋ฒ ๋ฆฌํŒŒ์ด)

์‚ฌ์šฉ์ž๊ฐ€ ๋งˆ์ดํฌ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๊ณ  ์žˆ๋Š” ๋™์•ˆ ์Œ์„ฑ์„ ๋…น์Œํ•˜์—ฌ ๋ผ์ฆˆ๋ฒ ๋ฆฌํŒŒ์ด์— ์ „์†ก

ํ•ต์‹ฌ ์ฝ”๋“œ:

GestureDetector(
  onLongPressStart: (details) {
    setState(() {
      isRecordPressed = true;
    });
    startRecording();
  },
  onLongPressEnd: (details) async {
    setState(() {
      isRecordPressed = false;
    });
    await stopRecording();
    await uploadAudio();
  },
  child: ElevatedButton(
    onPressed: () {},
    style: ElevatedButton.styleFrom(
      backgroundColor: isRecordPressed ? Colors.blue : Colors.white,
      shape: CircleBorder(),
      padding: const EdgeInsets.all(20),
    ),
    child: Icon(
      Icons.mic,
      color: isRecordPressed ? Colors.white : Colors.blue,
      size: 30,
    ),
  ),
),

5. ์Œ์„ฑ ์ˆ˜์‹  (๋ผ์ฆˆ๋ฒ ๋ฆฌํŒŒ์ด โ†’ ์•ฑ)

์‚ฌ์šฉ์ž๊ฐ€ ์Šคํ”ผ์ปค ๋ฒ„ํŠผ์„ ๋ˆŒ๋Ÿฌ ๋ผ์ฆˆ๋ฒ ๋ฆฌํŒŒ์ด์˜ ๋งˆ์ดํฌ ์ž…๋ ฅ์„ ์‹ค์‹œ๊ฐ„์œผ๋กœ ์ถœ๋ ฅ

ํ•ต์‹ฌ ์ฝ”๋“œ:

Future<void> _startStreaming() async {
  if (_savedIp.isEmpty) {
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text("๋จผ์ € IP ์ฃผ์†Œ๋ฅผ ์ €์žฅํ•ด์ฃผ์„ธ์š”.")),
    );
    return;
  }

  final String audioUrl = "http://$_savedIp:5000/audio";
  try {
    await _audioPlayer.stop();
    await _audioPlayer.setUrl(audioUrl);
    _audioPlayer.play();
    setState(() {
      _isPlaying = true;
    });
  } catch (e) {
    print("Error starting audio stream: $e");
  }
}

6. ๋„์–ด๋ฝ ์ œ์–ด

์‚ฌ์šฉ์ž๊ฐ€ ๋„์–ด๋ฝ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ๋ผ์ฆˆ๋ฒ ๋ฆฌํŒŒ์ด์— ์‹ ํ˜ธ๋ฅผ ์ „์†กํ•˜์—ฌ ๋„์–ด๋ฝ์„ ์ž ๊ทธ๊ฑฐ๋‚˜ ํ•ด์ œ ์‹ ํ˜ธ ์ „๋‹ฌ

ํ•ต์‹ฌ ์ฝ”๋“œ:

Future<void> _sendRequest() async {
  if (_savedIp.isEmpty) {
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text("๋จผ์ € IP ์ฃผ์†Œ๋ฅผ ์ €์žฅํ•ด์ฃผ์„ธ์š”.")),
    );
    return;
  }

  final url = Uri.parse("http://$_savedIp:5000/trigger");
  try {
    final response = await http.get(url);
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text("์š”์ฒญ์ด ์„ฑ๊ณต์ ์œผ๋กœ ์ „์†ก๋˜์—ˆ์Šต๋‹ˆ๋‹ค.")),
    );
  } catch (e) {
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text("์š”์ฒญ ์‹คํŒจ: $e")),
    );
  }
}

7. ๋…นํ™” ๋ชฉ๋ก ๊ด€๋ฆฌ

Firebase Storage์—์„œ ๋…นํ™”๋œ ์˜์ƒ ๋ชฉ๋ก์„ ๊ฐ€์ ธ์™€ ์‚ฌ์šฉ์ž์—๊ฒŒ ํ‘œ์‹œํ•˜๋ฉฐ, ์‚ฌ์šฉ์ž๋Š” ์›ํ•˜๋Š” ์˜์ƒ์„ ์„ ํƒํ•˜์—ฌ ์žฌ์ƒ

Firebase Storage์—์„œ ๋…นํ™” ๋ชฉ๋ก ๊ฐ€์ ธ์˜ค๊ธฐ:

Future<void> _fetchVideoList() async {
  try {
    final ref = FirebaseStorage.instance.ref('videos');
    final result = await ref.listAll();

    final videoInfo = await Future.wait(
      result.items.map((fileRef) async {
        final metadata = await fileRef.getMetadata();
        final url = await fileRef.getDownloadURL();
        final createdTime = metadata.timeCreated ?? DateTime.now();
        final formattedTime = '${createdTime.year}-${createdTime.month.toString().padLeft(2, '0')}-${createdTime.day.toString().padLeft(2, '0')} ${createdTime.hour.toString().padLeft(2, '0')}:${createdTime.minute.toString().padLeft(2, '0')}';

        return {'url': url, 'name': formattedTime};
      }).toList(),
    );

    setState(() {
      videoData = videoInfo;
      isLoading = false;
    });
  } catch (e) {
    print('Error fetching video list: $e');
    setState(() {
      isLoading = false;
    });
  }
}

๋น„๋””์˜ค ์žฌ์ƒ:

lass VideoPlayerScreen extends StatefulWidget {
  final String videoUrl;

  const VideoPlayerScreen({required this.videoUrl});

  @override
  _VideoPlayerScreenState createState() => _VideoPlayerScreenState();
}

class _VideoPlayerScreenState extends State<VideoPlayerScreen> {
  late VideoPlayerController _controller;

  @override
  void initState() {
    super.initState();
    _controller = VideoPlayerController.network(widget.videoUrl)
      ..initialize().then((_) {
        setState(() {});
        _controller.play();
      });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Play Video'),
      ),
      body: Center(
        child: _controller.value.isInitialized
            ? AspectRatio(
                aspectRatio: _controller.value.aspectRatio,
                child: VideoPlayer(_controller),
              )
            : CircularProgressIndicator(),
      ),
    );
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }
}

๐Ÿ–ฅ ๊ธฐ๋Šฅ ์‹œ์—ฐ

๋™์ž‘ ๊ฐ์ง€ ์„ผ์„œ & ๋…นํ™” ๊ธฐ๋Šฅ

๋™์ž‘ ๊ฐ์ง€ ์„ผ์„œ ์ด๋ฏธ์ง€ ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย ย  ๋…นํ™” ์ด๋ฏธ์ง€


  • ๋ฐฉ๋ฌธ์ž์˜ ์›€์ง์ž„ ๊ฐ์ง€ ์‹œ ์ž๋™์œผ๋กœ ๋…นํ™” ์‹œ์ž‘
  • ์ผ์ • ์‹œ๊ฐ„ ๋™์•ˆ ๊ฐ์ง€๋˜๋ฉด ์Šค๋งˆํŠธํฐ์œผ๋กœ ๋ฌธ ์•ž์— ๋ˆ„๊ฐ€ ์žˆ๋‹ค๊ณ  ์•Œ๋ฆผ ์ „์†ก
  • ๋…นํ™”๋œ ์˜์ƒ์€ ์Šค๋งˆํŠธํฐ ์•ฑ์—์„œ ํ™•์ธ ๊ฐ€๋Šฅ

์ดˆ์ธ์ข… ๊ธฐ๋Šฅ

์ดˆ์ธ์ข… ์ด๋ฏธ์ง€


  • ์ดˆ์ธ์ข… ๋ฒ„ํŠผ ๋ˆ„๋ฅผ ์‹œ ๋ถ€์ €๋ฅผ ํ†ตํ•ด "๋”ฉ๋™" ์†Œ๋ฆฌ๊ฐ€ ๋‚จ
  • ์Šค๋งˆํŠธํฐ์œผ๋กœ ๋ˆ„๊ตฐ๊ฐ€๊ฐ€ ์ดˆ์ธ์ข…์„ ๋ˆŒ๋ €๋‹ค๊ณ  ์•Œ๋ฆผ ์ „์†ก

ํ™”์ƒ ํ†ตํ™” ๊ธฐ๋Šฅ

ํ™”์ƒ ํ”„๋กœ์„ธ์Šค


ํ†ตํ™” ์˜์ƒ์„ ๋ณด๋ ค๋ฉด ํ†ตํ™” ํด๋ฆญ

  • ์ดˆ์ธ์ข…์˜ ์นด๋ฉ”๋ผ๋กœ ๋ฐ”๊นฅ ์ƒํ™ฉ์„ ์‹ค์‹œ๊ฐ„์œผ๋กœ ์•ฑ์—์„œ ํ™•์ธ
  • ์Šค๋งˆํŠธํฐ ์•ฑ์—์„œ ์Œ์„ฑ ๋ฐ ์˜์ƒ ํ†ตํ™” ์‹คํ–‰
  • ์ดˆ์ธ์ข… ๋งˆ์ดํฌ๋กœ ์Œ์„ฑ ์ „๋‹ฌ, ์Šค๋งˆํŠธํฐ ์Šคํ”ผ์ปค๋กœ ์ถœ๋ ฅ
  • ์Šค๋งˆํŠธํฐ ๋งˆ์ดํฌ๋กœ ์Œ์„ฑ ์ „๋‹ฌ, ์ดˆ์ธ์ข… ์Šคํ”ผ์ปค๋กœ ์ถœ๋ ฅ

๐Ÿ‘จ๐Ÿปโ€๐Ÿ’ป ํŒ€์› ์†Œ๊ฐœ ๋ฐ ์—ญํ•  ๋ถ„๋‹ด

Profile Role Part
ํŒ€์žฅ - ํ”„๋กœ์ ํŠธ ๋ฆฌ๋”
- ํ”„๋กœ์„ธ์Šค ์ผ์ • ๋ฐ ์ „๋ฐ˜์ ์ธ ๊ด€๋ฆฌ
- ๋ผ์ฆˆ๋ฒ ๋ฆฌํŒŒ์ด ํ†ตํ™” ํ”„๋กœ์„ธ์Šค ๊ตฌ์ถ•
- ์นด๋ฉ”๋ผ ์—ฐ๊ฒฐ ๋ฐ ํ•˜๋“œ์›จ์–ด ์—ฐ๊ฒฐ
- ํ™”์ƒ ํ”„๋กœ์„ธ์Šค ๊ตฌ์ถ•
ํŒ€์› - ๋ฉ”์ธ ์Šค๋ ˆ๋“œ ๊ตฌ์ถ•
- ํ”„๋กœ์„ธ์Šค ์—ฐ๊ฒฐ ๋‹ด๋‹น
- ์ดˆ์ธ์ข…, ๋™์ž‘ ๊ฐ์ง€ ์„ผ์„œ ๋“ฑ ํ•˜๋“œ์›จ์–ด ์‹œ์Šคํ…œ ๊ด€๋ฆฌ
ํŒ€์› - ๋ฐ์ดํ„ฐ ์†ก์ˆ˜์‹  ๊ด€๋ฆฌ
- firebase ์„œ๋ฒ„ ๊ตฌ์ถ• ๋ฐ ์†Œ์ผ“ ์—ฐ๊ฒฐ
- ์Šค๋งˆํŠธํฐ ํ†ตํ™” ํ”„๋กœ์„ธ์Šค ๊ตฌ์ถ•
- ํ™”์ƒ ํ”„๋กœ์„ธ์Šค ๊ตฌ์ถ•
- flutter ์•ฑ ๊ฐœ๋ฐœ
ํŒ€์› - ๋…นํ™” ์‹œ์Šคํ…œ ๊ฐœ๋ฐœ
- ๋…นํ™” ํŒŒ์ผ ์ €์žฅ ๋ฐ ์ „์†ก ์‹œ์Šคํ…œ ๊ตฌ์ถ•

๐Ÿ” ๊ฒฐ๋ก  ๋ฐ ํ–ฅํ›„ ๊ฐœ์„  ๋ฐฉํ–ฅ

ํ”„๋กœ์ ํŠธ ์„ฑ๊ณผ ์š”์•ฝ

  • ์‹ค์‹œ๊ฐ„ ์Œ์„ฑ ๋ฐ ์˜์ƒ ์ „์†ก: Raspberry Pi์™€ ์•ฑ ๊ฐ„์˜ ์‹ค์‹œ๊ฐ„ ์Œ์„ฑ ์ „๋‹ฌ ๋ฐ ์ŠคํŠธ๋ฆฌ๋ฐ ๊ธฐ๋Šฅ ๊ตฌํ˜„ ์„ฑ๊ณต
  • ์›€์ง์ž„ ๊ธฐ๋ฐ˜ ๋…นํ™”: PIR ์„ผ์„œ๋ฅผ ํ†ตํ•œ ์›€์ง์ž„ ๊ฐ์ง€ ๋ฐ ๋…นํ™”, Firebase Storage๋กœ ๋…นํ™” ์˜์ƒ ์—…๋กœ๋“œ ์‹œ์Šคํ…œ ๊ตฌ์ถ•
  • ์žฅ์น˜ ๊ฐ„ ์ถฉ๋Œ ๋ฌธ์ œ ํ•ด๊ฒฐ: ์˜ค๋””์˜ค, ์นด๋ฉ”๋ผ ๋ชจ๋“ˆ ๋“ฑ ์žฅ์น˜ ๊ฐ„ ๊ฐ„์„ญ ๋ฌธ์ œ๋ฅผ ์†Œํ”„ํŠธ์›จ์–ด ๋ฐ ํ•˜๋“œ์›จ์–ด์ ์œผ๋กœ ํ•ด๊ฒฐ

ํ•ด๊ฒฐ๋œ ์ฃผ์š” ๋ฌธ์ œ ๋ฐ ๋ฐฉ์•ˆ

  1. ์‹ค์‹œ๊ฐ„ ์Œ์„ฑ ์ „๋‹ฌ ๊ฐ„ ์˜ค๋ฅ˜ ๋ฐœ์ƒ

    • ๋ฌธ์ œ: ์Œ์„ฑ ์ฒ˜๋ฆฌ ์†๋„๊ฐ€ ์Œ์„ฑ ํŒŒ์ผ ์ƒ์„ฑ ์†๋„๋ฅผ ๋”ฐ๋ผ๊ฐ€์ง€ ๋ชปํ•ด ์Šค๋งˆํŠธํฐ์—์„œ์˜ WAV ํŒŒ์ผ ์ „๋‹ฌ ๊ฐ„ ํ ์˜ค๋ฒ„ํ”Œ๋กœ์šฐ ๋ฐœ์ƒ
    • ํ•ด๊ฒฐ ๋ฐฉ์•ˆ: ์•ฑ์—์„œ "๋ˆŒ๋Ÿฌ์„œ ๋งํ•˜๊ธฐ" ๊ธฐ๋Šฅ ๋„์ž…์œผ๋กœ ์Œ์„ฑ ํŒŒ์ผ ์ƒ์„ฑ๊ณผ ์ „์†ก ๊ฐ„์˜ ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ๋ฅผ ๊ฐœ์„ 
  2. ์˜ค๋””์˜ค ์žฅ์น˜ ์„ค์ • ๊ฐ„ ๋ฌธ์ œ ๋ฐœ์ƒ

    • ๋ฌธ์ œ: ๊ธฐ๋ณธ ์˜ค๋””์˜ค ์ž…๋ ฅ ๋ฐ ์ถœ๋ ฅ ์žฅ์น˜ ๊ฐ„์˜ ์ถฉ๋Œ
    • ํ•ด๊ฒฐ ๋ฐฉ์•ˆ:
      • pyaudio, pygame ๋‘˜๋‹ค ๊ธฐ๋ณธ ์žฅ์น˜ ๋ฒˆํ˜ธ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ถฉ๋Œ
      • ALSA์—์„œ ๊ธฐ๋ณธ ์žฅ์น˜ ์„ค์ • ๋ณ€๊ฒฝ (/etc/asound.conf)
      • Pygame์€ ๊ธฐ๋ณธ ์žฅ์น˜๋กœ 3.5mm ์Šคํ”ผ์ปค ์‚ฌ์šฉ, PyAudio๋Š” ์นด๋“œ ๋ฒˆํ˜ธ๋กœ ๋งˆ์ดํฌ ์žฅ์น˜ ์ง€์ •
  3. ์•ฑ์—์„œ ์ „์†กํ•œ ์Œ์„ฑ ์žฌ์ƒ ๋ถˆ๊ฐ€ ๋ฌธ์ œ

    • ๋ฌธ์ œ: ๋…น์Œ, ์ „์†ก, ์ €์žฅ ์„ฑ๊ณต ํ›„ Pygame์—์„œ ์žฌ์ƒ ์˜ค๋ฅ˜ ๋ฐœ์ƒ
    • ํ•ด๊ฒฐ ๋ฐฉ์•ˆ: ์•ฑ์˜ ์Œ์„ฑ ์ฝ”๋ฑ(Encoder)๊ณผ Raspberry Pi ์žฌ์ƒ ์ฝ”๋ฑ(Decoder)์„ ์ผ์น˜์‹œ์ผœ ์˜ค๋ฅ˜ ์ œ๊ฑฐ
  4. ์นด๋ฉ”๋ผ ๊ด€๋ จ ์ฝ”๋“œ ํ†ตํ•ฉ ๊ฐ„ ์ถฉ๋Œ

    • ๋ฌธ์ œ: ์ŠคํŠธ๋ฆฌ๋ฐ(Picamera2)๊ณผ ๋…นํ™”(Libcamera-Vid) ๋™์‹œ ์ฒ˜๋ฆฌ ์‹œ ํ•˜๋“œ์›จ์–ด ์„ฑ๋Šฅ ๋ฌธ์ œ๋กœ ํ”„๋ ˆ์ž„ ์ €ํ•˜ ๋ฐœ์ƒ
    • ํ•ด๊ฒฐ ๋ฐฉ์•ˆ:
      • Lock์„ ์‚ฌ์šฉํ•ด ์นด๋ฉ”๋ผ ๋ชจ๋“ˆ ์ถฉ๋Œ ๋ฐฉ์ง€
      • ์ŠคํŠธ๋ฆฌ๋ฐ: ๋กœ๋”ฉ ์†๋„๊ฐ€ ๋น ๋ฅธ Picamera2 ์‚ฌ์šฉ
      • ๋…นํ™”: ์•ˆ์ •์ ์ธ Libcamera-Vid ์‚ฌ์šฉ
  5. PIR ์„ผ์„œ ๋ฏผ๊ฐ๋„ ๋ฌธ์ œ

    • ๋ฌธ์ œ: PIR ์„ผ์„œ์˜ High ์‹ ํ˜ธ ์ „๋‹ฌ ์‹œ๊ฐ„์ด ๊ธธ์–ด ๋ถˆ๊ทœ์น™์  ๋™์ž‘ ๋ฐœ์ƒ
    • ํ•ด๊ฒฐ ๋ฐฉ์•ˆ:
      • ๊ฐ€๋ณ€ ์ €ํ•ญ์ด ๋‹ฌ๋ฆฐ PIR ์„ผ์„œ๋กœ ๊ต์ฒด
      • ํ•˜๋“œ์›จ์–ด์ ์œผ๋กœ ํ–‡๋น› ์ฐจ๋‹จ ๋ฐ ์ธก์ • ๋ฒ”์œ„ ์ œํ•œ
      • ์†Œํ”„ํŠธ์›จ์–ด์ ์œผ๋กœ LOW ์ƒํƒœ์—์„œ๋„ ๋…นํ™” ์ง€์†๋˜๋„๋ก ์กฐ์ •
  6. 3.5mm ์˜ค๋””์˜ค ๊ฐ„์„ญ ๋ฌธ์ œ

    • ๋ฌธ์ œ: GPIO ํ•€ ๊ฐ„์„ญ์œผ๋กœ ์ธํ•ด ์Šคํ”ผ์ปค์—์„œ ๋น„์ •์ƒ์ ์ธ ์†Œ์Œ ๋ฐœ์ƒ
    • ํ•ด๊ฒฐ ๋ฐฉ์•ˆ: ์„œ๋ณด๋ชจํ„ฐ์˜ GPIO ํ•€์„ 18, 19์—์„œ 13๋ฒˆ์œผ๋กœ ๋ณ€๊ฒฝํ•˜๊ณ  SoftPWM ๋ฐฉ์‹์œผ๋กœ ๊ฐ„์„ญ ์ œ๊ฑฐ

ํ–ฅํ›„ ๊ฐœ์„  ๋ฐ ๊ธฐ๋Šฅ ํ™•์žฅ ๊ฐ€๋Šฅ์„ฑ

  1. ์–ผ๊ตด ์ธ์‹์„ ํ†ตํ•œ ์‚ฌ์šฉ์ž ์•Œ๋ฆผ

    • OpenCV๋‚˜ YOLO์™€ ๊ฐ™์€ ์–ผ๊ตด ์ธ์‹ ๊ธฐ์ˆ ์„ ํ™œ์šฉํ•ด ๋ฐฉ๋ฌธ์ž๋ฅผ ๋“ฑ๋ก ๋ฐ ์‹๋ณ„
    • ์–ผ๊ตด ๋“ฑ๋ก์„ ํ†ตํ•ด "[๋ฐฉ๋ฌธ์ž 1]๋‹˜์ด ๋„์ฐฉํ•˜์˜€์Šต๋‹ˆ๋‹ค."์™€ ๊ฐ™์€ ๋งž์ถคํ˜• ์•Œ๋ฆผ ์ œ๊ณต
    • ๋ณด์•ˆ ์ˆ˜์ค€์„ ๋†’์ด๊ณ , ๊ฐ€์กฑ ๋ฐ ์นœ๊ตฌ์™€ ๊ฐ™์€ ์ž์ฃผ ๋ฐฉ๋ฌธํ•˜๋Š” ์‚ฌ๋žŒ๋“ค์—๊ฒŒ ํŽธ๋ฆฌํ•จ ์ œ๊ณต
  2. ์Œ์„ฑ ํ’ˆ์งˆ ํ–ฅ์ƒ

    • ์†Œ์Œ ์ œ๊ฑฐ ๋ฐ ํ•„ํ„ฐ๋ง ๊ธฐ์ˆ ์„ ํ™œ์šฉํ•œ ์Œ์„ฑ ํ’ˆ์งˆ ๊ฐœ์„  (์˜ˆ: WebRTC, Noise Suppression)
    • ๋งˆ์ดํฌ์™€ ์Šคํ”ผ์ปค ๊ฐ„ ๊ฐ„์„ญ ์ตœ์†Œํ™”๋ฅผ ์œ„ํ•œ ํ•˜๋“œ์›จ์–ด ๋ฐ ์†Œํ”„ํŠธ์›จ์–ด ์ตœ์ ํ™”
    • ๋” ์ž์—ฐ์Šค๋Ÿฌ์šด ์–‘๋ฐฉํ–ฅ ์Œ์„ฑ ํ†ต์‹  ์ง€์›
  3. ์ƒ์‹œ ์Œ์„ฑ ์ „๋‹ฌ ๊ตฌํ˜„

    • ์Šค๋งˆํŠธํฐ ์•ฑ์—์„œ ์ฒญํฌ ๋‹จ์œ„๋กœ ์Œ์„ฑ ํŒŒ์ผ ์ „๋‹ฌํ•˜๋Š” ๊ธฐ๋Šฅ ๊ตฌํ˜„ํ–ˆ์œผ๋‚˜ ์˜ค๋ฒ„ํ”Œ๋กœ์šฐ ๋ฐœ์ƒ
    • ํ•„ํ„ฐ๋ง ๊ธฐ์ˆ ์„ ํ†ตํ•œ ํŒŒ์ผ ์ „์†ก ์ตœ์†Œํ™”๋กœ ์—ด๋ฆฐ ๋งˆ์ดํฌ ๊ธฐ๋Šฅ ๊ตฌํ˜„
  4. ์‚ฌ์šฉ์ž ํŽธ์˜์„ฑ ์ฆ๋Œ€

    • ์•ฑ UI ๊ฐœ์„ : ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ์ค‘์‹ฌ์œผ๋กœ ์ง๊ด€์ ์ธ ๋””์ž์ธ์œผ๋กœ ๋ฆฌ๋‰ด์–ผ
    • ์•Œ๋ฆผ ์ปค์Šคํ„ฐ๋งˆ์ด์ง• ๊ธฐ๋Šฅ ์ถ”๊ฐ€: ์‚ฌ์šฉ์ž๊ฐ€ ์•Œ๋ฆผ ํ†ค, ์ง„๋™ ํŒจํ„ด ๋“ฑ์„ ์„ ํƒํ•  ์ˆ˜ ์žˆ๋„๋ก ์„ค์ • ๋ฉ”๋‰ด ์ œ๊ณต
    • ๋ฐฉ๋ฌธ์ž ๊ธฐ๋ก ํ™•์ธ ๊ธฐ๋Šฅ ์ถ”๊ฐ€: ๋ฐฉ๋ฌธ์ž ํžˆ์Šคํ† ๋ฆฌ๋ฅผ ๋‚ ์งœ๋ณ„๋กœ ํ™•์ธ ๊ฐ€๋Šฅ
  5. ๋ณด์•ˆ ๊ฐ•ํ™”

    • ๋™์˜์ƒ ๋ฐ์ดํ„ฐ์˜ ์•”ํ˜ธํ™” ๋ฐ ํด๋ผ์šฐ๋“œ ์ €์žฅ ์‹œ ๋ณด์•ˆ ๊ฐ•ํ™”
    • ์•ฑ๊ณผ ๋ผ์ฆˆ๋ฒ ๋ฆฌํŒŒ์ด ๊ฐ„ ํ†ต์‹ ์˜ HTTPS ์ ์šฉ์œผ๋กœ ๋ฐ์ดํ„ฐ ์ „์†ก ์ค‘ ๋ณด์•ˆ ํ™•๋ณด
    • ์•Œ๋ฆผ์— ์ด์ค‘ ์ธ์ฆ ๊ธฐ๋Šฅ ์ถ”๊ฐ€๋กœ ์‚ฌ์šฉ์ž์˜ ์ •๋ณด ๋ณดํ˜ธ

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 3

  •  
  •  
  •