#!/usr/bin/python
# -*- coding: utf-8 -*-

# virtGPIO.py   V0.9.7

"""
"Virtual GPIO" uses an arduino as a GPIO device to a Raspberry Pi or PC.
This system uses USB serial (or rPi's UART) to control (and power??) the arduino.
This file is the PC end of "virtual-gpio", ie the python library module that your own python code imports.
This code is compatible with python 2.7 and python 3.3.
Includes functionality of "import RPi.GPIO" and "import smbus" and "import spidev"

"""

# PC instructions (Linux or Windows or Mac):
# You require python 2.7 or 3.3 installed, and "pyserial".
# The arduino simply stays connected to PC by USB cable.
# On Windows, you may need to experiment with COM port numbers assigned to Arduino.

# Raspberry Pi instructions:
# You require "pyserial" installed on the rPi.
# You may choose: (1) USB cable between, or (2) TTL wires from rPi uart pins to arduino uart pins.
# Here is how to configure for using the rPi's inbuilt uart:
# (sudo) edit /boot/cmdline.txt to remove the two phrases "console=ttyAMA0,115200  kgdboc=ttyAMA0,115200"
# (sudo) edit /etc/inittab to comment out the line containing "/dev/ttyAMA0" (the last line?)
# (sudo) edit /boot/config.txt by adding a new line "init_uart_clock=16000000"
# Reboot the rPi.
# Now, (1) uart is free and available for virtGPIO (rx -> tx and tx -> rx),
#      (2) and it can go at baudrate speeds way over the usual 115200,
# Configured as above, the rPi's own GPIO-uart works quite well with virtGPIO arduino at 500000 baud.
# But see WARNING below on interfacing tx and rx wires.

# Probably you can power the arduino from rPi's 5V supply, even using UART connection.
# But any substantial current requirement (eg servos beyond one small test one) needs provision of proper supply.

# WARNING: rPI UART 3.3V TX and RX LINES MUST NOT CONNECT DIRECTLY TO 5V RX and TX OF ARDUINO ...
#   ... You must use some form of voltage level shifting to 5V arduino, or use a 3.3V version arduino.

# ADDITIONALLY ...
# Want to load the arduino sketch via this same rPi uart line?  Refer to notes at top of "VirtGPIO.ino"



if __name__ == '__main__':
    print ("virtGPIO.py is an importable module for PC or Raspberry Pi:")
    print ("...  import virtGPIO as GPIO")
    print ("virtGPIO.py talks by USB-serial with ARDUINO-ftdi loaded with VirtGPIO.ino")
    print ("")
    exit()


import time
try:
    import serial
except:
    print("Virtual-GPIO ERROR: pyserial seems not installed on your python?")
    exit()
import sys
import glob
import os


def hardResetARD():
    Serial.setDTR(False)
    time.sleep(0.3)
    Serial.setDTR(True)
    time.sleep(3)   # and wait for it

def softResetARD():
    # reset Arduino in software. (AVR registers may not be initialised to full reset)
    _SerialWrite("0-");
    time.sleep(2.1)


def _i8(x):
    # x is either an int8 or a single character string
    # Used for processing _SerialRead characters from either str (python2) or bytes (python3)
    # return is always int8
    if type(x) == type(""):
        return ord(x)
    return x

def _SerialWrite(s):
    # accepts List (of int8), or string (to int7), free choice, or mixed. Avoid strings with chrs over 0x7f
    if type(s) == type(""):  # type str
        if sys.version_info.major == 2:   # python2
            Serial.write(s)     #  py2 accepts strings (with int8) directly
        else:  # python3
            Serial.write(s.encode('UTF-8'))   # -> bytes.  But only to 7-bit int
    else:   # type list
        if sys.version_info.major == 2:
            Serial.write(''.join(chr(e) for e in s))   # -> str incl int8
        else:
            Serial.write(bytes(s))    # -> bytes with int8


def _SerialRead(count=1):
    r = Serial.read(count)
    # python2 returns type str of int8      strr[x] -> "?"
    # python3 returns type bytes of int8    bb[x] ->  int8
    # receiving routines to handle the difference!  [_i8() function useful]
    return r


def _serial_ports():
    #  <http://stackoverflow.com/questions/12090503/listing-available-com-ports-with-python>
    if sys.platform.startswith('win'):
        ports = ['COM' + str(i + 1) for i in range(256)]
    elif sys.platform.startswith('linux') or sys.platform.startswith('cygwin'):
        # this is to exclude your current terminal "/dev/tty"
        ports = glob.glob('/dev/tty[A-Za-z]*')
    elif sys.platform.startswith('darwin'):
        ports = glob.glob('/dev/tty.*')
    else:
        raise EnvironmentError('Unsupported platform')
    result = []
    for port in ports:
        try:
            s = serial.Serial(port)
            s.close()
            result.append(port)
        except (OSError, serial.SerialException):
            pass
    return result

def set_baudrate(self, baud):
    try:
        self.setBaudrate(baud)
    except AttributeError:
        self.baudrate = baud

Serial = None
verboseSetup = False
STDTIMEOUT = 0.8

# Following are DEFAULT portnames and baudrates that are attempted for virtGPIO.
# Optional file serialConfig.py can override these defaults.
baudlist = [500000, 115200, 250000]
portlist = ['/dev/ttyUSB0', '/dev/ttyUSB1', '/dev/ttyUSB2', '/dev/ttyACM0', 'COM1', "COM3", "COM8", "COM9"]
# (these lists may be edited. 500000 baud recommended, list it first.)
# (I'm not a Mac person. So of course, I haven't tested a Mac.
#           Try "ls /dev/tty.*" in terminal to help find your Mac serial port name.)
# Alternatively (BETTER!), use optional file "serialConfig.py" to override the baud and port settings above.

try:
    from serialConfig import *
except:
    pass

if portlist == "SMART":
    portlist = _serial_ports()
    if verboseSetup:
        print ("SMART Serial Port Seek:"),
        print (portlist)
elif verboseSetup:
    print ("Ports to test:")
    print (portlist)


for k in range(len(portlist)):
    try:
        Serial = serial.Serial(portlist[k], 9600, timeout=STDTIMEOUT, dsrdtr=False)
        break;
    except:
        pass

if type(Serial) == type(None):
    print ("Failed to initialise Serial Port.")
    print ("Tried %s." % portlist)
    print ("Check your port name??")
    exit()


if verboseSetup:
    print ("Device found at %s " % Serial.port)


if not Serial.port == "/dev/ttyAMA0":
    # NOT for Raspberry Pi UART port: it has no DTR line
    if verboseSetup:
        print ("Attempting hard reset of arduino ...")
    hardResetARD()

# Now, we know that the serial port exists. It COULD be another device, not our arduino !
# Try certain baudrates to talk with arduino ...
if verboseSetup:
    print ("Baudrates to test:")
    print (baudlist)

for k in range(len(baudlist)):
    try:
        if verboseSetup:
            print ("Trying baudrate"),
            print (baudlist[k])
        set_baudrate(Serial,baudlist[k])   # Try each baudrate in turn from our list
    except:
        print ("Device at %s can not operate at baudrate %d" % (Serial.port,baudlist[k]))
        print ("Change options in 'serialConfig.py'")
        exit()
    time.sleep(0.1)
    if verboseSetup:
        print ("Try Ping @"),
        print (Serial.baudrate)
    _SerialWrite("=")  # The sync() call used as a "ping"
    ping = _SerialRead()   # Fetch one reply character
    if len(ping)>0:
        if _i8(ping[0]) == ord("="):    # Does this look like correct response?
            break;
    if k==(len(baudlist)-1):   # tried & failed on all options
            print ("Arduino not in contact. Tried baudrates"),
            print (baudlist)
            exit()
if verboseSetup:
    print ("Found Arduino @ baudrate %d " % Serial.baudrate)

if Serial.port == "/dev/ttyAMA0":   # Raspberry Pi GPIO uart?
    # on rPi UART mode we needed to establish serial contact before reset: this reset is a software command.
    if verboseSetup:
        print ("Soft reset of arduino ...")
    softResetARD()

if verboseSetup:
    print ("Successful Setup")
    print ("")

# END OF SETUP



# All below is API for user functions















######################################################################


HIGH = 1
LOW = 0

A0 = 14   # the analog pins
A1 = 15
A2 = 16
A3 = 17
A4 = 18
A5 = 19
A6 = 20
A7 = 21

OUT = 1
IN = 0


# RPI.GPIO constants for compatibility:
PUD_UP = 22
PUD_DOWN = 21
PUD_OFF = 20
BCM = 11
BOARD = 10
VERSION = "0.9.7"
RPI_REVISION = 0

def setmode(mode):
    # Compatibility for rpi code GPIO.setmode(GPIO.BOARD/BCM)
    pass

def cleanup():
    GPIO.softReset()

def setwarnings(mode):
    # Not used. Compatibility for rpi code GPIO.setwarnings(false)
    pass


def setup(dpin, mode, pull_up_down=0):
    # Cast it into arduino numbers
    INPUT_PULLUP = 2
    if pull_up_down == PUD_UP:
        mode = INPUT_PULLUP
    if pull_up_down == PUD_DOWN:
        print ("Virtual-GPIO (arduino) has no pulldown. Continuing ...")
    _SerialWrite("s" + chr(dpin) + chr(mode));
def pinMode(dpin, mode):   # arduino style
    setup(dpin, mode)

def output(dpin, hilo):
    _SerialWrite("w" + chr(dpin) + chr(hilo))
    _SerialRead()  # sync only
def digitalWrite(dpin, hilo):  # arduino style
    output(dpin, hilo)

def input(dpin):
    _SerialWrite("r" + chr(dpin))
    return _serialread1int8(0xff)
def digitalRead(dpin):    # arduino style
    input(dpin)

def digitalPreciseRead(pin):
    # Uses analog comparator on to compare the pin's ANALOG volts against 1.1V chip reference.
    # ONLY for pins d7 and A0-A7
    AVR.bitClear(AVR.ADCSRA, 7)   # disable ADC
    AVR.write8(AVR.ACSR, 0x40);  # set internal ref 1.1 for comparator
    if pin == 7:
        AVR.write8(AVR.ADCSRB, 0)  # set d7 as input
    elif (pin>= A0 and pin <= A7):
        AVR.write8(AVR.ADCSRB, (pin-14) + 0x40)  # set adc mux address 0-7 as input
    else:
        AVR.bitSet(AVR.ADCSRA, 7)   # re-enable ADC
        return 0xf4   # == f4 bad pin
    r = ((~AVR.read8(AVR.ACSR)) >> 5) & 1   # comparator output bit
    AVR.bitSet(AVR.ADCSRA, 7)   # re-enable ADC
    return r


def digitalReadAll():
    return (AVR.read8(AVR.PIND) & 0xf6) +  ((AVR.read8(AVR.PINB)&0x3f)<<8)
    # d2 to d7, and d8 to d13

def pwmWrite(dpin, dcycle):
    _SerialWrite("p" + chr(dpin) + chr(dcycle))   # 8bit write
    # p3 p11 Timer2,  p9 p10 Timer1,   p5 p6 Timer0
    # This is the regular analogWrite of the arduino


def analogRead(apin):  # regular arduino style analog read
    # HOWEVER, use the A0 A1 numbering (ie 14-21, not 0-7)
    if apin<=7:
        apin += 14
    _SerialWrite("a" + chr(apin))
    return _serialread2int16(0xfffa)

def analogReadAll():
    _SerialWrite("A")
    buf2 = _SerialRead(16)
    # pyserial's serial.read() returns STRING or BYTES (of 16 CHARs)
    # but we want LIST of 8 x 16-bit integers as our return format
    L = len(buf2)
    if L<16:        # timeout?
        buf3 = [0] * 8    # empty dummy of the correct length/format
    else:
        buf3 = []
        for k in range(8):
            buf3.append(_i8(buf2[k*2]) + 256*_i8(buf2[1+k*2]))
    return buf3

def pulseOut(dpin, mode, usec):   # pulse to 255 uSec
    _SerialWrite("U")
    _SerialWrite([dpin, mode&1, usec])


def pulseIn(pin, hilo, timeout1, timeout2):   #  pulsein
    # int timeouts: To start 20 - 5000 usec, length 100uSec - 25000uSec   8 bit
    _SerialWrite("I" + chr(pin) + chr(hilo))
    _SerialWrite([timeout1//20,  timeout2//100])
    # 16-bit integer return in uSec
    return _serialread2int16(0xfff2)
    # Note return error codes could be FFE3 (pin conflict), FFFF (not idle),
    #    FFFE (not started on time), FFFA (fail to finish in time)
    # These come from pulse_In function or case 'I' section in VirtGPIO.ino


def analogHiSpeed():
        # This moves ADC clock from divider 128 to divider 16
        # Not very useful for single analog reads, but some speedup for analogReadAll()
        AVR.bitClear(AVR.ADCSRA, AVR.ADPS0);
        AVR.bitClear(AVR.ADCSRA, AVR.ADPS1);
        AVR.bitSet(AVR.ADCSRA, AVR.ADPS2);

def setActivityLed(pin):
  _SerialWrite ("_" + chr(pin))
  # The Activity Pin defaults as d13. It flashes ON while arduino is processing one incoming serial packet.
  # Can be anytime reassigned to be a different pin, or to 0 which means disabled.
  # Other need for pin13 needs ActivityPin to have been reassigned first. (Altho SPI will "kick" it off anyway)


###############################################

class _AVR_:    # class not intended to be instantiated by an outside script
    ADCSRA = 0x7a
    ADPS0 = 0
    ADPS1 = 1
    ADPS2 = 2
    TCNT1 = 0x84   # (0x84 lsB    0x85 msB)
    TCCR1A = 0x80
    TCCR1B = 0x81
    CS10 = 1
    CS11 = 2
    CS12 = 4
    PINB = 0x23    # direct register addresses of digital inputs
    PINC = 0x26
    PIND = 0x29
    PORTB = 0x25
    PORTC = 0x28
    PORTD = 0x2b
    DDRB = 0x24
    DDRC = 0x27
    DDRD = 0x2a
    ACSR = 0x50
    ADCSRA = 0x7a
    ADCSRB = 0x7B

    # add to this list from 328 manual as needed

    def __init__(self):
        pass


    def read8(self, register):
       _SerialWrite("#")
       _SerialWrite([register])
       return _serialread1int8(0)

    def write8(self, register, data8):
        _SerialWrite("*")
        _SerialWrite([register, data8])


    def read16(self, register, errcode=0):
       _SerialWrite("@")
       _SerialWrite([register])
       return _serialread2int16(errcode)

    def write16(self, register, data16):
        _SerialWrite("&")
        _SerialWrite([register, data16&0xff, data16>>8])


    def bitSet(self, register, bit):
        _SerialWrite("[")
        _SerialWrite([register&0xff, bit])

    def bitClear(self, register, bit):
        _SerialWrite("]")
        _SerialWrite([register&0xff, bit]);

AVR = _AVR_()
# We pre-define the AVR object HERE. Outside script can now use this AVR object this way.

###############################################



class Servo:
    # Using any servos disables PWM on pins 9-10  (uses Timer1)

    def __init__(self, pin):   #  pin 3 - 10
        setup(pin, OUT)    # fix V0.9.7
        self.pin = pin
        pass

    def write(self, value):
        _SerialWrite("V" + chr(self.pin))   # 2-10
        _SerialWrite([value])  #  0-180

    def stop(self):
        _SerialWrite("v" + chr(self.pin))


###############################################



class SpiDev:
    # minimal emulation of rPi-style spidev
    # Note arduino pin 10 is conventionally used as the CSN/SS* output pin.  It ought be preferred as first CE.
    # This code supports any number of devices on SPI bus,
    # Some of the comments below imply RF24L01 2.4GHz transceiver is attached to SPI ...
    # ... however the SPI should be generic enough anyway.

    max_speed_hz = 0  # not currently implemented

    def __init__(self):
        self.mode = 0   # default
        return

    def mode(self, mode):
        self.mode = mode&3

    def max_speed_hz(self, speed):
        pass

    def open(self, bus, device):
        # bus is for RPI syntax compatibility only
        # Also, where RPI uses 0 or 1 for device (CE0, CE1), virtGPIO uses actual chip enable pin
        self.cePin = device
        _SerialWrite("S" + chr(self.cePin));  # take SPI control of pins 11-13, plus CE-pin


    # RPI compatible:
    #     import spidev                spidev module.   Contains SpiDev class.
    #                                  (On virtGPIO, (virt)GPIO module contains SpiDev class)
    #     mySPI = spidev.SpiDev()      Create SpiDev object
    #     mySPI.open(bus, device)      bus 0 (Model B) or bus 0 / 1 (Model B+)   device = 0 or 1 (CE0 CE1)
    #     mySPI.mode = 2               SPI mode 0 to 3
    #     mySPI.xfer2([listofTXint8])  Returns RX list of int8



    def xfer2(self, buf):
        #  buf arrives as a list of 8-bit integers,
        #  max len 127, but this is unrealistic as the serial buffer is only 127. Suggest use <80
        #  buf includes first byte = cmd byte, plus the payload  - RF24
        #  At the arduino, the CE* select is automatically asserted (low) around this block.
        L = len(buf)
        # set it as STRING of CHARS for serial.write:
        #print "X" + chr(self.cePin) + chr(self.mode) + chr(L) + ''.join(chr(e) for e in buf)
        _SerialWrite("X" + chr(self.cePin) + chr(self.mode) + chr(L))
        _SerialWrite(buf)
        buf2 = _SerialRead(L)
        # pyserial's serial.read() returns STRING or BYTES (of CHARs)
        # but we want LIST of 8-bit integers as our return format
        L2 = len(buf2)
        if L2<L:        # timeout?
            buf3 = [0] * L    # empty dummy of the correct length/format
            buf3[0] = 0xfe    # our fail flag: optionally caller can watch for this.
        else:
            buf3 = []
            for k in range(L):
                buf3.append(_i8(buf2[k]))
        return buf3
        # return = same length as input, on RF24, first byte of return is STATUS


    def write(self, buf):
        return self.writebytes(buf)

    def writebytes(self, buf):   # one-way spi write,
        #  handles large blocks of data, breaks into chunks of 60
        #  At the arduino, the CE* select is automatically asserted (low) around this WHOLE (multi-)block.
        L = len(buf)

        while L > 60:
            buf1 = buf[0:60]  # save leading 60
            buf = buf[60:]    # dump leading 60
            L -= 60           # and counter drops by 60
            # process precisely 60 with TO_BE_CONTIN flag
            _SerialWrite("x" + chr(self.cePin) + chr(0x10 + self.mode) + chr(60))
            _SerialWrite(buf1)
            _SerialRead()  # wait on some reply  ( for syncing)

        # Now we have 1 to 60 in last block
        _SerialWrite("x" + chr(self.cePin) + chr(self.mode) + chr(L))
        _SerialWrite(buf)
        _SerialRead()  # wait on a reply
        return

    def fill(self, count16, data16):
        # Repetitive write of 1 or 2 bytes. NOT a RPI function!
        # Useful for TFT clear screen operations on virt-gpio (for speed)
        _SerialWrite("g" + chr(self.cePin) + chr(self.mode))
        _SerialWrite([count16&0xff, count16>>8,  data16&0xff, data16>>8])
        _SerialRead()  # just for sync-ing

    def close(self):
        print ("SPI CLOSE not supported")

###############################################

class SerialTx:
    # up to five TTL (5V) "COM ports", transmit only, bit-banged (no interrupts or timers)
    # unbuffered, 2400 baud, blocking (exc port 0) until sent.
    # All ports are at same baudrate, compiled at arduino.
    # (Can recompile to 75 300 2400 9600 19200 38400 57600.)
    # PORT 0 IS DIFFERENT: output at arduino is buffered and transmitted by loop() when idle.
    # Ports 1 to 4 are direct transmit.
    # Port 0 was used for internal sketch diagnostic. Using it here will over-ride diag use.

    # To send binary data, submit it as a character string.

    def __init__(self, port, pin):  # port 0-4, pin any digital-out capable pin
        self.port = port
        _SerialWrite([0xc0, port, pin])

    def Print(self, x):   # single parameter
        if not (type(x) == type ("123")):
                x = str(x)   # Whatever format, coerce it into string
        _SerialWrite([0xc1, self.port, len(x)])
        _SerialWrite(x)
        return _serialread1int8(0)

    def PrintLN(self, x):
        x = str(x) + "\n"
        return self.Print(x)

    def Write(self, x):
        self.Print(chr(x))


###################################################

class SerialRx:
    # pin 8 only
    def __init__(self, baud):
        _SerialWrite([0xd0, 3])

    def available(self):
        _SerialWrite([0xd1])
        # returns a count
        return _serialread1int8(0)

    def read(self, count):
        _SerialWrite([0xd2, count])
        buf2 = _SerialRead(count)
        # pyserial's serial.read() returns STRING or BYTES (of CHARs)
        # but we want LIST of 8-bit integers as our return format
        L2 = len(buf2)
        if L2<count:        # timeout?
            buf3 = [0] * count    # empty dummy of the correct length/format
        else:
            buf3 = []
            for k in range(count):
                buf3.append(_i8(buf2[k]))
        return buf3

###############################################

class PWM:
    pin = 0

    def __init__(self, pin, freq):
        self.pin = pin

    def start(self, dc):
        pwmWrite(self.pin, dc)

    def changeFrequency(self, freq):
        pass

    def changeDutyCycle(self, dc):
        pwmWrite(self.pin, dc)

    def stop(self):
        pwmWrite(self.pin, 0)


###############################################

class I2C:

    def __init__(self, enablePullups = True):
        _SerialWrite("!" + chr(enablePullups))

    # 3 primary calls:

    def write(self, port, txbuf):
        # txbuf = list of integers
        if len(txbuf) > 32:
            print ("I2C limit at arduino is 32 chars !!")
        L = len(txbuf)
        _SerialWrite("W" + chr(L) + chr(port))
        _SerialWrite(txbuf);
        _SerialRead()   # just for syncing   v0.9.5


    def read(self, port, count):
        # returns list, with result code then []*count
        _SerialWrite("c")
        _SerialWrite([count, port])
        buf2 = _SerialRead(count)
        count2 = len(buf2)
        buf3 = [0] * (count+1)    # empty dummy of the correct length/format
        if count2<count:        # timeout?
            buf3[0] = 0xf3    # our fail flag: optionally caller can watch for this.
        else:
            for k in range(count):
                buf3[k+1] = _i8(buf2[k])
        return buf3

    def writeRead(self, port, txchr, rxcount):   # accepts ONE byte of tx before rx
        self.write(port, [txchr])
        return self.read(port, rxcount)


    def detect(self):
        # Diagnostic function   equiv to i2cdetect utility in linux
        _SerialWrite("?")
        time.sleep(0.05)
        while(Serial.inWaiting()):   # anything to report
            print ("I2C detected at: \\0x%02x " % _i8(_SerialRead()))



    # FOLLOWING ARE SMBus STYLE COMMANDS (derived):

    def read_byte(self, addr):
        r=self.read(addr, 1)
        return r[1]

    def read_byte_data(self, addr, cmd):
        r = self.writeRead(addr, cmd, 1)
        return r[1]

    def read_word_data(self, addr, cmd):
        pass  # nyi

    def write_byte(self, addr, cmd):
        self.write(addr, [cmd])

    def write_byte_data(self, addr, cmd, data):
        self.write( addr, [cmd, data])

    def write_word_data(self, addr, cmd, data):
        pass  # nyi

    def write_block_data(self, addr, cmd, data):
        pass   # nyi

    def write_i2c_block_data(self, addr, cmd, data):   # March 2015
        self.write( addr, [cmd]+data)



###############################################

class Stepper:
    WIRE2 = 2
    WIRE4 = 4
    def __init__(self, id, wires, pin1, stepsPerRev):   # wires 2 or 4.
        _SerialWrite([0xb1, id & 1, wires & 7, pin1, stepsPerRev&0xff, stepsPerRev >>8])
        self.id = id&1
        self.SPR = stepsPerRev

    def setSpeedRPM(self, speedRPM):   #  integer speed in RPM (positive number)
        _SerialWrite([0xb2, self.id, speedRPM&0xff, speedRPM>>8])
        self.speedRPM = abs(speedRPM)

    def step(self, steps, wait=True):   # steps negative for reverse rotation +-32000
        self.steps = abs(steps)
        steps = cInt16(steps)
        _SerialWrite([0xb3, self.id, steps&0xff, steps>>8] )
        if wait:
            self.waitToFinish()

    def stepsLeft(self):
        _SerialWrite([0xb4, self.id] )
        return _serialread2int16(0)

    def waitToFinish(self):
        wtime = ((1.3 * self.steps) // self.speedRPM) * 60 // self.SPR
        t1 = time.time()
        r=0
        while (time.time() - t1) < wtime:
            if self.stepsLeft() == 0:
                r=1
                break
        return r    # optional return code 0=timeout   1=normal finish



###############################################

class Intcounter:   # two counters using INT0 and INT1
    # These counters operate only on pins d2/d3. (This is a 328 chip hardware constraint.)
    # if QUAD encoder selected, d2 pairs with d4,  d3 pairs with d5

    RISING = 0x03   # AVR code
    FALLING = 0x02  # AVR code
    CHANGE =  0x01  # AVR code
    QUAD = 0x05     # change + 0x04

    def __init__(self, pin, mode):    # pin 2 or 3, mode as above
        if pin==2 or pin == 3:
            _SerialWrite("2" + chr(pin) + chr(mode))


    def read(self, andZero=False):   # optional counter clear, read as time-matched pair.
      _SerialWrite("," + chr(andZero &1))
      c0 = _serialread2int16(0xffff)
      c1 = _serialread2int16(0xffff)
      return [c0, c1]



###############################################

class HWcounter:    # true (one-only) 16bit MCU hardware counter: no interrupts.
    # This counter operates only on pin d5. (This is a 328 chip hardware constraint.)
    # This function kills any other timer1 operation (or PWM on pins 9, 10)


    def __init__(self):
      AVR.write8(AVR.TCCR1A, 0)  # reset timer/counter control register A
      AVR.write8(AVR.TCCR1B, AVR.CS10+AVR.CS11+AVR.CS12) # Counter on external pin, rising edge
      self.zero()
      # once initialised, the counter always is active

    def zero(self):   # CLEAR the counter to 0x0000
      AVR.write16(AVR.TCNT1, 0)

    def read(self):
      return AVR.read16(AVR.TCNT1, 0xffff)



###############################################


class InfraRedRx:
    # Uses Timer2, so PWM on p3, p11 disabled

    def __init__(self, pin):
      self.devicenum = 0   # ID of hand remote
      _SerialWrite("F" + chr(pin & 0x0f))

    def read(self):     # fetch IR code from remote
        _SerialWrite("f");
        ir = _serialread4int32(0xffffffff)
        self.devicenum = ir & 0xFFFF
        return (ir >>16)&0xff

    def device(self):    # read remote device number of latest read
        return self.devicenum
        # only valid immediately after a read


###############################################

class PWMplus:
    # only pins 9 &/or 10.  Uses Timer1

    def __init__(self):
        _SerialWrite ([0x81])
        self.pinBusy = [0,0]

    def setPeriod(self, usec):   # usec from 1 to 1,000,000
        _SerialWrite ([0x82, usec & 0x0000ff, (usec & 0x0FF00)>>8, usec//65536] )

    def initPin(self, pin, duty):   # pin = 9 or 10 only.  duty 0 to 1023
        if pin < 9 or pin > 10:
            return
        if self.pinBusy[pin-9]:
            self.changeDuty(pin, duty)
            return
        self.pinBusy[pin-9] = 1
        _SerialWrite ([0x83, pin, duty & 0x00ff, duty >>8])

    def changeDuty(self,pin,duty):
        _SerialWrite ([0x84, pin, duty & 0x00ff, duty >>8])


    def releasePin(self,pin):
        if pin < 9 or pin > 10:
            return
        _SerialWrite ([0x85, pin] )
        self.pinBusy[pin-9] = 0

    def stop(self):
        _SerialWrite ([0x86, 0])

    def restart(self):
        _SerialWrite ([0x86, 1])

    def tone(self, pin, freq):
        self.setPeriod(1000000 // freq)
        self.initPin(pin, 511)

    def noTone(self, pin):
        self.releasePin(pin)

###############################################


# DIAGNOSTIC/DEBUG STUFF:

class Trace:   #developer use.
    def __init__(self):
        pass
    def Start(self):
            _SerialWrite("^")

    def Stop(self):
       _SerialWrite("<")

    def Print(self):
        _SerialWrite(">")
        # Will not print if Serial0 (ie COM port 0) is not initialised

def mopup():
    # Testing only
    #time.sleep(.003)
    #print "Mop up: " ,
    while(Serial.inWaiting()):   # anything to mopup???
        r=_SerialRead()
        print ("   {%x}" % ( _i8(r))),


###############################################


def sync(timeout = 0.25, printping = False):   # 0.5 - 1.0 mSec when no catchup needed
    # Use in user code after several non-reporting commands, so that sender can not
    # get too many serial characters ahead of slave unit.
    # Only likely calls that MIGHT need this are multiple AVR.write, Servo.write, SerialTX.write
    # Note GPIO.output and I2C.write should be immune, with inbuilt sync function.

    # Also useable as a visible "ping"
    _SerialWrite("=")
    Serial.timeout = timeout
    r=_serialread1int8(0xf6)
    Serial.timeout = STDTIMEOUT
    if printping:
        # Diagnostic use
        print ("Ping response %s %x" % (chr(r),r) )   # should report "=" chr
    return r

def readFreePins():
  # Diagnostic tool
  _SerialWrite ("q")
  print ("")
  print (" GP pins    AAAAAAAA111100000000TR")
  print (" 1=free     76543210321098765432rd")
  print (bin(0x80300000 | _serialread4int32(0)))
  # top bit: just to align the display. Bits for A6 A7 added for display. They remain always avbl.


def VccRead():
  _SerialWrite("+")
  return _serialread2int16(0)/1000.0


def printCompileDate():
  # Diagnostic tool
  _SerialWrite('t')
  time.sleep(.05)
  print ("")
  print ("Compile/Load Date:  " ),
  datstr=""
  while(Serial.inWaiting()):
    datstr += chr(_i8(_SerialRead()[0]))
  print (datstr)

def getVersion():
    _SerialWrite('T')
    return _serialread1int8(0)


def clearResetFlag():       # at slave
    _SerialWrite("i")

def resetFlagIsOn():        # optional test whether slave has since suffered a reset
    return (readFlags() & 1)

def readFlags():
    # Flag bits (confirm at VirtGPIO.ino):
    # enum {F_reset=0, F_spi, F_svo, F_ir, F_i2c, F_intctr, F_pwm,
    #        F_stepr, F_actled, F_nospi, F_TxCom, F_RxCom, F_badchar=15};
    _SerialWrite("n");
    return _serialread2int16(0xff)

def restart_program():
    #Restarts the current program.
    python = sys.executable
    os.execl(python, python, * sys.argv)

def checkPinsFree(pinslist):
    # returns false if ANY of the pins is reserved
    _SerialWrite ("q")
    a = _serialread4int32(0)
    for k in range(len(pinslist)):
        if not (a & (1<<pinslist[k])):
            return False
    return True

################################################################################

# INTERNAL SUBROUTINES

def _serialread4int32(errorflag32):
      # read 4 byte and format as a python integer
      # if read fail, return known error flag
      a = _SerialRead(4)
      if len(a) >3 :
          return _i8(a[0]) + (_i8(a[1])<<8) + (_i8(a[2]) << 16) + (_i8(a[3]) << 24)
      else:
          return errorflag32

def _serialread2int16(errorflag16):
      # read 2 byte and format as a python integer
      # if read fail, return known error flag
      a = _SerialRead(2)
      if len(a) >1 :
          return _i8(a[0]) + (_i8(a[1])<<8)
      else:
          return errorflag16

def _serialread1int8(errorflag8):
      # read 1 bytes and format as python integer
      a = _SerialRead()
      if len(a) :
          return _i8(a[0])
      else:
          return (errorflag8 & 0xff)


from ctypes import *
def cByte(i):
        return c_ubyte(i).value
def cInt16(i):
        return c_ushort(i).value
def cInt32(i):
        return c_uint(i).value
def cInt32FromFloat(f):
        return cInt32(int(f))
def str1FromByte(i):
        return chr(cByte(i))


# Still flagged for possible change:
# 1. HWcounter enhance to more than 16 bits - eg see "Arduino Internals" ebook
# 2. arduino end - more robust serial timeouts
# 3. Clean python docstrings


# Changes made V0.9 -> v0.9.5  :
# Added serialConfig.py for port settings
# Sonar removed from virtGPIO core, out to simple example script
# SPI devices get individual control of mode
# i2c - option to disable arduino's internal pullups
# SPI.xfer2() out to 80 chars, and added unlimited SPI.writebytes()
# Improved RPI syntax compatibility
# Now Python 2.7 / Python 3.3 compatible

"""
 * Copyright (c) 2014 Brian Lavery <vgpio@blavery.com>   http://virtgpio.blavery.com
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
"""


