R-Pi Clock Radio – Zeroed!

28 Comments

clock_radio_20160909_smallWe had a really old alarm clock in our bedroom.  Really old.  The LED number segments, which were a nice dim red in color, had been dying at the rate of 1 segment a year and it was getting hard to read the time.  My wife finally had enough of my “I’ll get a new one real soon” excuse and bought a new big, bright, blue LED clock to replace the old clock.  It was blue … and *really* bright … even in its dim-mode 🙁  It had to go!

My converted 1942 Crosley Radio was collecting dust on my workbench.  I had finally received a Raspberry Pi Zero and Zero4U USB hub to play with but was already lusting after the new R-Pi Zero with camera port.  I recently upgraded the audio-output on my Mac from an old USB HiFiMan Express DAC to  a Schiit Modi DAC.   Hmmmmm, seemed like I had the ingredients to make a BIG clock “radio” with alarm(s) and great stereo audio?

I had neglected my old media-center radio which had an original Raspberry Pi as its “heart”.  The R-Pi connected via HDMI to a display board driving a 9.7 inch, 1024 x 768, iPad LCD screen (non-touch).  Audio output was via the audio-jack to a small stereo amp.  The video was great but sound was mediocre.

radio_back_20160901

Dusty, old media-center radio with old R-Pi removed. Just waiting for the Zero!

My first step was to see if I could get decent sound out of the Zero via my HiFiMan Express HM-101 USB DAC.  After much Googling and research, I had an approach.  One of the best resources for configuring USB audio under Jessie OS is at Adafruit.  If you don’t want or need to use a USB DAC, just ignore this part 😉  You can use the “normal” audio-out jack on the R-Pi 1, 2 or 3 and omxplayer app.  As I was using a Zero, I needed another way to get audio out. Plus, I was after “good” audio, not just adequate.  I use the ALSA compatible mpg123 and mpg321 apps for audio playback.  These provide different options including various types of random playlist support.

I now had a working system with audio and video.  I added an Edimax WiFi dongle and  a Logitech wireless keyboard/trackpad via a UUGear Zero4U USB Hub.  The hub sits on top of the Zero with no soldering required.

hifiman_dac_201609

Zero connected via Zero4U USB hub to WiFi, keyboard & HiFiMan DAC

 

OK, I might have lost sight of the original goal — which was a bedroom clock.  I started thinking about displaying the time and complete weather forecast, while playing music.  I ran the idea by my wife who again informed me that “we” wanted a not-too-bright, readable-across-the-room, simple clock.  I did get her to agree that displaying (just) the outside temperature might be useful!

How best to show a clock and display temperature?  I needed to use an external resource of some kind to obtain the local outside temperature.  I was tempted to use another R-Pi with some kind of sensor HAT but I wanted something simple and quick.  I had investigated Weather Underground APIs for weather info in the past and still had an active developer account.  Providing I kept API calls to under 500 per day, it would be free. The Weather Underground APIs return data as JSON so I needed a way to GET and extract the temperature data.  I thought about a Python app but I’ve been doing some web development lately and decided to use a web “app” — i.e. use a browser to display a web page with associated JavaScript functions calling the JQuery JavaScript library.  The web page with associated scripts and CSS could be local to the R-Pi or remotely located and downloaded from a server.

I needed a browser that could be run in kiosk mode so there were no distracting elements.  I chose kweb.  The latest version I found was 1.7.4.  There’s a PDF doc that does a great job describing kweb and all its options by Günter Kreidl – here.  Providing you have the Jessie OS and a GUI installed, i.e. not a minimal Jessie image, you can install kweb:

cd ~
mkdir kweb
cd kweb
wget http://steinerdatenbank.de/software/kweb-1.7.4.tar.gz
tar -xzf kweb-1.7.4.tar.gz
cd kweb-1.7.4/
./debinstall

I like to have a variety of fonts, including the Microsoft core set:

Install the font installer 😉 and then run the installer when prompted.  You can then list all fonts installed:

cd ~
sudo apt-get install ttf-mscorefonts-installer
fc-list

To hide the distracting cursor while in browser kiosk mode, you can use “unclutter”:

sudo apt-get install unclutter

For a clean clock display, you need to create a kweb configuration file that loads the clock web page and hides all UI elements, including the cursor – while leaving the keyboard active.  I suggest creating a file, “mykiosk” in your pi home directory that contains:
pi@PiZero:~ $ cat mykiosk

#!/bin/sh
xset -dpms
xset s noblank
xset s off
matchbox-window-manager &
unclutter &
kweb -KHUAJ+-zbhrqfpoklgtjneduwxyavcsmi#?!., file:///home/pi/clock/index.html

Create a directory with the web page and associated script and CSS files:

cd ~
mkdir clock
cd clock

The directory should contain 3 files as listed below. I have made a zip file containing these 3 files plus the “mykiosk” config file that you can download from here.  NOTE: you need to get your own API key from Weather Underground and determine your location string.  See below.

html file “index.html”:

<!DOCTYPE html>
<html>
<head>
<title>Time and Temp</title>
<link href="style.css" rel="stylesheet" type="text/css">
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script src="script.js"></script>
</head>
<body onload="getTime(); getTemp()">
<div id="time_txt" class="timediv"></div>
<div id="temp_txt" class="tempdiv"></div>
</body>
</html>


CSS file “style.css”:

/*
style.css
for use with HTML clock
Andy Felong
08 Sep 2016
*/

body, div {
	font-family: Verdana, Geneva, sans-serif; 
	font-size: 320px;  /* adjust to preference and display size */
	background-color: #000000;
	color: #881200;  /* dim scarlet */
}

.timediv {
	font-size: 100%; 
	max-width: 1024px; /* use the width of the display */
	text-align: center;
}

.tempdiv {
	font-size: 60%;
	max-width: 1024px; 
	text-align: center; 
}


JavaScript file “script.js”

/*
script.js
for use with HTML clock
Andy Felong
09 Sep 2016
*/


// can't use "const" in IE -- so use "var"
var CLOCK_CHECK_SECS = 20 * 1000; // 20 seconds
var TEMP_CHECK_MINS = 10 * 60000; // 10 minutes
var WU_URL = "http://api.wunderground.com/api/XXXX_UW_API_KEY_XXXX/geolookup/conditions/q/XXX_STATE_XXX/XXX_YOUR_CITY_XXX.json";
var TEMP_UNITS = "temp_f"; // WU temperature units: Fahrenheit ("temp_f") or Celcius ("temp_c")


function getTime() {
    var today = new Date();
    var hrs = today.getHours();
    if (hrs > 12) { // use 12-hour clock, not 24
        hrs -= 12;
    } else if (hrs === 0) {
        hrs = 12;
    }
    var mins = today.getMinutes();
    if (mins < 10) {
        mins = "0" + mins; // will convert num to string but that's OK
    }
    document.getElementById('time_txt').innerHTML = hrs + ":" + mins; // update time div
    var t = setTimeout(getTime, CLOCK_CHECK_SECS);
}


// getTemp() - uses JQuery library to GET and parse JSON - included by index.html
// <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>

function getTemp() {
    jQuery(document).ready(function($) {
        $.ajax({
            url: WU_URL, // Weather Underground URL with key and location
            dataType: "jsonp", // cross-domain JSON request
            success: function(parsed_json) {
                var location = parsed_json['location']['city']; // could use city string if desired
                var temp_f = parsed_json['current_observation'][TEMP_UNITS]; // temperature of city passed in URL
                temp_f = Math.round(temp_f); // round temp to nearest degree
                var theTemp = temp_f + "&deg;";
                document.getElementById('temp_txt').innerHTML = theTemp; // update temperature div
            }
        });
    });
    var tmp = setTimeout(getTemp, TEMP_CHECK_MINS);
}

 

You need to include your specific Weather Underground API key as well as location for temperature disaply in the JavaScript file, “script.js” for the URL — variable “WU_URL”. In the U.S., the location is usually “2-letter state” + “/” + city — with underline substituted for a space.

Example:

var WU_URL = "http://api.wunderground.com/api/0123456789abcdef/geolookup/conditions/q/CA/Los_Angeles.json";

You should also set the browser width to your display width in the CSS file, “style.css”. Look for “max-width” and set this to your screen width. Change the font size inside the “body” section of CSS. Look for:

font-size: 320px; /* adjust to preference and display size */
and
max-width: 1024px; /* use the width of the display */

If you have installed kweb, setup the mykiosk configuration file, created the clock directory with the html, CSS and script files, you should be able to launch your clock with temperature via:

cd ~
xinit ./mykiosk

You can quit the display by hitting ALT-Q on your keyboard or by killing the process via ssh login.

Added window tint film

The LCD screen backlight was a bit bright in a dark room. I used Tap Plastics Window Tint Film to reduce “glare”. There was a slight color-shift but, for a clock-radio, this was not a concern.

clock_radio_20160909_small

Finished clock-radio. Easy to see across the room and not too bright!

28 Comments (+add yours?)

  1. Andy from Workshopshed
    Sep 12, 2016 @ 10:15:08

    Great project and neat idea using the web browser for the coding. You can set the max-width:100% to get it to work for any screensize.

    Reply

    • andy
      Sep 12, 2016 @ 10:28:15

      yes, setting “max-width: 100%” should work for all size screens BUT, I found that the kweb browser window may be larger than the screen and clip the right side. My quick & dirty fix was to set “max-width”. If you have configured kweb page-size correctly, you can and should use “max-width: 100%”. Check out the kweb docs. You could use the global “M’ or “N” arguments and set screen size.

      Reply

  2. John
    Sep 13, 2016 @ 02:51:29

    Why not just use a 1-wire temp sensor? It’s built in to the kernel but has to be attached to gpio 7

    Reply

    • andy
      Sep 13, 2016 @ 08:54:33

      John,

      You are correct it would be pretty simple to use a one-wire temperature sensor. Adafruit has a great how-to for this. I approached this project thinking about a “complete” weather forecast read-out and then scaled back to just the temperature to keep the display simple and clean — and easy to read across the room. Also it takes NO hardware 😉

      – Andy

      Reply

  3. Vicky T
    Sep 13, 2016 @ 09:25:49

    Great project. I should bring down my RPi s and Arduinos and we should build some stuff together. I’m starting to get into the ESP8266 which is a really inexpensive Arduino with a wi-fi module. And it’s really hard to use.

    Reply

  4. Tom Gidden
    Sep 13, 2016 @ 11:55:28

    I’ve done something very similar myself, using the official Raspberry Pi touchscreen, some clear red acrylic cut-to-order from eBay, and Python’s `pygame` framework for the software, running on the console rather than X11. It only took an evening to learn enough `pygame` and Python to do the job.

    Previously, I’d used a cheap Android tablet running a kiosk web browser, but it wasn’t reliable enough, and the font and graphics handling turned out much easier to get right with pygame, even though I have about twenty years more web experience (or even Xlib) than Python.

    Before that, I used large 7-segment displays from Adafruit.

    Recently, I’ve converted it to use MQTT for the sensors, letting me have a second identical RPi and screen on the other side of the room so I don’t have to turn over in bed to see it — yes, I’m just that lazy!

    With a plethora of sensors I have noticed that the humidity, pressure and even temperature readings are only useful as relative gauges: even temperature seems to vary ±2ºC depending on which sensor I consult. Regardless, rapid _change_ of barometric pressure is a migraine trigger for me, which is the whole point of this early warning system…

    Right now I’m trying to figure out how to read an outside temperature/humidity sensor over 433 MHz and Raspberry Pi GPIO, so I can loop that in too.

    The Pi also acts as a central hub for controlling 433MHz power sockets for lights and fan, a servo I’ve hacked to tilt the venetian blinds, two “ShuttleXpress” remote control consoles. I’m working on a 3D printed fitting to connect a stepper motor to a blackout roller blind (again, for the migraines) All of these features (all MQTT enabled) are also presented via a Bootstrap-structured web control panel that’s the homepage on my tablet and laptop browsers.

    The current display: https://www.instagram.com/p/BBk9We4l4zP
    Newly boxed: https://www.instagram.com/p/BIdCxJFh0zB
    The venetian blind controls: https://www.instagram.com/p/8aMGuzl46N

    Anyway, sorry for going on a bit… great job on your project, Andy… you’ve done a much nicer job on the case than my random Maplin black ABS box! 🙂

    Reply

  5. Peter van Evert
    Sep 18, 2016 @ 03:51:13

    Great Project idea for a lot of expansions, see above. I’m already on search after this simple solution to display time and other datas on a Raspi Screen. There’s only one thing I missed and that is to display the actual date, perhaps with the weekday. How can I expansion the code to see this ?

    Thanks for your support

    Yours
    Peter

    Reply

    • andy
      Sep 18, 2016 @ 10:32:19

      Peter, look in “script.js”. You’ll see the variable “today”. This has the ISO date/time. You can see where I extracted hours (“hrs”) and minutes (“min”). You could also get date and or day-of-week:

      theyear=date.getFullYear // returns 4 digit yesr
      themonth=date.getMonth() // return 0-11 for month
      monthday=date.getDate() // returns 0-31 for day of month
      weekday=date.getDay() // returns 0-6 for day of week

      handy functions for display can be found on this webpage

      Reply

  6. Randy
    Sep 25, 2016 @ 04:51:27

    Nice project! But how did you control the back lighting for the LCD? I build something similar with white numbers on a black background. But can’t reduce the back lighting enough to keep the display from causing a glow in a dark room.

    My display uses fluorescent so maybe I need to install LED back lighting instead?

    Reply

    • andy
      Sep 25, 2016 @ 08:57:17

      Hi Randy, most LCD display screens have light leakage, even on “black background”. To get “real black”, you can use what’s known as “blackout film” to cover the LCD front glass. This may alter colors a bit but works great for my dedicated clock & temperature display. It’s hard to find just small sizes of this film. Look on Amazon or Google for: “dark tint blackout film” or better: “dark tint black 50% static film”. I suggest using “static film” as opposed to “self-adhesive” as it is way easier to put on and take off.

      You can see some options at Tap Plastics. They sell film by the foot 🙂

      — Andy

      Reply

      • Randy
        Sep 29, 2016 @ 17:49:38

        I’ve already tried that. Used car window tinting material. In fact two layers of it! Viewing straight on, the background looks black. But walking by the display with it on a lower shelf and there is a real glare.

        Think I’ll try replacing the tube backlight with LEDs. Found a replacement kit on ebay that has a dimmer control.

        Another problem I have to fix is on initial power up. The LCD driver circuit comes up before the HDMI output of the Pi. This results in the display not recognizing the resolution of the Pi. If I do just a reboot of the Pi while leaving the LCD driver powered on, it works fine. Makes for a good way to know if the power has been out while at work!

        Reply

  7. Fremi
    Nov 23, 2016 @ 08:06:47

    Great project. I had Something similar planned: a raspberry pi alarm clock that can play music and control a smart lightbulb. After going through a lot of similar projects, your solution seems to be the best for me: despite my almost non-existent programming skills (and total lack of patience in these matters), I understodd your code and managed to replace the temperature reading by the calendar date with a different format (I know that sounds like nothing but it took me hours to get it).
    Have you got plans to add an alarm function within the script?

    Reply

    • andy
      Nov 30, 2016 @ 14:54:58

      Hi Fremi, I do plan on adding alarm clock functionality but it gets a bit tricky if you want to be able to change date/time and/or alarm sound/music. It is pretty easy to just play sound/music for short duration at the same time every day 🙂 More work with lots of code and UI to recreate a “normal” alarm clock 🙁

      Reply

  8. Dan
    Nov 24, 2016 @ 09:16:36

    I’m having trouble with the screen blanking. I have the code in my mykiosk file exactly how you have it, but the screen still goes blank. It stays on, but there is nothing displayed on it. I’ve scoured the web, and tried multiple things, but nothing seems to work. Any suggestions?

    Thank you for the great tutorial!

    Reply

    • andy
      Nov 30, 2016 @ 15:10:59

      Hi Dan, I’M assuming the clock works for a while before blanking? If so, check that you have “xset” installed. At the command line you can do: “which xset”. You should see “/usr/bin/xset” as the response. If not, install this package: “sudo apt-get install x11-xserver-utils”. If you do have “xset” installed, please check the mykiosk file to see this line, exactly: “xset -dpms”.

      LMK, Andy

      Reply

  9. Dan
    Feb 01, 2017 @ 05:46:41

    Andy,

    Thanks for your response. Unfortunately I had to take a break from working on my clock for a while. But, I’m back on the project now!

    The clock does work before the screen goes black, and continues to work while it is black. The screen stays powered on, but goes blank. When I tap a key or move the mouse, it comes back immediately with the correct time and everything.

    I checked “which xset” and did get the “/usr/bin/xset” response. I then checked the mykiosk file and have “xset -dpms” in there exactly. I’ve searched the web for a solution and it seems many people have this issue. I’ve tried changing different files and adding code, installing a screensaver and setting it to not come on, among other “fixes.” I don’t understand why the code isn’t working. Maybe it’s a bug of some sort?

    Thank you!

    Reply

    • Gary Howard
      Jun 15, 2017 @ 20:11:39

      Have you tried adding the following line to /etc/rc.local ?

      sudo sh -c “TERM=linux setterm -blank 0 >/dev/tty0”

      Reply

      • Dan Lowery
        Jul 08, 2017 @ 05:48:01

        Gary,

        I added the command you posted above in my rc.local file and the screen is still blanking. Do you have anything else installed that could be helping? I’m at a total loss as to why my screen won’t stay on for more than 10 minutes. I’ve tried different monitors just to make sure, and all of them go off at the 10 minute mark.

        Maybe you could copy and paste your rc.local file here? I’d also like to have it set to start when I boot the pi, that seems like a great idea.

        Thanks for your help!

        Dan

        Reply

        • andy
          Jul 08, 2017 @ 10:30:31

          Dan, permissions have recently changed with an X update. Try using “sudo” in front of the “xset” commads. Do this manually and try-it-out before placing in your script(s) — i.e.

          $ sudo xset s off
          $ sudo xset -dpms
          $ sudo xset s noblank

          – Andy

          Reply

          • Dan Lowery
            Jul 12, 2017 @ 08:27:15

            Andy,

            I tried entering those commands at the prompt, but just received the following after each one:

            xset: unable to open display “”

            Dan

  10. Sean
    Jun 04, 2017 @ 00:31:25

    Haha, I was wondering if many other people made these. You put a lot more effort into you clock than I did, and got a much nicer result. That DAC add-on was a nice touch.

    Reply

  11. Gary
    Jun 18, 2017 @ 20:42:25

    How do other people get kweb to autostart on boot up? I’ve added xinit to the rc.local script and it loads the page with the time ok but the weatherunderground api is not working.

    Reply

    • andy
      Jun 19, 2017 @ 10:59:57

      Hi Gary, does the webpage show weatherunderground info if you start manually? If so, check user/owner/permissions and PATH — if you are not starting as the same user these might be things to look at. Are you using full path names for references inside your HTML, JS and CSS files? LMK, Andy

      Reply

      • Gary
        Jun 30, 2017 @ 21:47:56

        Hi Andy, thanks for the suggestions.

        I’ve now fixed the issue by adding a delay before I call xinit in my /etc/rc.local script.

        sleep 10
        su -l pi -c “xinit ./mykiosk”

        Apparently rc.local gets run very early in the boot sequence so i guess networking or something was not setup when kweb was trying to make the ajax call to WU api.

        Reply

Leave a Reply