LIFX Developer Zone

Lights Discovery, all lights send the same port

I’m trying to build a project using the LAN API,
I’ve followed the guide on here: https://lan.developer.lifx.com/docs/workflow-diagrams#discovery trying to discover all the lights on my network.

problem is, the response i’m receiving has all the lights with the same port.

I have 3 lights in my house,
I send a UDP message to 255.255.255.255:56700 as documented,
tagged=1 in the header,
i receive in return 6 message:

UDP State service received:
  service: 1
  port: 56700
UDP State service received:
  service: 1
  port: 56700
UDP State service received:
  service: 5
  port: 56700
UDP State service received:
  service: 5
  port: 56700
UDP State service received:
  service: 1
  port: 56700
UDP State service received:
  service: 5
  port: 56700

service 1 makes sense as the documentation states, I assume service 5 is for future use, but the port is the same on all messages.
how can i address specific bulbs?

Here’s my code:

#include <ESP8266WiFi.h>
#include <SPI.h>        
#include <WiFiUdp.h>

unsigned int localPort = 8888; // Port we listen to
unsigned int lifxPort  = 56700; // Port for talking to LIFX devices

// Remote IP (In this case we broadcast to the entire subnet)
IPAddress broadcast_ip(255, 255, 255, 255);

// LIFX Header structure
#pragma pack(push, 1)
typedef struct {
  /* frame */
  uint16_t size;
  uint16_t protocol:12;
  uint8_t  addressable:1;
  uint8_t  tagged:1;
  uint8_t  origin:2;
  uint32_t source;
  /* frame address */
  uint8_t  target[8];
  uint8_t  reserved[6];
  uint8_t  res_required:1;
  uint8_t  ack_required:1;
  uint8_t  :6;
  uint8_t  sequence;
  /* protocol header */
  uint64_t :64;
  uint16_t type;
  uint16_t :16;
  /* variable length payload follows */
} lifx_header;
#pragma pack(pop)

// Device::StateService Payload
#pragma pack(push, 1)
typedef struct {
  uint8_t service;
  uint32_t port;
} lifx_payload_device_state_service;
#pragma pack(pop)

// Payload types
#define LIFX_DEVICE_GET_SERVICE 2
#define LIFX_DEVICE_STATE_SERVICE 3

#define LIFX_INCOMING_PACKET_BUFFER_LEN 300 // Packet buffer size
#define LIFX_SERIAL_LEN 6 // Length in bytes of serial numbers

// An EthernetUDP instance to let us send and receive packets over UDP
WiFiUDP Udp;

// Timing data
unsigned long timeoutInterval = 500;

void setup() {
  // ... Here i Connect to WiFi
  Udp.begin(localPort); // Listen for incoming UDP packets

  GetDevices();
}

void loop() {
  // ... Loop code
}

void GetDevices() {
  lifx_header header;
  
  // Initialise both structures
  memset(&header, 0, sizeof(header));
  
  // Setup the header
  header.size = sizeof(lifx_header); // Size of header + payload
  header.tagged = 1;
  header.addressable = 1;
  header.protocol = 1024;
  header.source = 123;
  header.ack_required = 0;
  header.res_required = 0;
  header.sequence = 100;
  header.type = LIFX_DEVICE_GET_SERVICE;
  
  // Send a packet on startup
  Udp.beginPacket(broadcast_ip, 56700);
  Udp.write((char *) &header, sizeof(lifx_header));
  Udp.endPacket();
  
  unsigned long started = millis();
  while (millis() - started < timeoutInterval) {
    int packetLen = Udp.parsePacket();
    byte packetBuffer[LIFX_INCOMING_PACKET_BUFFER_LEN];
    if (packetLen && packetLen < LIFX_INCOMING_PACKET_BUFFER_LEN) {
      Udp.read(packetBuffer, sizeof(packetBuffer));
      
      if (((lifx_header *)packetBuffer)->type == LIFX_DEVICE_STATE_SERVICE) {
        Serial.println("UDP State service recieved:");
        
        uint8_t service = ((lifx_payload_device_state_service *)(packetBuffer + sizeof(lifx_header)))->service;
        uint32_t port = ((lifx_payload_device_state_service *)(packetBuffer + sizeof(lifx_header)))->port;
        Serial.println("  service: " + String(service));
        Serial.println("  port: " + String(port));

      } else {
        Serial.print("Unexpected Packet type: ");
        Serial.println(((lifx_header *)packetBuffer)->type);
      }
    }
  }
}

am i doing something wrong?

Looks like you are close, but you need to record the IP address of each bulb so you can know where to send the messages. Check the WiFiUdp library for a remoteIP() function to retrieve the IP address for the bulbs when parsing the responses. Hope that helps!

Oh wow, i would have never guessed that, i wish it was more clear in the docs.

So after i get the ip i should send a UDP message directly to this device instead of 255.255.255.255?

thank you!

You got it!

The IP address 255.255.255.255 is a broadcast address meaning the message gets sent to everyone on your network. Once you get the IP address of a LIFX device, you can start to send unicast messages which are delivered to that device only.

Best of luck on your project!

So in that case, what is the purpose of the target field in the header?
and do i need to send it when sending a message directly to a device?

I recommend reading through the header description. It has been a while since I implemented my own controller and had just been looking at my undocumented code lol to respond before lol. https://lan.developer.lifx.com/docs/header-description contains the information you are looking for.

In short though, the target field can be used to target a device based on its MAC address rather than an IP address. But this is dependent on the “flagged” field. I believe the implementation I made ignored the target and used IP unicast only. But my implementation is not very refined and the header description does recommended setting the target. The state-service responses should contain the MAC address of the device.