If you haven't heard of Electromagnetic Field (EMF) Camp, imagine a field in the quaint English countryside, temporarily populated by 2,500 curious tech/maker enthusiasts; fiber internet, radio masts, robotic bartenders, lasers illuminating the sky, and wild/wacky inventions as far as the eye can see.
One of these wacky inventions was the EMF Roamer, a quarter-scale wooden Tesla Cybertruck, deployed to roam around the side, controlled by anyone over the internet!
Image credit VicHarkness.
Just allowing people to drive it line of sight wasn’t good enough! Especially not in the world of the Internet of Things. Live video from a camera, along with the location on a map is streamed to the user while they have control of the roamer.
A free for all in terms of control would have been a mess, so we devised a queue system. People could join the queue in their web browser and wait their turn, or provide a mobile phone number to receive an SMS when their turn is up (thanks Vonage!). The queue system is “serverless”, in that it does not require a dedicated server. Instead, it runs on simple shared hosting, with PHP coordinating the queue on-demand, which is stored in a MySQL database.
What We're Building
This article will describe how to set up the required libraries and database, then create a simple example queue, like that used to drive the EMF Roamer. If users provide a phone number and close the page after joining the queue, they will be sent an SMS using Vonage when it's their turn. The system is written in PHP with a MySQL backend, designed to run on shared hosting. If you don’t have a web host already, you can follow along on your own computer using software such as WAMP.
Installation
To make full use of the SMS delivery, you'll need a Vonage Developer account. You can sign up for one today and follow this tutorial using free credit. Find your API Key/Secret at the top of the Vonage API Dashboard.
You will need to clone the PHP Web User Queue from GitHub.
You will also need to install the Vonage PHP client library. This can be done using Composer.
composer require vonage/client
If you're new to Composer, you may find Composer's Getting Started page useful.
To install the PHP Web User Queue library to your project, copy the PHPWebUserQueue to your server and rename settings.template.php
to settings.php
, then edit it:
$selfdrive = TRUE; //Allow user page requests to self drive the worker in order to keep things running faster
$timeout =3*60; //Time in seconds, how long a session lasts
$noshowtimeout = 2*60; //Time in seconds their place at the front of the queue will be held
$sqlserver = "localhost";
$sql_db = "queuetest";
$sql_user = "queuetest";
$sql_pass = "abab";
$vonagekey = "abab";
$vonagesecret = "abab";
$composerpath = __DIR__ . "/../vendor/autoload.php";
$sitepath = "https://your.site";
Create a blank database using MySQL server with default settings and associate a user with the below permissions:
Then run the setup script to create the required tables:
http://[your site]/PHPWebUserQueue/setup.php
If you’re new to MySQL, check your hosting provider’s documentation, or use a tool such as phpMyAdmin.
It is recommended that you set your server up to periodically run the worker script as a cron job. This keeps things ticking over and text messages being sent, even if all users have closed the page. If your web host doesn’t support this, don’t worry. The queue is also “self driven” whenever a page is accessed which calls the Web User Queue library. A technique borrowed from “wp-cron”, how WordPress handles tasks. If your site is particularly high-traffic, you may only wish to run server crons and remove the overhead on each page execution. This can be achieved by setting the $selfdrive
flag FALSE
.
Ensure the cronworker.php
script is executable:
Example implementation running the worker every minute using crontab:
If you’re new to cron, check your hosting provider’s documentation, or use a tool such as cron-job.org.
Building an example
We are going to build a simple example queue. You will join the queue and wait your turn on index.php
, then finally reach control.php
when it’s your turn. Follow along with the below tutorial from scratch, or access the complete example in the “Example” folder on GitHub.
Joining and Queueing
Create the file index.php
, include the library file, and create your first queue:
<!--?php
include '../PHPWebUserQueue/WebUserQueue.php';
$q = new WebUserQueue();
</code-->
Our queue will also allow people to resume their position by following a link from the SMS, so we grab those values now: ```blok {"type":"codeBlock","props":{"lang":"php","code":"if%20(isset($_GET%5B%22i%22%5D)%20and%20isset($_GET%5B%22s%22%5D))%0A%7B%0A%20%20%20%20$_SESSION%5B%22i%22%5D%20=%20$_GET%5B%22i%22%5D;%0A%20%20%20%20$_SESSION%5B%22s%22%5D%20=%20$_GET%5B%22s%22%5D;%0A%7D%0A"}} ``` We check if the user already has a session, or if they’ve submitted the form to join the queue. If not, we show them the form: ```blok {"type":"codeBlock","props":{"lang":"php","code":"if%20(!isset($_SESSION%5B%22i%22%5D))%7B%20%0A%09//User%20is%20not%20already%20in%20the%20queue.%0A%09if(!isset($_POST%5B'name'%5D))%7B%0A%09%09//User%20has%20not%20posted%20the%20form,%20so%20show%20them%20the%20form.%0A%09%09echo%20%22%3Cform%20method=%22%5C"post%5C"%22%3E%3Cinput%20type=%22%5C"text%5C"%22%20name=%22%5C"name%5C"%22%20placeholder=%22%5C"Name%5C"%22%3E%20(optional)%3Cbr%3E%22;%0A%09%09echo%20%22%3Cinput%20type=%22%5C"text%5C"%22%20name=%22%5C"phone%5C"%22%20placeholder=%22%5C"Phone%22%20#=%22%22%20e.g.=%22%22%2007712345678%5C%22=%22%22%3E%20(optional)%3Cbr%3E%22;%0A%09%09echo%20%22We%20can%20send%20you%20an%20SMS%20when%20it's%20your%20turn.%3Cbr%3E%3Cbutton%3EPut%20me%20in%20the%20queue%3C/button%3E%3C/form%3E%22;%0A%09%7D%0A"}} ``` ![The form before joining the queue.](https://d226lax1qjow5r.cloudfront.net/blog/blogposts/building-a-queue-for-the-emf-roamer-with-the-vonage-sms-api-and-php/smsqueue.png "The form before joining the queue.") If the user has posted the form, we add them to the queue: ```blok {"type":"codeBlock","props":{"lang":"php","code":"%09else%7B%0A%09%09//user%20has%20posted%20the%20form,%20add%20them%20to%20the%20queue.%0A%09%09if($q->join($_POST%5B%22name%22%5D,$_POST%5B%22phone%22%5D))%7B%0A%09%09%09echo%20%22%3Cmeta%20http-equiv=%22%5C"refresh%5C"%22%20content=%22%5C"2%5C"%22%3E%22;%0A%20%20%20%20%20%20%20%20%20%20%20%20echo%20%22Welcome%20to%20the%20queue,%20your%20ID%20is:%20%22%20.%20$_SESSION%5B%22i%22%5D%20.%20%22%20%3Cbr%3E%22;%0A%09%09%7D%0A%09%7D%0A%7D%0A"}} ``` If the user does have a session and is in the queue, we check their details and let them know how long to wait. If it’s their turn, send them to the control page: ```blok {"type":"codeBlock","props":{"lang":"php","code":"else%7B//User%20is%20in%20the%20queue%0A%09if($q->checkValid($_SESSION%5B%22i%22%5D,$_SESSION%5B%22s%22%5D))%20%7B%20//Their%20session%20matches%20the%20database%0A%09%09if(isset($_GET%5B%22end%22%5D))%7B%20//Request%20to%20end%20their%20session%0A%09%09%09if($q->kick($_SESSION%5B%22i%22%5D,$_SESSION%5B%22s%22%5D))%7B%0A%20%20%20%20%20%20%20%20%09%09echo%20%22Thank%20you%20for%20queueing.%3Cbr%3E%3Ca%20href=%22index.php%22%3EClick%20here%20to%20enter%20the%20queue%20again%3C/a%3E%22;%0A%20%20%20%20%09%09%7D%0A%09%09%7D%0A%09%09else%7B%0A%09%20%20%20%20%20%20%20%20list($queuePos,$waitTime)%20=%20$q->checkWait($_SESSION%5B%22i%22%5D,$_SESSION%5B%22s%22%5D);%0A%09%20%20%20%20%20%20%20%20if%20($queuePos%20==%200)%7B%20//It's%20their%20turn%0A%09%20%20%20%20%20%20%20%20%20%20%20%20header(%22Location:%20control.php%22);%0A%09%20%20%20%20%20%20%20%20%7D%0A%09%20%20%20%20%20%20%20%20else%7B%20//They're%20waiting%0A%09%20%20%20%20%20%20%20%20%20%20%20%20echo%20%22%3Cmeta%20http-equiv=%22%5C"refresh%5C"%22%20content=%22%5C"5%5C"%22%3E%22;%0A%09%20%20%20%20%20%20%20%20%20%20%20%20echo%20%22There%20are%20%22%20.%20$queuePos%20.%20%22%20people%20in%20front%20of%20you.%20This%20will%20take%20about%20%22.%20$waitTime%20.%20%22%20%20seconds.%3Cbr%3E%22;%0A%09%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%09%7D%0A%09%7D%20else%20%7B%20//Session%20is%20not%20valid%0A%09%20%20%20%20echo%20%22Your%20session%20has%20expired.%20Please%20refresh%20the%20page%20to%20queue%20again!%22;%0A%09%20%20%20%20unset($_SESSION%5B'i'%5D);%0A%09%20%20%20%20unset($_SESSION%5B's'%5D);%0A%09%7D%0A%7D%0A?>%0A"}} ``` ![Waiting in the queue](https://d226lax1qjow5r.cloudfront.net/blog/blogposts/building-a-queue-for-the-emf-roamer-with-the-vonage-sms-api-and-php/smsqueue1.png "Waiting in the queue") ### Reaching the front Create the file `control.php`, include the library file, and a queue: ```blok {"type":"codeBlock","props":{"lang":"php","code":"%3C!--?php%0Ainclude%20'../PHPWebUserQueue/WebUserQueue.php';%0A$q%20=%20new%20WebUserQueue();%0A%3C/code--%3E"}} ``` ` Check the user’s session is valid, and that it is their turn: ```blok {"type":"codeBlock","props":{"lang":"php","code":"//User%20already%20has%20a%20session.%20Check%20it's%20not%20already%20used,%20all%20their%20details%20are%20valid,%20and%20they%20are%20first%20in%20the%20queue.%0Aif%20(isset($_SESSION%5B%22i%22%5D))%0A%7B%0A%20%20%20%20if($q->checkFirst($_SESSION%5B%22i%22%5D,$_SESSION%5B%22s%22%5D))%7B%0A%0A%20%20%20%20%7D%20else%20%7B%0A%20%20%20%20%20%20%20%20echo%20%22%3Ca%20href=%22index.php%22%3EYour%20session%20is%20not%20valid%20or%20has%20expired.%20Please%20click%20here%20to%20join%20the%20queue%3C/a%3E%22;%0A%20%20%20%20%20%20%20%20die();%0A%20%20%20%20%7D%0A%7D%20else%20%7B%0A%20%20%20%20header(%22Location:%20index.php%22);%0A%20%20%20%20echo%20%22%3Ca%20href=%22index.php%22%3EYour%20session%20is%20not%20valid.%20Please%20go%20back%20to%20the%20home%20page%3C/a%3E%22;%0A%20%20%20%20die();%0A%7D%0A?>%0A"}} ``` Then let them know the good news! ```blok {"type":"codeBlock","props":{"lang":"php","code":"%0A%20%20%0A%20%20%20%20%3Ctitle%3EFront%20of%20queue%3C/title%3E%0A%20%20%20%20%3Cscript%3E%0A%20%20%20%20%20%20var%20secondsleft%20=%20%3C?php%20echo%20$q-%3EcheckRemaining($_SESSION%5B%22i%22%5D,$_SESSION%5B%22s%22%5D);?%3E;%0A%20%20%20%20%20%20var%20countdowncaller%20=%20setInterval(function()%7Bcountdown()%7D,%201000);//Update%20the%20countdown%20every%20second.%0A%20%20%20%20%20%20function%20countdown()%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20secondsleft%20=%20secondsleft%20-%201;%0A%20%20%20%20%20%20%20%20%20%20%20%20document.getElementById('timeleft').innerHTML%20=%20secondsleft%20+%20%22%20seconds%20remaining.%22;%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20(secondsleft%20%3C%2010)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20document.body.style.backgroundColor%20=%20%22red%22;//warn%20them%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20(secondsleft%20%3C=%200)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20window.location.replace(%22index.php?end%22);//kick%20them%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%3C/script%3E%0A%20%20%0A%20%20%0A%20%20%20%20You%20are%20at%20the%20front%20of%20the%20queue!%20%3Cbr%3E%0A%20%20%20%20%3Cdiv%20id=%22timeleft%22%3ELoading...%3C/div%3E%0A%20%20%20%20%3Cbutton%20onclick=%22window.location.href='index.php?end'%22%3EEnd%20session%3C/button%3E%0A%20%20%0A%0A"}} ``` ![At the front of the queue](https://d226lax1qjow5r.cloudfront.net/blog/blogposts/building-a-queue-for-the-emf-roamer-with-the-vonage-sms-api-and-php/frontqueue.png "At the front of the queue") Next Steps ---------- Congratulations on setting up your first online queue. You’re now ready to build this into your own internet-controlled robot (plus a little electronics work)! This concept could be applied to a variety of applications which lend themselves to being operated by a single user at a time, rather than the alternative free-for-all of multiple users concurrently sending different commands. See [Twitch Plays Pokémon](https://en.wikipedia.org/wiki/Twitch_Plays_Pok%C3%A9mon) for an amusing example of this! If you’d like to build upon this example further with some administrative tools to manage the queue, check out the ‘bouncer’ script on the [GitHub repo](https://github.com/chrisstubbs93/WebUserQueue/tree/main/Admin). ![The queue bouncer (queue admin tool)](https://d226lax1qjow5r.cloudfront.net/blog/blogposts/building-a-queue-for-the-emf-roamer-with-the-vonage-sms-api-and-php/admin.png "The queue bouncer (queue admin tool)") We hope to see the EMF Roamer again at EMF Camp in 2024! Useful Resources ---------------- * [Vonage Developer Center - _free credits to get started!_](/home) * [Vonage Documentation](/documentation) * [Vonage Client Library for PHP](https://github.com/vonage/vonage-php-sdk-core) * [PHP Web User Queue on GitHub](https://github.com/chrisstubbs93/WebUserQueue) * [Electromagnetic Field](https://www.emfcamp.org/) `