Laptop Digital Picture Frame
I managed to get my hands on an old Dell Inspiron 3800 laptop and, after spending a day or so fiddling with it, I finally decided it had no use as a laptop. The laptop was useless for a number of reasons: The battery didn’t work, it had a single USB 1.1 port and only 64MB of RAM. Now, I know I could have bought some more RAM, but considering it takes 144-pin modules it was something I wasn’t prepared to buy.
So what now? There was only one way to go. Turning your old laptop into a digital picture frame seems rather popular at the moment and since Christmas is on its way, I thought I’d give it a go myself.
I apologise now. The laptop runs Windows XP. I know that for it to be at all reputable I shouldn’t have done that, but after playing with a number of Linux distributions and not being happy with any, it was a lot quicker to get XP running. If I’d had more time then I would have done the job properly but as I said, Christmas was closing in!
For obvious reasons I wanted the picture frame to be controlled from another machine on the network, so the first thing to be installed was Apache. Again, for quick setup I decided to install trusty old PHPDev, which I’d had lying around for ages. True, it’s not very secure, and using an old installer meant I got rather outdated versions of Apache and PHP, but the out-of-the-box setup was good enough for me.
I wanted the picture frame to show pictures from a specific network location if it was available — my parents have a network drive with all their pictures on it — so I messed around with showing pictures in a Flash slideshow that I’d made for our Media Center screensaver. The RAM stick reared its ugly head again; the cross fade in the slideshow was very jerky, so the Flash idea was ditched.
The next option was to write a program that would flip through random photographs on a network drive, but after revisiting Christoffer Järnåker’s write-up I went for IrfanView to produce the slideshows. Success! Cross fading was back!
IrfanView also features a lot of nice command-line options that meant I could control it using PHP’s
exec() function. Or so I thought.
With Apache running as a service, I could either run it as LocalSystem and allow it to interact with the desktop, or give it network privileges. Not both. I decided to give Apache the network privileges it required and work out some sort of message-passing system to start the slideshows on the desktop.
The rather clunky but strangely satisfying method I went for was this: a PHP script output a text file containing paths to images that should be shown in the slideshow, and then updates a configuration file with instructions to execute the new slideshow. A constantly running VB program checks the config file and sends
Shell() commands to IrfanView accordingly. Phew!
Here’s the code for the VB program (I’ve neglected to include the module that handles the forced shutdown of the laptop):
Option Explicit Private Declare Function GetLogicalDriveStrings Lib "kernel32" Alias _ "GetLogicalDriveStringsA" (ByVal nBufferLength As Long, _ ByVal lpBuffer As String) As Long Const bDebug = False Dim sIrfanPath As String Dim sDebugFlashDrive As String Dim sNoDebugFlashDrive As String Dim sFlashDrive As String Dim sHandlerPath As String Dim sTestImagePath As String Dim sFlashSlideshowPath As String Dim sDefaultImagesPath As String Dim sSettingsDate As String Dim bDeleteFile As Boolean Dim bInDefault As Boolean Dim bUpdate As Boolean Dim sAction As String Dim sActionCommand As String Dim fTestImage As File Private Sub Form_Click() End End Sub Private Sub Form_Load() Dim lFileHandle As Long lFileHandle = FreeFile() Open App.Path & "\settings.ini" For Input As lFileHandle Line Input #lFileHandle, sIrfanPath Line Input #lFileHandle, sDebugFlashDrive Line Input #lFileHandle, sNoDebugFlashDrive Line Input #lFileHandle, sHandlerPath Line Input #lFileHandle, sTestImagePath Line Input #lFileHandle, sFlashSlideshowPath Line Input #lFileHandle, sDefaultImagesPath Close #lFileHandle sIrfanPath = Trim(sIrfanPath) & " " sHandlerPath = App.Path & "\" & sHandlerPath sTestImagePath = App.Path & "\" & sTestImagePath sFlashSlideshowPath = App.Path & "\" & sFlashSlideshowPath sDefaultImagesPath = App.Path & "\" & sDefaultImagesPath sFlashDrive = IIf(bDebug, sDebugFlashDrive, sNoDebugFlashDrive) If bDebug Then Me.Visible = True Me.BackColor = vbWhite Me.Width = Screen.Width / 2 Me.Height = Screen.Height / 2 End If Dim oFSO As Object, fFolder As Folder, fSubfolder As Folder, fFile As File Set oFSO = CreateObject("Scripting.FileSystemObject") Set fTestImage = oFSO.GetFile(sTestImagePath) bDeleteFile = False End Sub Private Sub tmrPause_Timer() Dim sNewSettingsDate As String, sNewAction As String, sNewActionCommand As String Dim lFileHandle As Long, bManualChange As Boolean bManualChange = False bUpdate = True If DriveExists(sFlashDrive) Then 'should show some flash drive stuff If sActionCommand <> sFlashSlideshowPath Then bManualChange = True bInDefault = False CreateFlashDriveSlideshow End If sNewAction = "flash" sNewActionCommand = sFlashSlideshowPath ElseIf DriveExists("Z") Then 'should show networked stuff If FileExists(sHandlerPath) Then sNewSettingsDate = FileDateTime(sHandlerPath) If (bInDefault Or sNewSettingsDate <> sSettingsDate) _ And sNewSettingsDate <> vbNullString _ And FileLen(sHandlerPath) > 0 Then lFileHandle = FreeFile() bInDefault = False Open sHandlerPath For Input As lFileHandle Line Input #lFileHandle, sNewAction Line Input #lFileHandle, sNewActionCommand Close #lFileHandle End If Else If sActionCommand <> sDefaultImagesPath Then bManualChange = True End If sNewAction = "default" sNewActionCommand = sDefaultImagesPath bInDefault = True End If Else 'should do some default stuff If sActionCommand <> sDefaultImagesPath Then bManualChange = True End If sNewAction = "default" sNewActionCommand = sDefaultImagesPath bInDefault = True End If If bDeleteFile Then DeleteFile sActionCommand If sNewSettingsDate <> sSettingsDate Or bManualChange Then Select Case sNewAction Case "slideshow", "flash", "default" bDeleteFile = False If bDebug Then Print "Changing to slideshow at " & sNewActionCommand Else Shell sIrfanPath & "/killmesoftly" Shell sIrfanPath & "/slideshow=" & sNewActionCommand End If Case "single" RestorePreviousHandler bDeleteFile = True If FileLen(sNewActionCommand) > 0 Then If bDebug Then Print "Changing to uploaded image at " & sNewActionCommand Else Shell sIrfanPath & "/killmesoftly" Shell sIrfanPath & sNewActionCommand End If End If Case "delay" tmrPause.Interval = (CInt(sActionCommand)) * 1000 If bDebug Then Print "Changing timer interval to " & (CInt(sActionCommand)) * 1000 Case "kill" RestorePreviousHandler Shell sIrfanPath & "/killmesoftly" End Case "shutdown" RestorePreviousHandler If bDebug Then Print "Shutting down" Else Shell sIrfanPath & "/killmesoftly" ShutdownSystem EWX_FORCESHUTDOWN End If End Select Else If bDebug Then Print "Do nothing" bUpdate = False End If If bUpdate Then sAction = sNewAction sActionCommand = sNewActionCommand End If If FileExists(sHandlerPath) Then sSettingsDate = sNewSettingsDate End Sub Sub DeleteFile(sPathName As String) 'deletes given file If FileExists(sPathName) Then Dim oFSO As Object Set oFSO = CreateObject("Scripting.FileSystemObject") oFSO.DeleteFile sPathName 'delete the file End If End Sub Function DriveExists(ByVal sDrive As String) As Boolean Dim buffer As String buffer = Space(64) ' return False if invalid argument If Len(sDrive) = 0 Then Exit Function ' get the string that contains all drives GetLogicalDriveStrings Len(buffer), buffer ' check that the letter we're looking for is there DriveExists = InStr(1, buffer, Left$(sDrive, 1), vbTextCompare) End Function Function FileExists(sPathName As String) As Boolean ' checks if a file exists If Len(sPathName) > 0 Then ' checks filename entered FileExists = (Len(Dir(sPathName)) > 0) ' dir function returns directory if file exists Else: FileExists = False End If End Function Sub CreateFlashDriveSlideshow() Dim lFileHandle As Long lFileHandle = FreeFile() Open sFlashSlideshowPath For Output As lFileHandle ParseFolder sFlashDrive, lFileHandle Close #lFileHandle End Sub Sub ParseFolder(sFolder As String, lFileHandle As Long) Dim oFSO As Object, fFolder As Folder, fSubfolder As Folder, fFile As File Set oFSO = CreateObject("Scripting.FileSystemObject") Set fFolder = oFSO.GetFolder(sFolder) For Each fSubfolder In fFolder.SubFolders ParseFolder fSubfolder.Path, lFileHandle Next fSubfolder For Each fFile In fFolder.Files If fFile.Type = fTestImage.Type Then Print #lFileHandle, fFile.Path End If Next fFile End Sub Sub RestorePreviousHandler() Dim lFileHandle As Long lFileHandle = FreeFile() Open sHandlerPath For Output As lFileHandle Print #lFileHandle, sAction Print #lFileHandle, sActionCommand Close #lFileHandle End Sub
I left in the debugging mode just in case I needed to make some changes later on. All the program constants are loaded in from another configuration file, hopefully meaning minimal recompilation.
In addition to the VB handler, I needed to set IrfanView up to show everything correctly. I wanted the images to be displayed randomly, the slideshow to be full-screen and the cursor to be hidden.
To summarise the software, the picture frame will show photographs at random from a pre-determined network location, unless none are available, in which case it defaults to a small collection of images on the hard drive. If someone inserts a USB flash drive containing images, then the current slideshow is exited and the images from the flash drive are displayed randomly until it is removed, at which point the picture frame resumes the previous slideshow. Using the Web interface, a single image can be uploaded to the picture frame that will be displayed for one cycle before returning to the previous slideshow. At the last minute I decided to enable remote desktop if anything should go wrong.
To summarise the summary: I’m very happy with the software!
I don’t need most of what’s provided in this laptop unit. I don’t want the battery or CD-ROM drive, the keyboard or mouse pad, or any of the casing. All I need is the screen, the hard drive and the stripped-down motherboard.
I started with the screen. All the screws on my display unit are located under rubber covers; however they’re just glued down so they prise straight out. After removing those, I flipped over the laptop and removed the screws holding in the keyboard (these are nicely labelled “K” on many Dell laptops) and the display (marked “D”) before turning it back over and pulling the screen away from the main assembly.
I moved the keyboard out of the way so I could get to the connector for the screen. These connectors are rather sturdy but it’s still not advisable to pull the cable — attempt to gently rock the connector out.
With the screen loose I could remove it from the lid. I then set about removing unwanted components from the motherboard. I actually left the keyboard connected until the very end, thinking that I’d need it for the laptop to start properly. It wasn’t needed, so it was removed later on. I found the connecting cable was very stiff but precision brute force saved the day.
The rest of the components were hidden under the casing, so I removed the rest of the screws (Marked “P”) and dispensed with it. A few more screws and the COM ports held the metal tray to the motherboard; these were also removed. I had to temporarily remove the cover of the CPU to get one of the screws holding in the heat sink.
At this point, I decided a test was in order to see if everything was working. The laptop booted cleanly to the default slide show (with no network card).
The next part was to remove the track pad print layout from its metal frame. It was glued on rather well, but a sharp knife separated the two nicely. This also includes the speakers, which I don’t really want, so they were removed.
I decided to give it one last test, but somehow I managed to forget to hook up the keyboard and mouse. To my surprise, it booted fine, so I removed the keyboard and mouse completely, saving precious space behind the frame. Now the only connected devices are the PCMCIA slots and the hard drive.
The power button is a push-to-make switch with a built-in LED that comes on when the laptop is switched on.
The power button needed extending so it could be built into the frame. At first I used one of the buttons from the print layout, but then I found a nice button with a built-in LED, so I hooked that up to the laptop’s power LED.
The frame required a bit of cutting to get the laptop to fit in — the biggest problem was the wireless PCMCIA card sticking out from the side of the motherboard, but a little cutting into the (thankfully soft) frame and it fitted. However the bit stuck out from the back of the frame about 8mm — a couple of lengths of wood sorted this out. I applied a couple of coats of wood stain to the spacers to match the frame colour.
To finish, the spacers were glued to the backboard (to which the motherboard is affixed) and the back was screwed to the frame. I’m quite happy with the finished result; Sometimes the laptop struggles to connect to the network drive, but that’s OK since it defaults back to showing some pictures on the hard drive anyway.
The Finished Product
The total cost of this project was around £30 for a wireless card and a frame, only slightly helped by what I had lying around the house. The laptop was free, but you can pick them up for very little money from eBay. You don’t even need a working hard drive, as many other projects have demonstrated — you can boot from a CD-ROM, which would also make the project a lot quieter.
This is a really fun project that got me doing a bit of coding, a bit of woodworking and a bit of taking things apart to see how they work — all were thoroughly enjoyable and I’d recommend giving one of these projects a go. It took me a day to fully set up the laptop and code the software, then an afternoon to take it apart and a couple of evenings to construct the frame. And, like everyone says, if you do attempt one of these, take pictures!
You can also view more pictures of the project.