How to use 2x VM110N simultaneously with VB.NET 2012

Bonjour à tou(te)s,
[color=#008000]Hello Everybody,[/color]

Je vais poster en Français et en Anglais (en espérant une réponse en Français ;-)).
[color=#008000]I’m posting in French and English language (i hope a french answer ;-)).[/color]

Voilà, je possède 2 cartes VM110N que je souhaite utiliser simultanément sous VB.NET 2012.
Pour mon application, je n’ai besoin que des 10 entrées numériques (2x5).
[color=#008000]So, i have 2 VM110N Cards that i would like to use simultaneously Under VB.NET 2012.
For my app, i need only 10 digital in (2x5).[/color]

Chacune des 10 entrées est reliée à un contact NO différent, et je souhaite déclencher un événement spécifique en fonction du contact sollicité.
[color=#008000]Each of 10 D-IN is linked with a particular NO contact, and i would like to start a specific event relative to the activated contact.[/color]

Mon système fonctionne très bien avec une seule carte, mais je ne parviens pas à utiliser 2 cartes simultanément.
[color=#008000]Actually, my app is OK with 1x VM110N Card (5 D-IN), bycons i’m blocked with use of 2 cards simultaneously.[/color]

J’ai paramétré mes VM110N comme suit :

  • Carte Adresse 0 (contacts 1 à 5) (Cavalier SK5 ON et Cavalier SK6 ON)
  • Carte Adresse 1 (contacts 6 à 10) (Cavalier SK5 OFF et Cavalier SK6 ON)
    [color=#008000]I’ve setting my VM110N cards like this :
  • Card address 0 (contacts 1 to 5) (SK5 ON and SK6 ON on the board)
  • Card address 1 (contacts 6 to 10) (SK5 OFF and SK6 ON on the board)[/color]

Mes 2 cartes sont bien vues (je suis sous win7 64 bits et la dll est installée dans SYSWOW64).
[color=#008000]My 2 cards are recognized (i’m on win7 x64 and the dll is located in SYSWOW64).[/color]

Dans l’interface de démo, je passe bien d’une carte à l’autre, mais je ne vois pas comment utiliser les 2 cartes simultanément pour “écouter” mes 10 contacts sans avoir à basculer manuellement d’une carte à l’autre.
[color=#008000]In Demo Interface, i can switch between the 2 cards, but i don’t see how to use simultaneously to listening all 10 Contacts without manual switch between cards.[/color]

Pouvez-vous m’éclairer là-dessus, svp ?
[color=#008000]Can you help me, please ?[/color]

Merci d’avance.
[color=#008000]Thanks in advance.[/color]

PROISIS

Sorry, I haven’t used french in over 30 years and (regrettably) forgot pretty much all of it.

Use OpenDevice() two times to open both cards. Then use SetCurrentDevice() to select the “current” card for subsequent IO operations.

Regards,
Jan

Hello Mostlly Harmless :wink:

Thanks for your answer.

Ok, so, i f I use OpenDevice(0) and OpenDevice(1), the 2 cards are connected, but my problem is only i wan’t to listen simultaneously the 2 cards and not to SetCurrentDevice to listen alternatively the 2 cards, for 2 reasons :

  • I don’t know before wich of 10 buttons (contact NO - Digital IN) will be pressed.
  • It’s possible the 10 buttons was pressed approximatively at the same time and i must catch the more faster.

I don’t want to indicate wich card must be listened, i would like the 2 cards are listened without manual intervention. :wink:

Kind Regards
PROISIS

[quote=“MostlyHarmless”]proisis wrote:

In Demo Interface, i can switch between the 2 cards, but i don’t see how to use simultaneously to listening all 10 Contacts without manual switch between cards.

Use OpenDevice() two times to open both cards. Then use SetCurrentDevice() to select the “current” card for subsequent IO operations.[/quote]

For my App, i’ve re-used and modify demo code…
Under the Connect Button :

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim h1 As Integer = OpenDevice(0)
        Dim h2 As Integer = OpenDevice(1)

        If h1 = 0 Then
            LabelCard1.Text = "Card 1 connected"
            Timer1.Enabled = True
        Else
            LabelCard1.Text = "Card 1 not found"
        End If
        If h2 = 1 Then
            LabelCard2.Text = "Card 2 connected"
            Timer2.Enabled = True
        Else
            LabelCard2.Text = "Card 2 not found"
        End If


    End Sub

But, how to use SetCurrentDevice() with the 2 cards connected to catch simultaneously all of 10 digital-in ?
If I use a timer to make a SetCurrentDevice() alternating on each card, it may not function properly and mostly it seems to me not super clean … No?

Thanks for your help.

Regards.
PROISIS

You cannot receive all 10 buttons simultaneously in one call using the original K8055D.DLL.

First of all, the two cards are two separate USB devices, so their HID messages will always arrive separately. Second there are speed limitations dictated by the USB standard. Third the K8055D.DLL is not designed to operate on multiple cards simultaneously, only one at a time (via SetCurrentDevice()).

I can help you creating a custom DLL that can operate thread safe on multiple cards in a multi-threaded application in parallel (one background worker per card). But I am not sure that will be good enough since no matter how we code it, you cannot receive the input states from each card faster than every 2 milliseconds. So this solution will never be able to really determine the order of input pulses that happened closer than 2ms apart. That isn’t even possible with a single card.

That said, 2ms is still 5 times faster than 1/100th of a second. In many sports less than 1/100th is considered a tie. In many technical applications, 1/100th of a second is a long time. Can you tell us what the actual problem is, you are trying to solve, or at least what the timing constraints are? It always helps Software Engineers like me to know that. For example, if you tell me you need to capture a finish line with 10 slots to 1/100th of a second, I can come up with a solution using two VM110N boards. If you tell me that you need the same but at 1/1000th of a second, I can still come up with a solution, but not one that contains a VM110N.

Regards,
Jan

[quote=“proisis”]But, how to use SetCurrentDevice() with the 2 cards connected to catch simultaneously all of 10 digital-in ?
If I use a timer to make a SetCurrentDevice() alternating on each card, it may not function properly and mostly it seems to me not super clean … No?[/quote]

Regardless of my previous post, how do you currently “read” the input states? The code you quoted only shows how you open the two devices. The original demo application does it in a 50ms timer routine. If that is fast enough for you, then that same timer routine could do SetCurrentDevice(0), ReadAllDigital(), SetCurrentDevice(1), ReadAllDigital(). No need for a second timer.

[quote]But, how to use SetCurrentDevice() with the 2 cards connected to catch simultaneously all of 10 digital-in ?[/quote]For more details how to use SetCurrentDevice() please see:
[forum.vellemanprojects.eu/t/k8055n-questions/6668/1)
Here are some code snippets and speed test results for the K8055N:
[forum.vellemanprojects.eu/t/vm110-versus-vm110n-k8055-vs-k8055n/6391/1)

[quote=“VEL255”]Here are some code snippets and speed test results for the K8055N:
[forum.vellemanprojects.eu/t/vm110-versus-vm110n-k8055-vs-k8055n/6391/1)[/quote]
The 4ms delay when copying a digital input signal from one card to a digital output of another seems to be entirely caused by USB IO timing.

I have recreated VEL255’s test setup from the referenced topic. A 10Hz input signal to one card, measuring that input and the second card’s corresponding output signal with an oscilloscope. However, the test setup was slightly different:

[ul][li]The cards used ran the Open8055 firmware, which doesn’t require a “request” packet to send input state changes.[/li]
[li]The program on the PC was a Python script instead of a C program.[/li]
[li]The Python script communicated with another Python process (open8055server) via TCP/IP sockets, instead of accessing the cards directly.[/li]
[li]The open8055server accessed the cards via libusb 1.0 instead of using the Velleman K8055D.DLL.[/li]
[li]The whole thing ran on a CentOS 6.4 Linux system instead of Windows.[/li]
[li]The computer used was a rather slow Netbook (Atom N280 @1.66 GHz)[/li][/ul]

This is the result:

A steady 4ms delay (I checked it at a higher sampling speed). Linux doesn’t seem to have those up to 10ms spikes, VEL255 experienced under Windows. Then again, that computer wasn’t doing anything else at the moment, so the previous statement would need some serious testing to back it up.

Keep in mind that the open8055server is multi-threaded and operates on all used cards in separate threads. It appears that all USB communication however gets serialized again further down in the system. It might make a difference if the two cards get connected to different USB root hubs, but that is pure speculation at this time.

The fact that remains seems to be that we cannot get input messages faster than every 2ms. I think this applies to all Full Speed USB HID devices, not just the K8055N and Open8055.

First, Thanks to all :wink:

[quote=“MostlyHarmless”]You cannot receive all 10 buttons simultaneously in one call using the original K8055D.DLL.

First of all, the two cards are two separate USB devices, so their HID messages will always arrive separately. Second there are speed limitations dictated by the USB standard. Third the K8055D.DLL is not designed to operate on multiple cards simultaneously, only one at a time (via SetCurrentDevice()).

I can help you creating a custom DLL that can operate thread safe on multiple cards in a multi-threaded application in parallel (one background worker per card). But I am not sure that will be good enough since no matter how we code it, you cannot receive the input states from each card faster than every 2 milliseconds. So this solution will never be able to really determine the order of input pulses that happened closer than 2ms apart. That isn’t even possible with a single card.

That said, 2ms is still 5 times faster than 1/100th of a second. In many sports less than 1/100th is considered a tie. In many technical applications, 1/100th of a second is a long time. Can you tell us what the actual problem is, you are trying to solve, or at least what the timing constraints are? It always helps Software Engineers like me to know that. For example, if you tell me you need to capture a finish line with 10 slots to 1/100th of a second, I can come up with a solution using two VM110N boards. If you tell me that you need the same but at 1/1000th of a second, I can still come up with a solution, but not one that contains a VM110N.

Regards,
Jan[/quote]

So, Dear Jan, i explain to you :

  • Actually, i’m using a single VM110N card. I’ve 5 buzzers buttons (1 person by buzzer).
    When I give the “go”, the five people hit buzzers and I have to detect the fastest and trigger the event which corresponds to the buzzer.

I started with the demo version without changing the acquisition time, I apply a “SetCounterDebounceTime ()” 2ms to avoid rebound effects. All this works fine.

  • Now I want to do the same thing by doubling the number of buzzers, so 10 instead of 5, so I need two cards running in simultaneous.

Of course, the more time polling will be short, I would be more accurate to determine the fastest, but I think 2ms is more than enough (or even too much).

So if you can help me to create a multithreaded operation, that would be great :-).
(Ps: My Dev is VB.Net 2012)

Many Thanks in Advance (to all).

PROISIS

VEL255, I see your location in Belgium, do you speak French ? :slight_smile:

The debounce configuration has nothing to do with this problem. It is only used for the firmware maintained counters. I don’t think you are using them.

I also do not think that going multithreaded would help in this case. As shown in my previous post, the USB subsystem might serialize the IO anyway.

If you are working from the original VB example code, then the Timer1 interval is 50ms. I presume that you have removed everything but the i=ReadAllDigital from the Timer1_Tick subroutine, and then added your code to it that reacts to the button presses with whatever you want to happen.

If all that is right so far, then you probably want to reduce that interval. With a 50ms timer, your program cannot tell if the button was pressed 49ms or 1ms ago. It only knows that the input is ON now and was OFF 50ms ago. For this type of application I would think 10ms timer interval is more appropriate.

The following code inside of the Timer1_Tick subroutine will get you the states of all 10 digital inputs. The ones of card1 will be read 2ms later than the ones of card0. This difference should be tolerable, assuming that anything within the same 10ms (if you change the Timer1 interval to that) is a tie anyway.

SetCurrentDevice(0) i_card0 = ReadAllDigital SetCurrentDevice(1) i_card1 = ReadAllDigital

Regards,
Jan

[quote]VEL255, I see your location in Belgium, do you speak French ? :-)[/quote]I’m very sorry, I can’t speak enough French to reply :slight_smile:

Many thank you Jan
It’s OK, it works :slight_smile:
It was so simple, and I had not picked it enough to position myself in the SetCurrentDevice tick timer existing … On rereading, I realized that you had already given me the answer discouraging me to use another timer …
It always looks more complicated than necessary;-)

Thank you all.

Maybe one last question:

  • In the demo version, it appears 2 counters to indicate the number of hits.
    I modified to display 10 counters and I modified the code as follows:
    Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
        Dim i_card0 As Integer
        Dim i_card1 As Integer
        Timer1.Enabled = False
        SetCurrentDevice(0)
        i_card0 = ReadAllDigital
        CheckBox1.Checked = i_card0 And 1
        CheckBox2.Checked = (i_card0 >> 1) And 1
        CheckBox3.Checked = (i_card0 >> 2) And 1
        CheckBox4.Checked = (i_card0 >> 3) And 1
        CheckBox5.Checked = (i_card0 >> 4) And 1
        TextBoxC1.Text = CStr(ReadCounter(1))
        TextBoxC2.Text = CStr(ReadCounter(2))
        TextBoxC3.Text = CStr(ReadCounter(3))
        TextBoxC4.Text = CStr(ReadCounter(4))
        TextBoxC5.Text = CStr(ReadCounter(5))
        SetCurrentDevice(1)
        i_card1 = ReadAllDigital
        CheckBox6.Checked = i_card1 And 1
        CheckBox7.Checked = (i_card1 >> 1) And 1
        CheckBox8.Checked = (i_card1 >> 2) And 1
        CheckBox9.Checked = (i_card1 >> 3) And 1
        CheckBox10.Checked = (i_card1 >> 4) And 1
        TextBoxC6.Text = CStr(ReadCounter(6))
        TextBoxC7.Text = CStr(ReadCounter(7))
        TextBoxC8.Text = CStr(ReadCounter(8))
        TextBoxC9.Text = CStr(ReadCounter(9))
        TextBoxC10.Text = CStr(ReadCounter(10))

        Timer1.Enabled = True
    End Sub

But it’s not functionnal…

I’ve tried to do the same like this :

        ...        
        SetCurrentDevice(1)
        i_card1 = ReadAllDigital
        CheckBox6.Checked = i_card1 And 1
        CheckBox7.Checked = (i_card1 >> 1) And 1
        CheckBox8.Checked = (i_card1 >> 2) And 1
        CheckBox9.Checked = (i_card1 >> 3) And 1
        CheckBox10.Checked = (i_card1 >> 4) And 1
        TextBoxC6.Text = CStr(ReadCounter(1))
        TextBoxC7.Text = CStr(ReadCounter(2))
        TextBoxC8.Text = CStr(ReadCounter(3))
        TextBoxC9.Text = CStr(ReadCounter(4))
        TextBoxC10.Text = CStr(ReadCounter(5))
        ...

But it does not work either…

Are the DLL counters limited to 5 indexs ?

PROISIS

You are welcome, Proisis.

I assume that the K8055N/VM110N also has only 2 firmware maintained counters. Although the functionality was improved and using the new DLL v5 makes them available as 32 bit counters. There is a new backwards compatibility function ReadCounter16(counterNro). My assumption is based on the v5 DLL source code, which has been posted here: viewtopic.php?f=12&t=9005#p34898

The code shows clearly that the DLL internally limits the counter number to 1 or 2. That code would not make sense if the firmware did maintain 5 counters.

Regards,
Jan

One comment about your code:

Calling ReadAllDigital, then ReadCounter(0) and ReadCounter(1) causes the DLL to communicate 3 times with the same card, so it will take at least 6 milliseconds and the data is going to be from 3 different times.

You can get all the information with the ReadAll call, which takes a pointer to an 8 integer array. I have no clue how to define that in VB. In C the code would look like

[code]int iobuf[8];

SetCurrentDevice(0);
ReadAll(iobuf);[/code]

That should receive all digital, analog and 16-bit counter information at once. I am not 100% sure if the following is all correct. But the first one, iobuf[0], contains the digital inputs. I believe iobuf[1] and iobuf[2] are the two analog inputs, followed by the counters encoded as counter1 = iobuf[3] + iobuf[4] * 256 and counter2 = iobuf[5] + iobuf[6] * 256. The last one, iobuf[7], should be the card address.

Regards,
Jan