|
Hi, this is my first post on these forums. Hope this is in the right place.
Doing a college project where we control an rc car wired into an Arduino over the network from a computer.
It's been brilliant doing this project and all the stuff I've learned from the electronics to the coding, and I've gotten pretty well as far as I thought I could with a lot of help from this forum and many others, so thanks for that.
Decided to do the coding in VB 2008 cos of how quick and easy it was to get things running with VB in first year, but there was no constraint on language choice, and if anyone thinks another language is better suited to this I'd love to hear thoughts on that too. Anyway, having not used VB in a couple of years this forum and a few others were invaluable for rooting out which methods and things I needed to put this together, couldn't have even gotten started without it. But I wanted to postpone posting for as long as possible and get as much done on my own as I could first, which I think I have.
I have a solution pretty much working, and I wanted to get more expert opinions on how it could be improved or implemented better (even if it should be scrapped and a different approach taken, I'm sure it's far from perfect).
Here's the skiinny:
VB app checks for held keys with GetAsyncKeyState, and various combos (of arrow keys) cause particular character to be sent in UDP packet to Arduino, which does it's thing based on the character in the packet. The held keys was chosen as the most intuitive method of controlling an rc car from the pc, and I'm using a timer's tick to run the GetAsyncKeyState, and what I suspect might be a somewhat convoluted system of variable comparisons to prevent a packet from being sent until the state of the held keys changes.
UDP was chosen for speed, though I'm really not sure what difference it would make in the real world if trying to control the rc over the internet say with live keyboard control, and whether it would be viable with UDP and not an alternative, or whether it wouldn't make a practical difference, or whether it wouldn't be viable at all!
Anyway, the thing is working, and sending packets only on initial press or release of key(s). The one glitch I've so far detected is that when releasing all the keys, which should send the "z" character and stop the car, the same character that was previously sent is re-sent, rather than the 'stop' character "z".
So here's the code, VB 2008:
Imports System.Text
Imports System.Net
Imports System.Net.Sockets
Public Class Form1
Dim GLOIP As IPAddress
Dim GLOINTPORT As Integer
Dim bytCommand As Byte() = New Byte() {}
Dim udpClient As New UdpClient
Dim controlChoice As New Integer
Dim controlChoicePrev As New Integer
Dim cc As String = "z"
Public Declare Function GetAsyncKeyState Lib "user32" (ByVal vKey As Integer) As Integer
Dim WithEvents Tmr As New Timer
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
controlChoice = 0
controlChoicePrev = controlChoice
Tmr.Interval = 100
Tmr.Start()
End Sub
Private Sub Tmr_Tick(ByVal sender As Object, ByVal e As System.EventArgs) Handles Tmr.Tick
Tmr.Stop()
If Not controlChoice = controlChoicePrev Then
Try
GLOIP = IPAddress.Parse("192.168.1.177")
GLOINTPORT = 8888
udpClient.Connect(GLOIP, GLOINTPORT)
bytCommand = Encoding.ASCII.GetBytes(cc)
udpClient.Send(bytCommand, bytCommand.Length)
Catch ex As Exception
Console.WriteLine(ex.Message)
End Try
End If
If GetAsyncKeyState(Keys.Up) Or GetAsyncKeyState(Keys.Down) Then
Select Case True
Case GetAsyncKeyState(Keys.Up)
If (GetAsyncKeyState(Keys.Up)) And (GetAsyncKeyState(Keys.Left)) Then
Label1.Text = "Forwards Left"
controlChoicePrev = controlChoice
controlChoice = 11
cc = "b"
ElseIf (GetAsyncKeyState(Keys.Up)) And (GetAsyncKeyState(Keys.Right)) Then
Label1.Text = "Forwards Right"
controlChoicePrev = controlChoice
controlChoice = 12
cc = "c"
Else
Label1.Text = "Forwards"
controlChoicePrev = controlChoice
controlChoice = 10
cc = "a"
End If
Case GetAsyncKeyState(Keys.Down)
If (GetAsyncKeyState(Keys.Down)) And (GetAsyncKeyState(Keys.Left)) Then
Label1.Text = "Backwards Left"
controlChoicePrev = controlChoice
controlChoice = 21
cc = "e"
ElseIf (GetAsyncKeyState(Keys.Down)) And (GetAsyncKeyState(Keys.Right)) Then
Label1.Text = "Backwards Right"
controlChoicePrev = controlChoice
controlChoice = 22
cc = "f"
Else
Label1.Text = "Backwards"
controlChoicePrev = controlChoice
controlChoice = 20
cc = "d"
End If
End Select
Else
Label1.Text = "Stopped"
controlChoicePrev = controlChoice
controlChoice = 0
End If
Tmr.Start()
End Sub
End Class
So like I said, I'm just wondering if anyone would comment on this and make suggestions/crits.
Many TIA
PS. I'm still pretty code illiterate, so if layman's terms and really explicit and comprehensive and well commented code snippets is not too big an ask... like when people start using terms like call and pass, invoke, etc, I can just about follow things but not always. Sorry...
|
|
|
|
|
Hey there,
Interesting project you have here Must be a lot of fun to work on. I see this post was from a week back, so likely you have already figured this out for yourself, but for what it's worth I'll reply.
I added a few remarks/comments to your code; I'm not going to nitpick on the finer details, as most of the code looks good! I did notice that you are using the Select Case in an odd way and it's probably best to just make an If-Then-ElseIf statement of it.
Jose Sallamanca wrote: Anyway, the thing is working, and sending packets only on initial press or release of key(s). The one glitch I've so far detected is that when releasing all the keys, which should send the "z" character and stop the car, the same character that was previously sent is re-sent, rather than the 'stop' character "z".
So here's the code, VB 2008:
Private Sub Tmr_Tick(ByVal sender As Object, ByVal e As System.EventArgs) Handles Tmr.Tick
Tmr.Stop()
If Not controlChoice = controlChoicePrev Then
Try
GLOIP = IPAddress.Parse("192.168.1.177")
GLOINTPORT = 8888
udpClient.Connect(GLOIP, GLOINTPORT)
bytCommand = Encoding.ASCII.GetBytes(cc)
udpClient.Send(bytCommand, bytCommand.Length)
Catch ex As Exception
Console.WriteLine(ex.Message)
End Try
End If
If GetAsyncKeyState(Keys.Up) Or GetAsyncKeyState(Keys.Down) Then
Select Case True
Case GetAsyncKeyState(Keys.Up)
If (GetAsyncKeyState(Keys.Up)) And (GetAsyncKeyState(Keys.Left)) Then
Label1.Text = "Forwards Left"
controlChoicePrev = controlChoice
controlChoice = 11
cc = "b"
ElseIf (GetAsyncKeyState(Keys.Up)) And (GetAsyncKeyState(Keys.Right)) Then
Label1.Text = "Forwards Right"
controlChoicePrev = controlChoice
controlChoice = 12
cc = "c"
Else
Label1.Text = "Forwards"
controlChoicePrev = controlChoice
controlChoice = 10
cc = "a"
End If
Case GetAsyncKeyState(Keys.Down)
If (GetAsyncKeyState(Keys.Down)) And (GetAsyncKeyState(Keys.Left)) Then
Label1.Text = "Backwards Left"
controlChoicePrev = controlChoice
controlChoice = 21
cc = "e"
ElseIf (GetAsyncKeyState(Keys.Down)) And (GetAsyncKeyState(Keys.Right)) Then
Label1.Text = "Backwards Right"
controlChoicePrev = controlChoice
controlChoice = 22
cc = "f"
Else
Label1.Text = "Backwards"
controlChoicePrev = controlChoice
controlChoice = 20
cc = "d"
End If
End Select
Else
Label1.Text = "Stopped"
controlChoicePrev = controlChoice
controlChoice = 0
End If
Tmr.Start()
End Sub
End Class
My first comment 1) only applies if you are using Win64 and running/coding a 32 bits application (typical). There is a major bug in windows which prevents exceptions being thrown from the Load event to be caught by your program or the debugger - the exceptions are silently thrown away and execution continues with the hope that the exception isn't too serious. If you're interested in reading about it, here's a link to the article I found after many frustrations with this problem: http://blog.paulbetts.org/index.php/2010/07/20/the-case-of-the-disappearing-onload-exception-user-mode-callback-exceptions-in-x64/[^].
For this reason, it might actually be a good idea to place the majority of the code in the Load event in 'Sub New' (the constructor) instead. When you create a new function called Sub New in the IDE, by just typing in the Form code Sub New<enter> Visual Basic will automatically generate the following code for you:
Sub New()
InitializeComponent()
ADD THE LOAD CODE HERE
End Sub
This is the best place to put such code and still have exceptions work (actually, it's always the best place to initialise members).
Secondly, the way BLOCK 1 and BLOCK 2 are positioned is that at the first tick it is detected that a key is pressed but only at the second tick is the change transmitted, so you are manually introducing a 100 ms lag.
If you swap the positions of BLOCK 1 and BLOCK 2 then the code first checks the key states and then immediately transmits.
Finally, UDP is not a reliable protocol by design, so it might be a good idea to send the controlChoice even if it is equal to the controlChoicePrev every couple of ticks (like, every half second/5 ticks) to mitigate the results of package loss, which can occur with UDP.
Hope this helps you, if only a bit. Good luck.
Best Regards,
Richard
|
|
|
|
|
Hello,
I made a .batch script which is unmount usb disk F: or eject.
The command is very simple, "mountvol F: \D" and it's dissconnected or ejected.
What i was wodering is, how do i do in vb.net ?
|
|
|
|
|
You can use the Process Class[^] to run external commands.
Use the best guess
|
|
|
|
|
|
We got a customer who uses a Access DBs with code inside - one DB with Forms and Modules, the second stores the data. Two users work together from their computers on those DBs stored in the network.
Well, the devloper they had went into retirement. So I had the pleasure to port the Access 97/2000 DBs to 2003/2010. No there are some funny errors appearing just in their network not in our. I went to them cause I wasn't able to replicate those errors. Now it's even stranger. I tried to debug them, but they didn't appeared. I stopped debugging and the errors were thrown - not all the time but most times.
Access messages that a record is locked by another user although I was the only one today working on the db. There are error-messages, that an update wasn't successful but the data is stored correctly, after leaving the form all data is correct when I open it again.
It seems, that there is a timing / synchronisation error. I think the programm-code is faster than the operations in the data-db. But as debugging isn't possible it's hard to find the source.
Is there a way for a better synchronisation?
One user has Windows XP, the other one Windows 7 both use Access 2003 SP 3.
Regards,
Ingo
------------------------------
Author of Primary ROleplaying SysTem
How do I take my coffee? Black as midnight on a moonless night.
War doesn't determine who's right. War determines who's left.
|
|
|
|
|
Your first problem is your using Access.
There is no "timing/synchronisation" problem as Access is all file based. This sounds as though they have a problem with the server that these files are hosted on. Though, without further information on the exact errors they're getting and the server O/S and network config, it's impossible to say.
And, so far, this has nothing to do with Visual Basic at all.
|
|
|
|
|
Dave Kreskowiak wrote: Your first problem is your using Access.
I'm not using Access. They use it and so I have to. So my problem is that they use Access.
Dave Kreskowiak wrote: And, so far, this has nothing to do with Visual Basic at all.
Well I thought there was the chance to give more time per operation programmatically, as while I'm debugging - no error occurs.
Thank you for your answer,
Ingo
------------------------------
Author of Primary ROleplaying SysTem
How do I take my coffee? Black as midnight on a moonless night.
War doesn't determine who's right. War determines who's left.
|
|
|
|
|
ihoecken wrote: I'm not using Access. They use it and so I have to. So my problem is that
they use Access.
Again, without the error messages, it's pretty impossible to tell you what's going on.
But, with all the problems you're having, this is why you don't see very many multi-user Access applications.
|
|
|
|
|
There are no error messages that state the problem. There are different messages "error 3020 on tblXY Update or CancelUpdate without Addnew or edit"
There is always an addnew or edit and there is the error-message but the data is stored properly. The error isn't thrown when I go step-by-step through the code with debugging. The error isn't thrown here at my work in our network.
There are other errors, too. (record is locked by other user, but there was no other user today, when the error was thrown). All errors disappear while debugging and sometime during the normal work, too.
------------------------------
Author of Primary ROleplaying SysTem
How do I take my coffee? Black as midnight on a moonless night.
War doesn't determine who's right. War determines who's left.
|
|
|
|
|
I would start by reading up as much as you can after you Google for "Access error 3020". I have no idea if any of the situations or solutions applies to your situation, but it's a start.
|
|
|
|
|
Well, I do. It's just hard to test any possible solution as the errors don't occur in our environment. Thank you for your time.
------------------------------
Author of Primary ROleplaying SysTem
How do I take my coffee? Black as midnight on a moonless night.
War doesn't determine who's right. War determines who's left.
|
|
|
|
|
If you have more than one process updating the same record at the same time you will get a record locking issue in Access.
"Access messages that a record is locked by another user although I was the only one today working on the db." - how do you know that no other process was accessing the record(a lock is a lock and the only way this can happen is if another process has the record open)?
There really is no way around this unless you find some way to take a copy of the record for each user but then you may encounter a merge issue when you then go and update the original record overwriting the previous users data.
The first thing to do is to specifically isolate exactly when and why the record locking is occurring and take it from there.
“That which can be asserted without evidence, can be dismissed without evidence.”
― Christopher Hitchens
|
|
|
|
|
GuyThiebaut wrote: If you have more than one process updating the same record at the same time you will get a record locking issue in Access.
That's clear
GuyThiebaut wrote: how do you know that no other process was accessing the record
Easy. The other users weren't there today. There was only one user on which pc I worked, there was no ldb-file before I started, the ldb-file was deleted everytime I closed.
GuyThiebaut wrote: There really is no way around this unless you find some way to take a copy of the record for each user but then you may encounter a merge issue when you then go and update the original record overwriting the previous users data.
The different users work on different subjects which have own datarecords. The same records won't be opened by different users at the same time due to the workflow they use.
------------------------------
Author of Primary ROleplaying SysTem
How do I take my coffee? Black as midnight on a moonless night.
War doesn't determine who's right. War determines who's left.
|
|
|
|
|
Thanks - I think the only way I would be able to help was if I was sitting next to you when this happens.
What I would say is to keep a note of the the tables this happens on - this will be the clue.
Also check to see if there are any triggers on tables being updated which may be causing an issue to.
“That which can be asserted without evidence, can be dismissed without evidence.”
― Christopher Hitchens
|
|
|
|
|
GuyThiebaut wrote: Thanks - I think the only way I would be able to help was if I was sitting next to you when this happens.
Well thank you for your answer.
It's totally strange. I never saw such a problem although in former times I had to build some access solutions.
GuyThiebaut wrote: Also check to see if there are any triggers on tables being updated which may be causing an issue to.
Are triggers available before Access 2010? Well I didn't looked at them cause I thought they weren't , I will do it tomorrow. Thank you.
------------------------------
Author of Primary ROleplaying SysTem
How do I take my coffee? Black as midnight on a moonless night.
War doesn't determine who's right. War determines who's left.
|
|
|
|
|
ihoecken wrote: Are triggers available before Access 2010
No, Access (I should say Jet or ACE) doesn't support Triggers.
|
|
|
|
|
|
Thanks for your answer. To my sorrow these settings are all properly done. I checked them already.
------------------------------
Author of Primary ROleplaying SysTem
How do I take my coffee? Black as midnight on a moonless night.
War doesn't determine who's right. War determines who's left.
|
|
|
|
|
There is one more source of problems in customer installations: they use some kind of virus scanner. And such a virus scanner might block the access database file for a moment when data are changed (because the file contents are changed). When you debug step by step, the virus scanner can finish its job before the next request is done, but in production code the time span is too small. I'd suggest to remove the database file from the virus scanners's tasks.
|
|
|
|
|
Good idea, thanks. I will talk with them about that.
------------------------------
Author of Primary ROleplaying SysTem
How do I take my coffee? Black as midnight on a moonless night.
War doesn't determine who's right. War determines who's left.
|
|
|
|
|
I'm trying to create custom attachments to emails sent from my gmail account and linked to an access database, the attachment needs to have fields extracted from a record set inserted in bookmarks, and the process should run on the click of a vb button I can send emails with a standard attachment but I can't figure out how to customise the attachments could someone point me in the direction of a soultion
|
|
|
|
|
Member 9865508 wrote: I can send emails with a standard attachment Good. Can you show us that code?
Member 9865508 wrote: but I can't figure out how to customise the attachments That'll depend on their format. A textfile is easily manipulated. Did I understand it correctly that you want to use "emails" as an attachment, and customize those? You can open those (save as *.eml) in notepad, and you can replace tags just as with any text-file.
Bastard Programmer from Hell
If you can't read my code, try converting it here[^]
|
|
|
|
|
what i'm trying to do is send emails from vb that have a custom attachment based on the fields in an access database each email is sent to a different person and the attachment will be different for each person, the code i'm using so far is
Set cdomsg = CreateObject("CDO.message")
With cdomsg.Configuration.Fields
.Item("http://schemas.microsoft.com/cdo/configuration/sendusing") = 2
.Item("http://schemas.microsoft.com/cdo/configuration/smtpserver") = 465
.Item("http://schemas.microsoft.com/cdo/configuration/smtpserver") = "smtp.gmail.com"
.Item("http://schemas.microsoft.com/cdo/configuration/sendusername") = "xxx"
.Item("http://schemas.microsoft.com/cdo/configuration/sendpassword") = "xxx"
.Item("http://schemas.microsoft.com/cdo/configuration/smtpusessl") = True
.Item("http://schemas.microsoft.com/cdo/configuration/smtpauthenticate") = 1
.Update
End With
If Not (r.EOF And r.BOF) Then
r.MoveFirst
Do Until r.EOF = True
email = r.Fields("EmailAddress")
name = r.Fields("Firstname")
With cdomsg
.To = email
.FROM = "I@dontThinkThisIsUsed.com"
.Subject = "Test email " + name
.TextBody = "message"
.Addattachment "attachment loaction.docx"
.Send
End With
r.MoveNext
Loop
hope this helps thanks
I can send the emails but not the custom attachments
|
|
|
|
|
Member 9865508 wrote: I can send the emails but not the custom attachments
For that you'll have to modify the documents before sending. Am I looking at VBA or VB6? Either way, both aren't really suited to modify docx-files. I suggest you move to VB.NET, and write a small console-application that can display the contents of a docx-file.
If you can display it, you can change it.
Bastard Programmer from Hell
If you can't read my code, try converting it here[^]
|
|
|
|
|