IR receiver/transmitter for ESP8266?
IR receiver/transmitter for ESP8266?
It seems the ESP8266 has some hardware support for infrared remote control - see chapter 13 of http://www.espressif.com/sites/default/ ... e_en_0.pdf
Has anyone had a go at exposing this in micropython?
Has anyone had a go at exposing this in micropython?
Re: IR receiver/transmitter for ESP8266?
Interesting
For comparison, here's an Arduino flavoured, software only approach: https://github.com/esp8266/Basic/tree/m ... oteESP8266
For comparison, here's an Arduino flavoured, software only approach: https://github.com/esp8266/Basic/tree/m ... oteESP8266
- pythoncoder
- Posts: 5956
- Joined: Fri Jul 18, 2014 8:01 am
- Location: UK
- Contact:
Re: IR receiver/transmitter for ESP8266?
For IR reception there are demodulator chips e.g. Vishay TSOP4838 http://cpc.farnell.com/vishay/tsop4838/ ... dp/SC09303. These cost peanuts. They save having to deal with the 38KHz carrier, which presents a number of technical issues including rejecting optical interference signals. The chip gives you a relatively slow pulse waveform which you can decode.
I have some Arduino C code for doing this with the fairly common NEC protocol if anyone's interested - it should be reasonably easy to port to MicroPython.
I have some Arduino C code for doing this with the fairly common NEC protocol if anyone's interested - it should be reasonably easy to port to MicroPython.
Peter Hinch
Index to my micropython libraries.
Index to my micropython libraries.
Re: IR receiver/transmitter for ESP8266?
I spent a bit of time getting a basic NEC IR receiver working unfortunately its sensitive to time so if a GC goes off the timing is not met and you get a decode error. This describes the setup minus the UNO + ESP12 board. https://learn.sparkfun.com/tutorials/ir-communication
https://github.com/mhoffma/esp/blob/master/ir.py
I added a pin output to the isr routine to show how much time is being consumed capturing the timestamps.
[img][(https://github.com/mhoffma/esp/blob/master/irpy.png)]
To run this with the debug pin enabled you need to set the machine frequency to 160Mhz otherwise you will never see a successful decode. The polarity calculation also causes problems in that it adds a lot of extra latency to the tiny isr.
https://github.com/mhoffma/esp/blob/master/ir.py
I added a pin output to the isr routine to show how much time is being consumed capturing the timestamps.
[img][(https://github.com/mhoffma/esp/blob/master/irpy.png)]
To run this with the debug pin enabled you need to set the machine frequency to 160Mhz otherwise you will never see a successful decode. The polarity calculation also causes problems in that it adds a lot of extra latency to the tiny isr.
Re: IR receiver/transmitter for ESP8266?
Can't you use machine.time_pulse_us(pin, pulse_level, timeout_us=1000000) for your purpose?
B.T.W.: Lines 45 .. 47 could be written as:
You may also try the native code decorator @micropython.native for speed improvement. That typically gains a factor of two. Sometimes it works.
B.T.W.: Lines 45 .. 47 could be written as:
Code: Select all
self.wptr = (self.wptr + 1) % self.BUFSZ
Re: IR receiver/transmitter for ESP8266?
I started with that time_pulse_us, I didn't have a lot of success, the thing is so sensitive did you take a peak at the time line I posted a picture of it, the bottom trace is how much time I'm spending in the ISR its huge compared to the size of those pulse widths we are trying to recognize.
Thanks for the @micropython.native tip, I didn't know that existed does that assemble the python code into native assembler or something cool like that?
Thanks for the @micropython.native tip, I didn't know that existed does that assemble the python code into native assembler or something cool like that?
- pythoncoder
- Posts: 5956
- Joined: Fri Jul 18, 2014 8:01 am
- Location: UK
- Contact:
Re: IR receiver/transmitter for ESP8266?
See these references for explanations of the different code emitters:
https://www.kickstarter.com/projects/21 ... sts/664832
https://www.kickstarter.com/projects/21 ... sts/665145
You might also be interested in this https://github.com/peterhinch/micropyth ... aremote.py which is a decoder for NEC IR remotes using asyncio. This was tested on the Pyboard, so may have issues on the ESP8266.
https://www.kickstarter.com/projects/21 ... sts/664832
https://www.kickstarter.com/projects/21 ... sts/665145
You might also be interested in this https://github.com/peterhinch/micropyth ... aremote.py which is a decoder for NEC IR remotes using asyncio. This was tested on the Pyboard, so may have issues on the ESP8266.
Peter Hinch
Index to my micropython libraries.
Index to my micropython libraries.
Re: IR receiver/transmitter for ESP8266?
Peter how is the performance of your implementation? Does your code suffer from latency issues like mine does, e.g. if I add a Timer() to my system my codes robustness drops do you have a feeling about your nice implementation which I will start working with ASAP.
This is exactly what I want to learn next!
async def _run(self):
loop = asyncio.get_event_loop()
block_time = self.block_time
while True:
await self._ev_start # Wait unitl data collection has started
delta = block_time - ticks_diff(loop.time(), self._ev_start.value())
await asyncio.sleep_ms(delta) # Data block should have ended
self._decode() # decode, clear event, prepare for new rx, call cb
I really like that native decorator, that is so nice. Are you allowed to imported functions my routine which is a callback for a pin seems to fail on missing symbol tick_us when I add the @native it.
which all lead to rebbot.
This is exactly what I want to learn next!
async def _run(self):
loop = asyncio.get_event_loop()
block_time = self.block_time
while True:
await self._ev_start # Wait unitl data collection has started
delta = block_time - ticks_diff(loop.time(), self._ev_start.value())
await asyncio.sleep_ms(delta) # Data block should have ended
self._decode() # decode, clear event, prepare for new rx, call cb
I really like that native decorator, that is so nice. Are you allowed to imported functions my routine which is a callback for a pin seems to fail on missing symbol tick_us when I add the @native it.
Code: Select all
@micropython.native
def timeseq_isr(self,p):
'''compute edge to edge transition time, ignore polarity'''
e=ticks_us()
self.dbgport.high()
#s=2*p.value()-1
d=ticks_diff(e,self.ledge)
self.ledge=e
self.buf[self.wptr]=d #*s
self.wptr=(self.wptr+1)&self.BUFSZ
self.dbgport.low()
Code: Select all
>>>
>>> NameError: name 'ticks_us' is not defined
NameError: name 'ticks_us' is not defined
NameError: name 'ticks_us' is not defined
NameError: name 'ticks_us' is not defined
NameError: name 'ticks_us' is not defined
NameError: name 'ticks_us' is not defined
NameError: name 'ticks_us' is not defined
NameError: name 'ticks_us' is not defined
Re: IR receiver/transmitter for ESP8266?
if I rewrite my isr this way it work much better and the performance is so much better thanks for the tip:
Code: Select all
def __init__(self...)
....
# cache ticks functions for native call
self.ticks_diff = time.ticks_diff
self.ticks_us = time.ticks_us
...
@micropython.native
def timeseq_isr(self,p):
'''compute edge to edge transition time, ignore polarity'''
e=self.ticks_us()
self.dbgport.high()
s=2*p.value()-1
d=self.ticks_diff(e,self.ledge)
self.ledge=e
self.buf[self.wptr]=d*s
self.wptr=(self.wptr+1)&self.MASK
self.dbgport.low()
Re: IR receiver/transmitter for ESP8266?
@mmh: Thank you for sharing your experience. It helped my to overcome the error I ran into when trying to run a ISR as native code. It did not resolve the error at that time, but happily you did. I made the attempt to transform your example into viper code, which ought to be faster. I succeeded, but the performance gain is minor, like 10%, and therefore maybe not worth the reduced flexibility in the code, unless you are fighting for the last few clock cycles. And you might find direct access to the GPIO pins interesting, which is very fast (~75 ns per access).
The script skeleton below arbitrary selected GPIO bits. If you want to try it yourself, you have to adapt the read and write to the GPIO_BASE array. The GPIO number follow the bit numbers in that word. Unfortunately there is not simple way in viper to store an integer into a python object. So I had to use a two element array for the buffer index and e. The sample function takes about 280 µs @ 80 MHz and 180µs @ 160 MHz, compared to 318 µs @ 80 MHz using native code. The empty function call takes ~100 µs @ 80MHz.
The script skeleton below arbitrary selected GPIO bits. If you want to try it yourself, you have to adapt the read and write to the GPIO_BASE array. The GPIO number follow the bit numbers in that word. Unfortunately there is not simple way in viper to store an integer into a python object. So I had to use a two element array for the buffer index and e. The sample function takes about 280 µs @ 80 MHz and 180µs @ 160 MHz, compared to 318 µs @ 80 MHz using native code. The empty function call takes ~100 µs @ 80MHz.
Code: Select all
import machine
import time
import array
_BUFFERSIZE = const(32) # Ringbuffer Size
_BUFFERMASK = const(0x1f)
BURST = 40
class acquire:
def __init__(self):
self.ticks_us = time.ticks_us
self.ticks_diff = time.ticks_diff
self.data = array.array("L", [0] * _BUFFERSIZE)
self.index_ledge = array.array("L", [0] * 2)
@micropython.viper
def sample(self, x):
e = int(self.ticks_us())
GPIO_BASE = ptr32(0x60000300) # GPIO Base register
GPIO_BASE[1] = 0x10 # set bit 4 = GPIO4
s = ((GPIO_BASE[6] & 0x20) >> 4) - 1 # read GPIO 5
d = int(self.ticks_diff(e, self.index_ledge[1]))
index = int(self.index_ledge[0])
self.data[index] = d * s
index = (index + 1) & _BUFFERMASK
self.index_ledge[0] = index
self.index_ledge[1] = e
GPIO_BASE[2] = 0x10 # clear bit 4 = GPIO4
#