iButton integration

GPF
2008-08-24
2013-06-18
  • GPF
    GPF
    2008-08-24

    Hi there

    I was wondering if anyone has had any success with the iButton for users signing in?

    I found two messages here..

    http://sourceforge.net/forum/message.php?msg_id=4287439
    http://sourceforge.net/forum/message.php?msg_id=4290533

    ..but the instructions are incomplete.

    I have the USB device from Maxim that I have built into a reader and the standard iButtons (just an ID) on keyfobs for staff.

    Anyone have any ideas on how I could do this?

     
    • jimm
      jimm
      2008-08-24

      Hi,

      I added the ibutton function to the program  back in ver 0.22 and I still use a very modified version based on the ver.0.22 code. Nobody seemed interested in the function so I did not write the rest. Since it was for 0.22 most of what I did would not apply to the versions now available but it should no problem to insert the function in what is now the current version.

      Basically the iButton java  sdk has the routines to access a reader and the modified file that can be downloaded can be used as a base of what has to be changed in the sdk supplied code. In the sdk  the routine was used to report what iButton id was put in the reader for a fixed period of time (about 20 seconds if I remember). I removed this so that the thread continues to run and instead of just printing the id to the terminal it puts it in a static variable that can be accessed by the rest of the POS program and set on an event caused by a button inserted in the holder or removed.
      The "imports" concerning POS classes are now longer valid and have to be modified for current POS versions.
      The modified OWWatch.java takes care of the real work as far as iButtons are concerned.
      The "public static String ibut_code" holds the iButton id.

      Back when I did the mod I had to modify the "people" table to have a means to store the iButton id for each person. This may no longer be needed since I believe "card" field could be used to store the id.
      If this field is used you would have to disable any use made of the field as it works right now.

      Since I wanted two modes of operation for the iButtons( keyfob in place when selecting user at login or place the keyfob in reader and program logs in the owner and removing the keyfob from the holder logs out the owner and returns to login screen) I created a new key in the program "properties" file to chose the operation mode for logging into the POS. I gave the possibilities of 1)Hit user and logged in, 2)Hit user and require PIN, 3)Insert keyfob in holder and owner is logged in - keyfob may be removed and user stays logged in, 4)Insert keyfob in holder and owner logged in - remove keyfob from holder logs out user.
      It required modifying the configuration program to have an easy way to set the properties file correctly.

      When the program starts up it gets the key indicating the login mode from the properties file. This key is converted to an integer "lev" depending on what it contains for the operation mode.

      Here is modified code for my version of OWWatch.java(Jim_OW.java).

      /**
        *  Gets new button logging in
        */
         public void deviceArrival(DeviceMonitorEvent devt)
              {        for(int i=0; i<devt.getDeviceCount(); i++){
                       ibut_code = (devt.getAddressAsStringAt(i));}
                       if (JFrmTPV.lev==2){                  //only open if ibutton mode used
                       JPrincipalApp.but_code = ibut_code;
                       JFrmTPV.open_user(ibut_code);
                       }
                      
              }
        /**
        *  Returns iButton id string
        */
        public static String getbutton_code (){ return ibut_code ;  }     // call this to get code of button in reader
            

      /**
        *  Gets button logging out
        */   
          public void deviceDeparture (DeviceMonitorEvent devt)     // Depart event as a NetworkMonitorEventListener
              {
                   for(int i=0; i<devt.getDeviceCount(); i++){
                   ibut_code = (devt.getAddressAsStringAt(i));
                   JFrmTPV.jLogging.setSelected(false);
                   JFrmTPV.jLogging.setText("No iButton");
                   //JFrmTPV.jCheckBox1.setText("Loged Out");
                   JFrmTPV.sent_user_code ="";
                   JPrincipalApp.but_code= "";
                   ibut_code = "";
                   if( JFrmTPV.perm_button){  // will only break connection if permenant set
                   ibut_code = "";
                   JFrmTPV.close_user();
                   }
                                                             }
              }

      When I first did the mod I only used the iButton as a replacement to entering a PIN. Then having the id as a static variable was enough. Adding the automatic login/logout required the changes to the code that can be downloaded.
      There are things not really needed like "jLogging" which are just for extra information I put on the login screen.
      The main things are that at a button "arriving"  "JFrmTPV.open_user(ibut_code);" method checks the people table for a matching ibut_code then logs in the user. When a button is removed "departure" a check is done if "perm_button" is set. If true "JFrmTPV.close_user();" is called. In any case  "ibut_code" is cleared on button removal. (perm_button" is set according to the operation mode "lev").

      In my version the needed modifications where then done in  JFrmTPV.java

      Not using the present versions I cannot be very specific. Hope this helps to send you in the right direction.

      Jim

       
    • mger
      mger
      2008-08-25

      Hi,

      for the records - I started with OB POS 2.10 last week and have to do some adjustments for a current project. One thing is the ibutton support.

      So I would be glad for any hint how to accomplish this in 2.10.

      Michael

       
    • jimm
      jimm
      2008-08-25

      Hi Michael,

      First thing to do is go to the Maxim site and download the iButton java SDK.  It has lots of examples for reading various type iButtons. I suggest that you load into Netbeans and try running OWWatch.java. Once you can run OWWatch.java in Netbeans and see it report a button inserted or removed you can make a copy of OWWatch.java to make a new class with a different name( something like "MyOWWatch.java". Make sure the new file also works before starting to modify it.
      First thing to do is get ride of the time limit of how long the program will run for. You can comment out the code following
      " sleep for the specified time", "Kill any threads we have running", "free port used by adapter". Now make sure that the modified program still works and reports the buttons but that it stays active until you kill the run in Netbeans. When you made the copy of OWWatch.java Netbeans should have refactored the name of the class to the name you gave the file.

      Now you will have to change the name of "main" to another name which will be the method you call to start the ibutton reader monitoring.
      I changed it from "public static void main (String args [])" to "public static void One_on ()". Now you can call the One_on() method from POS. You will have to make sure that all the classes needed by One_on() are available when you move it into your OpenbravoPOS source. Also that you have an "import" of your new class in the POS start up class.
      Basically you want the One_on() method to be called as soon as the program is started probably in StartPOS.java.

      Now in your "MyOWWatch.java" you want to create a "public static String ibut_code = "";" to hold the iButton code of the button in the holder.

      I also made a method that can be called to get the code :
      public static String getbutton_code (){      // call this to get code of button in reader
             return ibut_code ;
         }

      Now the two methods "public void deviceArrival (DeviceMonitorEvent devt)" and "public void deviceDeparture (DeviceMonitorEvent devt) can be modified so that when a device "arrives" its "ibut_code = (devt.getAddressAsStringAt(i));" and when a device is removed "ibut_code = "";"

      From here on I cannot be of much help since the version that I use is very different than what is available now concerning logging in or out.

      You will have to find just where and how you want to use the "ibut_code". You could possibly modify the routine that checks against an employ card swipe to check the "ibut_code" against a code stored in the "people.card" field. In this case the "MyOWWatch.java" gives you the possibility to hand back the button id at any time if a button is in the reader.

      If you wanted you could include in the "deviceArrival" a call to the log in routines and use the "ibut_code" to look through the codes stored in "people.card" and log in the person with the matching code.

      The "deviceDeparture" method could be modified to immediately call the log out routines as soon as the button is removed from the holder.

      Lots of other things can be added to make operation configurable so that through the configuration panel you can choose if removing the button closes the terminal or not. Just having a simple boolean that you can store in the properties file and read back on start up. Once you have such a variable it is very easy to put a simple "if" test concerning auto logout on button removal.

      Hope this helps,
      Jim

       
    • GPF
      GPF
      2008-08-25

      Jim

      Thank you for posting the information. I will fire up NetBeans and see how I get on. I'm not a great Java programmer but I should be able to sort something out. I might come back here with a few questions though!

      The iButton is a great piece of kit. I got Maxim to supply me with the iButtons and USB reader as a not-for-resale price "to integrate with the POS system".

      Thanks!

       
    • mger
      mger
      2008-08-27

      Hi Jim,

      thanks for the informations - I will give it a try as soon as my ibutton equipment (I'm going to use Kelloxx) has arrived.

      Michael

       
    • jimm
      jimm
      2008-08-27

      Hi Michael,

      Not sure what equipment that you will be using from Kelloxx. When I did the mode for myself I used a Dallas 1 wire to serial adapter and a simple holder. If you are using the already mounted in a plastic box type of unit it may require  another direction to put it in. Some of the ready made units output the data already decoded just as the iButton ID rather than needing the java routines to control the adapter (the ones in the java ibutton SDK).

      You may want to a look at www.homechip.com ( it is a site run by a fellow in the UK called Nigel) and if you need Magnetic fobs the price is nothing to do with the Kelloxx prices. Nigel is involved in a home automation project whhich I also participate in and due to the sometimes excessive prices of iButton equipment in Europe started to import and sell the equipment about a year ago but at prices for developers rather than end clients.

      Let me know when you get the gear,

      Jim

       
    • mger
      mger
      2008-10-14

      Hi Jim,

      I finally got my equiment and managed to make almost all the modifications we need. The only thing I'm having troubles ist the ibutton reader.

      I have a Glancetron K400 Touchcomputer with integrated MSR/ibutton reader (not Kelloxx as mentioned before). The MSR acts as keyboard and the ibutton reader ist internally attached to COM6. I've tried every program from the 1-Wire iButton java SDK but I can't find neither the attached ibutton reader nor can I read the ibutton. So I fired up a terminal programm to see whats happening on the COM port. When I insert the key I get |||||| followed by 16 characters - according to the very short documentation this is 8 Bit CRC + 48 Bit Serial Number + 8 Bit family code (01h) - ending with '. When I remove the key I get ||||||'.

      Do you have any idea how I get this done.

      Thx,
      Michael

       
    • jimm
      jimm
      2008-10-15

      Hi Michael,

      One thing is to contact your supplier to see if there are any options that can be set for the iButton reader. Can it also emulate keyboard entry  or signal when a key is removed ?.

      The iButton is a Maxim/Dallas one wire device and exists in may forms. Some have r/w memory others can measure and even record temperatures. Other chips in the one wire family have many other functions and there is a good chance that there are some in your car talking the the vehicles on board computer. The thing that is common to them all is the unique ID of each device which consists of the information that you get back when you insert a key. All iButtons will return their ID when sent the same code.

      There are two ways to speak to an iButton.
      1) use a microcontroller(PIC etc) to do the converstion between the one-wire bus and a serial port that allows the functions that the designer of the interface thinks you want and only those functions.

      2) Use either a serial/one-wire or USB-one-wire converter chip made by Maxim/Dallas and a few passive components which gives full control of the interface to be able to use the full line of one-wire products and any function you want determined by the software used.

      The SDK is expecting to see solution 2.

      It appears that your equipment is using the first method and the designer decided that all you want is the ID when a key is inserted. Maybe there is a code that you can send to COM6 and have it give you a notification when a key is removed but I would not count on it.

      What can be done is write code that sets up COM6, store the incoming data, when you have received the "'" end character, use cleaned up(getting rid of the "|" and end "'") stored data string to check against the users ID code and let him log in if correct.

      Jim

       
    • mger
      mger
      2008-10-24

      Hi Jim,

      it took me some time, but I managed to get the thing going. I followed your tipps and made the following changes to OBPOS:

      1) I created an new Java class (iButtonWatch.java) and inserted this code:

      /*---------------------------------------------------------------------------
      * IButtonWatch
      *
      * V1.00 - 24.10.2008
      * Michael Geringer
      *---------------------------------------------------------------------------
      */

      package com.openbravo.pos.forms;

      import gnu.io.*;

      import java.io.IOException;
      import java.io.InputStream;
      import java.util.*;

      /**
      * Monitor serial iButton-Reader
      *
      * @version    1.00, 24 October 2008
      * @author     Michael Geringer
      */
      public class iButtonWatch implements Runnable {

          private static JRootApp m_appview = null;

          private static String iButton_Key = null;
          private static String iButton_Code = "";

          private byte[] data = new byte[400];

          //--------
          CommPortIdentifier serialPortId;
          Enumeration enumComm;
          SerialPort serialPort;
          InputStream inputStream;
          Boolean serialPortOpen = false;

          int baudrate = 9600;
          int dataBits = SerialPort.DATABITS_8;
          int stopBits = SerialPort.STOPBITS_1;
          int parity = SerialPort.PARITY_NONE;
          //- hardcoded port!!!
          String portName = "COM6";
          //--------

          public void main(String[] args) {
              Runnable runnable = new iButtonWatch();
              new Thread(runnable).start();
          }

         // call this to get code of button in reader
         public String GetIButton_Key (){
              return iButton_Key ;
          }

         // workaround
         public static void PutAppView(JRootApp appview) {
              m_appview = appview;
         }

          public void run() {
              if (openSerialPort(portName) != true) {
                  closeSerialPort();
                  if (openSerialPort(portName) != true) {
                      return;
                  }
              }
              try {
                  serialPort.addEventListener(new serialPortEventListener());
                  serialPort.notifyOnDataAvailable(true);
              } catch (TooManyListenersException ex) {
                  System.err.println(ex.getMessage());
              }
          }

          boolean openSerialPort(String portName) {
              Boolean foundPort = false;
              if (serialPortOpen != false) {
                  return false;
              }
              enumComm = CommPortIdentifier.getPortIdentifiers();
              while(enumComm.hasMoreElements()) {
                  serialPortId = (CommPortIdentifier) enumComm.nextElement();
                  if (portName.contentEquals(serialPortId.getName())) {
                      foundPort = true;
                      break;
                  }
              }
              if (foundPort != true) {
                  // System.out.println("Serialport not found: " + portName);
                  return false;
              }
              try {
                  serialPort = (SerialPort) serialPortId.open("open & send", 500);
              } catch (PortInUseException e) {
                  // System.out.println("Port in use");
              }
              try {
                  inputStream = serialPort.getInputStream();
              } catch (IOException e) {
                  // System.out.println("no access to InputStream");
              }
              try {
                  serialPort.addEventListener(new serialPortEventListener());
              } catch (TooManyListenersException e) {
                  // System.out.println("TooManyListenersException for Serialport");
              }
              serialPort.notifyOnDataAvailable(true);
              try {
                  serialPort.setSerialPortParams(baudrate, dataBits, stopBits, parity);
              } catch(UnsupportedCommOperationException e) {
                  // System.out.println("could not set parameter");
              }

              serialPortOpen = true;
              return true;
          }

          void closeSerialPort() {
              if ( serialPortOpen == true) {
                  serialPort.close();
                  serialPortOpen = false;
              }
          }

          public void serialPortDataAvail() {
              try {
                  int num, start;
                  String x;

                  //--- iButton String ---
                  // insert iButton:   ||||||EA00001235D65901'
                 // remove iButton: ||||||'

                  while(inputStream.available() > 0) {
                      num = inputStream.read(data, 0, data.length);

                      x = new String(data, 0, num);
                      if (x.contains("'")) {
                          //-- Data complete --
                          iButton_Code = iButton_Code + x;
                          if (iButton_Code.contains("||||||'")) {
                              //-- iButton removed --
                              m_appview.removeButton(iButton_Key);
                              iButton_Key = null;
                              iButton_Code = "";
                          } else {
                              start = iButton_Code.lastIndexOf("|");
                              if (start > 0) {
                                  //-- iButton inserted --
                                  iButton_Key = iButton_Code.substring(start+1, start+17);
                                  iButton_Code = "";
                                  m_appview.insertButton(iButton_Key);
                              }
                          }
                          iButton_Code = "";
                      } else {
                          iButton_Code = iButton_Code + x;
                      }
                  }
              } catch (IOException e) {
                      System.out.println("error reading data");
              }
          }

          private class serialPortEventListener implements SerialPortEventListener {
              public void serialEvent(SerialPortEvent event) {
                  // System.out.println("serialPortEventlistener");
                  switch (event.getEventType()) {
                  case SerialPortEvent.DATA_AVAILABLE:
                          serialPortDataAvail();
                          break;
                  case SerialPortEvent.BI:
                  case SerialPortEvent.CD:
                  case SerialPortEvent.CTS:
                  case SerialPortEvent.DSR:
                  case SerialPortEvent.FE:
                  case SerialPortEvent.OUTPUT_BUFFER_EMPTY:
                  case SerialPortEvent.PE:
                  case SerialPortEvent.RI:
                  default:
                  }
              }
          }
      }

      Perhaps there is a better way, but this was my first code in java ;-)

      2) I made also some changes to JRootApp.java:

      *** JRootApp.java ***
      ...
      import java.util.Locale;
      import java.util.regex.Matcher;
      // 24.10.2008 - MG
      import com.openbravo.pos.forms.iButtonWatch;
      ...
      ...
              // Inicializo los componentes visuales
              initComponents ();

              //- 24.10.2008 / MG
              // iButtonListener
              Runnable runnable = new iButtonWatch();
          new Thread(runnable).start();
      ...
      ...
                      jPeople.add(btn);
                  }
                  jScrollPane1.getViewport().setView(jPeople);

                  //- 24.10.2008 / MG
                  String loginmode = m_props.getLoginMode();
                  if (loginmode != null) {
                      if (loginmode.equals("iButton")) {
                          jScrollPane1.setVisible(false);
                          jLabel3.setVisible(true);
                      } else {
                          // Standard & Mixed Mode
                          jScrollPane1.setVisible(true);
                          jLabel3.setVisible(false);
                      }
                  }
      ...
      ...
              showView("login");

              // show welcome message
              printerStart();

              //-
              iButtonWatch.PutAppView(this);

              // keyboard listener activation
      //- 24.10.2008 / MG (disabled)
      //        inputtext = new StringBuffer();
      //        m_txtKeys.setText(null);
      //        java.awt.EventQueue.invokeLater(new Runnable() {
      //            public void run() {
      //                m_txtKeys.requestFocus();
      //            }
      //        });
          }
      ...
      ...
          //- 24.10.2008 / MG
          public void insertButton(String key) {
              if (key != null) {
                  AppUser user = null;
                  try {
                      user = m_dlSystem.findPeopleByCard(key);
                  } catch (BasicException e) {
                      e.printStackTrace();
                  }

                  if (user == null)  {
                      // user not found
                      MessageInf msg = new MessageInf(MessageInf.SGN_WARNING, AppLocal.getIntString("message.nocard"));
                      msg.show(this);
                  } else {
                      openAppView(user);
                  }
              }
          }

          //- 24.10.2008 / MG
          public void removeButton(String key) {
              if (key != null) {
                  closeAppView();
              }
          }

      Works perfect!

      Thx again for the help,
      Michael

       
    • Marc
      Marc
      2008-11-06

      Here is a similar patch for OpenBravo 2.20: <http://pastebin.com/f6d8e0c19>.  It should work with any Dallas 1-wire device (I used a DS9490 USB adapter).

      You also need to add the 'OneWireAPI.jar' to your netbeans project.  This can be downloaded here:
      <http://files.dalsemi.com/auto_id/public/owapi_1_10.zip>