K8096/K8097: problem with API DLL calls using in Free Pascal

Description of the problem of using Native API DLL in the Free Pascal program.

Available:

  1. SMC Velleman K8096
  2. Windows 7 x64 Professional
  3. Free Pascal Compiler 2.6.2
  4. Lazarus 1.0.8
  5. Native API DLL for Velleman K8096/K8097 & installed driver for K8096
  6. Pascal calls description based on viewtopic.php?f=3&t=8932&p=34570&hilit=K8097+pascal#p34570

The problem:

When trying to get the state ‘torque’ or ‘demo’, function only returns ‘false’.
The return value does not depend on the actual state of the flag - returns only ‘false’.

For example:

  1. Connect to SMC.
  2. Turn on the ‘torque’.
  3. Check its condition … and get ‘false’.
  4. Abort our program.
  5. Run MotorDemo.exe & see torque ‘On’ (wow!)

Source codes:

DLL interface:

[code]unit mtrapi32;

{$mode objfpc}{$H+}

interface

{
Note: LongWord is the same type as DWORD
}

function SMCConnect(Port: PChar; Motors: LongWord): Boolean; stdcall; external ‘mtrapi32.dll’;
function SMCConnected: Boolean; stdcall; external ‘mtrapi32.dll’;
function SMCDemo: Boolean; stdcall; external ‘mtrapi32.dll’;
procedure SMCDisconnect; stdcall; external ‘mtrapi32.dll’;
function SMCGetDirection(Motor: LongWord): LongWord; stdcall; external ‘mtrapi32.dll’;
function SMCGetInput(Input: LongWord): Boolean; stdcall; external ‘mtrapi32.dll’;
function SMCGetInputCount: LongWord; stdcall; external ‘mtrapi32.dll’;
function SMCGetMotorCount: LongWord; stdcall; external ‘mtrapi32.dll’;
function SMCGetMoving(Motor: LongWord): Boolean; stdcall; external ‘mtrapi32.dll’;
function SMCGetOutput: Boolean; stdcall; external ‘mtrapi32.dll’;
function SMCGetOutputCount: LongWord; stdcall; external ‘mtrapi32.dll’;
function SMCGetTorque(Motor: LongWord): Boolean; stdcall; external ‘mtrapi32.dll’;
procedure SMCMove(Motor, Steps: LongWord; Direction: LongWord; Speed: LongWord); stdcall; external ‘mtrapi32.dll’;
procedure SMCSetDemo(Enabled: Boolean); stdcall; external ‘mtrapi32.dll’;
procedure SMCSetOutput(Active: Boolean); stdcall; external ‘mtrapi32.dll’;
procedure SMCSetTorque(Motor: LongWord; Enabled: Boolean); stdcall; external ‘mtrapi32.dll’;
procedure SMCStop(Motor: LongWord); stdcall; external ‘mtrapi32.dll’;

implementation

end.[/code]

Simple test program:

[code]program project1;

uses
mtrapi32,
Strings,
Crt,
FileUtil;

label
Finish1;

var
PortName: String;
PortNameZLS: PChar;
tmp_Result: boolean;

begin
Write (‘Please enter port name (f.ex., COM5): ‘);
Readln (PortName);
Writeln (’===’);
PortNameZLS:= StrNew(PChar(PortName));
Write (UTF8ToConsole(‘Connect SMC to this port: ‘));
case SMCConnect (PortNameZLS,1) of
TRUE : Writeln (‘Success’);
FALSE: begin
Writeln (‘Unsuccess’);
goto Finish1;
end;
end;
Writeln (‘Press any key’);
ReadKey;
Writeln (’===’);
Writeln (‘Check TORQUE: ‘), SMCGetTorque (0));
Writeln (‘Press any key’);
ReadKey;
Writeln (’===’);
Writeln (‘Set TORQUE ON’); SMCSetTorque (0,TRUE);
Writeln (‘Check TORQUE: ‘), SMCGetTorque (0));
Writeln (‘Press any key’);
ReadKey;
Writeln (’===’);
Writeln (‘Check DEMO: ‘), SMCDemo);
Writeln (‘Press any key’);
ReadKey;
Writeln (’===’);
Writeln (‘Set DEMO ON’)); SMCSetDemo (TRUE);
Writeln (‘Check DEMO: ‘), SMCDemo);
Writeln (‘Press any key’);
ReadKey;
Writeln (’===’);
Writeln (‘Down both TORQUE and DEMO and disconnect’));
Writeln (‘Press any key’);
ReadKey;
StrDispose (PortNameZLS);
SMCSetTorque (0,FALSE);
SMCSetDemo (FALSE);
SMCDisconnect;
Finish1: Writeln (‘Done. Press any key’);
ReadKey;
end.[/code]

I only got ‘false’ for both ‘totque’ and ‘demo’.

I need your help!

Regards, Yuriy

If you are programming in Delphi, you should be careful with boolean types. The DLL uses the Windows API BOOL type, which is different from Boolean. Both are boolean types, but their representation of true and false is different.

There is an article on the JEDI blog about this:
blog.delphi-jedi.net/2008/09/25/ … d-integer/

To help you out I’ve created a correct Delphi unit for mtrapi32.dll:
MotorDLL.pas (download as zip)

unit MotorDLL;

interface

uses
  Windows, Messages, Motors;
  
const
  mtrapi32 = 'mtrapi32.dll';

function SMCConnect(Port: PChar; Motors: DWORD): BOOL; stdcall;
procedure SMCDisconnect; stdcall;
function SMCConnected: BOOL; stdcall;
function SMCGetMotorCount: DWORD; stdcall;
function SMCGetInputCount: DWORD; stdcall;
function SMCGetOutputCount: DWORD; stdcall;
procedure SMCSetDemo(Enabled: BOOL); stdcall;
function SMCDemo: BOOL; stdcall;
procedure SMCMove(Motor, Steps: DWORD; Direction: DWORD; Speed: DWORD); stdcall;
procedure SMCStop(Motor: DWORD); stdcall;
function SMCGetMoving(Motor: DWORD): BOOL; stdcall;
function SMCGetDirection(Motor: DWORD): DWORD; stdcall;
procedure SMCSetTorque(Motor: DWORD; Enabled: BOOL); stdcall;
function SMCGetTorque(Motor: DWORD): BOOL; stdcall;
function SMCGetInput(Index: DWORD): BOOL; stdcall;
procedure SMCSetOutput(Active: BOOL); stdcall;
function SMCGetOutput: BOOL; stdcall;

implementation

function SMCConnect; external mtrapi32 name 'SMCConnect';
procedure SMCDisconnect; external mtrapi32 name 'SMCDisconnect';
function SMCConnected; external mtrapi32 name 'SMCConnected';
function SMCGetMotorCount; external mtrapi32 name 'SMCGetMotorCount';
function SMCGetInputCount; external mtrapi32 name 'SMCGetInputCount';
function SMCGetOutputCount; external mtrapi32 name 'SMCGetOutputCount';
procedure SMCSetDemo; external mtrapi32 name 'SMCSetDemo';
function SMCDemo: BOOL; external mtrapi32 name 'SMCDemo';
procedure SMCMove; external mtrapi32 name 'SMCMove';
procedure SMCStop; external mtrapi32 name 'SMCStop';
function SMCGetMoving; external mtrapi32 name 'SMCGetMoving';
function SMCGetDirection; external mtrapi32 name 'SMCGetDirection';
procedure SMCSetTorque; external mtrapi32 name 'SMCSetTorque';
function SMCGetTorque; external mtrapi32 name 'SMCGetTorque';
function SMCGetInput; external mtrapi32 name 'SMCGetInput';
procedure SMCSetOutput; external mtrapi32 name 'SMCSetOutput';
function SMCGetOutput; external mtrapi32 name 'SMCGetOutput';

end.

Pseudo code:

var
    Value: BOOL;
begin
    Value := SMCGetTorque(0);
    if Value then
        ToqueIsEnabled else TorqueIsDisabled;
end;

I hope this solves your problem

[code]unit MotorDLL;

interface

uses
Windows, Messages, Motors;

const
mtrapi32 = ‘mtrapi32.dll’;[/code]

What is ‘Motors’ unit?

It is the Delphi library used by the DLL to operate the card.

You may safely remove Motors from the uses list.

Messages? Windows? I specify these details, because I will use Free Pascal.

Adjust the header file to your needs. Experiment with the return value to see what is returned for true and what is returned for false; use integer types (of the same size) if necessary. You will probably get -1 for true and 0 for false.

You tested it in Delphi only?

The example is for Embarcadero Delphi, not Free Pascal, modify it until it works.

The BOOL type in Embarcadero Delphi means: zero is False and any nonzero value is True.
BOOL corresponds to LongBool which is a 32-bit integer (DWORD).

The DLL can be used from any programming language.

I still trying to do it.

What non-dotnet programming systems have been used to validate the correctness of API DLL functions work, without using the wrapper dot-net?

I’m trying to understand exactly what I bought.

Dropdox: dropbox.com/s/jthovopjw0ka2 … rar?v=0mcn

It’s my test program (FPC project). Please try to use project1.exe with your local copy of native api dll.

You have done a good job modifying the unit, but I see that you are developing a console application, which is completely different from a windowed application. Is the problem that you are not getting the correct status or that the motor is not responding to the commands? I am guessing that the motor is responding but the status is not correct?

If the motor is responding, but you are not getting the correct status, then the problem is due to timing. The communication between the DLL and the card is asynchronous, meaning that when you call SetTorque, the command is sent to the card but the reply may come in later. Once torque has been applied, a confirmation is sent back to the DLL, and the DLL updates its internal status. It is possible that you request the status before the card’s answer has been received and processed.

Illustration:

[size=85]In this picture, GetTorque is called before the updated status is received.[/size]

It is very difficult to combine a console application with something event driven.

Check project1.lpr please:

// Writeln ('SMCSetTorque & SMCGetTorque'); Result:= SMCGetTorque (0); Writeln ('SMCGetTorque return: ', Result); Writeln ('Set Torque to ON: SMCSetTorque (0, TRUE)'); SMCSetTorque (0,TRUE); Sleep (100); Result:= SMCGetTorque (0); Writeln ('SMCGetTorque return: ', Result); Writeln ('Press ENTER'); Readln; //
I use timeouts 0.1s (Sleep(100) command).
Moreover, increasing of the timeout does not give any result.
Windows application written in Free Pascal behaves similarly bad.
Agree that it looks a little strange.
And this is the problem: either something wrong with the DLL, or the DLL is not fully documented.

So. I wrote a test application with the form.
In this form, the various API calls are handled by different event handlers (buttons).
All works Ok. But!
There is no documentation that could be about to guess in advance.
And it is looks strange.
Do you agree?

I would like to see the API, which allows the programmer to use different calls within the same procedure (function, handler) without using far not the easiest programming techniques.

If we use the description of API, then the code below (Free Pascal/Lazarus) should work fine:

SMCSetTorque(0,TRUE); // Timeout 0.25s Sleep(250); // Checking ChokeLI:= SMCGetTorque(0);
However, it did not work. Get-type call does not return the actual state.

We have to use the ‘pinch bar’:

SMCSetTorque(0,TRUE); // Timeout 0.25s Sleep(250); // Use the 'pinch bar' Application.ProcessMessages; // Checking ChokeLI:= SMCGetTorque(0);
As you can see, the problem here is not in the asynchronous operation.
The problem is badly written API.

[quote]If we use the description of API, then the code below (Free Pascal/Lazarus) should work fine: …Sleep(250)[/quote]That is if you don’t know what Sleep does. Sleep suspends the current thread, which simply means that it can not do anything.

[quote]We have to use the ‘pinch bar’: … Application.ProcessMessages;[/quote]The DLL uses the Windows message loop for thread synchronization, which is why Application.ProcessMessages appears to work and Sleep doesn’t.

This may be hard to understand, but the nature of the card requires that it works that way. Moving a motor for example may take 5 seconds, you can not put the application to sleep for that period.

My example - an indirect check that your controller is connected to the port.

And you are constantly trying to explain to me that I do not know how to write software.

It may be worth your while to document (and write) API based on the fact that it will be used not only for demonstration and education programs?

Please: Mind your language.
No need to insult the people that invest quite some time in trying to help you.

[quote=“VEL417”]Please: Mind your language.
No need to insult the people that invest quite some time in trying to help you.[/quote]
I did not mean to offend anyone.
But do not strike a pose ‘Caesar’s wife is above suspicion’.
I repeat: Documenting API DLL is desirable to improve.

[quote]It may be worth your while to document (and write) API based on the fact that it will be used not only for demonstration and education programs?[/quote]We have to weigh carefully the fact that not everyone that will be using the card is a software programmer. We can not use more difficult constructs like callbacks.

[quote]And you are constantly trying to explain to me that I do not know how to write software.[/quote]If you know how to write software and want more than the current API can offer, you are free to write your own API. The K8096 uses the serial port which is easy to write to / read from. The communication is documented and we are here to explain it should it not be clear.

Are you ready to publish inside information on commands and protocol of the controller?