;V-1 ;Compile with Crownhill Proton+ PIC Basic compiler. ;'Caller Line Identification' displays on a (2*16) LCD the phone number of the person who is ; ringing you, before you even answer, this number stays on the LCD till it wil replaced by ; a new received phone number OR if the button is pushed. ;With the button you can 'leaf' trough the phone numbers. ;The second line displays how many phone numbers are stored in memory. ;The LED burns when new number(s) are received and goes off when all the new numbers has ; been watched. ;This LED blinks fast for a while when a received number is already stored in memory. ;A (a)new received phone number is marked in the display with *N*, it disappears when it's ; number has been seen. ;Max.12 10-numerals telephone numbers can stored in PIC's EEPROM. ;When the memory is full, a message is displayd and the backlight and LED blinks slow. ;By a phone call or a push on the button the backlight from the LCD burns for about 30 ; seconds. ;When the button is pressed longer then 2 seconds, all the stored phone numbers are erased, ; however, when a (new) received number is not seen yet, erasing is not possible. ;All this works ofcourse only when your phone company sends these numbers to you. ;Direct after programming the PIC stays in TEST-mode. ;You can recognize it on the word TEST on the right on the second line in the LCD. ;In TEST-mode works the CLI normal, but you can also test it: ;Push on a parallel connected telephone first on the '*' button (this simulates a new ; receiving), then give within a second (fast) a number and close with telephone button '#'. ;When you don't close with '#', the PIC wil do it after a second (Time-out). ;You can enable TEST-mode also when you push the button while you switch the power on. ;When you shut the power off for a few seconds and switch it on again, the TEST-mode is ; disabled (The telephone buttons '*' and '#' doesn't work anymore). ;The PIC itself doesn't need a crystal, it runs on the intern oscillator. ;Time + date is an extension for the future (Ports A.6 and A.7 are reserved for this). ;This is a lowcost project, when a HD44780 2x16 LC-Display is already there. ;EEPROM address 0 keeps the address-counter from the EEPROM self. ;EEPROM address 1 keeps the counter from the number of phone numbers + a new phone number. ;EEPROM address 2 keeps in mind of the 'new numbers' LED was ON (in case of power-failure). ;EEPROM address 3 and further keeps the phone numbers, every LOW-NIB of a BYTE has 1 digit ; from a phone number. ;The HIGH-NIB from the BYTE from every first number from a phone number keeps the ; follow-number from that phone number. ;The HIGH-NIB from every 2nd number keeps in mind of that number has been seen or not. ;A received phone number starts with a 'D' (0000=0), this prevents displaying the phone- ; numbers when call with own modem or home telephone connected parallel on the same line. ;A received phone number is complete when the last number is a 'C' (1111=15) (standard) or ; when a time-out is happend. ; Components: +5V feeding (or a 7805 with adapter) ; PIC16F628A ; IC MT8870 or CM8870 + crystal 3.579545MHz ; LED + pulse-Button ; HD44780 LC-Display 2x16 (if possible with backlight) ; NPN transistor (i.e. BC547) ; VDR S07K130 (130V) ; Resistors 1k + 10k + 33k + 39k + 68k + 3x100k + 270k ; Capacitors 2x10n + 3x100n + 100uF(electrolytic) ;Er is ook een Nederlandse versie / There is a Dutch version too ;www.picbasic.nl / Frits Kieftenbelt, Raalte, Netherlands (Frizie) DEVICE 16F628A CONFIG INTRC_OSC_NOCLKOUT, WDT_OFF, PWRTE_ON, BODEN_OFF, LVP_OFF, CP_OFF, MCLRE_OFF ;PORTA.5 as INPUT (MCLRE_OFF) XTAL 4 ALL_DIGITAL TRUE ;Normal aliases (constants) SYMBOL BckLghtTijd = 30000 ;mSec: (30.000 = 30 sec) Max.65500! SYMBOL Debounce = 10 ;mSec: Looptime against contact-bounce SYMBOL EraseTime = 200 ;0,01 sec: (200 = 2 sec) Time to push the button for erase telephonenumbers SYMBOL MaxAddress = 3 + 120 ;EEPROM memory, every byte is 1 digit from a phonenumber, increase not possible because of maximum EEPROM memory SYMBOL MinAddress = 3 ;From MinAddress and higher are phonenumbers (0=EE_Address, 1=Teller, 2=ScreenAddress) SYMBOL RAM_Max = 15 ;Max. digits from a phonenumber that can store in RAM SYMBOL RAM_Min = 2 ;Min. digits to make a phonenumber legal SYMBOL TimeOutTime = 15000 ;0,1 mSec: (15.000 = 1,5 sec) Max.65500! SYMBOL WatchTime = 15000 ;mSec: Time for watching ;Logic aliases (constants) SYMBOL AAN = 0 ;Reversed ON SYMBOL FALSE = 0 SYMBOL OFF = 0 SYMBOL ON = 1 SYMBOL TRUE = 1 SYMBOL UIT = 1 ;Reversed OFF 'Port aliases 'By changing change also TRIS SYMBOL Q1 = PORTA.0 ;Outputs from MT8870 SYMBOL Q2 = PORTA.1 SYMBOL Q3 = PORTA.2 SYMBOL Q4 = PORTA.3 SYMBOL STD = PORTA.4 ;High when Q1...Q4 have a valid value in his register SYMBOL PushButton = PORTA.5 ;Short push = Step through phonenumbers, long push = Erase all phone numbers (After confirm) SYMBOL Reserve1 = PORTA.6 ;For clock chip SYMBOL Reserve2 = PORTA.7 ;For clock chip SYMBOL LED = PORTB.0 ;Burns when there are new (unseen) phonenumbers in memory SYMBOL BackLight = PORTB.1 ;LCD backlight, amplifie with a NPN transistor (BC547) ;YMBOL PORTB.2 til PORTB.7 ;Reserved for LCD ; 76543210 TRISA = %11111111 TRISB = %11111100 PORTB_PULLUPS OFF ;Arrays DIM Number[RAM_Max + 2]AS BYTE ;Keeps the received phonenumber before it's gonna write (after examination) to EEPROM ;Word DIM BackLightTimer AS WORD ;Counts to 0, after then the backlight from LCD is going off DIM TimeOut AS WORD ;Used for time-out function DIM WD1 AS WORD ;Word Dummy 1 ;Byte DIM Digit AS BYTE ;Keeps 1 digit from a phonenumber from or for the EEPROM DIM DTMF AS BYTE ;Keeps digit by digit what is reived from a phoneline DIM EE_Address AS BYTE ;Keeps the actual EEPROM address, where the latest numbers are written DIM EE_Teller AS BYTE ;Keeps the EEPROM Address by exist-number examination, so that the original EE_Address stays intact DIM IndexNumber AS BYTE ;Keeps the indexNumber from array Number DIM ScreenAddress AS BYTE ;Walks through memory by watching phonenumbers, so that EE_Address stays intact DIM Teller AS BYTE ;Keeps the number of phonenumbers, when Teller is i.e. 5 then there are 5 - 1 = 4 Numbers stock in memory (The 1 is for the next new number) DIM TelNr AS BYTE ;Keeps the examinate follownumber from a complete phonenumber DIM BD1 AS BYTE ;Byte Dummy 1 DIM BD2 AS BYTE ;Byte Dummy 2 ;Bit DIM ExistNumber AS BIT ;TRUE when a received phonenumber already in memory exist DIM TestMode AS BIT ;TRUE when this program is in TEST-mode DIM ValidNumber AS BIT ;TRUE when receiving a legal number PORTA = 0 PORTB = 0 ;All off CLEAR DELAYMS 500 ;LCD stabilisation ;Start adjustments BackLight = ON ;LCD backlight on IF PushButton = AAN THEN TestMode = TRUE ;When by power-on the PushButton is pushed, then TEST-mode = TRUE CLS :PRINT "TESTMODE,you can" PRINT AT 2, 1, "release button" WHILE PushButton = AAN: WEND ;Wait till PushButton is released (Else it would be going direct to 'Erasing') ENDIF CLS :PRINT "Caller ID UK V-1" ;Program name and version PRINT AT 2, 1, "www.picbasic.nl" DELAYMS 2000 ;Time for message on LCD EDATA MinAddress, 0, 0, 16 ;EE_Address, Teller to 0(!), LED off, HIGH-NIB to 1 (=16) (= First phonenumber) IF EREAD 1 = 0 THEN TestMode = TRUE ;After programming the PIC direct in TEST-mode GOTO EraseNumbers ;After PIC programming the counter stays the first time on 0 (by EDATA) only THEN to Erasing, after then never erase automatic again because EEPROM can phonenumbers stored (after power failure) ENDIF EE_Address = EREAD 0 ;The EE_Address itself stays in EEPROM Address 0 Teller = EREAD 1 ;Number of phonenumbers-Teller stays in EEPROM Address 1 ScreenAddress = MinAddress ;The ScreenAddress starts at MinAddress IF EREAD 2 > 0 THEN LED = ON ;If the LED was on (before power-failure), then set it on again IF Teller < 2 THEN ;If Teller = 0 or 1 (1 means NO numbers) GOSUB NoNumbers ELSE GOSUB Screen ENDIF GOTO Scan ;SUBROUTINES (In alphabetic order) NoNumbers: CLS :PRINT "No numbers" GOSUB Test ;Print "TEST" on the LCD when TestMode = TRUE RETURN Screen: CLS :PRINT "Present: ", DEC Teller - 1, " nr" IF Teller > 2 THEN PRINT "'s" IF LED = OFF THEN PRINT AT 2, 1,"No new numbers" RETURN Test: IF TestMode = TRUE THEN PRINT AT 2,13, "TEST" RETURN ;MAINPROGRAM (Labels in alphabetic order) MemoryFull: IndexNumber = 0 ;Release RAM so that a new phonenumber can print on LCD (but not store in EEPROM because memory full) PRINT AT 2, 1, "Memory full" WHILE STD = ON: WEND ;Wait until STD is low because else it would direct jump out the WHILE loop to NumberReceived again WHILE PushButton = UIT ;While there's no push on the PushButton... INC BD1 DELAYMS 4 ;...about every second (4ms * 250)... IF BD1 > 250 THEN TOGGLE BackLight ;...the backlight... LED = BackLight ;...and the LED blinks (in case the LCD has no backlight) BD1 = 0 ENDIF IF STD = ON THEN NumberReceived ;When the memory is full the last received phonenumber can still printed on the LCD WEND LED = ON GOTO Scan NumberComplete: IF IndexNumber < RAM_Min THEN Scan ;A phonenumber must have a minimum number of digits LED = ON ;'New received numbers' LED on EWRITE 2, [1] ;Tell EEPROM that LED is on (in case of power-failure) ;Examining of the phonenumber is already stored in memory (Does the number already exist) EE_Teller = MinAddress ;Start value to EE_Teller TelNr = 0 ;The phonenumber counter against examination WHILE TelNr < (Teller - 1) ;Teller keeps the number of the stored phonenumbers incl. a new phonenumber ExistNumber = TRUE ;First think the number does exist, underneath loop has to prove that it (maybe) isn't INC TelNr ;Next phonenumber BD1 = 0 ;The digit from a phonenumber against examining a phonenumber REPEAT INC BD1 ;Next digit from the phonenumber IF Number[BD1] <> (15 & EREAD EE_Teller) THEN ExistNumber = FALSE;If the digit is unequal to that from the one in the EEPROM, then the phonenumber is thus not the same INC EE_Teller UNTIL (240 & EREAD EE_Teller) > 0 AND BD1 > RAM_Min;Compare till the next phonenumber, but not the first two (depence on RAM_Min), because there's stock the follownumber and the *N* (new) sign IF IndexNumber <> BD1 THEN ExistNumber = FALSE;The new phonenumber is shorter or longer then the one in EEPROM, thus unequal IF ExistNumber = TRUE THEN BREAK ;Still TRUE? Then don't look further, the phonenumber is already in EEPROM memory WEND IF ExistNumber = FALSE THEN ;IF received phonenumber not exist then write the phonenumber in EEPROM EWRITE EE_Address + 1, [240];HIGH-NIB from the 2nd digit high (=240) (='unseen number' sign (*N*)) BD1 = 1 REPEAT BD2 = (240 & EREAD EE_Address) | (15 & Number[BD1]);Add the LOW-NIB to the HIGH-NIB in dummy BD2... EWRITE EE_Address, [BD2];...and write it then to EEPROM INC BD1 IF EE_Address < MaxAddress THEN INC EE_Address ELSE GOTO MemoryFull ENDIF UNTIL BD1 > IndexNumber INC Teller ;Increase Teller for a next phonenumber and... EWRITE EE_Address, [Teller * 16];...put Teller number to the first address for that next phonenumber in HIGH-NIB EWRITE 0, [EE_Address, Teller, 1];Write the new values from EE_Address itself, Teller and LED = 1 also in EEPROM ELSE ;ELSE EE_Teller = MinAddress ;Adjust the EE_Teller to the address where to start searching REPEAT ;Search which EEPROM address the exist number stays, to put the *N* sign BD1 = EREAD EE_Teller INC EE_Teller UNTIL BD1 >> 4 = TelNr ;Shift the HIGH-NIB to LOW-NIB so that this one can compare with TelNr BD1 = 240 | EREAD EE_Teller ;HIGH-NIB from the 2nd digit high (=240) (='unseen number' sign (*N*)) EWRITE EE_Teller, [BD1] ;...and write it then to EEPROM BD1 = 1 REPEAT TOGGLE LED ;Blink LED for a while, phonenumber exist DELAYMS 100 INC BD1 UNTIL BD1 > 40 ;Attention: even number only ENDIF WHILE STD = ON: WEND ;Wait till STD = low because else it would via 'Scan' direct jump to 'NumberReceived' again (in a loop) DELAYMS 50 ;Wait that STD is really 100% low ExistNumber = FALSE ;Prevents that if after an ExistNumber = TRUE, erased, the program get stuck GOTO Scan NumberReceived: DELAYMS 1 ;Wait till Q1...Q4 is really 100% on the inputs from the PIC ;A complete number starts with a 'D', then the phonenumber and finally ends with a 'C' (=Complete) DTMF = Q1 + (2 * Q2) + (4 * Q3) + (8 * Q4) ;0...15 = 0 till 9, *, #, A, B, C and D ;IF (DTMF = 11 (= '*') AND TestMode = TRUE) OR DTMF = 0 (= 'D') THEN... IF DTMF = 11 AND TestMode = TRUE OR DTMF = 0 THEN ;=D If it's a valid phonenumber then... (An INvalid phonenumber is i.e. the DTMF tones when call from your own home telephone (ignore it) CLS: PRINT "Nr:" ;Erase LCD, so that the new phonenumber can be displayd GOSUB Test ;Print "TEST" on the LCD when TestMode = TRUE CURSOR 1, 4 ;Put the cursor after "Nr:" again BackLight = ON ;LCD backlight on ValidNumber = TRUE ;A valid phonenumber is received ScreenAddress= MinAddress ;Start with first phonenumber by a next leaf through the phonenumbers ENDIF IF ValidNumber = TRUE THEN ;If receiving a valid phonenumber then... SELECT CASE DTMF CASE 0, 11 ;= D, *, (DON'T ERASE CASE 0 BECAUSE ELSE IT WOULD LEAVE THE SELECT CASE BY CASE ELSE!) CASE 1 TO 10 ;= 0...9 Digit, namely... IF DTMF = 10 THEN DTMF = 0;...a '10' from the MT8870 means digit '0' PRINT DEC DTMF ;Put the receiving number digit by digit on LCD INC IndexNumber ;Increase the index from array Number for next digit Number[IndexNumber] = DTMF;Place the digit from the phonenumber in RAM IF IndexNumber >= RAM_Max THEN NumberComplete;Safety, happens normaly never, would mean that phonenumber is longer then RAM_Max ;CASE 13 TO 14 ;13=A 14=B CASE 12 IF TestMode = TRUE THEN NumberComplete ;= #, works only in TEST-mode CASE 15 ;= C, the phonenumber is complete GOTO NumberComplete CASE ELSE GOTO Scan ;Else it would come in a loop ENDSELECT ELSE GOTO Scan ENDIF TimeOut = TimeOutTime WHILE STD = ON AND TimeOut > 0;Wait till STD = low or for a time-out DELAYUS 100 DEC TimeOut WEND TimeOut = TimeOutTime WHILE STD = OFF ;Wait till STD = high, a new digit is received DELAYUS 100 DEC TimeOut IF TimeOut = 0 THEN NumberComplete;Na TimeOutTime wordt het Number als compleet aangenomen (= Received van een D (WD1 = 15)) WEND GOTO NumberReceived ;Get next digit from a phonenumber Scan: BackLightTimer = BckLghtTijd ;Set backlight time ValidNumber = FALSE ;Reset IndexNumber = 0 ;Reset IndexNumber from array Number BD1 = 0 REPEAT Number[BD1] = 0 ;RAM erasing INC BD1 UNTIL BD1 > RAM_Max WHILE 1 = 1 ;Normaly runs the program in this loop, waiting for PushButton or NumberReceived IF PushButton = AAN THEN PushButtonRoutine IF STD = ON THEN NumberReceived IF BackLightTimer = 0 THEN ;If time is running out then... BackLight = OFF ;...backlight off ELSE DELAYMS 1 ;Little delay for the backlight time DEC BackLightTimer ;DECrease backlight timer ENDIF WEND PushButtonRoutine: BackLight = ON ;LCD backlight on BackLightTimer = BckLghtTijd;Set time for the backlight from LCD IF EE_Address > MinAddress THEN ;Run only when there are one or more phonenumbers WD1 = 0 REPEAT INC WD1 DELAYMS 10 IF LED = ON THEN WD1 = 0;If there are unseen phonenumbers, erasing isn't possible (Timer value WD1 can't go higher then 1) IF PushButton = UIT THEN BREAK ;If PushButton isn't pushed then jump out the FOR-NEXT UNTIL WD1 = EraseTime IF WD1 >= EraseTime THEN Erasing;If the whole WD1 counter has count then (FOR-EraseTime-NEXT) memory erasing (the PushButton is pushed a long time) Digit = EREAD ScreenAddress CLS: PRINT AT 2, 1, "Nr:", DEC (Digit & 240) / 16, "/", DEC Teller - 1 ;, "-->", DEC EE_Address 'Print HIGH-NIB en Teller-1 on LCD BD1 = ScreenAddress + 1 IF EREAD BD1 > 15 THEN PRINT " *N*" ;(a)new number received, so print *N* on the LCD BD2 = 15 & EREAD BD1 ;EREAD can't direct in EWRITE (EWRITE BD1, [15 & EREAD BD1]) EWRITE BD1, [BD2] ENDIF GOSUB Test ;Print "TEST" on the LCD when TestMode = TRUE CURSOR 1, 1 ;Put the cursor already to the right position REPEAT PRINT DEC Digit & 15 ;Print only the LOW-NIB on LCD (HIGH-NIB keeps the follownumber from the phonenumbers list) INC ScreenAddress ;Print next digit from phonenumber... Digit = EREAD ScreenAddress UNTIL Digit > 15 ;...till an other phonenumber starts IF (Digit & 240) / 16 = Teller THEN ScreenAddress = MinAddress;If the last phonenumber in the list is shown, then next phonenumber is the first one again (Show the phonenumbers in a loop) IF LED = ON THEN ;If the 'new numbers' LED is on the examine of there are more new phonenumbers, if it's not then LED off EE_Teller = MinAddress ;Starts examine with the first number REPEAT IF 240 & EREAD EE_Teller = 240 THEN BREAK ;There's another new number: so don't examine further INC EE_Teller UNTIL EE_Teller = MaxAddress ;Examine till the last address IF EE_Teller = MaxAddress THEN LED = OFF;If the whole loop is examined, it means there are nomore new numbers, so LED off EWRITE 2, [0] ;Tell EEPROM that LED = off (in case of power-failure) ENDIF WHILE PushButton = AAN: WEND ;Wait till the PushButton is released DELAYMS Debounce WD1 = 0 REPEAT ;If within WatchTime no push on the PushButton is happened, it falls back in the 'Scan' loop INC WD1 DELAYMS 1 ;Delay for aswel WD1 as the BackLightTimer IF BackLightTimer = 0 THEN BackLight = OFF ELSE DEC BackLightTimer ENDIF IF LED = ON THEN WD1 = 0;Stay in this loop forever if not all the new phonenumbers has been seen IF PushButton = AAN THEN PushButtonRoutine ;Jump to this routine, display the next phonenumber IF STD = ON THEN NumberReceived ;When watching phonenumbers and there's a phonecall on the same time, then jump to the NumberReceived routine UNTIL WD1 = WatchTime IF LED = OFF THEN GOSUB Screen;If all the phonenumbers has been seen then... ScreenAddress = MinAddress;For the next time starts from the beginning ENDIF GOTO Scan Erasing: CLS :PRINT "Erase numbers?" PRINT AT 2, 1, "BUTTON = Confirm" GOSUB Test ;Print "TEST" on the LCD when TestMode = TRUE WHILE PushButton = AAN :WEND ;PushButton which is pressed to come in mode "Erasing", must released first DELAYMS Debounce ;PushButton must been released 100%, else it would jump to Erasing again BD1 = 200 ;200 x 20ms = 4 seconds to confirm erasing WHILE PushButton = UIT AND BD1 > 0 ;While PushButton isn't pressed AND time BD1 is still running: WEND DELAYMS 20 DEC BD1 WEND IF BD1 = 0 THEN ;Erasing canceled, no push on the PushButton within the time GOSUB Screen ;Make LCD actual (Erase the 'Erase numbers?' question) GOTO Scan ;Scan PushButton and STD ENDIF EraseNumbers: ;Really erasing CLS: PRINT "Numbers are" PRINT AT 2, 1, "erased!" GOSUB Test ;Print "TEST" on the LCD when TestMode = TRUE EWRITE MinAddress, [16] ;Set phonenumber follownumber to 1 (HIGH-NIB (EEPROM Address 3) to 1 zetten (=16) (= First phonenumber) EE_Address = MinAddress + 1 REPEAT EWRITE EE_Address, [0] ;Erase the other EEPROM memory INC EE_Address UNTIL EE_Address > MaxAddress BD1 = 1 REPEAT ;Blink LED 5x: Sign the user: phonenumbers are erased TOGGLE LED DELAYMS 200 INC BD1 UNTIL BD1 > 10 ;Even number only! GOSUB NoNumbers ;Print "No numbers" on the LCD EE_Address = MinAddress ;The EE_Address pointer to the first phonenumber address Teller = 1 ;Teller to 1 ScreenAddress = MinAddress ;Make ScreenAddress equal to constant MinAddress LED = OFF ;New numbers' LED off EWRITE 0, [EE_Address, Teller, 0];Tell EEPROM: EE_Address, Teller, LED = 0 WHILE PushButton = AAN: WEND ;Wait till PushButton = released DELAYMS Debounce GOTO Scan