FANDOM


hemanager.pdeEdit

/* 
 * File: hemanager.pde
 * Vers: 1.0
 * Auth: C.Wharton, 1/4/2010
 * Desc: Arduino application to manage interaction with HomeEasy devices
 *
 * Send/receive commands to/from HomeEasy devices. Interaction is managed
 * through instructons sent via the serial port. Uses the HomeEasy 'Basic'
 * protocol (compatible with the HE200 remote control). 
 *
 * Commands consist of group code A-P, unit number 1-16, action (1=On,0=Off)
 * HomeEasy commands received by the arduino are emitted out on the serial link
 *
 * Note that most of the communication code is copied directly from the scripts 
 * provided by Barnaby Gray on the Arduino playground. For more information see:
 *    http://www.arduino.cc/playground/Code/HomeEasy
 * Thanks Barnaby!
 *
 ************************************************************
 *
 * Copyright (C) 2010  Chris Wharton
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

#include "hemanager.h"


int loopLED = 13;  // LED on digital pin 13
int rxLED = 17;    // LED on analog pin A5
int txLED = 19;    // LED on analog pin A3
int rxPin = 15;    // Nano - Analog pin A1
int txPin = 3;     // Nano - Digital pin D3

#define SERIALCMDMAX  50   // max length of string from serial port


//
// User-configurable settings
//
int cnfRxReadTimeOut;     // how long to wait for a transmission to arrive
int cnfDebug;             // whether to display debug messages
int cnfUseRxLED;          // whether to flash LED when we receive a message
int cnfUseTxLED;          // whether to flash LED when we transmite a message
int cnfUseLoopLED;        // whether to flash LED each time through main loop
                          
/******************************/
// SETUP
/******************************/

void setup()
{
  pinMode(loopLED, OUTPUT);  // sets the analog pin as output
  pinMode(rxLED, OUTPUT);    // sets the analog pin as output
  pinMode(txLED, OUTPUT);    // sets the analog pin as output
  
  pinMode(rxPin, INPUT);    // setup receiver pin
  pinMode(txPin, OUTPUT);   // setup transmitter pin

  Serial.begin(9600);
  
  // Set configuration variables to the defaults
  config_init();
}


/******************************/
// CONFIGURATION MANAGEMENT
/******************************/

// Initialise configuration settings to reasonable defaults
void config_init()
{
  cnfRxReadTimeOut = 200;   // how long to wait for incoming transmissions
  cnfDebug = 0;             // show debug messages
  
  cnfUseLoopLED = 0;        // turned off by default as it gets annoying
  cnfUseRxLED = 1;          // whether to flash LED when we receive a message
  cnfUseTxLED = 1;          // whether to flash LED when we transmite a message

  if (cnfDebug) Serial.println("DBG: initial configuration complete");
}

// Change a configuration setting
void config_update(char cmd[])
{
  if (cnfDebug) Serial.println("DBG: updating config");

  // Debug flag
  if (strstr(cmd, "DEBUG=")) {
    int val = atoi(cmd+strlen("DEBUG="));
    if (cnfDebug) {
      Serial.print("DBG: setting debug flag to ");
      Serial.println(val);
    }
    cnfDebug = val;

  // Timeout for radio reads
  } else if (strstr(cmd, "RXREADTIMEOUT=")) {
    int val = atoi(cmd+strlen("RXREADTIMEOUT="));
    if (cnfDebug) {
      Serial.print("DBG: setting cnfRxReadTimeOut to ");
      Serial.println(val);
    }
    cnfRxReadTimeOut = val;

  // LED each time through main loop
  } else if (strstr(cmd, "LOOPLED=")) {
    int val = atoi(cmd+strlen("LOOPLED="));
    if (cnfDebug) {
      Serial.print("DBG: setting cnfUseLoopLED to ");
      Serial.println(val);
    }
    cnfUseLoopLED = val;

  // LED for received message
  } else if (strstr(cmd, "RXLED=")) {
    int val = atoi(cmd+strlen("RXLED="));
    if (cnfDebug) {
      Serial.print("DBG: setting cnfUseRxLED to ");
      Serial.println(val);
    }
    cnfUseRxLED = val;

  // LED for transmitted message
  } else if (strstr(cmd, "TXLED=")) {
    int val = atoi(cmd+strlen("TXLED="));
    if (cnfDebug) {
      Serial.print("DBG: setting cnfUseTxLED to ");
      Serial.println(val);
    }
    cnfUseTxLED = val;
    
  // Bad config setting
  } else {  
    Serial.print("ERR: bad config setting: ");
    Serial.println(cmd);
  }
}

// print current config settings
void config_show()
{
  Serial.print("CNF: cnfRxReadTimeOut="); Serial.println(cnfRxReadTimeOut);
  Serial.print("CNF: cnfDebug="); Serial.println(cnfDebug);
  Serial.print("CNF: cnfUseLoopLED="); Serial.println(cnfUseLoopLED);
  Serial.print("CNF: cnfUseRxLED="); Serial.println(cnfUseRxLED);
  Serial.print("CNF: cnfUseTxLED="); Serial.println(cnfUseTxLED);  
}

/******************************/
// LED OPERATIONS
/******************************/

int flashPower = 255;          // amount of PWM power to send
int flashLength = 150;         // length of flashes, in milliseconds

// Turn on an LED attached to an analog pin
void ledOn(int pinNo)
{
  analogWrite(pinNo, flashPower);    // turn the LED on
}
// Turn off an LED attached to an analog pin
void ledOff(int pinNo)
{
  analogWrite(pinNo, 0);             // turn the LED off
}
// Turn an LED on and then off again
void ledBlink(int pinNo)
{
  ledOn(pinNo);         // turn the LED on
  delay(flashLength);   // wait for a 1/2 second
  ledOff(pinNo);        // turn the LED off
}

/******************************/
// SUPPORT FUNCTIONS
/******************************/

// Convert an integer to a string of 1's and 0's in LSB format
// NB: str must have size len+1 to allow for NULL terminator
void intToStringLSB(char *str, int val, int len)
{
  for (int i=0 ; i<len ; i++) {
    int v = (val >> i) & 1;
    if (v == 1) str[i] = '1'; else str[i] = '0';
  }
  str[len] = '\0';
}

/******************************/
// RECEIVER
/******************************/

boolean readBit()
{
  unsigned long t;
  do {
    t = pulseIn(rxPin, HIGH);
  } while (t < 300 || t > 1500);
  return t > 750;
}

void latch()
{
  boolean b = readBit();
  while (!b) {
    b = readBit();
  }
}

// Read a packet
int readPacket (RxDataClass *d)
{
  int i = 0;
  unsigned long t;
  unsigned int data = 0;
  int rxReadCount = 0;

  while (i < 25 && rxReadCount++ < cnfRxReadTimeOut) {
    // wait for first pulse to latch
    t = pulseIn(rxPin, HIGH, 1500);
    // pulses are expected to be either 375us or 1125us, but there's
    // some tolerance here. 
    if (t < 250 || t > 1250) {
      // pulse timing off or timeout - reset
      //Serial.println("pulse timing off or timeout - resetting");
      i = 0;
      data = 0;
      continue;
    }
    //Serial.println("got first pulse");

    // Even numbered bits are always 0, and can be ignored
    if (i % 2 == 0) {
      // If we got a 1 then something went wrong, so start again
      if (t > 400) {
        // should be zero pulses
        i = 0; data = 0;
        continue;
      }
    // Odd numbered bits contain actual data
    } else {
      // Add a 1 or 0 to the LSB
      data = (data>>1) + (t > 400 ? 0x800 : 0);
    }
    ++i;
  }

  // if finished 'cnfRxReadTimeOut' checks without getting
  // a full packet then return now
  if (i < 25 && rxReadCount >= cnfRxReadTimeOut) return 0;

  // convert data read
  d->group = (data & 15) + 65;        // group is first 4 bits, converted to ASCII range A-P
  d->unit  = ((data >> 4) & 15)+1;    // unit is second 4 bits, with 1 added to give range 1-16
  d->cmd   = (data >> 8) & 15;        // command is third 4 bits, either 0110 (off) or 0111 (on)

  // store converted values
  strcpy(d->cmdStr, (d->cmd == 14 ? "ON " : (d->cmd == 6 ? "OFF" : "UNK")));
  // save bit values to a string
  intToStringLSB(d->raw, data, 25);
  
  // blink receive LED once to indicate a message received
  if (cnfUseRxLED) ledBlink(rxLED);
  
  return 1;
}

/******************************/
// TRANSMITTER
/******************************/

// Sends a single bit
void sendBit(boolean b) {
  if (b) {
    digitalWrite(txPin, HIGH);
    delayMicroseconds(1125);
    digitalWrite(txPin, LOW);
    delayMicroseconds(375);
  }
  else {
    digitalWrite(txPin, HIGH);
    delayMicroseconds(375);
    digitalWrite(txPin, LOW);
    delayMicroseconds(1125);
  }
}

// Sends a single bit as a pair - the first bit is always 0, and is ignored
void sendPair(boolean b) {
  sendBit(false);
  sendBit(b);
}

// Send an array of bits
void sendBitString(char data[])
{
  if (cnfDebug) Serial.print("sendBitString: [");
  // loop through array, checking each bit
  for (int i=0 ; i<25 ; i++) {
    if (data[i] == '0') sendPair(false);
    else if (data[i] == '1') sendPair(true);
    if (cnfDebug) Serial.print(data[i]);
  }
  if (cnfDebug) Serial.println("]");
}

// Send a raw message, i.e. just a bunch of bits
// Its up the the user to make sure the data conforms to the spec
void sendRawCommand(char data[]) {
  
  // Display message being sent
  if (cnfDebug) {
    Serial.print("RAW:");  
    Serial.println(data); 
  }
  
  // Send it 3 times, as per the spec, with a 10 msec delay
  sendBitString(data);
  delayMicroseconds(10000);
  sendBitString(data);
  delayMicroseconds(10000);
  sendBitString(data);
  
  // blink transmit LED once to indicate a message sent
  if (cnfUseTxLED) ledBlink(txLED);
}

//
// Send a HomeEasy command. This should be of the form 'gUUa', where:
//    g is group character ('A' - 'P')
//   UU is unit number ('00' to '16', with leading zero)
//    a is action, '1' for ON and '0' for OFF
// We parse the command into a bit string, then send this as a raw command
//
void sendHECommand(char data[]) {
  char raw[26];  // holds 25 command bits plus null-terminator

  // Group must be A to P, which maps to 0 to 15
  int group = (int)data[0]-65;
  if (cnfDebug) {  
    Serial.print("DBG: group is ");  
    Serial.println(group);
  }
  if (group < 0 || group > 15) {
    Serial.print("ERR: command has invalid group: ");  
    Serial.println(data[0]);
    return;
  }
  // Add to raw string as the first 4 bits
  intToStringLSB(raw, group, 4);

  // Unit can be 01 to 16, and must have a leading zero
  int unit = (int)data[2]-49;
  if (data[1] == '1') unit += 10;
  else if (data[1] == '0')  unit = unit; // dummy statement as '0' is also ok
  else {
    Serial.print("ERR: command has bad tens digit: ");  
    Serial.print(data[1]);
    Serial.println(data[2]);
    return;
  }
  if (unit < 0 || unit > 15) {
    Serial.print("ERR: command has invalid unit: ");  
    Serial.print(data[1]);
    Serial.println(data[2]);
    return;
  }  
  if (cnfDebug) {  
    Serial.print("DBG: unit is ");  
    Serial.println(unit);
  }
  // Add to raw string as the second 4 bits
  intToStringLSB(&(raw[4]), unit, 4);
  
  // The on/off action is 0 or 1
  int action = (int)data[3]-48;
  if (action < 0 || action > 1) {
    Serial.print("ERR: command has invalid action: ");  
    Serial.println(data[3]);
    return;
  }
  if (cnfDebug) {  
    Serial.print("DBG: action is ");  
    Serial.println(action);
  }
  // Encoding of action always has a constant leading 011, ie. so final value will be 6 or 14 decimal
  action = (action*8)+6;
  // Add to raw command as the third 4 bits
  intToStringLSB(&(raw[8]), action, 4);
  
  // The remaining 13 bytes are unused and are all set to 0
  intToStringLSB(&(raw[12]), 0, 13);

  // Display constructed raw command
  if (cnfDebug) {  
    Serial.print("DBG: constructed bit string is: ");  
    Serial.println(raw);  
  }
  
  // Send the raw command  
  sendRawCommand(raw);

  // Write command to serial line
  Serial.print("CMD:");
  Serial.println(data);
}


/******************************/
// MAIN
/******************************/

int ledFlag = 0;

void loop()
{
  int incomingByte = 0;	// for incoming serial data
  RxDataClass rxData;
  
  if (cnfDebug) Serial.println("DBG: Start loop");
  
  // toggle LED each time through the loop
  if (cnfUseLoopLED && ledFlag == 0) {
    ledOn(loopLED);
    ledFlag = 1;
  } else {
    ledOff(loopLED);
    ledFlag = 0;
  }

  /* 
  // TESTING: send a RAW ON then OFF message to (Group A, Unit 1), flashing LED in between
  ledBlink(rxLED);
  sendRawCommand("0000000001100000000000000");
  ledBlink(txLED);
  sendRawCommand("0000000001110000000000000");
  ledBlink(rxLED);
  // TESTING: send a CMD ON then OFF message to (Group A, Unit 1), flashing LED in between
  ledBlink(txLED);
  sendHECommand("A011");
  ledBlink(rxLED);
  sendHECommand("A011");
  ledBlink(txLED);
  */

  // Try and read incoming HE message
  if (readPacket(&rxData)) {
    if (cnfDebug) Serial.println("DBG: RX data received!");
    Serial.print("RXD:");
    Serial.print(rxData.group);
    Serial.print("|");
    if (rxData.unit < 10) Serial.print('0');  // leading zero
    Serial.print(rxData.unit);
    Serial.print("|");
    Serial.print(rxData.cmdStr);
    Serial.print("|");
    Serial.println(rxData.raw);
  } else {
    if (cnfDebug) Serial.println("DBG: No rx data found");
  }
  
  // See if a serial message is available
  if (Serial.available() > 0) {
 
    //
    // Get the message
    //  
    // wait a bit for the entire message to arrive
    delay(100);    
    // read all the available characters up to a newline
    int byteCounter = 0;
    char byteBuffer[SERIALCMDMAX];
    incomingByte = Serial.read();
    while (incomingByte != '\n' && byteCounter<SERIALCMDMAX-1) {
      byteBuffer[byteCounter++] = incomingByte;
      // read the next byte
      if (Serial.available() > 0) { incomingByte = Serial.read(); }
      else { incomingByte = '\n'; }
    }
    byteBuffer[byteCounter] = '\0';
    // show what was received
    if (cnfDebug) {
      Serial.print("DBG: Serial command received: ");
      Serial.println(byteBuffer);
    }
    
    //
    // Process the message
    //
    // Parse and send a HomeEasy on/off command
    if (strstr(byteBuffer, "CMD:")) {
      if (cnfDebug) Serial.println("DBG: sending HE command");
      sendHECommand(byteBuffer+4);

    // Send a raw command, ie. 25 bits provided by the end-user
    } else if (strstr(byteBuffer, "RAW:")) {
      if (cnfDebug) Serial.println("DBG: sending RAW command");
      sendRawCommand(byteBuffer+4);
    
    // Print out current configuration
    } else if (!strcmp(byteBuffer, "CNF")) {
      config_show();
      
    // Update a configuration setting
    } else if (strstr(byteBuffer, "CNF:")) {
      config_update(byteBuffer+4);
    
    // Unknown command received
    } else {
      Serial.print("ERR: unknown command: ");
      Serial.println(byteBuffer);
    }
  }
  
}


hemanager.hEdit

/*
hemanager.h - Arduino sketch to manage interaction with HomeEasy devices
Copyright (C) 2010  Chris Wharton

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

typedef struct {
  char group;
  int unit;
  int cmd;
  char cmdStr[4];
  char raw[26];

} RxDataClass;

int readPacket (RxDataClass *d);


helink.pdeEdit

#!/usr/bin/perl
#
# File: helink.pl
# Vers: 1.0
# Auth: C.Wharton, 1/4/2010
# Desc: Listens on a UDP port for commands to send to the serial port,
#       and logs data received from the serial port to a logfile.
#
#       Used to provide an interface between a Arduino device
#		sending/receiving wireless HomeEasy commands, and scripts
#		running on a server.
#
############################################################
#
# Copyright (C) 2010  Chris Wharton
# 
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
# 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.
#
############################################################


use strict;
use IO::Socket;
use IO::Select;
use Net::hostent;
use Time::HiRes qw{usleep};

my $UDPPortNumber = 7070;
my $WinPortName = "COM8";
my $WinLogFile = 'C:\TEMP\helink.log';
my $LinuxPortName = "/dev/ttyUSB0";
my $LinuxLogFile = "/var/log/helink.log";

my $MaxLogLines = 100;   # max number of lines to write before clearing

my $verbose = 0;


#------------------------------------------------------------
# SERIAL PORT SETUP
#------------------------------------------------------------

my $portObj;
my $portName;

sub serial_port_open
{
	if ($^O =~ m/Win32/) {
		require Win32::SerialPort;
		$portName = $WinPortName;
		$portObj = new Win32::SerialPort ($portName);
		if (! $portObj) {
			print "[ERROR: cannot open $WinPortName: $!]\n";
			return 0;
		}
	}
	else {
		require Device::SerialPort;
		$portName = $LinuxPortName;
		$portObj = new Device::SerialPort ($portName);
		if (! $portObj) {
			print "[ERROR: cannot open $LinuxPortName: $!]\n";
			return 0;
		}
	}

	$portObj->user_msg(1);
	$portObj->databits(8);
	$portObj->baudrate(9600);
	$portObj->parity("none");
	$portObj->stopbits(1);
	$portObj->handshake("none");   # "none", "rts", "xoff", "dtr".
	$portObj->buffers(4096, 4096);
	$portObj->write_settings || undef $portObj;

	print "[SerialPort $portName connected and configured]\n";
	#$portObj->write("CNF:DEBUG=1\n");
	return 1;
}

#------------------------------------------------------------
# UDP PORT SETUP
#------------------------------------------------------------

my $hndl;
my $server;

sub udp_port_open 
{
	$server = IO::Socket::INET->new(
				Proto     => 'udp',
				LocalPort => $UDPPortNumber,
				Blocking  => 0)
		or die "Cannot listen on UDP port $UDPPortNumber: $@";

	# Register socket for select() calls
	$hndl = IO::Select->new();
	$hndl->add($server);
	print "[Listening on UDP port $UDPPortNumber]\n";
}

sub udp_port_read {
    my @ready = $hndl->can_read(0.1); # wait 1/10 second
	my $bytes_read = 0;
	my $buf;
    foreach my $sock (@ready) {
        $bytes_read = sysread($server, $buf, 1500);
    }
	return $buf;
}

#------------------------------------------------------------
# LOGGING SETUP
#------------------------------------------------------------

my $logfile;
my $logLines;

sub log_open {
	if ($^O =~ m/Win32/) {
		$logfile = $WinLogFile;
	} else {
		$logfile = $LinuxLogFile;
	}

	$| = 1;  # flush output immediately
	open(LOG, "> $logfile") || die "cannot write $logfile: $!";
	LOG->autoflush(1);
	$logLines = 0;
}

sub log_close {
	close(LOG);
}

sub logmsg {
	my @msg = @_;
	if ($logLines++ > $MaxLogLines) {
		print "[Logfile exceeds $MaxLogLines lines, clearing]\n";
		log_close();
		log_open();
	}
	# Write log entry with leading timestamp
	my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime();
	printf LOG "%02d%02d%02d %02d%02d%02d| @msg",
		$year+1900, $mon+1, $mday, $hour, $min, $sec;
}


#------------------------------------------------------------
# MAIN
#------------------------------------------------------------

# Setup
log_open();
my $portOpen = serial_port_open();
udp_port_open();

while (1) {

	# Check the port is still active
	my ($BlockingFlags, $InBytes, $OutBytes, $LatchErrorFlags);
	if ($portOpen) {
		($BlockingFlags, $InBytes, $OutBytes, $LatchErrorFlags)
			= $portObj->status
			|| warn "WARN: could not get serial port status\n";
	}
	# Serial port has been lost, wait 5 seconds then try and recreate it
	if ($BlockingFlags == 1 || ! $portOpen) {
		print "[ERROR: serial port $portName unavailble, reconnecting...]\n";
		sleep(5);
		$portOpen = serial_port_open();
		next;
	}

	# Poll to see if any data is coming in from serial port
	$verbose && print "[Check for serial data]\n";
	my $char = $portObj->lookfor();
	$char =~ s/[\n\r]+//;  # remove trailing newline/carriage return
	# If we get data, then print it
	while ($char) {
		$verbose && print "[Serial data received: $char]\n";
		logmsg "$char\n";
		$char = $portObj->lookfor();
		$char =~ s/[\n\r]+//;  # remove trailing newline/carriage return
	}

	# See if any UDP commands are available
	my $udpdata = udp_port_read();
    if (length($udpdata)) {
		print "[UDP data received: $udpdata]\n";
		$portObj->write($udpdata);
    } else {
		$verbose && print "[UDP data not found]\n";
    }

	# Pause 1/2 second between passes to reduce CPU usage
	usleep (500000);
}

log_close();


hesend.pdeEdit

#!/usr/bin/perl
#
# File: hesend.pl
# Vers: 1.0
# Auth: C.Wharton
# Desc: Sends HomEasy control commands via UDP to the helink server
#
############################################################
#
# Copyright (C) 2010  Chris Wharton
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.
#
############################################################


use strict;
use IO::Socket;
use Getopt::Long;
use English;

my $LISTEN_PORT = 7070;

my $usage = q{
Usage: hesend [-cmd gUUf] [-raw 00001111000011110000111] [-verbose]
Where options are:
  -cmd gUUf   Send ON (f=1) or OFF (f=0) to HE Group 'g', Unit 'UU'
  -raw bb..   Send raw command of 25 bits
  -cnf dump   Request current configuration
  -cnf {str}  Change current configuration
  -verbose    Show what is sent

};


# Connect the UDP socket
$|++;  # make non-buffered IO
my $client = IO::Socket::INET->new(
				Proto    => "udp",
				PeerPort => $LISTEN_PORT,
				PeerAddr => "localhost") 
  or die "Can't open UDP socket: $@";

# Get command line arguments
my %Options = ();
my $argsok = GetOptions(\%Options, "cmd=s", "raw=s", "cnf:s", "verbose");
die $usage unless $argsok;

# Process arguments
my $temp;
my $pkt;
# Send a command
if ($temp = $Options{"cmd"}) {
	if ($temp !~ m/^[A-P][0,1][0-9][0,1]$/) {
		die "Bad cmd, must be gUUf, eg. C131 is send ON to Group C, Unit 13\n";
	}
	$pkt = "CMD:$temp";

# Send a raw bit string
} elsif ($temp = $Options{"raw"}) {
	$temp = $Options{"raw"};
	if ($temp !~ m/^[0,1]{25}$/) {
		die "Bad raw string, must be 25 bits\n";
	}
	$pkt = "RAW:$temp";
 
# Display or change configuration
} elsif ($temp = $Options{"cnf"}) {
	if ($temp eq "dump") { $pkt = "CNF"; }
	else { $pkt = "CNF:$temp"; }
 
} else {
	die $usage;
}

# Send the message
$Options{'verbose'} && print "Sending to UDP $LISTEN_PORT: $pkt\n";
$client->send($pkt);

Ad blocker interference detected!


Wikia is a free-to-use site that makes money from advertising. We have a modified experience for viewers using ad blockers

Wikia is not accessible if you’ve made further modifications. Remove the custom ad blocker rule(s) and the page will load as expected.