Page 1 of 1

Using the SerialPort commands example - CallerID

Posted: Thu May 26, 2011 9:45 pm
by TerryHough
I was playing with the SerialPort commands and decided to create an example program.
Ended up with a little tray application example that grabs and displays Caller ID information
in a timed popup window that can be closed by clicking a button or pressing ESC.
Optionally it plays a sound for each RING on the line.
Cancel it by rightclicking on the icon in the tray and selecting Quit.

Demonstrates:
  • Creating/Updating a preference file
  • Making a tray application
  • Using some of the SerialPort commands
The code is a bit lengthy but should be commented enough for you to utilize it.
Improve, expand, modify, and do with it what you wish but contribute back here please.

Code: Select all

; ------------------------------------------------------------
;
; PureBasic - SerialPort - Caller ID example 
;
; a tray application by terryhough
;
; requires a Caller ID capable modem (many are not)
; ------------------------------------------------------------

Enumeration
  #CallerID_Wdw
  #CallerID_List
  #CallerID_Close_Btn
  #Font_Name
  #Font_Numbr
  #Font_Other
EndEnumeration

#SerialPort = 0
Global Msg$
Global Text$
Global *Buffer = AllocateMemory(1024)

Global DefaultSound.s   : DefaultSound.s  = "Y"
Global DefaultShow.s    : DefaultShow.s   = "Y" 
Global DefaultVerb.s    : DefaultVerb.s   = "N" 
Global Sound$
Global Verb$ 

CompilerIf #PB_Compiler_OS = #PB_OS_Windows
  Port$ = "COM4"  ; Substitute the COM port of your CallerID compatible modem
CompilerEndIf

Procedure Show_CallerID(CID_Date.s, CID_Time.s, CID_Name.s, CID_Number.s)
  ExamineDesktops()
  PosX = DesktopWidth(0) - 320 - 10
  PosY = DesktopHeight(0) - 140 - 34
  hParent = OpenWindow(#CallerID_Wdw, PosX, PosY, 320, 140, "Caller ID",#PB_Window_TitleBar)
  ButtonGadget(#CallerID_Close_Btn, 110, 110, 100, 20,"Close")
  AddKeyboardShortcut(#CallerID_Wdw,#PB_Shortcut_Escape,18)  ; add an ESC capture event
  AddWindowTimer(#CallerID_Wdw, 123, 250)
  
  LoadFont(#Font_Name,  "Arial", 15 , #PB_Font_Bold)
  LoadFont(#Font_Numbr, "Arial", 12 , #PB_Font_Bold)
  LoadFont(#Font_Other, "Arial", 10 , #PB_Font_Bold)
  
  If CreateImage(0, 300, 90)
    If StartDrawing(ImageOutput(0))
      DrawingMode(#PB_2DDrawing_Transparent)
      RoundBox(1, 1, 298, 88, 18, 18, RGB(255,255,255))
      
      DrawingFont(FontID(#Font_Name))
      
      Width = TextWidth(CID_Name) 
      Left = (WindowWidth(#CallerID_Wdw) - Width) / 2
      DrawText(Left,10,CID_Name,  RGB(255,0,0))
      
      DrawingFont(FontID(#Font_Numbr))
      
      Width = TextWidth(CID_Number) 
      Left = (WindowWidth(#CallerID_Wdw) - Width) / 2
      DrawText(Left, 45,CID_Number, RGB(0,0,255))
      
      DrawingFont(FontID(#Font_Other))
      
      Width = TextWidth(CID_Date) + TextWidth(" " ) + TextWidth(CID_Time)
      Left = (WindowWidth(#CallerID_Wdw) - Width) / 2
      DrawText(Left, 67,CID_Date + " " + CID_Time, RGB(0,0,0))
      
      StopDrawing()
    EndIf
    
    ; Create a gadget to display the image
    ImageGadget(0, 10, 10, 300, 90, ImageID(0),#PB_Image_Border)
    
  EndIf
  While WindowEvent():Wend       ; Give the window a chance to display
  
EndProcedure

Procedure RetrieveResponse()
  Repeat 
    Result = AvailableSerialPortInput(#SerialPort)      ; Check for available data in input buffer
    If Result 
      Delay(50)  ; Give modem a chance to respond 
      While Result 
        ReadSerialPortData(#SerialPort, *Buffer, Result) ; Read the data in the input buffer into memory
        Text$ = PeekS(*Buffer, Result)
        Msg$ + Text$  
        Result = AvailableSerialPortInput(#SerialPort)
      Wend
      Break 
    EndIf
    Delay(500)  ; Let system do other things 
  ForEver 
EndProcedure

Procedure SetupModem()
  Msg$ = "SerialPort opened succesfully" + #LF$
  Msg$ + "Send AT command to turn on reset the modem" + #LF$
  Result = WriteSerialPortString(#SerialPort, "ATZ" + Chr(13)) 
  Msg$ + "Waiting for modem response" + #LF$
  While AvailableSerialPortOutput(#SerialPort):Wend  ; Wait for send buffer to empty
  RetrieveResponse()  ; Should get an OK response
  ;MessageRequester("DB",Msg$,#MB_ICONINFORMATION)
  Msg$ + "Send AT command to turn on Caller ID" + #LF$
  Result = WriteSerialPortString(#SerialPort, "AT+VCID=1" + Chr(13)) 
  ; Usual AT command to activate CallerID sensing is AT+VCID=1
  ; Some modems require different AT commands;  AT#CID=1 , AT#CC1, AT%CCID=1 or AT*ID1
  While AvailableSerialPortOutput(#SerialPort):Wend  ; Wait for send buffer to empty
  RetrieveResponse()
  If FindString(Text$,"OK",1) 
    Msg$ + "Modem ready to accept Caller ID information" + #LF$
    SysTrayIconToolTip(1, "Modem ready to accept Caller ID information")
    ;MessageRequester("DB",Msg$,#MB_ICONINFORMATION)
    Show_CallerID("Modem ready. ", "Accepting Caller ID", "CallerID", "Example Program")
    ProcedureReturn #True
  Else  
    Msg$ + "Modem did not accept Caller ID turn on command" + #LF$
    MessageRequester("DB",Msg$,#MB_ICONINFORMATION)
    ProcedureReturn #False
  EndIf 
EndProcedure  

Procedure WaitForCallerID()
  Msg$ = ""
  Repeat 
    Result = AvailableSerialPortInput(#SerialPort)      ; Check for available data in input buffer
    If Result 
      While Result 
        ReadSerialPortData(#SerialPort, *Buffer, Result) ; Read the data in the input buffer into memory
        Msg$ + PeekS(*Buffer, Result)
        If FindString(Msg$,"RING", 1)
          If Sound$ = "Y"
            PlaySound(0,0)
          EndIf 
          Msg$ = RemoveString(Msg$,"RING")
        EndIf   
        ;MessageRequester("DB",Msg$,#MB_ICONINFORMATION)
        If FindString(Msg$,"DATE",1)
          Msg$ = Mid(Msg$,FindString(Msg$,"DATE",1))
          Msg$ = RemoveString(Msg$, Chr(13), #PB_String_NoCase, FindString(Msg$,"NMBR",1))
          Msg$ = RemoveString(Msg$, Chr(10), #PB_String_NoCase, FindString(Msg$,"NMBR",1))
          ;CID_ShowDate.s   = InsertString(Trim(StringField(StringField(Msg$, 2, Chr(13)),2,"=")),"/",3)  ; or just grab date/time from system
          ;CID_ShowTime.s   = Trim(StringField(StringField(Msg$, 3, Chr(13)),2,"="))  ; or just grab date/time from system
          DOW = DayOfWeek(Date)
          Select DOW
            Case 0 
              CID_ShowDOW.s = "SUN"
            Case 1
              CID_ShowDOW.s = "MON"
            Case 2
              CID_ShowDOW.s = "TUE"
            Case 3 
              CID_ShowDOW.s = "WED"
            Case 4 
              CID_ShowDOW.s = "THU"
            Case 5
              CID_ShowDOW.s = "FRI"
            Case 6
              CID_ShowDOW.s = "SAT"
          EndSelect    
          CID_ShowDate.s   = CID_ShowDOW.s + "  " + FormatDate("%mm/%dd", Date()) 
          Hour = Hour(Date())
          If Hour < 12 
            AMPM.s = " AM"
          ElseIf Hour = 12
            AMPM.s = " PM"
          Else            
            Hour - 12
            AMPM.s = " PM"
          EndIf   
          CID_ShowTime.s   = RSet(Str(Hour),2) + ":" + FormatDate("%ii", Date()) + AMPM.s   
          CID_ShowName.s   = Trim(StringField(StringField(Msg$, 3, Chr(13)),2,"="))
          CID_ShowNumber.s = Trim(StringField(StringField(Msg$, 4, Chr(13)),2,"="))
          CID_ShowNumber.s = InsertString(CID_ShowNumber.s,"(",1)
          CID_ShowNumber.s = InsertString(CID_ShowNumber.s,") ",5)
          CID_ShowNumber.s = InsertString(CID_ShowNumber.s," ",10)
          ; MessageRequester("DB",CID_ShowDate + #LF$ + CID_ShowTime + #LF$ + CID_ShowName + #LF$ + CID_ShowNumber,#MB_ICONINFORMATION)
          Show_CallerID(CID_ShowDate.s, CID_ShowTime.s, CID_ShowName.s, CID_ShowNumber.s)
          ; Do other processing with the Caller ID here.  Look up info in a database, store call history, etc.
          Msg$ = ""
          Break 2
        EndIf
        Result = AvailableSerialPortInput(#SerialPort)
      Wend
      Break 
    EndIf
  ForEver 
EndProcedure

Procedure ReCreateTheINIFile()
  If FileSize("CallerID.INI") = -1
    If CreatePreferences("CallerID.INI")
      PreferenceGroup("CallerID")
      WritePreferenceString("Sound"   , DefaultSound)
      PreferenceComment(" Sound is defaulted to Y for Yes, put N for no sound")
      WritePreferenceString("Show"    , DefaultShow)
      PreferenceComment(" Display message is defaulted to Y for Yes, put N for no display")
      WritePreferenceString("Verbose" , DefaultVerb)
      PreferenceComment(" Display verbose replys is defaulted to N for No, put Y for verbose response")
      PreferenceComment(" ")
      PreferenceComment(" This is a default INI file created by CallerID.")
      ClosePreferences()
    EndIf
  EndIf
EndProcedure

ReCreateTheINIFile()               ; Make sure INI file exists.

ReReadPreferences:
If OpenPreferences("CallerID.INI")
  PreferenceGroup("CallerID")
  Sound$  = ReadPreferenceString("Sound"   , "Y")
  Show$   = ReadPreferenceString("Show"    , "Y")
  Verb$   = ReadPreferenceString("Verbose" , "N")
  ClosePreferences()
Else
  MessageRequester("Error","Unable to find or read the INI file.",#MB_ICONERROR)
EndIf
RereadFlag.w = 0
If Sound$ = ""
  DefaultSound = InputRequester("Caller ID Ring Sound","Enable Sound? (Y/N)", "Y")
  RereadFlag = 1
Else
  DefaultSound.s  = Sound$
EndIf  
If Show$ = ""
  DefaultShow = InputRequester("Caller ID Show Display","Enable Display? (Y/N)", "Y")
  RereadFlag = 1
Else
  DefaultShow.s   = Show$
EndIf  
If Verb$ = ""
  DefaultShow = InputRequester("Caller ID Verbose","Enable Verbose? (Y/N)", "N")
  RereadFlag = 1
Else
  DefaultVerb.s   = Verb$
EndIf  
If RereadFlag = 1
  DeleteFile("SERVER.INI")         ; Delete the INI file
  ReCreateTheINIFile()             ; and go recreate it,
  Goto ReReadPreferences           ; then reread them.
EndIf  

CatchImage(3, ?EmbeddedImage)     ; catch the embedded image from memory
If Sound$ = "Y"
  If InitSound()
    CatchSound(0, ?EmbeddedSound)  ; Catch the embedded sound from memory
  EndIf
EndIf 

If OpenWindow(1, 0, 0, 80, 80, "Caller ID Display", #PB_Window_Invisible | #PB_Window_ScreenCentered)
  
  AddSysTrayIcon(1, WindowID(1), ImageID(3)) ; Add an icon to the System Tray --
  SysTrayIconToolTip(1, "Display Caller ID information for incoming calls.")
  
  If CreatePopupMenu(0)            ; Creating the pop-up menu
    OpenSubMenu("&Config") 
    MenuItem(5, "Toggle &Sound")   ; Sound toggle on or off
    MenuItem(6, "Toggle &Display") ; Display toggle on or off
    MenuItem(7, "Toggle &Verbose") ; Verbose toggle on or off
    CloseSubMenu() 
    MenuItem(9, "&About")
    MenuBar() 
    MenuItem(4, "&Quit")
  EndIf
  
  SetMenuItemState(0, 5, 0)          ; Show Sound status
  If Sound$ = "Y"
    SetMenuItemState(0, 5, 1) 
  EndIf
  
  SetMenuItemState(0, 6, 0)          ; Show Display status
  If Show$ = "Y"
    SetMenuItemState(0, 6, 1) 
  EndIf
  
  SetMenuItemState(0, 7, 0)          ; Show Verbose status
  If Verb$ = "Y"
    SetMenuItemState(0, 7, 1) 
  EndIf
  
  If OpenSerialPort(#SerialPort, Port$, 14400, #PB_SerialPort_NoParity, 8, 1, #PB_SerialPort_NoHandshake, 1024, 1024)
    
    ttimer = 10
    If SetupModem()
      
      Repeat
        
        Event = WaitWindowEvent(1000)
        Select Event
            
          Case #PB_Event_SysTray
            If EventType() = #PB_EventType_RightClick
              DisplayPopupMenu(0, WindowID(1))
            EndIf
            
          Case #PB_Event_Timer
            If EventTimer() = 123
              ttimer + 1
              If ttimer = 20     ;  Set to desired milliSeconds Delay/250 
                If IsWindow(#CallerID_Wdw)
                  CloseWindow(#CallerID_Wdw)
                EndIf
                ttimer = 0
              EndIf
            EndIf 
            
          Case #PB_Event_Menu
            Select EventMenu()  ; See which menu has been selected
              Case 4 ; Quit
                Quit = 1
              Case 5 ; Toggle sound
                If Sound$ = "Y"
                  SetMenuItemState(0, 5, 0) 
                  Sound$ = "N"
                Else
                  SetMenuItemState(0, 5, 1) 
                  Sound$ = "Y"
                  If Sound$ = "Y"
                    If InitSound()
                      CatchSound(0, ?EmbeddedSound)  ; Catch the embedded sound from memory
                    EndIf
                  EndIf 
                EndIf
                DefaultSound = Sound$    
                DeleteFile("SERVER.INI")
                ReCreateTheINIFile()
              Case 6 ; Toggle Display
                If Show$ = "Y"
                  SetMenuItemState(0, 6, 0) 
                  Show$ = "N"
                Else
                  SetMenuItemState(0, 6, 1) 
                  Show$ = "Y"
                EndIf
                DefaultShow = Show$
                DeleteFile("SERVER.INI")
                ReCreateTheINIFile()    
              Case 7 ; Toggle Verbose
                If Verb$ = "Y"
                  SetMenuItemState(0, 7, 0) 
                  Verb$ = "N"
                Else
                  SetMenuItemState(0, 7, 1) 
                  Verb$ = "Y"
                EndIf
                DefaultVerb = Verb$
                DeleteFile("SERVER.INI")
                ReCreateTheINIFile()    
              Case 9 ; About
                About$="Y"
                Show_CallerID("", "", "CallerID", "Example Program")
              Case 18 ; ESC key pressed
                If IsWindow(#CallerID_Wdw)
                  CloseWindow(#CallerID_Wdw)
                EndIf 
                ttimer = 0
            EndSelect
            
          Case #PB_Event_Gadget
            If EventGadget() = #CallerID_Close_btn
              If IsWindow(#CallerID_Wdw)
                CloseWindow(#CallerID_Wdw)
              EndIf   
              ttimer = 0
            EndIf
            
          Case #PB_Event_CloseWindow
            If EventWindow() = #CallerID_Wdw
              CloseWindow(#CallerID_Wdw)
            EndIf   
            ttimer = 0
            
        EndSelect
        
        If AvailableSerialPortInput(#SerialPort)      ; Check for available data in input buffer
          WaitforCallerID()
        EndIf   
        
      Until Quit = 1
      
    EndIf ; End of Setup Modem
    
    CloseSerialPort(#SerialPort)
    
  Else
    MessageRequester("Error", "Can't open the serial port: " + Port$)
  EndIf
Else
  MessageRequester("Error","The window cannot be opened!",#MB_ICONERROR)
EndIf

End

EmbeddedSound:  IncludeBinary "ringout.wav"   ; Embed the sound to play - substitute your sound file
EmbeddedImage:  IncludeBinary "lighton.ico"   ; Embed the image to show - substitute your image icon

Re: Using the SerialPort commands example - CallerID

Posted: Fri May 27, 2011 2:50 pm
by Peyman
thanks good source, but it never show me the caller number so i change the WaitForCallerID procedure like this to work for me :

Code: Select all

Procedure WaitForCallerID()
  Protected.s Msg, CID_ShowDOW, CID_ShowDate, AMPM, CID_ShowNumber, CID_ShowTime
  Protected.i Result.i, Date.i, Hour.i 
  
  Repeat
    Result = AvailableSerialPortInput(#SerialPort)      ; Check for available data in input buffer
    If Result
      While Result
        ReadSerialPortData(#SerialPort, *Buffer, Result) ; Read the data in the input buffer into memory
        Msg + PeekS(*Buffer, Result)
        If FindString(Msg, "RING", 1)
          If Sound$ = "Y"
            PlaySound(0,0)
          EndIf
        EndIf   

        If FindString(Msg, "DATE", 1)
          Date = Date()
          Msg = Mid(Msg, FindString(Msg, "NMBR", 1))
          Msg = RemoveString(Msg, Chr(13), #PB_String_NoCase)
          Msg = RemoveString(Msg, Chr(10), #PB_String_NoCase)
          SetClipboardText(Msg)
          Select DayOfWeek(Date)
            Case 0
              CID_ShowDOW = "SUN"
            Case 1
              CID_ShowDOW = "MON"
            Case 2
              CID_ShowDOW = "TUE"
            Case 3
              CID_ShowDOW = "WED"
            Case 4
              CID_ShowDOW = "THU"
            Case 5
              CID_ShowDOW = "FRI"
            Case 6
              CID_ShowDOW = "SAT"
          EndSelect
          
          CID_ShowDate = CID_ShowDOW + "  " + FormatDate("%mm/%dd", Date)
          
          Hour = Hour(Date)
          
          If Hour < 12
            AMPM = " AM"
          ElseIf Hour = 12
            AMPM = " PM"
          Else           
            Hour - 12
            AMPM = " PM"
          EndIf
          
          CID_ShowTime = RSet(Str(Hour), 2) + ":" + FormatDate("%ii", Date) + AMPM
          CID_ShowNumber = StringField(Msg, 2, "=")
          Show_CallerID(CID_ShowDate, CID_ShowTime, "", CID_ShowNumber)
          Break 2
        EndIf
        Result = AvailableSerialPortInput(#SerialPort)
      Wend
      Break
    EndIf
  ForEver
EndProcedure