Closed guser99 closed 7 months ago
In test 1 you transmit from an interrupt service routine (ISR) successfully, presumably by invoking the script manually. The same test fails when invoked by main.py
after a reboot (test 2). Yet calling the function directly (rather than via an ISR) works after a soft reboot (test3).
Test 4 is definitely wrong. Issuing .transmit
causes transmission to occur in the background. It is incorrect to call .transmit
in a tight loop because it will be called while the previous transmission is in progress. Also IR_DATA
appears to be undefined.
I would suggest inserting a pause after a reboot - it does look as if something is not being initialised correctly.
time.sleep(2) # Wait before running
def timer_or_btn_test(timer_or_pin):
global IR_ADDRESS, IR_DATA_ON
print("ON Before")
nec.transmit(IR_ADDRESS, IR_DATA_ON)
time.sleep(0.1)
print("ON After")
Timer().init(mode=Timer.ONE_SHOT, period=200, callback=timer_or_btn_test)
btn = Pin(9, Pin.IN, Pin.PULL_DOWN)
btn.irq(trigger=Pin.IRQ_RISING, handler=timer_or_btn_test)
As a general point it is not recommended to have sleep()
instructions in an ISR, but I don't think this is the cause of your problem.
Hello, thanks for the quick reply.
I entered all the tests (1-4) directly into the repl via my computer. The "soft reboot" was probably misleading, I just wanted to express that there is a clean system, I pressed Ctrl+D within the repl and then re-entered (copied) everything. I do not (yet) have a main.py that is automatically executed when the Pico is started.
from machine import Pin, Timer
from ir_tx.nec import NEC
import time
PIN_IR_TRANSMITTER = Pin(17, Pin.OUT, value=0)
IR_ADDRESS = 0xef00
IR_DATA_ON = 0x03
IR_DATA = 0x01
nec = NEC(PIN_IR_TRANSMITTER)
def timer_or_btn_test(timer_or_pin):
global IR_ADDRESS, IR_DATA_ON, IR_DATA
print("ON Before")
nec.tx(IR_ADDRESS, IR_DATA_ON)
time.sleep(0.1)
print("ON After")
for x in range(0, 4):
print("IR_DATA Before {}".format(x))
nec.transmit(IR_ADDRESS, IR_DATA)
time.sleep(0.1)
print("IR_DATA After {}".format(x))
The call via a timer or button IRQ is no longer blocked, but nothing is sent that controls the LED strip.
Timer().init(mode=Timer.ONE_SHOT, period=200, callback=timer_or_btn_test)
btn = Pin(9, Pin.IN, Pin.PULL_DOWN)
btn.irq(trigger=Pin.IRQ_RISING, handler=timer_or_btn_test)
Output:
>>> ON Before
ON After
IR_DATA Before 0
IR_DATA After 0
IR_DATA Before 1
IR_DATA After 1
IR_DATA Before 2
IR_DATA After 2
IR_DATA Before 3
IR_DATA After 3
When the function is called up manually via the repl, everything works as expected. The LED strip is switched on and becomes darker. The output is the same as with the timer, except that all commands are sent.
timer_or_btn_test(False)
How can I make the LED strip get darker quickly without using a loop? The brightness of the LED strip can be adjusted in 16 steps. I could not see any function that allows me to send the same command 16 times in a row, for example.
A side question: Why is it not recommended to have sleep() functions in an ISR?
The docs state
Actual transmission occurs as a background process
This means that the transmit call returns immediately, with transmission running in the background. The application should check the busy
status before starting another transmission - alternatively it should pause for a long enough period to guarantee completion. If you are using an interrupt to handle a pushbutton, you need to be aware of contact bounce.
My approach would be to forget interrupts and timers and use asyncio
with a driver such as this one to interface the switch.
import asyncio
from machine import Pin
from primitives import ESwitch # Switch device driver
from ir_tx.nec import NEC
es = ESwitch(Pin(9, Pin.IN, Pin.PULL_DOWN), lopen=0) # Create a switch instance. Close is high
PIN_IR_TRANSMITTER = Pin(17, Pin.OUT, value=0)
IR_ADDRESS = 0xef00
IR_DATA_ON = 0x03
IR_DATA = 0x03
nec = NEC(PIN_IR_TRANSMITTER)
async def switch_handler():
while True:
es.close.clear() # Clear the closure event
await es.close.wait() # Wait for the switch closure
print("Closed")
nec.transmit(IR_ADDRESS, IR_DATA_ON)
while nec.busy(): # Ensure transmission is complete before starting another
await asyncio.sleep_ms(100)
print("After transmission")
async def main():
await switch_handler() # Run forever
asyncio.run(main())
Timers and interrupts are relatively advanced techniques. See official docs for explanation about delays in interrupt handlers.
While asyncio
takes some learning, the effort is well worthwhile. See the tutorial.
Thanks for the explanation. Timers and interrupts seemed to me to be the best solution and much easier to implement than asyncio. I'll look into it in the new year, unfortunately I can't test anything this year. Many thanks in advance.
I can assure you that asyncio
is easier: you have already encountered a few of the problems with timers and interrupts.
Either way, you have a learning curve, but asyncio
is more generally useful. I speak with nearly 50 years experience of writing firmware.
Hi, I have recently started using Micropython and am currently using a Raspberry Pi Pico to control an LED strip via button press and timer using your library.
After initial tests with the repl and manual testing of the functions, I have found that when I call these functions using the Timer and Pin.irq functions provided by Micropython, it only works as long as there is only one transmit (Test 1).
Even one sleep prevents the transmit from taking place and the LED strip from lighting up when the function is called up by a timer or by pressing a button (test 2).
When the same function is called up manually, the transmit works (test 3).
It is particularly bad if I have a function in which several transmits are to take place, e.g. if the LED strip is to be switched on and set to the lowest brightness level. After the first transmit has been run through without sending anything, the Pico no longer responds because it seems to be stuck in the loop "while self.busy():" of the transmit function within the ".../micropython_ir-master/ir_tx/init.py" file. The only way to regain access to the Pico is to perform a manual hard reset by briefly disconnecting the Pico from the power supply. (Test 4)
Is the module not timer and button IRQ compatible or how can I get it to work?
This is how I proceed:
### repl ### # Basic configuration
Timer().init(mode=Timer.ONE_SHOT, period=200, callback=timer_or_btn_test) btn = Pin(9, Pin.IN, Pin.PULL_DOWN) btn.irq(trigger=Pin.IRQ_RISING, handler=timer_or_btn_test)
Timer().init(mode=Timer.ONE_SHOT, period=200, callback=timer_or_btn_test) btn = Pin(9, Pin.IN, Pin.PULL_DOWN) btn.irq(trigger=Pin.IRQ_RISING, handler=timer_or_btn_test)
timer_or_btn_test()
Timer().init(mode=Timer.ONE_SHOT, period=200, callback=timer_or_btn_test) btn = Pin(9, Pin.IN, Pin.PULL_DOWN) btn.irq(trigger=Pin.IRQ_RISING, handler=timer_or_btn_test)