Click here to Skip to main content
15,886,110 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
I'm trying to make a fairly simple program that takes temperature data from an Arduino Uno and sends it to VB 2010 to make a real time graph. So far I have the Arduino output temperatures on a new line, I know this works from the Arduino IDE serial monitor.

Basically I can get input but is not what I should be reading. Arduino is putting out 25 degC at 9600 baud but my VB program is either configured incorrectly or it can't keep up. For example if Arduino is sending a stream of 25.16s my program might pick out 5.16 or "" or 0.16.

A quick fix would be if I had a way to ignore any value that doesn't convert to a double and repeat until it gets a valid string without breaking the program. Then get rid of values that are too small as well. I don't know how to do the first part but it's what I think I need to do.

I'm new to programming so if my programs are totally wrong, please forgive me.
Also I would love any feedback on my two programs, even if it isn't relevant to my problem.

Here is my Arduino code just in case it is wrong. I'm using a digital thermometer.
C#
#include <LiquidCrystal.h>
#include <OneWire.h>
int DS18S20_Pin = 9; //DS18S20 Signal pin on digital 2
int buttonPin = 8;     // the number of the pushbutton pin
int ledPin = 7;        // LED pin
OneWire ds(DS18S20_Pin); // on digital pin 2
// Connections:
// rs (LCD pin 4) to Arduino pin 12
// rw (LCD pin 5) to Arduino pin 11
// enable (LCD pin 6) to Arduino pin 10
// LCD pin 15 to Arduino pin 13
// LCD pins d4, d5, d6, d7 to Arduino pins 5, 4, 3, 2
// Digital Temperature sensor to Arduino pin 9
// Button to Arduino pin 8
LiquidCrystal lcd(12, 11, 10, 5, 4, 3, 2);
int backLight = 13;    // pin 13 will control the backlight
int val = 0;
int oldVal = 0;
int state = 0;
void setup()
{
  Serial.begin(9600);
  pinMode(backLight, OUTPUT);
  pinMode(buttonPin, INPUT);
  pinMode(ledPin, OUTPUT);
  digitalWrite(backLight, HIGH); // turn backlight on. Replace 'HIGH' with 'LOW' to turn it off.
  lcd.begin(16,2);              // columns, rows.  use 16,2 for a 16x2 LCD, etc.
  lcd.clear();                  // start with a blank screen
  lcd.setCursor(0,0);           // set cursor to column 0, row 0 (the first row)
  lcd.print("Temperature");    // change this text to whatever you like. keep it clean.
}

void loop(void) 
{
val = digitalRead(buttonPin);  // Check for the button's state
if (val == HIGH && oldVal == LOW)
{
  state = 1 - state;
  delay(10);                  //Debounce
}
oldVal = val;   // Store val for comparison 
if (state == 1)
{
  float temperature = getTemp();
  temperature = temperature*(9/5)+32;
  lcd.setCursor(0,1);
  lcd.print(temperature);
  Serial.println(temperature);
  lcd.setCursor(6,1);
  lcd.print(char(223));
  lcd.setCursor(7,1);
  lcd.print("F");
  digitalWrite(ledPin, LOW); //Turn off LED when Tempearutre is in Celsius
  
  delay(10); //just here to slow down the output so it is easier to read
}
else
{
  float temperature = getTemp();
  lcd.setCursor(0,1);
  lcd.print(temperature);
  Serial.println(temperature);
  lcd.setCursor(6,1);
  lcd.print(char(223));
  lcd.setCursor(7,1);
  lcd.print("C");
  digitalWrite(ledPin, HIGH); //Turn on LED when Tempearutre is in Celsius
  delay(10);
}
}
float getTemp()
{
//returns the temperature from one DS18S20 in DEG Celsius
byte data[9];
byte addr[8];
if ( !ds.search(addr)) {
//no more sensors on chain, reset search
   ds.reset_search();
return -1000;
}
if ( OneWire::crc8( addr, 7) != addr[7]) {
Serial.print("CRC is not valid!\n");
return -1000;
}
if ( addr[0] != 0x10 && addr[0] != 0x28) {
Serial.print("Device is not recognized");
return -1000;
}
 ds.reset();
 ds.select(addr);
 ds.write(0x44,1); // start conversion, with parasite power on at the end
byte present = ds.reset();
 ds.select(addr);  
 ds.write(0xBE); // Read Scratchpad

for (int i = 0; i < 9; i++) { // we need 9 bytes
  data[i] = ds.read();
}
 ds.reset_search();
byte MSB = data[1];
byte LSB = data[0];
float tempRead = ((MSB << 8) | LSB); //using two's compliment
float TemperatureSum = tempRead / 16;
return TemperatureSum;
}


Here is my VB code which is probably where my mistake is:
VB
Imports System.Windows.Forms.DataVisualization.Charting
Imports System
Imports System.ComponentModel
Imports System.Threading
Imports System.IO.Ports

Public Class Form1
    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Dim Time As Long = 0
        Dim line As String = ""
        Dim Chart1 As New Chart

        If (SerialPort1.IsOpen = True) Then
            SerialPort1.Close()
            SerialPort1.DiscardInBuffer()
        End If

    End Sub


    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim i As Long = 0
        Dim temp As Double = 0
        Dim tempArray(0) As Double
        Dim length As Long = 0
        Dim message1 As String = ""
        Chart1.ChartAreas(0).AxisX.Maximum = 20
        Chart1.ChartAreas(0).AxisX.Minimum = 0
        Chart1.ChartAreas(0).AxisY.Maximum = 20
        Chart1.ChartAreas(0).AxisY.Minimum = 0
        Chart1.Series(0).ChartType = DataVisualization.Charting.SeriesChartType.Line

        SerialPort1.PortName = "COM3"
        SerialPort1.BaudRate = 9600
        SerialPort1.Parity = IO.Ports.Parity.None
        SerialPort1.DataBits = 8
        SerialPort1.StopBits = IO.Ports.StopBits.One
        'Dim message2 As String = ""
        SerialPort1.Open()
        'SerialPort1.DiscardInBuffer()

        While (SerialPort1.IsOpen())
            If SerialPort1.BytesToRead() = 0 Then
                SerialPort1.DiscardInBuffer()
            Else
                message1 = SerialPort1.ReadLine()
                SerialPort1.DiscardInBuffer()

                If message1 = "" Then
                    While message1 = ""
                        message1 = SerialPort1.ReadLine()
                        SerialPort1.DiscardInBuffer()
                    End While
                End If

                If message1 = " " Then
                    While message1 = " "
                        message1 = SerialPort1.ReadLine()
                        SerialPort1.DiscardInBuffer()
                    End While
                End If
                Try
                    temp = Convert.ToDouble(message1)
                Catch ex As FormatException

                End Try

                tempArray(i) = temp
                length = UBound(tempArray)
                If length >= 0 Then
                    ReDim Preserve tempArray(length + 1)
                End If

                Label1.text = message1
                Chart1.Series(0).Points.Add(tempArray(i))
                If (tempArray(i) > Chart1.ChartAreas(0).AxisY.Maximum) Then
                    Chart1.ChartAreas(0).AxisY.Maximum = tempArray(i)
                End If
                Chart1.Series(0).Points.AddY(tempArray(i))

                If Chart1.Series(0).Points.Count = 30 Then
                    Chart1.Series(0).Points.RemoveAt(0)
                    Chart1.ChartAreas(0).AxisX.Maximum = UBound(tempArray)
                    Chart1.ChartAreas(0).AxisX.Minimum = UBound(tempArray) - 30
                End If
                i += 1
            End If
        End While
        MessageBox.Show("Session has ended.")
    End Sub
End Class



Thanks Kyle
Posted
Updated 26-Jul-11 19:09pm
v2

1 solution

Probably, your problem is that you are calling DiscardInBuffer so often. If your VB code is out of sync with your Arduino code (and lets face it, it will be since the Arduino is continuously pumping data in realtime with a brief delay, and the VB is working when windows feels like it) There is a good chance that you data buffer contains more than one sample when you read a line from it:
25.16\n25.16\n25.16\n
You would read the first (oldest) sample, and discard the remainder. This appears to work fine, except when the data in the buffer is not complete:
25.16\n25.16\n2
Again, you read the oldest, and discard the rest. Your next sample is different:
5.16\n25.16\n25.
So this time, your sample is 5.16 and you again discard the rest, ready for the next error.

Change the data from the Arduino: Encapsulate it as (say) "\tnn.nn\n" and scan through your data to use complete samples only. I would also either parse all of the data rather than discarding later samples, or work from the last sample, rather than the first - that way you get the most recent sample as the current temp, rather than the oldest
 
Share this answer
 
Comments
Stephen Wiria 27-Jul-11 4:27am    
Great answer!
kile22 27-Jul-11 22:37pm    
Thanks a lot! I found a SerialPort.ReadTo("\n") command that works perfectly. My program works but I'm having a strange issue where the graph only updates if the mouse cursor is moving around inside the application. Any idea how I can fix that?
OriginalGriff 28-Jul-11 3:32am    
Depends on how you are drawing the graph: You may need to add a Control.Invalidate method call when you change the data.
kile22 28-Jul-11 22:18pm    
Thanks Again. I found a refresh method related to the one you suggested. One last issue, I hope. When I click the button, everything works perfectly, except I lose control. I can't press anything else, not even the exit button. Its like it locks up but is still running correctly.

Any suggestions?
Thanks again for all your help.
OriginalGriff 29-Jul-11 3:38am    
I suspect that may well be the SerialPort.ReadTo method locking the thread until it returns with data.
Ouch! That isn't simple to fix: you need to look at splitting your program into two, and moving your serial port handling code onto a different thread, so that it won;t affect the UI thread. That means that you will need to look at BackGroundWorker, Lock and also at Invoke, because you cannot access UI components (such as your graph) except from the thread they were created on. Google and MSDN can help with the basics - it's not too complex, but may need a bit of cogitation if you haven't dealt with threading before. Read and learn, because I can't fit it all into this reply box! :laugh:

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900