Bit of a long shot, but has anyone had any success sending a UDP packet of the LAN Protocol on an Arduino?
I’ve been trying to achieve LIFX lighting control on the Arduino platform but have kept coming up short porting example code to the platform. I can achieve LAN control using both C# and C++ on a Windows platform but Arduino uses a stripped down version of C++ so the code doesn’t port that well at all. Therefore I have resorted to trying to use Arduino’s native UDP library but that also has seemed to fail miserably.
So getting back to my main port, has anyone had any success performing basic LIFX functions on an arduino and, if so, could you possibly point me in the right direction?
Thank you for the help~!
In case anyone was interested, this was the native UDP attempt I have been working on (nothing seems to happen):
You were really close, but you code had a few issues:
You were using Udp.remoteIP(), which gives you the last ip address that sent you a packet, so you were sending packets to some random machine on the network.
You were sending the header in lots of seperate parts, which makes Arduino turn each into a single byte.
The size field needs to be set to the size of the headers, plus the size of the payload
You should read the whole packet at once, not just the headers, that way you can read the payload with ((lifx_payload_something*)packetBuffer + sizeof(lifx_header))->field_name.
Anyway, I rewrote the example you gave me, so that it sends a Device::SetPower to the whole network every 5 seconds and has the effect of turning on every bulb. It also listens to the replies and prints the reply type to the serial console as it receives them.
#include <SPI.h>
#include <Ethernet.h>
#include <EthernetUdp.h>
// The 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::SetColor Payload
#pragma pack(push, 1)
typedef struct {
uint16_t level;
} lifx_payload_device_set_power;
#pragma pack(pop)
// Payload types
#define LIFX_DEVICE_SETPOWER 21
// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network:
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
unsigned int localPort = 8888; // Port to listen for responses on
unsigned int lifxPort = 56700; // Port used to send to send to LIFX devices
// Remote IP (In this case we broadcast to the entire subnet)
IPAddress broadcast_ip(255, 255, 255, 255);
// Packet buffer size
#define LIFX_INCOMING_PACKET_BUFFER_LEN 300
// An EthernetUDP instance to let us send and receive packets over UDP
EthernetUDP Udp;
// Timing data
unsigned long sendInterval = 5000; // 5 seconds
unsigned long lastSend = 0;
void setup() {
Serial.begin(19200);
Ethernet.begin(mac); // Start Ethernet, using DHCP
Udp.begin(localPort); // Listen for incoming UDP packets
}
void loop() {
unsigned long currentTime = millis();
// If 5 seconds have passed, send another packet
if (currentTime - lastSend >= sendInterval) {
lastSend = currentTime;
lifx_header header;
lifx_payload_device_set_power payload;
// Initialise both structures
memset(&header, 0, sizeof(header));
memset(&payload, 0, sizeof(payload));
// Setup the header
header.size = sizeof(lifx_header) + sizeof(payload); // Size of header + payload
header.tagged = 1;
header.addressable = 1;
header.protocol = 1024;
header.source = 123;
header.ack_required = 1;
header.sequence = 100;
header.type = LIFX_DEVICE_SETPOWER;
// Setup payload
payload.level = 65535;
// Send a packet on startup
Udp.beginPacket(broadcast_ip, 56700);
Udp.write((char *) &header, sizeof(lifx_header)); // Treat the structures like byte arrays
Udp.write((char *) &payload, sizeof(payload)); // Which makes the data on wire correct
Udp.endPacket();
}
// Check for incoming packets
int packetLen = Udp.parsePacket();
byte packetBuffer[LIFX_INCOMING_PACKET_BUFFER_LEN];
if (packetLen && packetLen < LIFX_INCOMING_PACKET_BUFFER_LEN) {
Udp.read(packetBuffer, sizeof(packetBuffer));
Serial.print("Received Packet type: ");
Serial.println(((lifx_header *)packetBuffer)->type);
}
}
Thanks Daniel! That has been a huge help and I have been able to get messages to my lights now based on the example you gave.
Based on your provided code, I am trying to create a function to return a light’s power level. I seem to be having some interesting issues with this as I cannot seem to get the packet to stay in a variable. What I mean by that is I set the variable from the received packet but the value stays at a random value no matter what the lights actually respond. However when I print the variable from within the function, it returns just fine.
It’s sort of hard to explain what’s happening because I get very different results when I try and change my code to further debug the issue. Any insight would be greatly appreciated. >.<
uint16_t GetPower(uint8_t *dest)
{
uint16_t power = 0;
while (transmit == false)
{
transmit = true;
lifx_header header;
// Initialise structures
memset(&header, 0, sizeof(header));
int tag = 0;
if (dest[0] == 0x00 && dest[5] == 0x00)
{
tag = 1;
}
// Setup the header
header.size = sizeof(lifx_header); // Size of header + payload
header.tagged = tag;
header.addressable = 1;
header.protocol = 1024;
header.source = 123;
header.target[0] = dest[0];
header.target[1] = dest[1];
header.target[2] = dest[2];
header.target[3] = dest[3];
header.target[4] = dest[4];
header.target[5] = dest[5];
header.target[6] = 0x00;
header.target[7] = 0x00;
header.res_required = 1;
header.ack_required = 0;
header.sequence = 100;
header.type = LIFX_DEVICE_GETPOWER; //Added correct structures to code
// Send a packet on startup
Udp.beginPacket(broadcast_ip, 56700);
Udp.write((char *) &header, sizeof(lifx_header)); // Treat the structures like byte arrays
Udp.endPacket();
// Check for response packet
int packetLen = Udp.parsePacket();
byte packetBuffer[LIFX_INCOMING_PACKET_BUFFER_LEN];
if (packetLen && packetLen < LIFX_INCOMING_PACKET_BUFFER_LEN) {
Udp.read(packetBuffer, sizeof(packetBuffer));
}
delay(1);
power = ((lifx_payload_device_power *)packetBuffer + sizeof(lifx_header))->level;
Serial.println(power); //returns 65535
}
transmit = false;
return power; //returns random (but consistent) int
}
Hi there,
I’m working on building an ESP8266 lifx switch. I saw this post and it was incredibly helpful in allowing me to connect with the lifx bulbs directly over my wifi. I have a question though. When you use the following:
and
How are you getting the MAC address for the bulb? I haven’t built a discovery service and was wondering if I can convert the ID of the bulb into such a Hex array? I have currently a 12 character string, how would I convert it to such an array? (I tried simply adding 0x to each pair of numbers in my serial number and didn’t have any luck - is it that simple?)
I can connect using a direct IP and setting the dest variable to {0,0,0,0,0,0} but I’d really like to use the ID, as I can enter it into a web page and capture it that way when setting up the switch. Also, with DHCP, my addresses may change.
EDIT- I’m unable to send from ESP8266 module using 255.255.255.255 as a broadcast ip. I can get the light to respond to the direct IP request, but when addressing it with broadcast - I can see the broadcast on the network, but no action from the bulb. Are there any examples of an arduino client calling for status, sending an action and receiving an update status back? Sort of round trip for a call to the light?
Since I posted the questions above and my edit - I’ve been able to connect to the bulb, but only using a broadcast IP of 192.168.1.255. I am sending a destination or mac address in my header - just constructed manually at this time from the bulb ID.
I am now successful at turning the bulb on if off and off if on. I can tell what the power setting is by reading the payload.level value - (0 or 65535).
Here’s how I got it to work: I have to request the power level with the function provided above, which works if I broadcast UDP a message with the mac address in the dest array. I capture the IP from the remote host and feed it back into the next step to send my payload of a 0 or 65535. It won’t work if I have a dest, I have to set it to all 0’s and use the direct IP to get it to work. Is that a change in the protocol? Something I may be doing wrong when sending a packet? It’s the only difference I could discern.
I can provide my code if necessary - just didn’t want to junk up the works here.
Me too! I’m using the Arduino bootloader on a linksprite esp8266 Adruino-compatible board and have added a full-color GUI with touchscreen. Currently I can just change the color using my own color wheel.
@daniel_hall Is there any Arduino library out there for the LAN protocol? I can adapt them to the Esprissif.
This thread has been incredibly useful. I’ve been trying to control my lights with this app and it works ok, but I have a group that has 3 bulbs and I do the following to send the same command:
Udp.beginPacket(broadcast_ip1, 56700);
Udp.write((char *) &header, sizeof(lifx_header)); // Treat the structures like byte arrays
Udp.write((char *) &payload, sizeof(payload)); // Which makes the data on wire correct
Udp.endPacket();
delay(200);
Udp.beginPacket(broadcast_ip2, 56700);
Udp.write((char *) &header, sizeof(lifx_header)); // Treat the structures like byte arrays
Udp.write((char *) &payload, sizeof(payload)); // Which makes the data on wire correct
Udp.endPacket();
delay(200);
Udp.beginPacket(broadcast_ip3, 56700);
Udp.write((char *) &header, sizeof(lifx_header)); // Treat the structures like byte arrays
Udp.write((char *) &payload, sizeof(payload)); // Which makes the data on wire correct
Udp.endPacket();
Some messages get right away and some other get there after a few seconds if they even get there (sorry! not checking for ack for now)
Is this the correct approach? Send the same UDP packet three different times? I was expecting to have the group functionality, having to send a command to a group but this doesn’t seem to be right. Any light (pun intended!) that can be share don this will be highly appreciated.
Looking at how other implementations handle groups, it seems looping through the devices is indeed the way to do it. Groups don’t appear to have a callable endpoint.
Not used that one myself, am using an lolin lite esp32 and still in test mode but micropython is really easy to install and use as it also has touch sensors which has libraries ready to go in micropython.
There is a lot of detail on the micropython site.
You can use a python library, e.g. LIFXLAN but you can’t install libraries you need to add them and reference them in your python script.
for example with LIFXLAN it uses netifaces to find the correct subnet to broadcast on, you need to extract that part from the script and add your own broadcast address. e.g. [u’255.255.255.255]
You will need to add your dependencies if micropython does not have its own version. e.g. bitstruct is used, if that isn’t in micropython then you add the python file to the script and call it.
As per the doco you add the library in a folder on the device and call it.
Half the fun is working it out as I am still doing.
Unfortunately it looks like there’s not quite enough room on a NodeMCU v3 for the bitstring library, so I don’t think I can run LifxLAN directly. I might get an ESP32-based board and try again.
However, I might be able to get a little web client running on it, which can then POST to my existing LifxLAN server…
My current plan once I’ve sorted out the socket issue I’m having is to have standard code on a bunch of boards, and move the switch logic to the server, so it receives “switch 1 gpio 5 pressed/held” and knows what to do with that.
I feel like I should look into MQTT, but don’t have the brain space for another system at the moment!
Why ? One raspberry pi W and use the esp32 or the on you have with a sensor to post a message to mqtt on the pi and then run lifxlan there. Your switches are cheap then and a cheap central computer has the ram etc you need to control your lights.
The esp32 I got for $8.00 US this week and there are cheaper ones. Esp32 has touch sensors so easy to use that.
Setting up mosquitto is easy. You can also use node red on the pi W and create a flow from that. I did that quickly this week
That’s in essence what I’m doing. I thought you were recommending a pi over an Arduino for the sensor processing, not for the server. I have a perfectly capable server that’s already running LifxLAN. I also have a Smartthings system providing most of the automations I need.
I was mainly saying I don’t see the advantage of setting up MQTT over just running a dedicated python httpserver linked to LifxLAN. Perhaps if I was starting from scratch I’d consider it, but I try to keep most of my stuff as off-the-shelf as possible and then just add custom code around the edge cases.
The big advantage I see is the ability to log the MQTT messages to determine what your LIFX bulbs (or any IoT target for that matter) was supposed to do vs what it actually did.