Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ESP8266-01 and 433Mhz radio (transmit) #735

Closed
tve opened this issue Nov 26, 2015 · 22 comments
Closed

ESP8266-01 and 433Mhz radio (transmit) #735

tve opened this issue Nov 26, 2015 · 22 comments
Assignees
Labels
bug ESP8266 This is only a problem on ESP8266 devices

Comments

@tve
Copy link
Contributor

tve commented Nov 26, 2015

[originally openen by @olliephillips https://github.com/espruino/EspruinoBuilds/issues/3

I'm trying to run a 433Mhz radio on ESP8266/Espruino. The aim is to control 433Mhz wireless plug sockets. I have code that runs and triggers socket on Pico with 433Mhz transmitter. Same code will NOT work on ESP8266 with same 433Mhz transmitter.

My boards are the "01" variant.

I have used various builds since port project began. Current version I'm testing with is "espruino_1v81.tve_esp8266-flash-strings_662306b_esp8266.tgz". But issue has existed throughout.

My 433Mhz project relies on the Espruino method, "digitalPulse()"

Thinking/discussion around this topic on Gitter has so far implied ESP8266 has hardware timer constraints that may limit its application in areas that require precise, and in particular, very small/short timings. As well as 433Mhz I'm inferring that means projects base on infrared transmission too.

However, similar projects seem to exist for Arduino which would suggest hardware is not the constraint. An ESP8266 flashed with appropriate software seems able to use a 433Mhz radio to transmit to these RF controlled plug sockets. Link to video - https://www.youtube.com/watch?v=NAkyki615cY&feature=youtu.be

Of interest is that ESP8266 seems to be able to accurately record the signal from the remote control that came with the plug sockets. I can replay the resulting binary code using a Pico and have it trigger the socket. Looking at the code involved in receiving those signals, I would suggest (though it is only a suggestion) that it also relies on an accurate hardware timer to record the code so that it can be replayed.

Latest discussion on gitter with @tve suggested the default logDebug setting of "true" may be a factor, but in later tests adding this has changed nothing.

My limited understanding of why Arduino projects might work while Espruino does not is that the former compiles to C++ while Espruino is still interpreted at runtime - so the issues are different and potentially more complex.

What bugs me is that ESP8266 via Espruino can record a signal I can reuse (on Pico), but seems incapable of sending same.

If I can provide more info let me know. Would love to put this to bed - one way or the other.

@tve tve added bug ESP8266 This is only a problem on ESP8266 devices labels Nov 26, 2015
@olliephillips
Copy link

Here is the transmit code I appended to original issue:

require("ESP8266").logDebug(false);

var TX = D2; // On 8266
//var TX = A4; // On Pico

var newSend = [0.9, 0.3, 0.3, 0.9, 0.9, 0.3, 0.9, 0.3, 0.9, 0.3,0.3, 0.9,0.3, 0.9, 0.9, 0.3, 0.3, 0.9, 0.3, 0.9, 0.3, 0.9, 0.9, 0.3, 0.3, 0.9, 0.3, 0.9, 0.3, 0.9, 0.9, 0.3, 0.3, 0.9, 0.9, 0.3, 0.3, 0.9, 0.3, 0.9, 0.9, 0.3, 0.9, 0.3, 0.3, 0.9, 0.9, 0.3, 0.3, 0.9, 0.001];

function sendSignal() {
  var dP = digitalPulse;
  var interval = setInterval(function() {
  "compiled";  
  dP(TX, 1, newSend);
  if (times-- < 0) clearInterval(interval);
  }, 30);
}

setInterval(function(){
  console.log("Sending..");
  sendSignal();
}, 5000);

@nkolban
Copy link
Contributor

nkolban commented Nov 27, 2015

@olliephillips Can you provide some information, documentation on the wave form you are trying to produce? Ideally a signal transition diagram with timings at the appropriate scale would be perfect.

I'm wondering if this isn't a similar story to that we had for NeoPixels which requires sub microsecond precise resolution.

@olliephillips
Copy link

@nkolban Unfortunately, and rather frustratingly, I would not know where to begin in generating that. All I can add is that this is the signal (below) I'm trying to send which the above pulse timings translate to.

1011100100010001010011010

The approach for both receiving from the remote and then replaying it, is based on that on the epsruino site here http://www.espruino.com/Remote+Control+Sockets

Unfortunately, I doubt this information helps much.

@gfwilliams
Copy link
Member

For digitalPulse the lengths are in milliseconds, so you need to get down to 0.3ms.

Ahh, I see what's been done now. digitalPulse is supposed to use the Utility timer to get an IRQ called accurately (it executes quickly, so can be high priority).

While @tve has implemented this, the digitalPulse implementation is still doing a nasty blocking pulse.

Try changing:

https://github.com/espruino/Espruino/blob/master/targets/esp8266/jshardware.c#L551-L563

to this:

https://github.com/espruino/Espruino/blob/master/targets/stm32/jshardware.c#L2572-L2593

And assuming the utility timer works, it should all start working.

That function should probably be moved out of jshardware.c altogether and put into jstimer.c.

@olliephillips
Copy link

Way beyond me @gfwilliams but thanks for diving in here. It sounds very promising.

@tve tve self-assigned this Dec 3, 2015
@tve
Copy link
Contributor Author

tve commented Dec 5, 2015

I ran your test program (times is not defined anywhere, btw) and the pulse train looks pretty darn good on my scope. They're long by 6-10us, but I would be surprised if that was the problem. What is strange, however, is that you defined an array that is 30ms long, has a 0.001 pulse at the end, and sits in a 30ms setInterval. On the esp8266 the result is that the previous to last pulse is low 900us, then comes a 1us high, then low for about 1.2ms, and then comes the first 0.9us high pulse form the next iteration. I don't know how this looks on a pico, but here it does not look good... I would put a gap between the pulse trains so the receiver can resync cleanly.

Gordon, I understand what you suggest and I'll get there at some point...

@tve
Copy link
Contributor Author

tve commented Dec 5, 2015

Ha, well..... I tried the utility timer based implementation... and... surprise, surprise.... it doesn't work!

I captured the reason in a comment in the code:

  // Implementation using the utility timer. This doesn't work well on the esp8266, because the
  // utility timer uses tasks and these are not pre-emptible. So the timer won't actually fire
  // until the main espruino task becomes idle, which messes up timings. It also locks-up things
  // when someone defines a pulse train that is longer than the timer queue, because then the
  // main task busy-waits for the timer queue to drain a bit, which never happens.

There are 484 bytes left free in SRAM, which is where interrupt routines have to go and where jstUtilTimerInterruptHandler would have to go to truly run it at interrupt time. Somehow... I don't think this is gonna happen. What could be done is a custom implementation, but I think someone else is going to have to be motivated to try that :-)

@tve
Copy link
Contributor Author

tve commented Dec 5, 2015

I would try the following:

require("ESP8266").logDebug(false);

var TX = D4; // On esp8266
pinMode(TX, 'output');

var newSend = [0.9, 0.3, 0.3, 0.9, 0.9, 0.3, 0.9, 0.3, 0.9, 0.3,0.3, 0.9,0.3, 0.9, 0.9, 0.3, 0.3, 0.9, 0.3, 0.9, 0.3, 0.9, 0.9, 0.3, 0.3, 0.9, 0.3, 0.9, 0.3, 0.9, 0.9, 0.3, 0.3, 0.9, 0.9, 0.3, 0.3, 0.9, 0.3, 0.9, 0.9, 0.3, 0.9, 0.3, 0.3, 0.9, 0.9, 0.3, 0.3, 0.9, 0.001];

function sendSignal() {
  var dP = digitalPulse;
  var times = 3;
  var interval = setInterval(function() {
      dP(TX, 1, newSend);
      if (--times <= 0) clearInterval(interval);
    }, 50);
}

Although the newSend array is still weird because of the 0.001 at the end, and because without it, it doesn't leave the pin in the same state it started in.

@olliephillips
Copy link

@tve Great news, it works. Not so great news, it was maybe never a timer issue?

Above code works 'as is' (with pin names changed) on Pico.
It works 'as is' on ESP8266 👍
Does not work if pinMode is not set on ESP8266.
Does still work with pinMode not set on Pico.

So... and you know where I'm going with this.... :) I added that same line to my code - the code at the top of this issue, and guess what? It works. Remove it, and it does not.

So I guess the question is why is pinMode critical on ESP8266 and not Pico?

Thanks @tve for your help on this issue. Appreciated.

@olliephillips
Copy link

Actually, I think timing a factor and your suggestion improves it. Your code switches the 433Mhz socket much more reliably than mine - which is actually quite sporadic - though it is firing which it would not before the addition of pinMode

Thanks again!!

@tve
Copy link
Contributor Author

tve commented Dec 7, 2015

Thanks for the update! I'll close the ticket, feel free to reopen if you have new issues.

@tve tve closed this as completed Dec 7, 2015
@gfwilliams
Copy link
Member

digitalPulse should really set the pin mode to output if it hasn't been done explicitly beforehand - it could be that's missing?

@olliephillips
Copy link

Yes. I checked the docs on the digitalWrite and Pulse methods and saw that it is/should be output by default.

@tve
Copy link
Contributor Author

tve commented Dec 7, 2015

Ok, I'll add setting the pin to output if it's not an output. We do want to support setting it to open-drain output beforehand and it not changing, right? (I have to admit that I find it odd that digitalPulse changes the pin mode when digitalWrite doesn't (for a good reason).)

@tve tve reopened this Dec 7, 2015
@gfwilliams
Copy link
Member

digitalWrite does change pin mode - or it should do! It calls jshPinOutput which changes state if pinMode hasn't been called.

The reasoning is that most people (outside the Arduino world) get fed up if they say digitalWrite and nothing happens. If you're used to Arduino then you use pinMode and it then behaves the same.

Basically you just need to make sure the first call in PinPulse is to jshPinOutput, with only the last one as jshPinSetValue - which doesn't change state.

@tve
Copy link
Contributor Author

tve commented Dec 15, 2015

digitalWrite does change pin mode - or it should do! It calls jshPinOutput which changes state if pinMode hasn't been called.

I'm trying to track down how this is supposed to work. In jspin.c's jshPinOutput I see https://github.com/espruino/Espruino/blob/master/src/jspin.c#L253-L254:

    if (!jshGetPinStateIsManual(pin))
      jshPinSetState(pin, JSHPINSTATE_GPIO_OUT);

thus the pin is set to output if it's not in manual mode. jshGetPinStateIsManual keep track in its own array whether a pin mode has been set. But I don't see where reset() resets that array. It seems to only be cleared on a hardware reset?

tve added a commit to tve/Espruino that referenced this issue Dec 15, 2015
@gfwilliams
Copy link
Member

Wow, thanks for spotting that! I just filed a bug for it here: #765

I'm surprised it hasn't come up before as a problem with Espruino...

@tve
Copy link
Contributor Author

tve commented Dec 18, 2015

The esp8266 side of things will need some follow-up after #765 is resolved, so I'm leaving this open for now.

@gfwilliams
Copy link
Member

Just to say I have now fixed #765 (finally!)

@olliephillips
Copy link

What will this mean for need to set pinMode prior to using digitalPulse/Write/Read on ESP8226 port? Still need to explicitly set pinMode prior to calling, or does this fix mean can adopt core espruino approach, and not?

@gfwilliams
Copy link
Member

Afaik it means you can do like the other Espruino boards do now.

@MaBecker
Copy link
Contributor

Looks like every thing is fixed and nothing left open, so lets close it again

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug ESP8266 This is only a problem on ESP8266 devices
Projects
None yet
Development

No branches or pull requests

5 participants