I am trying to execute an If Statement in Visual Basic based on serial input from my arduino. The arduino is sending the data just fine, and the VB serial monitor tool shows the output fine. The condition will not execute. I tried multiple solutions on SoF and SE. Here is my code:
My VB code:
Imports System.IO.Ports
Public Class Form1
Private WithEvents SerialPort As New SerialPort()
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
SerialPort.PortName = "COM13" ' change to your serial port name
SerialPort.BaudRate = 9600
SerialPort.Open()
End Sub
Private Sub SerialPort_DataReceived(sender As Object, e As SerialDataReceivedEventArgs) Handles SerialPort.DataReceived
Dim receivedData As String = SerialPort.ReadExisting()
If receivedData = "1" Then
Label1.Text = "High Gas"
' do something
ElseIf receivedData = "0" Then
Label1.Text = "Low Gas"
' do something else
Else
' do a default action
End If
End Sub
End Class
My Arduino code:
int mq=A0;
int x;
void setup()
{
pinMode(A0,INPUT);
Serial.begin(9600);
}
void loop()
{
x=analogRead(A0);
delay(500);
if (x < 93 ){
Serial.println("0");}
if (x > 98 ){
Serial.println("1");}
}
I tried all the solutions I was able to find on SoF and SE. This includes using ReadChar, ReadLine, etc methods. I also tried changing the Arduino serial output from numeric to string and vice versa.
Related
I have a class called DataStorage that stores all of the publicly declared variables that I use in my program. My main class calls a public array from DataStorage and fills it with values. The main class then calls up a windows form and populates the textboxes on it with the values in the array. However, when I run the program, the form comes up with blank textboxes. I don't understand why this would be the case. I also tried populating the textboxes from the load function of the form rather than in the main class. I debugged and stepped through my code, and the main class filled the array with values fine. But when the form was called, and I started stepping through the code to populate the textboxes, it said the array was equal to nothing. If the array is stored in a separate class that both the main class and the form call, why are the values getting set to null between the main class and the form?
Here is what my code looks like:
Public Class Main
Private ds As New DataStorage
Public Sub Test()
For i = 0 to 2
ds.Info(i) = "Hello"
Next
Form1.ShowDialog()
End Sub
End Class
Public Class DataStorage
Public Info() As String
End Class
Public Class Form1
Private ds As New DataStorage
Private Sub Form1_Load(sender As Object, e As EventAgrs) Handles MyBase.Load
Textbox1.Text = ds.Info(0)
Textbox2.Text = ds.Info(1)
Textbox3.Text = ds.Info(2)
End Sub
I just called it Form1; the program starts with the sub Test in the main class.
I just need a way to have variables/arrays that are accessible by any class or form and don't change value or get set to null unless I tell it to.
You must give your array a size before you try to fill it.
Public Class TestArray
Private ds As New DataStorage
Private Sub TestArray_Load(sender As Object, e As EventArgs) Handles MyBase.Load
FillArray()
TextBox1.Text = ds.Info(0)
TextBox2.Text = ds.Info(1)
TextBox3.Text = ds.Info(2)
End Sub
Public Sub FillArray()
For i = 0 To 2
ds.Info(i) = "Hello"
Next
End Sub
End Class
Public Class DataStorage
Public Info(2) As String
End Class
If you don't want to give a size then use a List(Of T)
Public Class TestArray
Private ds As New DataStorage
Private Sub TestArray_Load(sender As Object, e As EventArgs) Handles MyBase.Load
FillList()
TextBox1.Text = ds.Info(0)
TextBox2.Text = ds.Info(1)
TextBox3.Text = ds.Info(2)
End Sub
Public Sub FillList()
For i = 0 To 2
ds.Info.Add("Hello")
Next
End Sub
End Class
Public Class DataStorage
Public Info As New List(Of String)
End Class
i wrote a program reading a com port for a signal, everything was working fine, but they wanted to make the application a service so i swapped the application type to 'windows service' and created a class and put everything in the form in there and i called the class in my Main() in the startup module. the line,
Me.Invoke(New myDelegate(AddressOf UPdateVariable), New Object() {})
in the class has invoke in red saying that, "'Invoke, is not a member of Moisture.Moisture.'" and the "Me" part of that line is no longer greyed out as it was in the form. it worked before dont know what made the difference.
this is the whole code for that class
Imports System
Imports System.IO.Ports
Imports System.Net.Mime
Public Class Moisture
Dim WithEvents serialPort As New IO.Ports.SerialPort
Public Delegate Sub myDelegate()
Public RawString As New System.Text.StringBuilder
Public value As String
Public Sub StartListening()
If serialPort.IsOpen Then
serialPort.Close()
End If
Try
With serialPort
.PortName = "COM3"
.BaudRate = 9600
.Parity = Parity.None
.StopBits = StopBits.One
.DataBits = 8
.Handshake = Handshake.None
.RtsEnable = True
End With
serialPort.Open()
Catch ex As Exception
End Try
End Sub
Private Sub serialPort_DataReceived(ByVal sender As Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort.DataReceived
Me.Invoke(New myDelegate(AddressOf UPdateVariable), New Object() {})
End Sub
Public Sub UPdateVariable()
With RawString
.Append(serialPort.ReadLine())
End With
If RawString.ToString().Count(Function(x As Char) x = "%"c) = 2 Then
PiTagUpdater(StringParser(RawString.ToString()))
RawString.Clear()
End If
End Sub
Public Function StringParser(RawString As String) As String
Dim Moisture = RawString
Dim value As String
Dim values As String() = Moisture.Split(New Char() {":"c})
value = values(values.Length - 1).Trim({" "c, "%"c})
Return value
End Function
Private Sub PiTagUpdater(Value As Decimal)
Try
Dim piserver As New PCA.Core.PI.PIServer(PCA.Core.Globals.Applications.Application("GENERAL").ConfigValues.ConfigValue("PI_SERVER_NAME").StringValue, PCA.Core.Globals.Applications.Application("GENERAL").ConfigValues.ConfigValue("PI_SERVER_UID").GetDeCryptedStringValue, PCA.Core.Globals.Applications.Application("GENERAL").ConfigValues.ConfigValue("PI_SERVER_PASSWD").GetDeCryptedStringValue, True)
Dim TimeStamp As DateTime = FormatDateTime(Now)
Dim RapidRingCrush = "M1:RapidRingCrush.T"
Try
piserver.WriteValue(RapidRingCrush, Value, TimeStamp)
Catch ex As Exception
MessageBox.Show("Error occured locating Pi Tag", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
Application.Exit()
End Try
Catch ex As Exception
MessageBox.Show("Cannot connect to Pi Server")
End Try
End Sub
End Class
Me refers to the current object, in this case the instance of your class. Your class doesn't have an Invoke() method (like the error says), though anything that derives from System.Windows.Forms.Control does (for instance a Form).
Control.Invoke() is used to move the execution of a method to the same thread that the control was created on. This is used to achieve thread-safety.
Since you switched to a service it is fair to assume that you do not have a user interface, thus there's no need to invoke. Removing the Invoke() call and calling the method manually should be enough:
Private Sub serialPort_DataReceived(ByVal sender As Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort.DataReceived
UPdateVariable()
End Sub
EDIT:
As LarsTech says, you also have to switch the message boxes to some kind of logging methods. A Windows Service usually runs under a different account, which means that it cannot display a user interface to the current user.
First a little background information: The purpose of this application is to capture images and save them automatically to a network directory that will be either created or appended using the input of the text box. This code DOES work on my computer (windows 7 home 64 bit). I've created it using microsoft visual basic express 2010.
However..... when attempting to run the application on a windows 10 tablet, I get the follow errors:
On form load:
An error occurred while capturing the image. The video capture will now be terminated.
Object reference not set to an instance of an object.
On button2_Click Event:
Object reference not set to an instance of an object.
Below is the entirety of the code.
Form2.vb
Public Class Form2
Public scanIsSet As Boolean
Private webcam As WebCam
Private Sub Form2_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
webcam = New WebCam()
webcam.InitializeWebCam(imgVideo)
webcam.Start()
scanIsSet = False
End Sub
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
Dim CFGfile As String
Dim SaveDir As String
Dim imgIndex As Integer
Dim existingImages() As String
SaveDir = "C:\somepath\"
'save image to directory with index number
Try
imgCapture.Image.Save(SaveDir & OrderNumber.Text & "\" & CStr(imgIndex) & ".jpg")
Catch ex As Exception
MsgBox("error while accessing object imgCapture" & ex.Message)
End Try
imgIndex = imgIndex + 1
Else
Beep()
MsgBox("Please scan or type in order number first")
End If
End Sub
End Class
WebCam.vb
Imports System
Imports System.IO
Imports System.Linq
Imports System.Text
Imports WebCam_Capture
Imports System.Collections.Generic
Imports ZXing
Imports ZXing.OneD
'Design by Pongsakorn Poosankam
Class WebCam
Public scanz As Boolean
Public Sub setScan(ByVal x As Boolean)
scanz = x
End Sub
Private webcam As WebCamCapture
Private _FrameImage As System.Windows.Forms.PictureBox
Private FrameNumber As Integer = 30
Public Sub InitializeWebCam(ByRef ImageControl As System.Windows.Forms.PictureBox)
webcam = New WebCamCapture()
webcam.FrameNumber = CULng((0))
webcam.TimeToCapture_milliseconds = FrameNumber
AddHandler webcam.ImageCaptured, AddressOf webcam_ImageCaptured
_FrameImage = ImageControl
End Sub
Private Sub webcam_ImageCaptured(ByVal source As Object, ByVal e As WebcamEventArgs)
_FrameImage.Image = e.WebCamImage
If scanz = True Then
Dim BCreader As New ZXing.BarcodeReader
'BCreader.Options.TryHarder = True
Try
Dim resu As Result = BCreader.Decode(e.WebCamImage)
Form2.OrderNumber.Text = resu.Text
setScan(False)
Form2.Label2.Text = ""
Beep()
Catch ex As Exception
'do nothing
End Try
End If
End Sub
Public Sub Start()
webcam.TimeToCapture_milliseconds = FrameNumber
webcam.Start(0)
End Sub
Public Sub [Stop]()
webcam.[Stop]()
End Sub
Public Sub [Continue]()
' change the capture time frame
webcam.TimeToCapture_milliseconds = FrameNumber
' resume the video capture from the stop
webcam.Start(Me.webcam.FrameNumber)
End Sub
Public Sub ResolutionSetting()
webcam.Config()
End Sub
Public Sub AdvanceSetting()
webcam.Config2()
End Sub
End Class
As you can see toward the end of Form2.vb, I've wrapped imgCapture.Image.Save(SaveDir & OrderNumber.Text & "\" & CStr(imgIndex) & ".jpg") in a Try-Catch block because I suspect it's some sort of problems with the pictureBox objects. The try catch block does indeed catch the exception, but I still have no idea what the problem is, why it happens on the tablet and not the PC, or how to fix it.
I've found similar questions, but none with a solution I can make use of.
Since you are using a library, EasyWebCam, that is outdated and not compatible with Win10, I would suggest switching libraries. Other options out there:
DirectX.Capture
Windows.Media.Capture
I FOUND THE SOLUTION BUT I DON'T KNOW IF YOU NEED IT NOW ANYWAY THE PROBLEM IS IF YOU HAVE CHANGED THE PICTUREBOX NAME THEN IN REFENCES USE THE EXACT NAME YOU HAVE CHANGED TO. EXAMPLE IF I CHANGE ALL MY PICTUREBOX NAMES AS -
PictureBox_A1 , PictureBox_A2 ,... and so on then my refence should be as -
Dim r As DataRow
For Each r In t1.Rows
CType(Controls("PictureBox_" & r(2)), PictureBox).Image = bookedicon
Next
MY REFERENCE IS - "PictureBox_"
I'm writing a code using vb.net that can get some information from a radar sensor connected to the computer through a device called CANcaseXL ,, I added the dll file reference corresponding to the CANcaseXL driver and in the manual there is a command that let me print the information that goes to the CANcaseXL to the console (the command is xlPrintRx(XLClass.xl_event receivedEvent)) and its a continuous real-time data going from the radar to the computer through the CANcaseXL.
Now I can see the data in the console but I need to extract it like print it to a textbox maybe then do the calculations but I don't know how.
I will appreciate any help !
this like the main code I have been working on, I got most of it using the XL Driver Library - .NET Wrapper Description >> http://www.labviewforum.de/attachment.php?aid=43764 and a ready made application.
Option Explicit On
Imports System
Imports System.Runtime.InteropServices
Imports System.Threading
Imports vxlapi_NET20
Imports System.IO
Public Class Main
Region "DLL_Import"
<DllImport("kernel32.dll", SetLastError:=True)> _
Shared Function WaitForSingleObject(ByVal handle As Integer, ByVal timeOut As Integer) As Integer
End Function
End Region
Region "GLOBALS"
Shared CANDemo_TxChannel As xlSingleChannelCAN_Port
Shared CANDemo_RxChannel As xlSingleChannelCAN_Port
Private RxThread As Thread
Private Enum WaitResults : int
WAIT_ABANDONED = &H80
WAIT_FAILED = &HFFFFFFF
WAIT_OBJECT_0 = &H0
WAIT_TIMEOUT = &H102
INFINITE = &HFFFF
End Enum
End Region
Declare Function AllocConsole Lib "kernel32" () As Int32
Declare Function FreeConsole Lib "kernel32" () As Int32
Public Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
AllocConsole()
ProgressBar1.Value = 0
Dim Max As Double
Dim Min As Double
Dim id As String
Dim dlc As String
Dim data As String
Max = Val(TextBox1.Text)
Min = Val(TextBox2.Text)
id = Val(TextBox3.Text)
dlc = Val(TextBox4.Text)
data = TextBox5.Text
If data = "" Then
data = 0
End If
Dim Max_hex As Double = Val("&H" & Max)
Dim Min_hex As Double = Val("&H" & Min)
If (Max < 1 Or Min < 0) Then
MsgBox("Please fill the range fields!!", MsgBoxStyle.Critical)
GoTo C
End If
CANDemo_TxChannel = New xlSingleChannelCAN_Port("xlCANdemo NET", Convert.ToUInt32(0))
CANDemo_RxChannel = New xlSingleChannelCAN_Port("xlCANdemo NET", Convert.ToUInt32(0))
If (CANDemo_TxChannel.xlCheckPort() And CANDemo_RxChannel.xlCheckPort()) Then
Label10.BackColor = Color.Green
Label11.Text = "CAN Connected ..."
' these two commands print the configuration for the CANcaseXL device
CANDemo_TxChannel.xlPrintConfig()
CANDemo_RxChannel.xlPrintConfig()
CANDemo_RxChannel.xlResetAcceptanceFilter()
CANDemo_RxChannel.xlCanAddAcceptanceRange(Convert.ToUInt32(Min_hex), Convert.ToUInt32(Max_hex))
CANDemo_TxChannel.xlActivate()
CANDemo_RxChannel.xlActivate()
Dim RxThread As New Thread(AddressOf RX_Thread)
RxThread.Start()
If MsgBox("Are you sure you wont to transmit ?", MsgBoxStyle.YesNo, "Transmission") = MsgBoxResult.Yes Then
CANDemo_TxChannel.xlTransmit(Convert.ToUInt32(id, 16), Convert.ToUInt16(dlc, 16), Convert.ToUInt64(data, 16))
ProgressBar1.Value = 100
MsgBox("Data Transmited!", MsgBoxStyle.Information, "Transmission")
Else
End If
Else
Label10.BackColor = Color.Red
Label11.Text = "CAN Disconnected!!"
MsgBox("Check the device connection!", MsgBoxStyle.Critical, "CAN disconnected")
CANDemo_TxChannel.xlClosePort()
CANDemo_RxChannel.xlClosePort()
End If
C:
End Sub
Region "EVENT THREAD (RX)"
Public Shared Sub RX_Thread()
Dim receivedEvent As XLClass.xl_event = New XLClass.xl_event
Dim xlStatus As XLClass.XLstatus = XLClass.XLstatus.XL_SUCCESS
Dim waitResult As WaitResults
While (True)
waitResult = WaitForSingleObject(CANDemo_RxChannel.eventHandle, 1000)
If (waitResult <> WaitResults.WAIT_TIMEOUT) Then
xlStatus = XLClass.XLstatus.XL_SUCCESS
While (xlStatus <> XLClass.XLstatus.XL_ERR_QUEUE_IS_EMPTY)
xlStatus = CANDemo_RxChannel.xlReceive(receivedEvent)
If (xlStatus = XLClass.XLstatus.XL_SUCCESS) Then
' this command let the coming results from the Radar printed to the console
CANDemo_RxChannel.xlPrintRx(receivedEvent)
End If
End While
End If
End While
End Sub
End Region
I'm trying to create a multithreaded program to poll machines for data but I can't seem to get it working correctly. The code below is working and is creating 4 threads as it should but the flow of the code seems to happen in series and on the main UI thread.
What I'm trying to achieve is for each row of the datagrid to update simultaneously without locking up the UI.
Below is a dumbed down version of what I have but it serves to demonstrate the problem.
For info the 'testclass' is a class used as an instance of a machine with each class element representing a property of a machine.
Hope I've given enough info to explain the problem. Thanks in advance.
Ps I shouldn't need to refresh form should I?
Imports System.Threading
Public Class TestForm
Public threadcount As Integer
Public Delegate Sub testclassDelegate(test As Object)
Private Class testclass
Public index As Integer
Public TestVal1 As Integer = 100
Public TestVal2 As Integer = 200
Public TestVal3 As Integer = 300
Public TestVal4 As Integer = 400
Public TestVal5 As Integer = 500
Public TestVal6 As Integer = 600
Public testDel As testclassDelegate
End Class
Private Sub TestForm_Load(sender As Object, e As EventArgs) Handles MyBase.Load
For i As Integer = 0 To 3
DataGridView1.Rows.Add()
DataGridView1.Rows(i).Cells(0).Value = i + 1
Next
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
For i As Integer = 0 To 3
DataGridView1.Rows(i).Cells(1).Value = ""
DataGridView1.Rows(i).Cells(2).Value = ""
DataGridView1.Rows(i).Cells(3).Value = ""
DataGridView1.Rows(i).Cells(4).Value = ""
DataGridView1.Rows(i).Cells(5).Value = ""
DataGridView1.Rows(i).Cells(6).Value = ""
Next
Poll_FreeThread()
End Sub
Private Sub Poll_FreeThread()
For i As Integer = 0 To DataGridView1.Rows.Count - 1
Dim test As New testclass
test.index = i
test.testDel = AddressOf UIUpdate
Interlocked.Increment(threadcount)
Me.Label2.Text = threadcount
Try
Dim thPoll As New Thread(Sub() invokeUIUpdate(test))
thPoll.IsBackground = True
thPoll.Priority = ThreadPriority.BelowNormal
thPoll.Start()
Catch ex As Exception
MsgBox(ex.Message)
End Try
Next
End Sub
Public Sub invokeUIUpdate(test As Object)
If DataGridView1.InvokeRequired Then
DataGridView1.Invoke(New testclassDelegate(AddressOf UIUpdate), test)
Else
UIUpdate(test)
End If
End Sub
Public Sub UIUpdate(test As Object)
Thread.Sleep(test.index * 100)
DataGridView1.Rows(test.index).Cells(1).Value = test.TestVal1
Me.Refresh()
Thread.Sleep(100)
DataGridView1.Rows(test.index).Cells(2).Value = test.TestVal2
Me.Refresh()
Thread.Sleep(100)
DataGridView1.Rows(test.index).Cells(3).Value = test.TestVal3
Me.Refresh()
Thread.Sleep(100)
DataGridView1.Rows(test.index).Cells(4).Value = test.TestVal4
Me.Refresh()
Thread.Sleep(100)
DataGridView1.Rows(test.index).Cells(5).Value = test.TestVal5
Me.Refresh()
Thread.Sleep(100)
DataGridView1.Rows(test.index).Cells(6).Value = test.TestVal6
Me.Refresh()
Interlocked.Decrement(threadcount)
Me.Label2.Text = threadcount
End Sub
End Class
Run your code a little bit different, This is how the Structure should look like for Multithreading in vb.net ( it has something to do with Vb.net not passing Namespaces into Models from what i Understand )
This would be your startThread from MainThread in load or w/e have you
Private Sub DoSomethingSimple()
Dim DoSomethingSimple_Thread As New Thread(AddressOf DoSimple)
DoSomethingSimple_Thread.Priority = ThreadPriority.AboveNormal
DoSomethingSimple_Thread.Start(Me)
End Sub
This would be the actual thread Itself ( new model / class or in the same class )
Private Sub DoSimple(beginform As Form)
'Do whatever you are doing that has nothing to do with ui
'For UI calls use the following
SomethingInvoked(PassibleVariable, beginform)
End Sub
Write a Delegate and Invoke Method for Each Call to the Main Thread.
Delegate Sub SomethingInvoked_Delegate(s As Integer, beginform As Form)
Sub SomethingInvoked_Invoke(ByVal s As Integer, beginform As Form)
If beginform.NameOfControlYouAreUpdating.InvokeRequired Then ' change NameOfControlYouAreUpdating to the Name of Control on the form you wish to update
Dim d As New SomethingInvoked_Delegate(AddressOf SomethingInvoked_Invoke)
beginform.Invoke(d, New Object() {s, beginform})
Else
'Do something...
beginform.NameOfControlYouAreUpdating.Condition = Parameter
End If
End Sub
This is tested ( non hanging ) way of writing Threads in vb.net
If you need further help implementing your code to this Template let me know :P
You can use timer and backgroundworker with event of DoWorkEventHandler and RunWorkerCompletedEventHandler.
Example: C# example:
private void Button1_Click(object sender, EventArgs e)
{
workerThreadForLetters.WorkerReportsProgress = true; workerThreadForLetters.WorkerSupportsCancellation = true;
workerThreadForLetters.DoWork -= new DoWorkEventHandler(workerThreadForLetters_DoWork);
workerThreadForLetters.DoWork += new DoWorkEventHandler(workerThreadForLetters_DoWork);
workerThreadForLetters.RunWorkerCompleted -= new RunWorkerCompletedEventHandler(workerThreadForLetters_RunWorkerCompleted);
workerThreadForLetters.RunWorkerCompleted += new RunWorkerCompletedEventHandler(workerThreadForLetters_RunWorkerCompleted);
}
private void workerThreadForLetters_DoWork(object sender, DoWorkEventArgs e)
{
//DO SOMETHING
}
private void workerThreadForLetters_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//DO AS REQUIRED
Timer1.enable = false;
}
private void tmCollectionLettersUpdate_Tick(object sender, EventArgs e)
{
//Content update like % of data progressed or any.
}