View Full Version : Controlling 8 Relay via LPT Port Using InpOut32.dll
ChandraMDE
06-05-2013, 16:49
Hi,
This is a script for controlling 8 relay connected to Data Port of LPT Parallel Port.
It's using InpOut32 library.
Hope somebody will find it useful.
Best Regards,
Chandra MDE
8249
8250
8248 <-- this is the wrong one ( do not download :) )
8251 <-- this is the right one
8253 <-- this is the last updated one (compatible with thinBasic 1.9.6.0) - Thanks Petr
Thank you Petr for trying, so I can make this correction immediately.
Edit: Add new screenshot. It's running perfectly on P2-333MHz with Win98.
8252
Petr Schreiber
06-05-2013, 20:44
Thanks for example of interesting application,
but it seems you possibly attached some older code - when I execute it, it looks different from the screenshot and it also does not use the library in any way :)
Petr
ChandraMDE
07-05-2013, 03:09
Thank you for trying.
But, I don't understand... the script is running OK and have been tested with my LPT Hardware.
I'm using thinBasic 1.8.9.0 on XEON 3.2GHz with 32-bit XP SP3.
The inpout32.dll should be placed in the same folder of the application, or in System32 or simply in Windows folder.
Correction:
You're right, Petr. I've mistakenly zipped the wrong file. It should've been tb_ltp8rel_gui.tbasic !
I have added the correct .zip file on the first post. Thanks.
Btw, here's the code. Please try again and I would love to know how it goes.
Thanks.
' Basic Template for custom dialog
' Start Date 05-06-2013
' Created by
Uses "UI"
' -- ID numbers of controls
Begin ControlID
%btnRelay1On
%btnRelay1Off
%btnRelay2On
%btnRelay2Off
%btnRelay3On
%btnRelay3Off
%btnRelay4On
%btnRelay4Off
%btnRelay5On
%btnRelay5Off
%btnRelay6On
%btnRelay6Off
%btnRelay7On
%btnRelay7Off
%btnRelay8On
%btnRelay8Off
%label1
%label2
%label3
End ControlID
Begin Const
%MAIN_WIDTH = 505
%MAIN_HEIGHT = 240
%_On = TRUE
%_Off = FALSE
End Const
Declare Sub Out32 Lib "inpout32.dll" Alias "Out32" (ByVal portaddress As Word, ByVal portvalue As Byte)
Declare Sub Inp32 Lib "inpout32.dll" Alias "Inp32" (ByVal portaddress As Word) As Byte
Global LPTAddress As Word = &H378
Global LPTValue As Byte
Dim RelayOn(8) As Byte = [1, 2, 4, 8, 16, 32, 64, 128]
Dim RelayOff(8) As Byte = [255-1, 255-2, 255-4, 255-8, 255-16, 255-32, 255-64, 255-128]
' -- Create dialog here
Function TBMain()
Local hDlg As DWord
Dialog Font "Tahoma", 9
Dialog New Pixels, 0, "LPT-8REL Controller Using INPOUT32.DLL (LPT @378H)",-1,-1, %MAIN_WIDTH, %MAIN_HEIGHT, _
%WS_POPUP Or %WS_VISIBLE Or %WS_CAPTION Or %WS_SYSMENU Or %WS_MINIMIZEBOX To hDlg
' -- Place controls here
Control Add Button, hDlg, %btnRelay1On, "RELAY-1 ON" , 15, 20, 100, 30
Control Add Button, hDlg, %btnRelay1Off, "RELAY-1 OFF", 15, 55, 100, 30
Control Add Button, hDlg, %btnRelay2On, "RELAY-2 ON" , 140, 20, 100, 30
Control Add Button, hDlg, %btnRelay2Off, "RELAY-2 OFF", 140, 55, 100, 30
Control Add Button, hDlg, %btnRelay3On, "RELAY-3 ON" , 265, 20, 100, 30
Control Add Button, hDlg, %btnRelay3Off, "RELAY-3 OFF", 265, 55, 100, 30
Control Add Button, hDlg, %btnRelay4On, "RELAY-4 ON" , 390, 20, 100, 30
Control Add Button, hDlg, %btnRelay4Off, "RELAY-4 OFF", 390, 55, 100, 30
Control Add Button, hDlg, %btnRelay5On, "RELAY-5 ON" , 15, 105, 100, 30
Control Add Button, hDlg, %btnRelay5Off, "RELAY-5 OFF", 15, 140, 100, 30
Control Add Button, hDlg, %btnRelay6On, "RELAY-6 ON" , 140, 105, 100, 30
Control Add Button, hDlg, %btnRelay6Off, "RELAY-6 OFF", 140, 140, 100, 30
Control Add Button, hDlg, %btnRelay7On, "RELAY-7 ON" , 265, 105, 100, 30
Control Add Button, hDlg, %btnRelay7Off, "RELAY-7 OFF", 265, 140, 100, 30
Control Add Button, hDlg, %btnRelay8On, "RELAY-8 ON" , 390, 105, 100, 30
Control Add Button, hDlg, %btnRelay8Off, "RELAY-8 OFF", 390, 140, 100, 30
Control Add Label, hDlg, %label1, "LPT-8REL Controller by Chandra MDE", 20, 200, 300, 15
Control Add Label, hDlg, %label2, "Teknik Elektro Links - http://teknikelektrolinks.com", 20, 216, 300, 15
Control Add Label, hDlg, %label3, "[Created using thinBasic]", 350, 216, 300, 15
Dialog Show Modal hDlg, Call cbDialog
End Function
' -- Callback for dialog
CallBack Function cbDialog()
' -- Test for messages
Select Case CBMSG
Case %WM_INITDIALOG
' -- Put code to be executed after dialog creation here
Case %WM_COMMAND
Select Case CBCTL
Case %btnRelay1On
If CBCTLMSG = %BN_CLICKED Then
Call Relay(1, TRUE)
End If
Case %btnRelay1Off
If CBCTLMSG = %BN_CLICKED Then
Call Relay(1, FALSE)
End If
Case %btnRelay2On
If CBCTLMSG = %BN_CLICKED Then
Call Relay(2, TRUE)
End If
Case %btnRelay2Off
If CBCTLMSG = %BN_CLICKED Then
Call Relay(2, FALSE)
End If
Case %btnRelay3On
If CBCTLMSG = %BN_CLICKED Then
Call Relay(3, TRUE)
End If
Case %btnRelay3Off
If CBCTLMSG = %BN_CLICKED Then
Call Relay(3, FALSE)
End If
Case %btnRelay4On
If CBCTLMSG = %BN_CLICKED Then
Call Relay(4, TRUE)
End If
Case %btnRelay4Off
If CBCTLMSG = %BN_CLICKED Then
Call Relay(4, FALSE)
End If
Case %btnRelay5On
If CBCTLMSG = %BN_CLICKED Then
Call Relay(5, TRUE)
End If
Case %btnRelay5Off
If CBCTLMSG = %BN_CLICKED Then
Call Relay(5, FALSE)
End If
Case %btnRelay6On
If CBCTLMSG = %BN_CLICKED Then
Call Relay(6, TRUE)
End If
Case %btnRelay6Off
If CBCTLMSG = %BN_CLICKED Then
Call Relay(6, FALSE)
End If
Case %btnRelay7On
If CBCTLMSG = %BN_CLICKED Then
Call Relay(7, TRUE)
End If
Case %btnRelay7Off
If CBCTLMSG = %BN_CLICKED Then
Call Relay(7, FALSE)
End If
Case %btnRelay8On
If CBCTLMSG = %BN_CLICKED Then
Call Relay(8, TRUE)
End If
Case %btnRelay8Off
If CBCTLMSG = %BN_CLICKED Then
Call Relay(8, FALSE)
End If
End Select
Case %WM_CLOSE
' -- Put code to be executed before dialog end here
End Select
End Function
Sub Relay(ByVal N As Integer, ByVal V As Boolean)
LPTValue = Inp32(LPTAddress)
If V Then
LPTValue = LPTValue Or RelayOn(N)
Else
LPTValue = LPTValue And RelayOff(N)
End If
Out32(LPTAddress, LPTValue)
End Sub
Petr Schreiber
07-05-2013, 09:12
Hi,
thanks for your code. There is one little mistake, which could make a problem in future releases of ThinBASIC. Instead of:
Declare Sub Out32 Lib "inpout32.dll" Alias "Out32" (ByVal portaddress As Word, ByVal portvalue As Byte)
Declare Sub Inp32 Lib "inpout32.dll" Alias "Inp32" (ByVal portaddress As Word) As Byte
there should be:
Declare Sub Out32 Lib "inpout32.dll" Alias "Out32" (ByVal portaddress As Word, ByVal portvalue As Byte)
Declare Function Inp32 Lib "inpout32.dll" Alias "Inp32" (ByVal portaddress As Word) As Byte
Why? Well, because the Inp32 returns a value. When procedure does not return value, it is SUB, when it returns value, it is FUNCTION.
The program runs okay now on my PC! :)
Petr
ChandraMDE
07-05-2013, 09:58
Hi,
thanks for your code. There is one little mistake, which could make a problem in future releases of ThinBASIC. Instead of:
Declare Sub Out32 Lib "inpout32.dll" Alias "Out32" (ByVal portaddress As Word, ByVal portvalue As Byte)
Declare Sub Inp32 Lib "inpout32.dll" Alias "Inp32" (ByVal portaddress As Word) As Byte
there should be:
Declare Sub Out32 Lib "inpout32.dll" Alias "Out32" (ByVal portaddress As Word, ByVal portvalue As Byte)
Declare Function Inp32 Lib "inpout32.dll" Alias "Inp32" (ByVal portaddress As Word) As Byte
Why? Well, because the Inp32 returns a value. When procedure does not return value, it is SUB, when it returns value, it is FUNCTION.
The program runs okay now on my PC! :)
Thanks, Petr.
Yesterday, I was curios about what is/are the difference/s between Sub and Function.
So, I did a simple experiment. Here is the code:
Sub Relay(ByVal N As Integer, ByVal V As Boolean)
LPTValue = 0 '#1 - reset the LPTValue with zero
LPTValue = Inp32(LPTAddress) '#2 - read data port using Inp32(), and still a Sub rather than Function
Console_Write LPTValue '#3 - printout the data port
If V Then
LPTValue = LPTValue Or RelayOn(N)
Else
LPTValue = LPTValue And RelayOff(N)
End If
Out32(LPTAddress, LPTValue)
End Sub
The result is the LPTValue printed out by Console_Write is correct. If the Sub Inp32() doesn't return a value, than LPTValue should be zero.
So, using Sub Inp32() has no problem because it is in fact returning the correct value.
And then I tried this code:
Uses "console"
Sub a(ByVal n As Integer) As Integer
PrintL "n = ", n
a = n+5
End Sub
PrintL a(54)
WaitKey()
Still no problem and printout the correct value for n and a(54).
n = 54
59
This surely needs explanation. Please advice... :)
Petr Schreiber
07-05-2013, 15:32
Good questions again.
There are two kinds of SUBs/FUNCTIONs:
defined in ThinBASIC code
declared from DLLs
Internally, ThinBASIC makes no difference between SUB and FUNCTION. This is why this works:
Uses "console"
Sub a(ByVal n As Integer) As Integer
PrintL "n = ", n
a = n+5
End Sub
PrintL a(54)
WaitKey()
SUB is there just for better portability from other BASICs.
For SUBs/FUNCTIONs declared from DLLs there is a big difference, because they are called differentely and the call and its cleanup must be done more precisely. I am sure Eros would be able to explain this part better.
In ThinBASIC 1.9.6.0, the following would cause error in ThinBASIC:
Declare Sub Inp32 Lib "inpout32.dll" Alias "Inp32" (ByVal portaddress As Word) As Byte
... this is why I recommended to use correct naming convention.
Petr
ChandraMDE
07-05-2013, 17:13
It is safer to follow the correct syntax.
I'm attaching the last updated of tb_lpt8rel_gui.zip here, and on the first post of this thread.
Thanks again, Petr.
Best Regards.
The last updated:
8253
ErosOlmi
07-05-2013, 18:27
Hi,
Petr has explained the situation in a perfect way.
In thinBasic, SUB and FUNCTIONS are synonymous. The interpreted nature of thinBasic permits to make internal manipulation of data the way we like.
SUB and FUNCTIONS syntax are present for compatibility with other Basic programming languages letting easy copy/paste of code in/out of thinBasic.
The above for native thinBasic SUB and FUNCTIONS.
A completely different situation occurs when SUB and FUNCTIONS are used to DECLARE external function libraries (DLLs).
In those case a perfect match between DECLARE and known external SUB or FUNCTIONS must take place because depending on the type of the declare (SUB or FUNCTION) calling process have to get and clear returning data (if needed). Also a perfect match of passed parameters must take place otherwise a stack calling corruption (and quite sure a GPF) is just round the corner.
Ciao
Eros
ChandraMDE
08-05-2013, 02:50
This is great...
Getting great responses from senior members are really comforting and motivating.
:)
Thanks & Best Regards.
Petr Schreiber
09-05-2013, 16:02
Hi Chandra MDE,
I will add some more notes to your code here, mostly to give you idea about what is possible.
Variable declaration
This is the most simple thing, yet ThinBASIC offers multiple ways to do it. Let's have a look at part of your code:
...
Global LPTAddress As Word = &H378
Global LPTValue As Byte
Dim RelayOn(8) As Byte = [1, 2, 4, 8, 16, 32, 64, 128]
Dim RelayOff(8) As Byte = [255-1, 255-2, 255-4, 255-8, 255-16, 255-32, 255-64, 255-128]
' -- Create dialog here
Function TBMain()
Local hDlg As DWord
...
End Function
Idea #1: The DIM keyword
You can use the DIM keyword to specify not just arrays, but also variables. In case you use DIM in global space, it becomes global variable, in case you us DIM in local space, it becomes local variable. Easy. The code above would become:
...
Dim LPTAddress As Word = &H378
Dim LPTValue As Byte
Dim RelayOn(8) As Byte = 1, 2, 4, 8, 16, 32, 64, 128 ' -- Note: The [] brackets are not necessary for 1D array, they serve to force row order assignment in 2D arrays. In 1D they can be used too, but they make no effect.
Dim RelayOff(8) As Byte = 255-1, 255-2, 255-4, 255-8, 255-16, 255-32, 255-64, 255-128
' -- Create dialog here
Function TBMain()
Dim hDlg As DWord
...
End Function
Idea #2: Super short variable declaration
Inspired by some other languages, you can use short variable declaration with default types too. The code would become this:
...
Word LPTAddress = &H378
Byte LPTValue
Byte RelayOn(8) = 1, 2, 4, 8, 16, 32, 64, 128
Byte RelayOff(8) = 255-1, 255-2, 255-4, 255-8, 255-16, 255-32, 255-64, 255-128
' -- Create dialog here
Function TBMain()
Dword hDlg
...
End Function
Usage of CALL keyword
You can use CALL keyword for standard functions, but is is not necessary. So you can use both:
Call Relay(8, TRUE)
but this is also valid:
Relay(8, TRUE) ' -- Without CALL
So... what is the CALL good for? For function calls, where the function name is created at run time!
Have a look here:
Uses "Console"
Function TBMain()
String functionName
Long resultValue
functionName = "MyFunctionA"
Call functionName() To resultValue
PrintL "Result was " + resultValue
PrintL
functionName = "MyFunctionB"
Call functionName() To resultValue
PrintL "Result was " + resultValue
WaitKey
End Function
Function MyFunctionA() As Long
PrintL "I am in function A"
Return 1
End Function
Function MyFunctionB() As Long
PrintL "I am in function B"
Return 2
End Function
I hope it is interesting for you.
Petr
ChandraMDE,
I would be nice to have the schematic to your project.
Looks very interesting.
Bill
ChandraMDE
12-05-2013, 18:02
Hi, Billbo.
I'm sorry I can not provide you the schematic in the time soon because I didn't draw the schematic but directly made the PCB layout.
I'll share it once I finished the drawing.
Hi, Petr.
Thank you for sharing the coding practices. I really much appreciate it.
And inspired by the discussion we made couple days ago, I have made a new version of the program.
Please take look and please advice.
Thanks & Regards.
' TB_LPT8REL_GUI
' Start Date 05-06-2013
' Created by Chandra MDE - http://teknikelektrolinks.com
Uses "UI"
' -- ID numbers of controls
Begin ControlID
%btnRelay1On
%btnRelay2On
%btnRelay3On
%btnRelay4On
%btnRelay5On
%btnRelay6On
%btnRelay7On
%btnRelay8On
%btnRelay1Off
%btnRelay2Off
%btnRelay3Off
%btnRelay4Off
%btnRelay5Off
%btnRelay6Off
%btnRelay7Off
%btnRelay8Off
%label1
%label2
%label3
End ControlID
Begin Const
%MAIN_WIDTH = 505
%MAIN_HEIGHT = 240
End Const
Declare Sub Out32 Lib "inpout32.dll" Alias "Out32" (ByVal portaddress As Word, ByVal portvalue As Byte)
Declare Function Inp32 Lib "inpout32.dll" Alias "Inp32" (ByVal portaddress As Word) As Byte
Dim LPTAddress As Word = &H378
Dim LPTValue As Byte
Dim RelayOn(8) As Byte = [1, 2, 4, 8, 16, 32, 64, 128]
Dim RelayOff(8) As Byte = [255-1, 255-2, 255-4, 255-8, 255-16, 255-32, 255-64, 255-128]
' -- Create dialog here
Function TBMain()
Local hDlg As DWord
Dialog Font "Tahoma", 9
Dialog New Pixels, 0, "LPT-8REL Controller Using INPOUT32.DLL (LPT @378H)",-1,-1, %MAIN_WIDTH, %MAIN_HEIGHT,_
%WS_POPUP Or %WS_VISIBLE Or %WS_CAPTION Or %WS_SYSMENU Or %WS_MINIMIZEBOX To hDlg
' -- Place controls here
Control Add Button, hDlg, %btnRelay1On, "RELAY-1 ON" , 15, 20, 100, 30, Call DoRelay()
Control Add Button, hDlg, %btnRelay1Off, "RELAY-1 OFF", 15, 55, 100, 30, Call DoRelay()
Control Add Button, hDlg, %btnRelay2On, "RELAY-2 ON" , 140, 20, 100, 30, Call DoRelay()
Control Add Button, hDlg, %btnRelay2Off, "RELAY-2 OFF", 140, 55, 100, 30, Call DoRelay()
Control Add Button, hDlg, %btnRelay3On, "RELAY-3 ON" , 265, 20, 100, 30, Call DoRelay()
Control Add Button, hDlg, %btnRelay3Off, "RELAY-3 OFF", 265, 55, 100, 30, Call DoRelay()
Control Add Button, hDlg, %btnRelay4On, "RELAY-4 ON" , 390, 20, 100, 30, Call DoRelay()
Control Add Button, hDlg, %btnRelay4Off, "RELAY-4 OFF", 390, 55, 100, 30, Call DoRelay()
Control Add Button, hDlg, %btnRelay5On, "RELAY-5 ON" , 15, 105, 100, 30, Call DoRelay()
Control Add Button, hDlg, %btnRelay5Off, "RELAY-5 OFF", 15, 140, 100, 30, Call DoRelay()
Control Add Button, hDlg, %btnRelay6On, "RELAY-6 ON" , 140, 105, 100, 30, Call DoRelay()
Control Add Button, hDlg, %btnRelay6Off, "RELAY-6 OFF", 140, 140, 100, 30, Call DoRelay()
Control Add Button, hDlg, %btnRelay7On, "RELAY-7 ON" , 265, 105, 100, 30, Call DoRelay()
Control Add Button, hDlg, %btnRelay7Off, "RELAY-7 OFF", 265, 140, 100, 30, Call DoRelay()
Control Add Button, hDlg, %btnRelay8On, "RELAY-8 ON" , 390, 105, 100, 30, Call DoRelay()
Control Add Button, hDlg, %btnRelay8Off, "RELAY-8 OFF", 390, 140, 100, 30, Call DoRelay()
Control Add Label, hDlg, %label1, "LPT-8REL Controller by Chandra MDE", 20, 200, 300, 15
Control Add Label, hDlg, %label2, "Teknik Elektro Links - http://teknikelektrolinks.com", 20, 216, 300, 15
Control Add Label, hDlg, %label3, "[Created using thinBasic]", 350, 216, 300, 15
Dialog Show Modal hDlg, Call cbDialog
End Function
' -- Callback for dialog
CallBack Function cbDialog()
Select Case CBMSG
Case %WM_INITDIALOG
Case %WM_COMMAND
Case %WM_CLOSE
End Select
End Function
CallBack Sub DoRelay()
Local ID As DWord
If CBMSG=%WM_COMMAND And CBCTLMSG = %BN_CLICKED Then
LPTValue = Inp32(LPTAddress)
ID = CBCTL - %btnRelay1On + 1
If In(ID, 1, 2, 3, 4, 5, 6, 7, 8) Then
LPTValue = LPTValue Or RelayOn(ID)
Else
LPTValue = LPTValue And RelayOff(ID-8)
End If
Out32(LPTAddress, LPTValue)
End If
End Sub
Now the code is about 80 lines shorter.
Petr Schreiber
12-05-2013, 18:15
Very nice,
there are few possible tips more:
Const and ControlID block
When using comma, you can fit more equates on line - sometimes it is useful, sometimes not. It allows to change this:
Begin ControlID
%btnRelay1On
%btnRelay2On
%btnRelay3On
%btnRelay4On
%btnRelay5On
%btnRelay6On
%btnRelay7On
%btnRelay8On
%btnRelay1Off
%btnRelay2Off
%btnRelay3Off
%btnRelay4Off
%btnRelay5Off
%btnRelay6Off
%btnRelay7Off
%btnRelay8Off
%label1
%label2
%label3
End ControlID
to (for example) this:
Begin ControlID
%btnRelay1On, %btnRelay2On, %btnRelay3On, %btnRelay4On,
%btnRelay5On, %btnRelay6On, %btnRelay7On, %btnRelay8On
%btnRelay1Off, %btnRelay2Off, %btnRelay3Off, %btnRelay4Off,
%btnRelay5Off, %btnRelay6Off, %btnRelay7Off, %btnRelay8Off
%label1, %label2, %label3
End ControlID
Testing for number being in interval
You used the In for this, but you can also use Between for fast test:
Before:
If In(ID, 1, 2, 3, 4, 5, 6, 7, 8) Then
LPTValue = LPTValue Or RelayOn(ID)
Else
LPTValue = LPTValue And RelayOff(ID-8)
End If
After:
If Between(ID, 1, 8) Then
LPTValue = LPTValue Or RelayOn(ID)
Else
LPTValue = LPTValue And RelayOff(ID-8)
End If
Code readability
CBMSG, CBCTL, CBCTLMSG - easy to write, but I always considered these shortcuts a bit confusing. They have synonyms - Callback_Message, Callback_Control, Callback_Control_Message. You don't have to use the long versions, but they are more easy for random readers.
Thanks,
Petr
Petr Schreiber
12-05-2013, 18:29
Rene,
because the controls are ordered in linear fashion, it think it will work correctly in posted example.
The ID in ELSE branch will have values 9 to 16 -> when 8 subtraction is applied, it will get to 1 to 8 range, which is valid one.
Petr
EDIT: This is reaction to post which magically disappeared :)
ReneMiner
12-05-2013, 18:34
Yes, I was too fast, I've overseen the Else :D
ChandraMDE
12-05-2013, 18:38
Hi, Petr.
I was thinking about RANGE but found IN. But now I know BETWEEN and INSIDE.
Thanks. :)
Rene,
I don't think the index will out of bounds because ID is ranging from 1 to 16.
So, the ID for the if part is ranging from 1 to 8 and the ( ID-8 ) for else part is also ranging from 1 to 8.
I tried the program using BETWEEN and it works.
I also tried the program using INSIDE and it also works.
Please advice.
Thanks & Regards.