$SCRDR over UDP

When the SpringCore device is configured for Smart Reader mode + $SCRDR over UDP, it sends $SCRDR Messages to a remote UDP server, and opens a local UDP port to accept $SCCMD Messages.

Configuration checklist

  • Define the IPv4 configuration of the device in register 0280. Leave this register empty to get the configuration from DHCP.
  • Set register 02C0 to 44 (UDP server, Smart Reader operation)
  • Set register 02A0 to 00xxxx01xx (SCRDR format over the Network link)
  • Insert the name or the address of the host into register 0294. Leave empty or set to 255.255.255.255 for broadcast.
  • (Optionaly) define the options in register 0295. Check that the $SCCMD Message comes from the expected source to increase the security of the system.
  • (Optionaly) define the Port for UDP and TCP servers in register 0285. Default port is 4000. Host and device must use the same port.

Sample host software

Use the following Python scripts as a reference for using the Smart Reader with configuration $SCRDR over UDP.

Listener

The Listener script is an UDP server, running on the host, that receives and shows any text message coming on port 4000.

NB: since this sample script doesn’t do any processing of the message but only displays it, it is suitable for both $SCRDR and JSON formats.

# libraries import
from threading import Thread, Event
import logging
import socket
import getopt
import sys
import os


# helpers import
from helpers.ctrlccatcher import CtrlCCatcher


# display_help function
def display_help( script_name ):

    print( script_name )
    print( "[--port=<port>]                 UDP port to listen to (default: 4000)" )
    sys.exit()


# main function  
def main( script_name, argv ):
    
    # default settings
    port = 4000

    # options parsing
    try:
        opts, _ = getopt.getopt(argv,"h",[ "help", "port=" ])
    except getopt.GetoptError as e:
        print(e)
        display_help( script_name )

    for opt, arg in opts:
        if opt in ( "-h", "--help"):
            display_help( script_name )
        elif opt == "--port":
            try:
                port = int(arg)
            except ValueError:
                print( "Port must be an integer number!" )
                sys.exit()

    # set logging options
    logging.basicConfig(level=logging.DEBUG)

    # CTRL+C catcher thread (this could be useful on some Windows platforms)
    ctrlccatch = CtrlCCatcher()
    ctrlccatch.start()

    # Create an UDP listening socket
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    sock.setblocking(0)
    sock.settimeout(0.5)

    # Bind the socket to the port
    server_address = ("0.0.0.0", port)
    logging.info( f"starting up on {server_address} port {port}" )
    sock.bind(server_address)

    # Messages are read from the socket using recvfrom(), which returns the data as well as the address of the client from which it was sent.
    logging.info( "waiting for messages" )
    while ctrlccatch.wait():
        
        try:
            data, address = sock.recvfrom(4096)        
            if data:
                data = data.decode( "ascii" )
                logging.info( data )
        except socket.timeout:
            pass

    # stop all process and exit
    logging.info( "Cleaning..." )
    ctrlccatch.terminate()
    ctrlccatch.join()
    logging.info( "Done." )


# entry point
if __name__ == "__main__":
    # add "this script" folder to the library search path
    # could be useful on Windows platform
    sys.path.append( os.path.dirname( os.path.realpath( __file__ ) ) )
  
    # start our application
    main( os.path.basename( __file__ ), sys.argv[1:] )
    
# EOF

Sender

The Sender script is an UDP client, running on the host, that sends a $SCCMD Message to a target device, or broadcasts the Message.

For demonstration purpose, we send the $SCCMD;SEQ:0E message to play the Wink sequence.

Change message to $SCCMD;SEQ=60 for the Access Granted sequence or $SCCMD;SEQ=61 for Access Denied. For a complete list of sequences, refer to the PLAY SEQUENCE instruction.

# libraries import
from threading import Thread, Event
import socket
import getopt
import sys
import os


# helpers import
from helpers.ctrlccatcher import CtrlCCatcher


# display_help function
def display_help( script_name ):

    print( script_name )
    print( "[--addr=<addr>]                 Address of the target device (default: broadcast)" )
    print( "[--port=<port>]                 UDP port on the target device (default: 4000)" )
    print( "[--message=\"<message>\"]       Message to send to the device (default: WINK Sequence)" )
    sys.exit()


# main function  
def main( script_name, argv ):
    
    # default settings
    message = b"$SCCMD;SEQ=0E"
    addr = '<broadcast>'
    port = 4000

    # options parsing
    try:
        opts, _ = getopt.getopt(argv,"h",[ "help", "addr=", "port=", "message=" ])
    except getopt.GetoptError as e:
        print(e)
        display_help( script_name )

    for opt, arg in opts:
        if opt in ( "-h", "--help"):
            display_help( script_name )
        elif opt == "--message":
            message = bytearray(arg.encode())
        elif opt == "--addr":
            addr = str(arg)
        elif opt == "--port":
            try:
                port = int(arg)
            except ValueError:
                print( "Port must be an integer number!" )
                sys.exit()

    # Create an UDP socket
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
    # Enable broadcasting mode
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
    # Send the message to the target address and port
    sock.sendto(message, (addr, port))

# entry point
if __name__ == "__main__":
    # add "this script" folder to the library search path
    # could be useful on Windows platform
    sys.path.append( os.path.dirname( os.path.realpath( __file__ ) ) )
  
    # start our application
    main( os.path.basename( __file__ ), sys.argv[1:] )
    
# EOF