Quellcode durchsuchen

figure out more of the protocol; remove hacky reinterpret_cast; add support for pairing/unpairing

Chris Mullins vor 8 Jahren
Ursprung
Commit
89a0898455
3 geänderte Dateien mit 127 neuen und 28 gelöschten Zeilen
  1. 56 9
      lib/MiLight/MiLightClient.cpp
  2. 39 19
      lib/MiLight/MiLightClient.h
  3. 32 0
      src/main.cpp

+ 56 - 9
lib/MiLight/MiLightClient.cpp

@@ -1,5 +1,34 @@
 #include <MiLightClient.h>
 
+void MiLightClient::deserializePacket(const uint8_t rawPacket[], MiLightPacket& packet) {
+  uint8_t ptr = 0;
+  
+  packet.deviceType = rawPacket[ptr++];
+  packet.deviceId = (rawPacket[ptr++] << 8) | rawPacket[ptr++];
+  packet.color = rawPacket[ptr++];
+  
+  packet.brightness = rawPacket[ptr] >> 3;
+  packet.groupId = rawPacket[ptr++] & 0x07;
+  
+  packet.button = rawPacket[ptr++];
+  packet.sequenceNum = rawPacket[ptr++];
+}
+
+void MiLightClient::serializePacket(uint8_t rawPacket[], const MiLightPacket& packet) {
+  uint8_t ptr = 0;
+  
+  rawPacket[ptr++] = packet.deviceType;
+  
+  // big endian
+  rawPacket[ptr++] = packet.deviceId >> 8;
+  rawPacket[ptr++] = packet.deviceId & 0xFF;
+  
+  rawPacket[ptr++] = packet.color;
+  rawPacket[ptr++] = (packet.brightness << 3) | (packet.groupId & 0x07);
+  rawPacket[ptr++] = packet.button;
+  rawPacket[ptr++] = packet.sequenceNum;
+}
+
 uint8_t MiLightClient::nextSequenceNum() {
   return sequenceNum++;
 }
@@ -9,18 +38,20 @@ bool MiLightClient::available() {
 }
 
 void MiLightClient::read(MiLightPacket& packet) {
-  uint8_t *packetBytes = reinterpret_cast<uint8_t*>(&packet);
-  size_t length = sizeof(packet);
+  uint8_t packetBytes[MILIGHT_PACKET_LENGTH];
+  size_t length;
   radio.read(packetBytes, length);
+  deserializePacket(packetBytes, packet);
 }
 
 void MiLightClient::write(MiLightPacket& packet, const unsigned int resendCount) {
-  uint8_t *packetBytes = reinterpret_cast<uint8_t*>(&packet);
+  uint8_t packetBytes[MILIGHT_PACKET_LENGTH];
+  serializePacket(packetBytes, packet);
   
   Serial.print("Packet bytes (");
-  Serial.print(sizeof(packet));
+  Serial.print(MILIGHT_PACKET_LENGTH);
   Serial.print(" bytes): ");
-  for (int i = 0; i < sizeof(packet); i++) {
+  for (int i = 0; i < MILIGHT_PACKET_LENGTH; i++) {
     Serial.print(packetBytes[i], HEX);
     Serial.print(" ");
   }
@@ -28,7 +59,7 @@ void MiLightClient::write(MiLightPacket& packet, const unsigned int resendCount)
   
   for (int i = 0; i < resendCount; i++) {
     Serial.print(".");
-    radio.write(packetBytes, sizeof(packet));
+    radio.write(packetBytes, MILIGHT_PACKET_LENGTH);
   }
   Serial.println();
 }
@@ -59,7 +90,8 @@ void MiLightClient::write(
   packet.deviceType = MiLightDeviceType::RGBW;
   packet.deviceId = deviceId;
   packet.color = adjustedColor;
-  packet.brightnessGroupId = (packetBrightnessValue << 3) | groupId;
+  packet.brightness = packetBrightnessValue;
+  packet.groupId = groupId;
   packet.button = button;
   packet.sequenceNum = nextSequenceNum();
   
@@ -76,11 +108,26 @@ void MiLightClient::updateBrightness(const uint16_t deviceId, const uint8_t grou
 
 void MiLightClient::updateStatus(const uint16_t deviceId, const uint8_t groupId, MiLightStatus status) {
   uint8_t button = MiLightButton::GROUP_1_ON + ((groupId - 1)*2) + status;
-  write(deviceId, 0, 0, 0, static_cast<MiLightButton>(button));
+  write(deviceId, 0, 0, groupId, static_cast<MiLightButton>(button));
 }
 
 void MiLightClient::updateColorWhite(const uint16_t deviceId, const uint8_t groupId) {
-  write(deviceId, 0, 0, groupId, COLOR_WHITE);
+  uint8_t button = MiLightButton::GROUP_1_MAX_LEVEL + ((groupId - 1)*2);
+  pressButton(deviceId, groupId, static_cast<MiLightButton>(button));
+}
+
+void MiLightClient::pair(const uint16_t deviceId, const uint8_t groupId) {
+  updateStatus(deviceId, groupId, ON);
+}
+
+void MiLightClient::unpair(const uint16_t deviceId, const uint8_t groupId) {
+  updateStatus(deviceId, groupId, ON);
+  delay(1);
+  updateColorWhite(deviceId, groupId);
+}
+    
+void MiLightClient::pressButton(const uint16_t deviceId, const uint8_t groupId, MiLightButton button) {
+  write(deviceId, 0, 0, groupId, button);
 }
 
 void MiLightClient::allOn(const uint16_t deviceId) {

+ 39 - 19
lib/MiLight/MiLightClient.h

@@ -4,42 +4,55 @@
 #ifndef _MILIGHTCLIENT_H
 #define _MILIGHTCLIENT_H
 
+#define MILIGHT_PACKET_LENGTH 7
+
 enum MiLightDeviceType {
   WHITE = 0xB0,
   RGBW = 0xB8
 };
 
 enum MiLightButton {
-  ALL_ON      = 0x01,
-  ALL_OFF     = 0x02,
-  GROUP_1_ON  = 0x03,
-  GROUP_1_OFF = 0x04,
-  GROUP_2_ON  = 0x05,
-  GROUP_2_OFF = 0x06,
-  GROUP_3_ON  = 0x07,
-  GROUP_3_OFF = 0x08,
-  GROUP_4_ON  = 0x09,
-  GROUP_4_OFF = 0x0A,
-  SPEED_UP    = 0x0B, 
-  SPEED_DOWN  = 0x0C, 
-  DISCO_MODE  = 0x0D,
-  BRIGHTNESS  = 0x0E,
-  COLOR       = 0x0F,
-  COLOR_WHITE = 0x11
+  ALL_ON            = 0x01,
+  ALL_OFF           = 0x02,
+  GROUP_1_ON        = 0x03,
+  GROUP_1_OFF       = 0x04,
+  GROUP_2_ON        = 0x05,
+  GROUP_2_OFF       = 0x06,
+  GROUP_3_ON        = 0x07,
+  GROUP_3_OFF       = 0x08,
+  GROUP_4_ON        = 0x09,
+  GROUP_4_OFF       = 0x0A,
+  SPEED_UP          = 0x0B, 
+  SPEED_DOWN        = 0x0C, 
+  DISCO_MODE        = 0x0D,
+  BRIGHTNESS        = 0x0E,
+  COLOR             = 0x0F,
+  ALL_MAX_LEVEL     = 0x11,
+  ALL_MIN_LEVEL     = 0x12,
+  
+  // These are the only mechanism (that I know of) to disable RGB and set the
+  // color to white.
+  GROUP_1_MAX_LEVEL = 0x13,
+  GROUP_1_MIN_LEVEL = 0x14,
+  GROUP_2_MAX_LEVEL = 0x15,
+  GROUP_2_MIN_LEVEL = 0x16,
+  GROUP_3_MAX_LEVEL = 0x17,
+  GROUP_3_MIN_LEVEL = 0x18,
+  GROUP_4_MAX_LEVEL = 0x19,
+  GROUP_4_MIN_LEVEL = 0x1A,
 };
 
 enum MiLightStatus { ON = 0, OFF = 1 };
   
-#pragma pack(push, 1)
 struct MiLightPacket {
   uint8_t deviceType;
   uint16_t deviceId;
   uint8_t color;
-  uint8_t brightnessGroupId;
+  uint8_t brightness;
+  uint8_t groupId;
   uint8_t button;
   uint8_t sequenceNum;
 };
-#pragma pack(pop)
 
 class MiLightClient {
   public:
@@ -63,10 +76,17 @@ class MiLightClient {
     void updateBrightness(const uint16_t deviceId, const uint8_t groupId, const uint8_t brightness);
     void updateStatus(const uint16_t deviceId, const uint8_t groupId, MiLightStatus status);
     void updateColorWhite(const uint16_t deviceId, const uint8_t groupId);
+    void pair(const uint16_t deviceId, const uint8_t groupId);
+    void unpair(const uint16_t deviceId, const uint8_t groupId);
     
     void allOn(const uint16_t deviceId);
     void allOff(const uint16_t deviceId);
     
+    void pressButton(const uint16_t deviceId, const uint8_t groupId, MiLightButton button);
+    
+    static void deserializePacket(const uint8_t rawPacket[], MiLightPacket& packet);
+    static void serializePacket(uint8_t rawPacket[], const MiLightPacket& packet);
+    
   private:
     MiLightRadio& radio;
     uint8_t sequenceNum;

+ 32 - 0
src/main.cpp

@@ -84,17 +84,49 @@ void handleUpdateGateway() {
     if (request["command"] == "all_off") {
       milightClient.allOff(gatewayId);
     }
+    
+    if (request["command"] == "unpair") {
+      milightClient.unpair(gatewayId, groupId);
+    }
+    
+    if (request["command"] == "pair") {
+      milightClient.pair(gatewayId, groupId);
+    }
   }
   
   server.send(200, "application/json", "true");
 }
 
+void handleListenGateway() {
+  while (!mlr.available()) {
+    yield();
+  }
+  
+  MiLightPacket packet;
+  milightClient.read(packet);
+  
+  String response = "Packet received (";
+  response += String(sizeof(packet)) + " bytes)";
+  response += ":\n";
+  response += "Request type : " + String(packet.deviceType, HEX) + "\n";
+  response += "Device ID    : " + String(packet.deviceId, HEX) + "\n";
+  response += "Color        : " + String(packet.color, HEX) + "\n";
+  response += "Brightness   : " + String(packet.brightness, HEX) + "\n";
+  response += "Group ID     : " + String(packet.groupId, HEX) + "\n";
+  response += "Button       : " + String(packet.button, HEX) + "\n";
+  response += "Sequence Num : " + String(packet.sequenceNum, HEX) + "\n";
+  response += "\n\n";
+  
+  server.send(200, "text/plain", response);
+}
+
 void setup() {
   Serial.begin(9600);
   wifiManager.autoConnect();
   mlr.begin();
   
   server.on("/gateway", HTTP_PUT, handleUpdateGateway);
+  server.on("/gateway", HTTP_GET, handleListenGateway);
   server.on("/update", HTTP_POST, 
     [](){
       server.sendHeader("Connection", "close");