A pulsating puzzle – with answer


#1

About this time of year I usually try to present one or more problems on the uLisp Forum, to provide a challenge during the festive season. This year the problem is to write the shortest uLisp program that pulsates the built-in LED smoothly on and off.

Introduction

The classic “Hello World” program in the Arduino ecosystem is Blink: a four-line C program that blinks the LED provided on most Arduino boards. If Blink doesn’t run on your board it’s probably dead (or it’s one of the few boards without an LED).

The Lisp equivalent of the Arduno blink is as follows:

(defun blink ()
  (pinmode :led-builtin t)
  (loop
   (digitalwrite :led-builtin t)
   (delay 1000)
   (digitalwrite :led-builtin nil) 
   (delay 1000)))

A slightly more interesting variant of blink is possible if the built-in LED is connected to an output that supports analogue output, using PWM. It’s then possible to pulse the LED smoothly on and off. Here’s a typical program:

(defun pulse ()
  (loop
   (dotimes (x 256) (analogwrite :led-builtin (- 255 x)) (delay 8))
   (dotimes (x 256) (analogwrite :led-builtin x) (delay 8))))

This pulse program outputs a triangular waveform to the LED, repeatedly decreasing the value on the PWM output from 255 to 0, and then increasing it back up again from 0 to 255:

This will work on the following uLisp platforms that support PWM output to the built-in LED:

Arduino Mega 2560, ATmega4809 Curiosity Nano, Arduino Zero, Adafruit Metro M4 Grand Central, Adafruit Metro M4, Adafruit Feather M4, Adafruit PyBadge, Adafruit PyGamer, Adafruit Clue, Adafruit ItsyBitsy nRF52840, Circuit Playground Bluefruit, XIAO nRF52840, Maxim MAX32620FTHR, Teensy 4.0, Teensy 4.1, Raspberry Pi Pico, Raspberry Pi Pico W, Adafruit Feather RP2040, Seeed Studio XIAO RP2040, LILYGO T-Display RP2040, Adafruit Huzzah Feather ESP8266, Adafruit ESP32 Feather, Arduino Uno R4 Minima, and Arduino Uno R4 Wi-Fi.

The puzzle

The above pulse program takes 47 uLisp objects, which you can measure by looking at the space remaining, shown before the ‘>’ prompt, before and after defining the function, and taking the difference.

The problem is to create the smallest possible pulse program with the same behaviour. If you’re getting it below 40 uLisp objects you’re doing well! I’ll give my best answer at the beginning of the New Year.

Previous puzzles

If you missed my previous Lisp puzzles here are links to them:

A small collection of uLisp challenges – with answers

Another collection of uLisp puzzles – with answers

2nd January 2024: I’ve changed the size of the original pulse program to 47 objects, not 50 as originally stated, to correspond to the correct value for 32-bit platforms.


#2

In the original post I gave the problem of creating a smaller version of the following pulse program, that pulsates the built-in LED on and off smoothly, using analogwrite. Its size is 47 Lisp objects, which you can measure by looking at the space remaining, shown before the ‘>’ prompt, before and after defining the function, and taking the difference:

; 47 Lisp objects
(defun pulse ()
  (loop
   (dotimes (x 256) (analogwrite :led-builtin (- 255 x)) (delay 8))
   (dotimes (x 256) (analogwrite :led-builtin x) (delay 8))))

The sizes here are for a 32-bit platform, and they will all be slightly higher on a 16-bit platform.

We can save one Lisp object by making pulse call itself recursively, taking advantage of the uLisp Tail Call Optimisation that makes this equivalent to a loop:

; 46 Lisp objects
(defun pulse ()
  (dotimes (x 256) (analogwrite :led-builtin (- 255 x)) (delay 8))
  (dotimes (x 256) (analogwrite :led-builtin x) (delay 8))
  (pulse))

We can save a further three Lisp objects with the following version in which x goes in a sawtooth waveform from from 0 to 510; this is converted into the triangle wave by the expression (abs (- x 255)):

; 43 Lisp objects
(defun pulse (&optional (x 0))
  (analogwrite :led-builtin (abs (- x 255)))
  (delay 8)
  (pulse (mod (incf x) 511)))

The major breakthrough comes from rewriting the function using space-time programming, as follows:

; 34 Lisp objects
(defun pulse ()
  (analogwrite :led-builtin (abs (- (mod (truncate (millis) 8) 511) 255)))
  (pulse))

Here we repeatedly call pulse, with the argument to analogwrite calculated as a function of the current time, millis, designed to give the correct triangular waveform. This gets the size down to just 34 Lisp objects, which I think is probably the best that can be done.