Skip to main content

Program and test a PIC in a standalone project

This tutorial builds a standalone project that programs a Microchip PIC microcontroller through a PICkit 5, then shows the result on the AT1000 screen. At the end, the AT1000 programs a board at the press of the knob, with no computer connected.

You start from an AT1000 with up-to-date firmware, a PICkit 5, a target board and a PC. The examples use a PIC32MX250F128D: replace its part number with yours where shown.

The project does the following each time it runs:

What you need​

  • An AT1000 with up-to-date firmware, and AT1000-Interface on your computer.
  • A Microchip PICkit 5 and its USB cable.
  • A target board with a programming header (ICSP) for the PICkit 5.
  • The firmware to program, as a .hex file.
  • To prepare MPLAB IPE once: an x86-64 Linux PC with Docker or Podman. See Requirements.

Step 1: Connect the hardware​

  1. Plug the PICkit 5 into an external USB port of the AT1000.

  2. Connect the PICkit 5 to the target's programming header. Pin 1 of the PICkit 5 connector is marked with a triangle.

    PICkit 5 pinSignalTarget connection
    1MCLR / VPPReset pin, MCLR
    2VDDTarget supply voltage
    3GNDGround
    4PGDProgramming data, PGEDx or ICSPDAT
    5PGCProgramming clock, PGECx or ICSPCLK

    Your device's datasheet gives the pin numbers of these signals.

  3. Power the target. The PICkit 5 uses the VDD pin to sense the target voltage, so connect it even when the target has its own supply.

Power the target from the AT1000

The AT1000 DUT power supply can power the target, so the application switches it on only while programming. Connect the DUT supply output to the target's VDD and GND, and set POWER_TARGET to true in the application of Step 5. See Main power supply.

Step 2: Create the project​

  1. In AT1000-Interface, select Standalone projects, then select + to create a project. Choose the Python or Node.js template.
  2. Run the project in Development mode.
  3. Open the Access tab and set up SSH: select Publish SSH port, then Import public keys or Set password. See Connect to the project over SSH.
  4. Check that you can open a terminal in the project, replacing <serial-number> with your device's serial number:
    ssh -p 2222 dev@AT1032S-<serial-number>.local

The commands in the next steps run either on your PC or in this project terminal. Each step says which.

Step 3: Install MPLAB IPE in the project​

ipecmd needs MPLAB IPE, which you prepare once on your PC and copy into the project. Follow these sections of Program Microchip MCUs with a PICkit 5:

  1. Produce MPLAB IPE on your PC. For a PIC32MX, use --families 32bit.
  2. Keep only the packs you need. For a PIC32MX250F128D, keep PICkit5_TP and PIC32MX_DFP.
  3. Copy it into your project.

Step 4: Copy the firmware and try ipecmd​

  1. On your PC, copy your firmware into the project's /workspace folder as firmware.hex:
    scp -P 2222 my-firmware.hex dev@AT1032S-<serial-number>.local:/workspace/firmware.hex
  2. In the project terminal, check that the PICkit 5 is detected:
    ipecmd -T
    The output lists it with its serial number:
    Available Tool List
    -------------------
    1 PICkit 5 S.No : 020026702RYN021240
  3. Check that the PICkit 5 reaches the target:
    ipecmd -TPPK5 -P32MX250F128D -I
    The output ends with:
    Target voltage detected
    Target device PIC32MX250F128D found.
    Device Revision Id = 0xA1
    Device Id = 0x4d04053
    Operation Succeeded

If a command fails, see Troubleshooting.

Keep a copy of the target's current firmware

Before programming a board for the first time, you can save what it holds: ipecmd -TPPK5 -P32MX250F128D -GFbackup.hex.

Step 5: Write the application​

The application runs ipecmd to program and verify the target, then shows the result. It reports PASS only when ipecmd exits with code 0 and its output contains Program Succeeded.: ipecmd can exit with 0 without programming anything, for example when the target has no power. A run that takes longer than TIMEOUT_S is stopped and reported as FAIL.

-OL releases the target from reset at the end, so the new firmware starts running. Without it, the target stays in reset.

Set PART to your part number, without the PIC prefix.

In the project terminal, replace the contents of /workspace/src/index.ts with:

src/index.ts
// Program a PIC with the PICkit 5, then show PASS or FAIL on the AT1000 screen.
import { execFile } from 'node:child_process';
import { AT1000 } from '@ikalogic/at1000';

const PART = '32MX250F128D'; // target part number, without the "PIC" prefix
const HEX_FILE = 'firmware.hex'; // firmware to program, in /workspace
const TIMEOUT_S = 120; // longest time a programming run may take

// Set to true if the target is powered by the AT1000 DUT supply.
const POWER_TARGET = false;
const TARGET_VOLTAGE = 3.3;

// Run ipecmd. Resolve to { passed, message }.
function programTarget(): Promise<{ passed: boolean; message: string }> {
const args = ['-TPPK5', `-P${PART}`, '-M', `-F${HEX_FILE}`, '-OL'];
return new Promise((resolve) => {
execFile('ipecmd', args, { timeout: TIMEOUT_S * 1000 }, (error, stdout, stderr) => {
const output = stdout + stderr;
console.log(output);
if (error?.killed) {
resolve({ passed: false, message: `No answer after ${TIMEOUT_S} s` });
return;
}
// ipecmd can exit with 0 when nothing was programmed: check the output too.
if (!error && output.includes('Program Succeeded.')) {
resolve({ passed: true, message: 'Program Succeeded.' });
return;
}
// Report the last line ipecmd printed, skipping its timestamped log lines.
const lines = output.split('\n').filter((line) => line.trim() && !line.startsWith('['));
resolve({ passed: false, message: lines.at(-1) ?? `ipecmd exit code ${error?.code}` });
});
});
}

let deviceAddress: string | undefined;
if (AT1000.isStandalone()) {
deviceAddress = await AT1000.findLocalDevice();
} else {
const devices = await AT1000.findDevices();
deviceAddress = devices[0];
}

if (!deviceAddress) {
console.log('No devices found');
process.exit(1);
}

const tester = await AT1000.open(deviceAddress);
const screen = tester.hmi.screen();
const speaker = tester.hmi.audio();
const dut = tester.power.dut(0);

await screen.clear();
await screen.print('Programming...');

if (POWER_TARGET) {
await dut.enable(TARGET_VOLTAGE);
await new Promise((resolve) => setTimeout(resolve, 500)); // let the target's supply settle
}
let result;
try {
result = await programTarget();
} finally {
if (POWER_TARGET) {
await dut.disable();
}
}

await screen.clear();
if (result.passed) {
await screen.print(`PASS\n${result.message}`);
await speaker.play({ sound_id: 'success', volume: 40 });
} else {
await screen.print(`FAIL\n${result.message}`);
await speaker.play({ sound_id: 'failure', volume: 40 });
}
console.log(result.passed ? 'PASS' : 'FAIL', result.message);
process.exit(result.passed ? 0 : 1);

Step 6: Run it from the terminal​

In the project terminal, run the application:

cd /workspace
npm start

The AT1000 screen shows Programming.... About 15 seconds later, it shows PASS and plays the success sound. The terminal shows the full ipecmd output, ending with:

Program Succeeded.
Operation Succeeded

PASS Program Succeeded.

Step 7: Run it from the front panel​

  1. In AT1000-Interface, open the project's Overview tab and set the Startup command:

    npm start

    Select Save.

  2. In the project header, select Normal, then Run. The project programs the target and shows the result.

  3. To program the next board, connect it, then select the project in the front-panel project menu with the rotary knob and press the knob.

Select Default project on the Overview tab to run the project automatically each time the AT1000 starts. See Standalone projects.

When programming fails​

On a failure, the screen shows FAIL with the last message from ipecmd, and the AT1000 plays the failure sound. The application exits with code 1, and the project's Logs tab shows the full ipecmd output.

Screen messageMeaning
FAIL Connection Failed.The PICkit 5 cannot reach the target: check its power and the programming connections.
FAIL No Tools Connected.The PICkit 5 is not detected: check its USB cable.
FAIL Hex File could not be read.firmware.hex is missing from /workspace.
FAIL No answer after 120 sipecmd did not finish in time.

For other messages, see Troubleshooting.

Next steps​

  • Program several boards one after another: wrap the programming part in a loop that waits for a knob press, as in the project template. See HMI.
  • Test the board after programming it: measure its supply current with the DUT power supply, or check its outputs with the digital I/O.