Proteus VSM DLL Workflow Explained: Create Custom Simulation Models in C++

 This is a note on how to build custom Proteus simulation components using VSM SDK (C++ DLL tutorial). That is, it a guide to Proteus VSM API, explaining how to write and compile custom DLL models for proteus simulation.

In Create Modify Custom DLL Component with Proteus VSM API in Visual Studio (C++) tutorial it was shown how to create the dll file using Visual Studio 2026. To create any dll component you need the proteus VSM API, of which the vsm.hpp header file is the most important. You can download Proteus v9.1 SP2 software and vsm.hpp file for VSM API based DLL models creation from the links below.

Proteus Design Suite 9.1 SP2 | Download Free

Proteus vsm.hpp file | download free

Picture conveys more information at a glance than words and so I made a diagram that shows how to write the C++ code for VSM DLL component creation.

Proteus VSM DLL Code Flow Diagram

Below picture shows how coding of proteus VSM C++  file with .dll project in Visual Studio 2026 looks like.

Create Custom Simulation Models in C++

I used the following code:

#include "pch.h"
#include <windows.h>
#include <string.h>   // for strcpy
#include <stdio.h>    // for sprintf
#include <math.h>     // for fabs
#include "vsm.hpp"

//-----------------------------------------------------------------------------
// Model class declaration
//-----------------------------------------------------------------------------
class my_device : public IACTIVEMODEL
{
public:
    my_device() : component(NULL), textstyle(NULL)
    {
        textorg.x = 0;
        textorg.y = 0;
        readout[0] = '\0';
    }

    VOID initialize(ICOMPONENT* cpt);
    ISPICEMODEL* getspicemodel(CHAR* device);
    IDSIMMODEL* getdsimmodel(CHAR* device);
    VOID plot(ACTIVESTATE state);
    VOID animate(INT element, ACTIVEDATA* newstate);
    BOOL actuate(WORD key, INT x, INT y, DWORD flags);

private:
    ICOMPONENT* component;
    POINT       textorg;
    HTEXTSTYLE  textstyle;
    CHAR        readout[10];
};

//-----------------------------------------------------------------------------
// Model construction and licensing
// (required boilerplate in every VSM DLL)
//-----------------------------------------------------------------------------

// --- DLL Export Functions ---
extern "C" __declspec(dllexport) IACTIVEMODEL* createactivemodel(CHAR* device, ILICENCESERVER* ils)
{
    return new my_device();
}

extern "C" __declspec(dllexport) VOID deleteactivemodel(IACTIVEMODEL* model)
{
    delete (my_device*)model;
}

// --- Implementation ---
//-----------------------------------------------------------------------------
// initialize()
// Called once, after licence authorization, to bind the model to its
// schematic component and set up the display's text origin/style.
//-----------------------------------------------------------------------------
VOID my_device::initialize(ICOMPONENT* cpt)
{
    // Store ICOMPONENT interface and initialize.
    component = cpt;

    // Get origin and style for readout text
    BOX textbox;
    cpt->getsymbolarea(1, &textbox);
    textorg.x = (textbox.left + textbox.right) / 2;
    textorg.y = (textbox.top + textbox.bottom) / 2;
    textstyle = cpt->createtextstyle((CHAR*)"ACTIVE READOUT");

    // Initial readout:
    strcpy(readout, " 0.00");
}

//-----------------------------------------------------------------------------
// Electrical interface stubs.
// READOUT is a purely graphical model - its electrical behaviour is provided
// externally by an RTVPROBE/RTIPROBE primitive (see the PRIMITIVE property
// on the library part), so these both return NULL.
//-----------------------------------------------------------------------------
ISPICEMODEL* my_device::getspicemodel(CHAR*) { return NULL; }
IDSIMMODEL* my_device::getdsimmodel(CHAR*) { return NULL; }

//-----------------------------------------------------------------------------
// plot()
// Called for a full, normal (non-animating) render of the component.
//-----------------------------------------------------------------------------
VOID my_device::plot(ACTIVESTATE state)
// Plot function - this is called for normal rendering.
{
    component->drawsymbol(-1);
    component->drawsymbol(1);
    component->drawtext(textorg.x, textorg.y, 0, TXJ_CENTRE | TXJ_MIDDLE, readout);
}

//-----------------------------------------------------------------------------
// animate()
// Called whenever the paired probe primitive produces a new reading.
// Only real-valued data is handled; the display is redrawn incrementally
// (just the my_device_1 panel + text, not the whole component).
//-----------------------------------------------------------------------------
VOID my_device::animate(INT element, ACTIVEDATA* data)
// Animate function - this is called whenever an event is
// produced by the simulator model.
// We interpret real values only, as follows:
{
    if (data->type == ADT_REAL)
    {
        // Decide whether to prefix with a +, a - or nothing:
        DOUBLE absval = fabs(data->realval);
        CHAR sign, result[10];
        if (data->realval > 0.001)
            sign = '+';
        else if (data->realval < -0.001)
            sign = '-';
        else
            sign = ' ';

        // Now we work out where to place the decimal point:
        if (absval >= 1000)
            sprintf(result, "%cMAX", sign);
        else if (absval >= 100)
            sprintf(result, "%c%3.0f", sign, absval);
        else if (absval >= 10)
            sprintf(result, "%c%4.1f", sign, absval);
        else
            sprintf(result, "%c%4.2f", sign, absval);

        // Finally, re-draw the display value within the result text
        // within it:
        component->drawsymbol(1);
        component->drawtext(textorg.x, textorg.y, 0,
            TXJ_CENTRE | TXJ_MIDDLE,
            strcpy(readout, result));
    }
}

//-----------------------------------------------------------------------------
// actuate()
// Mouse/keyboard event handler. READOUT is display-only, so it never
// consumes input events.
//-----------------------------------------------------------------------------
BOOL my_device::actuate(WORD key, INT x, INT y, DWORD flags)
{
    return FALSE;
}

The code structure follows what the above DLL creation diagram showed earlier and explained briefly below. 

The code starts with some header files of which the vsm.hpp header file is the most important (the download link is provided above). The pch.h header file is related to visual studio 2026 IDE which is automatically created and available when a DLL project is created. 

Then create a class my_device with the IACTIVEMODEL interface. Next initialize the class with functions which are required for the interface class to work. Note all of the function used in the class are required for the compilation of the C++ file into DLL file. Then export and delete function are used for the creation and deletion of the model into and from the memory. There are four major functions that can be used- ISPICEMODEL, DSIMMODEL, IMIXEDMODEL, animate() and actuate() function. The ISPICEMODEL and IDSIMMODEL electrical interfaces are not used but they are required for the IACTIVEMODEL (the graphical interface to ISIS) to function, at least they should return NULL. Once one of these is defined, we then use the plot() function to plot the graphics.

There are also alternative and easier way to create proteus simulation models. One is using spice model that you can download on the internet. For these models see the following tutorials:
- and the easier but temporal way is demonstrated in Import spice model in Proteus and draw JFET drain curve
Another tutorial on creating custom component using VSM API tutorial:


Post a Comment

Previous Post Next Post