IR receiver/transmitter for ESP8266?

Discuss development of drivers for external hardware and components, such as LCD screens, sensors, motor drivers, etc.
Target audience: Users and developers of drivers.
askvictor
Posts: 24
Joined: Tue Mar 22, 2016 9:08 pm

IR receiver/transmitter for ESP8266?

Post by askvictor » Wed Dec 28, 2016 2:46 am

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?

User avatar
mcauser
Posts: 507
Joined: Mon Jun 15, 2015 8:03 am

Re: IR receiver/transmitter for ESP8266?

Post by mcauser » Thu Dec 29, 2016 9:02 pm

Interesting :)

For comparison, here's an Arduino flavoured, software only approach: https://github.com/esp8266/Basic/tree/m ... oteESP8266

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: IR receiver/transmitter for ESP8266?

Post by pythoncoder » Fri Dec 30, 2016 9:07 am

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.
Peter Hinch
Index to my micropython libraries.

mmh
Posts: 13
Joined: Fri Jan 20, 2017 3:35 pm

Re: IR receiver/transmitter for ESP8266?

Post by mmh » Thu Mar 09, 2017 6:24 am

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.

User avatar
Roberthh
Posts: 3667
Joined: Sat May 09, 2015 4:13 pm
Location: Rhineland, Europe

Re: IR receiver/transmitter for ESP8266?

Post by Roberthh » Thu Mar 09, 2017 9:14 am

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:

Code: Select all

self.wptr = (self.wptr + 1) % self.BUFSZ
You may also try the native code decorator @micropython.native for speed improvement. That typically gains a factor of two. Sometimes it works.

mmh
Posts: 13
Joined: Fri Jan 20, 2017 3:35 pm

Re: IR receiver/transmitter for ESP8266?

Post by mmh » Thu Mar 09, 2017 5:26 pm

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?

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: IR receiver/transmitter for ESP8266?

Post by pythoncoder » Thu Mar 09, 2017 5:46 pm

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.
Peter Hinch
Index to my micropython libraries.

mmh
Posts: 13
Joined: Fri Jan 20, 2017 3:35 pm

Re: IR receiver/transmitter for ESP8266?

Post by mmh » Thu Mar 09, 2017 7:24 pm

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.

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

which all lead to rebbot.

mmh
Posts: 13
Joined: Fri Jan 20, 2017 3:35 pm

Re: IR receiver/transmitter for ESP8266?

Post by mmh » Sat Mar 11, 2017 2:23 am

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()                                                                                                                                                                    

User avatar
Roberthh
Posts: 3667
Joined: Sat May 09, 2015 4:13 pm
Location: Rhineland, Europe

Re: IR receiver/transmitter for ESP8266?

Post by Roberthh » Mon Mar 13, 2017 3:08 pm

@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.

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
#

Post Reply