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 )
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.
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
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.
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
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)