본문 바로가기

ESP32 Project

현대적인 작업 공간을 위한 스마트한 표시등

반응형

현대의 업무 환경은 협업을 통해 발전하지만, 동시에 방해받지 않는 집중력 또한 필수적입니다. 일반적인 사무실에서는 동료의 책상으로 다가가면 회의 중이거나 중요한 통화를 하고 있거나 어떤 업무에 몰두하고 있는 경우가 흔합니다. 이러한 예상치 못한 방해는 집중력을 흐트러뜨릴 뿐만 아니라 방해받는 사람과 도움을 요청하는 동료 모두의 생산성을 저하시킵니다.

 

 

 

 

온라인 협업 도구는 사용 가능 상태를 표시하지만, 대개 특정 컴퓨터에만 적용되어 사무실이나 작업 공간에 접근하는 사람들에게는 보이지 않습니다. 방문객이나 동료는 여전히 문을 두드리거나, 안을 들여다보거나, 누군가의 업무를 방해해야만 사용 가능한지 확인할 수 있습니다.

 

이 문제를 해결하기 위해 저는 책상 안팎에서 사용자의 상태를 확인할 수 있는 무선 사무실 재실 표시기를 개발했습니다. 이 시스템은 두 개의 동기화된 장치, 즉 소형 데스크탑 큐브와 문 부착형 표시기로 구성됩니다. 데스크탑 큐브를 돌리기만 하면 재실 상태( 사용 가능 , 사용 중 , 잠시 후 복귀 , 회의 중 )가 변경되며 , ESP-NOW 무선 통신을 통해 두 장치 모두 즉시 업데이트됩니다. 두 장치 모두 900mAh 용량의 3.7V 리튬 이온 충전식 배터리 로 작동하여 외부 전원 케이블 없이 완전 무선으로 간편하게 설치할 수 있습니다.

 

이 시스템은 Google 캘린더 연동 도 지원하여 예정된 회의 중에 상태가 자동으로 업데이트됩니다. 수동으로 제어하고 싶을 때는 기기를 간단히 흔드는 동작으로 수동 모드 와 캘린더 모드 간에 전환할 수 있어 사용자가 자신의 일정 관리 방식을 완벽하게 제어할 수 있습니다.

 

자, 이제 장치를 만들어 봅시다.

 

 

  1. 샤오 ESP32-C6 1개
  2. ESP32-C3 개발 키트 모듈 1개
  3. 3.7V 리튬 이온 배터리 2개
  4. TP4056 배터리 충전 모듈
  5. MPU6050 모듈
  6. WS2812 LED 스트립 1개
  7. WS2812 8LED 원형 모듈
  8. 점퍼 와이어
  9. 글루건
  10. 납땜 인두
  11. 3D 프린팅 케이스

 

1단계: 하드웨어 연결

 

하드웨어 연결

 

데스크 노드

 

구성 요소를 다음과 같이 연결하십시오.

GPIO 6 - SDA (MPU6050)

GPIO 7 - SCL (MPU6050)

GPIO 2 - WS2812 데이터

3.3V - MPU6050 VCC

GND - 공통 기반

 

도어 노드

 

샤오 ESP32-C6

D0 - WS2812 데이터

5V / VBAT - LED 전원

GND 공통 기반

 

샤오 차량에는 배터리 충전 회로가 내장되어 있으므로 뒷면 배터리 단자의 BAT+와 BAT-를 연결하기만 하면 됩니다.

 

2단계: 3D 프린팅 케이스 제작

 

3D 프린팅 케이스

 

이 프로젝트는 맞춤 설계된 두 개의 케이스를 사용합니다.

 

데스크 노드

 

컴팩트한 큐브 디자인

상단에는 흰색 PLA 소재의 불투명 디퓨저가 있습니다.

내부 MPU6050 평면 장착

디퓨저 아래쪽에 위치한 LED 링

USB-C 충전 포트

 

도어 노드

 

슬림형 수직형 케이스

불투명한 흰색 PLA 소재의 전면 디퓨저.

높은 시인성을 위한 WS2812 LED 4개

양면 접착 테이프/자석 부착

USB-C 충전 포트

 

확산기는 부드럽고 균일한 빛을 만들어 사무실 어디에서든 쉽게 볼 수 있도록 해줍니다. 아래 자료와 소프트웨어 코드는 이 문서으 출처로 가서 다운로드 하세요. 원 저자에게 감사합니다. 미리 씁니다. ^^

 

smallbox_3x3.stl 

box_6x6finl.stl 아이콘

box_6x6finl.stl

 

 

3단계: 구성 요소 조립

 

구성 요소 조립

구성 요소 조립

구성 요소 조립

구성 요소 조립

 

연결도를 참고하여 모든 부품을 납땜한 후, 맞춤형 3D 프린팅 케이스 안에 두 노드를 조립합니다. 디자인이 컴팩트하기 때문에, 부품을 올바른 순서대로 조립하면 케이블 정리가 훨씬 쉬워지고 깔끔한 최종 결과물을 얻을 수 있습니다.

 

배선을 케이스 안쪽에 깔끔하게 정리하고 디퓨저를 닫으세요.

 

완성된 도어 노드를 사무실 입구 옆에 부착하려면 케이스 뒷면에 양면 접착 테이프를 사용하십시오.

 

4단계: 코드 작성 및 작동

 

코드 및 작업

코드 및 작업

 

데스크 노드와 도어 노드 모두 아두이노 IDE를 사용하여 펌웨어를 개발했기 때문에 프로젝트를 쉽게 수정하고 확장할 수 있습니다. 이 시스템은 두 개의 독립적인 펌웨어 파일로 구성되어 있으며, 하나는 ESP32-C3 데스크 노드 에서 , 다른 하나는 Seeed Studio XIAO ESP32-C6 도어 노드 에서 실행됩니다. 두 장치는 ESP-NOW를 사용하여 직접 통신 하므로 Wi-Fi 라우터 없이도 빠르고 지연 시간이 짧은 무선 통신이 가능합니다.

 

필수 라이브러리, 이 프로젝트는 널리 사용되는 소수의 라이브러리에 의존합니다.

 

아다프루트 MPU6050

아다프루트 네오픽셀

ESP-나우

 

이 라이브러리들은 실시간 상태 동기화를 구현하기 위한 안정적이고 가벼운 프레임워크를 제공합니다.

 

데스크 노드 펌웨어, 데스크 노드는 시스템의 주요 컨트롤러 역할을 합니다.

 

그 책임에는 다음이 포함됩니다:

 

MPU6050 가속도계를 읽는 중입니다.

정육면체의 어느 면이 위쪽을 향하고 있는지 판별합니다.

각 방향을 미리 정의된 사용 가능 상태에 매핑합니다.

로컬 WS2812 LED 링을 업데이트합니다.

모드 전환을 위해 손떨림 동작을 감지합니다.

ESP-NOW를 통해 도어 노드에 상태 업데이트를 전송합니다.

캘린더 모드가 활성화된 경우 Google 캘린더와 동기화합니다.

 

방향 감지

 

MPU6050은 X, Y, Z축을 따라 가속도를 지속적으로 측정합니다.

 

펌웨어는 모든 움직임에 즉각적으로 반응하는 대신, 큐브가 안정될 때까지 기다린 후 방향을 결정합니다. 이는 큐브를 들거나 회전시키는 동안 의도치 않은 상태 변화를 방지합니다.

 

큐브의 각 면은 미리 정의된 가용성 상태에 해당합니다.

 

🟢 녹색 — 구매 가능

🔴 빨간색 — 바쁨

🟡 옐로우 — 잠시만 기다려 주세요

🔵 블루 — 회의 중

 

큐브를 윗면이나 아랫면을 바닥에 평평하게 놓으면 LED가 꺼져 기기가 대기 상태가 됩니다.

 

수동 모드 및 캘린더 모드

 

펌웨어는 두 가지 작동 모드를 지원합니다.

 

수동 모드: 사용자가 큐브를 회전시켜 사용 가능 여부를 완전히 제어할 수 있습니다.

 

방향이 바뀔 때마다 데스크 노드가 변경됩니다.

 

  • 새로운 얼굴을 감지합니다.
  • LED 링을 업데이트합니다.
  • 새로운 상태를 도어 노드에 브로드캐스트합니다.

 

이를 통해 두 기기 모두에서 즉각적인 시각적 피드백을 받을 수 있습니다.

 

캘린더 모드: 캘린더 모드에서 데스크 노드는 사용자의 회의 일정을 Google 캘린더에서 가져옵니다.

 

회의가 진행되는 동안에는 상태가 자동으로 ' 회의 중' 으로 변경되어 수동 조작 없이 두 노드 모두 업데이트됩니다.

 

이를 통해 물리적 표시기는 하루 종일 사용자의 캘린더와 동기화 상태를 유지할 수 있습니다.

 

ESP-NOW 커뮤니케이션

 

ESP-NOW는 데스크 노드와 도어 노드 간의 직접적인 무선 통신을 제공하는 데 사용됩니다.

 

기존 Wi-Fi 통신과 달리 ESP-NOW는 다음이 필요하지 않습니다.

 

  • 무선 라우터
  • 인터넷 연결
  • MQTT 브로커
  • 클라우드 서버

 

데스크 노드는 상태 변화를 감지할 때마다 다음과 같은 내용을 포함하는 간결한 데이터 패킷을 전송합니다.

 

  • 현재 상태
  • 활성 작동 모드

 

도어 노드는 이 패킷을 즉시 수신하고 LED 표시등을 업데이트하여 거의 실시간으로 동기화됩니다.

 

ESP-NOW는 이 애플리케이션에 여러 가지 장점을 제공하기 때문에 선정되었습니다.

 

  • 즉각적인 P2P 커뮤니케이션.
  • 낮은 전력 소비량.
  • 외부 인프라에 대한 의존성이 없습니다.
  • 안정적인 실내 통신.
  • 프로그래밍 오버헤드가 최소화됩니다.
  • 두 노드 간 거의 실시간 동기화.

 

도어 노드 코드, 도어 노드는 경량 수신기로 작동합니다.

 

주요 책임은 다음과 같습니다.

 

  • 수신되는 ESP-NOW 패킷을 수신 대기합니다.
  • 수신된 상태를 해독합니다.
  • WS2812 LED 4개를 업데이트합니다.
  • 데스크 노드와의 동기화를 유지합니다.

 

모든 의사 결정이 데스크 노드에서 이루어지기 때문에 도어 노드 펌웨어는 단순하고 반응성이 뛰어나며 에너지 효율이 높습니다.

 

전체 펌웨어 워크플로, 펌웨어 전체 설치 과정은 다음과 같습니다.

 

  1. 하드웨어 주변 장치를 초기화합니다.
  2. ESP-NOW 통신을 구성합니다.
  3. MPU6050 센서 데이터를 읽습니다.
  4. 큐브의 방향을 감지합니다.
  5. 악수 동작을 확인하세요.
  6. 활성 작동 모드를 확인합니다.
  7. 로컬 LED 표시등을 업데이트합니다.
  8. 현재 상태를 방송합니다.
  9. 도어 노드가 업데이트를 수신합니다.
  10. 도어 표시등은 새로운 사용 가능 상태를 나타냅니다.

 

이러한 이벤트 기반 접근 방식은 불필요한 처리를 최소화하면서 반응성이 뛰어나고 원활한 사용자 경험을 제공합니다.

 

첨부된 아두이노 IDE 코드를 참조하십시오.

 

msterDeskNode.ino

 

 /**************************************************************************
 * Meeting Indicator - Desk Node (MASTER)
 * ESP32-C6 + MPU6050 + WS2812 Ring
 * Uses ESP-NOW
 **************************************************************************/

#include <WiFi.h>
#include <esp_now.h>
#include <Wire.h>

#include <Adafruit_MPU6050.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_NeoPixel.h>

/************ Pins ************/
#define SDA_PIN     6
#define SCL_PIN     7

#define LED_PIN     2
#define LED_COUNT   8

/************ Status ************/
enum Status
{
   IDLE = 0,
    AVAILABLE ,
    BUSY,
    BRB,
    MEETING
};

struct StatusPacket
{
    uint8_t status;
};

/************ Objects ************/
Adafruit_MPU6050 mpu;
Adafruit_NeoPixel ring(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);

/************ Variables ************/
Status currentStatus = AVAILABLE;
Status previousStatus = AVAILABLE;
Status candidateStatus = AVAILABLE;

unsigned long stableStart = 0;

StatusPacket packet;

/************ Door Node MAC ************/
// Replace later
uint8_t receiverMAC[] =
{
    0xB4,0x3A,0x45,
    0x89,0x73,0x50
};

/********************************************************************/

void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status)
{
    Serial.print("Send Status: ");

    if (status == ESP_NOW_SEND_SUCCESS)
        Serial.println("Success");
    else
        Serial.println("Failed");
}

/********************************************************************/

void showStatus(Status s)
{
    uint32_t color;

    switch(s)
    {
      case IDLE:
            color = ring.Color(0, 0, 0);
            break;
      
        case AVAILABLE:
            color = ring.Color(0,255,0);
            break;

        case BUSY:
            color = ring.Color(255,0,0);
            break;

        case BRB:
            color = ring.Color(255,180,0);
            break;

        case MEETING:
            color = ring.Color(0,0,255);
            break;

        default:
            color = ring.Color(255,255,255);
            break;
    }

    for(int i=0;i<LED_COUNT;i++)
        ring.setPixelColor(i,color);

    ring.show();
}

/********************************************************************/

void sendStatus(Status s)
{
    packet.status = s;

    esp_err_t result = esp_now_send(
        receiverMAC,
        (uint8_t *)&packet,
        sizeof(packet)
    );

    if(result != ESP_OK)
    {
        Serial.print("Send Error : ");
        Serial.println(result);
    }
}

/********************************************************************/

Status detectFace()
{
    sensors_event_t accel, gyro, temp;

    mpu.getEvent(&accel,&gyro,&temp);

    float ax = accel.acceleration.x / 9.81;
    float ay = accel.acceleration.y / 9.81;
    float az = accel.acceleration.z / 9.81;

    Serial.print("X:");
    Serial.print(ax,2);

    Serial.print("  Y:");
    Serial.print(ay,2);

    Serial.print("  Z:");
    Serial.println(az,2);

    if (fabs(az) > 0.8)
            return IDLE;

    if(ay < -0.80)
        return AVAILABLE;

    if(ax < -0.80)
        return BUSY;

    if(ax > 0.80)
        return BRB;

    if(ay > 0.80)
        return MEETING;

    return currentStatus;
}

/********************************************************************/

void initMPU()
{
    if(!mpu.begin())
    {
        Serial.println("MPU6050 NOT FOUND");

        while(1)
            delay(100);
    }

    mpu.setAccelerometerRange(MPU6050_RANGE_2_G);
    mpu.setGyroRange(MPU6050_RANGE_250_DEG);
    mpu.setFilterBandwidth(MPU6050_BAND_21_HZ);

    Serial.println("MPU6050 Initialized");
}

/********************************************************************/

void initESPNow()
{
    WiFi.mode(WIFI_STA);

    if(esp_now_init()!=ESP_OK)
    {
        Serial.println("ESP-NOW Init Failed");

        while(1)
            delay(100);
    }

    esp_now_register_send_cb(OnDataSent);

    esp_now_peer_info_t peerInfo = {};

    memcpy(peerInfo.peer_addr,receiverMAC,6);

    peerInfo.channel = 0;
    peerInfo.encrypt = false;

    if(esp_now_add_peer(&peerInfo)!=ESP_OK)
    {
        Serial.println("Failed to Add Peer");

        while(1)
            delay(100);
    }

    Serial.println("ESP-NOW Ready");
}

/********************************************************************/

void setup()
{
    Serial.begin(115200);

    delay(1000);

    Serial.println();
    Serial.println("Meeting Indicator Desk Node");

    Wire.begin(SDA_PIN,SCL_PIN);

    ring.begin();
    ring.clear();
    ring.setBrightness(200);
    ring.show();

    initMPU();

    initESPNow();

    currentStatus = AVAILABLE;
    previousStatus = AVAILABLE;
    candidateStatus = AVAILABLE;

    showStatus(currentStatus);

    Serial.println("System Ready");
}

/**************************************************************************
 * Helper Functions
 **************************************************************************/

// White flash to indicate status change
void flashConfirm()
{
    for (int i = 0; i < LED_COUNT; i++)
        ring.setPixelColor(i, ring.Color(255, 255, 255));

    ring.show();
    delay(120);

    showStatus(currentStatus);
}

/**************************************************************************
 * Main Loop
 **************************************************************************/

void loop()
{
    // Read current cube orientation
    Status detected = detectFace();

    // New orientation detected?
    if (detected != candidateStatus)
    {
        candidateStatus = detected;
        stableStart = millis();
    }

    // Has it remained stable long enough?
    if ((millis() - stableStart) > 500)
    {
        if (candidateStatus != currentStatus)
        {
            previousStatus = currentStatus;
            currentStatus = candidateStatus;

            Serial.println();
            Serial.print("Status Changed -> ");

            switch(currentStatus)
            {
                case AVAILABLE:
                    Serial.println("AVAILABLE");
                    break;

                case BUSY:
                    Serial.println("BUSY");
                    break;

                case BRB:
                    Serial.println("BE RIGHT BACK");
                    break;

                case MEETING:
                    Serial.println("MEETING");
                    break;
            }

            // Update local LEDs
            showStatus(currentStatus);

            // Confirmation flash
            flashConfirm();

            // Send to Door Node
            sendStatus(currentStatus);
        }
    }

    delay(50);
}
/********************************************************************/
// loop() will be added in Part 2
/********************************************************************/

 

slavedoorNode.ino

 

/**************************************************************************
 * Meeting Indicator - Door Node (SLAVE)
 * ESP32-C6 + 4 WS2812 LEDs
 * Receives status from Desk Node using ESP-NOW
 **************************************************************************/

#include <WiFi.h>
#include <esp_now.h>
#include <Adafruit_NeoPixel.h>

/************ LED ************/
#define LED_PIN     D0
#define LED_COUNT   4

Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);

/************ Status ************/
enum Status
{
    IDLE = 0,
    AVAILABLE,
    BUSY,
    BRB,
    MEETING
};

struct StatusPacket
{
    uint8_t status;
};

Status currentStatus = IDLE;

/********************************************************************/

void showStatus(Status s)
{
    uint32_t color;

    switch(s)
    {
        case IDLE:
            color = strip.Color(0,0,0);
            break;

        case AVAILABLE:
            color = strip.Color(0,255,0);
            break;

        case BUSY:
            color = strip.Color(255,0,0);
            break;

        case BRB:
            color = strip.Color(255,180,0);
            break;

        case MEETING:
            color = strip.Color(0,0,255);
            break;

        default:
            color = strip.Color(255,255,255);
            break;
    }

    strip.fill(color);
    strip.show();
}

/********************************************************************/

void flashConfirm()
{
    for(int i=0;i<LED_COUNT;i++)
        strip.setPixelColor(i, strip.Color(255,255,255));

    strip.show();
    delay(100);

    showStatus(currentStatus);
}

/********************************************************************/

void OnDataRecv(const esp_now_recv_info_t *recv_info,
                const uint8_t *incomingData,
                int len)
{
    if (len != sizeof(StatusPacket))
        return;

    StatusPacket packet;
    memcpy(&packet, incomingData, sizeof(packet));

    Status newStatus = (Status)packet.status;

    if (newStatus != currentStatus)
    {
        currentStatus = newStatus;

        flashConfirm();
        showStatus(currentStatus);

        Serial.print("Status Updated: ");

        switch (currentStatus)
        {
            case IDLE:
                Serial.println("IDLE");
                break;

            case AVAILABLE:
                Serial.println("AVAILABLE");
                break;

            case BUSY:
                Serial.println("BUSY");
                break;

            case BRB:
                Serial.println("BE RIGHT BACK");
                break;

            case MEETING:
                Serial.println("MEETING");
                break;
        }
    }
}
/********************************************************************/

void setup()
{
    Serial.begin(115200);
    delay(1000);

    Serial.println();
    Serial.println("Meeting Indicator - Door Node");

    strip.begin();
    strip.setBrightness(225);
    strip.clear();
    strip.show();

    WiFi.mode(WIFI_STA);

    Serial.print("Door MAC: ");
    Serial.println(WiFi.macAddress());

    if (esp_now_init() != ESP_OK)
    {
        Serial.println("ESP-NOW Init Failed");

        while (true)
            delay(100);
    }

    esp_now_register_recv_cb(OnDataRecv);

    showStatus(IDLE);

    Serial.println("Waiting for packets...");
}

void loop()
{
    // Nothing required here.
    // ESP-NOW receive callback handles everything.
}

 

 

5단계: Google 캘린더 연동

 

큐브를 수동으로 회전시키는 방식은 사용자의 참석 가능 여부를 빠르고 직관적으로 업데이트하는 방법이지만, 회의에 참여하기 전에 상태를 변경하는 것을 잊어버리는 경우가 종종 있습니다. 이러한 문제를 해결하기 위해 본 프로젝트는 구글 캘린더와의 연동을 포함하여 사용자의 일정이 자동으로 표시되도록 했습니다.

 

캘린더 모드가 활성화 되면 데스크 노드는 설정된 Wi-Fi 네트워크에 연결하여 사용자의 Google 캘린더에서 활성 일정을 주기적으로 확인합니다. 진행 중인 회의가 감지되면 장치는 자동으로 상태를 ' 회의 중 '으로 업데이트하고 , 이 상태는 ESP-NOW를 통해 도어 노드에 즉시 동기화됩니다.

 

이 자동화 기능 덕분에 동료들은 수동 조작 없이도 항상 당신의 정확한 근무 가능 여부를 확인할 수 있습니다.

 

Google Apps Script는 개인 Google 캘린더를 읽어 간소화된 텍스트 피드를 게시합니다.

 

첨부된 스크립트를 확인하고 Google 스크립트 도구에 붙여넣으세요. '배포'를 클릭하면 캘린더 업데이트를 가져오는 데 사용할 URL이 코드에 생성됩니다.

 

또한, 구글 캘린더에서 이벤트를 가져오는 아두이노 IDE 데모 코드를 첨부했습니다.

 

fetchCalendar.ino

 

#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <WiFiClientSecure.h>
#include <FastLED.h>

#define LED_PIN 2
#define NUM_LEDS 8

CRGB leds[NUM_LEDS];

const char* ssid = "YOURWIFINAME";
const char* password = "PASSWORD";

const char* endpoint =
"https://script.googleusercontent.com/macros/echo?user_content_key=YOURKEY";

void setup() {
  Serial.begin(115200);

  FastLED.addLeds<NEOPIXEL, LED_PIN>(leds, NUM_LEDS);

  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
  }
}

void setColor(String status) {
  if (status == "meeting") {
    leds[0] = CRGB::Red;
  }
  else if (status == "busy") {
    leds[0] = CRGB::Yellow;
  }
  else {
    leds[0] = CRGB::Green;
  }

  FastLED.show();
}

void loop() {

  if (WiFi.status() == WL_CONNECTED) {

    WiFiClientSecure client;
client.setInsecure();

HTTPClient http;

http.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);

http.begin(client, endpoint);

int httpCode = http.GET();

    if (httpCode > 0) {

   String payload = http.getString();

Serial.println("PAYLOAD:");
Serial.println(payload);

      DynamicJsonDocument doc(256);

      deserializeJson(doc, payload);

      String status = doc["status"];

      Serial.println(status);

      setColor(status);
    }

    http.end();
  }

  delay(60000); // check every minute
}

 

구글스크립트.txt

 

function doGet() {
  const now = new Date();

  const calendar = CalendarApp.getDefaultCalendar();

  const events = calendar.getEvents(
    new Date(now.getTime() - 1 * 60000),
    new Date(now.getTime() + 1 * 60000)
  );

  let status = "available";

  for (const event of events) {
    const title = event.getTitle().toLowerCase();

    if (title.includes("focus")) {
      status = "busy";
      break;
    }

    status = "meeting";
  }

  return ContentService
    .createTextOutput(JSON.stringify({ status }))
    .setMimeType(ContentService.MimeType.JSON);
}

 

 

6단계: 결론

 

 

결론

 

 

이 프로젝트는 단순하면서도 세심하게 설계된 임베디드 시스템을 통해 사용자의 업무 가능 여부를 책상 안팎 어디에서든 즉시 확인할 수 있도록 함으로써 직장 내 소통을 개선하는 방법을 보여줍니다. 제스처 제어 데스크 큐브, 동기화된 출입 표시기, ESP-NOW 무선 통신, 그리고 선택적으로 구글 캘린더와 통합되는 이 시스템은 불필요한 업무 방해를 줄이는 동시에 사용자가 수동 및 자동 상태 업데이트 모드를 원활하게 전환할 수 있도록 유연성을 제공합니다.

 

무선 배터리 구동 방식 덕분에 추가 인프라 없이 어떤 사무실에도 쉽게 설치할 수 있으며, 모듈식 아키텍처로 Microsoft Teams 또는 Outlook 통합, 모바일 앱 연결, 다양한 표시등 지원 등 향후 기능 확장이 가능합니다. 궁극적으로 이 프로젝트는 동료들이 노크하기 전에 미리 알 수 있는 더욱 생산적인 작업 공간을 만들기 위한 실용적이고 직관적이며 확장 가능한 솔루션을 제공합니다 . 

 

자석이 필요하겠군.

 

이 기사의 원 출처는 다음 링크를 따라가세요. 좋은 아이디어와 영감을 제공하신 원 저자에게 감사합니다. 저는 늘 감사합니다.

 

당신도 그런가요? 

반응형

캐어랩 고객 지원

취업, 창업의 막막함, 외주 관리, 제품 부재!

당신의 고민은 무엇입니까? 현실과 동떨어진 교육, 실패만 반복하는 외주 계약, 아이디어는 있지만 구현할 기술이 없는 막막함.

우리는 알고 있습니다. 문제의 원인은 '명확한 학습, 실전 경험과 신뢰할 수 있는 기술력의 부재'에서 시작됩니다.

이제 고민을 멈추고, 캐어랩을 만나세요!

코딩(펌웨어), 전자부품과 디지털 회로설계, PCB 설계 제작, 고객(시장/수출) 발굴과 마케팅 전략으로 당신을 지원합니다.

제품 설계의 고수는 성공이 만든 게 아니라 실패가 만듭니다. 아이디어를 양산 가능한 제품으로!

귀사의 제품을 만드세요. 교육과 개발 실적으로 신뢰할 수 있는 파트너를 확보하세요.

지난 30년 여정, 캐어랩이 얻은 모든 것을 함께 나누고 싶습니다.

카카오 채널 추가하기

카톡 채팅방에서 무엇이든 물어보세요

당신의 성공을 위해 캐어랩과 함께 하세요.

캐어랩 온라인 채널 바로가기

캐어랩