Is there any way I can call an instance of a function repeatedly with the function retaining internally the last state of the GPIO pin

I am wanting to monitor some GPIO pins on a Raspberry Pi. Is there any way I can call an instance of a function repeatedly with the function retaining internally the last state of the GPIO pin - so that I can detect Rising and Falling edges. The only way I have found at the moment is to use Global variables, but there must be a more clever way of doing it. I’ve searched - and I can’t find it.
( I thought defining Structures within functions might help - but I have drawn a blank )

many thanks for any advice …

This thread has a few suggestions you might find helpfuL: In Julia, how to create a function that saves its own internal state?

Think this is the simpliest …

GlobalBool = [false, false , false , false, false , false , false]

then the function :-

function IdentifyEdgeOfPress(PB :: Int , GlobalBoolPtr :: Int ) :: Int
bTemp = gpio_read(PB)

if (( bTemp == true ) && ( GlobalBool[GlobalBoolPtr] == false )) ; println("Pressed Edge ") ; end
if (( bTemp == false ) && ( GlobalBool[GlobalBoolPtr] == true )) ; println(“Released Edge”) ; end

global GlobalBool[GlobalBoolPtr] = bTemp
end

then just call the function with a different index ( the indices must be not be repeated otherwise there’ll be cross contamination )

	IdentifyEdgeOfPress(TestPB,1)
	IdentifyEdgeOfPress(TunePB,2)

Bingo - you have a subroutine retaining it’s own state.
( Thought there might be a more elegant ‘Julia’ way of doing this )

Thanks for your response rdeits

You should at the very least make GlobalBool a const otherwise you’ll run into the very first performance tip.

3 Likes

Think I am mis-understanding ‘const’ as in the definition it says :-

In some cases changing the value of a const variable gives a warning instead of an error. However, this can produce unpredictable behavior or corrupt the state of your program, and so should be avoided.

The code is writing to this array to retain the status between polls of the routine - so I don’t see ‘const’ being appropriate.

The adjacent paragraph says also:

Note that “constant-ness” does not extend into mutable containers; only the association between a variable and its value is
constant. If x is an array or dictionary (for example) you can still modify, add, or remove elements.

So you can actually modify the contents of an array:


julia> const global a = [1,2,3]
3-element Vector{Int64}:
 1
 2
 3

julia> a[1] = 3
3

julia> a
3-element Vector{Int64}:
 3
 2
 3

In your case, making the array a const will let your function know that GlobalBool is always an array of type bool, allowing it to produce optimal code. If you don’t make it a const, the compiler cannot know that your array will not change its type at a later time, so it cannot create efficient code. This will affect your performance.

You can see this in the example above. If I try to put something that cannot be converted into an Int into my array, Julia will complain because the variable a must always have a type Array{Int}


julia> a[1] = "toto"
ERROR: MethodError: Cannot `convert` an object of type String to an object of type Int64
Closest candidates are:
  convert(::Type{T}, ::T) where T<:Number at number.jl:6
  convert(::Type{T}, ::Number) where T<:Number at number.jl:7
  convert(::Type{T}, ::Base.TwicePrecision) where T<:Number at twiceprecision.jl:250
  ...
Stacktrace:
 [1] setindex!(A::Vector{Int64}, x::String, i1::Int64)
   @ Base ./array.jl:839
 [2] top-level scope
   @ REPL[18]:1
4 Likes

Thanks Argel ,

That is a very good explanation. I’m coming from Old School and would expect a crash situation putting a variable of a different type into a previousy defined variable.

thank you

Since handling these indices by hand for every call seems really unsafe to me, I’d suggest giving the approaches in the topic rdeits linked a try! Like a functor for each pin you want:

julia> struct Edgedetector
           pinnumber::Int
           state::Ref{Bool}
           Edgedetector(pin, initialstate = false) = new(pin, Ref(initialstate))
       end

julia> function (e::Edgedetector)()
           newstate = getgpio(e.pinnumber)
           if newstate
               if !e.state[] println("Pin $(e.pinnumber): Button pressed!") end
           else
               if e.state[] println("Pin $(e.pinnumber): Button released!") end
           end
           e.state[] = newstate
           return newstate
       end

julia> getgpio(_) = rand(Bool); # mocked up
julia> p1 = Edgedetector(1)
Edgedetector(1, Base.RefValue{Bool}(false))

julia> p1()
false

julia> p1()
Pin 1: Button pressed!
true

julia> p1()
Pin 1: Button released!
false

julia> p1()
Pin 1: Button pressed!
true

Or if you don’t feel like carrying around an object per pin:

julia> let pinstates = Dict{Int,Bool}()
           global detectedge
           function detectedge(pinnumber::Int)
               newstate = getgpio(pinnumber)
               if haskey(pinstates, pinnumber)
                   if newstate
                       if !pinstates[pinnumber] println("Pin $(pinnumber): Button pressed!") end
                   else
                       if pinstates[pinnumber] println("Pin $(pinnumber): Button released!") end
                   end
               end
               pinstates[pinnumber] = newstate
               return newstate
           end
       end
detectedge (generic function with 1 method)

julia> getgpio(_) = rand(Bool)
julia> detectedge(1)
true

julia> detectedge(1)
Pin 1: Button released!
false

julia> detectedge(1)
false

julia> detectedge(1)
false

julia> detectedge(1)
Pin 1: Button pressed!
true

julia> detectedge(2)
false

julia> detectedge(2)
Pin 2: Button pressed!
true
1 Like