Contents

The laptop before I started attcking it.

The finished product.

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):

  1. Option Explicit
  2.  
  3. Private Declare Function GetLogicalDriveStrings Lib "kernel32" Alias _
  4. "GetLogicalDriveStringsA" (ByVal nBufferLength As Long, _
  5. ByVal lpBuffer As String) As Long
  6.  
  7. Const bDebug = False
  8.  
  9. Dim sIrfanPath As String
  10. Dim sDebugFlashDrive As String
  11. Dim sNoDebugFlashDrive As String
  12. Dim sFlashDrive As String
  13. Dim sHandlerPath As String
  14. Dim sTestImagePath As String
  15. Dim sFlashSlideshowPath As String
  16. Dim sDefaultImagesPath As String
  17.  
  18. Dim sSettingsDate As String
  19. Dim bDeleteFile As Boolean
  20. Dim bInDefault As Boolean
  21. Dim bUpdate As Boolean
  22. Dim sAction As String
  23. Dim sActionCommand As String
  24. Dim fTestImage As File
  25.  
  26. Private Sub Form_Click()
  27. End
  28. End Sub
  29.  
  30. Private Sub Form_Load()
  31. Dim lFileHandle As Long
  32.  
  33. lFileHandle = FreeFile()
  34. Open App.Path & "\settings.ini" For Input As lFileHandle
  35. Line Input #lFileHandle, sIrfanPath
  36. Line Input #lFileHandle, sDebugFlashDrive
  37. Line Input #lFileHandle, sNoDebugFlashDrive
  38. Line Input #lFileHandle, sHandlerPath
  39. Line Input #lFileHandle, sTestImagePath
  40. Line Input #lFileHandle, sFlashSlideshowPath
  41. Line Input #lFileHandle, sDefaultImagesPath
  42. Close #lFileHandle
  43.  
  44. sIrfanPath = Trim(sIrfanPath) & " "
  45. sHandlerPath = App.Path & "\" & sHandlerPath
  46. sTestImagePath = App.Path & "\" & sTestImagePath
  47. sFlashSlideshowPath = App.Path & "\" & sFlashSlideshowPath
  48. sDefaultImagesPath = App.Path & "\" & sDefaultImagesPath
  49.  
  50. sFlashDrive = IIf(bDebug, sDebugFlashDrive, sNoDebugFlashDrive)
  51. If bDebug Then
  52. Me.Visible = True
  53. Me.BackColor = vbWhite
  54. Me.Width = Screen.Width / 2
  55. Me.Height = Screen.Height / 2
  56. End If
  57.  
  58. Dim oFSO As Object, fFolder As Folder, fSubfolder As Folder, fFile As File
  59. Set oFSO = CreateObject("Scripting.FileSystemObject")
  60. Set fTestImage = oFSO.GetFile(sTestImagePath)
  61. bDeleteFile = False
  62. End Sub
  63.  
  64. Private Sub tmrPause_Timer()
  65. Dim sNewSettingsDate As String, sNewAction As String, sNewActionCommand As String
  66. Dim lFileHandle As Long, bManualChange As Boolean
  67. bManualChange = False
  68. bUpdate = True
  69.  
  70. If DriveExists(sFlashDrive) Then 'should show some flash drive stuff
  71. If sActionCommand <> sFlashSlideshowPath Then
  72. bManualChange = True
  73. bInDefault = False
  74. CreateFlashDriveSlideshow
  75. End If
  76. sNewAction = "flash"
  77. sNewActionCommand = sFlashSlideshowPath
  78.  
  79. ElseIf DriveExists("Z") Then 'should show networked stuff
  80. If FileExists(sHandlerPath) Then
  81.  
  82. sNewSettingsDate = FileDateTime(sHandlerPath)
  83.  
  84. If (bInDefault Or sNewSettingsDate <> sSettingsDate) _
  85. And sNewSettingsDate <> vbNullString _
  86. And FileLen(sHandlerPath) > 0 Then
  87. lFileHandle = FreeFile()
  88. bInDefault = False
  89.  
  90. Open sHandlerPath For Input As lFileHandle
  91. Line Input #lFileHandle, sNewAction
  92. Line Input #lFileHandle, sNewActionCommand
  93. Close #lFileHandle
  94. End If
  95.  
  96.  
  97. Else
  98. If sActionCommand <> sDefaultImagesPath Then
  99. bManualChange = True
  100. End If
  101. sNewAction = "default"
  102. sNewActionCommand = sDefaultImagesPath
  103. bInDefault = True
  104. End If
  105.  
  106. Else 'should do some default stuff
  107. If sActionCommand <> sDefaultImagesPath Then
  108. bManualChange = True
  109. End If
  110. sNewAction = "default"
  111. sNewActionCommand = sDefaultImagesPath
  112. bInDefault = True
  113. End If
  114.  
  115. If bDeleteFile Then DeleteFile sActionCommand
  116.  
  117. If sNewSettingsDate <> sSettingsDate Or bManualChange Then
  118. Select Case sNewAction
  119. Case "slideshow", "flash", "default"
  120. bDeleteFile = False
  121.  
  122. If bDebug Then
  123. Print "Changing to slideshow at " & sNewActionCommand
  124. Else
  125. Shell sIrfanPath & "/killmesoftly"
  126. Shell sIrfanPath & "/slideshow=" & sNewActionCommand
  127. End If
  128. Case "single"
  129. RestorePreviousHandler
  130.  
  131. bDeleteFile = True
  132. If FileLen(sNewActionCommand) > 0 Then
  133. If bDebug Then
  134. Print "Changing to uploaded image at " & sNewActionCommand
  135. Else
  136. Shell sIrfanPath & "/killmesoftly"
  137. Shell sIrfanPath & sNewActionCommand
  138. End If
  139. End If
  140. Case "delay"
  141. tmrPause.Interval = (CInt(sActionCommand)) * 1000
  142.  
  143. If bDebug Then Print "Changing timer interval to " & (CInt(sActionCommand)) * 1000
  144. Case "kill"
  145. RestorePreviousHandler
  146. Shell sIrfanPath & "/killmesoftly"
  147. End
  148.  
  149. Case "shutdown"
  150. RestorePreviousHandler
  151.  
  152. If bDebug Then
  153. Print "Shutting down"
  154. Else
  155. Shell sIrfanPath & "/killmesoftly"
  156. ShutdownSystem EWX_FORCESHUTDOWN
  157. End If
  158. End Select
  159. Else
  160. If bDebug Then Print "Do nothing"
  161. bUpdate = False
  162. End If
  163.  
  164. If bUpdate Then
  165. sAction = sNewAction
  166. sActionCommand = sNewActionCommand
  167. End If
  168. If FileExists(sHandlerPath) Then sSettingsDate = sNewSettingsDate
  169. End Sub
  170.  
  171. Sub DeleteFile(sPathName As String) 'deletes given file
  172. If FileExists(sPathName) Then
  173. Dim oFSO As Object
  174. Set oFSO = CreateObject("Scripting.FileSystemObject")
  175. oFSO.DeleteFile sPathName 'delete the file
  176. End If
  177. End Sub
  178.  
  179. Function DriveExists(ByVal sDrive As String) As Boolean
  180. Dim buffer As String
  181. buffer = Space(64)
  182. ' return False if invalid argument
  183. If Len(sDrive) = 0 Then Exit Function
  184. ' get the string that contains all drives
  185. GetLogicalDriveStrings Len(buffer), buffer
  186. ' check that the letter we’re looking for is there
  187. DriveExists = InStr(1, buffer, Left$(sDrive, 1), vbTextCompare)
  188. End Function
  189.  
  190. Function FileExists(sPathName As String) As Boolean ' checks if a file exists
  191. If Len(sPathName) > 0 Then ' checks filename entered
  192. FileExists = (Len(Dir(sPathName)) > 0) ' dir function returns directory if file exists
  193. Else: FileExists = False
  194. End If
  195. End Function
  196.  
  197. Sub CreateFlashDriveSlideshow()
  198. Dim lFileHandle As Long
  199. lFileHandle = FreeFile()
  200.  
  201. Open sFlashSlideshowPath For Output As lFileHandle
  202. ParseFolder sFlashDrive, lFileHandle
  203. Close #lFileHandle
  204. End Sub
  205.  
  206. Sub ParseFolder(sFolder As String, lFileHandle As Long)
  207. Dim oFSO As Object, fFolder As Folder, fSubfolder As Folder, fFile As File
  208. Set oFSO = CreateObject("Scripting.FileSystemObject")
  209. Set fFolder = oFSO.GetFolder(sFolder)
  210.  
  211. For Each fSubfolder In fFolder.SubFolders
  212. ParseFolder fSubfolder.Path, lFileHandle
  213. Next fSubfolder
  214.  
  215. For Each fFile In fFolder.Files
  216. If fFile.Type = fTestImage.Type Then
  217. Print #lFileHandle, fFile.Path
  218. End If
  219. Next fFile
  220. End Sub
  221.  
  222. Sub RestorePreviousHandler()
  223. Dim lFileHandle As Long
  224.  
  225. lFileHandle = FreeFile()
  226. Open sHandlerPath For Output As lFileHandle
  227. Print #lFileHandle, sAction
  228. Print #lFileHandle, sActionCommand
  229. Close #lFileHandle
  230. End Sub

Oops! I managed to leave debug mode enabled for this compile!

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

The covers for the screws are simply glued down and can be easily removed.

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.

Screws marked “K” hold down the keyboard.

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.

The laptop with the keyboard removed.

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.

The ZIF connector for the screen. Try to rock the cable connector from its socket.

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.

Screws marked “P” hold down the motherboard.

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.

The stripped-down laptop showing a single picture.

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 is a push-to-make switch with a built-in LED that comes on when the laptop is switched on.

The LED was wired to the PCB. Not easy!

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 marked out for cutting.

The spacers marked out for cutting.

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 inside of the finished picture frame.

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.

Leave a Comment

Comment details
Media Network authentication OpenID :: Media Network :: None


:'-( 8-) :-D :-x :-| :-( :-P :-) :-o ;-)