Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / VB

Arduino with Visual Basic

4.78/5 (17 votes)
22 Sep 2009CPOL4 min read 184.7K   4.8K  
How to use Visual Basic with the Arduino 2009 board.

Introduction

I have an Arduino 2009 board setup with a simple temperature monitor: http://arduino.cc/en/Main/ArduinoBoardDuemilanove. I wanted to use Visual Basic to communicate with the board and build a graphics display page. Arduino provides interface code for various other languages. There is also a very professional interface to Visual Basic available using Fermata written by Andrew Craigie: http://www.acraigie.com/programming/firmatavb/default.html. This is written so that a component can be added to the toolbox. This component can then be simply dragged onto any form to make it available to code within your project. Using this method hides the actual interface code from the Visual Basic programmer. There is an extra layer of code below your program.

The Firmata library implements the Firmata protocol for communicating with software on the host computer. As such, the library must be included in the Arduino board firmware, using up scarce program storage: http://arduino.cc/en/Reference/Firmata. As I did not need most of the methods available with this application and wanted to make the program as simple as possible, I decided to write my own code for the interface.

All the functions are in classes as this brings discipline to the coding.

Program requirements

The Arduino board samples the temperature every 10 seconds. After 6 such samples (1 minute), the board checks the serial input. If it is sent "SSSSSS", it will send the sample value on the serial output. If not, the sample will be stored on EEPROM. So, if the user's computer is not sending "SSSSSS", the samples will be stored on the EEPROM until it is full (1024 bytes).

If the serial input is "U", all (if any) of the stored EEPROM samples will be sent on the serial output. So the user's computer will at the start send a "U" to check for and then upload stored samples. After that, it will look for a sample using "SSSSSS" every 1 minute.

On closing, the user computer will save the samples in a user folder. These previously stored samples will be available to the user to view at any time the program is running.

Serial connection

The Arduino uses a FTDI USB to serial port chip. This enables the board to appear as a serial port via a USB connection. When first connected, I direct Windows to the FTDI driver. After loading the driver, my computer allocated com8 for the Arduino. Communication then is just a matter of sending and receiving strings on com8.

Upload_stored class

When the user form loads, I upload any stored samples available. As this runs before the form is activated, I let this code run on the form's thread.

The serial input code is enclosed in nested Try Catch blocks. The outer block is for if com8 is being used by another program or the Arduino is not connected. The next block catches if a response is not available within 20 seconds. The inner block catches any loss of connection every 100 msec.

VB
Try
    Dim com8 As IO.Ports.SerialPort = My.Computer.Ports.OpenSerialPort("com8", 9600)
    com8.ReadTimeout = 20000 'board will respond within 10 seconds
    com8.Write("U")  'arduino board command for upload on restart
    Try
        Dim addr_pointer As Integer = CInt(com8.ReadLine) ' get rid of cr lf
        If addr_pointer > 0 Then  'we have saved samples
            For i = 0 To (addr_pointer / 2 - 1) 'read all stored samples
                com8.ReadTimeout = 100  'if we dont get a new byte in 100 ms
                Try
                    display_data(i) = _
                      software_dcoffset - CInt(com8.ReadLine) * software_scalefactor 
                    rx_number += 1  'updata the sample buffer pointer
                Catch ex As TimeoutException
                    MsgBox("we have lost the circuit")
                End Try
            Next
        End If
        com8.Write("SSSSSS")  'arduino board command for upload single samples
        User_interface.Timer1.Enabled = True  'start the sampling proces
        com8.Dispose()
    Catch ex As TimeoutException
        MsgBox("we dont have a response")
    End Try
    Catch ex As Exception
    MsgBox("we cant open port to download") 'cant connect to port
End Try

Read_current class

The serial code is very similar to upload_stored, but as we now need to access the form during serial communication, this code is run on its own thread. This complicates the program as Windows controls are not thread safe. This means that we have to bend over backwards to use them from a thread we start. This involves using a delegate subroutine that will run on a thread that owns the underlying handle of the form. This is achieved using the Invoke (delegate) method: http://msdn.microsoft.com/en-us/library/zyzhdc6b.aspx.

VB
''''
 _frm.Invoke(New messdelegate(AddressOf showmessage)) 'register sample
''''
Delegate Sub messdelegate()
'a delegate will run on the main window thread
    Private Sub showmessage()
    'this runs on the main window thread because control not thread safe
        'register sample
        Upload_stored.rx_number = Upload_stored.rx_number + 1
        'reset value***and save full sample
        If Upload_stored.rx_number = 1439 Then Upload_stored.rx_number = 0
        If Display_files.show_current Then
            Dim d As New Display
            d.display_now = Upload_stored.display_data
            d.dt = Upload_stored.displaytime
            d.show_now()
            _frm.Label2.Text = "Current temperature = " & _
                (Upload_stored.display_data(Upload_stored.rx_number - 1) * _
                 Upload_stored.software_nominal).ToString("n1")
        End If
        _frm.Timer1.Enabled = True 'OK lets check for another sample
    End Sub

In a class, we can reference a label as form.label2, but we cannot do the same with form.invoke .It will compile OK, but give a runtime error of no handle available. We have to create an instance of the form in the class and then reference that in the code.

VB
Public Sub New(ByVal f As User_interface) 'constructor runs at start
    _frm = f 'need an instance of form to allow operation of Invoke
    Dim readthread As Threading.Thread = New Threading.Thread(AddressOf read)
    readthread.Start()  'see if have data to read using worker thread
End Sub
Private _frm As User_interface  'need instance of mainform to give invoke a handler

We instantiate the class by:

VB
Dim c As New Read_current(Me) 'must pass form to constructor

Display class

Image 1

This uses simple graphics on a bitmap to display the temperature samples.

VB
Private part As New Bitmap(1570, 1070, Drawing.Imaging.PixelFormat.Format24bppRgb)
    Public display_now(1439) As Int16  'only 0 to 1023 possible
    ''''
    For i = 0 To 1438
        If display_now(i + 1) = 0 Then Exit For 'only display the real samples
        g.DrawLine(Rpen, 62 + i, 1040 - display_now(i), 63 + i, 1040 - display_now(i + 1))
    Next
    ''''
    User_interface.PictureBox1.Image = part

Display_files class

This displays 24 (or less) hour samples that have been saved by the program. The date time for the samples are used as for the file names so that the time and date can be displayed on the graphics.

VB
Public Shared user_folder As String = _
  My.Computer.FileSystem.SpecialDirectories.MyMusic & "\met_samples\"

''''
'get access to file
Dim st As Stream = File.Open(files(displayed_time), _
                   FileMode.Open, FileAccess.Read)
Dim sample_n = My.Computer.FileSystem.GetFileInfo(files(displayed_time)).Length / 2 - 1
Using br As New BinaryReader(st)
    For i = 0 To sample_n  'read the number of samples in tyhe stored file
        display_now(i) = br.ReadInt16
    Next
End Using

''''
Dim sts As DateTime = rt.Replace(".", ":")  'fix up the stored time
Dim dt = (sts.Subtract(starttime)).ToString("t") 'just need short time
Dim yt = (sts.Subtract(starttime)).ToString("d MMM yyyy")
Dim d As New Display
d.display_now = display_now

Arduino firmware

The C++ code is shown here with comments. The Arduino sketch is include in the zip file:

C++
#include <EEPROM.h>
#include "WProgram.h"
void setup();
void loop();
void adc();
int inByte =0;  //received byte
unsigned int minutesample = 0;  //adc averaged over a minute
byte numbersample = 0;  //the number of samples so far -6 for 1 minute
unsigned long previousMillis = 0;        // will store last time we looked
int addr = 0;  //address of next data to be stored in the EEPROM
void setup()
{
    Serial.begin(9600);  // initialize serial communication with computer 
    pinMode(12, OUTPUT);      // sets the digital pin as output
    pinMode(11, OUTPUT);      // sets the digital pin as output
    analogReference(EXTERNAL);  //can be 1.8 to 5.5v we use 3.3v
}
void loop()
{ 
    if (millis() - previousMillis > 10000)  //every 10 seconds
    {
        // remember the last time we sampled the pressure
        previousMillis = millis();
        //flash the led
        digitalWrite(12, HIGH);
        digitalWrite(11, HIGH);
        //flash the led
        //is the computer comunicating

        if (Serial.available() > 0)
        // read the oldest byte in the serial buffer:
        {
            //if it is "U" we can upload saved samples to computer
            inByte = Serial.read();
            if (inByte == 'U')
            {
                //send current address pointer to computer
                Serial.println(addr);
                if (addr > 0)
                //have we got stored values to upload
                {
                    int i = 0;  //counter for upload
                    for  (i; i < addr; i +=2)
                    {
                        //restore integer value 
                        minutesample = ((EEPROM.read(i + 1)) * 256) +  
                                         EEPROM.read(i);
                        //send stored sample to computer
                        Serial.println(minutesample);
                    }//end of upload stored values
                    addr = 0; //we have now sent the lot
                    minutesample = 0;  //reset the sample
                    numbersample = 0;  //reset the counter
                } //end of have we got stored values  
            }//end of upload any stored data
            if (inByte == 'S')
            {    
                adc(); 
                if (numbersample == 6 )
                //we have 6 samples over 1 minute
                {
                    Serial.println(minutesample/6); //send it to computer
                    minutesample = 0;  //reset the sample
                    numbersample = 0;  //reset the counter
                }//end of have 6 samples
            } //end of received "S"
        }//end of serial available 
         //computer is not communicating
        else
        //no serial sent so the computer is not connected
        {
            //we need to store the samples in EEPROM untill reconnects
            if (addr < 1024)
            //328 has 1024 bytes ie 0 to 1023
            {
                //only write if have spare space in EEPROM
                adc(); 
                if (numbersample == 6 )
                //we have 6 samples over 1 minute
                {
                    minutesample /= 6;  //get average of 6 samples
                    EEPROM.write(addr, (minutesample%256));  //store least byte
                    EEPROM.write(addr + 1, (minutesample/256));  //store most byte
                    addr += 2;  //next available storage for sample 
                    minutesample = 0;  //reset the sample
                    numbersample = 0;  //reset the counter
                }//end of have 6 samples
                digitalWrite(11, LOW);  //turn led off
            } //end of < 1024  then full so dont write
        }//end of serial not available
    }//end of wait to sample pressure 
    digitalWrite(12, LOW);  //turn led off
}//end of loop

void adc()
{
    unsigned int sample = 0;   //reset the sample value
    int i = 0;
    for(i; i<=64; i++)
    sample += analogRead(2);  //get 64 samples added together 1024 x 64 max
    sample = sample / 64;  //get the average of 64 samples
    minutesample +=sample;  //add the 10 second sample
    numbersample +=1; //the next sample
}//end of adc

int main(void)
{
    init();
    setup();
    for (;;)
    loop();
    return 0;
}

Arduino hardware

The schematic is drawn using Eagle: http://www.cadsoft.de/download.htm. The files are in the zip file.

Image 2

The bread board picture is drawn using Peeble: http://rev-ed.co.uk/picaxe/pebble/. The files are in the zip file.

Image 3

License

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