toggle each button push?

Charlez

Member
Joined
Oct 10, 2009
Messages
5
Programming Experience
Beginner
Hi,

I'm making kind of home autmation system with a usb interface card.
8 digital & 8 analog inputs and 8 digital & 8 analog outputs.

The status of the inputs is refreshed with a timer.tick and written to variable.
Now the problem.

I want to make an digital output active when a button(real button, not a switch) is pushed and when it is pushed again the output should be set to non active.

action() is called every timer.tick

StatusDigInput1 = status of digital input =true when pushing button
StatusDigOutput1 = status used for displaying output status
SetDigitalChannel(Card1, 1) = sets digital output 1 of card1 to enabled
ClearDigitalChannel(Card1, 1) = sets digital output 1 of card1 to disabled


I've written something like this

VB.NET:
Private Sub action()
        If (StatusDigInput1 = True) And (StatusDigOutput1 = False) Then
            SetDigitalChannel(Card1, 1)
            StatusDigOutput1 = True
        End If
        If (StatusDigInput1 = True) And (StatusDigOutput1 = True) = True Then
            StatusDigOutput1 = False
            ClearDigitalChannel(Card1, 1)
        End If

End SubProblem is that the status of the inputs is updated so fast that i get a flashing output as long the input is active.
 
I'n not sure I'm understanding this correctly but I'm assuming that you mean the timer event is firing so fast that it is calling your Action sub multiple times. If thats the case then I would suggest that within your timer event, you disable the timer until after your called subs have ran and then re-activate it.
 
Use a Boolean variable declared with Static, so the value stays in memory after the sub ends. Alternatively, you can declare the variable at Class level instead of locally in the sub.

VB.NET:
Static onoff As Boolean
onoff = StatusDigInput1        
onoff = Not onoff
If onoff = False Then
      SetDigitalChannel(Card1, 1)
Else
      ClearDigitalChanneel(Card1, 1)
End If
 
As Solitaire said, you will need to hold all the values of the PLC in memory to be able to display them properly. The reason for this is that you can then change one value individually and refresh the screen, before the value is subsequently retrieved from the PLC.

Secondly, dont retrieve the values using a timer on the main thread - retrieve them using a BackgroundWorker so that the UI thread is not blocked. Use the Timer to redisplay the retrieved values as often as you need to.

If your form is still flashing, add this to your form :-

VB.NET:
Me.SetStyle(ControlStyles.OptimizedDoubleBuffer, True)

Remember to use the MouseUp event and not the MouseDown event for buttons to avoid your outputs going haywire!

You could also make your life easier by writing a new sub (or even overload your SetDigitalChannel sub) to accept the output value you want set.

VB.NET:
SetDigitalChannel(Card1, 1, True)
SetDigitalChannel(Card1, 2, False)


VB.NET:
Private Sub Button1_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Button1.MouseUp
    StatusDigInput1 = Not StatusDigInput1
    SetDigitalChannel(Card1, 1, StatusDigInput1)
End Sub

Dont forget that you can always store 8 bits as 1 byte, which also makes displaying/reading boolean values much simpler :)
 
I'n not sure I'm understanding this correctly but I'm assuming that you mean the timer event is firing so fast that it is calling your Action sub multiple times. If thats the case then I would suggest that within your timer event, you disable the timer until after your called subs have ran and then re-activate it.

That's correct, stopping the timer would stop refreshing the status of the other inputs, so when they change during the timer stop, you wouldn't know.

@InertiaM

Actually the values of the inputs are retreived from the card and stored in an array, then i put them into booleans at class level.
My form isn't flashing, its the output of the card that is.

You're giving an example with mouse.button, but i'm not using buttons on my form but real buttons that are attached to the interface card.
 
Last edited:
That's correct, stopping the timer would stop refreshing the status of the other inputs, so when they change during the timer stop, you wouldn't know.

That's why you should split the reading out onto a different thread, so that the values always get read.


You're giving an example with mouse.button, but i'm not using buttons on my form but real buttons that are attached to the interface card.

Then check your logic as it's your action sub that's wrong.

VB.NET:
Private Sub action()
        If (StatusDigInput1 = True) And (StatusDigOutput1 = False) Then
            SetDigitalChannel(Card1, 1)
            StatusDigOutput1 = True
        End If
        If (StatusDigInput1 = [B][COLOR="Red"]TRUE[/COLOR][/B]) And (StatusDigOutput1 = True) = True Then
            StatusDigOutput1 = False
            ClearDigitalChannel(Card1, 1)
        End If
End Sub

If you just want a direct "Input on gives output on", overload your SetDigitalChannel as I suggested earlier :-

VB.NET:
Private Sub action()
    SetDigitalChannel(Card1, 1, StatusDigInput1)
End Sub
 
I think i didn't explain myself clear enough.

The button is a N.O. push button (Normally Open).

What i want to do is.

button is pushed => output active
button is released => output stay's active
button is pushed => output set to non active
button is released => output stay's at non active
button is pushed => output active

So when you push the button when the output is non active it should make the output active
and
when you push the button when the output is active is should make it non active.

Spend already hours of trying al kinds of things.
 
Always helps to have all the information :)

What you need to do is only process the code on the rising edge of StatusDigInput1 (ie when it changes from False to True). In other words, you need to keep track of StatusDigInput1 as well as StatusDigOutput1.

VB.NET:
Private Last_StatusDigInput1_State as boolean = False

Private Sub action()
    If Last_StatusDigInput1_State <> StatusDigInput1 then
        'StatusDigInput1 has changed

        if StatusDigInput1 = True then
            'process on rising edge of StatusDigInput1
            if StatusDigOutput1 = False Then
                SetDigitalChannel(Card1, 1)
                StatusDigOutput1 = True
            Else
                StatusDigOutput1 = False
                ClearDigitalChannel(Card1, 1)
            End If
        End If

        'save state of StatusDigInput1 into Last_StatusDigInput1_State
        Last_StatusDigInput1_State = StatusDigInput1
    End If
End Sub
 
thank you very much !
your code is working perfectly.
And if you think about it, very simple.
Why i din't think about it :)
 
Back
Top