nodemcu / nodemcu-firmware

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

Bit banging not possible (?) -- too slow reading GPIO #612

Closed romanchyla closed 9 years ago

romanchyla commented 9 years ago

Hi, I need to read HX711 ADC - here is a code that i'm using, but it seems like it is too slow - I'm missing bits and zeroes. When I try to print delta (tmr.now() - previous_time) it is always longer than 50us. And according to the HX711 manual, that is the maximum length of the signal.

Can this be done using Lua? Or is there a tutorial on how to write a C library (to extend nodemcu-firmware? Or any plans to provide generic, C method for reading just the gpios?)

Here is the code I use:

DOUT = 6;
SCLK = 5;

gpio_mode = gpio.mode;
gpio_read = gpio.read;
gpio_write = gpio.write;

INPUT = gpio.INPUT;
OUTPUT = gpio.OUTPUT;
HIGH = gpio.HIGH;
LOW = gpio.LOW;
SCALE = 1;
OFFSET = 0;
GAIN = 1;

function is_ready()
    return gpio_read(DOUT) == LOW;
end

function set_gain(b)
    if b == 128 then
        GAIN = 1;
    elseif b == 64 then 
        GAIN = 2;
    elseif b == 32 then
        GAIN = 3;
    end
    gpio_write(SCLK, LOW);
end

-- read data from the sensor
function read()

  local bytes = {}
  local bitStream = {}
  for j=1,24,1 do
      bitStream[j] = 0
  end

  while (not is_ready()) do end

  for i = 24, 1, -1 do
    gpio_write(5, 1)
    bitStream[i] = gpio_read(6)
    gpio_write(5, 0)    
  end

  -- set thee gain for the next reading
  for x = 1, GAIN do
     gpio_write(5, 1)
     gpio_write(5, 0)
  end

  print(table.concat(bitStream, ""))

  local bitS = {}
  for j = 0, 2 do
    base = j * 8;
    for k=1,8 do
        bitS[k] = bitStream[base+k]
    end
    --print(table.concat(bitS, ""))
    bytes[j] = table.concat(bitS) -- tonumber(table.concat(bitS, ""), 2)    
  end

  bytes[2] = bit.bxor(bytes[2], 0x80)
  bytes[2] = bit.lshift(bytes[2], 16)
  bytes[1] = bit.lshift(bytes[1], 8)

  return bit.bor(bit.bor(bytes[2], bytes[1]), bytes[0])

end

function read_average(num_try)
    s = 0
    print (num_try)
    for i=1, num_try do
        s = s + read()
    end
    return s / num_try
end

function get_value(times)
    return read_average(times) - OFFSET;
end

function set_offset(off)
    OFFSET = off;
end

function set_scale(scale)
    SCALE = scale
end

function tare(times)
    set_offset(read_average(times))
end

function get_units(times)
    return get_value(times) / SCALE;
end

pulse1 = 0
du = 0

gpio_mode(DOUT, INPUT);
gpio_mode(SCLK, OUTPUT);

function pin1cb(level)
     du = tmr.now() - pulse1
     print(du)
     pulse1 = tmr.now()
     if level == 1 then gpio.trig(SCLK, "down ") else gpio.trig(SCLK, "up ") end
    end
    gpio.trig(SCLK, "down ",pin1cb)

set_gain(128);
TerryE commented 9 years ago

@romanchyla, Hi Roman, this issues list is for reporting bugs in the nodeMCU firmware or specific enhancement requests. Your Q is more of an application techniques issue. As to how to write a a C library to extend the firmware, this is reasonably documented (as far as standard Lua) goes in PiL and the open source for the firmware gives examples. The firmware is simply overlaid the standard esp8266 SDK, so if you want to access this directly via your own C then you need to refer to the SDK documentation.

Also have a read through my Unofficial FAQ, which gives help and links. I could give specific hint, but I've covered most in the FAQ which is why I wrote it after all! Beyond that and you need to refer to one of the application developer forums such as StackOverflow or esp8266.com.

dvv commented 9 years ago

@romanchyla as a last resort try to pre-allocate (rather) big tables: local bitStream = {0, 0, 22-times-more-0} for Lua not to perform table expansion steps (which may involve sudden memory allocation and/or garbage collection, afaik). Or make preallocated table an upvalue for read(). See here https://github.com/dvv/nodemcu-thingies/blob/master/dht22.lua#L26-L28

yanekm commented 9 years ago

I need to read HX711 ADC - here is a code that i'm using, but it seems like it is too slow - I'm missing bits and zeroes. When I try to print delta (tmr.now() - previous_time) it is always longer than 50us. And according to the HX711 manual, that is the maximum length of the signal...

Have you tried using interrupts?

romanchyla commented 9 years ago

thank you for your suggestions, I have tried using interrupts, unfortunately without success (please see below) - I'll try with the large table too. It would be great to have some low level c function for reading gpio - if nothing works, i'll try to submit something

here is the interrupt code (even with removed print statements, it was too slow - ~200us per interrupt) - is that to be expected?

DOUT = 6
SCLK = 5

gpio.write(SCLK, gpio.LOW)

gpio.mode(SCLK, gpio.OUTPUT)
gpio.mode(DOUT, gpio.INT, gpio.PULLUP)

gpio_write = gpio.write
gpio_read = gpio.read
gpio_trig = gpio.trig

bitStream = {}
timing = {}

delay = 1
i = 0
pulse = tmr.now()
function readhx(level)
    print(" i" .. i .. " " .. level .. " : " .. tmr.now() - pulse)
    pulse = tmr.now()
end
gpio_trig(DOUT, "high",readhx)

while (gpio_read(DOUT) == gpio.HIGH) do end
print("OK")

for x=1,25, 1 do
print ("i=" .. x)
i = i + 1
gpio_write(SCLK, gpio.HIGH)
gpio_write(SCLK, gpio.LOW)
end
romanchyla commented 9 years ago

@TerryE Hi Terry, I've read your terrific FAQ, unfortunately, I'm not familiar with C and the FAQ is geared towards "how to use lua well". As beginner, I am having difficulties with just where to start. I'll find my way around it, but if you have some pointers (it doesn't have to be: extend nodemcu firmware for dummies) it would kickstart me.

Thanks to everybody! This community is very nice, it is a lot of learning for beginners, but I like it!

romanchyla commented 9 years ago

sorry, this was the real code with interrupts

DOUT = 6
SCLK = 5

gpio.write(SCLK, gpio.LOW)

gpio.mode(SCLK, gpio.INT,gpio.PULLDOWN)
gpio.mode(DOUT, gpio.INPUT)

delay = 25
i = 0
function readhx(level)
    print(i .. " level: " .. level)
    if level == 1 then
        i=i+1
        if i < 25 then
            gpio.trig(SCLK, "down", readhx)     
            tmr.alarm(0, delay, 0, 
            function() 
                print("readout " .. gpio.read(DOUT))
                gpio.write(SCLK, gpio.LOW)
            end )

        end
    else
        gpio.trig(SCLK, "up", readhx)     
        tmr.alarm(0, delay, 0, 
            function() 
                gpio.write(SCLK, gpio.HIGH)
            end )
    end

end
gpio.trig(SCLK, "both",readhx)

while (gpio.read(DOUT) == gpio.HIGH) do end
print('ola')
gpio.write(SCLK, gpio.HIGH)
gpio.write(SCLK, gpio.LOW)
romanchyla commented 9 years ago

I tried varying the delay (and do without - but it seems interrupts are called at ~200µs intervals)

romanchyla commented 9 years ago

for reference, i've solved my problem using c: https://github.com/romanchyla/nodemcu-firmware/blob/hx711/app/modules/easygpio.c

TerryE commented 9 years ago

Roman, remember the 10mS caveat that Espressif advises. If you bit-bang for too long, then you will probably mess up the wifi and TCP stack, so you may need to reinitialise your wifi connection and any TCP link after you've done. I discuss this in more detail in my Unofficial FAQ.

romanchyla commented 9 years ago

Thanks @TerryE, I thought that 10mS was milliseconds, is it microseconds? I'm running fine, without restarts, have seen dht code sleeping for 40us

TerryE commented 9 years ago

Yes I meant 10 milliseconds -- that's the time from your lua callback (e.g. a timer alarm firing) until the routine returns control the the Lua firmware. You can happily run if you take longer and the UART will still work. It when you then try to pass info back using the wifi that you might start posting issues like "Why does my wfii keep dropping? ...

HonzaKortus commented 8 years ago

Hello, i had same problem. Point is, that CLK pulse has to be shorten, that 50us. I archieve it with trick using PWM:

gpio.mode(data, gpio.INPUT)
pwm.setup(clk,1000, 1023) pwm.start(clk)
function read_HX711 ()
  local out=0 
  pwm.setduty(clk, 0)
  tmr.delay(600000)
  for i=1,24 do 
    pwm.setduty(clk, 10) tmr.delay(500) pwm.setduty(clk, 0)--short pulse
    if gpio.read(data)==1 then out=bit.set(out,24-i) end 
  end
  pwm.setduty(clk, 1023)
return (out) end
romanchyla commented 8 years ago

thank you for sharing! I didn't realize it could be done with pwm (I'm learning)

for my project, i am taking 10 measurements and averaging the results; without any problems so far (but also the readings are made with 1us interrupts; if you try to do the same maybe you will start seeing restarts)