Skip to main content

Experiment With impOS’ Pin-triggered Pulse Generator

How To Use imp Pins To Control Other Pins

impOS’ Pin-triggered Pulse Generator (PTPG) feature lets you set up a one-shot digital output pulse, triggered by another digital input pin. You choose the polarity, input trigger pin, initial delay and on-period when you configure the output:

hardware.pin5.configure(PTPG_OUT_ACTIVE_HIGH, hardware.pin8, 0.0055, 0.001);

You can then change the delay by simply calling pin.write():


The PTPG timing resolution is 1μs (much faster than can be achieved in Squirrel) and the output pulse length (delay plus on-period) can be up to 65.5ms. Since some rounding may occur, you can read back the actual values with pin.getdelay() and pin.getperiod():


As it happens, these timings fit nicely for controlling the gate of a triac in response to a 50/60Hz AC zero crossing detector.

What Do You Need?

If you want to try this yourself, you’ll need the following items:

  • An imp001 and April breakout board
  • A USB mini cable (for 5V power)
  • A 24V AC-AC adapter (to keep mains electricity at arms length)
  • Four 1N4001 diodes (to make a diode bridge)
  • Two BC547 NPN transistors
  • An MOC3021 non-zero crossing triac opto-coupler
  • A 2N6073A triac
  • An incandescent bulb and holder (rated 34V peak for 24V RMS)
  • Some resistors

Zero-Crossing Detector

The first task is to generate a zero-crossing signal from the 24V AC, which will be connected to our digital input pin.

One approach is to use a comparator to square off the AC input and produce an edge for each zero crossing event. The PTPG input supports this by triggering on every edge transition it sees.

If you don’t have an op-amp to hand you can make a zero crossing detector from four diodes, an NPN transistor and a few resistors:

Zero-crossing detector circuit

This detector outputs a pulse (not just an edge) for each zero-crossing event. The PTPG input supports this too, as long as the detector pulse is shorter than the minimum delay on your output pulse. What’s happening is that PTPG is triggering on the rising edge of the pulse and then re-triggering on the falling edge. Effectively, the output pulse delay starts from the falling edge of the detector.

Zero-crossing signal on oscilloscope

Triac Driver

The next job is to build a circuit to drive the triac gate input, which will switch on your bulb until the next AC zero crossing. It’s essential to isolate your imp pins from AC voltages and switching transients, so this circuit incorporates an MOC3021 opto-coupler. The imp’s PTPG output controls the opto-coupler LED, and a 2N6073A triac is connected to the opto-coupler output (since the integral triac is not designed for switching big loads).

Triac driver circuit

Input Controller And Squirrel Code

Now we need some way to set the brightness of the bulb. You can use the Little Devil iOS app, which provides an ideal slider control. The Pitchfork app provides something similar. Either way, you simply configure the app with your agent URL (obtained from impCentral™) and it will send a JSON message to your agent whenever the slider value is changed.

You can use the following snippet of agent code to receive the JSON message and forward it to the device:

http.onrequest(function(req, resp) {
    try {
        resp.send(200, "OK");
        local data = http.jsondecode(req.body);
        if ("slider" in data) device.send("brightness", data.slider);
    } catch (ex) {
        resp.send(500, "Internal Server Error: " + ex);

Here’s the accompanying device code. Just to be fancy, it ramps the bulb brightness up and down before it receives the first message from the agent:

const upperDelayS = 0.0075;
const lowerDelayS = 0.00275;
const rampPeriodS = 1;
const wakeIntervalS = 0.1;
const onPeriodS = 0.0001;

delayS <- lowerDelayS;
stepS <- (upperDelayS - lowerDelayS) / (rampPeriodS / wakeIntervalS);

triggerPin <- hardware.pin8;
ptpgPin <- hardware.pin5;

ptpgPin.configure(PTPG_OUT_ACTIVE_HIGH, triggerPin, delayS, onPeriodS);

agent.on("brightness", function(brightness) {
    stepS = 0;
    delayS = lowerDelayS + (1-brightness.tofloat()) * (upperDelayS-lowerDelayS);

function poll() {
    delayS = delayS + stepS;
    if ((delayS > upperDelayS) || (delayS < lowerDelayS)) {
        stepS = stepS * -1.0;
    imp.wakeup(wakeIntervalS, poll);


Bringing It All Together

Here’s the overall circuit:

Bulb demo complete circuit

The imp is powered from a separate USB cable, with GND tied to the low output of the bridge rectifier. You could regulate 3V3 from the rectified AC instead though. It’s worth noting that if the load you’re switching has significant reactance, you will need additional components around the triac to ensure it switches off at each zero crossing. This is described in the data sheet for the opto-coupler.