Example #1
0
    def run(self):

        ser = Serial('/dev/ttyS1')
        ser.setDTR(True)

        wait_signals = (TIOCM_RNG | TIOCM_DSR | TIOCM_CD | TIOCM_CTS)
        log("Waiting for signal...")
        last_time = None
        while True:
            try:
                ioctl(ser.fd, TIOCMIWAIT, wait_signals)
                if ser.getCD():
                    # Take action
                    now = time.time()
                    if not last_time or now - last_time > 1:
                        last_time = now
                        continue
                    else:
                        log("CD seen within 1s")
                        subprocess.call(
                            ["shutdown", "-h", "now", "Front button pressed"])
                        last_time = None
            except (KeyboardInterrupt, Exception) as e:
                log("Stop requested")
                log(e)
                break

        ser.setDTR(False)
Example #2
0
    def run(self):

        ser = Serial('/dev/ttyS1')
        ser.setDTR(True)
        
        wait_signals = (TIOCM_RNG |
                        TIOCM_DSR |
                        TIOCM_CD  |
                        TIOCM_CTS)
        log( "Waiting for signal...")
	last_time = None
        while True:
            try:
                ioctl(ser.fd, TIOCMIWAIT, wait_signals)
		if ser.getCD():
                    # Take action
		    now = time.time()
		    if not last_time or now - last_time > 1: 
                        last_time = now
			continue
                    else:
                        log( "CD seen within 1s")
			subprocess.call(["shutdown", "-h", "now", "Front button pressed"])
			last_time = None
            except (KeyboardInterrupt, Exception) as e:
                log("Stop requested" )
                log(e)
		break
        
        ser.setDTR(False)
Example #3
0
class CopyNES(object):
    '''
    Interface to a CopyNES device
    '''

    ###########################################################################
    ##
    ## Status operations
    ##
    ###########################################################################

    def __init__(self, data_device, control_device):
        '''
        Connects to a USB CopyNES
        '''

        self.data_channel = Serial(data_device, 115200, timeout = 5)
        self.control_channel = Serial(control_device, 115200, timeout = 5)

        # Empty the read buffer
        time.sleep(0.1)
        self.data_channel.flushInput()

    def disconnect(self):
        '''
        Disconnects from the CopyNES device and frees up all resources
        '''
        self.data_channel.close()
        self.control_channel.close()

    def power(self):
        '''
        Returns True if the CopyNES is powered on
        '''
        return not self.control_channel.getCD()

    def reset(self):
        '''
        Resets the CopyNES CPU.
        In "play" mode, this runs the cartridge program
        In "copy" mode, the CopyNES will be waiting for commands
        '''
        self.control_channel.setDTR(False)
        self.control_channel.setDTR(True)

    def play_mode(self):
        '''
        Resets the CopyNES and puts it in "play" mode
        This disables the BIOS, so no I/O operations will be available
        until "copy" mode is enabled again
        '''
        self.control_channel.setRTS(False)
        self.reset()

    def copy_mode(self):
        '''
        Resets the CopyNES and puts it in "copy" mode
        '''
        self.control_channel.setRTS(True)
        self.reset()

    ###########################################################################
    ##
    ## IO operations
    ##
    ###########################################################################

    def write(self, data):
        '''
        Writes a sequence of bytes to the CopyNES
        '''
        if isinstance(data, int):
            data = bytes((data,))
        result = self.data_channel.write(data)
        self.data_channel.flush()
        return result

    def wait_for_data(self, timeout):
        '''
        Wait until data is available for reading
        '''
        while self.data_channel.inWaiting() == 0 and timeout > 0:
            time.sleep(0.1)
            timeout -= 0.1 # XXX Not very exact method of waiting
        return self.data_channel.inWaiting() > 0

    def read(self, size = 1):
        '''
        Reads a sequence of bytes transferred from the CopyNES
        '''
        if size == 0:
            return

        result = self.data_channel.read(size)
        if len(result) != size:
            raise Exception("timeout")
        return result

    def read_int(self):
        '''
        Reads a single byte transferred from the CopyNES
        '''
        return int(self.read()[0])

    def read_string(self):
        '''
        Reads a zero terminated string
        '''
        result = bytearray()
        byte = self.read()
        while ord(byte) != 0x00:
            result += byte
            byte = self.read()
        return result.decode()

    def flush(self):
        self.data_channel.flush()

    ###########################################################################
    ##
    ## BIOS operations
    ##
    ###########################################################################

    def version_string(self):
        '''
        Returns the CopyNES firmware version string
        '''
        self.write(0xa1)
        return self.read_string()

    def version(self):
        '''
        Returns the CopyNES firmware version number
        '''
        self.write(0xa2)
        return self.read_int()

    def __send_command(self, prolog, address, length, epilog):
        '''
        Send a CopyNES command
        '''
        address_lsb = (address & 0x00ff)
        address_msb = (address & 0xff00) >> 8
        length_lsb = (length & 0x00ff)
        length_msb = (length & 0xff00) >> 8

        if length_lsb != 0x00:
            raise Exception('Memory may only be read/written in $0100 byte chunks, you are trying with a $%04x byte chunk' % length)

        self.write(prolog)
        self.write(address_lsb)
        self.write(address_msb)
        self.write(length_msb)
        self.write(epilog)

    def read_cpu_memory(self, address, length):
        self.__send_command(0x3a, address, length, 0xa3)
        return self.read(length)

    def write_cpu_memory(self, address, data):
        self.__send_command(0x4b, address, len(data), 0xb4)
        return self.write(data)

    def execute_code(self, address):
        self.__send_command(0x7e, address, 0x00, 0xe7)

    ###########################################################################
    ##
    ## Convenience methods
    ##
    ###########################################################################

    def run_plugin(self, plugin):
        self.write_cpu_memory(0x0400, plugin.data)
        self.execute_code(0x0400)

    def download_rom(self, plugin, ines_mapper):

        ##
        ## Packet types sent by dumping plugins
        ##
        PACKET_EOD = 0
        PACKET_PRG_ROM = 1
        PACKET_CHR_ROM = 2
        PACKET_WRAM = 3
        PACKET_RESET = 4

        self.run_plugin(plugin)
        prom = crom = wram = []
        mirroring = self.read_int() ^ 0x01
        (packet_type, data) = self.__read_packet()
        while packet_type != PACKET_EOD:
            if packet_type == PACKET_PRG_ROM:
                prom = data
            elif packet_type == PACKET_CHR_ROM:
                crom = data
            elif packet_type == PACKET_WRAM:
                wram = data
            elif packet_type == PACKET_RESET:
                self.reset()
            else:
                raise Exception('Unexpected packet type: %d' % packet_type)
            (packet_type, data) = self.__read_packet()
        logging.debug('Downloaded prg: %d bytes, chr: %d bytes, wram: %d bytes, mirroring: %d, mapper: %d', len(prom), len(crom), len(wram), mirroring, ines_mapper)
        return NESROM(prom, crom, ines_mapper, mirroring)

    def __read_packet(self):
        pages = self.read_int() | (self.read_int() << 8)
        length = pages << 8
        packet_type = self.read_int()
        if packet_type == 0:
            return (packet_type, 0)
        data = self.read(length)
        return (packet_type, data)

    
    @staticmethod
    def default_data_device():
        if platform.system() == 'Windows':
            return 'COM3'
        elif platform.system() == 'Darwin':
            serial_devices = glob.glob('/dev/tty.usbserial-*')
            return serial_devices[0] if len(serial_devices) >= 2 else '?'
        else:
            return '/dev/ttyUSB0'

    @staticmethod
    def default_control_device():
        if platform.system() == 'Windows':
            return 'COM4'
        elif platform.system() == 'Darwin':
            serial_devices = glob.glob('/dev/tty.usbserial-*')
            return serial_devices[1] if len(serial_devices) >= 2 else '?'
        else:
            return '/dev/ttyUSB1'
class IridiumConnection(SerialConnection):
    '''
    classdocs
    '''


    def __init__(self, modem, port, baudrate, number, timeout=0.1):
        '''
        Constructor
        '''
        self._incoming_line_buffer = ""

        self.connection_type = "direct_iridium"

        self.modem = modem
        self.port = port
        self.baudrate = baudrate
        self.timeout = timeout

        self._serialport = Serial(port, baudrate, timeout=self.timeout)

        self._thread = Thread(target=self._listen)
        self._thread.setDaemon(True)
        self._thread.start()

        self.state = 'DISCONNECTED'
        self.modem = modem
        self.number = str(number)
        self.counter = 0

    @property
    def is_connected(self):
        return self._serialport.getCD()

    @property
    def can_change_baudrate(self):
        return True

    def change_baudrate(self,baudrate):
        return self.baudrate


    def _listen(self):
        while True:
            if self._serialport.isOpen():
                msg = self.readline()
                if not self._serialport.getCD():
                    # Not connected via Iridium
                    # Processing I/O with Iridium dialer.
                    self.process_io(msg)
                else:
                    # We are connected, so pass through to NMEA
                    self.modem._process_incoming_nmea(msg)
                    self.modem._process_outgoing_nmea()
            else:  # not connected
                sleep(0.5) # Wait half a second, try again.

    def process_io(self, msg):
        # This is called by the primary serial processing loop.
        # It will be called whenever we have a line of data, or periodically (based on a timeout)
        if msg is None:
            msg = ""

        if self.state == "DIALING":
            if "CONNECT" in msg:
                self.state = "CONNECTED"
            elif "NO CARRIER" in msg:
                self.state = "DISCONNECTED"
            elif "NO ANSWER" in msg:
                self.state = "DISCONNECTED"
            elif "BUSY" in msg:
                self.state = "DISCONNECTED"
            elif "ERROR" in msg:
                self.state = "DISCONNECTED"
            if self.counter > 600: #Counts are about 0.1s
                self.state = "DISCONNECTED"
                self.modem._daemon_log.info("$IRIDIUM,{0},Dialing Attempt Timed Out.".format(self.modem.name))
            self.counter += 1
        elif self.state == 'DISCONNECTED':
            self.counter = 0
            self.do_dial()
        elif self.state == "CONNECTED":
            # In theory, we shouldn't be here, because if we are connected, traffic is passed through to the umodem module.
            # So, give us a 1 message margin of error (basically, ignore this input) and try dialing on the next timeout/message.
            self.state = "DISCONNECTED"

        if msg is not "":
            self.modem._daemon_log.info("$IRIDIUM,{0},{1}".format(self.modem.name, msg.strip()))
        self.modem._daemon_log.debug("$IRIDIUM,{0},Current State:{1}".format(self.modem.name, self.state))

    def do_dial(self):
        # Toggle DTR
        self.modem._daemon_log.info("$IRIDIUM,{0},Dialing {1}".format(self.modem.name, self.number))
        sleep(2)
        self._serialport.setDTR(False)
        sleep(0.1)
        self._serialport.setDTR(True)
        sleep(0.1)
        self._serialport.write("AT+CREG?\r\n")
        sleep(1)
        self._serialport.write("AT+CEER\r\n")
        sleep(1)
        self._serialport.write("AT+CSQ?\r\n")
        sleep(5)
        self._serialport.write("ATD{0}\r\n".format(self.number))
        self.state = "DIALING"

    def close(self):
        self._serialport.setDTR(False)
        sleep(0.2)
        self._serialport.close()

    def wait_for_connect(self):
        while self.state != "CONNECTED":
            sleep(1)
Example #5
0
class IridiumConnection(SerialConnection):
    '''
    classdocs
    '''
    def __init__(self, modem, port, baudrate, number, timeout=0.1):
        '''
        Constructor
        '''
        self._incoming_line_buffer = ""

        self.connection_type = "direct_iridium"

        self.modem = modem
        self.port = port
        self.baudrate = baudrate
        self.timeout = timeout

        self._serialport = Serial(port, baudrate, timeout=self.timeout)

        self._thread = Thread(target=self._listen)
        self._thread.setDaemon(True)
        self._thread.start()

        self.state = 'DISCONNECTED'
        self.modem = modem
        self.number = str(number)
        self.counter = 0

    @property
    def is_connected(self):
        return self._serialport.getCD()

    @property
    def can_change_baudrate(self):
        return True

    def change_baudrate(self, baudrate):
        return self.baudrate

    def _listen(self):
        while True:
            if self._serialport.isOpen():
                msg = self.readline()
                if not self._serialport.getCD():
                    # Not connected via Iridium
                    # Processing I/O with Iridium dialer.
                    self.process_io(msg)
                else:
                    # We are connected, so pass through to NMEA
                    self.modem._process_incoming_nmea(msg)
                    self.modem._process_outgoing_nmea()
            else:  # not connected
                sleep(0.5)  # Wait half a second, try again.

    def process_io(self, msg):
        # This is called by the primary serial processing loop.
        # It will be called whenever we have a line of data, or periodically (based on a timeout)
        if msg is None:
            msg = ""

        if self.state == "DIALING":
            if "CONNECT" in msg:
                self.state = "CONNECTED"
            elif "NO CARRIER" in msg:
                self.state = "DISCONNECTED"
            elif "NO ANSWER" in msg:
                self.state = "DISCONNECTED"
            elif "BUSY" in msg:
                self.state = "DISCONNECTED"
            elif "ERROR" in msg:
                self.state = "DISCONNECTED"
            if self.counter > 600:  #Counts are about 0.1s
                self.state = "DISCONNECTED"
                self.modem._daemon_log.info(
                    "$IRIDIUM,{0},Dialing Attempt Timed Out.".format(
                        self.modem.name))
            self.counter += 1
        elif self.state == 'DISCONNECTED':
            self.counter = 0
            self.do_dial()
        elif self.state == "CONNECTED":
            # In theory, we shouldn't be here, because if we are connected, traffic is passed through to the umodem module.
            # So, give us a 1 message margin of error (basically, ignore this input) and try dialing on the next timeout/message.
            self.state = "DISCONNECTED"

        if msg is not "":
            self.modem._daemon_log.info("$IRIDIUM,{0},{1}".format(
                self.modem.name, msg.strip()))
        self.modem._daemon_log.debug("$IRIDIUM,{0},Current State:{1}".format(
            self.modem.name, self.state))

    def do_dial(self):
        # Toggle DTR
        self.modem._daemon_log.info("$IRIDIUM,{0},Dialing {1}".format(
            self.modem.name, self.number))
        sleep(2)
        self._serialport.setDTR(False)
        sleep(0.1)
        self._serialport.setDTR(True)
        sleep(0.1)
        self._serialport.write("AT+CREG?\r\n")
        sleep(1)
        self._serialport.write("AT+CEER\r\n")
        sleep(1)
        self._serialport.write("AT+CSQ?\r\n")
        sleep(5)
        self._serialport.write("ATD{0}\r\n".format(self.number))
        self.state = "DIALING"

    def close(self):
        self._serialport.setDTR(False)
        sleep(0.2)
        self._serialport.close()

    def wait_for_connect(self):
        while self.state != "CONNECTED":
            sleep(1)
class IridiumCsdListener(object):
    def __init__(self, port, baudrate=9600, timeout=0.1, unified_log=None):
        self._incoming_line_buffer = ""

        self.connection_type = "iridium_csd_listener"

        self.port = port
        self.baudrate = baudrate
        self.timeout = timeout

        if unified_log is None:
            unified_log = UnifiedLog(log_path='.')
        self._daemon_log = unified_log.getLogger("iridium_csd_listener")
        self._nmea_in_log = unified_log.getLogger("nmea.from.iridium_csd")
        self._nmea_out_log = unified_log.getLogger("nmea.to.iridium_csd")
        self._daemon_log.setLevel(logging.INFO)
        self._nmea_in_log.setLevel(logging.INFO)
        self._nmea_out_log.setLevel(logging.INFO)

        self._serialport = Serial(port, baudrate, timeout=self.timeout)
        self.serial_tx_queue = Queue()

        self.nmea_listeners = []

        self.iridium_buffer_length = 20

        self._last_connected_status = self.is_connected

        self._thread = Thread(target=self._listen)
        self._thread.setDaemon(True)
        self._thread.start()

        self._daemon_log.info("Iridium Listener Started")
        self.iridium_init()

    @property
    def is_connected(self):
        connected = self._serialport.getCD() and self._serialport.isOpen()
        return connected

    @property
    def can_change_baudrate(self):
        return True

    def change_baudrate(self, baudrate):
        self.baudrate = baudrate
        self._serialport.baudrate = baudrate
        return baudrate

    def iridium_init(self):
        sleep(1)
        self._serialport.setDTR(False)
        self._daemon_log.info("Clear DTR")
        sleep(0.1)
        self._serialport.setDTR(True)
        self._daemon_log.info("Set DTR")
        sleep(0.1)
        self._serialport.write("AT+CREG?\r\n")
        self._daemon_log.info("> AT+CREG?")
        sleep(1)
        self._serialport.write("AT+CEER\r\n")
        self._daemon_log.info("> AT+CEER")
        sleep(1)
        self._serialport.write("AT+CSQ?\r\n")
        self._daemon_log.info("> AT+CSQ?")
        sleep(5)

    def close(self):
        self._serialport.setDTR(False)
        sleep(0.2)
        self._serialport.close()

    def _listen(self):
        while True:
            # Log connection changes
            if self.is_connected is not self._last_connected_status:
                self._daemon_log.info("Iridium Connected: {}".format(
                    self.is_connected))
                self._last_connected_status = self.is_connected

            if self._serialport.isOpen():
                msg = self.readline()
                if msg is None:
                    msg = ""

                # Figure out if this is an NMEA message or Iridium traffic, in the dumbest way possible.
                if len(msg) > 0:
                    if msg[0] == '$':
                        self._process_incoming_nmea(msg)
                    else:
                        self.process_iridium(msg)

                self._process_outgoing_nmea()

            else:  # not connected
                sleep(0.5)  # Wait half a second, try again.

    def process_iridium(self, msg):
        # This is called by the primary serial processing loop.
        # It will be called whenever we have a line of data, or periodically (based on a timeout)

        # Basically, all we do here is pick up the phone.
        if "RING" in msg:
            self._serialport.write("ATA\r\n")
            self._daemon_log.info("> ATA")

        # and log messages
        if msg is not "":
            self._daemon_log.info("< {}".format(msg.strip()))

    def _process_incoming_nmea(self, msg):
        self._nmea_in_log.info(msg.rstrip('\r\n'))

        msg = Message(msg)

        try:
            for func in self.nmea_listeners:
                func(msg)  # Pass the message to any custom listeners.
        except Exception as e:
            self._daemon_log.warn("Error in NMEA listener: ")
            self._daemon_log.warn(repr(e))

    def _process_outgoing_nmea(self):
        # Now, transmit anything we have in the outgoing queue.
        if self.is_connected:
            try:
                txstring = self.serial_tx_queue.get_nowait()
                self._serialport.write(txstring)
                self._nmea_out_log.info(txstring.rstrip('\r\n'))
            #If the queue is empty, then pass, otherwise log error
            except Empty:
                pass
            except:
                self._daemon_log.exception("NMEA Output Error")
        else:
            # If we aren't connected, remove old messages from the queue.
            overflow = self.serial_tx_queue.qsize(
            ) - self.iridium_buffer_length
            if overflow > 0:
                for i in range(overflow):
                    txstring = self.serial_tx_queue.get_nowait()
                    self._daemon_log.info("Dropped outgoing NMEA: {}".format(
                        txstring.rstrip('\r\n')))

    def write_nmea(self, msg):
        """Call with the message to send, as an NMEA message.  Correct checksum will be computed."""

        if type(msg) == str:
            # Automagically convert it into an NMEA message (or try, at least)
            msg = Message(msg)

        message = ",".join([str(p) for p in [msg['type']] + msg['params']])
        chk = nmeaChecksum(message)
        message = "$" + message.lstrip('$').rstrip(
            '\r\n*') + "*" + chk + "\r\n"

        # Queue this message for transmit in the serial thread
        self._daemon_log.debug("Writing NMEA to output queue: %s" %
                               (message.rstrip('\r\n')))
        try:
            self.serial_tx_queue.put(message, block=False)
        # If queue full, then ignore
        except Full:
            self._daemon_log.debug("write_nmea: Serial TX Queue Full")

    def write_string(self, string):
        self._daemon_log.debug("Writing string to output queue: %s" %
                               (string.rstrip('\r\n')))
        try:
            self.serial_tx_queue.put(string, block=False)
        # If queue full, then ignore
        except Full:
            self._daemon_log.debug("write_string: Serial TX Queue Full")

    def readline(self):
        """Returns a \n terminated line from the modem.  Only returns complete lines (or None on timeout)"""
        rl = self._serialport.readline()

        if rl == "":
            return None

        # Make sure we got a complete line.  Serial.readline may return data on timeout.
        if rl[-1] != '\n':
            self._incoming_line_buffer += rl
            return None
        else:
            if self._incoming_line_buffer != "":
                rl = self._incoming_line_buffer + rl
            self._incoming_line_buffer = ""
            return rl

    def write(self, data):
        self._serialport.write(data)
Example #7
0
#
# swift.set_position(x=200, y=0, z=100)
# time.sleep(5)
# print(swift.get_polar())
#
#
# # # def test(ret):
# # #     print(ret)
# # #
# # # print(metal.send_cmd_sync('G0'))
# # # metal.send_cmd_async('G0', callback=test)
# #
# # while True:
# #     time.sleep(1)

import time
from serial import Serial

com = Serial("COM12", baudrate=115200)

print('getCD:', com.getCD())
print('getCTS:', com.getCTS())
print('getDSR:', com.getDSR())
print('getRI:', com.getRI())
print('get_settings:', com.get_settings())
print('timeout:', com.timeout)

while True:
    time.sleep(1)

class IridiumCsdListener(object):
    def __init__(self, port, baudrate=9600, timeout=0.1, unified_log=None):
        self._incoming_line_buffer = ""

        self.connection_type = "iridium_csd_listener"

        self.port = port
        self.baudrate = baudrate
        self.timeout = timeout

        if unified_log is None:
            unified_log = UnifiedLog(log_path='.')
        self._daemon_log = unified_log.getLogger("iridium_csd_listener")
        self._nmea_in_log = unified_log.getLogger("nmea.from.iridium_csd")
        self._nmea_out_log = unified_log.getLogger("nmea.to.iridium_csd")
        self._daemon_log.setLevel(logging.INFO)
        self._nmea_in_log.setLevel(logging.INFO)
        self._nmea_out_log.setLevel(logging.INFO)

        self._serialport = Serial(port, baudrate, timeout=self.timeout)
        self.serial_tx_queue = Queue()

        self.nmea_listeners = []

        self.iridium_buffer_length = 20

        self._last_connected_status = self.is_connected

        self._thread = Thread(target=self._listen)
        self._thread.setDaemon(True)
        self._thread.start()

        self._daemon_log.info("Iridium Listener Started")
        self.iridium_init()


    @property
    def is_connected(self):
        connected = self._serialport.getCD() and self._serialport.isOpen()
        return connected

    @property
    def can_change_baudrate(self):
        return True

    def change_baudrate(self,baudrate):
        self.baudrate = baudrate
        self._serialport.baudrate = baudrate
        return baudrate

    def iridium_init(self):
        sleep(1)
        self._serialport.setDTR(False)
        self._daemon_log.info("Clear DTR")
        sleep(0.1)
        self._serialport.setDTR(True)
        self._daemon_log.info("Set DTR")
        sleep(0.1)
        self._serialport.write("AT+CREG?\r\n")
        self._daemon_log.info("> AT+CREG?")
        sleep(1)
        self._serialport.write("AT+CEER\r\n")
        self._daemon_log.info("> AT+CEER")
        sleep(1)
        self._serialport.write("AT+CSQ?\r\n")
        self._daemon_log.info("> AT+CSQ?")
        sleep(5)

    def close(self):
        self._serialport.setDTR(False)
        sleep(0.2)
        self._serialport.close()

    def _listen(self):
        while True:
            # Log connection changes
            if self.is_connected is not self._last_connected_status:
                self._daemon_log.info("Iridium Connected: {}".format(self.is_connected))
                self._last_connected_status = self.is_connected

            if self._serialport.isOpen():
                msg = self.readline()
                if msg is None:
                    msg = ""

                # Figure out if this is an NMEA message or Iridium traffic, in the dumbest way possible.
                if len(msg) > 0:
                    if msg[0] == '$':
                        self._process_incoming_nmea(msg)
                    else:
                        self.process_iridium(msg)

                self._process_outgoing_nmea()

            else:  # not connected
                sleep(0.5)  # Wait half a second, try again.

    def process_iridium(self, msg):
        # This is called by the primary serial processing loop.
        # It will be called whenever we have a line of data, or periodically (based on a timeout)

        # Basically, all we do here is pick up the phone.
        if "RING" in msg:
            self._serialport.write("ATA\r\n")
            self._daemon_log.info("> ATA")

        # and log messages
        if msg is not "":
            self._daemon_log.info("< {}".format(msg.strip()))

    def _process_incoming_nmea(self, msg):
        self._nmea_in_log.info(msg.rstrip('\r\n'))

        msg = Message(msg)

        try:
            for func in self.nmea_listeners:
                func(msg)  # Pass the message to any custom listeners.
        except Exception as e:
            self._daemon_log.warn("Error in NMEA listener: ")
            self._daemon_log.warn(repr(e))

    def _process_outgoing_nmea(self):
        # Now, transmit anything we have in the outgoing queue.
        if self.is_connected:
            try:
                txstring = self.serial_tx_queue.get_nowait()
                self._serialport.write(txstring)
                self._nmea_out_log.info(txstring.rstrip('\r\n'))
            #If the queue is empty, then pass, otherwise log error
            except Empty:
                pass
            except:
                self._daemon_log.exception("NMEA Output Error")
        else:
            # If we aren't connected, remove old messages from the queue.
            overflow = self.serial_tx_queue.qsize() - self.iridium_buffer_length
            if overflow > 0:
                for i in range(overflow):
                    txstring = self.serial_tx_queue.get_nowait()
                    self._daemon_log.info("Dropped outgoing NMEA: {}".format(txstring.rstrip('\r\n')))



    def write_nmea(self, msg):
        """Call with the message to send, as an NMEA message.  Correct checksum will be computed."""

        if type(msg) == str:
            # Automagically convert it into an NMEA message (or try, at least)
            msg = Message(msg)

        message = ",".join([str(p) for p in [msg['type']] + msg['params']])
        chk = nmeaChecksum(message)
        message = "$" + message.lstrip('$').rstrip('\r\n*') + "*" + chk + "\r\n"

        # Queue this message for transmit in the serial thread
        self._daemon_log.debug("Writing NMEA to output queue: %s" % (message.rstrip('\r\n')))
        try:
            self.serial_tx_queue.put(message, block=False)
        # If queue full, then ignore
        except Full:
            self._daemon_log.debug("write_nmea: Serial TX Queue Full")

    def write_string(self, string):
        self._daemon_log.debug("Writing string to output queue: %s" % (string.rstrip('\r\n')))
        try:
            self.serial_tx_queue.put(string, block=False)
        # If queue full, then ignore
        except Full:
            self._daemon_log.debug("write_string: Serial TX Queue Full")

    def readline(self):
        """Returns a \n terminated line from the modem.  Only returns complete lines (or None on timeout)"""
        rl = self._serialport.readline()

        if rl == "":
            return None

        # Make sure we got a complete line.  Serial.readline may return data on timeout.
        if rl[-1] != '\n':
            self._incoming_line_buffer += rl
            return None
        else:
            if self._incoming_line_buffer != "":
                rl = self._incoming_line_buffer + rl
            self._incoming_line_buffer = ""
            return rl

    def write(self, data):
        self._serialport.write(data)
class SerialButtonManager(object):
    def __init__(self, device, service):
        self.device = Serial(device)
        self.service_name = service

    @property
    def service_is_running(self):
        output = check_output(
            [
                SERVICE_PATH,
                self.service_name,
                'status',
            ]
        )
        output = output.strip()
        logger.debug('Service status result: %s', output)

        if 'stop' in output:
            logger.debug('Service is not running.')
            return False
        elif 'start' in output:
            logger.debug('Service is running.')
            return True

        raise RuntimeError('Unable to get service status: "%s"' % output)

    @property
    def button_is_pressed(self):
        return self.device.getCD()

    def start_service(self):
        try:
            check_call(
                [
                    SERVICE_PATH,
                    self.service_name,
                    'start',
                ]
            )
        except CalledProcessError as e:
            logger.error(
                "Unable to start service: %s", e
            )

    def stop_service(self):
        try:
            check_call(
                [
                    SERVICE_PATH,
                    self.service_name,
                    'stop',
                ]
            )
        except CalledProcessError as e:
            logger.error(
                "Unable to stop service: %s", e
            )

    def _resolve_state_mismatch(self):
        running = self.service_is_running
        pressed = self.button_is_pressed

        if pressed and not running:
            logger.warning(
                "State mismatch detected; button was pressed, but "
                "service was not running; starting service."
            )
            self.start_service()
        elif not pressed and running:
            logger.warning(
                "State mismatch detected; button was not pressed, but "
                "service was running; stopping service."
            )
            self.stop_service()

    def run(self):
        self._resolve_state_mismatch()
        last_checked_state = time.time()

        last_state = self.button_is_pressed
        while True:
            pressed = self.button_is_pressed
            if pressed != last_state:
                last_checked_state = time.time()

                if pressed:
                    logger.info(
                        "Button press detected: starting service."
                    )
                    self.start_service()
                else:
                    logger.info(
                        "Button unpress detected: starting service."
                    )
                    self.stop_service()
            elif last_checked_state + STATE_CHECK_INTERVAL < time.time():
                last_checked_state = time.time()
                self._resolve_state_mismatch()

            last_state = pressed
            time.sleep(0.5)