nodemcu / nodemcu-firmware

Lua based interactive firmware for ESP8266, ESP8285 and ESP32
https://nodemcu.readthedocs.io
MIT License
7.61k stars 3.12k forks source link

uart.on('data', ... not firing without \r\n (0x0d 0x0a) #1467

Closed ameeuw closed 7 years ago

ameeuw commented 7 years ago

Intro

I am currently writing drivers for indoor air quality sensors (CO2 and particulate matter) in lua for the NodeMCU firmware.

The sensors are connected via the alternative UART pins GPIO13/15. On issuing a measure command, the ESP switches uart.alt(1) and registers a uart.on("data", 9, ...) function for firing after nine bytes are received. I have tested this with two ch340 connected to native and alternative UART pins. The reading of the values is fine if i manually enter the data and add \r\n (0d 0a).

However my used sensors do not have \r\n at the end of their replies - how can I change my code to read out the UART buffer after 9 bytes received?

Expected behavior

uart.on('data', 9, function(data) print(data) end) firing after 9 bytes of received UART data.

Actual behavior

uart.on('data', 9, function(data) print(data) end) only fires after 9 bytes + \r\n

Test code

Here is the particular part of my code:

function MHZ19:measure(callback)
-- timeout and restore UART
tmr.alarm(self.timer, self.timeout*1000, 0,
function()
    uart.alt(0)
    uart.setup(0, 115200, 8, uart.PARITY_NONE, uart.STOPBITS_1, 1)
    uart.on('data')
    print("Timed out. Restored UART.")
    callback(nil)
end)

uart.on('data', 9,
    function(data)
        -- unregister uart.on callback
        uart.on('data')
        tmr.stop(self.timer)
        uart.alt(0)
        uart.setup(0, 115200, 8, uart.PARITY_NONE, uart.STOPBITS_1, 1)
        -- First two bytes are control bytes 0xFF && 0x86
        local a,b = string.byte(data,1,2)
        if (a==tonumber('FF',16)) and (b==tonumber('86',16)) then
            local high,low = string.byte(data,3,4)
            local co2value = high * 256 + low
            callback(co2value)
        else
            callback(nil)
        end
    end)

    uart.alt(self.altUart)
    uart.setup(0, 9600, 8, uart.PARITY_NONE, uart.STOPBITS_1, 0)

    -- Send request sequence to get value (refer to datasheet)
    -- send: FF 01 86 00 00 00 00 00 79
    -- receive: FF 86 02 E8 42 04 2B 1C 03
    uart.write(0, 0xff, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79)
end

NodeMCU version

1.5.1

Hardware

ESP12F Witty Boards & MH-Z19 CO2 sensor

marcelstoer commented 7 years ago

Not sure whether you're suggesting this be a bug as you also posted on Stack Overflow at http://stackoverflow.com/q/39095456/131929. Potentially related to #1012.

ameeuw commented 7 years ago

Not sure whether its a bug or a feature. I would expect a function to explicitly read out the UART buffer when a certain amount of bytes are available, not just after \r\n has been received.

ameeuw commented 7 years ago

Problem solved: uart.on takes an additional parameter whether to only process input after \r or \n, or to listen the amount of bytes. Thank you Marcel.

marcelstoer commented 7 years ago

Great you got it solved but I don't understand. Your initial code already does use the number-of-characters variant (uart.on('data', 9,) rather than the stop-char variant (uart.on('data', '\r', or so).

ymxcc commented 7 years ago

Hi, everyboday!

Now I am facing a very hard problem, even make the whole day confusion on my mind, as folllows:

I want to read CO2 and PM2.5 value from sensors which are connected with UART. But I found that the received data were sometimes right and sometimes wrong, e.g. PM2.5 received data: 22 17 11 0 0 2 245 0 0 2 249 0 0 2 240 2 0 0 3 229 In my code, I just judged the existing "22" in the received data, then the pm2.5 could be calculated using the formulation. However, the data absolutely were wrong, PM2.5 = 757. The correct data were: received data: 22 17 11 0 0 0 28 0 0 0 28 0 0 0 25 0 0 0 3 122, the reasonable value is PM2.5 = 28. The bold numbers above made a big difference in some time, especially I downloaded the code into me device with plugging off the USB, making it operation under the battery.

Maybe some guys could help me to find my code where I made a big mistake to make it happen.

`function measure(num, switch)

local tem = switch uart.on("data", num, function(data) switch = 0 uart.on("data") uart.alt(0) uart.setup(0, 115200, 8, uart.PARITY_NONE, uart.STOPBITS_1, 1)

        print("receive from uart:", data)
        print("received data:", string.byte(data, 1, num))
        print("tem", tem)
        if num == 20 then
        pm25_i = 1
        while(pm25_i < num)
        do
            pm25_a = string.byte(data, pm25_i)
            if pm25_a == tonumber('16', 16) then
                pm25_b = tonumber(string.byte(data, pm25_i - 1 + 7), '10') --+ string.byte(data,5) * math.pow(2,8) + string.byte(data,4) * * math.pow(2,16) + string.byte(data,5) * * math.pow(2,24)
                pm25_c = tonumber(string.byte(data, pm25_i - 1 + 6), '10')
                pm25_e = tonumber(string.byte(data, pm25_i - 1 + 5), '10')
                pm25_f = tonumber(string.byte(data, pm25_i - 1 + 4), '10')
                qiaqia_pm25 = pm25_b + bit.lshift(pm25_c, 8) + bit.lshift(pm25_e, 16) + bit.lshift(pm25_f, 24)
                m:valuechange("4", qiaqia_pm25)
                print(qiaqia_pm25)
                break
            end
            pm25_i = pm25_i + 1
        end
        end

        if num == 9 then
        co2_i = 1
        while(co2_i < num)
        do
            co2_a = string.byte(data, co2_i)
            co2_b = string.byte(data, co2_i + 1)
            if co2_a == tonumber('ff', 16) and co2_b == tonumber('86', 16) then
                co2_c = tonumber(string.byte(data, co2_i + 2), '10')
                co2_d = tonumber(string.byte(data, co2_i + 1), '10')
                qiaqia_co2 = bit.lshift(co2_c, 8) + co2_d
                m:valuechange("3", qiaqia_co2)
                print(qiaqia_co2)
                break
            end
            co2_i = co2_i + 1
        end
        end

end, 0)

if switch == 1 then
uart.alt(1)
uart.setup(0, 9600, 8, uart.PARITY_NONE, uart.STOPBITS_1, 0)
pin = 10
gpio.mode(pin,gpio.OUTPUT)
gpio.write(pin,gpio.LOW)
uart.write(0, 0xff, 0x01, 0x86, 0, 0, 0, 0, 0, 0x79)
end

if switch == 2 then
uart.alt(1)
uart.setup(0, 9600, 8, uart.PARITY_NONE, uart.STOPBITS_1, 0)
pin = 10
gpio.mode(pin,gpio.OUTPUT)
gpio.write(pin,gpio.HIGH)
uart.write(0, 0x11, 0x02, 0x0B, 0x01, 0xE1)
end

end awp = 1 tmr.alarm(1, 2000, 1, function() if awp == 1 then
measure(9, 1) awp = 0 elseif awp == 0 then measure(20, 2) awp = 1 end end)`

Thanks so much!