Created
March 11, 2016 14:19
-
-
Save kasperkamperman/89f67f049a2ec45a5c9c to your computer and use it in GitHub Desktop.
Example implementation to receive commands over TCP on your Photon running FastLED animations.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Example implementation to receive commands over TCP on your Photon running FastLED animations. | |
FastLED is not implemented you have to do that yourself. | |
It makes use of this Serial cmd protocol concept: | |
https://gist.github.com/kasperkamperman/7e827bb481dfcdb4cb52 | |
See this Processing sketch to send TCP Data. Modify the IPAddress to the local address of the Photon. | |
https://gist.github.com/kasperkamperman/1f185758519fa3131ea0 | |
This setup will make it possible to run animations that won't break on cloud or WiFi activity. | |
We use a Timer interrupt to make sure our leds update with a fixed frequency. | |
The Particle won't break this block by background events. | |
Code based on my questions and the advices I got in this topic: | |
https://community.particle.io/t/control-system-thread-duration-or-keeping-a-really-fixed-time-loop/20757 | |
Uses the SparkIntervalTimer libary by Paul Kourany and Daniel Gilbert | |
https://github.com/pkourany/SparkIntervalTimer | |
*/ | |
// you need to add this when you compile offline. | |
#include "application.h" | |
#include "SparkIntervalTimer/SparkIntervalTimer.h" | |
//https://docs.particle.io/reference/firmware/photon/#system-events | |
SYSTEM_THREAD(ENABLED); | |
//https://docs.particle.io/support/troubleshooting/mode-switching/photon/#semi-automatic-mode | |
SYSTEM_MODE(SEMI_AUTOMATIC); | |
// pre-define | |
void fixedLoop(); | |
void parseTCPCommands(); | |
void setTime(); | |
void printTime(); | |
void sendReply(); | |
volatile int counter = 0; | |
IntervalTimer loopTimer; | |
TCPServer server = TCPServer(12345); | |
TCPClient client; | |
boolean clientAlreadyConnected = false; | |
struct Cmd { | |
uint8_t type; | |
uint8_t typeId; | |
uint8_t cmdId; | |
uint8_t paddingByte; // unused | |
uint16_t cmdValue0; | |
uint16_t cmdValue1; | |
uint16_t cmdValue2; | |
uint16_t cmdValue3; | |
}; | |
Cmd aCmd; | |
boolean cmdIsWrittenFlag = false; | |
boolean cmdNeedsProcessingFlag = false; | |
// for time measuring purposes | |
unsigned long t1; | |
unsigned long t2; | |
unsigned long t3 = 0; | |
void setup() { | |
Serial.begin(57600); | |
// in case you want the whole cloud | |
//Particle.connect(); | |
//waitUntil(Particle.connected); | |
// in case you only want WiFi, we don't use the cloud | |
// in this example | |
WiFi.connect(); | |
waitUntil(WiFi.ready); | |
// IP address only available after particle process has run | |
Particle.process(); | |
Serial.println(WiFi.localIP()); | |
delay(500); | |
server.begin(); | |
// 400 fps loop | |
loopTimer.begin(fixedLoop, 2500, uSec); | |
} | |
void loop() { | |
// this is the normal loop | |
// here we do things that are not time critical. | |
// take in account that the Particle Photon runs it's own | |
// hidden loop (Particle.process()) to do things like the cloud | |
// connection. | |
parseTCPCommands(); | |
} | |
void fixedLoop() { | |
/* | |
This is kind of dummy code. Just to show we code that almost | |
uses all of the 2500 avaible microseconds. | |
And code that uses 800 microseconds (for example the time to write out some leds). | |
*/ | |
// https://docs.particle.io/reference/firmware/photon/#single_threaded_block- | |
SINGLE_THREADED_BLOCK() { | |
if(counter<5) { | |
delayMicroseconds(2450); | |
} | |
else { | |
delayMicroseconds(1000); | |
// here we do something with the received commands. | |
// in this case we just print out the received value. | |
// take in account that printing also takes time.. | |
if(!cmdIsWrittenFlag && cmdNeedsProcessingFlag) { | |
Serial.println(aCmd.cmdValue0); | |
cmdNeedsProcessingFlag = false; | |
} | |
} | |
counter++; | |
if(counter>15) counter=0; | |
} | |
} | |
void parseTCPCommands() { | |
// routine partly based on | |
// http://www.forward.com.au/pfod/HomeAutomation/InlineWiFiPowerSwitch/InlinePowerSwitch.ino.txt | |
// see if a client is available | |
if (!client) { | |
// evaluates to false if no connection | |
client = server.available(); | |
} | |
else | |
{ // have client | |
// check if that client isn't connected anymore | |
if (!client.connected()) { | |
// only disconnect if it was previously connected | |
if (clientAlreadyConnected) { | |
client.stop(); | |
clientAlreadyConnected = false; | |
// check for new connection | |
client = server.available(); | |
} | |
} | |
else { | |
//we are connected | |
if (!clientAlreadyConnected) { | |
clientAlreadyConnected = true; | |
//clear out the input buffer | |
client.flush(); | |
// welcome if we were not already connected | |
// print the address to Serial | |
IPAddress clientIP = client.remoteIP(); | |
Serial.print("Hi :"); | |
Serial.println(clientIP); | |
} | |
// we use if() instead of while(). We only want to get one command from our buffer | |
// so we leave other commands in the client buffer. | |
// we only continue reading the buffer when the cmdNeedsProcessingFlag == false. | |
// we then know that we processed (that happens in our fixedLoop) the previous | |
// message. | |
if(client.available() > 0 && !cmdNeedsProcessingFlag) { | |
cmdIsWrittenFlag = true; | |
client.readBytes((char*) &aCmd,sizeof(Cmd)); | |
cmdIsWrittenFlag = false; | |
cmdNeedsProcessingFlag = true; | |
} | |
} | |
} | |
} | |
// in case you want to debug time | |
void setTime() { | |
t1 = micros(); | |
} | |
void printTime() { | |
t2 = micros()-t1; | |
if(t2>t3) t3 = t2; | |
Serial.print(F("time: ")); | |
Serial.print(t3); | |
Serial.print(" "); | |
Serial.println(t2); | |
} | |
// not used right now | |
// probably takes longer then the available time | |
void sendReply() { | |
// test if we received the values | |
Serial.print(aCmd.type); | |
Serial.print(","); | |
Serial.print(aCmd.typeId); | |
Serial.print(","); | |
Serial.print(aCmd.cmdId); | |
Serial.print(","); | |
Serial.print(aCmd.cmdValue0); | |
Serial.print(","); | |
Serial.print(aCmd.cmdValue1); | |
Serial.print(","); | |
Serial.print(aCmd.cmdValue2); | |
Serial.print(","); | |
Serial.print(aCmd.cmdValue3); | |
Serial.println(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment