Code covered by the BSD License  

Highlights from
MATLAB Support Package for Parallax BASIC Stamp

image thumbnail

MATLAB Support Package for Parallax BASIC Stamp

by

 

25 Jul 2011 (Updated )

MATLAB library for communicating with a BASIC Stamp 2 and Board of Education (BoE) kit

stamp.BasicStamp
% bs = stamp.BasicStamp(comport)
%   Creates a stamp.BasicStamp object bs for a BASIC Stamp 2 connected to
%   serial port comport. Requires that the provided firmware is running on 
%   the BasicStamp and that the BasicStamp is connected to the computer 
%   through a serial port (either RS-232 or USB). If object creation is 
%   successful, use the syntax bs.methodName to access the BasicStamp's 
%   functionality.
%
% bs = stamp.BasicStamp('test')
%   Creates a stamp.BasicStamp object bs running in test mode. A BasicStamp
%   object uses a virtual serial port that doesn't try to access any
%   hardware. Use test mode to test out code or to try out the BasicStamp 
%   class without affecting any connected BASIC Stamp devices.

%   MATLAB Support Package for BASIC Stamp
%   Version 1.0
%   Copyright 2011 The MathWorks, Inc.

classdef BasicStamp < handle

    properties(Access = private)
        TestMode %Is the class in test mode?
        SerialPort %Serial port connected to the BASIC Stamp
    end %end properties
    
    properties(Access = private) %class aliases
        
        NUMINPUTS = 6 % Number of inputs STAMP expects (includes command and pin)
        
        
        %BASIC Stamp serial commands        
        TEST = 0    % Test command
        GETIO = 1   % Get IO state command
        SETIO = 2   % Set IO state command
        READI = 3   % Read pin state command
        WRITEO = 4  % Write to pin command
        PULSIN = 5  % Read pulse length command
        PULSOUT = 6 % Output pulse command
        RCTIME = 7  % Measure RC charge time command
        PWM = 8     % Output PWM/analog command
        COUNT = 9   % Count state transisions command
        FREQOUT = 10% Output frequency command
        DTMFOUT = 11% Output DTMF frequency command 
        SERIN = 12  % Read asynchronous serial command
        SEROUT = 13 % Write asynchronous serial command
        SYNCIN = 14 % Read synchronous serial command
        SYNCOUT = 15% Write synchronous serial command
        XOUT = 16   % Output X10 power-line command
        
        NOWAIT = 0  % More readable version of 0 for serialCommand
        
    end %end aliases
    
    methods
        function obj = BasicStamp(comport)
% BasicStamp(comport)
%   Constructor for the stamp.BasicStamp class. Attempts to connect with
%   the serial port comport and tests both the connection and that the
%   proper firmware is running on the BASIC Stamp. See class help for more
%   information.

            if strcmp(comport,'test')
                obj.TestMode = true;
                obj.SerialPort = stamp.DummySerial;
                
                fopen(obj.SerialPort);
                
                fprintf('Running in test mode\n');
                
            else
                obj.SerialPort = serial(comport,'BaudRate',2400,...
                    'Timeout',3);
                obj.TestMode = false;
                
                try fopen(obj.SerialPort);
                catch
                    error('stamp:ConnectionFailed',['Could not connect to the BASIC Stamp' char(10)...
                        'Check that it is attached and that the specified serial port is correct']);
                end
                
                fprintf('\nEstablishing Connection');
                for i = 1:3
                    fprintf('.')
                    pause(.5);
                end
                fprintf('\n');
                
                try
                    conntest = obj.serialCommand(1,obj.NOWAIT,[obj.TEST,0]);
                catch
                    error('stamp:BadFirmware',['Firmware error' char(10)...
                        'Check that the BASIC Stamp is connected and powered' char(10)...
                        'Try downloading the firmware to the BS2 again' char(10)...
                        'See README.pdf for information on downloading firmware']);
                end
                
                if ~(isscalar(conntest) && conntest == 1)
                    error('stamp:BadFirmware',['Firmware error' char(10)...
                        'Check that the BASIC Stamp is connected and powered' char(10)...
                        'Try downloading the firmware to the BS2 again' char(10)...
                        'See README.pdf for information on downloading firmware']);
                end
                
                fprintf('Connection Successful!\n');
            end
        end
        
        function delete(obj)
% delete()
%   Destructor for the stamp.BasicStamp class. Closes the connection to the
%   BASIC Stamp.
            if isvalid(obj.SerialPort)
                if strcmp(obj.SerialPort.Status,'open') && ~obj.TestMode 
                    fclose(obj.SerialPort);
                end
                delete(obj.SerialPort);
                fprintf('BASIC Stamp disconnected\n');
            end
        end
        

        function result = getState(obj,pin)
% result = obj.getState(pin)
%   Gets the current IO state of pin. Returns a 0 if the pin is an input,
%   or a 1 if it is set up as an output. The IO state of a pin determines
%   whether it is driving voltage or not.


            if nargin ~= 2
                error('stamp:ImproperArguments',['Calling syntax: obj.getState(pin)' char(10) ...
                    'Where pin = pin to get IO state of (0 <= pin <= 15)'])
            end
            validateattributes(pin,{'numeric'},{'scalar','integer','>=',0,'<=',15});
            
            
            result = double(obj.serialCommand(1,obj.NOWAIT,[obj.GETIO,pin]));
        end

        function setState(obj,pin,state)
% obj.setState(pin,state)
%   Sets the IO state of pin to state, where state is 0 (input) or 1
%   (output). The IO state of a pin determines whether it is driving
%   voltage or not.
            if nargin ~= 3
                error('stamp:ImproperArguments',['Calling syntax: obj.setState(pin,state)' char(10) ...
                    'Where pin = pin to get IO state of (0 <= pin <= 15)' char(10)...
                    'and state = IO state to set pin to (0 <= data <= 1)'])
            end
            validateattributes(pin,{'numeric'},{'scalar','integer','>=',0,'<=',15});
            validateattributes(state,{'numeric'},{'scalar','binary'});
            
            obj.serialCommand(1,obj.NOWAIT,[obj.SETIO,pin,state]);
        end
        
        function result = readPin(obj,pin)
% result = obj.readPin(pin)
%   Reads the digital state of pin, returning a 0 if it's low or a 1 if 
%   it's high. Note that this does not change the IO state of a pin; if the 
%   pin is an output, it will still read the current state of the pin, 
%   which should be the state of the output (assuming no shorts).

            if nargin ~= 2
                error('stamp:ImproperArguments',['Calling syntax: obj.readPin(pin)' char(10) ...
                    'Where pin = pin to read (0 <= pin <= 15)']);
            end
            validateattributes(pin,{'numeric'},{'scalar','integer','>=',0,'<=',15});

            result = double(obj.serialCommand(1,obj.NOWAIT,[obj.READI,pin]));
        end
        
        function writePin(obj,pin,value)
% obj.writePin(pin,value)
%   Sets pin to the digital state value, where value is 0 (low) or 1
%   (high). Will set pin to an output if it's not already.

            if nargin ~= 3
                error('stamp:ImproperArguments',['Calling syntax: obj.writePin(pin,value)' char(10) ...
                    'Where pin = pin to write to (0 <= pin <= 15)' char(10)...
                    'and value = state to set pin to (0 <= data <= 1)'])
            end
            validateattributes(pin,{'numeric'},{'scalar','integer','>=',0,'<=',15});
            validateattributes(value,{'numeric'},{'scalar','binary'});

            obj.serialCommand(1,obj.NOWAIT,[obj.WRITEO,pin,value]);
        end
        
        function result = recvPulse(obj,pin,pulsestate)
% result = obj.recvPulse(pin,pulsestate)
%   Waits for up to 131.07ms for pin to be in the state determined by
%   pulsestate (0 = pin must be low, 1 = pin must be high). When the pin is
%   in pulsestate (or immediately if the pin is in pulsestate at the
%   start), waits up to an additional 131.07ms for the end of the pulse
%   (ie. for the state to go to not-pulsestate). Returns the length of the
%   pulse in microseconds. If no pulse is detected, or if the pulse is
%   longer than 131.07ms, returns 0.

            if nargin ~= 3
                error('stamp:ImproperArguments',['Calling syntax: obj.recvPulse(pin,pulsestate)' char(10) ...
                    'Where pin = pin being pulsed (0 <= pin <= 15)' char(10)...
                    'and pulsestate = which kind of pulse to measure (0 <= data <= 1)'])
            end
            validateattributes(pin,{'numeric'},{'scalar','integer','>=',0,'<=',15});
            validateattributes(pulsestate,{'numeric'},{'scalar','binary'});

            result = obj.serialCommand(2,obj.NOWAIT,[obj.PULSIN,pin,pulsestate]);
            result = double(typecast(result,'uint16')) * 2; % Result is in us
        end
        
        function sendPulse(obj,pin,duration)
% obj.sendPulse(pin,duration)
%   Sends out a pulse on pin. The pulse length is determined by duration,
%   which is measured in microseconds and can be up to 131.07ms long. The
%   pulse will be the opposite of whatever the pin was outputing last; for
%   example, if the pin was low, calling sendPulse would pull the pin high
%   for duration and then pull it low again. Use writePin to set the pin
%   state before calling sendPulse.

            if nargin ~= 3
                error('stamp:ImproperArguments',['Calling syntax: obj.sendPulse(pin,duration)' char(10) ...
                    'Where pin = pin to send pulse on (0 <= pin <= 15)' char(10)...
                    'and duration = duration of the pulse in us (0 <= duration <= 131070)'])
            end
            validateattributes(pin,{'numeric'},{'scalar','integer','>=',0,'<=',15});
            validateattributes(duration,{'numeric'},{'scalar','integer','>=',0,'<=',131070});
        
            duration8bit = typecast(uint16(duration/2),'uint8'); %convert duration to 2bytes of unit 2us
            obj.serialCommand(1,duration/(10^6),[obj.PULSOUT,pin,duration8bit]);
        end
        
        function result = getRCTime(obj,pin,onstate)
% result = obj.getRCTime(pin,onstate)
%   Measures the amount of time pin is in onstate (0 = low, 1 = high). For
%   example, getRCTime(0,1) will measure the amount of time pin 0 is high,
%   starting from the method call (plus overhead due to communication
%   latency). If the pin is not in onstate when the function is called,
%   will return 2us, the minimum step size. getRCTime can only measure
%   results up to 131.07ms long; if the pin takes longer to transition, the
%   method will return 0.
%
%   This method is called getRCTime because it can be used to measure the
%   discharge time of a RC (resistor-capacitor) circuit. This can be used
%   to measure resistance, capacitance, or capacitor voltage. See the
%   documentation on PBASIC's RCTIME for more details.

            if nargin ~= 3
                error('stamp:ImproperArguments',['Calling syntax: obj.getRCTime(pin,onstate)' char(10) ...
                    'Where pin = pin to measure (0 <= pin <= 15)' char(10)...
                    'and onstate = state of the pin to measure (0 <= onstate <= 1)'])
            end
            validateattributes(pin,{'numeric'},{'scalar','integer','>=',0,'<=',15});
            validateattributes(onstate,{'numeric'},{'scalar','binary'});

            result = obj.serialCommand(2,obj.NOWAIT,[obj.RCTIME,pin,onstate]); %result in unit 2us
            result = double(typecast(result,'uint16'))*2; %convert to one var in us
        end
        
        function outputPWM(obj,pin,dutycycle,duration)
% obj.outputPWM(pin,dutycycle,duration)
%   Outputs a pseudo-PWM (pulse width modulation) signal with a duty cycle
%   of dutycycle to pin. The signal will be output for duration, where
%   duration is a time in milliseconds and can be up to 65.535 seconds
%   long. Dutycycle is a fractional value between 0 and 1 where 0
%   means the pin is always off and 1 means the pin is always on. Can be
%   used to approximate an analog output by charging a capacitor in an RC
%   circuit (assuming duration is long enough, the capacitor will be
%   charged to dutycycle*5V). 
%
%   Note that this is not a typical PWM signal because its period is not 
%   fixed. This means that, among other things, it cannot be used to 
%   control PWM servo motors.

            if nargin ~= 4
                error('stamp:ImproperArguments',['Calling syntax: obj.outputPWM(pin,dutycycle,duration)' char(10) ...
                    'Where pin = pin to output to (0 <= pin <= 15)' char(10)...
                    'dutycycle = duty cycle of the PWM signal (0 <= dutycycle <= 1)' char(10)...
                    'and duration = how long to output the signal in ms (0 < duration < 255)'])
            end
            validateattributes(pin,{'numeric'},{'scalar','integer','>=',0,'<=',15});
            validateattributes(dutycycle,{'numeric'},{'scalar','>=',0,'<=',1});
            validateattributes(duration,{'numeric'},{'scalar','integer','>=',0,'<=',255});

            dutycycle = round(dutycycle * 255);
            obj.serialCommand(1,duration/1000,[obj.PWM,pin,dutycycle,duration]); %duration in ms
        end
        
        function result = countStateChanges(obj,pin,duration)
% result = obj.countStateChanges(pin,duration)
%   Counts the number of times pin changes state (from high to low or from
%   low to high). Waits for duration, where duration is a time in
%   milliseconds and can be up to 65.535 seconds long. Does not debounce, 
%   so may return an unexpected result for noisy inputs, such as button
%   presses.

            if nargin ~= 3
                error('stamp:ImproperArguments',['Calling syntax: obj.countStateChanges(pin,duration)' char(10) ...
                    'Where pin = pin to watch (0 <= pin <= 15)' char(10)...
                    'and duration = how long to watch in ms (0 <= duration <= 65535)'])
            end
            validateattributes(pin,{'numeric'},{'scalar','integer','>=',0,'<=',15});
            validateattributes(duration,{'numeric'},{'scalar','integer','>=',0,'<=',65535});

            duration8bit = typecast(uint16(duration),'uint8'); %duration in ms
            result = obj.serialCommand(2,duration/1000,[obj.COUNT,pin,duration8bit]);
            result = double(typecast(result,'uint16'));
        end
        
        function outputSine(obj,pin,duration,freq)
% obj.outputSine(pin,duration,freq)
%   Outputs a PWM approximation of a sine wave to pin. The output frequency
%   is based on freq, which is measured in Hertz and can be up to
%   32.767KHz. The output is maintained for duration, which is a time in
%   milliseconds and can be up to 65.535 seconds long. 
%
%   The output signal should be passed through a low-pass (ex. RC) filter
%   to provide a better approximation of a sine wave. See documentation for
%   PBASIC's FREQOUT function for more information on hardware setup.

            if nargin ~= 4
                error('stamp:ImproperArguments',['Calling syntax: obj.outputSine(pin,duration,freq)' char(10) ...
                    'Where pin = pin to output to (0 <= pin <= 15)' char(10)...
                    'duration = how long to output in ms (0 <= duration <= 65535)' char(10)...
                    'and freq = frequency of the sine wave (0 <= freq <= 32767)'])
            end
            validateattributes(pin,{'numeric'},{'scalar','integer','>=',0,'<=',15});
            validateattributes(duration,{'numeric'},{'scalar','integer','>=',0,'<=',65535});
            validateattributes(freq,{'numeric'},{'scalar','integer','>=',0,'<=',32767});

            duration8bit = typecast(uint16(duration),'uint8'); %duration in ms
            freq8bit = typecast(uint16(freq),'uint8'); %duration in hz
            obj.serialCommand(1,duration/1000,[obj.FREQOUT,pin,duration8bit,freq8bit]);
        end
        
        function outputDTMF(obj,pin,duration,tone)
% obj.outputDTMF(pin,duration,tone)
%   Outputs a DTMF (dual-tone, multi-frequency) tone for communicating with
%   telephone systems or some radio equipment. The signal will be output on
%   pin for duration ms, where duration can be up to 65.535s. Tone is a
%   number between 0 and 15, with 0-9 corresponding to 0-9 on a dialpad, 10
%   corresponding to star (*), and 11 corresponding to pound (#). 12-15 are
%   "fourth column" tones use to communicate with radio equipment.
%
%   See the documentation for the PBASIC function DTMFOUT for more
%   information on usage.

            if nargin ~= 4
                error('stamp:ImproperArguments',['Calling syntax: obj.outputDTMF(pin,duration,tone)' char(10) ...
                    'Where pin = pin to output to (0 <= pin <= 15)' char(10)...
                    'duration = how long to output in ms (0 <= duration <= 65535)' char(10)...
                    'and tone = dial tone to output (0 <= tone <= 15)'])
            end
            validateattributes(pin,{'numeric'},{'scalar','integer','>=',0,'<=',15});
            validateattributes(duration,{'numeric'},{'scalar','integer','>=',0,'<=',65535});
            validateattributes(tone,{'numeric'},{'scalar','integer','>=',0,'<=',15});
            
            obj.serialCommand(1,obj.NOWAIT,[obj.DTMFOUT,pin,duration,tone]);
        end
        
        function result = readSerialAsync(obj,pin,baudrate,parity,polarity)
% obj.readSerialAsync(pin,baudrate,parity,polarity)
%   Reads a asynchronous serial byte on pin. Serial settings are
%   controlled by baudrate, parity (0 = no partity, 1 = even parity with 7
%   bits per transmission), and polarity (0 = true, 1 = inverted). Do not
%   call this function unless there's an incoming serial signal; the BasicStamp
%   will wait for a signal until it is reset.
%
%   Baud rate can be any number from 243 to 50,000 baud. However, the BS2
%   does not have an input buffer and may have trouble reading serial
%   streams of 4800 baud or more. Also, note that because of the lack of an
%   input buffer, the BasicStamp will only be able to recieve serial inputs
%   after calling a read function (accounting for communication and
%   processing overhead of 50ms or more).

            if nargin ~= 5
                error('stamp:ImproperArguments',['Calling syntax: obj.outputAsync(pin,baudrate,parity,polarity)' char(10) ...
                    'Where pin = pin to output to (0 <= pin <= 15)' char(10)...
                    'baudrate = baud rate in baud (243 <= baudrate <= 50000)' char(10)...
                    'parity = parity/data bits setting (0 <= tone <= 1)' char(10)...
                    'and polarity = polarity setting (0 <= polarity <= 1)']); 
            end
            validateattributes(pin,{'numeric'},{'scalar','integer','>=',0,'<=',15});
            validateattributes(baudrate,{'numeric'},{'scalar','>=',243,'<=',50000});
            validateattributes(parity,{'numeric'},{'scalar','binary'});
            validateattributes(polarity,{'numeric'},{'scalar','binary'});      
            
            baudmode = uint16(1000000/baudrate) - 20;
            baudmode = bitset(baudmode,14,parity);
            baudmode = bitset(baudmode,15,polarity);
            baudmode = typecast(baudmode,'uint8');
            
            result = double(obj.serialCommand(1,obj.NOWAIT,[obj.SERIN,pin,baudmode]));
        end
            
        function writeSerialAsync(obj,pin,baudrate,parity,polarity,databyte)
% obj.writeSerialAsync(pin,baudrate,parity,polarity,databyte)
%   Writes a asynchronous serial byte on pin. Serial settings are
%   controlled by baudrate, parity (0 = no partity, 1 = even parity with 7
%   bits per transmission), and polarity (0 = true, 1 = inverted). The
%   contents of databyte will be output; this means that databyte must be a
%   number between 0 and 255 (0 and 127 if parity = 1).
%
%   Baud rate can be any number from 243 to 50,000 baud. The serial output 
%   will occur immediately after communication and processing overhead.
            
            if nargin ~= 6
                error('stamp:ImproperArguments',['Calling syntax: obj.writeAsync(pin,baudrate,parity,polarity,databyte)' char(10) ...
                    'Where pin = pin to output to (0 <= pin <= 15)' char(10)...
                    'baudrate = baud rate in baud (243 <= baudrate <= 50000)' char(10)...
                    'parity = parity/data bits setting (0 <= tone <= 1)' char(10)...
                    'polarity = polarity setting (0 <= polarity <= 1)' char(10)...
                    'databyte = one byte of data to send (0 <= databyte <= 255 or 0 <= databyte <= 127)']); 
            end
            validateattributes(pin,{'numeric'},{'scalar','integer','>=',0,'<=',15});
            validateattributes(baudrate,{'numeric'},{'scalar','>=',243,'<=',50000});
            validateattributes(parity,{'numeric'},{'scalar','binary'});
            validateattributes(polarity,{'numeric'},{'scalar','binary'}); 
            
            if ~parity
                validateattributes(databyte,{'numeric'},{'scalar','integer','>=',0,'<=',255});
            else
                validateattributes(databyte,{'numeric'},{'scalar','integer','>=',0,'<=',127});
            end
            
            baudmode = uint16(1000000/baudrate) - 20;
            baudmode = bitset(baudmode,14,parity);
            baudmode = bitset(baudmode,15,polarity);
            baudmode = typecast(baudmode,'uint8');
            
            obj.serialCommand(1,obj.NOWAIT,[obj.SEROUT,pin,baudmode,databyte]);
        end            
        
        function result = readSerialSync(obj,pin,clockpin,LSBfirst,edge)
% obj.readSerialSync(pin,clockpin,LSBfirst,edge)
%   Reads a synchronous serial byte from pin. A clock signal will be output
%   on clockpin, which is on for ~14us and off for ~46us. LSBfirst 
%   determines if the least significant or most significant bit is sent 
%   first (1 = LSB sent first). edge determines which edge of the clock 
%   signal signals are sampled on (0 = signal sampled on rising edge, 1 = 
%   falling edge).
%
%   Do not call this function unless there's an incoming serial signal; the
%   BasicStamp will wait for a signal until it is reset.
            
            if nargin ~= 5
                error('stamp:ImproperArguments',['Calling syntax: obj.readSync(pin,clockpin,mode)' char(10) ...
                    'Where pin = pin to read (0 <= pin <=15)' char(10)...
                    'clockpin = pin to use as clock signal (0 <= clockpin <= 15)' char(10)...
                    'LSBfirst = whether the LSB is transmitted first (0 <= LSBfirst <= 15)'...
                    'and edge = which edge to sample signals on (0 <= edge <= 1)'])
            end
            validateattributes(pin,{'numeric'},{'scalar','integer','>=',0,'<=',15});
            validateattributes(clockpin,{'numeric'},{'scalar','integer','>=',0,'<=',15});
            validateattributes(LSBfirst,{'numeric'},{'scalar','binary'});
            validateattributes(edge,{'numeric'},{'scalar','binary'});
            
            if(clockpin == pin)
                error('stamp:SamePin','The same pin is being used as both an input and output')
            end
            
            mode = bitset(0,1,LSBfirst);
            mode = bitset(mode,2,edge);
            
            result = double(obj.serialCommand(1,obj.NOWAIT,[obj.SYNCIN,pin,clockpin,mode]));    
        end
            
        function writeSerialSync(obj,pin,clockpin,LSBfirst,edge,databyte)
% obj.writeSerialSync(pin,clockpin,LSBfirst,edge,databyte)
%   Writes a synchronous serial byte to pin. A clock signal will be output
%   on clockpin, which is on for ~14us and off for ~46us. LSBfirst 
%   determines if the least significant or most significant bit is sent 
%   first (1 = LSB sent first). edge determines which edge of the clock 
%   signal signals are sampled on (0 = signal sampled on rising edge, 1 = 
%   falling edge).
%
%   databyte defines the byte that will be output, and therefore must be an 
%   integer between 0 and 255.
            
            if nargin ~= 6
                error('stamp:ImproperArguments',['Calling syntax: obj.writeSync(pin,clockpin,mode,databyte)' char(10) ...
                    'Where pin = pin to read (0 <= pin <=15)' char(10)...
                    'clockpin = pin to use as clock signal (0 <= clockpin <= 15)' char(10)...
                    'LSBfirst = whether the LSB is transmitted first (0 <= LSBfirst <= 15)'...
                    'edge = which edge to sample signals on (0 <= edge <= 1)' ...
                    'and databyte = one byte of data to send (0 <= databyte <= 255)'])
            end
            validateattributes(pin,{'numeric'},{'scalar','integer','>=',0,'<=',15});
            validateattributes(clockpin,{'numeric'},{'scalar','integer','>=',0,'<=',15});
            validateattributes(LSBfirst,{'numeric'},{'scalar','binary'});
            validateattributes(edge,{'numeric'},{'scalar','binary'});
            validateattributes(databyte,{'numeric'},{'scalar','integer','>=',0,'<=',255});
            
            if(clockpin == pin)
                error('stamp:SamePin','The same pin is being used as both an input and output')
            end
            
            mode = bitset(0,1,LSBfirst);
            mode = bitset(mode,2,edge);
            
            obj.serialCommand(1,obj.NOWAIT,[obj.SYNCOUT,pin,clockpin,mode,databyte]);    
        end

        function outputX10(obj,signalpin,zeroxpin,housecode,commandcode)
% obj.outputX10(signalpin,zeroxpin,housecode,commandcode)
%   Outputs a X10 command to signalpin. <a href="http://en.wikipedia.org/wiki/X10_%28industry_standard%29">X10</a> is a standard for sending
%   digital signals over a AC house power signal, and can be used to
%   remotely control electronics such as appliances and lights. The BASIC
%   Stamp will output a signal on signalpin, and requires a zero-crossing
%   signal on zeroxpin. housecode and commandcode determine which house
%   code and command, respectively, will be output. housecode must be a
%   number between 0-15, and commandcode must be between 0-30. stamp.X10
%   defines aliases for both X10 command and house codes. Use import
%   stamp.X10 to import these aliases and X10.{code} to use them.
 
            if nargin ~= 5
                error('stamp:ImproperArguments',['Calling syntax: obj.outputX10(signalpin,zeroxpin,housecode,commandcode)' char(10) ...
                    'Where signalpin = pin to output to (0 <= signalpin <= 15)' char(10)...
                    'zeroxpin = pin with zero-reference input (0 <= zeroxpin <= 15)' char(10)...
                    'housecode = the X10 house code  (0 <= tone <= 15)' char(10)...
                    'and commandcode = command code to send (0 <= polarity <= 1)']); 
            end
            validateattributes(signalpin,{'numeric'},{'scalar','integer','>=',0,'<=',15});
            validateattributes(zeroxpin,{'numeric'},{'scalar','integer','>=',0,'<=',15});
            validateattributes(housecode,{'numeric'},{'scalar','integer','>=',0,'<=',15});
            validateattributes(commandcode,{'numeric'},{'scalar','integer','>=',0,'<=',30});
            
            if(signalpin == zeroxpin)
                error('stamp:SamePin','The same pin is being used as both an input and output')
            end            
            
            obj.serialCommand(1,obj.NOWAIT,[obj.XOUT,signalpin,zeroxpin,housecode,commandcode]);
        end

            
            
        function result = serialCommand(obj,numoutputs,wait,args)
% result = obj.serialCommand(numoutputs,wait,args)
%   Sends a command signal to the BASIC Stamp. Will output args to the
%   BASIC Stamp over serial, where args is a row vector containing signals
%   to be output (in general, args should be [command pin arg1...argn]).
%   The method will then check that the commands are properly echoed (the
%   BS2 will echo all serial input due to its hardware setup) and wait for
%   wait seconds (should be used when the BASIC Stamp is collecting data).
%   The method will then read a number of outputs defined by numoutputs and
%   output them as a column vector.

            serialout = zeros(obj.NUMINPUTS,1);
            serialout(1:min(size(args,2),obj.NUMINPUTS)) = args;
            
            try
                fwrite(obj.SerialPort,serialout,'uint8');
            catch
                error('stamp:WriteFailed',['Serial write failed!' char(10)...
                    'Please check connection to the BASIC Stamp.']);
            end
            
            
            try
                check = uint8(fread(obj.SerialPort,obj.NUMINPUTS));
            catch
                obj.flushBuffer;
                error('stamp:WriteFailed',['Serial write failed!' char(10)...
                    'Please check connection to the BASIC Stamp.']);
            end
            if ~isequal(check,serialout)
                obj.flushBuffer;
                error('stamp:WriteFailed',['Serial write failed!' char(10)...
                    'Please check connection to the BASIC Stamp.']);
            end
            
            
            if (wait ~= obj.NOWAIT) && ~obj.TestMode
                pause(wait);
            end
            
            
            if numoutputs == 0
                result = 0;
            else
                try
                    result = uint8(fread(obj.SerialPort,numoutputs));
                catch
                    obj.flushBuffer;
                    error('stamp:NotEnoughOutput',['Not enough output from BASIC Stamp' char(10)...
                        'If this is a built-in function, try redownloading firmware' char(10)...
                        'If this is a user function, make sure serialCommand is called correctly'])
                end
            end
        end
        
        function flushBuffer(obj)
% obj.flushBuffer()
%   Flushes the input buffer of BasicStamp's serial object. For the most 
%   part, this class assumes that the input buffer is empty before 
%   performing any new serial IO operations. If there are lingering values
%   in the buffer, these serial operations will attempt to use values that 
%   are not what they expect, likely resulting in an error (and a 
%   still-filled buffer).
%
%   Note that this clears MATLAB's serial input buffer for the associated
%   BASIC Stamp connection only. The BASIC Stamp does not have an input
%   buffer to clear, so serial interactions on the stamp are untouched.
            if strcmp(obj.SerialPort.Status,'open') && obj.SerialPort.BytesAvailable ~= 0
                fread(obj.SerialPort,obj.SerialPort.BytesAvailable);
            end
        end
        
    end %end methods
    
end %end classdef

Contact us