Shop OBEX P1 Docs P2 Docs Learn Events
For Review: Wiegand Reader Object — Parallax Forums

For Review: Wiegand Reader Object

JonnyMacJonnyMac Posts: 9,102
edited 2018-11-26 03:05 in Propeller 1
A friend wants to add a commercial RFID reader / keypad with Wiegand output to a project so I thought it would be fun to write a driver for him. It does work (I've compared to a Proxmark3) but I'm always happy to get a code review in case I looked past something in the stress of trying to get everything to work.

Thanks for any feedback you wish to offer ( on the code -- not on me! <grin> )

JonnyMac

Comments

  • kwinnkwinn Posts: 8,697
    Don't seem to be able to view or download the file. Here is my Weigand code from 2015 in case you are interested.
  • JonnyMacJonnyMac Posts: 9,102
    edited 2018-11-26 17:21
    That's odd -- I uploaded it with one computer, and downloaded it to another. Here it is -- current state -- inline.

    Updated 2018-11-26 for Proxmark-compatible hex string output
    '' =================================================================================================
    ''
    ''   File....... jm_wiegand_reader.spin
    ''   Purpose....
    ''   Author..... Jon "JonnyMac" McPhalen
    ''               -- see below for terms of use
    ''   E-mail.....
    ''   Started....
    ''   Updated.... 26 NOV 2018
    ''
    '' =================================================================================================
    
    {{
    
        Connections:
                                  Reader
                                  --------------
          d0pin <-----[4.7K]----- DAT0   (green)
          d1pin <-----[4.7K]----- DAT1   (white)
          Vss   <---------------- Ground (black)
    
    }}
    
    
    con { fixed io pins }
    
      RX1  = 31  { I }                                              ' serial / programming
      TX1  = 30  { O }
    
      SDA1 = 29  { I/O }                                            ' i2c / eeprom
      SCL1 = 28  { I/O }
    
    
    con { string storage }
    
      MAX_BIN = 64
      MAX_HEX = 20
    
    
    var
    
      long  cog                                                     ' cog running driver
    
      long  bitcount                                                ' # bits in last capture
      long  wiegand0                                                ' space for 64 bits
      long  wiegand1
    
    
    dat
    
      BinDigits     byte    0[MAX_BIN]                              ' reserve string space
      HexDigits     byte    0[MAX_HEX]
    
      HexTable      byte    "0123456789ABCDEF", 0
    
    
    pub start(d0pin, d1pin, toms)
    
    '' Start Wiegand reader cog
    '' -- d0pin is DAT0 input (through 4.7K resistor)
    '' -- d1pin is DAT1 input (through 4.7K resistor)
    '' -- toms is timeout in milliseconds
    
      stop                                                          ' stop if running
    
      bitcount.byte[0] := d0pin                                     ' compress pins 
      bitcount.byte[1] := d1pin
    
      wiegand0 := clkfreq / 1000 * toms                             ' covert timeout millis to ticks
    
      cog := cognew(@entry, @bitcount) + 1                          ' launch the cog
      if (cog)                                                      ' if successful
        repeat while (bitcount <> -1)                               '  let cog initialize
    
      return cog
    
    
    pub stop
    
    '' Stop Wiegand reader cog
    
      if (cog)                                                      ' running?
        cogstop(cog-1)                                              '  yes, stop
        cog := 0                                                    '  and mark stopped
    
    
    pub count
    
    '' Return bit count from last read
    
      return bitcount
    
    
    pub ready
    
    '' Return true after successful read
    
      return (bitcount > 0)
    
    
    pub enable
    
      longfill(@wiegand0, 0, 2)                                    ' clear old bits
      bitcount := 0                                                 ' clear the count, trigger bg cog
    
    
    pub raw(part) | idx
    
    '' Returns raw bits from last read
    
      if (part == 0)
        return wiegand0
      else
        return wiegand1
    
    
    pub bin_str | p_str, bitz, mask
    
    '' Returns binary string representation of raw bits
    '' -- first bit in is first character in string
    ''    * matches format charts which show bits left-to-right
    
      bytefill(@BinDigits, 0, MAX_BIN)                              ' clear old string
      p_str := @BinDigits                                           ' point to it
    
      bitz := bitcount                                              ' make copy of count
    
      if (bitz > 32)                                                ' get high bits (if present)
        mask := 1 << (bitz-33)
        repeat (bitz-32)
          if (mask & wiegand1)
            byte[p_str++] := "1"
          else
            byte[p_str++] := "0"
          mask >>= 1
        bitz := 32
    
      mask := 1 << (bitz-1)                                         ' get low bits
      repeat bitz
        if (mask & wiegand0)
          byte[p_str++] := "1"
        else
          byte[p_str++] := "0"
        mask >>= 1
    
      return @BinDigits
    
    
    dat { proxmark preable bits }
    
    ' adapted from:
    ' -- https://github.com/linklayer/BLEKey/blob/master/wiegand.c
    
      ProxPre       word    $000, $000, $000, $000, $000, $000      ' 44..39
                    word    $000, $000, $003, $005, $009, $011      ' 38..33
                    word    $021, $041, $081, $101, $201, $401      ' 32..27
                    word    $801                                    ' 26   
    
    
    pub hex_str | wbits[2], pre, p_str, idx
    
    '' Returns Proxmark-compatible hex string representation of raw bits
    '' -- always 5 bytes (10 hex digits)
    
      longmove(@wbits, @wiegand0, 2)                                ' copy (for preamble)
    
      if ((bitcount => 26) and (bitcount =< 44))                    ' HID card?
        pre := ProxPre[44-bitcount]                                 ' get preable from table
        if (bitcount => 32)
          wbits[1] |= pre << (bitcount-32)
        else
          wbits[1] |= pre >> (32-bitcount)
          wbits[0] |= pre << bitcount
    
      bytefill(@HexDigits, 0, MAX_HEX)                              ' clear old string
      p_str := @HexDigits                                           ' point to it
    
      repeat idx from 4 to 0
        byte[p_str++] := HexTable[wbits.byte[idx] >> 4]             ' high nib
        byte[p_str++] := HexTable[wbits.byte[idx] & $F]             ' low nib
    
      return @HexDigits
    
    
    dat { card info }
    
      CardInfo    byte      26, 24, 17, 16,  1
                  byte      35, 32, 21, 20,  1
                  byte      37, 35, 20, 19,  1
                  byte      99, 99, 99, 99, 99                      ' end of table marker
    
    
    pub extract_card(p_fc, p_cc) | p_table, bc, bounds              ' not tested
    
    '' Extract facility and card values from stream
    '' -- p_fc and p_cc are pointers to facility code and card code (longs) 
    '' -- assumes good data from reader
    '' -- no parity checking
    
      p_table := @CardInfo                                          ' point to start of table
    
      repeat
        bc := byte[p_table]
        if (bc == bitcount)                                         ' found it
          bytemove(@bounds, ++p_table, 4)                           ' read boundaries
          long[p_fc] := extract_bits(bounds.byte[0], bounds.byte[1]) 
          long[p_cc] := extract_bits(bounds.byte[2], bounds.byte[3])
          return
        elseif (bc > MAX_BIN)                                       ' end of table?
          long[p_fc] := 0  
          long[p_cc] := 0
          return
        else
          p_table += 5                                              ' next line in table     
    
    
    pub key_code
    
    '' Returns ASCII char of key pressed
    '' -- for readers with 3x4 kepad
    
      if (bitcount <> 4)                                            ' validate bit count
        return "?"
    
      result := extract_bits(3, 0)                                  ' get the key code
    
      case result                                                   ' convert to ASCII
        0..9  : result += "0"
        10    : result := "*"
        11    : result := "#"
        other : result := "?"
    
    
    pri extract_bits(msb, lsb) | mask
    
    '' Extract bits from wiegand stream
    
      if (msb < 32)
        result := wiegand0 << (31-msb) >> (31+lsb-msb)
    
      else
        result := wiegand1 << (32-(msb-31)) >> (lsb-(msb-31))
        result |= wiegand0 >> lsb
    
    
    dat { wiegand receiver }
    
                            org     0
    
    entry                   mov     t1, par                         ' hub address of parameters
    
                            rdlong  t2, t1                          ' read pins 
                            mov     t3, t2                          ' make a copy
    
                            add     t1, #4                          ' bump pointer
                            rdlong  totix, t1                       ' read timeout ticks
    
                            and     t2, #$3F                        ' isolate dat0 pin
                            mov     d0mask, #1                      ' convert to mask
                            shl     d0mask, t2
                            andn    dira, d0mask
    
                            mov     t2, t3                          ' restore t2 for DAT1
                            shr     t2, #8                          ' get byte1
                            and     t2, #$3F                        ' isolate dat1 pin
                            mov     d1mask, #1                      ' convert to mask
                            shl     d1mask, t2
                            andn    dira, d1mask
    
                            mov     dxmask, d0mask                  ' off mask (both pins)
                            or      dxmask, d1mask
    
                            mov     ctra, FREE_RUN                  ' configure timeout timer
                            mov     frqa, #1
                            mov     phsa, #0                        ' reset timeout
    
                            mov     t1, par                         ' alert hub we're ready
                            add     t1, #4
                            mov     t2, #0
                            wrlong  t2, t1
                            add     t1, #4
                            wrlong  t2, t1
                            neg     t2, #1
                            wrlong  t2, par
    
    
    trigger_hold            rdlong  t1, par                         ' wait for hub bitcount == 0
                            tjnz    t1, #trigger_hold
    
                            mov     bcount, #0
                            mov     wbitz0, #0
                            mov     wbitz1, #0
    
    check_timeout           tjz     bcount, #check_bit0             ' skip timeout if no bits yet
    
                            cmp     totix, phsa             wc, wz  ' check timout
            if_a            jmp     #check_bit0                     ' okay, escape
    
    report_bits             mov     t1, par
                            add     t1, #4
                            wrlong  wbitz0, t1
                            add     t1, #4
                            wrlong  wbitz1, t1
                            wrlong  bcount, par
                            jmp     #trigger_hold                   ' back to top
    
    check_bit0              test    d0mask, ina             wc      ' move DAT0 status to C
            if_c            jmp     #check_bit1                     ' if idle, no bit
                            mov     t1, #0                          ' else bit value is 0
                            jmp     #collect_bit
    
    check_bit1              test    d1mask, ina             wc      ' move DAT1 status to C
            if_c            jmp     #check_timeout
                            mov     t1, #1
    
    collect_bit             shl     wbitz0, #1              wc
                            or      wbitz0, t1
                            shl     wbitz1, #1
                            muxc    wbitz1, #1
                            add     bcount, #1                      ' inc bit count
                            mov     phsa, #0                        ' reset timeout
    
    wait_idle               mov     t1, ina                         ' snapshot ina
                            and     t1, dxmask                      ' isolate d0 and d1
                            cmp     t1, dxmask              wc, wz  ' check for both idle
            if_ne           jmp     #wait_idle
    
                            jmp     #check_timeout                  ' back to top
    
    
    ' -------------------------------------------------------------------------------------------------
    
    FREE_RUN                long    %11111 << 26                    ' increment every clock
    
    d0mask                  res     1                               ' pin mask for DAT0
    d1mask                  res     1                               ' pin mask for DAT1
    dxmask                  res     1                               ' mask for both pins
    totix                   res     1                               ' ticks in timeout period
    phub                    res     1                               ' pointer to bitbuf[0]
    
    hub                     res     1                               ' working hub pointer
    bcount                  res     1                               ' current bit count
    wbitz0                  res     1                               ' low 32 bits
    wbitz1                  res     1                               ' high 32 bits
    
    t1                      res     1                               ' work vars
    t2                      res     1
    t3                      res     1
    
                            fit     496
    
    
    dat { license }
    
    {{
    
      Terms of Use: MIT License
    
      Permission is hereby granted, free of charge, to any person obtaining a copy of this
      software and associated documentation files (the "Software"), to deal in the Software
      without restriction, including without limitation the rights to use, copy, modify,
      merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
      permit persons to whom the Software is furnished to do so, subject to the following
      conditions:
    
      The above copyright notice and this permission notice shall be included in all copies
      or substantial portions of the Software.
    
      THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
      INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
      PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
      HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
      CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
      OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    
    }}
    
  • kwinn wrote: »
    Don't seem to be able to view or download the file. Here is my Weigand code from 2015 in case you are interested.
    Strange. I can download and open Jon's file.
  • kwinnkwinn Posts: 8,697
    Publison wrote: »
    kwinn wrote: »
    Don't seem to be able to view or download the file. Here is my Weigand code from 2015 in case you are interested.
    Strange. I can download and open Jon's file.

    Even stranger, I just tried to download it again so I could take a screen shot of the error message and it downloaded perfectly.
Sign In or Register to comment.