ActiveXperts Serial Port Component

Quicklinks

Case Study: "CallCare" (automatic phone dialing)

Call care case   Download the CallCare case (ASP files + database)

1. Background

CallCare is a call center with 30 employees. The call center is hired by one of the largest newspaper companies in the UK for marketing purposes. This press company asked CallCare to actively chase after new readers.

CallCare has a large database of potential customers and their telephone numbers. CallCenter's task is to call these potential customers by regular phone, and ask them if they are interested in a fee newspaper subscription for one month.


2. Problem Statement

The call center-agents make a few hundred phone calls a day. The phone numbers are offered in an Excel worksheet.

For each new phone call, the agent needs to press down the phone number buttons manually on the phone.

The current system has the following disadvantages:

  • The call center agents spend a lot of time on dialing the numbers; they read it from screen and manually type it in on the phone;
  • The call center agents easily make mistakes; they often dial the wrong number;
  • It takes a lot of time to see whether a customer has been contacted or not;
  • It takes a lot of time to create a list of phone-numbers that the call center-agents need to call that day.

3. Goals of the new System

Goals of the new system are:
  • No more manual dialing; dialing a number should be a matter of clicking one button on the screen;
  • A central database with all phone numbers the agents need to dial;
  • Ability to get an overview of outstanding phone calls and completed phone calls;
  • Plain old telepfone devices should be replaced by a modern headset;
  • Call dialing should be logged.

4. Design

Client Side vs. Server Side

The system consists of server side and a client side.

  • Server side (database) - To make sure every user is able to access his or her phone calls on any computer in the building; we're using an ASP based web-interface. We're using a simple Access database to fetch the customers the employees need to call. The ASP pages query the database and produce HTML code for the agent's borwser.

  • Client side (dialer) - The agent uses a voice modem with a headset, connected to the local PC. The agent access the system using their browser. They query phone numbers through the ASP pages and receive the telephone numbers they need to call. The call is made on the client (i.e. workstation). This workstation uses JavaScript embedded in HTML (produced by the ASP pages) to control the modem to make an outgoing call.

ActiveXperts Serial Port Component

To create a system that is able to make phone calls, Serial Port Component is used on the client side to access the modems attached to the agent's workstation. This makes it easy to create a system that makes a modem execute commands and log everything that happens.


5. Server Side implementation

Database

To make a phone call, we need a phone number. The phone numbers are stored in a database on the server. We're fetching the information with ASP. ASP creates a HTML source code that is sent to the client.

We're setting up a database that contains customer information like the address of customers, their telephone numbers, and so on. We also want to store user or employee information like the usernames and passwords. We also need to know what customers the employee needs to call. Thats why we're having 3 different tables and one query:

  • Employee
  • Customers
  • Callist

Database

(Click on the picture to enlarge)

We're also creating a query so we can fetch all the required information from just one table. The query looks like this

SELECT CallList.employeeID,
Customers.phoneNumber, 
Customers.name, 
Customers.firstName, 
Customers.address, 
Customers.postCode, 
Customers.residence, 
Customers.customerCode, 
CallList.ID, 
CallList.done
FROM Customers INNER JOIN CallList ON Customers.customerID = CallList.customerID;

We're calling this query "CallListQ". CustomerID is the primary key. We're mainly accessing this "table".

ASP

To get the database and the modem combined in a web interface we're using ASP and JavaScript. Because every employee has to dial the phone-numbers individually we're building that part in JavaScript. The entire solution contains 5 scripts:

  • counter.js
    This script is displayed on the bottom of the page
  • AxSerialPortDailer.js
    This script is displayed on the bottom of the page
  • login.asp

<HTML>

<HEAD>
  <link rel=stylesheet href=style.css>
  <TITLE>Please log in!!</TITLE>
</HEAD>

<BODY>

<% 

if request("submit") <> "" then
  Session( "username" ) = request("username")
  Session( "password" ) = request("password")
  Response.Redirect "index.asp"
else
  Response.Write("<b>Please fill in your username and password....</b>")
  Response.Write("<br><br>")
end if


%>

<form action=login.asp method=post>
 Username::<input name=username type=text><br>
 Password::<input name=password type=password><br>
 <br>
 <input name=submit type=submit value="Fill in my username and password!!"><br>
</form>


</BODY>
</HTML>
  • index.asp
<%

Function GetRootDir() 
  strFile = server.mappath( "\dummyfile" )
  arr = split( strFile, "\dummyfile" )
  GetRootDir = arr( 0 )
End Function

'fist of alle make a connection to your database, this needs to be the full path!
strDatabase = GetRootDir() & "\YourCommercial.mdb"
Set objConn = Server.CreateObject("ADODB.Connection")
objConn.Open "Driver={Microsoft Access Driver (*.mdb)}; DBQ=" & strDatabase & ";"

%>

<!-- now we're going to check if the logged on user is valid //-->
<% call checkUsernameAndPassword() %>


<html>

  <head>

    <title>Callcenter YourCommercial Webinterface</title>

    <!-- set a link to the stylesheet //-->
    <link rel=stylesheet href=style.css>

    <!-- Now we're creating the Serial Port object //-->
    <object id="objComport" codeBase="http://www.activexperts.com/files/serial-port-component/AxSerial32.dll" height="30" width="200" 
        classid="CLSID:07ECB42B-322A-40B9-A8A9-3815AF3C4F60" viewastext></object>
    <script language="JavaScript" type="text/javascript" src="counter.js"></script>
    <script language="JavaScript" type="text/javascript" src="AxSerialPortDailer.js"></script>

  </head>

  <body>

<!--  ####################### Controle center ############################## -->

  <b>Controlecenter:</b>
   <table>
    <tr>
      <td style="border: 0px solid white;">

         Logged in as: 
         <font color=red>
             <b>
               <% = session("username") %>
            </b>
         </font>
          
         ( <a href="logoff.asp">log me off</a> )

      </td>
      <td width=400>
         <center>
            <font color=red>Modem is on</font>
            <b> COM</b>

            <select size="1" name="comport" ID="Select1"></select>
            
            <script language="JavaScript" type="text/javascript">

              //now read witch comports are available and list them in the dropdown box
              nCount  = objComport.GetDeviceCount ();
              
              for ( i = 0 ; i < nCount ; i++ ){
                  comport.options [ i ] = new Option ( objComport.GetDevice ( i ), "" );
              }

              for ( i = 1 ; i < 9 ; i++ ){
                  comport.options [ i + nCount - 1 ] = new Option ( "COM" + i , "" );
              }

            </script>

         </center>
      </td>
    </tr>
    <tr>
      <td colspan=2>
         <center>
           <br />
  
           <font color=red>Microphone volume:</font>
              
           <b>
              <a onclick="micSofter()" style="cursor: pointer;"><< Decrease</a>
                |  
              <a onclick="micLouder()" style="cursor: pointer;">Increase >></a>
           </b>
           <br />
      
           <font color=red>Speaker volume:</font>
              
           <b>
             <a onclick="speakerSofter()" style="cursor: pointer;"><< Decrease</a>
               |  
             <a onclick="speakerLouder()" style="cursor: pointer;">Increase >></a>
           </b>
           <br />
      
           <font color=red>Hang up:</font>
              
           <b>
              <a onclick="hangUp()" style="cursor: pointer;">Hang up the phone</a>
           </b>

           <br />
           <br /> 
      
         </center>
       </td>
     </tr>
   </table>

  <br>


<!-- function for processing finished phonecalles (the done button) -->
  <% call isPhoneCallFinished() %>

  
  
<!-- display the calls that need to be made -->
  <b>Calls to be made by <% = session("username") %>:</b><br>
  <% call display(session("userID"),false) %>

  <br><br>

  
  
<!-- display the completed calls -->
  <b>Completed calls by <% = session("username") %>:</b><br>
  <% call display(session("userID"),true) %>


 </body>
</html>


<!-- ##################### THE ASP FUNCTIONS ############################### -->

<!-- ######################### THE DISPLAY FUNCTION ######################## -->

<%

sub display(employeeID,done)

  ' first of alle we're going to collect only the calls the current user needs to make
  'the query
  strQuery = "SELECT * FROM CallListQ WHERE employeeID=" & employeeID & " AND done=" & done

  'execute the query
  set RS = objConn.Execute( strQuery )

  'open a table tag to display the information in a table:
  response.write("<table cellspacing=0 cellpadding=0>")
  
  'echo the information
  while not RS.EOF

%>     

<!-- all the results are displayed in a row //-->
<tr>

 <td> <% = RS("name") %>, </td>
 <td> <% = RS("firstName") %> </td>
 <td> <% = RS("address") %> </td>
 <td> <% = RS("postCode") %> </td>
 <td> <% = RS("residence") %> </td>
 <td> <% = RS("customerCode") %> </td>
     
 <td width=200>
   <form method=post action=index.asp>

      <!-- Check if the phonecall is allreade made or not //-->
      <!-- If the phonecall is allreade made, the dail and done button dont need to be displayed //-->
      <!-- If the phonecall is made, we're going to display a button to put it back on the list //-->

      <% if RS("done") = false then %>

        <input type=button onclick=<% = "dial('" & RS("phoneNumber") & "')" %> value="Dial <% = RS("phoneNumber") %>">
        <input type=hidden name=id value=<% = RS("ID") %>>
        <input type=submit name=submitbutton value=Done!>

      <% else %>

        <input type=hidden name=id value=<% = RS("ID") %>>
        <input type=submit name=submitbackonlist value="Put back on list">           

      <% end if %>

    </form>
  </td>

<tr>

<%

  'tell the loop to continue
  RS.MoveNext

  'close the loop
  wend

  'close the table tag
  response.write("</table>")

'close the sub
end sub

%>



<!-- ################### CHECK IF A PHONECALL HAS FINISHED ############### //-->
<!-- if a "done" button has been clicked, this function is being triggerd  //-->



<%

sub isPhoneCallFinished()

  if request("submitbutton") <> "" then

    'find out what record it is about
    ID = request("ID")

    'the query
    strQuery = "UPDATE CallList SET done=true WHERE ID=" & ID

    'execute the query
    set RS = objConn.Execute( strQuery )

  else
  end if

  if request("submitbackonlist") <> "" then

    'find out what record it is about
    ID = request("ID")

    'the query
    strQuery = "UPDATE CallList SET done=false WHERE ID=" & ID

    'execute the query
    set RS = objConn.Execute( strQuery )

  else
  end if

'close the sub
end sub

%>



<!-- ########################### CHECK THE USER ########################## //-->



<%

sub checkUsernameAndPassword()
  
  dim userOK

  'check if the password is filled in 
  if session("password") = "" then
     response.redirect "login.asp"
  else
  end if

  'check if the username is filled in
  if session("username") = "" then
     response.redirect "login.asp"
  else
  end if

  'check if they're right
  'first collect the users
  strQuery = "SELECT * FROM Employee WHERE firstName='" & session("username") & "'"
  set RS = objConn.Execute( strQuery )

  'in case no record is found, the default value will be "notOk"
  userOK = "notOK"

  while not RS.EOF

     'if the password matches, we have a valid user
     if RS("passWord") = session("password") then
       userOK = "OK"
       session("userID") = RS("employeeID")
     else
       userOK = "notOK"
     end if

    'tell the script to move on
    RS.MoveNext

  'end of the loop
  wend

  'and if they're not, redirect to the login page so they can try again
  if userOK = "notOK" then
    response.redirect "login.asp"
  else
  end if

'close the function
end sub

%>
<font size=1 color=red>This sample is using Serial Port Component.</font>
  • logoff.asp
<%

session("username") = ""
session("password") = ""

response.redirect "login.asp"

%>

6. The Client Side (JavaScript, controlling the voice modem)

The main thing in this project is controlling the modem using AT commands. To set up a connection to another telephone actually means to send several commands to the device.

With the sample program "Query device" witch is shipped with Serial Port Component, you're able to send and test these commands.

To switch the modem in the right mode we're using the following commands (the commands are explained below):

AT#CLS=8 or AT#FCLASS=8
AT#VLS=6
AT#VRN=0

First of all we need to set the modem in sound mode. To do that, depending on the type of modem, we need to execute the command "AT#CLS=8" or "AT#FCLASS=8". The modem will reply "OK" and "h", witch means the modem is off hook.

Now your modem is ready to make a call but is not yet able to make a so called "voice call". To make a voice call we need to switch on the speakerphone mode. This simply means you're switching on the microphone and the speaker. Therefore we need the VLS command. To check if your modem is supporting speakerphone and at the same time switching it on you need to execute the AT command "AT#VLS=6".

After that we're setting the "Ringback Never Came" settings. To set these settings, we need the AT command "AT#VRN". This command knows the values 0 till 255. The value represents the value multiplied by 100 milliseconds the modem will wait before it switches to online voice mode. We want the device to be in online voice mode immediately, so we're using the following command: "AT#VRN=0".

Now we're able to make a phone call. You can dial a number using the command ATDT like "ATDT0.phonenumber". The modem will reply "VCON" and you'll hear the phone ringing. If someone picks up the phone you are able to talk to him and hear him.

You can hang up the phone using the command ATH of ATZ.

All settings will now be reset to default.

This makes the complete set of commands to make a phone call look like this:

AT#CLS=8 or AT#FCLASS=8
AT#VLS=6
AT#VRN=0
ATDT0,0123456789
ATH

Because this needs to be done on the clients computer, we need to generate a source file that accesses the modem. Therefore I created the following source files:

  • counter.js

//******************************************************************************
//
//                          ActiveXperts Software
//
//******************************************************************************

//This file calculates the volumes of the microphone and the speaker.
//To set the volumes we need to use the AT command SPK.
//This command has 3 paramters, one for the mode, one for the speaker volume 
//and one for the microphone volume..


//set the variables
var mute;
var speaker;
var mic;

//fill in the default values
mute = 1;
speaker = 5;
mic = 1;


//################# Funtion turn up the microphone volume ######################

function micLouder(){
  //the mic volume can not be louder then 3
  if(mic < 3){
    mic = mic + 1;
  }
 
  //now execute the values
  result();

//close function
}

//############### Funtion micSofter to decrease the volume #####################

function micSofter(){
  //the mic volume cannot be lower then 0
  if(mic > 0){
    mic = mic - 1;
  }

  //execute the values
  result();

//close function
}


//########### Function speakerSofter to turn down the speaker volume ###########

//the speaker volume has 4 values: 
// - 0,  witch is the loudest
// - 5,  witch decreases the volume with 10 dB
// - 15, witch decreases the volume with 30 db
// - 16, witch mutes the volume

function speakerSofter(){

  switch(speaker){
    case 0:
      speaker = 5;
    break
   
    case 5:
      speaker = 15;
    break

    case 15:
      speaker = 16
    break
  }  

  //Now execute the results
  result();

//close function
}



//################## Function to turn up the volume ############################

function speakerLouder(){

  switch(speaker){
    case 5:
      speaker = 0;
    break
     
    case 15:
      speaker = 5;
    break
     
    case 16:
      speaker = 15
    break
  }

  //now execute the results
  result();

//close function
}

//################# Function result to execute the SPK command #################

function result(){

  strResult = "AT#SPK=" + mute + "," + speaker + "," + mic;
  objComport.WriteString(strResult);

//close function
}

  • activeComportDailer.js

//******************************************************************************
//
//                          ActiveXperts Software
//
//******************************************************************************


//########################## Function dail #####################################

function dial(telephonenumber){
      
      //Set a couple of properties of the device, some are collected from the page others hard coded..
      //If property LogFile is not filled in, it won't be created..
      objComport.Device              = comport.options [comport.selectedIndex].text;
      objComport.BaudRate            = 115200;
      objComport.ComTimeout          = 2000;
      objComport.LogFile             = "";

      //Now open the device so we can dial a phone number
      objComport.Open();

      //Check for errors, if the error is not 0, whitch equals "Succes", then alert the error..
      if(objComport.lasterror != 0){
        window.alert("Failed to dail!\n " + objComport.lasterror + ":" + objComport.geterrordescription(objComport.lasterror));
      }        

      //If the device succesfully opened, we can send commands to make a connection to another phone..
      if( objComport.IsOpened == -1 ){

        //First of all we need to make sure the modum is in the right mode..
        //To make a phone call means your modem needs to be in speakerphone mode..
        //You can set your modem in speakerphone mode using the following commands:
        //
        //AT#CLS=8
        //AT#VRN=0
        //AT#VLS=6
        //

        objComport.WriteString( "AT#CLS=8" );
        //Wait a second, so the device has enough time to execute the command, before executing the next command
        objComport.Sleep(1000);
          
        objComport.WriteString( "AT#VRN=0" );
        //Wait..
        objComport.Sleep(1000);
          
        objComport.WriteString( "AT#VLS=6" );
        //Wait
        objComport.Sleep(1000);

        //Now dail the phonenumber using AT command ATDT
        strDailstring = "ATDT0," + telephonenumber;
          objComport.WriteString( strDailstring );
        }
 
        //Now the user is on the phone
        //Hanging up and closing the device is done in function "hangUp", below..
        //

//Close function dial
}


//########################## Function hang up ##################################

function hangUp(){
  
  //Hang up using command ATH (AT Hang) 
  objComport.WriteString("ATH");
  
  //Close the device
  objComport.Close();
  
//End function hangUp  
}