Hello friends, I hope you are doing well. This is the nineteenth tutorial in our series on how to create a Proteus library. In the previous tutorial, How to Add a Live Control Panel to a Proteus Component, our Traffic Light Module learned to show its pins and lamps on a panel and to change its LOGIC setting with a click while the simulation runs. So far, we tested every part with logic switches. Today, we test the library the way its users will use it. Our topic is how to test a custom Proteus library with Arduino.

A library is only finished when it works with a real program. Logic switches show that a part reacts to its pins; a sketch on a simulated microcontroller shows that it reacts to them at the right speed, in the right order and together with other parts. We will build a test project with an Arduino UNO, our Traffic Light Module and our Push Button Module, write a small traffic light sketch with a pedestrian button, compile it to a HEX file, load it into the simulated Arduino and run three tests: the normal cycle, a button press and a deliberate configuration mistake.

Everything shown here was done in Proteus 8.5 Professional on our PC. The picture below shows the test project during a run: the sketch has just switched to red, the Traffic Light Module shows it on its lamps and its panel, and the Virtual Terminal shows the sketch's own messages.

Figure: The test project during a run: the sketch has just switched to red.

Why Test a Proteus Library with a Real Sketch?

In the earlier tutorials, we tested each part in isolation: lamp currents with ammeters, states with LOGICSTATE parts, the push button with a power terminal and the DLL model with logic switches. These tests are quick and precise, but they all come from us, the authors of the library, and we know which inputs the parts expect.

What a Sketch Adds

A sketch drives the parts like a user's project does. It changes several pins within microseconds of each other, it reads the button at its own pace, and it runs for minutes instead of seconds. A model that handles every single input correctly can still fail in such a run, for example when it misses a quick sequence of changes or reports a state too late. And the person who downloads a library will almost certainly connect it to an Arduino first.

What We Want to Find Out

Our test answers four questions. Does the Traffic Light Module follow the Arduino's pins exactly, phase by phase? Does the Push Button Module give the Arduino a clean HIGH and LOW, with no floating input? Does the timing of the sketch arrive unchanged on the lamps? And can a user tell, from what the simulation shows, whether a problem lies in the sketch or in the library?

What You Need for the Test

Besides our two parts from TEPTUTORIAL, the test needs three things.

An Arduino UNO for Proteus

On our PC, Pick Devices offered the ATmega328P microcontroller, but a search for ARDUINO found only connectors, an audio shield element and Grove parts, no Arduino board. We used our own TEP Arduino UNO V3, which comes with every TEP library package as ArduinoV3TEP.LIB. We copied ArduinoV3TEP.LIB and ArduinoV3TEP.IDX from one of our packages into the LIBRARY folder of Proteus. Pick Devices listed the new board only after we restarted Proteus; a search for ARDUINO then showed ARDUINO UNO V3.0 from the library ArduinoV3TEP, described as Arduino UNO R3 V3.0 designed by www.TheEngineeringProjects.com.

A Virtual Terminal

The Virtual Terminal is one of Proteus's instruments, in the Virtual Instruments mode of the left toolbar. It shows what the Arduino sends on its serial TX pin. We use it as the sketch's diary: every phase change is printed with the time in milliseconds.

A Compiled Sketch

Proteus does not compile Arduino sketches by itself; the simulated Arduino runs a HEX file, the same machine code that would be uploaded to a real board. With the Arduino IDE, Sketch, Export Compiled Binary writes such a file next to the sketch. On our PC, we compiled with the AVR GCC compiler and the Arduino AVR core 1.8.6 for an ATmega328P at 16 MHz, the toolchain behind the IDE's UNO support.

The Test Sketch

Our sketch is a small traffic light controller: four seconds green, one second yellow, four seconds red, and again. A pedestrian button cuts the green phase short: if it is pressed while the light is green, green ends after at least one second, and the light changes to yellow and then red. Here is the complete sketch, TrafficLight_Test.ino:

// TEP tutorial 19: testing our Traffic Light Module and Push Button Module with an Arduino UNO
// The Engineering Projects - www.TheEngineeringProjects.com
//
// Traffic Light Module: R -> D12, Y -> D11, G -> D10, GND -> GND
// Push Button Module  : OUT -> A0, VCC -> 5V, GND -> GND (the module has its own pull-down)
// Serial output       : TX (D1) -> Virtual Terminal RXD, 9600 baud
// A press during green asks for a pedestrian phase: green ends early, then yellow and red.

const byte RED_PIN = 12;
const byte YELLOW_PIN = 11;
const byte GREEN_PIN = 10;
const byte BUTTON_PIN = A0;

// HIGH for a module whose lamps light on a HIGH pin (LOGIC = High).
// Change to LOW for a module whose lamps light on a LOW pin (LOGIC = Low).
const byte LAMP_ON = HIGH;

const unsigned long GREEN_TIME = 4000;    // milliseconds
const unsigned long MIN_GREEN = 1000;     // green stays at least this long after a press
const unsigned long YELLOW_TIME = 1000;
const unsigned long RED_TIME = 4000;

enum Phase { GREEN, YELLOW, RED };
Phase phase = GREEN;
unsigned long phaseStart = 0;
bool requested = false;
bool lastButton = false;

void setLamp(byte pin, bool on) {
  digitalWrite(pin, on ? LAMP_ON : !LAMP_ON);
}

void showPhase(Phase p) {
  setLamp(RED_PIN, p == RED);
  setLamp(YELLOW_PIN, p == YELLOW);
  setLamp(GREEN_PIN, p == GREEN);
  Serial.print(millis());
  Serial.print(" ms: ");
  Serial.println(p == RED ? "RED" : p == YELLOW ? "YELLOW" : "GREEN");
}

void startPhase(Phase p) {
  phase = p;
  phaseStart = millis();
  showPhase(p);
}

void setup() {
  pinMode(RED_PIN, OUTPUT);
  pinMode(YELLOW_PIN, OUTPUT);
  pinMode(GREEN_PIN, OUTPUT);
  pinMode(BUTTON_PIN, INPUT);
  Serial.begin(9600);
  Serial.println("TEP traffic light test");
  startPhase(GREEN);
}

void loop() {
  const bool button = digitalRead(BUTTON_PIN) == HIGH;
  if (button && !lastButton && phase == GREEN && !requested) {
    requested = true;
    Serial.println("Button pressed: pedestrian request");
  }
  lastButton = button;

  const unsigned long elapsed = millis() - phaseStart;
  switch (phase) {
    case GREEN:
      if (elapsed >= GREEN_TIME || (requested && elapsed >= MIN_GREEN)) {
        requested = false;
        startPhase(YELLOW);
      }
      break;
    case YELLOW:
      if (elapsed >= YELLOW_TIME) startPhase(RED);
      break;
    case RED:
      if (elapsed >= RED_TIME) startPhase(GREEN);
      break;
  }
}

Pins and Wiring

The comment at the top of the sketch is the wiring plan. The module's R, Y and G go to D12, D11 and D10, and its GND to ground. The button's OUT goes to A0, used as a digital input, its VCC to +5V and its GND to ground. The Arduino's TX pin, D1, goes to the RXD input of the Virtual Terminal.

No Pull-Down in the Sketch

The sketch sets the button pin as a plain INPUT, without the internal pull-up. It does not need one, because our Push Button Module has its own 10k pull-down resistor, the RPULL property from the sixteenth tutorial. This is exactly the kind of detail a library test should confirm: if the module's pull-down did not work, the input would float while the button is released, and the sketch could see false presses.

The LAMP_ON Constant

LAMP_ON says which level lights a lamp. It is HIGH for our module with LOGIC set to High. A user with an active-low module changes it to LOW, and setLamp writes the opposite level for a lamp that should be off. We come back to it in the third test.

Reading the Button Once per Press

The sketch compares the button with its previous reading and reacts only to the change from LOW to HIGH, and only during green. One press gives one request, however long the button is held, and a press during yellow or red is ignored.

Which Version of the Module to Test

Our library has two versions of the Traffic Light Module, and they answer different questions.

TRAFFICLIGHTTEP: the Analogue Version

The version from the fourteenth and fifteenth tutorials uses a schematic model with resistors, LEDs and current probes. It shows real currents in ammeters, honours the resistor properties RRED, RYEL and RGRN, and lights a lamp only when enough current flows. It is the right part for questions about the electrical side, such as whether an Arduino pin can drive the module with a given resistor.

TRAFFICLIGHTVSMTEP: the DLL Version

The version from the last two tutorials uses TEPTRAFFIC.DLL. It is purely digital, honours LOGIC and has the control panel. It is the right part for questions about the program: which pin is driven when, and whether the module and the sketch agree. Our test is about the program, so we used this version.

How to Test a Custom Proteus Library with Arduino: Step by Step

We built and ran the test in five steps.

Step 1: Create a Test Project

We created a new project, TrafficLight-Arduino-Test, in the same folder as our other projects, with a schematic and no PCB layout or firmware project, and placed the ARDUINO UNO V3, a TRAFFICLIGHTVSMTEP, a PUSHBUTTONTEP and a Virtual Terminal. A test project of its own keeps the library test separate from the projects we used to build the parts.

Step 2: Wire It as the Sketch Says

We wired the parts exactly as in the comment of the sketch, with ground terminals for the module and the button and a power terminal labelled +5V for the button's VCC. Proteus connects terminals with the same name, so the ground terminals need no wire between them.

Step 3: Load the HEX File

In the Edit Component dialogue of ARD1, the Upload Hex File property takes the program. We typed the file name TrafficLight_Test.hex and copied the file into the project's folder, so the project and its program stay together. The Clock Frequence property stays at 16MHz, the clock the sketch was compiled for.

Figure: The HEX file goes into Upload Hex File; the clock stays at 16MHz.

Step 4: Run and Watch

After Run, the Virtual Terminal opened and printed the phases as the sketch reached them, and the module followed. We describe the results in the next section.

Step 5: Pause to Hit the Right Moment

A test sometimes needs an action at a specific moment, like pressing the button during green. The Pause button of the animation controls stops the simulation clock, so we can wait for the phase we want, pause, prepare the action and continue.

The Test Results

We ran the project several times. These are the results of the three tests, with the times printed by the sketch.

Test 1: The Normal Cycle

The terminal printed 0 ms: GREEN, 4000 ms: YELLOW and 5000 ms: RED, then 9000 ms: GREEN, 13000 ms: YELLOW, 14000 ms: RED and so on, a cycle of exactly nine seconds of simulation time. Whenever we looked, the module showed exactly one lamp, the one of the current phase. The panel confirmed it from the other side, for example RED pin HIGH lamp ON with YELLOW and GREEN pin LOW lamp OFF during red, and its counter matched the number of phases: 24 switch-ons when the 24th phase had started.

Simulation Time, Not Clock Time

The times on the terminal come from millis() in the sketch, and the simulated Arduino counts simulation time. On a slow PC, nine seconds of simulation can take longer than nine seconds on the wall clock, and the status bar of Proteus shows the current simulation time and the CPU load during a run. The printed times are therefore exact for the program, whatever the speed of the computer, which makes them a reliable basis for timing tests.

Test 2: A Pedestrian Request

We paused the simulation when the terminal had just printed 63000 ms: GREEN, clicked the TOGGLE marker of the push button to latch it, the method from the sixteenth tutorial, and continued. The terminal printed Button pressed: pedestrian request, followed by 65784 ms: YELLOW and 66784 ms: RED. Without the request, yellow would have started at 67000 ms. Green ended early, after more than the minimum of one second, and the following phases kept their normal lengths.

Figure: A press during green: yellow starts at 65,784 ms instead of 67,000 ms.

The test also confirmed the button module's pull-down. Every green phase before the press lasted the full four seconds, so the sketch never saw a false press while the button was released, and while it was latched, the sketch reported exactly one request, so it saw one clean edge. We released the button with a second click on its marker.

Test 3: A Mismatch Between Sketch and Module

For the last test, we made a deliberate mistake. While the simulation was running, we clicked LOGIC on the module's panel and switched the module to active low, while the sketch still had LAMP_ON set to HIGH. At 70784 ms, the terminal printed GREEN, but the module lit red and yellow and left green dark.

Figure: The sketch says green; the panel shows why the module lights red and yellow.

This is the situation the panel was made for. A user who sees the wrong lamps cannot tell from the module alone whether the sketch or the library is at fault. The panel answers it at a glance: GREEN pin HIGH, RED and YELLOW pins LOW, so the sketch drives the pins correctly, and LOGIC: ACTIVE LOW explains why the lamps show the opposite. The fix is either LOGIC back to High or LAMP_ON set to LOW in the sketch.

A Test Plan for Your Own Library

Our three tests follow a pattern that works for most parts. Before you publish a library, run at least these checks with a real sketch.

A library test plan with a real sketch
CheckHow we did itWhat to look for
Every pin worksEach phase drives a different pinThe part reacts to each pin, and only to it
Timing arrives unchangedTimes printed on the Virtual TerminalPhase lengths in the simulation match the sketch
Inputs are cleanButton read once per pressNo false presses while released
Interaction during a runButton latched at a chosen momentThe sketch sees the action at once
Wrong configurationLOGIC changed against the sketchThe part makes the mistake visible

Keep the Test Project with the Library

A test project is worth keeping. When you change a model in a later version, the same project and HEX file show at once whether the parts still behave as before. In the next tutorial, the test project becomes part of the package we share.

Common Mistakes When Testing a Proteus Library with Arduino

Arduino test problems and their solutions
ProblemCauseSolution
The Arduino part is not in Pick DevicesNew library files are read at start-upRestart Proteus after copying the LIB and IDX files
Nothing happens after RunNo program in Upload Hex File, or the file is not foundPut the HEX file next to the project and enter its name
The timing is wrongClock Frequence differs from the compiled clockKeep 16MHz for a sketch compiled for the UNO
Garbage on the Virtual TerminalBaud rates differUse the sketch's rate, 9600 in ours, on the terminal
The button press is missedPressed outside the phase that reads itPause in the right phase, latch the button, continue
The lamps show the opposite of the sketchLOGIC and LAMP_ON do not matchCheck the panel: pin levels right, LOGIC wrong

In the Next Tutorial

Our library is built and tested: a Traffic Light Module in two versions, a Push Button Module, their models, a C++ DLL with a panel, a test sketch and a test project. In the final tutorial of the series, How to Package and Share Your Own Proteus Library, we clean up the library files, collect everything a user needs into one package and check that it installs on another PC.

FAQ

Can I test a custom Proteus library without real hardware?

Yes. A simulated Arduino runs the same HEX file as a real board, so a sketch can drive your parts in Proteus exactly as it would drive the real modules.

How do I load an Arduino sketch into Proteus?

Compile the sketch to a HEX file, for example with Sketch, Export Compiled Binary in the Arduino IDE, and enter the file in the Arduino part's program property, Upload Hex File on our TEP Arduino UNO V3.

Why does Proteus not show my new Arduino library?

On our PC, Pick Devices showed a newly copied library only after a restart of Proteus.

How can I press a button at an exact moment in a Proteus simulation?

Pause the simulation in the phase you need, latch the button with its marker and continue. The sketch sees the press as soon as the simulation runs again.

That is all for today. Our library has passed its first real test with an Arduino, and the control panel has already earned its place. If you have any questions, ask in the comments. Take care.