Detecting dust using the Shinyei PPD42NS dust sensor


This simple little project was forced onto me by sudden attacks of unexplained sneezing. Dust was immediately suspected but apart from wiping a finger over any convenient surface and inspecting the suddenly black appendage, I had no quantitative way to judge just how much dust might be floating around our house.

The PPD42NS is not a very precise device, but it gives a good estimation of how dusty the surrounding air is. It tends to go wild if there is a draught or one fans the air in front of it, but it is better than no counting methods at all. Connecting the device is simplicity itself. The black and yellow leads are connected to 0V and analogue port 1 and the red suply lead is connected to 5V.

Velleman board with deut detector

Notice that I have connected this lead to an extra vero pin which I have soldered to the Velleman board in a convenient place. It connects to the USB 5V line. You can of course also connect it to either of the inner two pins just behind the analogue-in connectors. They are in front of the two potentiometers. The top one of these should be turned fully to the right for this project to work.

Notice the way the sensor is mounted. It must be mounted top side up. It relies on the heat of an internal resistor to float dust particles past the infra-red diode, hence the device will not function in any other position.

I have included  a program which displays current dust levels in real time and gives a quantitative output of long term dust levels for comparison. This output is purely numeric and only a rough indication.

The program has three modes. Mode 1 is a single half hour run measuring the dust level. Mode 2 is a continuous run updating itself every half hour whilst displaying the dust level of the past runs.

Mode 3 is just like mode 2 but it writes those half hourly levels to the hard drive. For this to work there must be a folder called 'DustyRooms' in the same folder as the program - it doesn't matter where on the hard drive this is. The resultant file can be examined in any text editor.


Below is a link to the executable file of the program. I use Windows 7 but the file should work with all recent versions of Windows.

For those lucky enough to own BBC BASIC for Windows, I've included the program file so that you change the program at will.

The BBC BASic file Dust_Counter.bbc

The executable file Dust_Counter.exe

If you have any questions or comments, please drop me an e-mail.
 
   10 REM Dust counter
   20
   30 REM Plot the level of dust in a room
   40 REM Needs Velleman interface board and Chinyet PPD 42 dust detector
   50 REM
   60 REM Jochen Lueg
   70 REM http://roevalley.com
   80 REM Limavady, February 2016
   90 REM Issue 1.2
  100
  110
  120
  130 PROCK8055_init
  140 ON ERROR PROCerror:STOP
  150 PROCinit
  160
  170 SYS K8055_CloseDevice%,0
  180 SYS K8055_OpenDevice%,0
  190 PROCselect
  200 OFF
  210
  220 IF Mode%=1 THEN
  230   PROCdraw_axis
  240   PROCstart_dusting
  250   MOVE 0,Y%-120
  260   PRINT " Total dust count:  ";total_dustcount%
  270  
  280   REPEAT UNTIL INKEY(-99)= FALSE
  290   REPEAT UNTIL INKEY(-113)
  300 ENDIF
  310
  320 IF Mode%=2 THEN
  330   DIM Totals%(50)
  340   total_x%=10
  350   totals_count%=1
  360  
  370   REPEAT
  380     PROCdraw_axis
  390     IF totals_count%>1 THEN
  400       MOVE 0,Y%-120
  410       PRINT " Total dust count:  "
  420      
  430       FOR J%=1 TO totals_count%-1
  440         MOVE X%+J%*80,Y%-120
  450         PRINT;Totals%(J%)
  460       NEXT
  470     ENDIF
  480     PROCstart_dusting
  490     Totals%(totals_count%)=total_dustcount%
  500     totals_count%+=1
  510     CLG
  520   UNTIL FALSE
  530 ENDIF
  540
  550 IF Mode%=3 THEN
  560   CLS
  570   PROCwhats_there
  580   DIM Totals%(50)
  590   total_x%=10
  600   totals_count%=1
  610  
  640   REPEAT
  650     PROCdraw_axis
  660     IF totals_count%>1 THEN
  670       MOVE 0,Y%-120
  680       PRINT " Total dust count:  "
  690      
  700       FOR J%=1 TO totals_count%-1
  710         MOVE X%+J%*80,Y%-120
  720         PRINT;Totals%(J%)
  730       NEXT
  740     ENDIF
  750    
  760     FileHandle%=OPENUP FileName$
  770     PTR#FileHandle%=EXT#FileHandle%
  780     PRINT#FileHandle%,TIME$
  790     PROCstart_dusting
  800     PRINT#FileHandle%,STR$(total_dustcount%)
  810     CLOSE#FileHandle%
  820     Totals%(totals_count%)=total_dustcount%
  830     totals_count%+=1
  840     CLG
  850   UNTIL FALSE
  860 ENDIF
  870
  880
  890 SYS K8055_CloseDevice%
  900 SYS "FreeLibrary",K8055_Board%
  910 *QUIT
  920 END
  930
  940
  950 DEFPROCdraw_axis
  960 VDU 5
  970 CLG
  980 ORIGIN 0,0
  990 VDU 23,23,2;0;0;0;
 1000 X%=300:Y%=700:Step%=400
 1010 GCOL 7
 1020 MOVE X%,Y%
 1030 DRAW X%,Y%+1300
 1040 MOVE X%,Y%
 1050 DRAW X%+2400,Y%
 1060 MOVE X%,Y%
 1070
 1080 FOR J%=5 TO 60 STEP 5
 1090   MOVE X%+(J%/5)*Step%,Y%
 1100   DRAW X%+(J%/5)*Step%,Y%-24
 1110   MOVE X%+(J%/5)*Step%/2,Y%
 1120   DRAW X%+(J%/5)*Step%/2,Y%-24
 1130 NEXT
 1140
 1150 FOR J%=5 TO 30  STEP 5
 1160   MOVE X%+(J%/5)*Step%-16,Y%-32
 1170   IF J%= 5   MOVE X%+(J%/5)*Step%-8,Y%-32
 1180   PRINT; J%
 1190 NEXT
 1200
 1210 MOVE X%,Y%
 1220 FOR J%= 1 TO 10
 1230   MOVE X%,Y%+J%*130
 1240   DRAW X%-24,Y%+J%*130
 1250   MOVE X%-62,Y%+18+J%*130
 1260   PRINT;J%
 1270 NEXT
 1280 MOVE X%,Y%
 1290
 1300 ENDPROC
 1310
 1320
 1330 DEFPROCstart_dusting
 1340 LOCAL time_so_far%,R%,dust_count%,step_min%,scale%
 1350 pulse_length%=0
 1360 dust_count%=1
 1370 step_min%=7
 1380 REMstep_min%=400
 1390 scale%=10
 1400 total_dustcount%=0
 1410 pulse_Y%=0
 1420 time_so_far%=0
 1430 total_time%=500
 1440 GCOL 3
 1450 REPEAT
 1460   TIME=0
 1470   REPEAT
 1480     REM Look for signal going low
 1490     REPEAT
 1500       SYS K8055_ReadAnalogChannel%,1 TO R%
 1510     UNTIL R%<=120 OR TIME >= total_time%
 1520     time_so_far% = TIME
 1530    
 1540     REM Look for signal going high
 1550     IF TIME < total_time% THEN
 1560       TIME=0
 1570       REPEAT
 1580         SYS K8055_ReadAnalogChannel%,1 TO R%
 1590       UNTIL R%>120 OR (time_so_far%+TIME) >=total_time%
 1600       pulse_length%=pulse_length%+TIME
 1610       total_dustcount%+=1
 1620     ENDIF
 1630    
 1640     TIME=time_so_far%+pulse_length%
 1650    
 1660   UNTIL TIME>=total_time%
 1670  
 1680   pulse_Y%=pulse_length%*scale%
 1690   IF pulse_Y%>1500 pulse_Y%=1500
 1700  
 1710   MOVE X%+dust_count%*step_min%,Y%
 1720   DRAW  X%+dust_count%*step_min%,Y%+pulse_Y%
 1730   GCOL 5
 1740   MOVE X%+dust_count%*step_min%,Y%-100
 1750   DRAW X%+dust_count%*step_min%,Y%-80
 1760   GCOL 3
 1770   pulse_length%=0
 1780   pulse_Y%=0
 1790   time_so_far%=0
 1800   dust_count%+=1
 1810   TIME=0
 1820 UNTIL dust_count%*step_min%>=2400 OR INKEY(-99)
 1830 ENDPROC
 1840
 1850
 1860 DEFPROCselect
 1870 PRINTTAB(5,10)"Pleae select one of the following modes."
 1880 PRINTTAB(5,12)"1 ....................  Single run"
 1890 PRINTTAB(5,13)"2 ....................  Continuous run"
 1900 PRINTTAB(5,14)"3 ....................  Recorded run"
 1910 PRINTTAB(5,25)"This program will recorded the dust in its environment."
 1920 PRINTTAB(5,26)"Press the Spacebar for up to 5 seconds to interrupt the current aquisition"
 1930 PRINTTAB(5,27)"and then press Escape"
 1940
 1950 REPEAT
 1960   PRINTTAB(5,16)"Please enter 1, 2 or 3 and press Return: ";:INPUT Mode%
 1970 UNTIL Mode%>0 AND Mode%<4
 1980 ENDPROC
 1990
 2000
 2010 DEFPROCwhats_there
 2020 LOCAL dir%,sh%,res%,F$,File$
 2030 f$=@dir$+"\DustyRooms"
 2040 OSCLI "CD """+f$+""""
 2050
 2060 PRINTTAB(10,20)"The following files are allready on the disc: "
 2070 PRINT
 2080 PROCfind_files
 2090 ON
 2100 INPUT TAB(10) "Please enter a file name: "File$
 2120 FileName$=f$+"\"+File$
 2130 file_handle%=OPENOUT FileName$
 2140 CLOSE#file_handle%
 2150 OFF
 2160 CLS
 2170 ENDPROC
 2180
 2190
 2200 DEFPROCfind_files
 2210 DIM dir% LOCAL 317
 2220 SYS"FindFirstFile","*dat",dir%TOsh%
 2230 IF sh%<>-1 THEN
 2240   REPEAT
 2250     PRINT $$(dir%+44)
 2260     SYS "FindNextFile",sh%,dir% TO res%
 2270   UNTIL res%=0
 2280   SYS "FindClose",sh%
 2290 ENDIF
 2300 ENDPROC
 2310
 2320
 2330 DEFPROCinit
 2340 *FONT Lucida Console,14,2
 2350 MODE 28
 2360 CLS
 2370 DIM Totals%(50)
 2380 totals_count%=1
 2390 ENDPROC
 2400
 2410
 2420 DEFPROCwindow(WindowWidth%,WindowHeight%)
 2430 MODE 30
 2440 SYS "SetWindowPos",@hwnd%,0,0,0,WindowWidth%,WindowHeight%,6
 2450 SYS "SetWindowText","The chip and the pendulum"
 2460 VDU 26
 2470 CLS
 2480 ENDPROC
 2490
 2500
 2510 DEFPROCerror
 2520 PRINT REPORT$;" at line ";ERL
 2530 SYS K8055_ClearAllDigital%
 2540 SYS K8055_ClearAllAnalog%
 2550 SYS K8055_CloseDevice%
 2560 SYS "FreeLibrary",K8055_Board%
 2570 END
 2580 ENDPROC
 2590
 2600
 2610 DEFPROCK8055_init
 2620 SYS"LoadLibrary","K8055D.dll" TO K8055_Board%
 2630 SYS"GetProcAddress",K8055_Board%,"OpenDevice" TO K8055_OpenDevice%
 2640 SYS"GetProcAddress",K8055_Board%,"CloseDevice" TO K8055_CloseDevice%
 2650 SYS"GetProcAddress",K8055_Board%,"SearchDevices" TO K8055_SearchDevices%
 2660 SYS"GetProcAddress",K8055_Board%,"SetCurentDevice" TO K8055_SetCurrentDevice%
 2670 SYS"GetProcAddress",K8055_Board%,"Version" TO K8055_Version%
 2680 SYS"GetProcAddress",K8055_Board%,"ReadAnalogChannel" TO K8055_ReadAnalogChannel%
 2690 SYS"GetProcAddress",K8055_Board%,"ReadAllAnalog" TO K8055_ReadAllAnalog%
 2700 SYS"GetProcAddress",K8055_Board%,"OutputAnalogChannel" TO K8055_OutputAnalogChannel%
 2710 SYS"GetProcAddress",K8055_Board%,"OutputAllAnalog" TO K8055_OutputAllAnalog%
 2720 SYS"GetProcAddress",K8055_Board%,"ClearAnalogChannel" TO K8055_ClearAnalogChannel%
 2730 SYS"GetProcAddress",K8055_Board%,"ClearAllAnalog" TO K8055_ClearAllAnalog%
 2740 SYS"GetProcAddress",K8055_Board%,"SetAnalogChannel" TO K8055_SetAnalogChannel%
 2750 SYS"GetProcAddress",K8055_Board%,"SetAllAnalog"  TO K8055_SetAllAnalog%
 2760 SYS"GetProcAddress",K8055_Board%,"WriteAllDigital" TO K8055_WriteAllDigital%
 2770 SYS"GetProcAddress",K8055_Board%,"ClearDigitalChannel" TO K8055_ClearDigitalChannel%
 2780 SYS"GetProcAddress",K8055_Board%,"ClearAllDigital" TO K8055_ClearAllDigital%
 2790 SYS"GetProcAddress",K8055_Board%,"SetDigitalChannel" TO K8055_SetDigitalChannel%
 2800 SYS"GetProcAddress",K8055_Board%,"SetAllDigital"  TO K8055_SetAllDigital%
 2810 SYS"GetProcAddress",K8055_Board%,"ReadDigitalChannel" TO K8055_ReadDigitalChannel%
 2820 SYS"GetProcAddress",K8055_Board%,"ReadAllDigital"  TO K8055_ReadAllDigital%
 2830 SYS"GetProcAddress",K8055_Board%,"ResetCounter"  TO K8055_ResetCounter%
 2840 SYS"GetProcAddress",K8055_Board%,"ReadCounter"  TO K8055_ReadCounter%
 2850 SYS"GetProcAddress",K8055_Board%,"SedtCounterDebouceTime"  TO K8055_SetCounterDebounceTime%
 2860 SYS"GetProcAddress",K8055_Board%,"ReadBackDigitalOut" TO K8055_ReadBackDigitalOut%
 2870 SYS"GetProcAddress",K8055_Board%,"ReadBackAnalogOut" TO K8055_ReadBackAnalogOut%
 2880
 2890
 2900 ENDPROC

 
 


Return to interfacing index


Back to the start
Tudor with sign