Control Rasp Pi slide shows wirelessly

Evolving the Wireless Clicker

The wireless clicker represents the next evolution of slide-changing technology (Figures 4 and 5). It bypasses the GPIO pins on the Rasp Pi and sends button pushes directly to the Python script over the network. It all happens through the magic of the ESP8266 WiFi microcontroller [4].

Figure 4: ESP8266 wiring on the breadboard.
Figure 5: ESP8266 schematic. The FTDI basic programmer is a USB to serial IC breakout board; the logic level converter steps voltage up or down between 5V and 3.3V; the ESP8266 is the wireless module; and the 78XX is a fixed voltage regulator IC for 3.3V output.

Guess what? You can program the ESP8266 module using the Arduino IDE [5], which has become an industry standard development environment. After downloading the latest version from the Arduino site [6] and starting it up, add the libraries, if needed, by clicking on Sketch | Include Library | Manage Libraries. Copy the code in Listing 1 into a new edit window and save the file as slide-scroll. The IDE might create a new directory.

Listing 1


001 // 8266 wireless clicker firmware code
002 #include <ESP8266WiFi.h>
003 #include <DNSServer.h>
004 #include <ESP8266WebServer.h>
005 #include <WiFiManager.h>
007 void configModeCallback (WiFiManager *myWiFiManager) {
008 Serial.println("Entered config mode");
009 Serial.println(WiFi.softAPIP());
010 //if you used auto generated SSID, print it
011 Serial.println(myWiFiManager->getConfigPortalSSID());
012 }
014 const int ledPin = 5;
015 const int buttonPin4 = 4;
016 int buttonState4 = 0;
017 const int buttonPin16 = 16;
018 int buttonState16 = 0;
019 WiFiServer server(1337);
021 void printWiFiStatus();
023 void setup(void) {
024   Serial.begin(115200);
026   WiFiManager wifiManager;
027   // wifiManager.resetSettings();
028   wifiManager.setAPCallback(configModeCallback);
030   if(!wifiManager.autoConnect()) {
031     Serial.println("failed to connect and hit timeout");
032     ESP.reset();
033     delay(1000);
034   }
036   // Configure GPIO2 as OUTPUT.
037   pinMode(ledPin, OUTPUT);
038   pinMode(buttonPin4, INPUT);
040   // Start TCP server.
041   server.begin();
042 }
043 void loop(void) {
045   // Check if module is still connected to WiFi.
046   if (WiFi.status() != WL_CONNECTED) {
047     Serial.println("WiFi connected inside void loop");
048     while (WiFi.status() != WL_CONNECTED) {
049       Serial.println("WiFi.status connected loop");
050       delay(500);
051     }
052     // Print the new IP to Serial.
053     printWiFiStatus();
054   }
056   WiFiClient client = server.available();
058   if (client) {
059     Serial.println("Client connected.");
061     while (client.connected()) {
063       buttonState4 = digitalRead(buttonPin4);
064         // Serial.println(buttonState4);
065         if (buttonState4 == HIGH) {
066           // digitalWrite(5, HIGH);
067           Serial.println("UP Button pushed");
068           client.write("U\n");
069           delay(250);
070         }
072       buttonState16 = digitalRead(buttonPin16);
073         // Serial.println(buttonState16);
074         if (buttonState16 == HIGH) {
075           // digitalWrite(5, HIGH);
076           Serial.println("DOWN Button pushed");
077           client.write("D\n");
078           delay(250);
079         }
081       if (client.available()) {
083         char command =;
084         if (command == 'H') {
085           digitalWrite(ledPin, HIGH);
086           Serial.println("LED is now on.");
087           client.write("LED is now on.");
088         }
089         else if (command == 'L') {
090           digitalWrite(ledPin, LOW);
091           Serial.println("LED is now off.");
092           client.write("LED is now off.");
093         }
094      }
095     }
096     Serial.println("Client disconnected.");
097     client.stop();
098   }
099 }
101 void printWiFiStatus() {
102   Serial.println("");
103   Serial.print("Connected to ");
104   // Serial.println(ssid);
105   Serial.print("IP address: ");
106   Serial.println(WiFi.localIP());
107 }

Getting the ESP8266 into programming mode requires a little trick. First, disconnect ESP8266 GPIO pin 0 from the positive 3.3V connection and move it to Ground (GND) on the breadboard. Next, disconnect the TX line and temporarily short the Reset line (REST on the schematic) to GND. Immediately remove REST from GND and plug the TX line back into its proper place in the breadboard. The 8266 is now configured, reset, and ready for a firmware upload.

Hit the Upload button in the IDE, and you should see the program compile the code. After a short while, the blue LED on the ESP8266 should start flashing as the code is uploaded to the chip. Uploading is complete when the LED stops flashing.

To see the text, open the Arduino IDE serial window and push one of the buttons on the clicker. You should see UP Button pushed or DOWN Button pushed each time you push a button.

Configuring networking on the 8266 clicker is done through a web page on the device. Open your standard WiFi access point configuration window on your smartphone and find and click on a new access point with a name 8266, ESP, or something along those lines. Once, that's done, start up a browser on your phone and punch in Hit Enter and you should see a WiFi configuration pop up (Figure 6). Select Configure WiFi, use your local LAN SSID, and enter the correct passphrase, if asked. The ESP8266 will then reboot and connect to your local LAN. You should see a message in the IDE serial terminal saying that you are connected.

Figure 6: ESP8266 local network config on Galaxy 5 smartphone.

Notice the lines of code for a mysterious LED on the wireless clicker. I left that code in so readers could play around with two-way communication. I used the command-line program netcat [7] from a terminal on my Linux notebook to prototype this funcionality with:

rob% netcat 1337

If you type an H, the LED should light; type an L, and it should go out. Ctrl+C exits netcat. You might write some Python code to send an H to the clicker to turn the LED on when you have 5 minutes left in your presentation, for example.

The Python script on the presentation machine (Rasp Pi side) used for wireless operation is just a slightly modded version of the original wired code (Listing 2). The only real differences are in capturing data from a network connection, instead of watching a couple of GPIO pins. I like the idea that it's fairly straightforward to modify the readily available hardware's behavior by simply changing a few lines of code (Listing 3).

Listing 2

Original Wired Script

01 import os
02 import RPi.GPIO as GPIO
03 import time
05 GPIO.setmode(GPIO.BCM)
07 GPIO.setup(20, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
08 GPIO.setup(21, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
10 while True:
11     input_state = GPIO.input(20)
12     if input_state == True:
13         os.system("xdotool search --name 'Impress' key Up")
14         print('Up Button Pressed')
15         time.sleep(0.2)
17     input_state = GPIO.input(21)
18     if input_state == True:
19         os.system("xdotool search --name 'Impress' key Down")
20         print('Down Button Pressed')
21         time.sleep(0.2)

Listing 3

01 #client example
02 import socket
03 import time
04 import os
06 client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
07 client_socket.connect(('', 1337))
08 while 1:
09     data = client_socket.recv(512)
10     if 'U' in data:
11         os.system("xdotool search --name 'Impress' key Up")
12         print('UP Button Pressed')
13         time.sleep(0.2)
15     # input_state = GPIO.input(24)
16     if 'D' in data:
17         os.system("xdotool search --name 'Impress' key Down")
18         print('DOWN Button Pressed')
19         time.sleep(0.2)

Of course, I'm assuming that the ESP8266 wireless clicker connects to the local LAN using an IP address of You might have to adjust that address, depending on your network settings.

Using the Wireless Clicker

Once the wireless clicker is connected to the local LAN, it's easy to use it with the presentation machine. To begin, log in to a terminal on the presentation machine and start the wireless clicker Python script:

rob% python

You should see an UP Button Pressed or DOWN Button Pressed message appear on the terminal screen as you push the buttons on the clicker.

Fire up LibreOffice Impress [8] with your presentation of choice. Under the Slide Sorter tab, be sure to highlight the first slide. Next, click Slide Show | Start from first Slide in the main Impress menu. The first slide in your presentation should appear on your display. Push the Down button on the wireless clicker and you should move to the next slide.

It's important to make sure the cursor is positioned highlighting one of the slides under the Slide Sorter tab; otherwise, Impress won't capture the keystroke in the right window. You'll end up wondering why the clicker doesn't work. I didn't recognize the behavior during one of my conference talks using the wired model and had to revert back to using the keyboard and mousepad. It was a bit of a let-down for the audience.

Buy Raspberry Pi Geek

Get it on Google Play

US / Canada

Get it on Google Play

UK / Australia

Related content