import smbus
import time
import RPi.GPIO as GPIO

#Ball sensor
BALL_SENSOR = 12

#Speed
SPEED = 50
SENSOR_MAX = 50.0
SENSOR_MIN = 20.0

P = 2
I = 0
D = 0

#I2C ADDRs
I2C_ADDR_1 = 0x38
I2C_ADDR_2 = 0x39
I2C_ADDR_3 = 0x3A
I2C_ADDR_4 = 0x48

#LED Constants
LED_ON = 0x00
LED_OFF = 0xFF

LED_1_OFF = 0xFF
LED_1_G1  = 0xFE
LED_1_G2  = 0xFC
LED_1_G3  = 0xF8
LED_1_Y1  = 0xF0
LED_1_Y2  = 0xE0
LED_1_Y3  = 0xC0
LED_1_R1  = 0x80
LED_1_R2  = 0x00
LED_1_ON  = 0x00

LED_2_OFF = 0xFF
LED_2_G1  = 0xFE
LED_2_G2  = 0xFC
LED_2_G3  = 0xF8
LED_2_Y1  = 0xF0
LED_2_Y2  = 0xE0
LED_2_Y3  = 0xC0
LED_2_R1  = 0x80
LED_2_R2  = 0x00
LED_2_ON  = 0x00

LED_3_OFF = 0xFF
LED_3_R1B = 0xFE
LED_3_R2B = 0xFD
LED_3_G1B = 0xFB
LED_3_Y1B = 0xF7
LED_3_R1T = 0xEF
LED_3_R2T = 0xDF
LED_3_G1T = 0xBF
LED_3_Y1T = 0x7F

#PWM Constants
PWM_PIN = 14
PWM_FREQ = 50

# Define GPIO to LCD mapping
LCD_RS = 7
LCD_E  = 8
LCD_D4 = 25
LCD_D5 = 24
LCD_D6 = 23
LCD_D7 = 18


# Define some device constants
LCD_WIDTH = 16    # Maximum characters per line
LCD_CHR = True
LCD_CMD = False

LCD_LINE_1 = 0x80 # LCD RAM address for the 1st line
LCD_LINE_2 = 0xC0 # LCD RAM address for the 2nd line

# Timing constants
E_PULSE = 0.0005
E_DELAY = 0.0005

#Global variables
ball_count = 0


def update_led3( bus, value ):
  data = value ^ 0xFF
  bus.write_byte( I2C_ADDR_3, data )
  return;

def update_led2( bus, value ):
  if value > 99:
    data = 0b00000000         
  elif value > 87:
    data = 0b10000000                
  elif value > 75:
    data = 0b11000000
  elif value > 62:
    data = 0b11100000
  elif value > 50:
    data = 0b11110000
  elif value > 37:
    data = 0b11111000
  elif value > 25:
    data = 0b11111100
  elif value > 12:
    data = 0b11111110
  else:
    data = 0b11111111
      
  bus.write_byte( I2C_ADDR_2, data)
  return;

def update_led1( bus, value ):
  if value > 99:
    data = 0b00000000         
  elif value > 87:
    data = 0b10000000                
  elif value > 75:
    data = 0b11000000
  elif value > 62:
    data = 0b11100000
  elif value > 50:
    data = 0b11110000
  elif value > 37:
    data = 0b11111000
  elif value > 25:
    data = 0b11111100
  elif value > 12:
    data = 0b11111110
  else:
    data = 0b11111111
                
  bus.write_byte( I2C_ADDR_1, data)
  return;

def read_ADC_0( bus ):
  # ADC 0
  bus.write_byte(I2C_ADDR_4, 0x40)
  bus.read_byte(I2C_ADDR_4)           # dummy read to start conversion
  return bus.read_byte(I2C_ADDR_4)

def read_ADC_1():            
  # ADC 1
  bus.write_byte(I2C_ADDR_4, 0x41)
  bus.read_byte(I2C_ADDR_4)           # dummy read to start conversion
  return bus.read_byte(I2C_ADDR_4)

def read_ADC_2():
  # ADC 2
  bus.write_byte(I2C_ADDR_4, 0x42)
  bus.read_byte(I2C_ADDR_4)           # dummy read to start conversion
  return bus.read_byte(I2C_ADDR_4)

def read_ADC_3():
  # ADC 3
  bus.write_byte(I2C_ADDR_4, 0x43)
  bus.read_byte(I2C_ADDR_4)           # dummy read to start conversion
  return bus.read_byte(I2C_ADDR_4)

def lcd_init():
  # Initialise display
  lcd_byte(0x33,LCD_CMD) # 110011 Initialise
  lcd_byte(0x32,LCD_CMD) # 110010 Initialise
  lcd_byte(0x06,LCD_CMD) # 000110 Cursor move direction
  lcd_byte(0x0C,LCD_CMD) # 001100 Display On,Cursor Off, Blink Off
  lcd_byte(0x28,LCD_CMD) # 101000 Data length, number of lines, font size
  lcd_byte(0x01,LCD_CMD) # 000001 Clear display
  time.sleep(E_DELAY)

def lcd_byte(bits, mode):
  # Send byte to data pins
  # bits = data
  # mode = True  for character
  #        False for command

  GPIO.output(LCD_RS, mode) # RS

  # High bits
  GPIO.output(LCD_D4, False)
  GPIO.output(LCD_D5, False)
  GPIO.output(LCD_D6, False)
  GPIO.output(LCD_D7, False)
  if bits&0x10==0x10:
    GPIO.output(LCD_D4, True)
  if bits&0x20==0x20:
    GPIO.output(LCD_D5, True)
  if bits&0x40==0x40:
    GPIO.output(LCD_D6, True)
  if bits&0x80==0x80:
    GPIO.output(LCD_D7, True)

  # Toggle 'Enable' pin
  lcd_toggle_enable()

  # Low bits
  GPIO.output(LCD_D4, False)
  GPIO.output(LCD_D5, False)
  GPIO.output(LCD_D6, False)
  GPIO.output(LCD_D7, False)
  if bits&0x01==0x01:
    GPIO.output(LCD_D4, True)
  if bits&0x02==0x02:
    GPIO.output(LCD_D5, True)
  if bits&0x04==0x04:
    GPIO.output(LCD_D6, True)
  if bits&0x08==0x08:
    GPIO.output(LCD_D7, True)

  # Toggle 'Enable' pin
  lcd_toggle_enable()

def lcd_toggle_enable():
  # Toggle enable
  time.sleep(E_DELAY)
  GPIO.output(LCD_E, True)
  time.sleep(E_PULSE)
  GPIO.output(LCD_E, False)
  time.sleep(E_DELAY)

def lcd_string(message,line):
  # Send string to display

  message = message.ljust(LCD_WIDTH," ")

  lcd_byte(line, LCD_CMD)

  for i in range(LCD_WIDTH):
    lcd_byte(ord(message[i]),LCD_CHR)
    
def pin_event( pin ):
  global ball_count
  ball_count =  ball_count + 1
  lcd_string("Ball Count",LCD_LINE_1)
  lcd_string(str( ball_count ),LCD_LINE_2)
  return;

def main():
  # Main program block
  global ball_count
  led_update_count = 0

  GPIO.setwarnings(False)
  GPIO.setmode(GPIO.BCM)       # Use BCM GPIO numbers
  GPIO.setup(LCD_E, GPIO.OUT)  # E
  GPIO.setup(LCD_RS, GPIO.OUT) # RS
  GPIO.setup(LCD_D4, GPIO.OUT) # DB4
  GPIO.setup(LCD_D5, GPIO.OUT) # DB5
  GPIO.setup(LCD_D6, GPIO.OUT) # DB6
  GPIO.setup(LCD_D7, GPIO.OUT) # DB7

  bus = smbus.SMBus(1)

  GPIO.setup(PWM_PIN, GPIO.OUT)

  pwm = GPIO.PWM(PWM_PIN, PWM_FREQ)
  pwm.start(0)
  pwm.ChangeDutyCycle( 70 )
  
  GPIO.setup( BALL_SENSOR, GPIO.IN, pull_up_down = GPIO.PUD_UP )
  GPIO.add_event_detect( BALL_SENSOR, GPIO.RISING, callback=pin_event, bouncetime=500 )

  # Initialise display
  lcd_init()

  # Send some test
  lcd_string("Rasbperry Pi",LCD_LINE_1)
  lcd_string("Hello World Test",LCD_LINE_2)

  time.sleep(3) # 3 second delay

  while True:
    sensor = read_ADC_0( bus )
    #print sensor
                
    error = SPEED - sensor

    control = error * P

    if control > 99:
      control = 100

    if control < 0:
      control = 0
                
    pwm.ChangeDutyCycle( control )

    if led_update_count == 20:
      led_update_count = 0
      normalised_sensor = ((sensor - SENSOR_MIN)/(SENSOR_MAX - SENSOR_MIN)) * 100

      #print normalised_sensor
      #print sensor
      
      if normalised_sensor > 99:
        normalised_sensor = 100
      if normalised_sensor < 0:
        normalised_sensor = 0
      update_led1( bus, normalised_sensor )
      update_led2( bus, control )
      update_led3( bus, ball_count )
      
    else:
      led_update_count = led_update_count + 1
      
    time.sleep(0.001)



if __name__ == '__main__':

  try:
    main()
  except KeyboardInterrupt:
    pass
  finally:
    lcd_byte(0x01, LCD_CMD)
    lcd_string("Goodbye!",LCD_LINE_1)
    #pwm.stop()
    GPIO.cleanup()



