Contents
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.
The software
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!
The code
Here’s the code for the VB program (I’ve neglected to include the module that handles the forced shutdown of the laptop):
Option ExplicitPrivate Declare Function GetLogicalDriveStrings Lib "kernel32" Alias _"GetLogicalDriveStringsA" (ByVal nBufferLength As Long, _ByVal lpBuffer As String) As LongConst bDebug = FalseDim sIrfanPath As StringDim sDebugFlashDrive As StringDim sNoDebugFlashDrive As StringDim sFlashDrive As StringDim sHandlerPath As StringDim sTestImagePath As StringDim sFlashSlideshowPath As StringDim sDefaultImagesPath As StringDim sSettingsDate As StringDim bDeleteFile As BooleanDim bInDefault As BooleanDim bUpdate As BooleanDim sAction As StringDim sActionCommand As StringDim fTestImage As FilePrivate Sub Form_Click()EndEnd SubPrivate Sub Form_Load()Dim lFileHandle As LonglFileHandle = FreeFile()Open App.Path & "\settings.ini" For Input As lFileHandleLine Input #lFileHandle, sIrfanPathLine Input #lFileHandle, sDebugFlashDriveLine Input #lFileHandle, sNoDebugFlashDriveLine Input #lFileHandle, sHandlerPathLine Input #lFileHandle, sTestImagePathLine Input #lFileHandle, sFlashSlideshowPathLine Input #lFileHandle, sDefaultImagesPathClose #lFileHandlesIrfanPath = Trim(sIrfanPath) & " "sHandlerPath = App.Path & "\" & sHandlerPathsTestImagePath = App.Path & "\" & sTestImagePathsFlashSlideshowPath = App.Path & "\" & sFlashSlideshowPathsDefaultImagesPath = App.Path & "\" & sDefaultImagesPathsFlashDrive = IIf(bDebug, sDebugFlashDrive, sNoDebugFlashDrive)If bDebug ThenMe.Visible = TrueMe.BackColor = vbWhiteMe.Width = Screen.Width / 2Me.Height = Screen.Height / 2End IfDim oFSO As Object, fFolder As Folder, fSubfolder As Folder, fFile As FileSet oFSO = CreateObject("Scripting.FileSystemObject")Set fTestImage = oFSO.GetFile(sTestImagePath)bDeleteFile = FalseEnd SubPrivate Sub tmrPause_Timer()Dim sNewSettingsDate As String, sNewAction As String, sNewActionCommand As StringDim lFileHandle As Long, bManualChange As BooleanbManualChange = FalsebUpdate = TrueIf DriveExists(sFlashDrive) Then 'should show some flash drive stuffIf sActionCommand <> sFlashSlideshowPath ThenbManualChange = TruebInDefault = FalseCreateFlashDriveSlideshowEnd IfsNewAction = "flash"sNewActionCommand = sFlashSlideshowPathElseIf DriveExists("Z") Then 'should show networked stuffIf FileExists(sHandlerPath) ThensNewSettingsDate = FileDateTime(sHandlerPath)If (bInDefault Or sNewSettingsDate <> sSettingsDate) _And sNewSettingsDate <> vbNullString _And FileLen(sHandlerPath) > 0 ThenlFileHandle = FreeFile()bInDefault = FalseOpen sHandlerPath For Input As lFileHandleLine Input #lFileHandle, sNewActionLine Input #lFileHandle, sNewActionCommandClose #lFileHandleEnd IfElseIf sActionCommand <> sDefaultImagesPath ThenbManualChange = TrueEnd IfsNewAction = "default"sNewActionCommand = sDefaultImagesPathbInDefault = TrueEnd IfElse 'should do some default stuffIf sActionCommand <> sDefaultImagesPath ThenbManualChange = TrueEnd IfsNewAction = "default"sNewActionCommand = sDefaultImagesPathbInDefault = TrueEnd IfIf bDeleteFile Then DeleteFile sActionCommandIf sNewSettingsDate <> sSettingsDate Or bManualChange ThenSelect Case sNewActionCase "slideshow", "flash", "default"bDeleteFile = FalseIf bDebug ThenPrint "Changing to slideshow at " & sNewActionCommandElseShell sIrfanPath & "/killmesoftly"Shell sIrfanPath & "/slideshow=" & sNewActionCommandEnd IfCase "single"RestorePreviousHandlerbDeleteFile = TrueIf FileLen(sNewActionCommand) > 0 ThenIf bDebug ThenPrint "Changing to uploaded image at " & sNewActionCommandElseShell sIrfanPath & "/killmesoftly"Shell sIrfanPath & sNewActionCommandEnd IfEnd IfCase "delay"tmrPause.Interval = (CInt(sActionCommand)) * 1000If bDebug Then Print "Changing timer interval to " & (CInt(sActionCommand)) * 1000Case "kill"RestorePreviousHandlerShell sIrfanPath & "/killmesoftly"EndCase "shutdown"RestorePreviousHandlerIf bDebug ThenPrint "Shutting down"ElseShell sIrfanPath & "/killmesoftly"ShutdownSystem EWX_FORCESHUTDOWNEnd IfEnd SelectElseIf bDebug Then Print "Do nothing"bUpdate = FalseEnd IfIf bUpdate ThensAction = sNewActionsActionCommand = sNewActionCommandEnd IfIf FileExists(sHandlerPath) Then sSettingsDate = sNewSettingsDateEnd SubSub DeleteFile(sPathName As String) 'deletes given fileIf FileExists(sPathName) ThenDim oFSO As ObjectSet oFSO = CreateObject("Scripting.FileSystemObject")oFSO.DeleteFile sPathName 'delete the fileEnd IfEnd SubFunction DriveExists(ByVal sDrive As String) As BooleanDim buffer As Stringbuffer = Space(64)' return False if invalid argumentIf Len(sDrive) = 0 Then Exit Function' get the string that contains all drivesGetLogicalDriveStrings Len(buffer), buffer' check that the letter we’re looking for is thereDriveExists = InStr(1, buffer, Left$(sDrive, 1), vbTextCompare)End FunctionFunction FileExists(sPathName As String) As Boolean ' checks if a file existsIf Len(sPathName) > 0 Then ' checks filename enteredFileExists = (Len(Dir(sPathName)) > 0) ' dir function returns directory if file existsElse: FileExists = FalseEnd IfEnd FunctionSub CreateFlashDriveSlideshow()Dim lFileHandle As LonglFileHandle = FreeFile()Open sFlashSlideshowPath For Output As lFileHandleParseFolder sFlashDrive, lFileHandleClose #lFileHandleEnd SubSub ParseFolder(sFolder As String, lFileHandle As Long)Dim oFSO As Object, fFolder As Folder, fSubfolder As Folder, fFile As FileSet oFSO = CreateObject("Scripting.FileSystemObject")Set fFolder = oFSO.GetFolder(sFolder)For Each fSubfolder In fFolder.SubFoldersParseFolder fSubfolder.Path, lFileHandleNext fSubfolderFor Each fFile In fFolder.FilesIf fFile.Type = fTestImage.Type ThenPrint #lFileHandle, fFile.PathEnd IfNext fFileEnd SubSub RestorePreviousHandler()Dim lFileHandle As LonglFileHandle = FreeFile()Open sHandlerPath For Output As lFileHandlePrint #lFileHandle, sActionPrint #lFileHandle, sActionCommandClose #lFileHandleEnd 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!
The disassembly
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 reassembly
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 pictures of the project.