Real time communication

I want to make a real time application using node.js and meteor. I tried to use Publish to send data to my web app but its not reliable and it has many seconds delay. So cores send data to my app using tcpClient and receive commands from it through Spark Cloud API. This solution works but it’s still slow because there’s a conflict between tcpClient and cloud API. I have to stop tcpclient to receive cloud data.
I thought of not using cloud API at all and relying on tcpClient to receive commands from server too. However tcpClient loses its connection now and then and I have to restart it which takes a lot of time.Also tcpClient is not secure.
So at present what’s the most reliable method to create a realtime app (<100ms delay) using Spark?

Hi @Amir,

Make sure if you’re using publish and subscribe, that you’re not publishing on average more than once per second. Beyond that limit events may be filtered out, which is why it might seem unreliable. We also have a webhooks beta, where a published event can immediately cause a request to be sent to your app.

For the fastest possible response time though, a direct socket from the device to your app would have the fewest hops, and let you push the most data. There isn’t a conflict between TCPClient and the cloud stuff, it’s just possible to open too many sockets, or write code that can cause your Core to drop its connection. The hardware on the Photon will make this much more robust though. Do you mind sharing your code? Maybe we can help spot issues and get you back up and running.

Thanks,
David

Hi @Dave,
I simplified my code to pinpoint the problem. My Core can either receive input from a pin (i.e. a switch) or cloud API. Whenever the state of input pins changes, Core communicates to my app through tcpClient. If I send a few commands from cloud API one after the other, Core hangs for a while, starts to blinking yellow and cyan, or responds with a very long delay. I’m aiming for delay time about 100ms but its sometimes a few seconds. Please let me know how to improve the code.

indent preformatted text by 4 spaces

// ----- Class Timer -----

class TimerObj {
    public:
     void init(int duration)    {_duration = duration;}
     void turnOn()  { _doneTime = millis() + _duration; _off = false; }
     void turnOff() { _doneTime = 0; _off = true; }
     bool isDone()  { return (millis() >= _doneTime); }
     bool isOff()   {return _off;}
    private:
     int _duration=0; //ms
     unsigned long _doneTime = 0;
     bool _off = true;
};

// ----- Class Http -----

class HttpObj {
    public:
     void init(int state);
     void run();
    private:
    char * _request();
    String  _path;
    const char * _hostname ="zealinx.meteor.com";
    const char * _success = "{\"result\":\"pass\"}";
    static const int _maxBuffer = 1024;
    char _buffer[_maxBuffer];
    char _part[_maxBuffer];
    int _timerDelay = 100;
    TimerObj _timer;
    TCPClient _client;
 };

 void HttpObj::init(int state) {
    _timer.init(_timerDelay);
    _timer.turnOn();
    char path[254];
    sprintf(path,
       "/api?state={\"deviceID\":\"%s\",\"date\":%u,\"output\":%u}", 
        Spark.deviceID().c_str(), Time.now(), state);
    String strPath(path);
    _path = strPath;
    Serial.println("HttpObj::init  " + _path);
  };

 char * HttpObj::_request() {
    Serial.println("resolving host...");
    if (_client.connected()) {
        Serial.println("connected still!");
    }else {
        if (_client.connect(_hostname, 80)) {
        Serial.println("connected!");
     } else {
        Serial.println("\r\n\r\nConnection Failed!");
       _client.stop();
       return NULL;
     }
   }
   _client.print("GET " + _path + " HTTP/1.1\r\n" + "HOST: " + _hostname + "\r\n\r\n\r\n");


  Serial.println("\r\nReading Data......");
  _buffer[0] = '\0'; // empty buffer
  bool breakFlag = false;
  int i,k = 0;
  uint32_t lastRead = millis();
  while ((millis() - lastRead) < 1000) {
  while (_client.available()) {
    char c = _client.read();
    Serial.print(c);
    if(i++ > 100) {
      delay(100);
      i = 0;
   }
   if (c == -1) break;
  _buffer[k++] = c;
  if( (k == _maxBuffer-2) || (c=='}') ){
    // protocol: json response doesn't contain an array
    _buffer[k++] = '\0';
    breakFlag = true;
    break;
  }
  lastRead = millis();
 }
  if (breakFlag) break;
  }
  Serial.println("\r\nData Read......");
  _client.flush();
  _client.stop();
  return _buffer;
 };

void HttpObj::run() {
  if (!_timer.isOff()) {
   Serial.println("HttpObj");
   if (_timer.isDone()) {
       char * response = _request();
       Serial.println(strstr(response, _success));
       if ( strstr(_request(), _success) == NULL) {
          // stop client because sometimes _client.connected() is true but connection is lost.
          _client.stop();
          _timer.turnOn();
      } else {
           _timer.turnOff();
       }
   }
  }
};


 HttpObj HTTP;
 int outputPin1 = A1;
 int outputPin2 = A3;
 int inputPin = D1;
 int outputPin1_state = 0;
 int outputPin2_state = 0;
 int outputState = 0;

 void setup() {
     pinMode(outputPin1, OUTPUT);
     pinMode(outputPin2, OUTPUT);
     pinMode(inputPin, INPUT_PULLDOWN);
     Serial.begin(115200);
     while (!Serial.available()) SPARK_WLAN_Loop();
     Spark.function("pinControl", pinControl);
     Serial.println("strstr(response, _success)");
 }

 void loop() {
     outputPin1_state = digitalRead(inputPin);
     digitalWrite(outputPin1, outputPin1_state);
     int state = 100 + outputPin1_state * 10 + outputPin2_state;
     if (state!=outputState) {
     Serial.println(state);
         outputState = state;
         HTTP.init( outputState );
    }
     HTTP.run();
 }

 int pinControl(String command){
    outputPin2_state = (command=="HIGH")?1:0;
    digitalWrite(outputPin2,outputPin2_state);
    return 200;
}

Hi @amir,

I’m sorry about the very slow response, I’ve been really deep into a few projects, and am only now getting a chance to respond to mail / pms. I probably will get a chance to test this next week and see if I can find anything, but I wanted you to know I didn’t forget about you!

Thanks,
David

Hi @Dave,
Hope you’re fine. I have been working on other aspects of my app for a while. Back to integration testing, still have the old issue. Please have a look at my code. My app is http://zealinx.meteor.com/

1 Like

Hi @Amir,

Sorry about the slow response, I’m finally getting to catch up on emails! :smile:

I see a few opportunities for delay in your code, especially DNS lookups (using a hostname instead of an IP address), as well I am mostly reading the code, and haven’t stepped through it, but it looks like there is some logic to read back the HTTP response in chunks of 1 second at a time, checkout this line:

  while ((millis() - lastRead) < 1000) {

This makes me think every http request will spend at least a second listening for responses from the server, which might be stopping you from getting 100ms turnaround. I could be wrong though! I wonder if changing that 1000 to something smaller, and using an IP instead of a hostname for your requests might help?

Thanks,
David

Maybe look at changing the client.print to client.write I noticed some massive delays when using print. I usually use a function called ‘out’ and all you have to do is replace client.print with out.

void out(const char *s) {
client.write( (const uint8_t*)s, strlen(s) ); 
}
1 Like

@Hootie81, maybe that’s one for the Firmware Tips and Tricks! :wink:

I like to think of this as a work-around until client.print gets fixed, which @mdma has said will happen.

2 Likes