Search code examples
moduleluanodemcuinfrared

How to get the KY-022 Infrared Receiver module to work on NodeMCU in Lua?


I have a KY-022 IR module that I can't get to work on my NodeMCU. I've been searching for some code samples in Lua on the internet with no luck. Can anyone point me in the right direction? Any code samples would be greatly appreciate it.

At the moment I have the following code:

local pin = 4
gpio.mode(pin, gpio.OPENDRAIN, gpio.PULLUP)
gpio.trig(pin, "down", function (level, micro)
    print(gpio.read(pin), level, micro)
end)

When I press a button on the remote, I get something like this:

0 0 571940709 0 0 571954086 0 0 571955257 1 0 571958694 1 0 571963275 1 0 571969917 0 0 571974347 0 0 571980989 1 0 571983203 1 0 571987709 0 0 571993359 1 0 572000078 0 0 572004508 0 0 572047513 0 0 572058674

So, how do I get from that to figuring out which key was pressed on the remote?

After a month or so i've reopened this project and played around with it some more. As piglet suggested, I started listening for both high and low signals. The data is still very inconsistent and can't get a stable reading.

(And by the way, thanks for the vote-down piglet, that was greatly appreciated. I wish you could have seen my search history before you decided that i'm ignorant)

I'm going to post my curent code maybe somebody can point out what I'm doing wrong here.

local pin = 4
local prevstate = false
local prevmicro = 0
local prevtime = 0
local count = 0

gpio.mode(pin, gpio.INT)
gpio.trig(pin, "both", function (level, micro)
    --local state = gpio.read(pin)
    local state = level
    if (micro - prevmicro) > 90000 then
        prevmicro = 0
        prevstate = false
        count = 0
        print("\n#", "st", "lv", "microtime", "timing")
    end
    if prevstate ~= state then
        time = math.floor((micro - prevmicro)/100)
        prevstate = state
        prevmicro = micro
        if time > 3 and time < 1000 then
            if prevtime > 80 and prevtime < 100 then
                if time > 17 and time < 25 then
                    print('Repeat')
                elseif time > 40 and time < 50 then
                    print('Start')
                end
            else
                print(count, gpio.read(pin), level, micro, time)
                count = count + 1
            end
            prevtime = time
        end
    end
end)

and here are some sample readouts from pushing the same button:

#   st  lv  microtime   timing
1   1   1   1504559531  16
2   1   0   1504566995  74
3   0   1   1504567523  5
4   1   0   1504573619  60
5   0   1   1504587422  138
6   1   0   1504588011  5
7   1   1   1504604250  162
8   1   0   1504605908  16
9   1   1   1504659929  540
10  1   0   1504662154  22

#   st  lv  microtime   timing
1   1   1   1505483535  16
2   1   0   1505491003  74
3   0   1   1505491558  5
4   1   0   1505497627  60
5   0   1   1505511409  137
6   1   0   1505512023  6
7   1   1   1505518186  61
8   1   0   1505527733  95
9   1   0   1505586167  22
10  1   1   1505586720  5

#   st  lv  microtime   timing
1   1   1   1507990937  16
2   1   0   1507998405  74
3   0   1   1507998934  5
4   1   0   1508005029  60
5   0   1   1508018811  137
6   1   0   1508019424  6
7   1   1   1508035641  162
8   1   0   1508037322  16
9   1   1   1508091345  540
10  1   0   1508093570  22

Solution

  • As it turns out, the Lua code required for this is actually quite simple.

    Where the code above is falling over is actually the print statements. These are extremely expensive and basically, kill your sampling resolution until it's useless.

    You are in essence, writing an interrupt service routine, you have a limited time budget before you have to read the next edge change and if it happens before you are done processing, tough luck! So you need to make the ISR as efficient as you can.

    In the example below, we listen to the "both" edge event, when one occurs, we simply record an indication of which edge and what duration.

    Periodically (using a timer) we print out the contents of the waveform.

    This perfectly matches the waveform on my logic analyzer, you still have the challenge of decoding the signal. Though, there are lots of great protocol docs that explain how to take accurate waveform data and use it to determine the signal being sent. I found that a lot of cheap "brand x" remotes appear to be using the NEC protocol, so this might be a good place to start depending on your project.

    IR transmission because of its nature is not completely error-free so you may get a spurious edge signal from time to time but the code below is pretty stable and runs quite well in isolation, I have yet to test it when the Microcontroller is under more load than just listening for IR.

    It may turn out that using Lua for this purpose is not the best due to the fact that it is an interpreted language (each command issued is parsed and then executed at runtime, this is not at all efficient.) But I will see how far I can get before I decide to write a c module.

    local irpin = 2
    local lastTimestamp = 0
    local waveform = {}
    local i = 1
    
    gpio.mode(irpin,gpio.INT)
    gpio.trig(irpin, "both", function(level, ts)
        onEdge(level, ts)
    end)
    
    function onEdge(level, ts)
        waveform[i] = level
        waveform[i+1] = ts - lastTimestamp
        lastTimestamp = ts
        i = i+2   
    end
    
    -- Print out the waveform
    function showWaveform ()
        if table.getn(waveform) > 65 then
            for k,v in pairs(waveform) do
                print(k,v)
            end
            i = 1;
            waveform = {}
        end
    end
    tmr.alarm(0, 1000, 1, showWaveform)
    
    print("Ready")