YOGYUI

광교아이파크::거실 조명 Apple 홈킷 연동 (4) - Final 본문

홈네트워크(IoT)/광교아이파크

광교아이파크::거실 조명 Apple 홈킷 연동 (4) - Final

요겨 2021. 3. 11. 19:59
반응형

4. 월패드 장착

월패드를 다시 뜯은 뒤에 제작한 보드를 연결

제작 보드 월패드 연결

USB 케이블로 펌웨어를 이리저리 변경하면서 원하는대로 동작하는지 검증

동작 검증

 

※ 실험하면서 2가지 큰 문제를 발견했다

[1] 터치패드 입력 라인 관련된 커넥터의 입력단과 출력단이 뒤바뀌었다

터치패드 TS04 출력신호

터치패드의 TS04 출력단은 J2의 6번 핀으로 나와서, J1의 5번 핀으로 들어가서 월패드로 신호가 인가되어야 한다

이 신호를 내가 만든 보드에서는 DAC 출력 신호를 인가할 지, 원래 터치패드의 신호를 인가할 지를 MUX를 통해 선택하고자 설계하려고 했다

그런데 회로 설계할 때 술기운이 남아서 그랬는지 MUX 스위치의 입력과 출력단을 반대로 설계해버렸다

설계 실수... ㅠ

TMUX1237의 Source 1번 (3번 핀)에는 LCMD_IN이 아니라 LCMD_OUT이 와야 하고, Drain (4번 핀)에는 LCMD_IN이 와야 한다 (스위치의 입/출력단)

원래 설계 의도

>> 해결책

다행히도 MUX 핀들에 저항 패키지들(R23, R24)을 배치해뒀기 때문에 (다행...) 다음과 같이 저항을 떼내고 와이어 2개를 치렁치렁 납땜해주면 된다

회로 수정 방식
와이어 납땜으로 회로 수정 완료

회로 수정 이후, MUX 출력을 DAC로 설정한 뒤 전압 출력값을 이리저리 바꿔가면서 다음 사실을 발견

  • +4V 수준이면 1번 조명이 On/Off 된다

  • +3V 수준이면 2번 조명이 On/Off 된다

앞서 회로 분석 시에는 2.8V랑 2.2V로 계산했었는데, 아무래도 저항값을 측정할 때 부정확한 값이 읽힌 게 아닌가 의심해본다 (조금만 복잡한 네트워크로 구성된 회로도 납땜된 상태에서 멀티미터만으로는 저항값을 올바르게 읽을 수가 없다...)

 

[2] 월패드로부터 인가되는 5V의 전력이 부족하다

디버깅을 위해 연결했던 USB 케이블을 제거하고 회로의 전원을 월패드에서 나오는 +5V로 전환하니 바로 ESP-12F의 전원이 꺼졌다 켜졌다를 반복하기 시작했다

무슨 일인가 싶어 멀티미터로 5V 전원단을 측정해보니 2.3V 수준으로 떨어져버린 것을 알 수 있었다

ESP-12F가 와이파이에 연결되면서부터 전류를 많이 끌어다쓰는 바람에 벌어진 문제가 아닌가 추측

(전류계가 따로 없어서 내가 만든 보드의 소모 전력이 얼마인지 알 수 있는 방법이 없다 ㅠ)

 

이럴 줄 알았으면 그냥 +11V 전원단을 따와서 +5V 레귤레이터를 하나 추가해줄 걸 그랬다...

(전력이 부족할 줄은 몰랐지... 11V는 터치패드 LED를 켜야하니 당연히 전력은 충분하지 않을까?)

 

>> 해결책

월패드 우측 하단에 사진첩 기능을 위한 메모리스틱을 꽂을 수 있도록 USB-A Type 커넥터가 하나 있는데, 다행히도 +5V가 월패드로부터 인가되고 있다

월패드 USB-A Type 커넥터

어차피 따로 쓸일도 없으니 이놈을 회로 전원으로 사용하기로 결정

볼트를 풀어주고 다음과 같이 사용하니, 안정적으로 ESP-12F가 동작했다 (다행...)

전원 인가

 

이제 회로상의 문제는 해결이 되었다...

딱히 상품화할 일도 없으니 회로를 수정하지는 않기로 한다 ㅋㅋ

 

펌웨어 업로드

WiFi + MQTT로 조명 제어 및 상태값 전송을 위한 아두이노 코드를 다음과 같이 작성

라이브러리는 #include 구문에서 알 수 있듯이 Adafruit_MCP4725, PubSubClient, ArduinoJson 3개를 사용했다

(ESP8266WiFi는 ESP8266 구동을 위해서는 필수, 보드 매니저에서 알아서 설치해준다)

MQTT Topic 문자열은 기존에 구현했던 Homebridge 악세서리들과 유사한 포맷으로 정했다

#include <ESP8266WiFi.h>
#include <Adafruit_MCP4725.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>

char publish_msg[256];
StaticJsonDocument<256> json_doc;

const char* WIFI_SSID = "Your WiFi SSID";
const char* WIFI_PW = "Your WiFi Password";
WiFiClient wifi_client;

const char* MQTT_BROKER_ADDR = "MQTT Broker Address";
const int   MQTT_BROKER_PORT = 30003;
const char* MQTT_ID = "MQTT Broker Auth ID";
const char* MQTT_PW = "MQTT Broker Auth Password";
PubSubClient mqtt_client(wifi_client);

const int DAC_RESOLUTION = 12;
const double DAC_VREG = 5.0;
Adafruit_MCP4725 dac;

#define LED_BUILTIN 2
const int MUX_SEL_PIN = 13;
const int LIGHT1_STATE_PIN = 14;
const int LIGHT2_STATE_PIN = 12;
int last_state_light1 = -1;
int last_state_light2 = -1;

const int MONITOR_INTERVAL_MS = 250;
long last_monitor_time = 0;

enum MUXOUT {
  WALLPAD = 0,
  DACOUT = 1
};

uint16_t convert_dac_value(double voltage) {
  return uint16_t( (pow(2, DAC_RESOLUTION) - 1) / DAC_VREG * voltage);
}

void setDacOutVoltage(double voltage) {
  uint16_t conv_val = convert_dac_value(voltage);
  Serial.printf("Set DAC Output Voltage: %f V\n", voltage);
  dac.setVoltage(conv_val, false);
}

void setMuxOut(MUXOUT value) {
  if (value == WALLPAD) {
    digitalWrite(MUX_SEL_PIN, LOW);
    Serial.println("MUX OUT >> WALLPAD");
  } else if (value == DACOUT) {
    digitalWrite(MUX_SEL_PIN, HIGH);
    Serial.println("MUX OUT >> DAC OUT");
  }
}

void changeLightState(int index) {
  if (index == 1) {
    setDacOutVoltage(4.0);
  } else if (index == 2) {
    setDacOutVoltage(3.0);
  }
  setMuxOut(DACOUT);
  delay(100);
  setMuxOut(WALLPAD);
  setDacOutVoltage(5.0);
}

void mqtt_callback(char* topic, byte* payload, unsigned int length) {
  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("] ");
  for (int i = 0; i < length; i++) {
    Serial.print((char)payload[i]);
  }
  Serial.println();

  if (!strcmp(topic, "home/ipark/livingroom/light/command/0")) {
    changeLightState(1);
  } else if(!strcmp(topic, "home/ipark/livingroom/light/command/1")) {
    changeLightState(2);
  }
}

void setup() {
  Serial.begin(115200);
  WiFi.begin(WIFI_SSID, WIFI_PW);
  Serial.print("\nWiFi Connecting");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println();

  mqtt_client.setServer(MQTT_BROKER_ADDR, MQTT_BROKER_PORT);
  mqtt_client.setCallback(mqtt_callback);

  Serial.print("Connected, IP address: ");
  Serial.println(WiFi.localIP());
  Serial.printf("MAC address = %s\n", WiFi.softAPmacAddress().c_str());

  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(MUX_SEL_PIN, OUTPUT);
  pinMode(LIGHT1_STATE_PIN, INPUT);
  pinMode(LIGHT2_STATE_PIN, INPUT);

  digitalWrite(LED_BUILTIN, LOW);
  dac.begin(0x60);
  setDacOutVoltage(5.0);
  setMuxOut(WALLPAD);

  readLightStateAll();
  last_monitor_time = millis();
}

void establish_mqtt_connection() {
  if (mqtt_client.connected())
    return;
  while (!mqtt_client.connected()) {
    Serial.println("Try to connect MQTT Broker");
    if (mqtt_client.connect("ESP8266_WALLPAD_LIVINGROOM", MQTT_ID, MQTT_PW)) {
      Serial.println("Connected");
      mqtt_client.subscribe("home/ipark/livingroom/light/command/0");
      mqtt_client.subscribe("home/ipark/livingroom/light/command/1");
    } else {
      Serial.print("failed, rc=");
      Serial.print(mqtt_client.state());
      delay(2000);
    }
  }
}

void readLightState(int index) {
  int state = -1;
  if (index == 1) {
    state = digitalRead(LIGHT1_STATE_PIN);
    if (state != last_state_light1) {
      last_state_light1 = state;
      publishLightState(1);
    }
  } else if (index == 2) {
    state = digitalRead(LIGHT2_STATE_PIN);
    if (state != last_state_light2) {
      last_state_light2 = state;
      publishLightState(2);
    }
  }
}

void readLightStateAll() {
  readLightState(1);
  readLightState(2);
}

void publishLightState(int index) {
  size_t n = 0;
  if (index == 1) {
    json_doc["state"] = last_state_light1;
    n = serializeJson(json_doc, publish_msg);
    mqtt_client.publish(
      "home/ipark/livingroom/light/state/0",
      publish_msg,
      n);
    Serial.print("Published (home/ipark/livingroom/light/state/0): ");
    Serial.println(publish_msg);
  } else if (index == 2) {
    json_doc["state"] = last_state_light2;
    n = serializeJson(json_doc, publish_msg);
    mqtt_client.publish(
      "home/ipark/livingroom/light/state/1",
      publish_msg,
      n);
    Serial.print("Published (home/ipark/livingroom/light/state/1): ");
    Serial.println(publish_msg);
  }
}

void loop() {
  establish_mqtt_connection();
  mqtt_client.loop();

  long current = millis();
  if (current - last_monitor_time >= MONITOR_INTERVAL_MS) {
    last_monitor_time = current;
    readLightStateAll();
  }
}

파일을 분리하지 않고 하나로만 하려다보니 코드가 좀 길긴 하다 ㅠ

 

Homebridge 악세서리 설정

{
    "accessory": "mqttthing",
    "type": "switch",
    "name": "Living room Light1 (MQTT)",
    "url": "mqtt:://mosquitto address",
    "username": "mosquitto ID",
    "password": "mosquitto password",
    "topics": {
        "getOn": {
            "topic": "home/ipark/livingroom/light/state/0",
            "apply": "return JSON.parse(message).state;"
        },
        "setOn": {
            "topic": "home/ipark/livingroom/light/command/0",
            "apply": "return JSON.stringify({state: message});"
        }
    },
    "integerValue": true,
    "onValue": 1,
    "offValue": 0,
    "history": true,
    "logMqtt": true
},
{
    "accessory": "mqttthing",
    "type": "switch",
    "name": "Living room Light2 (MQTT)",
    "url": "mqtt:://mosquitto address",
    "username": "mosquitto ID",
    "password": "mosquitto password",
    "topics": {
        "getOn": {
            "topic": "home/ipark/livingroom/light/state/1",
            "apply": "return JSON.parse(message).state;"
        },
        "setOn": {
            "topic": "home/ipark/livingroom/light/command/1",
            "apply": "return JSON.stringify({state: message});"
        }
    },
    "integerValue": true,
    "onValue": 1,
    "offValue": 0,
    "history": true,
    "logMqtt": true
}

 

 

동작확인

월패드로 상태 변경 시 악세서리 상태도 빠르게 반영되고, Apple HomeKit으로 제어 시에도 빠른 응답속도를 보인다

완전 만족스럽게 잘된다!!!

(조명 2개는 상호 배타적이다::1번은 형광등 3개 ON, 2번은 형광등 5개 ON)

끝~!

[시리즈 링크]

광교아이파크::거실 조명 Apple 홈킷 연동 (1)

광교아이파크::거실 조명 Apple 홈킷 연동 (2)

광교아이파크::거실 조명 Apple 홈킷 연동 (3)

광교아이파크::거실 조명 Apple 홈킷 연동 (4)

반응형