045 - MicroPython TechNotes: Infrared Transmitter

Introduction

In this article, I will tackle how you can use an Infrared transmitter module with ESP32 using MicroPython. GorillaCell Infrared Transmitter module from GorillaCell ESP32 development kits uses an infrared transmitter LED. It is low cost and easy to use.

Bill Of Materials

  1. ESP32 development board.

  2. Gorillacell ESP32 shield.

  3. 3-pin female-female dupont wires.

  4. Gorillacell Infrared Transmitter module.

Pinout

It has 3 pins namely: 1. G – for the ground pin. 2. V – for the supply voltage. 3. S – for the infrared transmitter signal pin.

Hardware Instruction

  1. First, attach the ESP32 development board on top of the ESP32 shield and make sure that both the USB port are on the same side.

  2. Next, attach the dupont wires to the Infrared Transmitter module by following the color coding that is black for the ground, red for the VCC, and yellow for the signal pin.

  3. Next, attach the other end of the dupont wires to the ESP32 shield by matching the colors of the wires to the colors of the pin headers that is black is to black, red is to red, and yellow is to yellow pin headers. For this lesson, I choose GPIO 26 to serve as the output pin for the Infrared Transmitter module.

  4. Next, power the ESP32 shield with an external power supply with a type-C USB connector and make sure that the power switch is set to ON state.

  5. Lastly, connect the ESP32 to the computer using a micro-USB connector cable.

Software Instruction

  1. Copy the ir_tx.py from the SOURCE CODE section and paste it to Thonny IDE.

  2. Save it to MicroPython root directory by clicking the File menu and select Save As.

  3. Select MicroPython Device.

  4. And save it as ir_tx.py.

  5. Copy other examples to Thonny IDE and run it accordingly.

  6. Feel free to modify and adapt according to your needs.

Video Demonstration

Call To Action

If you have any concern regarding this video, please write your question in the comment box.

You might also liked to support my journey on Youtube by subscribing on my channel, TechToTinker. Click this to Subscribe.

Thank you and have a good days ahead.

See you, – George Bantique | tech.to.tinker@gmail.com

Source Code

1. Example # 1, basics of transferring Infrared data:

1# More details can be found in TechToTinker.blogspot.com 
2# George Bantique | tech.to.tinker@gmail.com
3
4from machine import Pin
5from ir_tx import NEC
6
7nec = NEC(Pin(26, Pin.OUT, value = 0))
8
9#nec.transmit(<addr>, <data>)

2. Example # 2, basic application of Infrared transmitter:

 1# George Bantique | tech.to.tinker@gmail.com
 2
 3from machine import Pin
 4from ir_tx import NEC
 5from time import sleep_ms
 6
 7nec = NEC(Pin(26, Pin.OUT, value = 0))
 8sw = Pin(0, Pin.IN)
 9
10while True:
11    if sw.value()==0:
12        nec.transmit(0x0000, 0x09)
13    sleep_ms(100)

3. micropython_ir Library of Peter Hinch, save it as ir_tx.py:

  1# MIT License
  2# 
  3# Copyright (c) 2020 Peter Hinch
  4# 
  5# Permission is hereby granted, free of charge, to any person obtaining a copy
  6# of this software and associated documentation files (the "Software"), to deal
  7# in the Software without restriction, including without limitation the rights
  8# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9# copies of the Software, and to permit persons to whom the Software is
 10# furnished to do so, subject to the following conditions:
 11# 
 12# The above copyright notice and this permission notice shall be included in all
 13# copies or substantial portions of the Software.
 14# 
 15# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 16# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 17# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 18# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 19# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 20# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 21# SOFTWARE.
 22
 23# Author: Peter Hinch
 24# Copyright Peter Hinch 2020-2021 Released under the MIT license
 25# http://github.com/peterhinch/micropython_ir
 26
 27# __init__.py Nonblocking IR blaster
 28# Runs on Pyboard D or Pyboard 1.x (not Pyboard Lite), ESP32 and RP2
 29
 30# Released under the MIT License (MIT). See LICENSE.
 31
 32# Copyright (c) 2020-2021 Peter Hinch
 33from sys import platform
 34from micropython import const
 35ESP32 = platform == 'esp32'  # Loboris not supported owing to RMT
 36RP2 = platform == 'rp2'
 37if ESP32:
 38    from machine import Pin, PWM
 39    from esp32 import RMT
 40elif RP2:
 41    from .rp2_rmt import RP2_RMT
 42else:
 43    from pyb import Pin, Timer  # Pyboard does not support machine.PWM
 44from esp32 import RMT
 45from array import array
 46from time import ticks_us
 47from time import ticks_diff
 48# import micropython
 49# micropython.alloc_emergency_exception_buf(100)
 50
 51
 52# Shared by NEC
 53STOP = const(0)  # End of data
 54
 55# IR abstract base class. Array holds periods in μs between toggling 36/38KHz
 56# carrier on or off. Physical transmission occurs in an ISR context controlled
 57# by timer 2 and timer 5. See TRANSMITTER.md for details of operation.
 58class IR:
 59    _active_high = True  # Hardware turns IRLED on if pin goes high.
 60    _space = 0  # Duty ratio that causes IRLED to be off
 61    timeit = False  # Print timing info
 62
 63    @classmethod
 64    def active_low(cls):
 65        if ESP32:
 66            raise ValueError('Cannot set active low on ESP32')
 67        cls._active_high = False
 68        cls._space = 100
 69
 70    def __init__(self, pin, cfreq, asize, duty, verbose):
 71        if ESP32:
 72            self._rmt = RMT(0, pin=pin, clock_div=80, carrier_freq=cfreq,
 73                            carrier_duty_percent=duty)  # 1μs resolution
 74        elif RP2:  # PIO-based RMT-like device
 75            self._rmt = RP2_RMT(pin_pulse=None, carrier=(pin, cfreq, duty))  # 1μs resolution
 76        else:  # Pyboard
 77            if not IR._active_high:
 78                duty = 100 - duty
 79            tim = Timer(2, freq=cfreq)  # Timer 2/pin produces 36/38/40KHz carrier
 80            self._ch = tim.channel(1, Timer.PWM, pin=pin)
 81            self._ch.pulse_width_percent(self._space)  # Turn off IR LED
 82            # Pyboard: 0 <= pulse_width_percent <= 100
 83            self._duty = duty
 84            self._tim = Timer(5)  # Timer 5 controls carrier on/off times
 85        self._tcb = self._cb  # Pre-allocate
 86        self._arr = array('H', 0 for _ in range(asize))  # on/off times (μs)
 87        self._mva = memoryview(self._arr)
 88        # Subclass interface
 89        self.verbose = verbose
 90        self.carrier = False  # Notional carrier state while encoding biphase
 91        self.aptr = 0  # Index into array
 92
 93    def _cb(self, t):  # T5 callback, generate a carrier mark or space
 94        t.deinit()
 95        p = self.aptr
 96        v = self._arr[p]
 97        if v == STOP:
 98            self._ch.pulse_width_percent(self._space)  # Turn off IR LED.
 99            return
100        self._ch.pulse_width_percent(self._space if p & 1 else self._duty)
101        self._tim.init(prescaler=84, period=v, callback=self._tcb)
102        self.aptr += 1
103
104    # Public interface
105    # Before populating array, zero pointer, set notional carrier state (off).
106    def transmit(self, addr, data, toggle=0, validate=False):  # NEC: toggle is unused
107        t = ticks_us()
108        if validate:
109            if addr > self.valid[0] or addr < 0:
110                raise ValueError('Address out of range', addr)
111            if data > self.valid[1] or data < 0:
112                raise ValueError('Data out of range', data)
113            if toggle > self.valid[2] or toggle < 0:
114                raise ValueError('Toggle out of range', toggle)
115        self.aptr = 0  # Inital conditions for tx: index into array
116        self.carrier = False
117        self.tx(addr, data, toggle)  # Subclass populates ._arr
118        self.trigger()  # Initiate transmission
119        if self.timeit:
120            dt = ticks_diff(ticks_us(), t)
121            print('Time = {}μs'.format(dt))
122
123    # Subclass interface
124    def trigger(self):  # Used by NEC to initiate a repeat frame
125        if ESP32:
126            self._rmt.write_pulses(tuple(self._mva[0 : self.aptr]), start = 1)
127        elif RP2:
128            self.append(STOP)
129            self._rmt.send(self._arr)
130        else:
131            self.append(STOP)
132            self.aptr = 0  # Reset pointer
133            self._cb(self._tim)  # Initiate physical transmission.
134
135    def append(self, *times):  # Append one or more time peiods to ._arr
136        for t in times:
137            self._arr[self.aptr] = t
138            self.aptr += 1
139            self.carrier = not self.carrier  # Keep track of carrier state
140            self.verbose and print('append', t, 'carrier', self.carrier)
141
142    def add(self, t):  # Increase last time value (for biphase)
143        assert t > 0
144        self.verbose and print('add', t)
145        # .carrier unaffected
146        self._arr[self.aptr - 1] += t
147
148
149# Given an iterable (e.g. list or tuple) of times, emit it as an IR stream.
150class Player(IR):
151
152    def __init__(self, pin, freq=38000, verbose=False):  # NEC specifies 38KHz
153        super().__init__(pin, freq, 68, 33, verbose)  # Measured duty ratio 33%
154
155    def play(self, lst):
156        for x, t in enumerate(lst):
157            self._arr[x] = t
158        self.aptr = x + 1
159        self.trigger()
160
161
162_TBURST = const(563)
163_T_ONE = const(1687)
164
165class NEC(IR):
166    valid = (0xffff, 0xff, 0)  # Max addr, data, toggle
167
168    def __init__(self, pin, freq=38000, verbose=False):  # NEC specifies 38KHz
169        super().__init__(pin, freq, 68, 33, verbose)  # Measured duty ratio 33%
170
171    def _bit(self, b):
172        self.append(_TBURST, _T_ONE if b else _TBURST)
173
174    def tx(self, addr, data, _):  # Ignore toggle
175        self.append(9000, 4500)
176        if addr < 256:  # Short address: append complement
177            addr |= ((addr ^ 0xff) << 8)
178        for _ in range(16):
179            self._bit(addr & 1)
180            addr >>= 1
181        data |= ((data ^ 0xff) << 8)
182        for _ in range(16):
183            self._bit(data & 1)
184            data >>= 1
185        self.append(_TBURST)
186
187    def repeat(self):
188        self.aptr = 0
189        self.append(9000, 2250, _TBURST)
190        self.trigger()  # Initiate physical transmission.

References And Credits

  1. Purchase your Gorillacell ESP32 development kits from: https://gorillacell.kr/

  2. Peter Hinch micropython IR library: http://github.com/peterhinch/micropython_ir



Posts in this series



No comments yet!

GitHub-flavored Markdown & a sane subset of HTML is supported.