X-Git-Url: https://vault307.fbx.one/gitweb/micorpython_ir.git/blobdiff_plain/9bcc511c45d4122dd615a23002c30c13415896ad..8ac6c4b0b81221439541db8f7bdbab29913da9bb:/TRANSMITTER.md?ds=sidebyside diff --git a/TRANSMITTER.md b/TRANSMITTER.md index 4f15c65..2cf169a 100644 --- a/TRANSMITTER.md +++ b/TRANSMITTER.md @@ -97,19 +97,40 @@ Constructor args: 3. `verbose=False` If `True` emits (a lot of) debug output. Method: - 1. `transmit(addr, data, toggle=0)` Integer args. `addr` and `data` are - normally 8-bit values and `toggle` is normally 0 or 1; details are protocol - dependent and are described below. + 1. `transmit(addr, data, toggle=0, validate=False)` Args `addr`, `data` and + `toggle` are positive integers. The maximum vaues are protocol dependent. If + `validate` is `True` passed values are checked and a `ValueError` raised if + they are out of range. If `validate` is false invalid bits are silently + discarded. For example if an address of 0x11 is passed to `MCE.transmit`, the + address sent will be 1 because that protocol supports only a four bit address + field. The `toggle` field is unused by some protocols when 0 should be passed. + +Class method: + 1. `active_low` No args. Pyboard only. A `ValueError` will be thrown on ESP32. + The IR LED drive circuit is usually designed to turn the LED on if the driver + pin is high. If it has opposite polarity the method must be called before + instantiating the class - it will be ineffective if called later. + +Class varaible: + 1. `timeit=False` If `True` the `.transmit` method times itself and prints the + result in μs. The `transmit` method is synchronous with rapid return. Actual transmission -occurs as a background process, on the Pyboard controlled by timers 2 and 5. -Execution times on a Pyboard 1.1 were 3.3ms for NEC, 1.5ms for RC5 and 2ms -for RC6. - -Class variable: - 1. `active_high=True` Normally the IR LED drive circuit turns the LED on if - the pin goes high. If it works with the opposite polarity the variable should - be set `False` before instantiating. +occurs as a background process, on the Pyboard controlled by timers 2 and 5. On +ESP32 the RMT class is used. Execution times were measured on a Pyboard 1.1 and +the ESP32 reference board without SPIRAM. Tests were done at stock frequency and +with `validate=True`, `verbose=False`. A small saving could be achieved by +skipping validation. + +| Protocol | ESP32 | Pyboard | +|:--------:|:-----:|:-------:| +| NEC | 7.8ms | 3.2ms | +| SONY12 | 3.2ms | 1.3ms | +| SONY15 | 3.6ms | 1.5ms | +| SONY20 | 4.5ms | 1.9ms | +| RC5 | 4.9ms | 1.5ms | +| RC6_M0 | 6.0ms | 2.0ms | +| MCE | 6.7ms | 2.0ms | #### NEC class @@ -199,8 +220,6 @@ require 3. # 4. Principle of operation -## 4.1 Pyboard - The classes inherit from the abstract base class `IR`. This has an array `.arr` to contain the duration (in μs) of each carrier on or off period. The `transmit` method calls a `tx` method of the subclass which populates this @@ -212,18 +231,25 @@ this is required for bi-phase (manchester) codings. The `.add` method takes a single μs time value and adds it to the last value in the array: this pulse lengthening is used in bi-phase encodings. -On completion of the subclass `.tx`, `.transmit` appends a special `STOP` value -and initiates physical transmission which occurs in an interrupt context. +On completion of the subclass `.tx`, `.transmit` calls `.trigger` which +initiates transmission as a background process. Its behaviour is platform +dependent. + +## 4.1 Pyboard -This is performed by two hardware timers initiated in the constructor. Timer 2, -channel 1 is used to configure the output pin as a PWM channel. Its frequency -is set in the constructor. The OOK is performed by dynamically changing the -duty ratio using the timer channel's `pulse_width_percent` method: this varies -the pulse width from 0 to a duty ratio passed to the constructor. +Tramsmission is performed by two hardware timers initiated in the constructor. +Timer 2, channel 1 is used to configure the output pin as a PWM channel. Its +frequency is set in the constructor. The OOK is performed by dynamically +changing the duty ratio using the timer channel's `pulse_width_percent` method: +this varies the pulse width from 0 to the duty ratio passed to the constructor. The duty ratio is changed by the Timer 5 callback `._cb`. This retrieves the next duration from the array. If it is not `STOP` it toggles the duty cycle -and re-initialises T5 for the new duration. +and re-initialises T5 for the new duration. If it is `STOP` it ensures that the +duty ratio is set to the `_SPACE` + +Here `.trigger` appends a special `STOP` value and initiates physical +transmission by calling the Timer5 callback. ## 4.2 ESP32 @@ -235,7 +261,59 @@ The carrier is generated by PWM instance `.pwm` running continuously. The ABC constructor converts the 0-100 duty ratio specified by the subclass to the 0-1023 range used by ESP32. +The `.trigger` method calls `RMT.write_pulses` and returns with `RMT` operating +in the background. + ## 4.3 Duty ratio In every case where I could find a specified figure it was 30%. I measured that from a variety of remotes, and in every case it was close to that figure. + +# 5. Unsupported protocols + +You can use the receiver module to capture an IR burst and replay it with the +transmitter. This enables limited support for unknown protocols. This is +strictly for experimenters and I haven't documented it in detail. + +There are two limitations. The first is timing accuracy: both receiving and +transmitting processes introduce some timing uncertainty. This is only likely +to be a practical problem with fast protocols. In brief testing with a known +protocol the scripts below worked. + +The more tricky problem is handling repeat keys: different protocols use widely +varying approaches. If repeat keys are to be supported some experimentation and +coding is likely to be required. + +The following captures a single burst and saves it to a file: +```python +from ir_rx.acquire import test +import ujson + +lst = test() # May report unsupported or unknown protocol +with open('burst.py', 'w') as f: + ujson.dump(lst, f) +``` +This replays it: +```python +from ir_tx import Player +from sys import platform +import ujson + +if platform == 'esp32': + from machine import Pin + pin = (Pin(23, Pin.OUT, value = 0), Pin(21, Pin.OUT, value = 0)) +else: + from pyb import Pin, LED + pin = Pin('X1') +with open('burst.py', 'r') as f: + lst = ujson.load(f) +ir = Player(pin) +ir.play(lst) +``` +The `ir_tx.Player` class is a minimal subclass supporting only the `.play` +method. This takes as an arg an iterable comprising time values of successive +mark and space periods (in μs). + +The `ir_rx.acquire.test` function makes assumptions about the likely maximum +length and maximum duration of a burst. In some cases this may require some +modification e.g. to instantiate `IR_GET` with different args.