Tuesday, September 18, 2012

More Bugs Solved and Meter Reading Program Performance              


There were a lot of little bugs to work out of the program. It tended to crash at like 3 in the morning when I was not around. Occasionally the program misreads an image digit to give a nonsensical result. There are two types of misreads. Because of the fish-eye camera distortion, the meter’s end digits are squished and they required some serious tweaking to get consistent reads. Almost none of these types of misreads occur now. Also, I check each of the meter’s ID images and Test images (“151- -” and “88888”) to see that they decoded correctly. If there is an error in decoding either of these images then a repeat calibration is performed.


The other kind of misread occurs when an image is taken just as the LCD segments are changing from one display to the next.  The camera is so fast that it can catch the segments in mid change. It would sometimes catch the Blank image with a little of the previous Test image’s ghost. Since the Blank image is subtracted from all the other images it would really screw up the decoded  values. This problem was solved by quickly taking another image of the Blank before it disappears in five seconds. The second image is always good. The rest of the following four meter images are shot at sequenced five second internals and shouldn’t catch the segments changing, except that it turns out the power usage Rate is displayed in real-time and updates about once a second. So there is no way around a rare misread of the Rate image; about one misread an hour, out of a total of 144 reads an hour. The solution is to just not record the misread Rate result. The graphing software easily handles missing data because it is essentially an X-Y plot where Y is the rate and X is the time. Any missing data goes completely unnoticed. 

Here are a couple of graphs of the first few days of collected data.



The compiled VB6 program is a measly 60K in size and runs in the background with no tab in the taskbar using less than 2% of my modest CPU’s resources. During calibrations, (a few times per hour), the CPU usage increases to about 15% for less than a second.











The finished meter reading program's code is a bit gangly and I may spend some time cleaning it up to make it more structured. Sometimes this causes more problems than it is worth. I will probably let the program run for a couple of months first, to flush out any other bugs.

=====================================================

I am starting to think about new projects:
I am interested in taking the doghouse off the power grid (I would need to produce about 200 watts 24/7) or...
a solar run spa project or ...
use a trained artificial intelligence program rather than this algorithm based program to read the electric meter or...
making an electric vehicle or...
start a new robotic project (a 6 legged dandelion flower seeker and destroyer) or...
document and finish a micro-controller animatronic samurai project I started last winter or...
who knows what.

Wednesday, August 15, 2012


Using  the Serial Camera to Read the Smart-Meter                              



I had a couple of technical problems with the serial camera to solve before I could have my computer turn an image of the meter face into a numeric electric usage rate. During the day, there are reflections off the meter face. The very bright reflections from the white LEDs I had installed on the camera caused the image to go to saturation in places. The difference between day and night lighting made it difficult to detect the widely changing pixel values over a 24 hour cycle.  I solved these problems by using an electroluminesent (EL) flat panel light source and by putting a black plastic bag over the camera and meter.   I bought a blue-green EL panel and 5vdc driver on eBay ($15.49 delivered!). With a pair of scissors, I trimmed the EL panel down to the size of the camera box and glued it on to the box face such that the lens would stick though a hole in the center of the panel. The resulting camera images were in shades of blue-green with RGB pixel values of approximately, G = B and R = 0. There are still some reflections of the camera lens itself and the images are reminiscent of the HAL camera/eye in 2001Space Odyssey, except  this image is blue-green, instead of red.

The Smart Meter display, cycles through five different readouts of 5 seconds each, which repeat every 25 seconds. The cycle times are not exact; I suspect that the meter’s microcontroller occasionally becomes busy with networking functions and so the timing becomes inconsistent. The first order of business is to correctly identify each of these images:
1.       A Blank image, where the meter is blank (except for the letters CLS off-screen to the left.)
2.       A Meter reading of total Kwatts used so far.
3.       A meter Identifier, which for my meter is always “151”.
4.       The Rate of electric usage in Kwatts per hour.
5.       A Test screen where all the meter's LCD elements are active.

The screen identification algorithm I  went with, is as follows: I start out by reading images from the camera, as fast as it will go, for the first 35 seconds. An image brightness is calculated by adding up each of the 160 green-blue bytes along a single centered horizontal line for each image. Transitions between successive images are scored by comparing brightness to the prior image. The biggest difference between brightnesses is when the Test screen transitions to the Blank screen. This maximum score is saved and used to calculate a cutoff value to detect successive blank screens. Now when a Blank screen is detected, a timer is started to read the next four images at five second intervals, but after the reading the fifth (Test) image, the timer reverts to quick reading images until a Blank image is found again. This algorithm seems to work well and I have not seen any misreading of image types.


Any image of interest can be turned in to a monochromatic image by subtracting from each pixel, the corresponding pixel from the Blank image. Subtracting the Blank’s background color and testing against a cutoff value generates a two toned image. I use the Test monochromatic image to find the locations of pixels in each segment of five LCD numerals. This process is CPU intense but when found, these segment pixel locations can be used for all images until the camera is moved. These segment pixel locations are scored against pixels on the Meter reading images and on the Rate images to generate a seven segment score that is translated to a numeric digit with the help of a look-up table (“1111111” is the digit 8 and “0101100” is the digit 7.) Common misreads are incorporated into the lookup table as well. There is a fair amount of parallax that distorts the furthest left and right digits. The decoded meter reading, rate and system time are saved in an array and backed to a Daily named file on an hourly basis. I included a little real-time electrical usage graph in a picture control, but I usually use another Graphing utility program to view the data  from the saved files . All files are read/written  to a 1G flash drive to allow my hard drive to get some rest at night.

Tuesday, July 31, 2012


Download VB6 CameraTest Files                           

I experimented around with the free Google Doc web storage system this afternoon and it was easy to upload the VB6 files that I have been working on. You can download them at the following link. Feel free to use them in anyway that you want. 




 
 Camera Image Quality

I have the camera pointed at the very plain looking meter face from about one and a half  inches away. I cannot see  much difference in the image quality for the whole range of compression ratios. I do know that the camera loads the image the fastest when the compression is set to about 210. From that short focal distance, there is quite a bit of fish-eye type of distortion to the image. I plan to experiment with other camera lenses that I have lying about. There is quite a bit of reflection off of the meter face. The reflected LED light washes out the image in places. I will be experimenting with other kinds of illumination. I have already written a preliminary image recognition program but I am having problems because of the lighting.

Monday, July 30, 2012


A Serial Camera Test Program                                            












The current 54K version of my Camera Test program works but there are still some bugs. There is a glitch with saving JPGs at the biggest image size because the Mscomm input buffer can’t handle binary input of that size. It’s a low priority problem for me because the 320x240 image size loads much faster and that is what I will be using for my Smart Meter project. I am still thinking about the problem though. I did not include an option to change the Mscomm port because I only have one on my computer. I did not include a power saving control because my power source is hard wired. I included a text box that shows how long a picture takes to read, download, save to a JPG file and then load into a picturebox control. The time is under 2 seconds for a 320x240 image at 115200. The JPG is saved to the path\name in the textbox. You can use the drive/directory/file listboxes to change the path\name or you can just type it in. An invalid path\name crashes the program. Try to use the “End Program” control button instead of the red X close icon to shutdown the program.

Here is the code for the current version of the camera test program:

'A Test Program for the LinkSprite LS-Y201 Serial Camera

Dim CurrentImageSize As String

Private Sub Form_Load()
MSComm1.CommPort = 1
MSComm1.Settings = "38400,n,8,1"
MSComm1.InputMode = comInputModeBinary
MSComm1.InBufferSize = 25000
MSComm1.InputLen = 25000
MSComm1.PortOpen = True
CurrentImageSize = "160x120"
DoEvents

txtFileName.Text = "M:\Meter\new.jpg"
'send picture size instruction to camera
txtError.Text = SetCameraImageSize(MSComm1, CurrentImageSize)
Pause 100 'wait 100 ms
'send picture compression instruction to camera
hsbCompression.Value = 54 'calls setcompression and empties the input buffer
UpdateForm
End Sub


Private Sub cmdEnd_Click()

Dim S As String
S = ResetCamera(MSComm1) ' reset the camera
S = CameraHexOutputString(MSComm1) 'read and clear the input buffer.
MSComm1.PortOpen = False ' close MScomm1
DoEvents
End
End Sub


Private Sub UpdateForm() 'sets the background colors of the commamd buttons
Dim S As String

S = MSComm1.Settings ' going to extract baud rate string
S = Mid(S, 1, Len(S) - 6)

For I = 0 To 4 'set background colors on baud rate buttons
    cmdBaud(I).BackColor = &H8000000F
    If cmdBaud(I).Caption = S Then cmdBaud(I).BackColor = vbGreen
Next I

For I = 0 To 2 'set background colors on image size buttons
    cmdImageSize(I).BackColor = &H8000000F
    If cmdImageSize(I).Caption = CurrentImageSize Then cmdImageSize(I).BackColor = vbGreen
Next I

End Sub


Private Sub cmdReset_Click()

txtCameraOutput.Text = "Wait"
txtError.Text = ResetCamera(MSComm1)
txtCameraOutput.Text = CameraHexOutputString(MSComm1)
UpdateForm
End Sub


Private Sub cmdSaveJPG_Click()
t = Timer 'txtError.Text = ""
txtCameraOutput.Text = "Wait"

txtError.Text = SaveCameraJPG(MSComm1, txtFileName.Text)
txtCameraOutput.Text = CameraHexOutputString(MSComm1)
Set Picture1.Picture = LoadPicture(txtFileName.Text) '("M:\Meter\new.jpg")
txtTime.Text = Format(Timer - t, "0.00")
End Sub


Private Sub cmdBaud_Click(Index As Integer)
txtError.Text = ""
txtCameraOutput.Text = "Wait"

txtError.Text = SetCameraBaud(MSComm1, cmdBaud(Index).Caption)
txtCameraOutput.Text = CameraHexOutputString(MSComm1)

UpdateForm
End Sub

Private Sub cmdImageSize_Click(Index As Integer)
txtError.Text = ""
txtCameraOutput.Text = "Wait"
txtError.Text = SetCameraImageSize(MSComm1, cmdImageSize(Index).Caption)
txtCameraOutput.Text = CameraHexOutputString(MSComm1)
CurrentImageSize = cmdImageSize(Index).Caption
UpdateForm
End Sub


Private Sub hsbCompression_Change()
txtError.Text = ""
txtCameraOutput.Text = "Wait"

'send compression command to camera.
txtError.Text = SetCameraCompression(MSComm1, Str(hsbCompression.Value))
txtCameraOutput.Text = CameraHexOutputString(MSComm1)
End Sub


Private Sub hsbCompression_Scroll()
txtCompression.Text = Str(hsbCompression.Value) ' write compression value into a text box.
End Sub


‘***  I lifted the following code from somewhere else... It if left mostly as found. ****

Private Sub DirListBox_Change()
FileListBox.Path = DirListBox.Path
End Sub

Private Sub FileListBox_Click()
Dim S As String
S = FileListBox.Path
If Right(FileListBox.Path, 1) <> "\" Then S = FileListBox.Path + "\"
txtFileName.Text = S + FileListBox.FileName
End Sub

Private Sub cmdNew_Click()
Dim S As String
S = FileListBox.Path
If Right(FileListBox.Path, 1) <> "\" Then S = FileListBox.Path + "\"
txtFileName.Text = S + "new.jpg"
End Sub

Private Sub DriveListBox_Change()
Dim msg As String
Dim result As String

On Error GoTo Error
DirListBox.Path = DriveListBox.Drive
Exit Sub

Error: msg = "Error: " & Err.Number & ": " & Err.Description
result = MsgBox(msg, vbOKCancel + vbExclamation, "No Data")

If result = vbOK Then
Resume
Else
DriveListBox.Drive = DirListBox.Path
Err.Clear
Exit Sub
End If
End Sub

Using Serial Camera Functions in a VB6 Form    

  







The Mscomm control must be initialized before using the camera functions. You can do this at design-time by changing the values in the Mscomm control or during run-time by including the following code:

Private Sub Form_Load()
   MSComm1.CommPort = 1
   MSComm1.Settings = "38400,n,8,1"
   MSComm1.InputMode = comInputModeBinary
   MSComm1.InBufferSize = 25000
   MSComm1.InputLen = 25000
   MSComm1.PortOpen = True
End Sub

Here is an example of using  a serial camera function.

Private Sub cmdBaud_Click(Index As Integer)
   txtCameraOutput.Text = "Wait"
   txtError.Text = SetCameraBaud(MSComm1, cmdBaud(Index).Caption)
   txtCameraOutput.Text = CameraHexOutputString(MSComm1)
End Sub

Pressing an indexed cmdBaud button sends that button’s caption “115200” baud rate to the camera, writes “Camera Baud Rate = 115200” in the txtError textbox and writes the returned “76  00  24  00  00” string in the txtCameraOutput textbox.

Note on the current design of the CameraHexOutputString function: This function will read and clear everything in the Mscomm input buffer. First the function checks the buffer size every 0.2 seconds until the buffer size is the same for two consecutive checks. Then the buffer is read all at once and becomes the String value of this function. There is a limit of something over 25,000 on the size of the input buffer in binary mode. I am working on a different way to read the input buffer that allows for bigger input strings. At the moment, this problem is interfering with the saving of JPGs at the largest image size setting. 



SerialCameraFunctions.bas module Code                               


Try copying and pasting the following VB6 code into a standard module named SerialCameraFunctions.bas and then use the functions as needed on a form that you create.



'******************************************************************************************
'******************************************************************************************
'***                                                                                                                                                                       ***
'***  Module: SerialCameraFunctions for the LS-Y201 RS232 Camera                                                                ***
'***    SaveCameraJPG(Comm As MSComm, FileName As String) As String. Example:"F:\Camera\Pic1.jpg")    ***
'***    SetCameraBaud(Comm As MSComm, Baud As String) As String. Example: "38400"  )                             ***
'***    SetCameraImageSize(Comm As MSComm, Size As String) As String. Example: "320x240" )                   ***
'***    SetCameraCompression(Comm As MSComm, CompRate As String) As String.("1" to "255")                   ***
'***    ResetCamera (Comm As MSComm)                                                                                                           ***
'***    CameraHexOutputString(Comm As MSComm)                                                                                          ***
'***  All functions must include the mscomm name as the first argument.                                                         ***
'***  All camera functions output a string about the camera's health.                                                               ***
'***  I do not need the power saving command so no sub for that here.                                                          ***
'*****************************************************************************************
'*****************************************************************************************

'These declarations needed for the very cool Pause function.
Private Declare Function GetTickCount Lib "kernel32" () As Long
Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
'Borrowed Pause function, so I can get delays in this standard module,
'where normal timer controls are not available.
Public Sub Pause(Optional ms As Long = 200) ' delay in ms. 200 ms default.
    On Error Resume Next
    Dim tc As Long
    tc = GetTickCount
    While GetTickCount < tc + ms: Sleep 1: DoEvents: Wend
End Sub


'I do these steps to save a picture:
'(1) Freeze the image in the camera with the "Take Picture" command.
'(2) Find the approximate file length with the "Read JPG File Size" command.
'(3) Tell the camera to send its JPG content to Comm1 input buffer.
'(4) Transfer the input buffer into a byte array.
'(5) Repair the byte array by trimming extra bytes off beginning and end.
'(6) Send the "Stop Taking Pictures" command so new pictures can happen.
'(7) Write the byte array to a JPG file.
Public Function SaveCameraJPG(Comm As MSComm, ByVal FileName As String) As String
Dim S As String
Dim TempArray() As Byte
Dim InputArray() As Byte
Dim OutputArray() As Byte
Dim CurrentInputSize As Integer
Dim lngFile As Long
On Error GoTo ErrorHandler

Comm.Output = CameraCommand("56 00 36 01 00") ' (1) Send take-picture command to camera.
    Pause 500 ' Wait 500 ms.
Comm.InBufferCount = 0 'discard input buffer.

Comm.Output = CameraCommand("56 00 34 01 00") ' (2) Send read-jpg-size command to camera.

S = CameraHexOutputString(Comm) 'reads the size string of the jpg still in the camera
L = Len(S) ' the number of charactors in the size string.
S = Mid(S, 29, 3) + Mid(S, 33, 2) 'pick out the two hex string digits concerning file size.

' (3) Send read jpg command to camera.
Comm.Output = CameraCommand("56 00 32 0C 00 0A 00 00 00 00 00 00 " + S + " 00 0A")

Do 'this loop until comm buffer count remains constant for 0.2 seconds.
    CurrentInputSize = Comm.InBufferCount
    Pause 200 'Wait (0.2)
Loop Until CurrentInputSize = Comm.InBufferCount

TempArray = Comm.Input ' (4) All data fits into a Byte Array!

OutputArray = Repaired(TempArray) ' (5) Byte array for output to file was repaired.

Comm.Output = CameraCommand("56 00 36 01 03") ' (6) Send stop-taking-picture command to camera.


Kill (FileName) 'deletes old file if any, goes to error handler if no file.
DoEvents


lngFile = FreeFile() 'get next free file number from the system.
Open FileName For Binary Access Write As lngFile ' open JPG file for writing.
Put lngFile, , OutputArray ' (7) File is written all at once.
Close lngFile ' close file
SaveCameraJPG = "JPG saved to " + FileName ' String returned by this function.

Exit Function

ErrorHandler:
If Err.Number = 53 Then
    SaveCameraJPG = " Making new file " + FileName
Else
    SaveCameraJPG = Error(Err.Number)                                    'returned message
End If
Resume Next
    
End Function


'OK, for the image size to take effect, a reset has to happen.
'A reset changes the baud rate back to the default 38400. What a pain in the ass!
'I am going to take care of the mess here, so that it is transparent to the user.
Public Function SetCameraImageSize(Comm As MSComm, ByVal Size As String) As String
Dim S As String, BaudRate As String
On Error GoTo ErrorHandler

S = Comm.Settings ' going to extract baud rate string
BaudRate = Mid(S, 1, Len(S) - 6)

If Size = "160x120" Then S = "22"   ' Find the rate specific last byte for
If Size = "320x240" Then S = "11"   ' the hex image size command string.
If Size = "640x480" Then S = "00"

' Send image size command to camera.
Comm.Output = CameraCommand("56 00 31 05 04 01 00 19 " + S)
Pause 500 ' wait 500 ms before sending next command

S = ResetCamera(Comm) 'Send reset command to camera.
Pause 500 ' wait 500 ms before sending next command

S = SetCameraBaud(Comm, BaudRate) 'Send prior baud rate command to camera.

SetCameraImageSize = "Image size = " + Size 'returned message is image size.

Exit Function

ErrorHandler:
SetCameraImageSize = Error(Err.Number)  'returned message

End Function

Public Function SetCameraBaud(Comm As MSComm, ByVal Baud As String) As String
    Dim S As String
    On Error GoTo ErrorHandler

    If Baud = "9600" Then S = "AE C8" ' hex codes associated with these baud rates.
    If Baud = "19200" Then S = "56 E4"
    If Baud = "38400" Then S = "2A F2"
    If Baud = "57600" Then S = "1C 4C"
    If Baud = "115200" Then S = "0D A6"
   
    Comm.Output = CameraCommand("56 00 24 03 01 " + S) ' Send baud rate command to camera.
    'Pause 500 ' Give the camera a chance to output responce using old baud rate.
   
    Comm.Settings = Baud + ",N,8,1" ' Change Comm1 baud rate to match the camera's new rate.
    SetCameraBaud = "Camera Baud Rate = " + Baud           'returned message

Exit Function

ErrorHandler:
    SetCameraBaud = Error(Err.Number)                          'returned message

End Function


Public Function SetCameraCompression(Comm As MSComm, ByVal CompRate As String) As String
Dim S As String, L As Integer
On Error GoTo ErrorHandler

    S = Hex(Val(CompRate)) ' converts CompRate string to decimal to hex string
    If Len(S) = 1 Then S = "0" + S ' adds formating leading zero if needed.
    Comm.Output = CameraCommand("56 00 31 05 01 01 12 04 " + S) ' Send compression command to camera.
   
    SetCameraCompression = "Camera compression = " + CompRate               'returned message

Exit Function

ErrorHandler:
SetCameraCompression = Error(Err.Number)                                    'returned message
'Resume Next
End Function


Public Function ResetCamera(Comm As MSComm) As String

On Error GoTo ErrorHandler

Comm.Output = CameraCommand("56 00 26 00") ' Send reset command to camera.
Comm.Settings = "38400,N,8,1" 'Resetting camera changes its baud rate to the default.
ResetCamera = "Camera Reset. Baud rate now 38400" 'returned message

Exit Function

ErrorHandler:
ResetCamera = Error(Err.Number)                         'returned message

End Function



Public Function CameraCommand(ByVal strInput As String) As Byte()
'This function takes a hex looking string input and changes it into a binary array for output.
'Input must be in the form "HH HH HH ...HH" (3 * N - 1 charactors long including spaces)

Dim Temp(20) As Byte
Dim I As Integer, N As Integer
  
N = Int((Len(strInput) + 1) / 3) ' number of hex input pairs.

For I = 0 To N - 1 ' build temp array. First pair will be #0 in array
    Temp(I) = CByte("&H" + Mid(strInput, I * 3 + 1, 2)) ' string to hex to decimal byte
Next I

CameraCommand = Temp ' Output is a byte array representation of a camera command
End Function




Public Function CameraHexOutputString(Comm As MSComm) As String
'Makes a string of the camera output waiting in the comm input buffer.
'Exits function when input buffer is quiessent for 0.2 seconds.

Dim CurrentInputSize As Integer
Dim I As Integer
Dim S As String
Dim TempArray() As Byte

Do 'this loop until comm buffer count remains constant for 0.2 seconds.
    CurrentInputSize = Comm.InBufferCount
    Pause 200 'Wait 0.2 seconds
Loop Until CurrentInputSize = Comm.InBufferCount

TempArray = Comm.Input '  All data fits into Bytearray!

For I = 0 To UBound(TempArray)  ' Build up the output string
    S = Hex(TempArray(I))   ' Hex output.
    If Len(S) = 1 Then S = "0" + S 'add a leading "0" if only one hex digit.
    CameraHexOutputString = CameraHexOutputString + S + "  " 'Build output string.
Next I

End Function

Public Function Repaired(InArray() As Byte) As Byte()
Dim OutArray() As Byte
Dim I As Integer, J As Integer, N As Integer
Dim Start As Integer, Finish As Integer
Dim AddOn As Boolean

AddOn = False
N = UBound(InArray)
Finish = N

For I = N - 1 To 1 Step -1 'start at end of input array and work backwards.

    If Hex(InArray(I)) = "FF" Then
        If Hex(InArray(I + 1)) = "D8" Then Start = I 'this is the start byte
        If Hex(InArray(I + 1)) = "D9" Then Finish = I + 1 ' this is the stop byte
    End If
Next I

J = Finish - Start 'the length of the repaired array
If Finish = N Then ' never found the stop byte, so better add the stop bytes
    J = J + 2   'increase the length of the repaired array for the two stop bytes
    AddOn = True ' remind me to add stop bytes
End If

ReDim OutArray(J) As Byte

For I = Start To Finish
    OutArray(I - Start) = InArray(I)
Next I

If AddOn Then 'need to add stop bytes
    B = 255
    OutArray(J - 1) = B
    B = 217
    OutArray(J) = B
End If
Repaired = OutArray

End Function
About SerialCameraFunctions.bas 
                                












Then when I am designing a VB6 form and I need to communicate with the camera, I use the following functions contained in the module SerialCameraFunctions.bas.  In the following examples, S is a variable of the String type:

S = ResetCamera(Mscomm1)
S = SetCameraBaud (Mscomm1, BaudRate as String)  ' Example: "38400"               
S = SetCameraImageSize (Mscomm1, ImageSize as String) 'Example: "320x240"
S = SetCameraCompression(Mscomm1, CompRate as String)  'Examples: "1"to"255"              
S = SaveCameraJPG (Mscomm1, FileName as String) 'Example: "F:\Camera\Pic1.jpg"
S = CameraHexOutputString(Mscomm1)

All the functions include a comm argument to point to the port you are using.

All other arguments are of String type.

All the functions return a String indicating what the camera just did or else an error description.

The ResetCamera function does change the baud rate back to the default but, I fixed the SetImageSize function such that it takes effect immediately and the baud rate stays unchanged.

The SaveCameraJPG function takes as little as 1.55 seconds to complete (160x120, 115200, 210 comp) or as long as 51 seconds (640x480, 9600, 54 comp).

I didn’t need the camera’s Save Power command so I did not write a corresponding function.

About Camera Instructions , VB6 and Camera Quirks                                                                         

There is some confusion about the camera instructions and camera output. Typing commands into Hyperterminal leads to nonsensical output. This is because  the camera takes, and outputs, binary data, not ASCII data. What’s more, the camera documentation would lead you to believe that the commands are in hexadecimal form. All data is binary, which for VB6 at least, is of decimal Byte type. Each byte of data is a decimal from 0 to 255 and corresponds to hexadecimals of 00 to FF.  The documentation uses the hexadecimal format, but VB6 easy converts hex to  decimal by preceding the hex number with “&H”. In VB6, &H1A is the same as using the decimal 26.  Decimal numbers can be changed into hex (in String format) by using the Hex command. Print Hex(26) would print “1A”, and Print Hex(15) would print “F”  (to get back the missing “0”, test for a string length of one and then append the leading “0”).

The other key piece of information you need to know, is when using the Microsoft Comm controls, the way you get binary data in and out of the comm buffers is by using Binary Arrays. In VB6, when the comm control is set to binary mode, a comm buffer can send all (or part) of its data and store it in a Byte Array like this:

Dim InputArray() as Byte
InputArray = Mscomm1.Input

Each of the binary bytes is then individually accessible by instructions like:

                                                  ByteNumberZero = InputArray(0)

The lower bound of the byte array is zero and the upper bound is found with:

Ubound(InputArray).

Camera Command Quirks


When the camera’s Reset command is sent or if the camera is turned off and then on, most of the camera’s settings are retained, but the baud rate returns to it’s default value of 38400. (I suspect the compression rate may be reset as well; it's hard to tell.) Any program trying to communicate with the camera should try to anticipate the baud rate change or communication will be lost.

When a camera Image Size command is sent, it must be followed by a Reset command in order for the image size change to take effect. The Reset may change the baud rate and so you must anticipate that as well.

If camera commands are sent too rapidly in succession, the camera gets confused. 

It sometimes takes many seconds for the camera’s data to accumulate into the Mscomm input buffer. Reading the input buffer too soon causes incomplete reads.

When you exit the program, the camera retains the parameters you were using at the time of exit. It is best to return the camera to some default values before terminating the program so that you can know what state the camera is in when the program starts anew.

I address most of these issues in a set of camera functions I wrote in VB6 and placed in a standard module called SerialCameraFunctions.bas. 

Connecting the RS-232 Camera                               


I bought a LinkSprite LS-Y201 serial port camera module off of the web a few weeks ago. Careful, LinkSprite sells two serial cameras; this one has the max232 chip and is ready to connect directly to the computer’s serial port. My understanding is that the one without the UART chip uses TTL level voltages to connect directly to micro-controllers. Most of the discussion on the web is by robot enthusiasts, using the micro-controller interfaceable camera. There is documentation and a demo program available for this camera, but as I found out later, the info can be confusing and the program worked intermittently for me.


Some of the problem may be the translation from Chinese. Searching the web I found that others have had similar problems. Through trial and error, I have mostly figured this camera out and now have a working demo program written in VB6.

The Camera module fits nicely into a 2” x 2.5” x .75” project box with holes drilled for the lens and cable. The cable connector is in the down direction of the camera field. Inside the box, there was room for a couple of illuminating LEDs with current limiting resistors, wired to the 5v supply. The camera can view images in infrared light but I found out that LCD images (like the numerals on the smart meter face) are invisible in infrared light! I mounted the camera in front of the smart meter and connected it to the four wire telephone line leading back to the RS-232 port connector. Make sure the camera’s TXD is connected to the computer’s RXD (and TXD to RXD). For the 5vdc connection, I used the computer’s power supply through an inline 0.75 amp fuse. I blew the fuse several times during the installation of the camera. If you do not use a fuse, you are going to ruin your computer.

When the camera is powered up you should see the “ Init end” message when Comm1 is viewed by Hyperterminal. 


The Electric Meter Saga                               

For over three years, I had been happily monitoring and recording my household electric usage by detecting how often the analog electric meter’s aluminum disk revolved. I had a blinking LED at the bottom of the meter and a synced detector at the top of the meter. Twice each revolution of the disk, there was a clear light path to the detector which would send a pulse to a UART that would send a byte through my computer’s serial port to be  recorded by a small Visual Basic 6 program I had running in the background.


In late June, SCE installed a new Smart meter at my house. They claim I can monitor my electric usage on-line but it appears  to me they have pulled that function off of their website for residential customers. Even if the site did become active, the information is in averaged one hour blocks. This is much different from the real-time graphing to which I had grown accustomed.



There are several strategies one could use to monitor electric usage. There are very good commercial products available on the web that sense the current flow above the mains in the SCE panel. One might detect small voltage drops along the main conductors that would be proportional to the current flow. I could use clamping current meters around the AC power lines that feed in though the roof.  Some folks have attempted to intercept and interpret the smart meter’s network signal. I thought about using an old CD drive’s laser, focused on the meter face to detect the LCD bar that moves in response to current usage.  Could I teach  my computer to visually read the meter using a camera? Because I wanted to know more about computer vision, I decided to take this approach.

The electric meter is about 75 feet from my backyard workshop (the doghouse) computer (modest XP sp3). There is an abandoned four-conductor unshielded telephone wire that runs around the eves of my house from the doghouse to near the meter. This is what I used with my old detection system. I found through trial and error that a USB camera stops working when USB cables are longer than about ten feet. I found a cheep RS-232 camera on the web and thought I would see if I could get that to work.