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.
#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; }
