Retro-Joystick to USB Keyboard Adapter

Project introduction

I made an adapter to take classic video game controllers for Sega, Atari (2600, 7800) and Commodore (VIC20, C64, C128) and adapt them to work as a 5 button USB keyboard (4 directions and a fire/trigger key).

The joystick now works like an external numeric keypad. Pressing the fire/trigger button will act like pressing the right-hand Control/Ctrl key. This was all written in the Arduino IDE, and can be easily changed (if for example you prefer using W,A,S,D for direction and space bar to fire, or the arrow keys for movement, it’s a quick change to make.

I picked this configuration mostly because the C64 emulator VICE has it as an option.

Hardware

For the brains I used a board with an Atmel ATMega32U4, similar to the Arduino Pro Micro (see parts list below).

As an enclosure I used a DE-9 adapter housing. This one had terminal blocks for quickly throwing cables together, which I removed to make room.

I chose this particular DE-9 adapter because it looked like it might be roomy enough to put the microcontroller into, however it wasn’t quite long enough. So, with some heavy duty cutters I use for chomping up bits of PCB I cut the last two pin holes off the microcontroller board.

After cutting I sanded the edge and visually inspected to try to make sure no obvious unexpected connections between layers were being made. Cutting this down gave me enough room to fit it into the enclosure. I’ve shown this modification in the microcontroller PCB image below as a red line through the PCB.

I soldered up some wires between the DE-9 PCB and the microcontroller PCB. The silk screen of the DE-9 PCB made it easy to quickly identify which pins were which. The I/O pins I chose on the microcontroller were really just a matter of convenience to get the cables to cooperate nicely with the DE-9 PCB and leave enough room to close the adapter enclosure. I didn’t annotate it very well in the image below, but the DE-9 ground (GND) pin should be wired to any one of the available microcontroller GND pins.

To prevent accidental electrical connections between microcontroller PCB and the DE-9 PCB I wrapped the microcontroller in some Kapton tape, but hot glue or something would probably work fine. The end result looks like a big mess, thankfully I don’t have to look at it with the enclosure closed.

I ended up needing to use a hobby knife to carve out a little of the cable opening of the DE-9 enclosure to widen it for the USB micro connector end.

Software

To find the numeric keypad directional key values I referenced the USB HID Usage Tables found here:
http://www.usb.org/developers/hidpage/Hut1_12v2.pdf

A quirk of the Arduino USB keyboard report handling requires you to add 136 (decimal) to the value you find in the usage table. This value is later removed by other code in the USB library, but helps the source make some kind of determination on how to handle the key event (it seems pretty crazy to me too, but whatever, it works).

Because the board I’m using has an ATmega32U4 with an Arduino bootloader it shares enough in common with the Arduino Leonardo that “Arduino Leonardo” should be selected as the target board in the Arduino IDE to properly compile and flash the software.

download joykey.ino

// Some more meaningful names for referencing applicable I/O pins
#define FIRE_PIN  2
#define UP_PIN  3
#define DOWN_PIN  16
#define RIGHT_PIN  15
#define LEFT_PIN  14

// Arduino-friendly (value+136) keyboard values of direction keys of numeric keypad
#define KEY_PAD_UP 232
#define KEY_PAD_DOWN 226
#define KEY_PAD_RIGHT 230
#define KEY_PAD_LEFT 228

// Map a keyboard value to the input pin. First Pins[] value is first Keys[] value, etc
byte Pins[] = {FIRE_PIN,        UP_PIN,     DOWN_PIN,     RIGHT_PIN,     LEFT_PIN    };
byte Keys[] = {KEY_RIGHT_CTRL,  KEY_PAD_UP, KEY_PAD_DOWN, KEY_PAD_RIGHT, KEY_PAD_LEFT};

void setup() {
  // pullup all button input pins to high
  for (int i = 0; i <= sizeof(Pins); i++) {
    pinMode(Pins[i], INPUT_PULLUP);
  }
 
  Keyboard.begin();
}

void loop() {

  {
    int keyStates = 0;
    for (int i = 0; i <= sizeof(Pins); i++) {
      if (digitalRead(Pins[i]) == LOW) {
        Keyboard.press(Keys[i]);
        keyStates = 1;
      }
    }
    if (keyStates == 1) {
      // pause a moment to give any pressed keys a chance to report, and possibly ignore some joystick bounce as well
      delay(10);
    }
  }

  // Release any keys that need to be released
  for (int i = 0; i <= sizeof(Pins); i++) {
    if (digitalRead(Pins[i]) == HIGH) {
      Keyboard.release(Keys[i]);
    }
  }

  // If no buttons are being pressed, releaseAll() to ensure no keys are stuck down
  {
    int pinStates = 1;
    for (int i = 0; i <= sizeof(Pins); i++) {
      pinStates = pinStates & digitalRead(Pins[i]);
    }
      if (pinStates == HIGH) {
        Keyboard.releaseAll();
      }
  }

}

Here is a description of what the code is doing.

#defines
The source defines some names for the pins that connect to the corresponding DE-9 pins (see the “Hardware” notes above for clarification) and some names for the keypad values. The right-hand Ctrl key value(KEY_RIGHT_CTRL) is already defined in the Arduino USB library.

setup()
The connected pins are internally pulled high, and are considered activated when connected to ground through the normal use of the joystick. The pins to be monitored for these state changes are put into the Pins[] array and the corresponding key values to send in the USB report are put in the Keys[] array at the respective array index position.
The function setup() gets ran at device power-up and loops through all the pins listed in Pins[] to configure the internal pullup to high state. Keyboard.begin() prepares the Arduino keyboard library.

loop()
The main loop() iterates through each pin in Pins[] looking for a low state and applicably sets the corresponding key. If any keys are found to be pressed a ~10ms pause occurs to allow the operating system ample time to see the USB report, but not offer too much delay as to cause the joystick to feel unresponsive to rapid changes. This delay also helps remove sporadic changes in pin state due to imperfect contact between the joystick internal connection plates. Another iteration through the Pins[] array occurs to remove the appropriate data from the USB report for any keys no longer being held down. A final (possibly unnecessary) check occurs which calls Keyboard.releaseAll() when no positions of the joystick are active (low). I had some issues during initial develop with keys getting stuck “down” and found it very irritating to have to physically unplug the adapter in order to resolve the problem. This check was added to make life a little easier, but may no longer be required. Keyboard.releaseAll() only removes reports from memory on the microcontroller and won’t cause the USB host to ignore held keys on other connected keyboards, so it seems harmless enough to leave in place.

Bill of Materials

Arduino Pro Micro-like board
https://www.amazon.com/gp/product/B012FOV17O

“DB9” / DE-9 9 pin adapter module (terminal blocks will need to be removed)
https://www.amazon.com/gp/product/B00YM3W7OI

Micro USB cable
https://www.amazon.com/gp/product/B003YKX6WM

Resources

USB HID Usage Tables
http://www.usb.org/developers/hidpage/Hut1_12v2.pdf

-Have fun!

Posted in General Nonsense | Leave a comment

Arduino Leonardo (ATMega32u4) USB Power Button

Project introduction

For fun I made this USB power button with an Arduino Micro/Leonardo-like board (by which I mean one using the Atmel ATMega32u4 MCU with USB controller).

The project details are below in case you should want to read or watch how I did it.

I used an ATMega32U “arduino”-like board from Amazon.com although there is an official Arduino Micro that should work just as well. For development/testing I used the Arduino Leonardo. Any of those could work in a similar project, the only notable difference being size and available I/O pins.

I put mine inside of an “Emergency Stop” button. I decided to get a red USB cable too because hey, why not?

USB HID control of desktop power management features is possible using the Generic Desktop / System Control Collection (Usage Page: 0x01Generic Desktop Page“, Usage Id: 0x80System Control“) with the Usage 0x81System Power Down“. For more details consult the USB HID Usage Tables.

Software

Initially I planned to use the Arduino USB Keyboard functions, but I came to realize the USB HID keyboard device doesn’t contain the power management buttons. Similarly media keys such as Play/Pause, Next, Previous, Volume Up/Down, are also not handled in the same way as other keyboard input. Most examples of using the Arduino Leonardo to send media key events to the computer reference some work by Stefan Jones. Following his blog post I updated my Arduino libraries to include his modifications. With his changes and a couple of small tweaks, I was able to send a system power down request over USB.

I used the Arduino IDE version 1.6.5, I think the USB libraries were changed a lot in 1.6.6, so if you are using a later version things may require substantial adjustments.

I left Stefan’s additions to the file hardware/arduino/avr/cores/arduino/USBAPI.h unchanged.

As shown below, inside of the file hardware/arduino/avr/cores/arduino/HID.cpp I commented out the “Consumer Devices” (0x05, 0x0c) value and the “Consumer Control” (0x09, 0x01) value and added the “Desktop” (0x05, 0x01) and “System Control” (0x09, 0x80) values. Next I commented out the “Mute” value (0x09, 0xe2) and added the “System Power Down” value (0x09, 0x81), hijacking the Remote.mute() function to work as my “System Power Down” function.

//-----------------------------------------------------------------------------

    /* Cross-platform support for controls found on IR Remotes */

    //0x05, 0x0c,                    // Usage Page (Consumer Devices)
    0x05, 0x01,                    //   Usage Page (Desktop)
    //0x09, 0x01,                    // Usage (Consumer Control)
    0x09, 0x80,                    //   Usage (System Control)
    0xa1, 0x01,                    //   Collection (Application)
    0x85, 0x04,                    //   REPORT_ID (4)
    0x15, 0x00,                    //   Logical Minimum (0)
    0x25, 0x01,                    //   Logical Maximum (1)

    0x09, 0xe9,                    //   Usage (Volume Up)
    0x09, 0xea,                    //   Usage (Volume Down)
    0x75, 0x01,                    //   Report Size (1)
    0x95, 0x02,                    //   Report Count (2)
    0x81, 0x06,                    //   Input (Data, Variable, Relative)

    //0x09, 0xe2,                    // Usage (Mute)
    0x09, 0x81,                    //   Usage (System Power Down)
    0x95, 0x01,                    //   Report Count (1)
    0x81, 0x06,                    //   Input (Data, Variable, Relative)

The Arduino sketch is taken from Stefan’s example for using Remote.mute, (which I’ve re-purposed above). I’ve added a couple of extra lines to wait for the button press before sending the shutdown report to the USB host.

void setup() {
  pinMode(2, INPUT_PULLUP);
}

void loop() {
  while(digitalRead(2) == HIGH) {
    // nop nap
    delay(1);
  }

  Remote.mute ();
  Remote.clear();

  delay(5000); /

}

In the Arduino IDE I selected “Arduino Leonardo” from Tools->Board menu and select the correct com port under Tools-Port, and flashed the new software to my board.

Hardware

With that done all that was left was soldering up some wires and connecting them to the button.

I soldered a piece of yellow wire to pin 2 of my board and a piece of green wire to the near by ground pin as shown below.

Then I just had to attach an exposed section of the other side of the wires to the screw terminals on the switch. On the switch I bought there was a side labeled “A” and a side labeled “B”.

Using a multimeter set on continuity test I determined the conditions of the connections between terminals with the switch depressed and released. On my switch the two terminals within the green side labeled “A” were closed when the push button was pressed, and the two terminals on the orange side labeled “B” were opened once the button was pressed.

Usage Notes

The effect of the power button can be changed in the OS. In Windows 10 you can get to this under Power Options as shown below.

I have mine set to sleep, which looks a bit cooler / quicker on the video I above, and also was a lot less annoying for troubleshooting.

Bill of Materials

“Emergency Stop” Push Button
https://www.amazon.com/gp/product/B00MJVMV32

Red USB cable
https://www.amazon.com/gp/product/B013DO561W

“Pro Micro” ATmega32u4 “arduino Leonardo”-like board
https://www.amazon.com/gp/product/B012FOV17O

Resources

Sending HID Media Key (modified this)
http://stefanjones.ca/blog/arduino-leonardo-remote-multimedia-keys/

USB HID Usage Tables
http://www.usb.org/developers/hidpage/Hut1_12v2.pdf

Posted in General Nonsense | Leave a comment

Raspberry Pi RTS / CTS Flow Control

I had need of a 3.3v TTL serial interface to reprogram a device I was working on. Looking around I realized I had mostly 5v logic USB to serial adapters. I figured the Raspberry Pi would work well enough, but I also needed hardware flow control. As it turned out the Pi can do RTS / CTS flow control, so I documented my notes on the subject here in case I or someone else might find them useful in the future.

What is flow control

In serial communications occasionally we need a way to tell the device or computer on the far-end of our serial connection that we need it to shut up for a bit while we collect our thoughts. Flow control exists for this purpose, although it is not a requirement of serial communication. Modern devices are fast, with large buffers for collecting incoming data, and often times flow control is omitted, particularly if data loss isn’t critical.

Flow control can be done in software (Xon/Xoff) by sending special control characters to say stop or start the data flow. This creates some hassle since, particularly in the transmission of binary data, those control characters might come up naturally without the intent to change the flow of data. If you’ve ever accidentally frozen your Linux terminal (^s / ^q) you know what I’m talking about.

Flow control can also be done out of band of the data transmission using dedicated signal pins/wires, we call this hardware flow control. Serial communications are old, there are a lot of variations so it probably isn’t much of a surprise to learn that there are multiple ways of doing hardware flow control. The most common are RTS / CTS and DTR / DSR. We are focusing on RTS / CTS, although DTR is still useful to know about (it gets used for example in programming the Arduino, to trigger a reset of the Arduino before programming). Because we can arbitrarily set the state of RTS and DTR via software we can abuse them to signal all sorts of stuff to connected hardware beyond just flow control.

RTS stands for “Request to Send”, and in the current common use it is a terrible name for what it actually does. The name implies that it signals a request to send data to the equipment on the other side of the serial connection. CTS stands for “Clear to Send”, and as we might (correctly) assume it is a signal being sent to us from the far-end that indicates we are cleared to send our data. The problem with this is that it is only half-duplex (it goes just one way). How could we tell the far-end that we aren’t ready to receive data? Originally this was actually how it worked, it was intentionally half-duplex and it was designed with some rather old modem technology in mind.

RTR or “Request to Receive” is probably a better name for RTS as it is used today, and some people prefer to use this term instead. We, or rather the UART controlling our communication doesn’t actually change the state of RTS when it wants to transmit data. RTS/RTR gets changed when the UART is unable to receive any more data from the far-end. This probably indicates that our hardware receive buffer is full and hasn’t been read and emptied out by software on our system yet.

On the Raspberry Pi RTS is set low (0v) as long as the Pi’s UART is ready to receive data. If it can’t handle more incoming data it sets RTS high (3.3v). Our Pi should only send data to the far-end when the state of our CTS pin reads low and we should stop sending data if our CTS pin is high. So now we’ve got a working plan for bi-directional flow control. Our RTS goes to the CTS of the far-end and the far-end RTS goes to our CTS (and hopefully we both agree on the logic levels).

In our serial connection RTS and TX are our outputs, and CTS and RX are our inputs.

Where are the RTS / CTS pins?

On older versions of the Raspberry Pi with 26-pin GPIO header, you need to solder on an additional 2×4 section of dual row male 0.1″ (2.54mm) header pins on the unpopulated “P5” header.

On early Pi’s like the original version B, this header isn’t there, and so if you are still using one of those devices you’ll probably need to work something else out.

The P5 header is actually intended to be mounted on the bottom of the Pi board (opposite to the 26 pin GPIO). That seemed pretty crazy to me, because the thing would obviously never lay flat or fit in standard cases that way. I think the intent is to avoid interfering with Raspberry Pi hats that sit on top of the 26-pin header, but even with P5 populated on the reverse side of the board (same side as the 26-pin headers) I was able to put the one hat I have on my Pi. Your miles may vary of course. If you do put the P5 header on the same/sane side of the board as the 26 pin header keep in mind that many of the pin-outs you run into online might be mirrored.

To remove ambiguity I’ve just colored the correct pins below rather than giving pin or GPIO numbers:

On newer Raspberry Pis with the 40-pin header, this is much easier, there is nothing to solder first:

Enabling RTS / CTS flow control

OK, so now we’ve got the physical connections handled, but we still need to enable the RTS/CTS mode of these pins. As you might know several (most) of the Raspberry Pi GPIO pins live double (or triple) lives. By instructing the Broadcom SoC to enable the alternate functions we can get additional hardware features, I2C, hardware flow control for our UART, etc.

We need to change a bit of memory to inform the Broadcom SoC which function of the GPIO we want. An easy way to do this for RTS / CTS is to use Matthew Hollingworth’s rpirtscts utility:

https://github.com/mholling/rpirtscts

root@raspberrypi:~# rpirtscts
Version: 1.5
Usage: rpirtscts on|off
Enable or disable hardware flow control pins on ttyAMA0.

For 26 pin GPIO header boards:
P5 header pins remap as follows:
    P5-05 (GPIO30) -> CTS (input)
    P5-06 (GPIO31) -> RTS (output)

For 40 pin GPIO header boards:
    P1-36 (GPIO16) -> CTS (input)
    P1-11 (GPIO17) -> RTS (output)

You may also need to enable flow control in the driver:
    stty -F /dev/ttyAMA0 crtscts

The last little note about stty in the rpirtscts help message is useful to mention. If you are using software that is aware of hardware flow control (i.e. will use the ioctl syscall to enable / disable it) you don’t need to worry about setting the stty option. If you are sending input/output from programs over serial that are not specifically intended to be sent over a serial line, then you will probably want to tell stty to tell the system to enable RTS / CTS. The rpirtscts utility sets the Broadcom SoC to use the RTS / CTS alternate functions of the applicable GPIO pins, but it doesn’t tell Linux to enable RTS / CTS flow control.

Current versions of rpirtscts can detect which Raspberry Pi you are using and take the appropriate action for that hardware.

Using rpirtscts on the 26-pin Pi looks like this:

root@raspberrypi:~# rpirtscts on
26-pin GPIO header detected
Enabling CTS0 and RTS0 on GPIOs 30 and 31

The 40-pin Pi is set with the same command, but uses the (different) correct GPIOs for that hardware:

root@raspberrypi:~# rpirtscts on
40-pin GPIO header detected
Enabling CTS0 and RTS0 on GPIOs 16 and 17

As an alternative to rpirtscts, if you’d like the CTS / RTS alternate function to be the usual function of your particular Pi (and persist across reboots) you might instead consider configuring a device tree overlay:

https://www.raspberrypi.org/documentation/configuration/device-tree.md

Also note that the Raspberry Pi 3 uses the ttyAMA0 port for driving Bluetooth, so there are some additional considerations to deal with. Thankfully Weave has this is all well documented here:

http://www.deater.net/weave/vmwprod/hardware/pi-rts/

Good luck!

Check out http://www.pighixxx.com/ for great art of popular electronics.

Posted in General Nonsense | Leave a comment

Elastix *69 (Call Trace) Fix

We’re running an older installation of Elastix at my office, version 2.0.0 (release 58) on top of freePBX version 2.7.0 (release 10). The call trace feature was bothering me. This feature is mapped to the *69 feature code as is common with many LECs in North America. In the default implementation you hear an announcement about the number of the last incoming call, and can then optionally press the “1” key to call the party back. I was frequently annoyed with having to wait for the IVR attendant to finish announcing the number of my last incoming call, I couldn’t interrupt the announcement without waiting for her to finish. Seemed pretty clear that I needed to replace Playback() with Background() some place.

Here is a video showing the original behavior, and the modified behavior.

Inside of /etc/asterisk/extensions_additional.conf is a context for [app-calltrace-perform]. Originally I made the correction in there, completely ignoring the header comment that read “Do NOT edit this file as it is auto-generated by FreePBX”. This worked, for a little bit, until changes were made and applied in the Elastix web interface, and then as promised the config was overwritten by a new auto-generated configuration.

It appears the correct place to add this is in /etc/asterisk/extensions_override_freepbx.conf

I appended the following lines to the end of extensions_override_freepbx.conf to accomplish the desired change.

[app-calltrace-perform]
include => app-calltrace-perform-custom
exten => s,1,Answer
exten => s,n,Wait(1)
exten => s,n,Macro(user-callerid,)
exten => s,n,Set(lastcaller=${DB(CALLTRACE/${AMPUSER})})
exten => s,n,GotoIf($[ $[ "${lastcaller}" = "" ] | $[ "${lastcaller}" = "unknown" ] ]?noinfo)
exten => s,n,Background(info-about-last-call&telephone-number)
exten => s,n,SayDigits(${lastcaller})
exten => s,n,Set(TIMEOUT(digit)=3)
exten => s,n,Set(TIMEOUT(response)=7)
exten => s,n,Background(to-call-this-number&press-1)
exten => s,n,Goto(fin)
exten => s,n(noinfo),Playback(from-unknown-caller)
exten => s,n,Macro(hangupcall,)
exten => s,n(fin),Noop(Waiting for input)
exten => s,n,WaitExten(60,)
exten => s,n,Playback(sorry-youre-having-problems&goodbye)
exten => 1,1,Goto(from-internal,${lastcaller},1)
exten => i,1,Playback(vm-goodbye)
exten => i,n,Macro(hangupcall,)
exten => t,1,Playback(vm-goodbye)
exten => t,n,Macro(hangupcall,)
; end of [app-calltrace-perform]

When done, save the file and reload the dialplan

asterisk -rx "dialplan reload"

This all comes from Elastix (well technically FreePBX I think) other than the replacement of Playback() with Background() during the playback of info-about-last-call.gsm. Of course I also had to move Set(lastcaller..) and stuff above Background() since we can now jump straight to exten => 1,1 without having those variables set.

I’m not sure if this has changed in later versions of Elastix 2.x. I have to assume this annoys other people as well, but maybe it’s just me. If you have anything to add let me know in the comments.

Thanks

Posted in General Nonsense | Leave a comment

Grandstream GXP21xx Weather

UPDATE: This article (and sadly many others) have sat in my “Drafts” folder for far too long. I had originally intended to give this a look-over before release about a year ago, but I never got around to it. One of my co-workers recently reported a problem with the weather display on their IP phone. It seems that the Yahoo! feed may now be down (as predicted in this article). I’m publishing this now to assist anyone who may be interested in working on a MITM-type replacement for it. The original intent of this article was to help people find the appropriate city-code for their area.

Grandstream GXP21xx-series phones have a neat weather application. I’m using a GXP2140 with color screen and I thought it would be nice to display the weather on the home screen (a feature native to the phone).

grandstream_weather

The trick is trying to find the Location ID, or as Grandstream calls it “City Code” for your area. The phone automatically tries to find the best Location ID to use, but at least in my case, it wasn’t as accurate as I would have liked. So how do you find the correct location?

A packet capture shows that the phone grabs its weather information from Yahoo!

GET /forecastrss?p=USNJ0234&u=f HTTP/1.1
User-Agent: Grandstream Model HW GXP2120 SW 1.0.8.4 DevId 000b8200ff00
Host: xml.weather.yahoo.com
Accept: */*

A little research shows that Yahoo! gets this data from Weather.com, and these Location IDs are Weather.com related. You can query weather.com for a Location ID for a given place name in text form at the following link:

http://wxdata.weather.com/wxdata/search/search?where=London

Only the first 10 results are shown:

<search ver="3.0">
 <loc id="UKXX0085" type="1">London, GLA, United Kingdom</loc>
 <loc id="USAR0340" type="1">London, AR</loc>
 <loc id="USCA9301" type="1">London, CA</loc>
 <loc id="USKY1090" type="1">London, KY</loc>
 <loc id="USOH0520" type="1">London, OH</loc>
 <loc id="USTX0788" type="1">London, TX</loc>
 <loc id="USWV0443" type="1">London, WV</loc>
 <loc id="SFXX6559" type="1">London, LP, South Africa</loc>
 <loc id="SFXX7547" type="1">London, MP, South Africa</loc>
 <loc id="FJXX0101" type="1">Londoni, C, Fiji</loc>
</search>

Let’s use New York, NY as an example.

$ wget -q -O- "http://wxdata.weather.com/wxdata/search/search?where=New%20York"
<search ver="3.0">
 <loc id="USNY0996" type="1">New York, NY</loc>
 <loc id="UKXX7149" type="1">New York, LIN, United Kingdom</loc>
 <loc id="JMXX0950" type="1">New York, 14, Jamaica</loc>
 <loc id="JMXX1405" type="1">New York, 06, Jamaica</loc>
</search>

A few different results are returned for “New York“, but the one we want it USNY0996. Once we know this code we update it in the web interface of the IP phone. In the case of my phone this was found under Settings -> Web Services

grandstream_weather

It looks like Yahoo! is using a newer API for weather these days, so I’m not sure how much longer these phones will be able to get weather. Documentation about the API is available here:

https://developer.yahoo.com/weather/archive.html

For posterity I’ve included a quick partial example of the output from these requests, in case you should ever need to implement your own service (to be intercepted at your firewall, or via DNS) in order to maintain this feature on your phone.

curl -s "http://xml.weather.yahoo.com/forecastrss?p=UKXX0085&u=c"

The results have been trimmed down for brevity

<channel>
 <lastBuildDate>Thu, 20 Aug 2015 7:19 pm BST</lastBuildDate>
 <ttl>60</ttl>
 <yweather:location city="London" region=""   country="UK"/>
 <yweather:units temperature="C" distance="km" pressure="mb" speed="km/h"/>
 <yweather:wind chill="20"   direction="210"   speed="16.09" />
 <yweather:atmosphere humidity="78"  visibility="9.99"  pressure="1015.92"  rising="0" />
 <yweather:astronomy sunrise="5:50 am"   sunset="8:12 pm"/>
<item>
 <pubDate>Thu, 20 Aug 2015 7:19 pm BST</pubDate>
 <yweather:condition  text="Mostly Cloudy"  code="28"  temp="20"  date="Thu, 20 Aug 2015 7:19 pm BST" />
 <yweather:forecast day="Thu" date="20 Aug 2015" low="17" high="22" text="Mostly Cloudy" code="27" />
 <yweather:forecast day="Fri" date="21 Aug 2015" low="15" high="24" text="AM Clouds/PM Sun" code="30" />
 <yweather:forecast day="Sat" date="22 Aug 2015" low="18" high="29" text="Sunny" code="32" />
 <yweather:forecast day="Sun" date="23 Aug 2015" low="14" high="22" text="Showers" code="11" />
 <yweather:forecast day="Mon" date="24 Aug 2015" low="12" high="20" text="Showers" code="11" />
</item>
</channel>
Posted in General Nonsense | Leave a comment