0

I'm using the WiringPi gem. This block of code works as expected:

pinNumber = 7
io = WiringPi::GPIO.new do |gpio| 
    gpio.pin_mode(pinNumber, WiringPi::INPUT) 
end 

pin_state = io.digital_read(pinNumber) 
return pin_state

However, when I enclose this in a method so I can make a call using Sinatra, I get the following error when I try to refresh:

wiringPiSetup*: You must only call this once per program run. This is a fatal error. Please fix your code.

Why must this be run only once, and what is the best solution? Ultimately pin_state needs to be retrieved every time I navigate to the root url.

Here's the full code:

require 'wiringpi'
require 'sinatra'

def getstate()
    pinNumber = 7
    io = WiringPi::GPIO.new do |gpio|
      gpio.pin_mode(pinNumber, WiringPi::INPUT)
    end     

    pin_state = io.digital_read(pinNumber)
    return pin_state
end 

get '/' do
    getstate()
end 
Aaron Thomas
  • 5,054
  • 8
  • 43
  • 89

2 Answers2

2

After creating your GPIO instance, you can make repeated calls to read pins from it. Your code is creating a new instance on each call, however.

require 'wiringpi'
require 'sinatra/base'

class MyApp < Sinatra::Base
  InputPin = 7

  IO = WiringPi::GPIO.new do |gpio|
    gpio.pin_mode(InputPin, WiringPi::INPUT)
  end     

  def get_state(pin_number = MyApp::InputPin)
    MyApp::IO.digital_read(pin_number)
  end 

  get '/' do
    get_state
  end
end
coreyward
  • 77,547
  • 20
  • 137
  • 166
  • This demonstrates good use of Ruby idioms. The constant value InputPin is implemented as .... a constant! – zetetic Jan 11 '17 at 18:57
  • running this gives me a 404 error, even if i replace the `get_state` with "hello world". some other boilerplate needed perhaps? – Aaron Thomas Jan 12 '17 at 20:04
  • @AaronThomas You've got to require `sinatra/base` for a modular app instead of `sinatra` which is for top-level apps. Also, you might need to adjust the way you access the `get_state` method with this depending on the scope. Sinatra does some hoop jumping that gets confusing. When in doubt, [check the docs](http://www.sinatrarb.com/intro.html). – coreyward Jan 12 '17 at 21:21
1
pinNumber = 7

def io
    @io ||= begin
        WiringPi::GPIO.new do |gpio|
          gpio.pin_mode(pinNumber, WiringPi::INPUT)
        end
    end
end

def getstate()
    pin_state = io.digital_read(pinNumber)
    return pin_state
end 

get '/' do
    getstate() end

Initialize the API once when your application starts (outside getState() method). You're initializing the API multiple times, thus the error.

myfashionhub
  • 427
  • 3
  • 11
  • Thanks, but unfortunately when I move that portion of the code outside of the method, I get "undefined local variable or method `io' for #" when I navigate to the url the first time. – Aaron Thomas Jan 10 '17 at 22:02
  • @AaronThomas you need to cache the `io` object outside the method. Initialize the object and save it to a variable, then use the variable inside the `getstate` method. Move the part that is in the `do .. end` block up into the body of the `getstate` method. – zetetic Jan 11 '17 at 03:03
  • @zetetic I'm not following - could you give an example of what you have in mind? – Aaron Thomas Jan 11 '17 at 03:18
  • @AaronThomas See edited code. ||= is memoization. It basically caches the value of io so that the initialization code block doesn't run every single time. This is assuming pinNumber doesn't change every time? – myfashionhub Jan 11 '17 at 17:32
  • 1
    `def` statements are scope boundaries, so `pinNumber` is not in-scope inside of `getstate`. – coreyward Jan 11 '17 at 17:45