Wireless message display

This is a wireless message display based on a Raspberry Pi Pico W, and written in Lisp. You can send a message to it via Wi-Fi from a web browser on a mobile phone or computer, and it's displayed on an 8-character alphanumeric display:


You could use it for reminders, or short messages to another person in your household.

This application was first published on the uLisp forum.


It uses two Adafruit four-character I2C alphanumeric modules [1]; alternatively you could use my eight-character alphanumeric display [2].

Here's a circuit showing how they are connected up:


The two alphanumeric display modules should be set to different I2C addresses using the solder links on the back of each module. Leave the left-hand module in its default configuration, so it has I2C address #x70, and bridge the A0 link of the right-hand module with a blob of solder to give it address #x71.

I built it on a full sized breadboard [3] with the power rails removed to make it more compact.

Once you've set up the program to load and run automatically, as described below, the message display can be powered either via the USB port, or from a 3.7V Lipo battery connected to the VSYS and GND pins on the board.

Running the Wireless Message Display

Installing uLisp

Install the latest version of ARM uLisp, Version 4.2, which contains the wireless extensions for the Raspberry Pi Pico W, by following the instructions on the RP2040 boards page, but first uncomment the directive at the start of the source file:

#define resetautorun

This allows you to make the program load and run automatically when power is applied.

Installing the message display program

The simplest way to install the Wireless Message Display program is to open the Arduino IDE Serial Monitor, copy the entire text of the program from the link at the end of this article, paste it into the Serial Monitor input box, and press return.

Change the statements:

(defvar *ssid* "MyNetwork")
(defvar *password* "MyPassword")

to the correct network name and password to log on to your Wi-Fi network.

Testing the program

Then test the program by evaluating:


After a few seconds the Raspberry Pi Pico W will connect to the Wi-Fi network, and the alphanumeric displays should show the IP address assigned to the message display:


Now try sending a message to the message display from a computer or phone by opening a web browser and entering something like:

Replace the IP address with whatever was displayed when the message display started up.

The message should have up to eight characters, and only contain characters that are valid in URLs. If you want to include a space, enter it as an underline.

The message will appear on the display, and the web browser will respond with:

Received OK

Exit from the message-display program by typing a ~ character and pressing return.

Making the program run automatically

Now save the Lisp image with the command:

(save-image 'message-display)

Now when you power-up the Raspberry Pi Pico W, uLisp will load the saved image, and then automatically execute the message-display function (provided you remembered to upload uLisp with the #define resetautorun directive uncommented, as explained above).

The program

Here's the uLisp program for the Wireless Message Display.

The function on initialises the two HT16K33 display drivers used in the alphanumeric display modules:

(defun on (bri)
  (dotimes (a 2)
    (with-i2c (str (+ a #x70))
      (write-byte #x21 str)
      (restart-i2c str) 
      (write-byte (+ bri #xe0) str)
      (restart-i2c str)
      (write-byte #x81 str))))

It takes a parameter bri to set the brightness of the displays from 0 (off) to 15 (maximum).

The list *codes* defines the segment codes for each of the characters from ASCII 32, space, to ASCII 95, underline:

(defvar *codes* '(#x0 #x6 #x220 #x12CE #x12ED #xC24 #x235D #x400 #x2400 #x900 #x3FC0 #x12C0 
 #x800 #xC0 #x4000 #xC00 #xC3F #x6 #xDB #x8F #xE6 #x2069 #xFD #x7 #xFF #xEF #x1200 #xA00
 #x2400 #xC8 #x900 #x1083 #x2BB #xF7 #x128F #x39 #x120F #xF9 #x71 #xBD #xF6 #x1200 #x1E
 #x2470 #x38 #x536 #x2136 #x3F #xF3 #x203F #x20F3 #xED #x1201 #x3E #xC30 #x2836 #x2D00
 #x1500 #xC09 #x39 #x2100 #xF #xC03 #x0))

The function display writes the segment code for the character x to the display as two bytes, to the I2C stream str:

(defun display (x dp str)
  (if (>= x #x60) (setq x (- x #x20)))
  (let ((c (nth (- x 32) *codes*)))
    (when dp (incf c #x4000))
    (write-byte c str)
    (write-byte (ash c -8) str)))

The first line converts lower-case letters to upper case. If the flag dp is non-nil, the decimal point is also illuminated. This is used when displaying the message display's IP address.

The function show displays a string of characters on the alphanumeric displays:

(defun show (txt)
  (let ((i 0) (len (length txt)) dp)
    (dotimes (a 2)
      (with-i2c (str (+ a #x70))
        (write-byte #x00 str)
        (dotimes (x 4)
          (let ((c (char-code (if (< i len) (char txt i) #\space))))
            (incf i)
            (display c (and (< i len) (eq (char txt i) #\.) (incf i)) str)))))))

It handles decimal points in the message, and if the message is shorter than eight characters it displays spaces on the subsequent displays.

You can test the displays by entering, for example:

(on 8)
(show "Testing")

The Wi-Fi routines

The following two statements define the Wi-Fi network name and password:

(defvar *ssid* "MyNetwork")
(defvar *password* "MyPassword")

You should change these to the correct strings to access your Wi-Fi network.

The routine println outputs a string with the the line ending required by most web protocols: return, newline:

(defun println (x s) (princ x s) (princ #\return s) (princ #\newline s))

Finally, message-display runs the server:

(defun message-display ()
  (show (wifi-connect *ssid* *password*))
  (on 8)
   (with-client (s)
     (let* ((line (read-line s)))
       (print line)
       (when line
         (loop (unless (= 0 (available s))) (return))
         (println "HTTP/1.1 200 OK" s)
         (println "Content-Type: text/html" s)
         (println "Connection: close" s)
         (println "" s)
         (println "<!DOCTYPE HTML><html>Received OK</html>" s)
         (when (eq (char line 5) #\?)
           (show (subseq line 6 (- (length line) 10)))))))
   (delay 1000)))

It checks once a second to see if a web client is connected. If it is, the program reads the first line of the request from the web browser, which will be something like:

GET /?Buy_Beer HTTP/1.1 

The subseq statement extracts the message from this line after sending a response to the web client. Finally, the program calls show to display the text on the alphanumeric displays.

The whole program

Here's the whole program in a single file: Wireless message display program.

Other suggestions

It's quite fun thinking how to express messages in eight characters, but if this seems a bit restrictive you could display longer messages by making them scroll along the display. See Scrolling text display for an example of how to do this.

  1. ^ Quad Alphanumeric Display - Red 0.54" Digits on Adafruit.
  2. ^ Eight-Character Alphanumeric Display on Technoblogy.
  3. ^ Full sized premium breadboard on Adafruit.