How to receive a stream on a NodeMCU?

I am trying to make a bidirectional streaming system using two NodeMCUs.

Essentially, when something happens, one NodeMCU sends a signal using thing.call_device(recvname, "torecv");, and the other NodeMCU goes into “reception mode”. The first NodeMCU then starts streaming using thing.stream(thing["button"]);.

How do I receive this stream on the second NodeMCU which knows it is being streamed to?

I have the following resources defined:

thing["button"] >> [](pson& out){
    out = digitalRead(BUTTON);
};
thing["listener"] << [](pson& in){
    recvstate = in;
};
thing["torecv"] = [](){
    recv = !recv;
};

where recv is a variable that is checked for “reception mode”, and recvstate is an integer variable that should store the data temporarily (the data is simply a 1 or a 0).

How do I receive streamed data?

1 Like

Is there really no method or solution to receive a data stream? It seems doable, I just can’t figure it out. Any help would be greatly appreciated.

Hi Sergey,

Specifically what do you need to do? because when you say “streaming” I understand that you need to send data constantly, and this microcontrollers has no memory to store a huge amount of information.

Tell me what is your particular need, not the solution you are thinking is going to solve your need. :wink:

Thanks for the reply!
You’re right, here’s what I’m trying to do:

  • Two NodeMCUs have a button, a lamp, and a buzzer
  • A switch is pressed on one NodeMCU to enter “sending mode”, the other goes into “reception mode”
  • Pressing the button on the sending NodeMCU lights up the lamp and buzzes the buzzer on the receiving NodeMCU
  • The data has to be received in real time: a minor delay is alright, but the timing (aka how long the button is pressed and released for) has to be precise
  • This is to simulate a morse machine, where morse signals are send from one device to the other
  • The data is not stored, it is received and used in real time.

So, I thought that streaming the button data in real time to the other device would be the easiest way. Is it possible? Is there a better way?

Do you need that specifically both have a certain mode (sender and receiver) to assure one way communication?
Because what I’m thinking is that can be detected the state change (of the morse signal) and send the state according that change, it is not neccesary that the microcontroller have a certain state to send or receive, it is so fast that it can do it simultaneously, so you can send and receive data at the same time.

Sure, that works too.

How do I get the morse signals to go in real time, though? Doing thing.call_device(...) with either a 1 or a 0 on every loop cycle is not fast enough.

That was what I was thinking, with interruptions

I would try something like this on the sender:

void Setup(){
pinMode(interruptPin, INPUT);
attachInterrupt(interruptPin, Zero, FALLING);
attachInterrupt(interruptPin, One, RISING);
}
void Zero(){
thing.call_device(...);// the command to write a zero at the receiver.
}

void One(){
thing.call_device(...);// the command to write a one at receiver.
}

loop(){
thing.handle();
}

And for receiving, define the “thing” like

if (in["Var"].is_empty()){
  in["Var"] = incomingVar;
}
else{
  incomingVar = in["Var"];
  if(incomingVar){
    // turn on led and buzzer;
  }
  else{
    //turn off led and buzzer;
  }
}

Please confirm all the instructions, do not remember if all are well invoked, I wrote this on the fly lol.

I do not know if you can stream directly to a device, never have the need to.

Test this and let me know how it goes ;).

Oh, that’s a method I completely ignored! Thanks, will try and get back to you with my results :slight_smile:

Hey again!

After some digging around, your method did, indeed, work.

However, call_device(…) is a slow function. Running it from within the interrupt blocks the program for so long, that I cannot reasonably send any signals. After replacing the call with a simple digitalRead() and Serial.println(), I realised that debouncing is a problem as well. However, adding a manual wait (using while and millis()), it became too slow again. It doesn’t register all of my inputs.

Is there some other method (streaming, maybe? or can you think of anything else?) to make sure this data isn’t lost?

Good that it worked!

I thought that… You need to sort the debouncing issue with electronic, you need to estimate how long will be the short pulse, and with that number calculate the RC circuit, something as the attached circuit should work.

Also you may try the delayMicroseconds(); function, is similar to delay(); but, yes… in microseconds :wink:

Hey, another problem.
Due to varying network conditions, I feel like calling the device to turn on and then afterwards to turn off is very unpredictable. I’m not sure whether it’s the debouncing causing the problem or not, but it yields very unpleasant results.

I also tried an approach where, after the button is held down for a period of time, the time it was held is sent over, where the buzzer is played for that duration. However, that did not give too good results either.

How would the RC circuit help? Would it significantly reduce the problem? If yes, how do I calculate the values for the circuit/delay?

The circuit will help avoiding the false detections, this kind of pushers may provoque an very undesirable effect, is that when it almost make the contact, the microcontroler may detect that little variations, this is translated into when you want to detect a rising change, the microcontroller may detect 5 risings and 4 fallings, and of course may have a unpredictable behavior because the code is written thinking that the time between those changes is enough to execute the interrupt code.

The circuit will detect a logic one (or a zero) at the capacitor when it is charged (or discharged) due it does not accept instant voltage changes.

t= R * C
Where:
t = is time to charge the capacitor
R = The value of the resistor
C = The value of capacitor.

Google can be a useful friend for understanding the equation of charge and discharge… In this particular case the capacitor will charge (give a logic one) when the button is not pushed.

Hey again!

I finally figured it out! Turns out debouncing wasn’t necessary at all. Your solution wouldn’t have worked even after the RC circuit, as the variable latency would have screwed up the communication anyway.

I went through a plethora of solutions, but none of them worked. I finally managed to do it by sychronising the millis() timers when the NodeMCUs are turned on, and then sending the current timestamp along with either a HIGH or LOW command when a button is pressed or released.

You can see my final code (and long battle through my git commit log) if you’re interested here:

Oh clever!

But how do you synchronize the millis?? and after all how do you send the commands?? I’m curious :wink:

1 Like

Essentially, when one NodeMCU boots, it attempts to send a synchronisation message to the other to try and sync the time. If the other one is turned off, nothing happens.
When the second one wakes up, it sends an identical message. Upon receiving it, the first one calculates the millis difference between itself and the 2nd NodeMCU, and sends this time difference (but negative) back. Both store it in a variable.

As for sending the message, that isn’t difficult: turns out interrupts only cause more problems than they fix!
I just put a big debounce function (from the default Arduino tutorial :slight_smile:) around the logic that sends a message. The message simply contains the current millis() value and either a HIGH or LOW signal. When the second NodeMCU receives the message, it adds the time difference to its millis() value plus a constant to make sure it has enough time to compensate for the latency, and puts that value in an std::vector. It then either turns the LED and buzzer off or on (depending on the HIGH or LOW value) exactly when its millis() counter reaches the first item in the vector, and removes it.

That’s the gist of it, more info in the Github repository :wink:
Thanks for your help!