diff --git a/Code/Python_Code/03.1.1_LightWater/LightWater2.py b/Code/Python_Code/03.1.1_LightWater/LightWater2.py index fcd9640..7df1d38 100644 --- a/Code/Python_Code/03.1.1_LightWater/LightWater2.py +++ b/Code/Python_Code/03.1.1_LightWater/LightWater2.py @@ -7,6 +7,7 @@ ######################################################################## from gpiozero import LEDBoard from time import sleep +from signal import pause print ('Program is starting ... ') diff --git a/Code/Python_GPIOZero_Code/00.0.0_Hello/Hello.py b/Code/Python_GPIOZero_Code/00.0.0_Hello/Hello.py new file mode 100644 index 0000000..dec7f2e --- /dev/null +++ b/Code/Python_GPIOZero_Code/00.0.0_Hello/Hello.py @@ -0,0 +1,12 @@ +#!/usr/bin/env python3 +######################################################################## +# Filename : Hello.py +# Description : Print "Hello World!". +# auther : www.freenove.com +# modification: 2023/05/11 +######################################################################## + +def Hello(): + print('Hello World!') + +Hello() diff --git a/Code/Python_GPIOZero_Code/01.1.1_Blink/Blink.py b/Code/Python_GPIOZero_Code/01.1.1_Blink/Blink.py new file mode 100644 index 0000000..1bbb6b6 --- /dev/null +++ b/Code/Python_GPIOZero_Code/01.1.1_Blink/Blink.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 +######################################################################## +# Filename : Blink.py +# Description : Basic usage of GPIO. Let led blink. +# auther : www.freenove.com +# modification: 2023/05/11 +######################################################################## +from gpiozero import LED +from time import sleep + +led = LED(17) # define LED pin according to BCM Numbering +#led = LED("J8:11") # BOARD Numbering +''' +# pins numbering, the following lines are all equivalent +led = LED(17) # BCM +led = LED("GPIO17") # BCM +led = LED("BCM17") # BCM +led = LED("BOARD11") # BOARD +led = LED("WPI0") # WiringPi +led = LED("J8:11") # BOARD +''' +def loop(): + while True: + led.on() # turn on LED + print ('led turned on >>>') # print message on terminal + sleep(1) # wait 1 second + led.off() # turn off LED + print ('led turned off <<<') # print message on terminal + sleep(1) # wait 1 second + +if __name__ == '__main__': # Program entrance + print ('Program is starting ... \n') + try: + loop() + except KeyboardInterrupt: # Press ctrl-c to end the program. + print("Ending program") diff --git a/Code/Python_GPIOZero_Code/02.1.1_ButtonLED/ButtonLED.py b/Code/Python_GPIOZero_Code/02.1.1_ButtonLED/ButtonLED.py new file mode 100644 index 0000000..b180747 --- /dev/null +++ b/Code/Python_GPIOZero_Code/02.1.1_ButtonLED/ButtonLED.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 +######################################################################## +# Filename : ButtonLED.py +# Description : Control led with button. +# Author : www.freenove.com +# modification: 2023/05/11 +######################################################################## +from gpiozero import LED, Button + +led = LED(17) # define LED pin according to BCM Numbering +button = Button(18) # define Button pin according to BCM Numbering + +def loop(): + while True: + if button.is_pressed: # if button is pressed + led.on() # turn on led + print("Button is pressed, led turned on >>>") # print information on terminal + else : # if button is relessed + led.off() # turn off led + print("Button is released, led turned off <<<") + +if __name__ == '__main__': # Program entrance + print ('Program is starting...') + try: + loop() + except KeyboardInterrupt: # Press ctrl-c to end the program. + print("Ending program") diff --git a/Code/Python_GPIOZero_Code/02.2.1_Tablelamp/Tablelamp.py b/Code/Python_GPIOZero_Code/02.2.1_Tablelamp/Tablelamp.py new file mode 100644 index 0000000..4377fb1 --- /dev/null +++ b/Code/Python_GPIOZero_Code/02.2.1_Tablelamp/Tablelamp.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 +######################################################################## +# Filename : Tablelamp.py +# Description : DIY MINI table lamp +# Author : www.freenove.com +# modification: 2023/05/11 +######################################################################## +from gpiozero import LED, Button +import time + +led = LED(17) # define LED pin according to BCM Numbering +button = Button(18) # define Button pin according to BCM Numbering + +def onButtonPressed(): # When button is pressed, this function will be executed + led.toggle() + if led.is_lit : + print("Led turned on >>>") + else : + print("Led turned off <<<") +def loop(): + #Button detect + button.when_pressed = onButtonPressed + while True: + time.sleep(1) +def destroy(): + led.close() + button.close() +if __name__ == '__main__': # Program entrance + print ('Program is starting...') + try: + loop() + except KeyboardInterrupt: # Press ctrl-c to end the program. + destroy() + print("Ending program") diff --git a/Code/Python_GPIOZero_Code/03.1.1_LightWater/LightWater.py b/Code/Python_GPIOZero_Code/03.1.1_LightWater/LightWater.py new file mode 100644 index 0000000..0481cec --- /dev/null +++ b/Code/Python_GPIOZero_Code/03.1.1_LightWater/LightWater.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python3 +######################################################################## +# Filename : LightWater.py +# Description : Use LEDBar Graph(10 LED) +# Author : www.freenove.com +# modification: 2023/05/11 +######################################################################## +from gpiozero import LEDBoard +from time import sleep + +#ledPins = ["J8:11", "J8:12","J8:13","J8:15","J8:16","J8:18","J8:22","J8:3","J8:5","J8:24"] +ledPins = [17, 18, 27, 22, 23, 24, 25, 2, 3, 8] + +leds = LEDBoard(*ledPins, active_high=False) + +def loop(): + while True: + for index in range(0,len(ledPins),1): # make led(on) move from left to right + leds.on(index) + sleep(0.1) + leds.off(index) + for index in range(len(ledPins)-1,-1,-1): #move led(on) from right to left + leds.on(index) + sleep(0.1) + leds.off(index) + +if __name__ == '__main__': # Program entrance + print ('Program is starting...') + try: + loop() + except KeyboardInterrupt: # Press ctrl-c to end the program. + print("Ending program") + diff --git a/Code/Python_GPIOZero_Code/04.1.1_BreathingLED/BreathingLED.py b/Code/Python_GPIOZero_Code/04.1.1_BreathingLED/BreathingLED.py new file mode 100644 index 0000000..8f54f20 --- /dev/null +++ b/Code/Python_GPIOZero_Code/04.1.1_BreathingLED/BreathingLED.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python3 +######################################################################## +# Filename : BreathingLED.py +# Description : Breathing LED +# Author : www.freenove.com +# modification: 2023/05/11 +######################################################################## +from gpiozero import PWMLED +import time + +led = PWMLED(18 ,initial_value=0 ,frequency=1000) +def loop(): + while True: + for b in range(0, 101, 1): # make the led brighter + led.value = b / 100.0 # set dc value as the duty cycle + time.sleep(0.01) + time.sleep(1) + for b in range(100, -1, -1): # make the led darker + led.value = b / 100.0 # set dc value as the duty cycle + time.sleep(0.01) + time.sleep(1) +def destroy(): + led.close() +if __name__ == '__main__': # Program entrance + print ('Program is starting ... ') + try: + loop() + except KeyboardInterrupt: # Press ctrl-c to end the program. + destroy() + print("Ending program") diff --git a/Code/Python_GPIOZero_Code/05.1.1_ColorfulLED/ColorfulLED.py b/Code/Python_GPIOZero_Code/05.1.1_ColorfulLED/ColorfulLED.py new file mode 100644 index 0000000..53d568b --- /dev/null +++ b/Code/Python_GPIOZero_Code/05.1.1_ColorfulLED/ColorfulLED.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 +######################################################################## +# Filename : ColorfulLED.py +# Description : Random color change ColorfulLED +# Author : www.freenove.com +# modification: 2023/05/11 +######################################################################## +from gpiozero import RGBLED +import time +import random + +#led = RGBLED(red="J8:11", green="J8:12", blue="J8:13", active_high=False) # define the pins for R:11,G:12,B:13 +led = RGBLED(red=17, green=18, blue=27, active_high=False) # define the pins for R:GPIO17,G:GPIO18,B:GPIO27 +# If your RGBLED is a common cathode LED, set active_high to True + +def setColor(r_val,g_val,b_val): # change duty cycle for three pins to r_val,g_val,b_val + led.red=r_val/100 # change pwmRed duty cycle to r_val + led.green = g_val/100 # change pwmRed duty cycle to r_val + led.blue = b_val/100 # change pwmRed duty cycle to r_val + +def loop(): + while True : + r=random.randint(0,100) #get a random in (0,100) + g=random.randint(0,100) + b=random.randint(0,100) + setColor(r,g,b) #set random as a duty cycle value + print ('r=%d, g=%d, b=%d ' %(r ,g, b)) + time.sleep(1) + +def destroy(): + led.close() + +if __name__ == '__main__': # Program entrance + print ('Program is starting ... ') + try: + loop() + except KeyboardInterrupt: # Press ctrl-c to end the program. + destroy() + print("Ending program") \ No newline at end of file diff --git a/Code/Python_GPIOZero_Code/06.1.1_Doorbell/Doorbell.py b/Code/Python_GPIOZero_Code/06.1.1_Doorbell/Doorbell.py new file mode 100644 index 0000000..c28f608 --- /dev/null +++ b/Code/Python_GPIOZero_Code/06.1.1_Doorbell/Doorbell.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 +######################################################################## +# Filename : Doorbell.py +# Description : Make doorbell with buzzer and button +# Author : www.freenove.com +# modification: 2023/05/11 +######################################################################## +from gpiozero import Buzzer, Button +import time + +buzzer = Buzzer(17) +button = Button(18) + +def onButtonPressed(): + buzzer.on() + print("Button is pressed, buzzer turned on >>>") + +def onButtonReleased(): + buzzer.off() + print("Button is released, buzzer turned on <<<") + +def loop(): + button.when_pressed = onButtonPressed + button.when_released = onButtonReleased + while True : + time.sleep(1) + +def destroy(): + buzzer.close() + button.close() + +if __name__ == '__main__': # Program entrance + print ('Program is starting ... ') + try: + loop() + except KeyboardInterrupt: # Press ctrl-c to end the program. + destroy() + print("Ending program") \ No newline at end of file diff --git a/Code/Python_GPIOZero_Code/06.2.1_Alertor/Alertor.py b/Code/Python_GPIOZero_Code/06.2.1_Alertor/Alertor.py new file mode 100644 index 0000000..0360e13 --- /dev/null +++ b/Code/Python_GPIOZero_Code/06.2.1_Alertor/Alertor.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 +######################################################################## +# Filename : Alertor.py +# Description : Make Alertor with buzzer and button +# Author : www.freenove.com +# modification: 2019/12/27 +######################################################################## +from gpiozero import TonalBuzzer,Button +from gpiozero.tones import Tone +import time +import math + +buzzer = TonalBuzzer(17) +button = Button(18) # define Button pin according to BCM Numbering + +def loop(): + while True: + if button.is_pressed: # if button is pressed + alertor() + print ('alertor turned on >>> ') + else : + stopAlertor() + print ('alertor turned off <<<') +def alertor(): + for x in range(0,361): # Make frequency of the alertor consistent with the sine wave + sinVal = math.sin(x * (math.pi / 180.0)) # calculate the sine value + toneVal = 2000 + sinVal * 500 # Add to the resonant frequency with a Weighted + b.play(Tone(toneVal)) # Change Frequency of PWM to toneVal + time.sleep(0.001) + +def stopAlertor(): + buzzer.stop() + +def destroy(): + buzzer.close() + +if __name__ == '__main__': # Program entrance + print ('Program is starting...') + try: + loop() + except KeyboardInterrupt: # Press ctrl-c to end the program. + destroy() + print("Ending program") \ No newline at end of file diff --git a/Code/Python_GPIOZero_Code/07.1.1_ADC/ADC.py b/Code/Python_GPIOZero_Code/07.1.1_ADC/ADC.py new file mode 100644 index 0000000..52c20a2 --- /dev/null +++ b/Code/Python_GPIOZero_Code/07.1.1_ADC/ADC.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 +######################################################################## +# Filename : ADC.py +# Description : Use ADC module to read the voltage value of potentiometer. +# Author : www.freenove.com +# modification: 2023/05/11 +######################################################################## +import time +from ADCDevice import * + +adc = ADCDevice() # Define an ADCDevice class object + +def setup(): + global adc + if(adc.detectI2C(0x48)): # Detect the pcf8591. + adc = PCF8591() + elif(adc.detectI2C(0x4b)): # Detect the ads7830 + adc = ADS7830() + else: + print("No correct I2C address found, \n" + "Please use command 'i2cdetect -y 1' to check the I2C address! \n" + "Program Exit. \n"); + exit(-1) + +def loop(): + while True: + value = adc.analogRead(0) # read the ADC value of channel 0 + voltage = value / 255.0 * 3.3 # calculate the voltage value + print ('ADC Value : %d, Voltage : %.2f'%(value,voltage)) + time.sleep(0.1) + +def destroy(): + adc.close() + +if __name__ == '__main__': # Program entrance + print ('Program is starting ... ') + try: + setup() + loop() + except KeyboardInterrupt: # Press ctrl-c to end the program. + destroy() + print("Ending program") + \ No newline at end of file diff --git a/Code/Python_GPIOZero_Code/07.1.1_ADC/ADCDevice.py b/Code/Python_GPIOZero_Code/07.1.1_ADC/ADCDevice.py new file mode 100644 index 0000000..b8ea063 --- /dev/null +++ b/Code/Python_GPIOZero_Code/07.1.1_ADC/ADCDevice.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 +######################################################################## +# Filename : ADCDevice.py +# Description : Freenove ADC Module library. +# Author : www.freenove.com +# modification: 2023/05/11 +######################################################################## + +import smbus + +class ADCDevice(object): + def __init__(self): + self.cmd = 0 + self.address = 0 + self.bus=smbus.SMBus(1) + # print("ADCDevice init") + + def detectI2C(self,addr): + try: + self.bus.write_byte(addr,0) + print("Found device in address 0x%x"%(addr)) + return True + except: + print("Not found device in address 0x%x"%(addr)) + return False + + def close(self): + self.bus.close() + +class PCF8591(ADCDevice): + def __init__(self): + super(PCF8591, self).__init__() + self.cmd = 0x40 # The default command for PCF8591 is 0x40. + self.address = 0x48 # 0x48 is the default i2c address for PCF8591 Module. + + def analogRead(self, chn): # PCF8591 has 4 ADC input pins, chn:0,1,2,3 + value = self.bus.read_byte_data(self.address, self.cmd+chn) + value = self.bus.read_byte_data(self.address, self.cmd+chn) + return value + + def analogWrite(self,value): # write DAC value + self.bus.write_byte_data(address,cmd,value) + +class ADS7830(ADCDevice): + def __init__(self): + super(ADS7830, self).__init__() + self.cmd = 0x84 + self.address = 0x4b # 0x4b is the default i2c address for ADS7830 Module. + + def analogRead(self, chn): # ADS7830 has 8 ADC input pins, chn:0,1,2,3,4,5,6,7 + value = self.bus.read_byte_data(self.address, self.cmd|(((chn<<2 | chn>>1)&0x07)<<4)) + return value diff --git a/Code/Python_GPIOZero_Code/08.1.1_Softlight/ADCDevice.py b/Code/Python_GPIOZero_Code/08.1.1_Softlight/ADCDevice.py new file mode 100644 index 0000000..b8ea063 --- /dev/null +++ b/Code/Python_GPIOZero_Code/08.1.1_Softlight/ADCDevice.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 +######################################################################## +# Filename : ADCDevice.py +# Description : Freenove ADC Module library. +# Author : www.freenove.com +# modification: 2023/05/11 +######################################################################## + +import smbus + +class ADCDevice(object): + def __init__(self): + self.cmd = 0 + self.address = 0 + self.bus=smbus.SMBus(1) + # print("ADCDevice init") + + def detectI2C(self,addr): + try: + self.bus.write_byte(addr,0) + print("Found device in address 0x%x"%(addr)) + return True + except: + print("Not found device in address 0x%x"%(addr)) + return False + + def close(self): + self.bus.close() + +class PCF8591(ADCDevice): + def __init__(self): + super(PCF8591, self).__init__() + self.cmd = 0x40 # The default command for PCF8591 is 0x40. + self.address = 0x48 # 0x48 is the default i2c address for PCF8591 Module. + + def analogRead(self, chn): # PCF8591 has 4 ADC input pins, chn:0,1,2,3 + value = self.bus.read_byte_data(self.address, self.cmd+chn) + value = self.bus.read_byte_data(self.address, self.cmd+chn) + return value + + def analogWrite(self,value): # write DAC value + self.bus.write_byte_data(address,cmd,value) + +class ADS7830(ADCDevice): + def __init__(self): + super(ADS7830, self).__init__() + self.cmd = 0x84 + self.address = 0x4b # 0x4b is the default i2c address for ADS7830 Module. + + def analogRead(self, chn): # ADS7830 has 8 ADC input pins, chn:0,1,2,3,4,5,6,7 + value = self.bus.read_byte_data(self.address, self.cmd|(((chn<<2 | chn>>1)&0x07)<<4)) + return value diff --git a/Code/Python_GPIOZero_Code/08.1.1_Softlight/Softlight.py b/Code/Python_GPIOZero_Code/08.1.1_Softlight/Softlight.py new file mode 100644 index 0000000..d7763a7 --- /dev/null +++ b/Code/Python_GPIOZero_Code/08.1.1_Softlight/Softlight.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python3 +######################################################################## +# Filename : ADC.py +# Description : Use ADC module to read the voltage value of potentiometer. +# Author : www.freenove.com +# modification: 2023/05/11 +######################################################################## +from gpiozero import PWMLED +import time +from ADCDevice import * + +led = PWMLED(17,frequency=1000) # define LED pin according to BCM Numbering +adc = ADCDevice() # Define an ADCDevice class object + +def setup(): + global adc + if(adc.detectI2C(0x48)): # Detect the pcf8591. + adc = PCF8591() + elif(adc.detectI2C(0x4b)): # Detect the ads7830 + adc = ADS7830() + else: + print("No correct I2C address found, \n" + "Please use command 'i2cdetect -y 1' to check the I2C address! \n" + "Program Exit. \n"); + exit(-1) + +def loop(): + while True: + value = adc.analogRead(0) # read the ADC value of channel 0 + led.value = value / 255.0 # Mapping to PWM duty cycle + voltage = value / 255.0 * 3.3 # calculate the voltage value + print ('ADC Value : %d, Voltage : %.2f'%(value,voltage)) + time.sleep(0.03) + +def destroy(): + led.close() + adc.close() + +if __name__ == '__main__': # Program entrance + print ('Program is starting ... ') + try: + setup() + loop() + except KeyboardInterrupt: # Press ctrl-c to end the program. + destroy() + print("Ending program") \ No newline at end of file diff --git a/Code/Python_GPIOZero_Code/09.1.1_ColorfulSoftlight/ADCDevice.py b/Code/Python_GPIOZero_Code/09.1.1_ColorfulSoftlight/ADCDevice.py new file mode 100644 index 0000000..b8ea063 --- /dev/null +++ b/Code/Python_GPIOZero_Code/09.1.1_ColorfulSoftlight/ADCDevice.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 +######################################################################## +# Filename : ADCDevice.py +# Description : Freenove ADC Module library. +# Author : www.freenove.com +# modification: 2023/05/11 +######################################################################## + +import smbus + +class ADCDevice(object): + def __init__(self): + self.cmd = 0 + self.address = 0 + self.bus=smbus.SMBus(1) + # print("ADCDevice init") + + def detectI2C(self,addr): + try: + self.bus.write_byte(addr,0) + print("Found device in address 0x%x"%(addr)) + return True + except: + print("Not found device in address 0x%x"%(addr)) + return False + + def close(self): + self.bus.close() + +class PCF8591(ADCDevice): + def __init__(self): + super(PCF8591, self).__init__() + self.cmd = 0x40 # The default command for PCF8591 is 0x40. + self.address = 0x48 # 0x48 is the default i2c address for PCF8591 Module. + + def analogRead(self, chn): # PCF8591 has 4 ADC input pins, chn:0,1,2,3 + value = self.bus.read_byte_data(self.address, self.cmd+chn) + value = self.bus.read_byte_data(self.address, self.cmd+chn) + return value + + def analogWrite(self,value): # write DAC value + self.bus.write_byte_data(address,cmd,value) + +class ADS7830(ADCDevice): + def __init__(self): + super(ADS7830, self).__init__() + self.cmd = 0x84 + self.address = 0x4b # 0x4b is the default i2c address for ADS7830 Module. + + def analogRead(self, chn): # ADS7830 has 8 ADC input pins, chn:0,1,2,3,4,5,6,7 + value = self.bus.read_byte_data(self.address, self.cmd|(((chn<<2 | chn>>1)&0x07)<<4)) + return value diff --git a/Code/Python_GPIOZero_Code/09.1.1_ColorfulSoftlight/ColorfulSoftlight.py b/Code/Python_GPIOZero_Code/09.1.1_ColorfulSoftlight/ColorfulSoftlight.py new file mode 100644 index 0000000..bc28442 --- /dev/null +++ b/Code/Python_GPIOZero_Code/09.1.1_ColorfulSoftlight/ColorfulSoftlight.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 +############################################################################# +# Filename : Softlight.py +# Description : Control RGBLED with Potentiometer +# Author : www.freenove.com +# modification: 2023/05/11 +######################################################################## +from gpiozero import RGBLED +import time +from ADCDevice import * + +led = RGBLED(red=22, green=27, blue=17, active_high=False) # define the pins for R:GPIO22,G:GPIO27,B:GPIO17 +#led = RGBLED(red="J8:15", green="J8:13", blue="J8:11") # according to BOARD Numbering define the pins for R:11,G:12,B:13 +adc = ADCDevice() # Define an ADCDevice class object + +def setup(): + global adc + if(adc.detectI2C(0x48)): # Detect the pcf8591. + adc = PCF8591() + elif(adc.detectI2C(0x4b)): # Detect the ads7830 + adc = ADS7830() + else: + print("No correct I2C address found, \n" + "Please use command 'i2cdetect -y 1' to check the I2C address! \n" + "Program Exit. \n"); + exit(-1) + +def loop(): + while True: + value_Red = adc.analogRead(0) # read ADC value of 3 potentiometers + value_Green = adc.analogRead(1) + value_Blue = adc.analogRead(2) + led.red =value_Red/255 # map the read value of potentiometers into PWM value and output it + led.green =value_Green/255 + led.blue =value_Blue/255 + # print read ADC value + print ('ADC Value value_Red: %d ,\tvlue_Green: %d ,\tvalue_Blue: %d'%(value_Red,value_Green,value_Blue)) + time.sleep(0.01) + +def destroy(): + adc.close() + led.close() + +if __name__ == '__main__': # Program entrance + print ('Program is starting ... ') + setup() + try: + loop() + except KeyboardInterrupt: # Press ctrl-c to end the program. + destroy() + print("Ending program") diff --git a/Code/Python_GPIOZero_Code/10.1.1_Nightlamp/ADCDevice.py b/Code/Python_GPIOZero_Code/10.1.1_Nightlamp/ADCDevice.py new file mode 100644 index 0000000..6319305 --- /dev/null +++ b/Code/Python_GPIOZero_Code/10.1.1_Nightlamp/ADCDevice.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 +######################################################################## +# Filename : ADCDevice.py +# Description : Freenove ADC Module library. +# Author : www.freenove.com +# modification: 2020/04/21 +######################################################################## + +import smbus + +class ADCDevice(object): + def __init__(self): + self.cmd = 0 + self.address = 0 + self.bus=smbus.SMBus(1) + # print("ADCDevice init") + + def detectI2C(self,addr): + try: + self.bus.write_byte(addr,0) + print("Found device in address 0x%x"%(addr)) + return True + except: + print("Not found device in address 0x%x"%(addr)) + return False + + def close(self): + self.bus.close() + +class PCF8591(ADCDevice): + def __init__(self): + super(PCF8591, self).__init__() + self.cmd = 0x40 # The default command for PCF8591 is 0x40. + self.address = 0x48 # 0x48 is the default i2c address for PCF8591 Module. + + def analogRead(self, chn): # PCF8591 has 4 ADC input pins, chn:0,1,2,3 + value = self.bus.read_byte_data(self.address, self.cmd+chn) + value = self.bus.read_byte_data(self.address, self.cmd+chn) + return value + + def analogWrite(self,value): # write DAC value + self.bus.write_byte_data(address,cmd,value) + +class ADS7830(ADCDevice): + def __init__(self): + super(ADS7830, self).__init__() + self.cmd = 0x84 + self.address = 0x4b # 0x4b is the default i2c address for ADS7830 Module. + + def analogRead(self, chn): # ADS7830 has 8 ADC input pins, chn:0,1,2,3,4,5,6,7 + value = self.bus.read_byte_data(self.address, self.cmd|(((chn<<2 | chn>>1)&0x07)<<4)) + return value diff --git a/Code/Python_GPIOZero_Code/10.1.1_Nightlamp/Nightlamp.py b/Code/Python_GPIOZero_Code/10.1.1_Nightlamp/Nightlamp.py new file mode 100644 index 0000000..4b9f9c5 --- /dev/null +++ b/Code/Python_GPIOZero_Code/10.1.1_Nightlamp/Nightlamp.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 +############################################################################# +# Filename : Nightlamp.py +# Description : Control LED with Photoresistor +# Author : www.freenove.com +# modification: 2023/05/11 +######################################################################## +from gpiozero import PWMLED +import time +from ADCDevice import * + +ledPin = 17 # define ledPin +led = PWMLED(ledPin) +adc = ADCDevice() # Define an ADCDevice class object + +def setup(): + global adc + if(adc.detectI2C(0x48)): # Detect the pcf8591. + adc = PCF8591() + elif(adc.detectI2C(0x4b)): # Detect the ads7830 + adc = ADS7830() + else: + print("No correct I2C address found, \n" + "Please use command 'i2cdetect -y 1' to check the I2C address! \n" + "Program Exit. \n"); + exit(-1) + +def loop(): + while True: + value = adc.analogRead(0) # read the ADC value of channel 0 + led.value = value / 255.0 # Mapping to PWM duty cycle + voltage = value / 255.0 * 3.3 + print ('ADC Value : %d, Voltage : %.2f'%(value,voltage)) + time.sleep(0.01) + +def destroy(): + led.close() + adc.close() + +if __name__ == '__main__': # Program entrance + print ('Program is starting ... ') + setup() + try: + loop() + except KeyboardInterrupt: # Press ctrl-c to end the program. + destroy() + print("Ending program") + \ No newline at end of file diff --git a/Code/Python_GPIOZero_Code/11.1.1_Thermometer/ADCDevice.py b/Code/Python_GPIOZero_Code/11.1.1_Thermometer/ADCDevice.py new file mode 100644 index 0000000..b8ea063 --- /dev/null +++ b/Code/Python_GPIOZero_Code/11.1.1_Thermometer/ADCDevice.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 +######################################################################## +# Filename : ADCDevice.py +# Description : Freenove ADC Module library. +# Author : www.freenove.com +# modification: 2023/05/11 +######################################################################## + +import smbus + +class ADCDevice(object): + def __init__(self): + self.cmd = 0 + self.address = 0 + self.bus=smbus.SMBus(1) + # print("ADCDevice init") + + def detectI2C(self,addr): + try: + self.bus.write_byte(addr,0) + print("Found device in address 0x%x"%(addr)) + return True + except: + print("Not found device in address 0x%x"%(addr)) + return False + + def close(self): + self.bus.close() + +class PCF8591(ADCDevice): + def __init__(self): + super(PCF8591, self).__init__() + self.cmd = 0x40 # The default command for PCF8591 is 0x40. + self.address = 0x48 # 0x48 is the default i2c address for PCF8591 Module. + + def analogRead(self, chn): # PCF8591 has 4 ADC input pins, chn:0,1,2,3 + value = self.bus.read_byte_data(self.address, self.cmd+chn) + value = self.bus.read_byte_data(self.address, self.cmd+chn) + return value + + def analogWrite(self,value): # write DAC value + self.bus.write_byte_data(address,cmd,value) + +class ADS7830(ADCDevice): + def __init__(self): + super(ADS7830, self).__init__() + self.cmd = 0x84 + self.address = 0x4b # 0x4b is the default i2c address for ADS7830 Module. + + def analogRead(self, chn): # ADS7830 has 8 ADC input pins, chn:0,1,2,3,4,5,6,7 + value = self.bus.read_byte_data(self.address, self.cmd|(((chn<<2 | chn>>1)&0x07)<<4)) + return value diff --git a/Code/Python_GPIOZero_Code/11.1.1_Thermometer/Thermometer.py b/Code/Python_GPIOZero_Code/11.1.1_Thermometer/Thermometer.py new file mode 100644 index 0000000..11c1d78 --- /dev/null +++ b/Code/Python_GPIOZero_Code/11.1.1_Thermometer/Thermometer.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python3 +############################################################################# +# Filename : Thermometer.py +# Description : DIY Thermometer +# Author : www.freenove.com +# modification: 2023/05/11 +######################################################################## +import time +import math +from ADCDevice import * + +adc = ADCDevice() # Define an ADCDevice class object + +def setup(): + global adc + if(adc.detectI2C(0x48)): # Detect the pcf8591. + adc = PCF8591() + elif(adc.detectI2C(0x4b)): # Detect the ads7830 + adc = ADS7830() + else: + print("No correct I2C address found, \n" + "Please use command 'i2cdetect -y 1' to check the I2C address! \n" + "Program Exit. \n"); + exit(-1) + +def loop(): + while True: + value = adc.analogRead(0) # read ADC value A0 pin + voltage = value / 255.0 * 3.3 # calculate voltage + Rt = 10 * voltage / (3.3 - voltage) # calculate resistance value of thermistor + tempK = 1/(1/(273.15 + 25) + math.log(Rt/10)/3950.0) # calculate temperature (Kelvin) + tempC = tempK -273.15 # calculate temperature (Celsius) + print ('ADC Value : %d, Voltage : %.2f, Temperature : %.2f'%(value,voltage,tempC)) + time.sleep(0.01) + +def destroy(): + adc.close() + +if __name__ == '__main__': # Program entrance + print ('Program is starting ... ') + setup() + try: + loop() + except KeyboardInterrupt: # Press ctrl-c to end the program. + destroy() + print("Ending program") \ No newline at end of file diff --git a/Code/Python_GPIOZero_Code/12.1.1_Joystick/ADCDevice.py b/Code/Python_GPIOZero_Code/12.1.1_Joystick/ADCDevice.py new file mode 100644 index 0000000..b8ea063 --- /dev/null +++ b/Code/Python_GPIOZero_Code/12.1.1_Joystick/ADCDevice.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 +######################################################################## +# Filename : ADCDevice.py +# Description : Freenove ADC Module library. +# Author : www.freenove.com +# modification: 2023/05/11 +######################################################################## + +import smbus + +class ADCDevice(object): + def __init__(self): + self.cmd = 0 + self.address = 0 + self.bus=smbus.SMBus(1) + # print("ADCDevice init") + + def detectI2C(self,addr): + try: + self.bus.write_byte(addr,0) + print("Found device in address 0x%x"%(addr)) + return True + except: + print("Not found device in address 0x%x"%(addr)) + return False + + def close(self): + self.bus.close() + +class PCF8591(ADCDevice): + def __init__(self): + super(PCF8591, self).__init__() + self.cmd = 0x40 # The default command for PCF8591 is 0x40. + self.address = 0x48 # 0x48 is the default i2c address for PCF8591 Module. + + def analogRead(self, chn): # PCF8591 has 4 ADC input pins, chn:0,1,2,3 + value = self.bus.read_byte_data(self.address, self.cmd+chn) + value = self.bus.read_byte_data(self.address, self.cmd+chn) + return value + + def analogWrite(self,value): # write DAC value + self.bus.write_byte_data(address,cmd,value) + +class ADS7830(ADCDevice): + def __init__(self): + super(ADS7830, self).__init__() + self.cmd = 0x84 + self.address = 0x4b # 0x4b is the default i2c address for ADS7830 Module. + + def analogRead(self, chn): # ADS7830 has 8 ADC input pins, chn:0,1,2,3,4,5,6,7 + value = self.bus.read_byte_data(self.address, self.cmd|(((chn<<2 | chn>>1)&0x07)<<4)) + return value diff --git a/Code/Python_GPIOZero_Code/12.1.1_Joystick/Joystick.py b/Code/Python_GPIOZero_Code/12.1.1_Joystick/Joystick.py new file mode 100644 index 0000000..a0499c9 --- /dev/null +++ b/Code/Python_GPIOZero_Code/12.1.1_Joystick/Joystick.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 +############################################################################# +# Filename : Joystick.py +# Description : Read Joystick state +# Author : www.freenove.com +# modification: 2023/05/11 +######################################################################## +from gpiozero import Button +import time +from ADCDevice import * + +Z_Pin = 18 # define Z_Pin +button = Button(Z_Pin) # define Button pin according to BCM Numbering +adc = ADCDevice() # Define an ADCDevice class object + +def setup(): + global adc + if(adc.detectI2C(0x48)): # Detect the pcf8591. + adc = PCF8591() + elif(adc.detectI2C(0x4b)): # Detect the ads7830 + adc = ADS7830() + else: + print("No correct I2C address found, \n" + "Please use command 'i2cdetect -y 1' to check the I2C address! \n" + "Program Exit. \n"); + exit(-1) + +def loop(): + while True: + val_Z = not button.value # read digital value of axis Z + val_Y = adc.analogRead(0) # read analog value of axis X and Y + val_X = adc.analogRead(1) + print ('value_X: %d ,\tvlue_Y: %d ,\tvalue_Z: %d'%(val_X,val_Y,val_Z)) + time.sleep(0.01) + +def destroy(): + adc.close() + button.close() + + +if __name__ == '__main__': + print ('Program is starting ... ') # Program entrance + setup() + try: + loop() + except KeyboardInterrupt: # Press ctrl-c to end the program. + destroy() + print("Ending program") diff --git a/Code/Python_GPIOZero_Code/13.1.1_Motor/ADCDevice.py b/Code/Python_GPIOZero_Code/13.1.1_Motor/ADCDevice.py new file mode 100644 index 0000000..b8ea063 --- /dev/null +++ b/Code/Python_GPIOZero_Code/13.1.1_Motor/ADCDevice.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 +######################################################################## +# Filename : ADCDevice.py +# Description : Freenove ADC Module library. +# Author : www.freenove.com +# modification: 2023/05/11 +######################################################################## + +import smbus + +class ADCDevice(object): + def __init__(self): + self.cmd = 0 + self.address = 0 + self.bus=smbus.SMBus(1) + # print("ADCDevice init") + + def detectI2C(self,addr): + try: + self.bus.write_byte(addr,0) + print("Found device in address 0x%x"%(addr)) + return True + except: + print("Not found device in address 0x%x"%(addr)) + return False + + def close(self): + self.bus.close() + +class PCF8591(ADCDevice): + def __init__(self): + super(PCF8591, self).__init__() + self.cmd = 0x40 # The default command for PCF8591 is 0x40. + self.address = 0x48 # 0x48 is the default i2c address for PCF8591 Module. + + def analogRead(self, chn): # PCF8591 has 4 ADC input pins, chn:0,1,2,3 + value = self.bus.read_byte_data(self.address, self.cmd+chn) + value = self.bus.read_byte_data(self.address, self.cmd+chn) + return value + + def analogWrite(self,value): # write DAC value + self.bus.write_byte_data(address,cmd,value) + +class ADS7830(ADCDevice): + def __init__(self): + super(ADS7830, self).__init__() + self.cmd = 0x84 + self.address = 0x4b # 0x4b is the default i2c address for ADS7830 Module. + + def analogRead(self, chn): # ADS7830 has 8 ADC input pins, chn:0,1,2,3,4,5,6,7 + value = self.bus.read_byte_data(self.address, self.cmd|(((chn<<2 | chn>>1)&0x07)<<4)) + return value diff --git a/Code/Python_GPIOZero_Code/13.1.1_Motor/Motor.py b/Code/Python_GPIOZero_Code/13.1.1_Motor/Motor.py new file mode 100644 index 0000000..b3c3d7f --- /dev/null +++ b/Code/Python_GPIOZero_Code/13.1.1_Motor/Motor.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python3 +############################################################################# +# Filename : Motor.py +# Description : Control Motor with L293D +# Author : www.freenove.com +# modification: 2023/05/11 +######################################################################## +from gpiozero import DigitalOutputDevice,PWMOutputDevice +import time +from ADCDevice import * + +# define the pins connected to L293D +motoRPin1 = DigitalOutputDevice(27) # define L293D pin according to BCM Numbering +motoRPin2 = DigitalOutputDevice(17) # define L293D pin according to BCM Numbering +enablePin = PWMOutputDevice(22,frequency=1000) +adc = ADCDevice() # Define an ADCDevice class object + +def setup(): + global adc + if(adc.detectI2C(0x48)): # Detect the pcf8591. + adc = PCF8591() + elif(adc.detectI2C(0x4b)): # Detect the ads7830 + adc = ADS7830() + else: + print("No correct I2C address found, \n" + "Please use command 'i2cdetect -y 1' to check the I2C address! \n" + "Program Exit. \n"); + exit(-1) +# mapNUM function: map the value from a range of mapping to another range. +def mapNUM(value,fromLow,fromHigh,toLow,toHigh): + return (toHigh-toLow)*(value-fromLow) / (fromHigh-fromLow) + toLow + +# motor function: determine the direction and speed of the motor according to the input ADC value input +def motor(ADC): + value = ADC -128 + if (value > 0): # make motor turn forward + motoRPin1.on() # motoRPin1 output HIHG level + motoRPin2.off() # motoRPin2 output LOW level + print ('Turn Forward...') + elif (value < 0): # make motor turn backward + motoRPin1.off() + motoRPin2.on() + print ('Turn Backward...') + else : + motoRPin1.off() + motoRPin2.off() + print ('Motor Stop...') + b=mapNUM(abs(value),0,128,0,100) + enablePin.value = b / 100.0 # set dc value as the duty cycle + print ('The PWM duty cycle is %d%%\n'%(abs(value)*100/127)) # print PMW duty cycle. + +def loop(): + while True: + value = adc.analogRead(0) # read ADC value of channel 0 + print ('ADC Value : %d'%(value)) + motor(value) + time.sleep(0.2) + +def destroy(): + motoRPin1.close() + motoRPin2.close() + enablePin.close() + adc.close() + +if __name__ == '__main__': # Program entrance + print ('Program is starting ... ') + setup() + try: + loop() + except KeyboardInterrupt: # Press ctrl-c to end the program. + destroy() + print("Ending program") diff --git a/Code/Python_GPIOZero_Code/14.1.1_Relay/Relay.py b/Code/Python_GPIOZero_Code/14.1.1_Relay/Relay.py new file mode 100644 index 0000000..545c04e --- /dev/null +++ b/Code/Python_GPIOZero_Code/14.1.1_Relay/Relay.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 +######################################################################## +# Filename : Relay.py +# Description : Control Relay and Motor via Button +# Author : www.freenove.com +# modification: 2023/05/15 +######################################################################## +from gpiozero import DigitalOutputDevice, Button +import time + +relayPin = 17 # define the relayPin +buttonPin = 18 # define the buttonPin +relay = DigitalOutputDevice(relayPin) # define LED pin according to BCM Numbering +button = Button(buttonPin) # define Button pin according to BCM Numbering + +def onButtonPressed(): # When button is pressed, this function will be executed + relay.toggle() + if relay.value : + print("Turn on relay ...") + else : + print("Turn off relay ... ") + +def loop(): + button.when_pressed = onButtonPressed + while True: + time.sleep(1) + +def destroy(): + relay.close() + button.close() + +if __name__ == '__main__': # Program entrance + print ('Program is starting...') + try: + loop() + except KeyboardInterrupt: # Press ctrl-c to end the program. + destroy() + print("Ending program") \ No newline at end of file diff --git a/Code/Python_GPIOZero_Code/14.1.1_Relay/Relay2.py b/Code/Python_GPIOZero_Code/14.1.1_Relay/Relay2.py new file mode 100644 index 0000000..2a7042e --- /dev/null +++ b/Code/Python_GPIOZero_Code/14.1.1_Relay/Relay2.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python3 +######################################################################## +# Filename : Relay.py +# Description : Control Relay and Motor via Button +# Author : www.freenove.com +# modification: 2023/05/15 +######################################################################## +from gpiozero import DigitalOutputDevice, Button +import time + +relayPin = 17 # define the relayPin +buttonPin = 18 # define the buttonPin +relay = DigitalOutputDevice(relayPin) # define LED pin according to BCM Numbering +button = Button(buttonPin) # define Button pin according to BCM Numbering +debounceTime = 50 + +def loop(): + relayState = 0 + lastChangeTime = round(time.time()*1000) + buttonState = 1 + lastButtonState = 1 + reading = 1 + while True: + reading = not button.value + if reading != lastButtonState : + lastChangeTime = round(time.time()*1000) + if ((round(time.time()*1000) - lastChangeTime) > debounceTime): + if reading != buttonState : + buttonState = reading; + if buttonState == 0: + print("Button is pressed!") + relayState = not relayState + if relayState: + print("Turn on relay ...") + else : + print("Turn off relay ... ") + else : + print("Button is released!") + relay.on() if (relayState==1) else relay.off() + lastButtonState = reading # lastButtonState store latest state + +def destroy(): + relay.close() + button.close() + +if __name__ == '__main__': # Program entrance + print ('Program is starting...') + try: + loop() + except KeyboardInterrupt: # Press ctrl-c to end the program. + destroy() + print("Ending program") + \ No newline at end of file diff --git a/Code/Python_GPIOZero_Code/15.1.1_Sweep/Sweep.py b/Code/Python_GPIOZero_Code/15.1.1_Sweep/Sweep.py new file mode 100644 index 0000000..420e68a --- /dev/null +++ b/Code/Python_GPIOZero_Code/15.1.1_Sweep/Sweep.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 +######################################################################## +# Filename : Sweep.py +# Description : Servo sweep +# Author : www.freenove.com +# modification: 2023/05/12 +######################################################################## +from gpiozero import AngularServo +import time + +myGPIO=18 +SERVO_DELAY_SEC = 0.001 +myCorrection=0.0 +maxPW=(2.5+myCorrection)/1000 +minPW=(0.5-myCorrection)/1000 +servo = AngularServo(myGPIO,initial_angle=0,min_angle=0, max_angle=180,min_pulse_width=minPW,max_pulse_width=maxPW) + +def loop(): + while True: + for angle in range(0, 181, 1): # make servo rotate from 0 to 180 deg + servo.angle = angle + time.sleep(SERVO_DELAY_SEC) + time.sleep(0.5) + for angle in range(180, -1, -1): # make servo rotate from 180 to 0 deg + servo.angle = angle + time.sleep(SERVO_DELAY_SEC) + time.sleep(0.5) + +if __name__ == '__main__': # Program entrance + print ('Program is starting...') + try: + loop() + except KeyboardInterrupt: # Press ctrl-c to end the program. + print("Ending program") \ No newline at end of file diff --git a/Code/Python_GPIOZero_Code/15.1.1_Sweep/Sweep2.py b/Code/Python_GPIOZero_Code/15.1.1_Sweep/Sweep2.py new file mode 100644 index 0000000..f229682 --- /dev/null +++ b/Code/Python_GPIOZero_Code/15.1.1_Sweep/Sweep2.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python3 +######################################################################## +# Filename : Sweep.py +# Description : Servo sweep +# Author : www.freenove.com +# modification: 2023/05/12 +######################################################################## +import os +os.system("sudo pigpiod") +from gpiozero import AngularServo +from gpiozero.pins.pigpio import PiGPIOFactory +import time + +my_factory = PiGPIOFactory() +myGPIO=18 +SERVO_DELAY_SEC = 0.001 +myCorrection=0.0 +maxPW=(2.5+myCorrection)/1000 +minPW=(0.5-myCorrection)/1000 +servo = AngularServo(myGPIO,initial_angle=0,min_angle=0, max_angle=180,min_pulse_width=minPW,max_pulse_width=maxPW,pin_factory=my_factory) + +def loop(): + while True: + for angle in range(0, 181, 1): # make servo rotate from 0 to 180 deg + servo.angle = angle + time.sleep(SERVO_DELAY_SEC) + time.sleep(0.5) + for angle in range(180, -1, -1): # make servo rotate from 180 to 0 deg + servo.angle = angle + time.sleep(SERVO_DELAY_SEC) + time.sleep(0.5) + +if __name__ == '__main__': # Program entrance + print ('Program is starting...') + try: + loop() + except KeyboardInterrupt: # Press ctrl-c to end the program. + servo.close() + os.system("sudo killall pigpiod") + print("Ending program") \ No newline at end of file diff --git a/Code/Python_GPIOZero_Code/16.1.1_SteppingMotor/SteppingMotor.py b/Code/Python_GPIOZero_Code/16.1.1_SteppingMotor/SteppingMotor.py new file mode 100644 index 0000000..b705b59 --- /dev/null +++ b/Code/Python_GPIOZero_Code/16.1.1_SteppingMotor/SteppingMotor.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 +######################################################################## +# Filename : SteppingMotor.py +# Description : Drive SteppingMotor +# Author : www.freenove.com +# modification: 2023/05/12 +######################################################################## +from gpiozero import OutputDevice +import time + +motorPins = (18, 23, 24, 25) # define pins connected to four phase ABCD of stepper motor +# motorPins = ("J8:12", "J8:16", "J8:18", "J8:22") # define pins connected to four phase ABCD of stepper motor +motors = list(map(lambda pin: OutputDevice(pin), motorPins)) +CCWStep = (0x01,0x02,0x04,0x08) # define power supply order for rotating anticlockwise +CWStep = (0x08,0x04,0x02,0x01) # define power supply order for rotating clockwise + +# as for four phase stepping motor, four steps is a cycle. the function is used to drive the stepping motor clockwise or anticlockwise to take four steps +def moveOnePeriod(direction,ms): + for j in range(0,4,1): # cycle for power supply order + for i in range(0,4,1): # assign to each pin + if (direction == 1):# power supply order clockwise + motors[i].on() if (CCWStep[j] == 1< 0 else self.CCW + # Decrement the number of steps, moving one step each time + while steps_left > 0: + if self.direction == self.CCW: + self.step_number = (self.step_number + 1) % self.number_of_steps + else: + self.step_number = (self.step_number - 1) % self.number_of_steps + steps_left -= 1 + self.step_motor() + + """Moves the motor forward or backwards""" + def step_motor(self): + # Select the correct control signal sequence + this_step = self.step_number % len(self.step_sequence) + seq = self.step_sequence[this_step] + # Set pin state accordingly + for pin in range(self.pin_count): + if seq[pin] == 1: + self.motor_pins[pin].on() + else: + self.motor_pins[pin].off() + sleep(self.step_delay) + + """Rotates the motor clockwise indefinitely""" + def forward(self): + for i in range(0,1024,1): + self.step_number = (self.step_number - 1) % self.number_of_steps + self.step_motor() + + """Rotates the motor counter-clockwise indefinitely""" + def backward(self): + for i in range(0,1024,1): + self.step_number = (self.step_number + 1) % self.number_of_steps + self.step_motor() + """Number of motor revolutions""" + def movearound(self, step_around): + if step_around >= 0: + while step_around: + self.step(32*64) + step_around -= 1 + elif step_around < 0: + while step_around: + self.step(-32*64) + step_around += 1 + """Motor rotation Angle""" + def moveangle(self, step_angle): + self.step((step_angle*32*64)/360) + \ No newline at end of file diff --git a/Code/Python_GPIOZero_Code/17.1.1_LightWater02/LightWater02.py b/Code/Python_GPIOZero_Code/17.1.1_LightWater02/LightWater02.py new file mode 100644 index 0000000..472fe50 --- /dev/null +++ b/Code/Python_GPIOZero_Code/17.1.1_LightWater02/LightWater02.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python3 +############################################################################# +# Filename : LightWater02.py +# Description : Control LED with 74HC595 +# Author : www.freenove.com +# modification: 2023/05/12 +######################################################################## +from gpiozero import OutputDevice +import time +# Defines the data bit that is transmitted preferentially in the shiftOut function. +LSBFIRST = 1 +MSBFIRST = 2 +# define the pins for 74HC595 +dataPin = OutputDevice(17) # DS Pin of 74HC595(Pin14) +latchPin = OutputDevice(27) # ST_CP Pin of 74HC595(Pin12) +clockPin = OutputDevice(22) # CH_CP Pin of 74HC595(Pin11) + +# shiftOut function, use bit serial transmission. +def shiftOut(order,val): + for i in range(0,8): + clockPin.off() + if(order == LSBFIRST): + dataPin.on() if (0x01&(val>>i)==0x01) else dataPin.off() + elif(order == MSBFIRST): + dataPin.on() if (0x80&(val<>=1 + time.sleep(0.1) + +def destroy(): + dataPin.close() + latchPin.close() + clockPin.close() + +if __name__ == '__main__': # Program entrance + print ('Program is starting...' ) + try: + loop() + except KeyboardInterrupt: # Press ctrl-c to end the program. + destroy() + print("Ending program") diff --git a/Code/Python_GPIOZero_Code/18.1.1_SevenSegmentDisplay/SevenSegmentDisplay.py b/Code/Python_GPIOZero_Code/18.1.1_SevenSegmentDisplay/SevenSegmentDisplay.py new file mode 100644 index 0000000..5d334df --- /dev/null +++ b/Code/Python_GPIOZero_Code/18.1.1_SevenSegmentDisplay/SevenSegmentDisplay.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python3 +############################################################################# +# Filename : SevenSegmentDisplay.py +# Description : Control SevenSegmentDisplay with 74HC595 +# Author : www.freenove.com +# modification: 2023/05/15 +######################################################################## +from gpiozero import OutputDevice +import time + +LSBFIRST = 1 +MSBFIRST = 2 +# define the pins for 74HC595 +dataPin = OutputDevice(17) # DS Pin of 74HC595(Pin14) +latchPin = OutputDevice(27) # ST_CP Pin of 74HC595(Pin12) +clockPin = OutputDevice(22) # CH_CP Pin of 74HC595(Pin11) +# SevenSegmentDisplay display the character "0"- "F" successively +num = [0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e] + +def shiftOut(order,val): + for i in range(0,8): + clockPin.off() + if(order == LSBFIRST): + dataPin.on() if (0x01&(val>>i)==0x01) else dataPin.off() + elif(order == MSBFIRST): + dataPin.on() if (0x80&(val<>i)==0x01) else dataPin.off() + elif(order == MSBFIRST): + dataPin.on() if (0x80&(val<>i)==0x01) else dataPin.off() + elif(order == MSBFIRST): + dataPin.on() if (0x80&(val<>=1 + for k in range(0,len(data)-8): #len(data) total number of "0-F" columns + for j in range(0,20): # times of repeated displaying LEDMatrix in every frame, the bigger the "j", the longer the display time. + x=0x80 # Set the column information to start from the first column + for i in range(k,k+8): + latchPin.off() + shiftOut(MSBFIRST,data[i]) + shiftOut(MSBFIRST,~x) + latchPin.on() + time.sleep(0.001) + x>>=1 +def destroy(): + dataPin.close() + latchPin.close() + clockPin.close() +if __name__ == '__main__': # Program entrance + print ('Program is starting...' ) + try: + loop() + except KeyboardInterrupt: # Press ctrl-c to end the program. + destroy() + print("Ending program") + diff --git a/Code/Python_GPIOZero_Code/20.1.1_I2CLCD1602/I2CLCD1602.py b/Code/Python_GPIOZero_Code/20.1.1_I2CLCD1602/I2CLCD1602.py new file mode 100644 index 0000000..d47f3bb --- /dev/null +++ b/Code/Python_GPIOZero_Code/20.1.1_I2CLCD1602/I2CLCD1602.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 +######################################################################## +# Filename : I2CLCD1602.py +# Description : Use the LCD display data +# Author : freenove +# modification: 2023/05/15 +######################################################################## +import smbus +from time import sleep, strftime +from datetime import datetime +from LCD1602 import CharLCD1602 + +lcd1602 = CharLCD1602() +def get_cpu_temp(): # get CPU temperature from file "/sys/class/thermal/thermal_zone0/temp" + tmp = open('/sys/class/thermal/thermal_zone0/temp') + cpu = tmp.read() + tmp.close() + return '{:.2f}'.format( float(cpu)/1000 ) + ' C ' + +def get_time_now(): # get system time + return datetime.now().strftime(' %H:%M:%S') + +def loop(): + lcd1602.init_lcd() + count = 0 + while(True): + lcd1602.clear() + lcd1602.write(0, 0, 'CPU: ' + get_cpu_temp() )# display CPU temperature + lcd1602.write(0, 1, get_time_now() ) # display the time + sleep(1) +def destroy(): + lcd1602.clear() +if __name__ == '__main__': + print ('Program is starting ... ') + try: + loop() + except KeyboardInterrupt: + destroy() + \ No newline at end of file diff --git a/Code/Python_GPIOZero_Code/20.1.1_I2CLCD1602/LCD1602.py b/Code/Python_GPIOZero_Code/20.1.1_I2CLCD1602/LCD1602.py new file mode 100644 index 0000000..5ebb7b5 --- /dev/null +++ b/Code/Python_GPIOZero_Code/20.1.1_I2CLCD1602/LCD1602.py @@ -0,0 +1,136 @@ +#!/usr/bin/env python3 +import time +import smbus +import subprocess + +class CharLCD1602(object): + def __init__(self): + # Note you need to change the bus number to 0 if running on a revision 1 Raspberry Pi. + self.bus = smbus.SMBus(1) + self.BLEN = 1 # turn on/off background light + self.PCF8574_address = 0x27 # I2C address of the PCF8574 chip. + self.PCF8574A_address = 0x3f # I2C address of the PCF8574A chip. + self.LCD_ADDR =self.PCF8574_address + def write_word(self,addr, data): + temp = data + if self.BLEN == 1: + temp |= 0x08 + else: + temp &= 0xF7 + self.bus.write_byte(addr ,temp) + + def send_command(self,comm): + # Send bit7-4 firstly + buf = comm & 0xF0 + buf |= 0x04 # RS = 0, RW = 0, EN = 1 + self.write_word(self.LCD_ADDR ,buf) + time.sleep(0.002) + buf &= 0xFB # Make EN = 0 + self.write_word(self.LCD_ADDR ,buf) + # Send bit3-0 secondly + buf = (comm & 0x0F) << 4 + buf |= 0x04 # RS = 0, RW = 0, EN = 1 + self.write_word(self.LCD_ADDR ,buf) + time.sleep(0.002) + buf &= 0xFB # Make EN = 0 + self.write_word(self.LCD_ADDR ,buf) + + def send_data(self,data): + # Send bit7-4 firstly + buf = data & 0xF0 + buf |= 0x05 # RS = 1, RW = 0, EN = 1 + self.write_word(self.LCD_ADDR ,buf) + time.sleep(0.002) + buf &= 0xFB # Make EN = 0 + self.write_word(self.LCD_ADDR ,buf) + # Send bit3-0 secondly + buf = (data & 0x0F) << 4 + buf |= 0x05 # RS = 1, RW = 0, EN = 1 + self.write_word(self.LCD_ADDR ,buf) + time.sleep(0.002) + buf &= 0xFB # Make EN = 0 + self.write_word(self.LCD_ADDR ,buf) + + def i2c_scan(self): + cmd = "i2cdetect -y 1 |awk \'NR>1 {$1=\"\";print}\'" + result = subprocess.check_output(cmd, shell=True).decode() + result = result.replace("\n", "").replace(" --", "") + i2c_list = result.split(' ') + return i2c_list + + def init_lcd(self,addr=None, bl=1): + i2c_list = self.i2c_scan() +# print(f"i2c_list: {i2c_list}") + if addr is None: + if '27' in i2c_list: + self.LCD_ADDR = self.PCF8574_address + elif '3f' in i2c_list: + self.LCD_ADDR = self.PCF8574A_address + else: + raise IOError("I2C address 0x27 or 0x3f no found.") + else: + self.LCD_ADDR = addr + if str(hex(addr)).strip('0x') not in i2c_list: + raise IOError(f"I2C address {str(hex(addr))} or 0x3f no found.") + self.BLEN = bl + try: + self.send_command(0x33) # Must initialize to 8-line mode at first + time.sleep(0.005) + self.send_command(0x32) # Then initialize to 4-line mode + time.sleep(0.005) + self.send_command(0x28) # 2 Lines & 5*7 dots + time.sleep(0.005) + self.send_command(0x0C) # Enable display without cursor + time.sleep(0.005) + self.send_command(0x01) # Clear Screen + self.buswrite_byte(self.LCD_ADDR, 0x08) + except: + return False + else: + return True + + def clear(self): + self.send_command(0x01) # Clear Screen + + def openlight(self): # Enable the backlight + self.bus.write_byte(0x27,0x08) + self.bus.close() + + def write(self,x, y, str): + if x < 0: + x = 0 + if x > 15: + x = 15 + if y <0: + y = 0 + if y > 1: + y = 1 + # Move cursor + addr = 0x80 + 0x40 * y + x + self.send_command(addr) + for chr in str: + self.send_data(ord(chr)) + def display_num(self,x, y, num): + addr = 0x80 + 0x40 * y + x + self.send_command(addr) + self.send_data(num) + +def loop(): + count = 0 + while(True): + lcd1602.clear() + lcd1602.write(0, 0, ' Hello World! ' )# display CPU temperature + lcd1602.write(0, 1, ' Counter: ' + str(count) ) # display the time + time.sleep(1) + count += 1 +def destroy(): + lcd1602.clear() +lcd1602 = CharLCD1602() +if __name__ == '__main__': + print ('Program is starting ... ') + lcd1602.init_lcd(addr=None, bl=1) + try: + loop() + except KeyboardInterrupt: + destroy() + diff --git a/Code/Python_GPIOZero_Code/21.1.1_DHT11/DHT11.py b/Code/Python_GPIOZero_Code/21.1.1_DHT11/DHT11.py new file mode 100644 index 0000000..fa63095 --- /dev/null +++ b/Code/Python_GPIOZero_Code/21.1.1_DHT11/DHT11.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +############################################################################# +# Filename : DHT11.py +# Description : read the temperature and humidity data of DHT11 +# Author : freenove +# modification: 2020/10/16 +######################################################################## +import RPi.GPIO as GPIO +import time +import Freenove_DHT as DHT +DHTPin = 11 #define the pin of DHT11 + +def loop(): + dht = DHT.DHT(DHTPin) #create a DHT class object + counts = 0 # Measurement counts + while(True): + counts += 1 + print("Measurement counts: ", counts) + for i in range(0,15): + chk = dht.readDHT11() #read DHT11 and get a return value. Then determine whether data read is normal according to the return value. + if (chk is dht.DHTLIB_OK): #read DHT11 and get a return value. Then determine whether data read is normal according to the return value. + print("DHT11,OK!") + break + time.sleep(0.1) + print("Humidity : %.2f, \t Temperature : %.2f \n"%(dht.humidity,dht.temperature)) + time.sleep(2) + +if __name__ == '__main__': + print ('Program is starting ... ') + try: + loop() + except KeyboardInterrupt: + GPIO.cleanup() + exit() + diff --git a/Code/Python_GPIOZero_Code/21.1.1_DHT11/Freenove_DHT.py b/Code/Python_GPIOZero_Code/21.1.1_DHT11/Freenove_DHT.py new file mode 100644 index 0000000..72ef1a0 --- /dev/null +++ b/Code/Python_GPIOZero_Code/21.1.1_DHT11/Freenove_DHT.py @@ -0,0 +1,130 @@ +#!/usr/bin/env python3 +############################################################################# +# Filename : Freenove_DHT.py +# Description : DHT Temperature & Humidity Sensor library for Raspberry +# Author : freenove +# modification: 2020/10/16 +######################################################################## +import RPi.GPIO as GPIO +import time + +class DHT(object): + DHTLIB_OK = 0 + DHTLIB_ERROR_CHECKSUM = -1 + DHTLIB_ERROR_TIMEOUT = -2 + DHTLIB_INVALID_VALUE = -999 + + DHTLIB_DHT11_WAKEUP = 0.020#0.018 #18ms + DHTLIB_TIMEOUT = 0.0001 #100us + + humidity = 0 + temperature = 0 + + def __init__(self,pin): + self.pin = pin + self.bits = [0,0,0,0,0] + GPIO.setmode(GPIO.BOARD) + #Read DHT sensor, store the original data in bits[] + def readSensor(self,pin,wakeupDelay): + mask = 0x80 + idx = 0 + self.bits = [0,0,0,0,0] + # Clear sda + GPIO.setup(pin,GPIO.OUT) + GPIO.output(pin,GPIO.HIGH) + time.sleep(0.5) + # start signal + GPIO.output(pin,GPIO.LOW) + time.sleep(wakeupDelay) + GPIO.output(pin,GPIO.HIGH) + # time.sleep(0.000001) + GPIO.setup(pin,GPIO.IN) + + loopCnt = self.DHTLIB_TIMEOUT + # Waiting echo + t = time.time() + while True: + if (GPIO.input(pin) == GPIO.LOW): + break + if((time.time() - t) > loopCnt): + return self.DHTLIB_ERROR_TIMEOUT + # Waiting echo low level end + t = time.time() + while(GPIO.input(pin) == GPIO.LOW): + if((time.time() - t) > loopCnt): + #print ("Echo LOW") + return self.DHTLIB_ERROR_TIMEOUT + # Waiting echo high level end + t = time.time() + while(GPIO.input(pin) == GPIO.HIGH): + if((time.time() - t) > loopCnt): + #print ("Echo HIGH") + return self.DHTLIB_ERROR_TIMEOUT + for i in range(0,40,1): + t = time.time() + while(GPIO.input(pin) == GPIO.LOW): + if((time.time() - t) > loopCnt): + #print ("Data Low %d"%(i)) + return self.DHTLIB_ERROR_TIMEOUT + t = time.time() + while(GPIO.input(pin) == GPIO.HIGH): + if((time.time() - t) > loopCnt): + #print ("Data HIGH %d"%(i)) + return self.DHTLIB_ERROR_TIMEOUT + if((time.time() - t) > 0.00005): + self.bits[idx] |= mask + #print("t : %f"%(time.time()-t)) + mask >>= 1 + if(mask == 0): + mask = 0x80 + idx += 1 + #print (self.bits) + GPIO.setup(pin,GPIO.OUT) + GPIO.output(pin,GPIO.HIGH) + return self.DHTLIB_OK + #Read DHT sensor, analyze the data of temperature and humidity + def readDHT11Once(self): + rv = self.readSensor(self.pin,self.DHTLIB_DHT11_WAKEUP) + if (rv is not self.DHTLIB_OK): + self.humidity = self.DHTLIB_INVALID_VALUE + self.temperature = self.DHTLIB_INVALID_VALUE + return rv + self.humidity = self.bits[0] + self.temperature = self.bits[2] + self.bits[3]*0.1 + sumChk = ((self.bits[0] + self.bits[1] + self.bits[2] + self.bits[3]) & 0xFF) + if(self.bits[4] is not sumChk): + return self.DHTLIB_ERROR_CHECKSUM + return self.DHTLIB_OK + def readDHT11(self): + result = self.DHTLIB_INVALID_VALUE + for i in range(0,15): + result = self.readDHT11Once() + if result == self.DHTLIB_OK: + return self.DHTLIB_OK + time.sleep(0.1) + return result + + +def loop(): + dht = DHT(11) + sumCnt = 0 + okCnt = 0 + while(True): + sumCnt += 1 + chk = dht.readDHT11() + if (chk is 0): + okCnt += 1 + okRate = 100.0*okCnt/sumCnt; + print("sumCnt : %d, \t okRate : %.2f%% "%(sumCnt,okRate)) + print("chk : %d, \t Humidity : %.2f, \t Temperature : %.2f "%(chk,dht.humidity,dht.temperature)) + time.sleep(3) + +if __name__ == '__main__': + print ('Program is starting ... ') + try: + loop() + except KeyboardInterrupt: + pass + exit() + + diff --git a/Code/Python_GPIOZero_Code/21.1.1_DHT11/setup.py b/Code/Python_GPIOZero_Code/21.1.1_DHT11/setup.py new file mode 100644 index 0000000..e9e5c1a --- /dev/null +++ b/Code/Python_GPIOZero_Code/21.1.1_DHT11/setup.py @@ -0,0 +1,13 @@ + +from setuptools import setup,find_packages + +setup( + name = "Freenove_DHT", + version = "V1.0.1", + description = "Read DHT Sensor", + author = "Freenove", + url = "http://www.freenove.com", + license = " ", + packages = find_packages(), + scripts = ["Freenove_DHT.py"], + ) diff --git a/Code/Python_GPIOZero_Code/22.1.1_MatrixKeypad/Keypad.py b/Code/Python_GPIOZero_Code/22.1.1_MatrixKeypad/Keypad.py new file mode 100644 index 0000000..07c7453 --- /dev/null +++ b/Code/Python_GPIOZero_Code/22.1.1_MatrixKeypad/Keypad.py @@ -0,0 +1,203 @@ +#!/usr/bin/env python3 +######################################################################## +# Filename : Keypad.py +# Description : The module of matrix keypad +# Author : freenove +# modification: 2023/05/15 +######################################################################## +from gpiozero import InputDevice, OutputDevice +import time +#class Key:Define some of the properties of Key +class Key(object): + NO_KEY = '\0' + #Defines the four states of Key + IDLE = 0 + PRESSED = 1 + HOLD = 2 + RELEASED = 3 + #define OPEN and CLOSED + OPEN = 0 + CLOSED =1 + #constructor + def __init__(self): + self.kchar = self.NO_KEY + self.kstate = self.IDLE + self.kcode = -1 + self.stateChanged = False + +class Keypad(object): + NULL = '\0' + LIST_MAX = 10 #Max number of keys on the active list. + MAPSIZE = 10 #MAPSIZE is the number of rows (times 16 columns) + bitMap = [0]*MAPSIZE + key = [Key()]*LIST_MAX + holdTime = 500 #key hold time + holdTimer = 0 + startTime = 0 + #Allows custom keymap, pin configuration, and keypad sizes. + def __init__(self,usrKeyMap,row_Pins,col_Pins,num_Rows,num_Cols): + self.rowPins = row_Pins + self.colPins = col_Pins + self.numRows = num_Rows + self.numCols = num_Cols + + self.keymap = usrKeyMap + self.setDebounceTime(10) + #Returns a single key only. Retained for backwards compatibility. + def getKey(self): + single_key = True + if(self.getKeys() and self.key[0].stateChanged and (self.key[0].kstate == self.key[0].PRESSED)): + return self.key[0].kchar + single_key = False + return self.key[0].NO_KEY + #Populate the key list. + def getKeys(self): + keyActivity = False + #Limit how often the keypad is scanned. + if((time.time() - self.startTime) > self.debounceTime*0.001): + self.scanKeys() + keyActivity = self.updateList() + self.startTime = time.time() + return keyActivity + #Hardware scan ,the result store in bitMap + def scanKeys(self): + #Re-intialize the row pins. Allows sharing these pins with other hardware. + inputs = list(map(lambda pin: InputDevice(pin, pull_up=True), self.rowPins)) + #bitMap stores ALL the keys that are being pressed. outputs = OutputDevice(pin_c,active_high=False) + for pin_c in self.colPins: + outputs = OutputDevice(pin_c) + outputs.off() + i=0 + for r in self.rowPins: #keypress is active low so invert to high. inputs[i].is_active inputs[i].value + self.bitMap[self.rowPins.index(r)] = self.bitWrite(self.bitMap[self.rowPins.index(r)],self.colPins.index(pin_c), inputs[i].value) + i =i+1 + #Set pin to high impedance input. Effectively ends column pulse. + outputs.on() + outputs.close() + outputs = InputDevice(pin_c,pull_up=True) + #Manage the list without rearranging the keys. Returns true if any keys on the list changed state. + def updateList(self): + anyActivity = False + kk = Key() + #Delete any IDLE keys + for i in range(self.LIST_MAX): + if(self.key[i].kstate == kk.IDLE): + self.key[i].kchar = kk.NO_KEY + self.key[i].kcode = -1 + self.key[i].stateChanged = False + # Add new keys to empty slots in the key list. + for r in range(self.numRows): + for c in range(self.numCols): + button = self.bitRead(self.bitMap[r],c) + keyChar = self.keymap[r * self.numCols +c] + keyCode = r * self.numCols +c + idx = self.findInList(keyCode) + #Key is already on the list so set its next state. + if(idx > -1): + self.nextKeyState(idx,button) + #Key is NOT on the list so add it. + if((idx == -1) and button): + for i in range(self.LIST_MAX): + if(self.key[i].kchar == kk.NO_KEY): #Find an empty slot or don't add key to list. + self.key[i].kchar = keyChar + self.key[i].kcode = keyCode + self.key[i].kstate = kk.IDLE #Keys NOT on the list have an initial state of IDLE. + self.nextKeyState(i,button) + break #Don't fill all the empty slots with the same key. + #Report if the user changed the state of any key. + for i in range(self.LIST_MAX): + if(self.key[i].stateChanged): + anyActivity = True + return anyActivity + #This function is a state machine but is also used for debouncing the keys. + def nextKeyState(self,idx, button): + self.key[idx].stateChanged = False + kk = Key() + if(self.key[idx].kstate == kk.IDLE): + if(button == kk.CLOSED): + self.transitionTo(idx,kk.PRESSED) + self.holdTimer = time.time() #Get ready for next HOLD state. + elif(self.key[idx].kstate == kk.PRESSED): + if((time.time() - self.holdTimer) > self.holdTime*0.001): #Waiting for a key HOLD... + self.transitionTo(idx,kk.HOLD) + elif(button == kk.OPEN): # or for a key to be RELEASED. + self.transitionTo(idx,kk.RELEASED) + elif(self.key[idx].kstate == kk.HOLD): + if(button == kk.OPEN): + self.transitionTo(idx,kk.RELEASED) + elif(self.key[idx].kstate == kk.RELEASED): + self.transitionTo(idx,kk.IDLE) + + def transitionTo(self,idx,nextState): + self.key[idx].kstate = nextState + self.key[idx].stateChanged = True + #Search by code for a key in the list of active keys. + #Returns -1 if not found or the index into the list of active keys. + def findInList(self,keyCode): + for i in range(self.LIST_MAX): + if(self.key[i].kcode == keyCode): + return i + return -1 + #set Debounce Time, The default is 50ms + def setDebounceTime(self,ms): + self.debounceTime = ms + #set HoldTime,The default is 500ms + def setHoldTime(self,ms): + self.holdTime = ms + # + def isPressed(keyChar): + for i in range(self.LIST_MAX): + if(self.key[i].kchar == keyChar): + if(self.key[i].kstate == self.self.key[i].PRESSED and self.key[i].stateChanged): + return True + return False + # + def waitForKey(): + kk = Key() + waitKey = kk.NO_KEY + while(waitKey == kk.NO_KEY): + waitKey = getKey() + return waitKey + + def getState(): + return self.key[0].kstate + # + def keyStateChanged(): + return self.key[0].stateChanged + + def bitWrite(self,x,n,b): + if(b): + x |= (1<>n)&1 == 1): + return True + else: + return False + +#######################EXAMPLE################################## +ROWS = 4 +COLS = 4 +keys = [ '1','2','3','A', + '4','5','6','B', + '7','8','9','C', + '*','0','#','D' ] +rowsPins = [18, 23, 24, 25] +colsPins = [10, 22, 27, 17] + +def loop(): + keypad = Keypad(keys,rowsPins,colsPins,ROWS,COLS) + keypad.setDebounceTime(50) + while(True): + key = keypad.getKey() + if(key != keypad.NULL): + print ("You Pressed Key : %c "%(key) ) + +if __name__ == '__main__': # Program start from here + print ("Program is starting ... ") + try: + loop() + except KeyboardInterrupt: # When 'Ctrl+C' is pressed, the child program destroy() will be executed. + print("Ending program") \ No newline at end of file diff --git a/Code/Python_GPIOZero_Code/22.1.1_MatrixKeypad/Keypad2/keypad2.py b/Code/Python_GPIOZero_Code/22.1.1_MatrixKeypad/Keypad2/keypad2.py new file mode 100644 index 0000000..2e97367 --- /dev/null +++ b/Code/Python_GPIOZero_Code/22.1.1_MatrixKeypad/Keypad2/keypad2.py @@ -0,0 +1,38 @@ +import time +from gpiozero import InputDevice, OutputDevice + +output_pins = [10, 22, 27, 17] +input_pins = [18, 23, 24, 25] + +inputs = list(map(lambda pin: InputDevice(pin, pull_up=True), input_pins)) +outputs = list(map(lambda pin: InputDevice(pin, pull_up=True), output_pins)) + +mapping = [ + ['1', '2', '3', 'A'], + ['4', '5', '6', 'B'], + ['7', '8', '9', 'C'], + ['*', '0', '#', 'D'], +] + +pressed = set([]) + +while True: + for o in range(4): + outputs[o].close() + tmp = OutputDevice(output_pins[o], active_high=False) + tmp.on() + for i in range(4): + key = mapping[i][o] + if inputs[i].is_active: + if key == '': + continue + if key not in pressed: + print(key) + pressed.add(key) + else: + if key in pressed: + pressed.remove(key) + + tmp.close() + outputs[o] = InputDevice(output_pins[o], pull_up=True) + time.sleep(0.02) diff --git a/Code/Python_GPIOZero_Code/22.1.1_MatrixKeypad/MatrixKeypad.py b/Code/Python_GPIOZero_Code/22.1.1_MatrixKeypad/MatrixKeypad.py new file mode 100644 index 0000000..04ded3c --- /dev/null +++ b/Code/Python_GPIOZero_Code/22.1.1_MatrixKeypad/MatrixKeypad.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python3 +######################################################################## +# Filename : MatrixKeypad.py +# Description : obtain the key code of 4x4 Matrix Keypad +# Author : freenove +# modification: 2023/05/15 +######################################################################## +import Keypad #import module Keypad +ROWS = 4 # number of rows of the Keypad +COLS = 4 #number of columns of the Keypad +keys = [ '1','2','3','A', #key code + '4','5','6','B', + '7','8','9','C', + '*','0','#','D' ] +rowsPins = [18, 23, 24, 25] #connect to the row pinouts of the keypad +colsPins = [10, 22, 27, 17] #connect to the column pinouts of the keypad +def loop(): + keypad = Keypad.Keypad(keys,rowsPins,colsPins,ROWS,COLS) #creat Keypad object + keypad.setDebounceTime(50) #set the debounce time + while(True): + key = keypad.getKey() #obtain the state of keys + if(key != keypad.NULL): #if there is key pressed, print its key code. + print ("You Pressed Key : %c "%(key)) + +if __name__ == '__main__': #Program start from here + print ("Program is starting ... ") + try: + loop() + except KeyboardInterrupt: #When 'Ctrl+C' is pressed, exit the program. + print("Ending program") \ No newline at end of file diff --git a/Code/Python_GPIOZero_Code/23.1.1_SenseLED/SenseLED.py b/Code/Python_GPIOZero_Code/23.1.1_SenseLED/SenseLED.py new file mode 100644 index 0000000..38e5287 --- /dev/null +++ b/Code/Python_GPIOZero_Code/23.1.1_SenseLED/SenseLED.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python3 +######################################################################## +# Filename : SenseLED.py +# Description : Control led with infrared Motion sensor. +# auther : www.freenove.com +# modification: 2023/05/11 +######################################################################## +from gpiozero import LED,MotionSensor +import time + +ledPin = 18 # define ledPin +sensorPin = 17 # define sensorPin +led = LED(ledPin) +sensor = MotionSensor(sensorPin) +sensor.wait_for_no_motion() +def loop(): + # Variables to hold the current and last states + currentstate = False + previousstate = False + while True: + # Read sensor state + currentstate = sensor.motion_detected + # If the sensor is triggered + if currentstate == True and previousstate == False: + led.on() + print("Motion detected!led turned on >>>") + # Record previous state + previousstate = True + # If the sensor has returned to ready state + elif currentstate == False and previousstate == True: + led.off() + print("No Motion!led turned off <<") + previousstate = False + # Wait for 10 milliseconds + time.sleep(0.01) + +def destroy(): + led.close() + sensor.close() + +if __name__ == '__main__': # Program entrance + print ('Program is starting...') + try: + loop() + except KeyboardInterrupt: # Press ctrl-c to end the program. + destroy() + print("Ending program") \ No newline at end of file diff --git a/Code/Python_GPIOZero_Code/24.1.1_UltrasonicRanging/UltrasonicRanging.py b/Code/Python_GPIOZero_Code/24.1.1_UltrasonicRanging/UltrasonicRanging.py new file mode 100644 index 0000000..84a3188 --- /dev/null +++ b/Code/Python_GPIOZero_Code/24.1.1_UltrasonicRanging/UltrasonicRanging.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +######################################################################## +# Filename : UltrasonicRanging.py +# Description : Get distance via UltrasonicRanging sensor +# auther : www.freenove.com +# modification: 2023/05/13 +######################################################################## +from gpiozero import DistanceSensor +from time import sleep + +trigPin = 23 +echoPin = 24 +sensor = DistanceSensor(echo=echoPin, trigger=trigPin ,max_distance=3) + +def loop(): + while True: + print('Distance: ', sensor.distance * 100,'cm') + sleep(1) + +if __name__ == '__main__': # Program entrance + print ('Program is starting...') + try: + loop() + except KeyboardInterrupt: # Press ctrl-c to end the program. + sensor.close() + print("Ending program") diff --git a/Code/Python_GPIOZero_Code/24.1.1_UltrasonicRanging/UltrasonicRanging2.py b/Code/Python_GPIOZero_Code/24.1.1_UltrasonicRanging/UltrasonicRanging2.py new file mode 100644 index 0000000..1fce3f4 --- /dev/null +++ b/Code/Python_GPIOZero_Code/24.1.1_UltrasonicRanging/UltrasonicRanging2.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python3 +######################################################################## +# Filename : UltrasonicRanging.py +# Description : Get distance via UltrasonicRanging sensor +# auther : www.freenove.com +# modification: 2023/05/13 +######################################################################## +import os +os.system("sudo pigpiod") +from gpiozero import DistanceSensor +from gpiozero.pins.pigpio import PiGPIOFactory +from time import sleep + +trigPin = 23 +echoPin = 24 +my_factory = PiGPIOFactory() +sensor = DistanceSensor(echo=echoPin, trigger=trigPin ,max_distance=3,pin_factory=my_factory) + +def loop(): + while True: + print('Distance: ', sensor.distance * 100,'cm') + sleep(1) + +if __name__ == '__main__': # Program entrance + print ('Program is starting...') + try: + loop() + except KeyboardInterrupt: # Press ctrl-c to end the program. + sensor.close() + os.system("sudo killall pigpiod") + print("Ending program") + + \ No newline at end of file diff --git a/Code/Python_GPIOZero_Code/25.1.1_MPU6050/LICENSE b/Code/Python_GPIOZero_Code/25.1.1_MPU6050/LICENSE new file mode 100644 index 0000000..2f78a9a --- /dev/null +++ b/Code/Python_GPIOZero_Code/25.1.1_MPU6050/LICENSE @@ -0,0 +1,24 @@ +The MIT License (MIT) + +MPU6050 Python I2C Class + +Copyright (c) 2015 Geir Istad + +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. + diff --git a/Code/Python_GPIOZero_Code/25.1.1_MPU6050/MPU6050.py b/Code/Python_GPIOZero_Code/25.1.1_MPU6050/MPU6050.py new file mode 100644 index 0000000..a826113 --- /dev/null +++ b/Code/Python_GPIOZero_Code/25.1.1_MPU6050/MPU6050.py @@ -0,0 +1,946 @@ +__author__ = 'Geir Istad' +""" +MPU6050 Python I2C Class +Copyright (c) 2015 Geir Istad + +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. + + +Code based on +I2Cdev library collection - MPU6050 I2C device class +by Jeff Rowberg +============================================ +I2Cdev device library code is placed under the MIT license +Copyright (c) 2012 Jeff Rowberg +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. +=============================================== +""" + +import math +import ctypes +import time +import smbus +import csv +from MPUConstants import MPUConstants as C +from Quaternion import Quaternion as Q +from Quaternion import XYZVector as V + + +class MPU6050: + __buffer = [0] * 14 + __debug = False + __DMP_packet_size = 0 + __dev_id = 0 + __bus = None + + def __init__(self, a_bus=1, a_address=C.MPU6050_DEFAULT_ADDRESS, + a_xAOff=None, a_yAOff=None, a_zAOff=None, a_xGOff=None, + a_yGOff=None, a_zGOff=None, a_debug=False): + self.__dev_id = a_address + # Connect to num 1 SMBus + self.__bus = smbus.SMBus(a_bus) + # Set clock source to gyro + self.set_clock_source(C.MPU6050_CLOCK_PLL_XGYRO) + # Set accelerometer range + self.set_full_scale_accel_range(C.MPU6050_ACCEL_FS_2) + # Set gyro range + self.set_full_scale_gyro_range(C.MPU6050_GYRO_FS_250) + # Take the MPU out of time.sleep mode + self.wake_up() + # Set offsets + if a_xAOff: + self.set_x_accel_offset(a_xAOff) + if a_yAOff: + self.set_y_accel_offset(a_yAOff) + if a_zAOff: + self.set_z_accel_offset(a_zAOff) + if a_xGOff: + self.set_x_gyro_offset(a_xGOff) + if a_yGOff: + self.set_y_gyro_offset(a_yGOff) + if a_zGOff: + self.set_z_gyro_offset(a_zGOff) + self.__debug = a_debug + + # Core bit and byte operations + def read_bit(self, a_reg_add, a_bit_position): + return self.read_bits(a_reg_add, a_bit_position, 1) + + def write_bit(self, a_reg_add, a_bit_num, a_bit): + byte = self.__bus.read_byte_data(self.__dev_id, a_reg_add) + if a_bit: + byte |= 1 << a_bit_num + else: + byte &= ~(1 << a_bit_num) + self.__bus.write_byte_data( + self.__dev_id, a_reg_add, ctypes.c_int8(byte).value) + + def read_bits(self, a_reg_add, a_bit_start, a_length): + byte = self.__bus.read_byte_data(self.__dev_id, a_reg_add) + mask = ((1 << a_length) - 1) << (a_bit_start - a_length + 1) + byte &= mask + byte >>= a_bit_start - a_length + 1 + return byte + + def write_bits(self, a_reg_add, a_bit_start, a_length, a_data): + byte = self.__bus.read_byte_data(self.__dev_id, a_reg_add) + mask = ((1 << a_length) - 1) << (a_bit_start - a_length + 1) + # Get data in position and zero all non-important bits in data + a_data <<= a_bit_start - a_length + 1 + a_data &= mask + # Clear all important bits in read byte and combine with data + byte &= ~mask + byte = byte | a_data + # Write the data to the I2C device + self.__bus.write_byte_data( + self.__dev_id, a_reg_add, ctypes.c_int8(byte).value) + + def read_memory_byte(self): + return self.__bus.read_byte_data(self.__dev_id, C.MPU6050_RA_MEM_R_W) + + def read_bytes(self, a_data_list, a_address, a_length): + if a_length > len(a_data_list): + print('read_bytes, length of passed list too short') + return a_data_list + # Attempt to use the built in read bytes function in the adafruit lib + # a_data_list = self.__bus.read_i2c_block_data(self.__dev_id, a_address, + # a_length) + # Attempt to bypass adafruit lib + #a_data_list = self.__mpu.bus.read_i2c_block_data(0x68, a_address, a_length) + #print('data' + str(a_data_list)) + for x in range(0, a_length): + a_data_list[x] = self.__bus.read_byte_data(self.__dev_id, + a_address + x) + return a_data_list + + def write_memory_block(self, a_data_list, a_data_size, a_bank, a_address, + a_verify): + success = True + self.set_memory_bank(a_bank) + self.set_memory_start_address(a_address) + + # For each a_data_item we want to write it to the board to a certain + # memory bank and address + for i in range(0, a_data_size): + # Write each data to memory + self.__bus.write_byte_data(self.__dev_id, C.MPU6050_RA_MEM_R_W, + a_data_list[i]) + + if a_verify: + self.set_memory_bank(a_bank) + self.set_memory_start_address(a_address) + verify_data = self.__bus.read_byte_data(self.__dev_id, + C.MPU6050_RA_MEM_R_W) + if verify_data != a_data_list[i]: + success = False + + # If we've filled the bank, change the memory bank + if a_address == 255: + a_address = 0 + a_bank += 1 + self.set_memory_bank(a_bank) + else: + a_address += 1 + + # Either way update the memory address + self.set_memory_start_address(a_address) + + return success + + def wake_up(self): + self.write_bit( + C.MPU6050_RA_PWR_MGMT_1, C.MPU6050_PWR1_SLEEP_BIT, 0) + + def set_clock_source(self, a_source): + self.write_bits(C.MPU6050_RA_PWR_MGMT_1, C.MPU6050_PWR1_CLKSEL_BIT, + C.MPU6050_PWR1_CLKSEL_LENGTH, a_source) + + def set_full_scale_gyro_range(self, a_data): + self.write_bits(C.MPU6050_RA_GYRO_CONFIG, + C.MPU6050_GCONFIG_FS_SEL_BIT, + C.MPU6050_GCONFIG_FS_SEL_LENGTH, a_data) + + def set_full_scale_accel_range(self, a_data): + self.write_bits(C.MPU6050_RA_ACCEL_CONFIG, + C.MPU6050_ACONFIG_AFS_SEL_BIT, + C.MPU6050_ACONFIG_AFS_SEL_LENGTH, a_data) + + def reset(self): + self.write_bit(C.MPU6050_RA_PWR_MGMT_1, + C.MPU6050_PWR1_DEVICE_RESET_BIT, 1) + + def set_sleep_enabled(self, a_enabled): + set_bit = 0 + if a_enabled: + set_bit = 1 + self.write_bit(C.MPU6050_RA_PWR_MGMT_1, + C.MPU6050_PWR1_SLEEP_BIT, set_bit) + + def set_memory_bank(self, a_bank, a_prefetch_enabled=False, + a_user_bank=False): + a_bank &= 0x1F + if a_user_bank: + a_bank |= 0x20 + if a_prefetch_enabled: + a_bank |= 0x20 + self.__bus.write_byte_data( + self.__dev_id, C.MPU6050_RA_BANK_SEL, a_bank) + + def set_memory_start_address(self, a_address): + self.__bus.write_byte_data( + self.__dev_id, C.MPU6050_RA_MEM_START_ADDR, a_address) + + def get_x_gyro_offset_TC(self): + return self.read_bits(C.MPU6050_RA_XG_OFFS_TC, + C.MPU6050_TC_OFFSET_BIT, + C.MPU6050_TC_OFFSET_LENGTH) + + def set_x_gyro_offset_TC(self, a_offset): + self.write_bits(C.MPU6050_RA_XG_OFFS_TC, + C.MPU6050_TC_OFFSET_BIT, + C.MPU6050_TC_OFFSET_LENGTH, a_offset) + + def get_y_gyro_offset_TC(self): + return self.read_bits(C.MPU6050_RA_YG_OFFS_TC, + C.MPU6050_TC_OFFSET_BIT, + C.MPU6050_TC_OFFSET_LENGTH) + + def set_y_gyro_offset_TC(self, a_offset): + self.write_bits(C.MPU6050_RA_YG_OFFS_TC, + C.MPU6050_TC_OFFSET_BIT, + C.MPU6050_TC_OFFSET_LENGTH, a_offset) + + def get_z_gyro_offset_TC(self): + return self.read_bits(C.MPU6050_RA_ZG_OFFS_TC, + C.MPU6050_TC_OFFSET_BIT, + C.MPU6050_TC_OFFSET_LENGTH) + + def set_z_gyro_offset_TC(self, a_offset): + self.write_bits(C.MPU6050_RA_ZG_OFFS_TC, + C.MPU6050_TC_OFFSET_BIT, + C.MPU6050_TC_OFFSET_LENGTH, a_offset) + + def set_slave_address(self, a_num, a_address): + self.__bus.write_byte_data( + self.__dev_id, C.MPU6050_RA_I2C_SLV0_ADDR + a_num * 3, a_address) + + def set_I2C_master_mode_enabled(self, a_enabled): + bit = 0 + if a_enabled: + bit = 1 + self.write_bit(C.MPU6050_RA_USER_CTRL, + C.MPU6050_USERCTRL_I2C_MST_EN_BIT, bit) + + def reset_I2C_master(self): + self.write_bit(C.MPU6050_RA_USER_CTRL, + C.MPU6050_USERCTRL_I2C_MST_RESET_BIT, 1) + + def write_prog_memory_block(self, a_data_list, a_data_size, a_bank=0, + a_address=0, a_verify=True): + return self.write_memory_block(a_data_list, a_data_size, a_bank, + a_address, a_verify) + + def write_DMP_configuration_set(self, a_data_list, a_data_size): + index = 0 + while index < a_data_size: + bank = a_data_list[index] + offset = a_data_list[index + 1] + length = a_data_list[index + 2] + index += 3 + success = False + + # Normal case + if length > 0: + data_selection = list() + for subindex in range(0, length): + data_selection.append(a_data_list[index + subindex]) + success = self.write_memory_block(data_selection, length, bank, + offset, True) + index += length + # Special undocumented case + else: + special = a_data_list[index] + index += 1 + if special == 0x01: + # TODO Figure out if write8 can return True/False + success = self.__bus.write_byte_data( + self.__dev_id, C.MPU6050_RA_INT_ENABLE, 0x32) + + if success == False: + # TODO implement error messagemajigger + return False + pass + return True + + def write_prog_dmp_configuration(self, a_data_list, a_data_size): + return self.write_DMP_configuration_set(a_data_list, a_data_size) + + def set_int_enable(self, a_enabled): + self.__bus.write_byte_data( + self.__dev_id, C.MPU6050_RA_INT_ENABLE, a_enabled) + + def set_rate(self, a_rate): + self.__bus.write_byte_data( + self.__dev_id, C.MPU6050_RA_SMPLRT_DIV, a_rate) + + def set_external_frame_sync(self, a_sync): + self.write_bits(C.MPU6050_RA_CONFIG, + C.MPU6050_CFG_EXT_SYNC_SET_BIT, + C.MPU6050_CFG_EXT_SYNC_SET_LENGTH, a_sync) + + def set_DLF_mode(self, a_mode): + self.write_bits(C.MPU6050_RA_CONFIG, C.MPU6050_CFG_DLPF_CFG_BIT, + C.MPU6050_CFG_DLPF_CFG_LENGTH, a_mode) + + def get_DMP_config_1(self): + return self.__bus.read_byte_data(self.__dev_id, C.MPU6050_RA_DMP_CFG_1) + + def set_DMP_config_1(self, a_config): + self.__bus.write_byte_data( + self.__dev_id, C.MPU6050_RA_DMP_CFG_1, a_config) + + def get_DMP_config_2(self): + return self.__bus.read_byte_data(self.__dev_id, C.MPU6050_RA_DMP_CFG_2) + + def set_DMP_config_2(self, a_config): + self.__bus.write_byte_data( + self.__dev_id, C.MPU6050_RA_DMP_CFG_2, a_config) + + def set_OTP_bank_valid(self, a_enabled): + bit = 0 + if a_enabled: + bit = 1 + self.write_bit(C.MPU6050_RA_XG_OFFS_TC, + C.MPU6050_TC_OTP_BNK_VLD_BIT, bit) + + def get_OTP_bank_valid(self): + return self.read_bit(C.MPU6050_RA_XG_OFFS_TC, + C.MPU6050_TC_OTP_BNK_VLD_BIT) + + def set_motion_detection_threshold(self, a_threshold): + self.__bus.write_byte_data( + self.__dev_id, C.MPU6050_RA_MOT_THR, a_threshold) + + def set_zero_motion_detection_threshold(self, a_threshold): + self.__bus.write_byte_data( + self.__dev_id, C.MPU6050_RA_ZRMOT_THR, a_threshold) + + def set_motion_detection_duration(self, a_duration): + self.__bus.write_byte_data( + self.__dev_id, C.MPU6050_RA_MOT_DUR, a_duration) + + def set_zero_motion_detection_duration(self, a_duration): + self.__bus.write_byte_data( + self.__dev_id, C.MPU6050_RA_ZRMOT_DUR, a_duration) + + def set_FIFO_enabled(self, a_enabled): + bit = 0 + if a_enabled: + bit = 1 + self.write_bit(C.MPU6050_RA_USER_CTRL, + C.MPU6050_USERCTRL_FIFO_EN_BIT, bit) + + def set_DMP_enabled(self, a_enabled): + bit = 0 + if a_enabled: + bit = 1 + self.write_bit(C.MPU6050_RA_USER_CTRL, + C.MPU6050_USERCTRL_DMP_EN_BIT, bit) + + def reset_DMP(self): + self.write_bit(C.MPU6050_RA_USER_CTRL, + C.MPU6050_USERCTRL_DMP_RESET_BIT, True) + + def dmp_initialize(self): + # Reset the MPU + self.reset() + # time.Sleep a bit while resetting + time.sleep(50 / 1000) + # Disable time.sleep mode + self.set_sleep_enabled(0) + + # get MPU hardware revision + if self.__debug: + print('Selecting user bank 16') + self.set_memory_bank(0x10, True, True) + + if self.__debug: + print('Selecting memory byte 6') + self.set_memory_start_address(0x6) + + if self.__debug: + print('Checking hardware revision') + HW_revision = self.read_memory_byte() + if self.__debug: + print('Revision @ user[16][6] = ' + hex(HW_revision)) + + if self.__debug: + print('Resetting memory bank selection to 0') + self.set_memory_bank(0) + + # check OTP bank valid + # TODO Find out what OTP is + OTP_valid = self.get_OTP_bank_valid() + if self.__debug: + if OTP_valid: + print('OTP bank is valid') + else: + print('OTP bank is invalid') + + # get X/Y/Z gyro offsets + if self.__debug: + print('Reading gyro offet TC values') + x_g_offset_TC = self.get_x_gyro_offset_TC() + y_g_offset_TC = self.get_y_gyro_offset_TC() + z_g_offset_TC = self.get_z_gyro_offset_TC() + if self.__debug: + print("X gyro offset = ", repr(x_g_offset_TC)) + print("Y gyro offset = ", repr(y_g_offset_TC)) + print("Z gyro offset = ", repr(z_g_offset_TC)) + + # setup weird slave stuff (?) + if self.__debug: + print('Setting slave 0 address to 0x7F') + self.set_slave_address(0, 0x7F) + if self.__debug: + print('Disabling I2C Master mode') + self.set_I2C_master_mode_enabled(False) + if self.__debug: + print('Setting slave 0 address to 0x68 (self)') + self.set_slave_address(0, 0x68) + if self.__debug: + print('Resetting I2C Master control') + self.reset_I2C_master() + # Wait a bit for the device to register the changes + time.sleep(20 / 1000) + + # load DMP code into memory banks + if self.__debug: + print('Writing DMP code to MPU memory banks ' + + repr(C.MPU6050_DMP_CODE_SIZE) + ' bytes') + if self.write_prog_memory_block(C.dmpMemory, C.MPU6050_DMP_CODE_SIZE): + # TODO Check if we've actually verified this + if self.__debug: + print('Success! DMP code written and verified') + + # Write DMP configuration + if self.__debug: + print('Writing DMP configuration to MPU memory banks ' + + repr(C.MPU6050_DMP_CONFIG_SIZE) + ' bytes in config') + if self.write_prog_dmp_configuration(C.dmpConfig, + C.MPU6050_DMP_CONFIG_SIZE): + if self.__debug: + print('Success! DMP configuration written and verified.') + print('Setting clock source to Z gyro') + self.set_clock_source(C.MPU6050_CLOCK_PLL_ZGYRO) + + if self.__debug: + print('Setting DMP and FIFO_OFLOW interrupts enabled') + self.set_int_enable(0x12) + + if self.__debug: + print('Setting sample rate to 200Hz') + self.set_rate(4) + + if self.__debug: + print('Setting external frame sync to TEMP_OUT_L[0]') + self.set_external_frame_sync(C.MPU6050_EXT_SYNC_TEMP_OUT_L) + + if self.__debug: + print('Setting DLPF bandwidth to 42Hz') + self.set_DLF_mode(C.MPU6050_DLPF_BW_42) + + if self.__debug: + print('Setting gyro sensitivity to +/- 2000 deg/sec') + self.set_full_scale_gyro_range(C.MPU6050_GYRO_FS_2000) + + if self.__debug: + print('Setting DMP configuration bytes (function unknown)') + self.set_DMP_config_1(0x03) + self.set_DMP_config_2(0x00) + + if self.__debug: + print('Clearing OTP Bank flag') + self.set_OTP_bank_valid(False) + + if self.__debug: + print('Setting X/Y/Z gyro offset TCs to previous values') + self.set_x_gyro_offset_TC(x_g_offset_TC) + self.set_y_gyro_offset_TC(y_g_offset_TC) + self.set_z_gyro_offset_TC(z_g_offset_TC) + + # Uncomment this to zero offsets when dmp_initialize is called + # if self.__debug: + # print('Setting X/Y/Z gyro user offsets to zero') + # self.set_x_gyro_offset(0) + # self.set_y_gyro_offset(0) + # self.set_z_gyro_offset(0) + + if self.__debug: + print('Writing final memory update 1/7 (function unknown)') + pos = 0 + j = 0 + dmp_update = [0] * 16 + while (j < 4) or (j < dmp_update[2] + 3): + dmp_update[j] = C.dmpUpdates[pos] + pos += 1 + j += 1 + # Write as block from pos 3 + self.write_memory_block(dmp_update[3:], dmp_update[2], + dmp_update[0], dmp_update[1], True) + + if self.__debug: + print('Writing final memory update 2/7 (function unknown)') + j = 0 + while (j < 4) or (j < dmp_update[2] + 3): + dmp_update[j] = C.dmpUpdates[pos] + pos += 1 + j += 1 + # Write as block from pos 3 + self.write_memory_block(dmp_update[3:], dmp_update[2], + dmp_update[0], dmp_update[1], True) + + if self.__debug: + print('Resetting FIFO') + self.reset_FIFO() + + if self.__debug: + print('Reading FIFO count') + FIFO_count = self.get_FIFO_count() + + if self.__debug: + print('FIFO count: ' + repr(FIFO_count)) + + if self.__debug: + print('Getting FIFO buffer') + FIFO_buffer = [0] * 128 + FIFO_buffer = self.get_FIFO_bytes(FIFO_count) + + if self.__debug: + print('Setting motion detection threshold to 2') + self.set_motion_detection_threshold(2) + + if self.__debug: + print('Setting zero-motion detection threshold to 156') + self.set_zero_motion_detection_threshold(156) + + if self.__debug: + print('Setting motion detection duration to 80') + self.set_motion_detection_duration(80) + + if self.__debug: + print('Setting zero-motion detection duration to 0') + self.set_zero_motion_detection_duration(0) + + if self.__debug: + print('Resetting FIFO') + self.reset_FIFO() + + if self.__debug: + print('Enabling FIFO') + self.set_FIFO_enabled(True) + + if self.__debug: + print('Enabling DMP') + self.set_DMP_enabled(True) + + if self.__debug: + print('Resetting DMP') + self.reset_DMP() + + if self.__debug: + print('Writing final memory update 3/7 (function unknown)') + j = 0 + while (j < 4) or (j < dmp_update[2] + 3): + dmp_update[j] = C.dmpUpdates[pos] + pos += 1 + j += 1 + # Write as block from pos 3 + self.write_memory_block(dmp_update[3:], dmp_update[2], + dmp_update[0], dmp_update[1], True) + + if self.__debug: + print('Writing final memory update 4/7 (function unknown)') + j = 0 + while (j < 4) or (j < dmp_update[2] + 3): + dmp_update[j] = C.dmpUpdates[pos] + pos += 1 + j += 1 + # Write as block from pos 3 + self.write_memory_block(dmp_update[3:], dmp_update[2], + dmp_update[0], dmp_update[1], True) + + if self.__debug: + print('Writing final memory update 5/7 (function unknown)') + j = 0 + while (j < 4) or (j < dmp_update[2] + 3): + dmp_update[j] = C.dmpUpdates[pos] + pos += 1 + j += 1 + # Write as block from pos 3 + self.write_memory_block(dmp_update[3:], dmp_update[2], + dmp_update[0], dmp_update[1], True) + + if self.__debug: + print('Waiting for FIFO count > 2') + FIFO_count = self.get_FIFO_count() + while FIFO_count < 3: + FIFO_count = self.get_FIFO_count() + + if self.__debug: + print('Current FIFO count = ' + repr(FIFO_count)) + print('Reading FIFO data') + FIFO_buffer = self.get_FIFO_bytes(FIFO_count) + + if self.__debug: + print('Reading interrupt status') + MPU_int_status = self.get_int_status() + + if self.__debug: + print('Current interrupt status = ' + hex(MPU_int_status)) + print('Writing final memory update 6/7 (function unknown)') + j = 0 + while (j < 4) or (j < dmp_update[2] + 3): + dmp_update[j] = C.dmpUpdates[pos] + pos += 1 + j += 1 + # Write as block from pos 3 + self.write_memory_block(dmp_update[3:], dmp_update[2], + dmp_update[0], dmp_update[1], True) + + if self.__debug: + print('Waiting for FIFO count > 2') + FIFO_count = self.get_FIFO_count() + while FIFO_count < 3: + FIFO_count = self.get_FIFO_count() + + if self.__debug: + print('Current FIFO count = ' + repr(FIFO_count)) + print('Reading FIFO count') + FIFO_buffer = self.get_FIFO_bytes(FIFO_count) + + if self.__debug: + print('Reading interrupt status') + MPU_int_status = self.get_int_status() + + if self.__debug: + print('Current interrupt status = ' + hex(MPU_int_status)) + print('Writing final memory update 7/7 (function unknown)') + j = 0 + while (j < 4) or (j < dmp_update[2] + 3): + dmp_update[j] = C.dmpUpdates[pos] + pos += 1 + j += 1 + # Write as block from pos 3 + self.write_memory_block(dmp_update[3:], dmp_update[2], + dmp_update[0], dmp_update[1], True) + + if self.__debug: + print('DMP is good to go! Finally.') + print('Disabling DMP (you turn it on later)') + self.set_DMP_enabled(False) + + if self.__debug: + print('Setting up internal 42 byte DMP packet buffer') + self.__DMP_packet_size = 42 + + if self.__debug: + print( + 'Resetting FIFO and clearing INT status one last time') + self.reset_FIFO() + self.get_int_status() + + else: + if self.__debug: + print('Configuration block loading failed') + return 2 + + else: + if self.__debug: + print('Main binary block loading failed') + return 1 + + if self.__debug: + print('DMP initialization was successful') + return 0 + + # Acceleration and gyro offset setters and getters + def set_x_accel_offset(self, a_offset): + self.__bus.write_byte_data(self.__dev_id, C.MPU6050_RA_XA_OFFS_H, + ctypes.c_int8(a_offset >> 8).value) + self.__bus.write_byte_data(self.__dev_id, C.MPU6050_RA_XA_OFFS_L_TC, + ctypes.c_int8(a_offset).value) + + def set_y_accel_offset(self, a_offset): + self.__bus.write_byte_data(self.__dev_id, C.MPU6050_RA_YA_OFFS_H, + ctypes.c_int8(a_offset >> 8).value) + self.__bus.write_byte_data(self.__dev_id, C.MPU6050_RA_YA_OFFS_L_TC, + ctypes.c_int8(a_offset).value) + + def set_z_accel_offset(self, a_offset): + self.__bus.write_byte_data(self.__dev_id, C.MPU6050_RA_ZA_OFFS_H, + ctypes.c_int8(a_offset >> 8).value) + self.__bus.write_byte_data(self.__dev_id, C.MPU6050_RA_ZA_OFFS_L_TC, + ctypes.c_int8(a_offset).value) + + def set_x_gyro_offset(self, a_offset): + self.__bus.write_byte_data(self.__dev_id, C.MPU6050_RA_XG_OFFS_USRH, + ctypes.c_int8(a_offset >> 8).value) + self.__bus.write_byte_data(self.__dev_id, C.MPU6050_RA_XG_OFFS_USRL, + ctypes.c_int8(a_offset).value) + + def set_y_gyro_offset(self, a_offset): + self.__bus.write_byte_data(self.__dev_id, C.MPU6050_RA_YG_OFFS_USRH, + ctypes.c_int8(a_offset >> 8).value) + self.__bus.write_byte_data(self.__dev_id, C.MPU6050_RA_YG_OFFS_USRL, + ctypes.c_int8(a_offset).value) + + def set_z_gyro_offset(self, a_offset): + self.__bus.write_byte_data(self.__dev_id, C.MPU6050_RA_ZG_OFFS_USRH, + ctypes.c_int8(a_offset >> 8).value) + self.__bus.write_byte_data(self.__dev_id, C.MPU6050_RA_ZG_OFFS_USRL, + ctypes.c_int8(a_offset).value) + + # Main interfacing functions to get raw data from MPU + def get_acceleration(self): + raw_data = self.__bus.read_i2c_block_data(self.__dev_id, + C.MPU6050_RA_ACCEL_XOUT_H, 6) + accel = [0] * 3 + accel[0] = ctypes.c_int16(raw_data[0] << 8 | raw_data[1]).value + accel[1] = ctypes.c_int16(raw_data[2] << 8 | raw_data[3]).value + accel[2] = ctypes.c_int16(raw_data[4] << 8 | raw_data[5]).value + return accel + + def get_rotation(self): + raw_data = self.__bus.read_i2c_block_data(self.__dev_id, + C.MPU6050_RA_GYRO_XOUT_H, 6) + gyro = [0] * 3 + gyro[0] = ctypes.c_int16(raw_data[0] << 8 | raw_data[1]).value + gyro[1] = ctypes.c_int16(raw_data[2] << 8 | raw_data[3]).value + gyro[2] = ctypes.c_int16(raw_data[4] << 8 | raw_data[5]).value + return gyro + + # Interfacing functions to get data from FIFO buffer + def DMP_get_FIFO_packet_size(self): + return self.__DMP_packet_size + + def reset_FIFO(self): + self.write_bit(C.MPU6050_RA_USER_CTRL, + C.MPU6050_USERCTRL_FIFO_RESET_BIT, True) + + def get_FIFO_count(self): + data = [0] * 2 + data = self.read_bytes(data, C.MPU6050_RA_FIFO_COUNTH, 2) + return (data[0] << 8) | data[1] + + def get_FIFO_bytes(self, a_FIFO_count): + return_list = list() + for index in range(0, a_FIFO_count): + return_list.append( + self.__bus.read_byte_data(self.__dev_id, + C.MPU6050_RA_FIFO_R_W)) + return return_list + + def get_int_status(self): + return self.__bus.read_byte_data(self.__dev_id, + C.MPU6050_RA_INT_STATUS) + + # Data retrieval from received FIFO buffer + def DMP_get_quaternion_int16(self, a_FIFO_buffer): + w = ctypes.c_int16((a_FIFO_buffer[0] << 8) | a_FIFO_buffer[1]).value + x = ctypes.c_int16((a_FIFO_buffer[4] << 8) | a_FIFO_buffer[5]).value + y = ctypes.c_int16((a_FIFO_buffer[8] << 8) | a_FIFO_buffer[9]).value + z = ctypes.c_int16((a_FIFO_buffer[12] << 8) | a_FIFO_buffer[13]).value + return Q(w, x, y, z) + + def DMP_get_quaternion(self, a_FIFO_buffer): + quat = self.DMP_get_quaternion_int16(a_FIFO_buffer) + w = quat.w / 16384.0 + x = quat.x / 16384.0 + y = quat.y / 16384.0 + z = quat.z / 16384.0 + return Q(w, x, y, z) + + def DMP_get_acceleration_int16(self, a_FIFO_buffer): + x = ctypes.c_int16(a_FIFO_buffer[28] << 8 | a_FIFO_buffer[29]).value + y = ctypes.c_int16(a_FIFO_buffer[32] << 8 | a_FIFO_buffer[33]).value + z = ctypes.c_int16(a_FIFO_buffer[36] << 8 | a_FIFO_buffer[37]).value + return V(x, y, z) + + def DMP_get_gravity(self, a_quat): + x = 2.0 * (a_quat.x * a_quat.z - a_quat.w * a_quat.y) + y = 2.0 * (a_quat.w * a_quat.x + a_quat.y * a_quat.z) + z = 1.0 * (a_quat.w * a_quat.w - a_quat.x * a_quat.x - + a_quat.y * a_quat.y + a_quat.z * a_quat.z) + return V(x, y, z) + + def DMP_get_linear_accel_int16(self, a_v_raw, a_grav): + x = ctypes.c_int16(a_v_raw.x - (a_grav.x*8192)).value + y = ctypes.c_int16(a_v_raw.y - (a_grav.y*8192)).value + y = ctypes.c_int16(a_v_raw.y - (a_grav.y*8192)).value + return V(x, y, z) + + def DMP_get_euler(self, a_quat): + psi = math.atan2(2*a_quat.x*a_quat.y - 2*a_quat.w*a_quat.z, + 2*a_quat.w*a_quat.w + 2*a_quat.x*a_quat.x - 1) + theta = -asin(2*a_quat.x*a_quat.z + 2*a_quat.w*a_quat.y) + phi = math.atan2(2*a_quat.y*a_quat.z - 2*a_quat.w*a_quat.x, + 2*a_quat.w*a_quat.w + 2*a_quat.z*a_quat.z - 1) + return V(psi, theta, phi) + + def DMP_get_roll_pitch_yaw(self, a_quat, a_grav_vect): + # roll: (tilt left/right, about X axis) + roll = math.atan(a_grav_vect.y / + math.sqrt(a_grav_vect.x*a_grav_vect.x + + a_grav_vect.z*a_grav_vect.z)) + # pitch: (nose up/down, about Y axis) + pitch = math.atan(a_grav_vect.x / + math.sqrt(a_grav_vect.y*a_grav_vect.y + + a_grav_vect.z*a_grav_vect.z)) + # yaw: (about Z axis) + yaw = math.atan2(2*a_quat.x*a_quat.y - 2*a_quat.w*a_quat.z, + 2*a_quat.w*a_quat.w + 2*a_quat.x*a_quat.x - 1) + return V(roll, pitch, yaw) + + def DMP_get_euler_roll_pitch_yaw(self, a_quat, a_grav_vect): + rad_ypr = self.DMP_get_roll_pitch_yaw(a_quat, a_grav_vect) + roll = rad_ypr.x * (180.0/math.pi) + pitch = rad_ypr.y * (180.0/math.pi) + yaw = rad_ypr.z * (180.0/math.pi) + return V(roll, pitch, yaw) + + def DMP_get_linear_accel(self, a_vector_raw, a_vect_grav): + x = a_vector_raw.x - a_vect_grav.x*8192 + y = a_vector_raw.y - a_vect_grav.y*8192 + z = a_vector_raw.z - a_vect_grav.z*8192 + return V(x, y, z) + + +class MPU6050IRQHandler: + __mpu = MPU6050 + __FIFO_buffer = list() + __count = 0 + __packet_size = None + __detected_error = False + __logging = False + __log_file = None + __csv_writer = None + __start_time = None + __debug = None + + # def __init__(self, a_i2c_bus, a_device_address, a_x_accel_offset, + # a_y_accel_offset, a_z_accel_offset, a_x_gyro_offset, + # a_y_gyro_offset, a_z_gyro_offset, a_enable_debug_output): + # self.__mpu = MPU6050(a_i2c_bus, a_device_address, a_x_accel_offset, + # a_y_accel_offset, a_z_accel_offset, + # a_x_gyro_offset, a_y_gyro_offset, a_z_gyro_offset, + # a_enable_debug_output) + def __init__(self, a_mpu, a_logging=False, a_log_file='log.csv', + a_debug=False): + self.__mpu = a_mpu + self.__FIFO_buffer = [0]*64 + self.__mpu.dmp_initialize() + self.__mpu.set_DMP_enabled(True) + self.__packet_size = self.__mpu.DMP_get_FIFO_packet_size() + mpu_int_status = self.__mpu.get_int_status() + if a_logging: + self.__start_time = time.clock() + self.__logging = True + self.__log_file = open(a_log_file, 'ab') + self.__csv_writer = csv.writer(self.__log_file, delimiter=',', + quotechar='|', + quoting=csv.QUOTE_MINIMAL) + self.__debug = a_debug + + def action(self, channel): + if self.__detected_error: + # Clear FIFO and reset MPU + mpu_int_status = self.__mpu.get_int_status() + self.__mpu.reset_FIFO() + self.__detected_error = False + return + + try: + FIFO_count = self.__mpu.get_FIFO_count() + mpu_int_status = self.__mpu.get_int_status() + except: + self.__detected_error = True + return + + # If overflow is detected by status or fifo count we want to reset + if (FIFO_count == 1024) or (mpu_int_status & 0x10): + try: + self.__mpu.reset_FIFO() + except: + self.__detected_error = True + return + + elif (mpu_int_status & 0x02): + # Wait until packet_size number of bytes are ready for reading, + # default is 42 bytes + while FIFO_count < self.__packet_size: + try: + FIFO_count = self.__mpu.get_FIFO_count() + except: + self.__detected_error = True + return + + while FIFO_count > self.__packet_size: + + try: + self.__FIFO_buffer = \ + self.__mpu.get_FIFO_bytes(self.__packet_size) + except: + self.__detected_error = True + return + accel = \ + self.__mpu.DMP_get_acceleration_int16(self.__FIFO_buffer) + quat = self.__mpu.DMP_get_quaternion_int16(self.__FIFO_buffer) + grav = self.__mpu.DMP_get_gravity(quat) + roll_pitch_yaw = self.__mpu.DMP_get_euler_roll_pitch_yaw(quat, + grav) + if self.__logging: + delta_time = time.clock() - self.__start_time + data_concat = ['%.4f' % delta_time] + \ + [accel.x, accel.y, accel.z] + \ + ['%.3f' % roll_pitch_yaw.x, + '%.3f' % roll_pitch_yaw.y, + '%.3f' % roll_pitch_yaw.z] + self.__csv_writer.writerow(data_concat) + + if (self.__debug) and (self.__count % 100 == 0): + print('roll: ' + str(roll_pitch_yaw.x)) + print('pitch: ' + str(roll_pitch_yaw.y)) + print('yaw: ' + str(roll_pitch_yaw.z)) + self.__count += 1 + FIFO_count -= self.__packet_size diff --git a/Code/Python_GPIOZero_Code/25.1.1_MPU6050/MPU6050RAW.py b/Code/Python_GPIOZero_Code/25.1.1_MPU6050/MPU6050RAW.py new file mode 100644 index 0000000..e964f06 --- /dev/null +++ b/Code/Python_GPIOZero_Code/25.1.1_MPU6050/MPU6050RAW.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python3 +######################################################################## +# Filename : MPU6050RAW.py +# Description : Read data of MPU6050. +# auther : www.freenove.com +# modification: 2019/12/28 +######################################################################## +import MPU6050 +import time + +mpu = MPU6050.MPU6050() # instantiate a MPU6050 class object +accel = [0]*3 # define an arry to store accelerometer data +gyro = [0]*3 # define an arry to store gyroscope data +def setup(): + mpu.dmp_initialize() # initialize MPU6050 + +def loop(): + while(True): + accel = mpu.get_acceleration() # get accelerometer data + gyro = mpu.get_rotation() # get gyroscope data + print("a/g:%d\t%d\t%d\t%d\t%d\t%d "%(accel[0],accel[1],accel[2],gyro[0],gyro[1],gyro[2])) + print("a/g:%.2f g\t%.2f g\t%.2f g\t%.2f d/s\t%.2f d/s\t%.2f d/s"%(accel[0]/16384.0,accel[1]/16384.0, + accel[2]/16384.0,gyro[0]/131.0,gyro[1]/131.0,gyro[2]/131.0)) + time.sleep(0.1) + +if __name__ == '__main__': # Program entrance + print("Program is starting ... ") + setup() + try: + loop() + except KeyboardInterrupt: # Press ctrl-c to end the program. + pass + diff --git a/Code/Python_GPIOZero_Code/25.1.1_MPU6050/MPU6050_cal.py b/Code/Python_GPIOZero_Code/25.1.1_MPU6050/MPU6050_cal.py new file mode 100644 index 0000000..e73f0ed --- /dev/null +++ b/Code/Python_GPIOZero_Code/25.1.1_MPU6050/MPU6050_cal.py @@ -0,0 +1,186 @@ +from MPU6050 import MPU6050 +from SimplePID import SimplePID + + +def avg_from_array(a_array): + sum = 0.0 + for index in range(0, len(a_array)): + sum += a_array[index] + + return sum/len(a_array) + + +i2c_bus = 1 +device_address = 0x68 +# The offsets are different for each device and should be changed +# accordingly using a calibration procedure +x_accel_offset = 0 +y_accel_offset = 0 +z_accel_offset =0 +x_gyro_offset = 0 +y_gyro_offset = 0 +z_gyro_offset = 0 +enable_debug_output = True + +mpu = MPU6050(i2c_bus, device_address, x_accel_offset, y_accel_offset, + z_accel_offset, x_gyro_offset, y_gyro_offset, z_gyro_offset, + enable_debug_output) + +kp = 0.03125 +ki = 0.25 +kd = 0 + +pidax = SimplePID(0, -15000, 15000, kp, ki, kd, 100, True) +piday = SimplePID(0, -15000, 15000, kp, ki, kd, 100, True) +pidaz = SimplePID(0, -15000, 15000, kp, ki, kd, 100, True) +pidgx = SimplePID(0, -15000, 15000, kp, ki, kd, 100, True) +pidgy = SimplePID(0, -15000, 15000, kp, ki, kd, 100, True) +pidgz = SimplePID(0, -15000, 15000, kp, ki, kd, 100, True) + +accel_reading = mpu.get_acceleration() + +x_accel_reading = accel_reading[0] +y_accel_reading = accel_reading[1] +z_accel_reading = accel_reading[2] + +x_accel_avg = [0]*100 +y_accel_avg = [0]*100 +z_accel_avg = [0]*100 + +x_accel_offset_avg = [0]*100 +y_accel_offset_avg = [0]*100 +z_accel_offset_avg = [0]*100 + +axindex = 0 +ayindex = 0 +azindex = 0 + +gyro_reading = mpu.get_rotation() + +x_gyro_reading = gyro_reading[0] +y_gyro_reading = gyro_reading[1] +z_gyro_reading = gyro_reading[2] + +x_gyro_avg = [0]*100 +y_gyro_avg = [0]*100 +z_gyro_avg = [0]*100 + +x_gyro_offset_avg = [0]*100 +y_gyro_offset_avg = [0]*100 +z_gyro_offset_avg = [0]*100 + +gxindex = 0 +gyindex = 0 +gzindex = 0 + +try: + while True: + accel_reading = mpu.get_acceleration() + x_accel_reading = accel_reading[0] + y_accel_reading = accel_reading[1] + z_accel_reading = accel_reading[2] + + gyro_reading = mpu.get_rotation() + x_gyro_reading = gyro_reading[0] + y_gyro_reading = gyro_reading[1] + z_gyro_reading = gyro_reading[2] + + if pidax.check_time(): + x_accel_offset = pidax.get_output_value(x_accel_reading) + + mpu.set_x_accel_offset(int(x_accel_offset)) + + x_accel_avg[axindex] = x_accel_reading + x_accel_offset_avg[axindex] = x_accel_offset + + axindex += 1 + if axindex == len(x_accel_avg): + axindex = 0 + print('x_avg_read: ' + + str(avg_from_array(x_accel_avg)) + + ' x_avg_offset: ' + + str(avg_from_array(x_accel_offset_avg))) + print('y_avg_read: ' + + str(avg_from_array(y_accel_avg)) + + ' y_avg_offset: ' + + str(avg_from_array(y_accel_offset_avg))) + print('z_avg_read: ' + + str(avg_from_array(z_accel_avg)) + + ' z_avg_offset: ' + + str(avg_from_array(z_accel_offset_avg))) + + if piday.check_time(): + y_accel_offset = piday.get_output_value(y_accel_reading) + + mpu.set_y_accel_offset(int(y_accel_offset)) + + y_accel_avg[ayindex] = y_accel_reading + y_accel_offset_avg[ayindex] = y_accel_offset + + ayindex += 1 + if ayindex == len(y_accel_avg): + ayindex = 0 + + if pidaz.check_time(): + z_accel_offset = pidaz.get_output_value(z_accel_reading) + + mpu.set_z_accel_offset(int(z_accel_offset)) + + z_accel_avg[azindex] = z_accel_reading + z_accel_offset_avg[azindex] = z_accel_offset + + azindex += 1 + if azindex == len(z_accel_avg): + azindex = 0 + + # Gyro calibration + if pidgx.check_time(): + x_gyro_offset = pidgx.get_output_value(x_gyro_reading) + + mpu.set_x_gyro_offset(int(x_gyro_offset)) + + x_gyro_avg[gxindex] = x_gyro_reading + x_gyro_offset_avg[gxindex] = x_gyro_offset + + gxindex += 1 + if gxindex == len(x_gyro_avg): + gxindex = 0 + print('x_avg_read: ' + + str(avg_from_array(x_gyro_avg)) + + ' x_avg_offset: ' + + str(avg_from_array(x_gyro_offset_avg))) + print('y_avg_read: ' + + str(avg_from_array(y_gyro_avg)) + + ' y_avg_offset: ' + + str(avg_from_array(y_gyro_offset_avg))) + print('z_avg_read: ' + + str(avg_from_array(z_gyro_avg)) + + ' z_avg_offset: ' + + str(avg_from_array(z_gyro_offset_avg))) + + if pidgy.check_time(): + y_gyro_offset = pidgy.get_output_value(y_gyro_reading) + + mpu.set_y_gyro_offset(int(y_gyro_offset)) + + y_gyro_avg[gyindex] = y_gyro_reading + y_gyro_offset_avg[gyindex] = y_gyro_offset + + gyindex += 1 + if gyindex == len(y_gyro_avg): + gyindex = 0 + + if pidgz.check_time(): + z_gyro_offset = pidgz.get_output_value(z_gyro_reading) + + mpu.set_z_gyro_offset(int(z_gyro_offset)) + + z_gyro_avg[gzindex] = z_gyro_reading + z_gyro_offset_avg[gzindex] = z_gyro_offset + + gzindex += 1 + if gzindex == len(z_gyro_avg): + gzindex = 0 + +except KeyboardInterrupt: + pass diff --git a/Code/Python_GPIOZero_Code/25.1.1_MPU6050/MPUConstants.py b/Code/Python_GPIOZero_Code/25.1.1_MPU6050/MPUConstants.py new file mode 100644 index 0000000..0387993 --- /dev/null +++ b/Code/Python_GPIOZero_Code/25.1.1_MPU6050/MPUConstants.py @@ -0,0 +1,755 @@ +__author__ = 'Geir Istad' + +''' +MPU6050 Python I2C Class +Copyright (c) 2015 Geir Istad + +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. + + +Code based on I2Cdev library collection - MPU6050 I2C device class +by Jeff Rowberg +============================================ +I2Cdev device library code is placed under the MIT license +Copyright (c) 2012 Jeff Rowberg +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. +=============================================== +''' + + +class MPUConstants: + # From MPU6050.h + MPU6050_ADDRESS_AD0_LOW = 0x68 # address pin low (GND), default + MPU6050_ADDRESS_AD0_HIGH = 0x69 # address pin high (VCC) + MPU6050_DEFAULT_ADDRESS = MPU6050_ADDRESS_AD0_LOW + + # [7] PWR_MODE, [6:1] XG_OFFS_TC, [0] OTP_BNK_VLD + MPU6050_RA_XG_OFFS_TC = 0x00 + # [7] PWR_MODE, [6:1] YG_OFFS_TC, [0] OTP_BNK_VLD + MPU6050_RA_YG_OFFS_TC = 0x01 + # [7] PWR_MODE, [6:1] ZG_OFFS_TC, [0] OTP_BNK_VLD + MPU6050_RA_ZG_OFFS_TC = 0x02 + # [7:0] X_FINE_GAIN + MPU6050_RA_X_FINE_GAIN = 0x03 + # [7:0] Y_FINE_GAIN + MPU6050_RA_Y_FINE_GAIN = 0x04 + # [7:0] Z_FINE_GAIN + MPU6050_RA_Z_FINE_GAIN = 0x05 + # [15:0] XA_OFFS + MPU6050_RA_XA_OFFS_H = 0x06 + MPU6050_RA_XA_OFFS_L_TC = 0x07 + # [15:0] YA_OFFS + MPU6050_RA_YA_OFFS_H = 0x08 + MPU6050_RA_YA_OFFS_L_TC = 0x09 + # [15:0] ZA_OFFS + MPU6050_RA_ZA_OFFS_H = 0x0A + MPU6050_RA_ZA_OFFS_L_TC = 0x0B + # [15:0] XG_OFFS_USR + MPU6050_RA_XG_OFFS_USRH = 0x13 + MPU6050_RA_XG_OFFS_USRL = 0x14 + # [15:0] YG_OFFS_USR + MPU6050_RA_YG_OFFS_USRH = 0x15 + MPU6050_RA_YG_OFFS_USRL = 0x16 + # [15:0] ZG_OFFS_USR + MPU6050_RA_ZG_OFFS_USRH = 0x17 + MPU6050_RA_ZG_OFFS_USRL = 0x18 + MPU6050_RA_SMPLRT_DIV = 0x19 + MPU6050_RA_CONFIG = 0x1A + MPU6050_RA_GYRO_CONFIG = 0x1B + MPU6050_RA_ACCEL_CONFIG = 0x1C + MPU6050_RA_FF_THR = 0x1D + MPU6050_RA_FF_DUR = 0x1E + MPU6050_RA_MOT_THR = 0x1F + MPU6050_RA_MOT_DUR = 0x20 + MPU6050_RA_ZRMOT_THR = 0x21 + MPU6050_RA_ZRMOT_DUR = 0x22 + MPU6050_RA_FIFO_EN = 0x23 + MPU6050_RA_I2C_MST_CTRL = 0x24 + MPU6050_RA_I2C_SLV0_ADDR = 0x25 + MPU6050_RA_I2C_SLV0_REG = 0x26 + MPU6050_RA_I2C_SLV0_CTRL = 0x27 + MPU6050_RA_I2C_SLV1_ADDR = 0x28 + MPU6050_RA_I2C_SLV1_REG = 0x29 + MPU6050_RA_I2C_SLV1_CTRL = 0x2A + MPU6050_RA_I2C_SLV2_ADDR = 0x2B + MPU6050_RA_I2C_SLV2_REG = 0x2C + MPU6050_RA_I2C_SLV2_CTRL = 0x2D + MPU6050_RA_I2C_SLV3_ADDR = 0x2E + MPU6050_RA_I2C_SLV3_REG = 0x2F + MPU6050_RA_I2C_SLV3_CTRL = 0x30 + MPU6050_RA_I2C_SLV4_ADDR = 0x31 + MPU6050_RA_I2C_SLV4_REG = 0x32 + MPU6050_RA_I2C_SLV4_DO = 0x33 + MPU6050_RA_I2C_SLV4_CTRL = 0x34 + MPU6050_RA_I2C_SLV4_DI = 0x35 + MPU6050_RA_I2C_MST_STATUS = 0x36 + MPU6050_RA_INT_PIN_CFG = 0x37 + MPU6050_RA_INT_ENABLE = 0x38 + MPU6050_RA_DMP_INT_STATUS = 0x39 + MPU6050_RA_INT_STATUS = 0x3A + MPU6050_RA_ACCEL_XOUT_H = 0x3B + MPU6050_RA_ACCEL_XOUT_L = 0x3C + MPU6050_RA_ACCEL_YOUT_H = 0x3D + MPU6050_RA_ACCEL_YOUT_L = 0x3E + MPU6050_RA_ACCEL_ZOUT_H = 0x3F + MPU6050_RA_ACCEL_ZOUT_L = 0x40 + MPU6050_RA_TEMP_OUT_H = 0x41 + MPU6050_RA_TEMP_OUT_L = 0x42 + MPU6050_RA_GYRO_XOUT_H = 0x43 + MPU6050_RA_GYRO_XOUT_L = 0x44 + MPU6050_RA_GYRO_YOUT_H = 0x45 + MPU6050_RA_GYRO_YOUT_L = 0x46 + MPU6050_RA_GYRO_ZOUT_H = 0x47 + MPU6050_RA_GYRO_ZOUT_L = 0x48 + MPU6050_RA_EXT_SENS_DATA_00 = 0x49 + MPU6050_RA_EXT_SENS_DATA_01 = 0x4A + MPU6050_RA_EXT_SENS_DATA_02 = 0x4B + MPU6050_RA_EXT_SENS_DATA_03 = 0x4C + MPU6050_RA_EXT_SENS_DATA_04 = 0x4D + MPU6050_RA_EXT_SENS_DATA_05 = 0x4E + MPU6050_RA_EXT_SENS_DATA_06 = 0x4F + MPU6050_RA_EXT_SENS_DATA_07 = 0x50 + MPU6050_RA_EXT_SENS_DATA_08 = 0x51 + MPU6050_RA_EXT_SENS_DATA_09 = 0x52 + MPU6050_RA_EXT_SENS_DATA_10 = 0x53 + MPU6050_RA_EXT_SENS_DATA_11 = 0x54 + MPU6050_RA_EXT_SENS_DATA_12 = 0x55 + MPU6050_RA_EXT_SENS_DATA_13 = 0x56 + MPU6050_RA_EXT_SENS_DATA_14 = 0x57 + MPU6050_RA_EXT_SENS_DATA_15 = 0x58 + MPU6050_RA_EXT_SENS_DATA_16 = 0x59 + MPU6050_RA_EXT_SENS_DATA_17 = 0x5A + MPU6050_RA_EXT_SENS_DATA_18 = 0x5B + MPU6050_RA_EXT_SENS_DATA_19 = 0x5C + MPU6050_RA_EXT_SENS_DATA_20 = 0x5D + MPU6050_RA_EXT_SENS_DATA_21 = 0x5E + MPU6050_RA_EXT_SENS_DATA_22 = 0x5F + MPU6050_RA_EXT_SENS_DATA_23 = 0x60 + MPU6050_RA_MOT_DETECT_STATUS = 0x61 + MPU6050_RA_I2C_SLV0_DO = 0x63 + MPU6050_RA_I2C_SLV1_DO = 0x64 + MPU6050_RA_I2C_SLV2_DO = 0x65 + MPU6050_RA_I2C_SLV3_DO = 0x66 + MPU6050_RA_I2C_MST_DELAY_CTRL = 0x67 + MPU6050_RA_SIGNAL_PATH_RESET = 0x68 + MPU6050_RA_MOT_DETECT_CTRL = 0x69 + MPU6050_RA_USER_CTRL = 0x6A + MPU6050_RA_PWR_MGMT_1 = 0x6B + MPU6050_RA_PWR_MGMT_2 = 0x6C + MPU6050_RA_BANK_SEL = 0x6D + MPU6050_RA_MEM_START_ADDR = 0x6E + MPU6050_RA_MEM_R_W = 0x6F + MPU6050_RA_DMP_CFG_1 = 0x70 + MPU6050_RA_DMP_CFG_2 = 0x71 + MPU6050_RA_FIFO_COUNTH = 0x72 + MPU6050_RA_FIFO_COUNTL = 0x73 + MPU6050_RA_FIFO_R_W = 0x74 + MPU6050_RA_WHO_AM_I = 0x75 + + MPU6050_TC_PWR_MODE_BIT = 7 + MPU6050_TC_OFFSET_BIT = 6 + MPU6050_TC_OFFSET_LENGTH = 6 + MPU6050_TC_OTP_BNK_VLD_BIT = 0 + + MPU6050_VDDIO_LEVEL_VLOGIC = 0 + MPU6050_VDDIO_LEVEL_VDD = 1 + + MPU6050_CFG_EXT_SYNC_SET_BIT = 5 + MPU6050_CFG_EXT_SYNC_SET_LENGTH = 3 + MPU6050_CFG_DLPF_CFG_BIT = 2 + MPU6050_CFG_DLPF_CFG_LENGTH = 3 + + MPU6050_EXT_SYNC_DISABLED = 0x0 + MPU6050_EXT_SYNC_TEMP_OUT_L = 0x1 + MPU6050_EXT_SYNC_GYRO_XOUT_L = 0x2 + MPU6050_EXT_SYNC_GYRO_YOUT_L = 0x3 + MPU6050_EXT_SYNC_GYRO_ZOUT_L = 0x4 + MPU6050_EXT_SYNC_ACCEL_XOUT_L = 0x5 + MPU6050_EXT_SYNC_ACCEL_YOUT_L = 0x6 + MPU6050_EXT_SYNC_ACCEL_ZOUT_L = 0x7 + + MPU6050_DLPF_BW_256 = 0x00 + MPU6050_DLPF_BW_188 = 0x01 + MPU6050_DLPF_BW_98 = 0x02 + MPU6050_DLPF_BW_42 = 0x03 + MPU6050_DLPF_BW_20 = 0x04 + MPU6050_DLPF_BW_10 = 0x05 + MPU6050_DLPF_BW_5 = 0x06 + + MPU6050_GCONFIG_FS_SEL_BIT = 4 + MPU6050_GCONFIG_FS_SEL_LENGTH = 2 + + MPU6050_GYRO_FS_250 = 0x00 + MPU6050_GYRO_FS_500 = 0x01 + MPU6050_GYRO_FS_1000 = 0x02 + MPU6050_GYRO_FS_2000 = 0x03 + + MPU6050_ACONFIG_XA_ST_BIT = 7 + MPU6050_ACONFIG_YA_ST_BIT = 6 + MPU6050_ACONFIG_ZA_ST_BIT = 5 + MPU6050_ACONFIG_AFS_SEL_BIT = 4 + MPU6050_ACONFIG_AFS_SEL_LENGTH = 2 + MPU6050_ACONFIG_ACCEL_HPF_BIT = 2 + MPU6050_ACONFIG_ACCEL_HPF_LENGTH = 3 + + MPU6050_ACCEL_FS_2 = 0x00 + MPU6050_ACCEL_FS_4 = 0x01 + MPU6050_ACCEL_FS_8 = 0x02 + MPU6050_ACCEL_FS_16 = 0x03 + + MPU6050_DHPF_RESET = 0x00 + MPU6050_DHPF_5 = 0x01 + MPU6050_DHPF_2P5 = 0x02 + MPU6050_DHPF_1P25 = 0x03 + MPU6050_DHPF_0P63 = 0x04 + MPU6050_DHPF_HOLD = 0x07 + + MPU6050_TEMP_FIFO_EN_BIT = 7 + MPU6050_XG_FIFO_EN_BIT = 6 + MPU6050_YG_FIFO_EN_BIT = 5 + MPU6050_ZG_FIFO_EN_BIT = 4 + MPU6050_ACCEL_FIFO_EN_BIT = 3 + MPU6050_SLV2_FIFO_EN_BIT = 2 + MPU6050_SLV1_FIFO_EN_BIT = 1 + MPU6050_SLV0_FIFO_EN_BIT = 0 + + MPU6050_MULT_MST_EN_BIT = 7 + MPU6050_WAIT_FOR_ES_BIT = 6 + MPU6050_SLV_3_FIFO_EN_BIT = 5 + MPU6050_I2C_MST_P_NSR_BIT = 4 + MPU6050_I2C_MST_CLK_BIT = 3 + MPU6050_I2C_MST_CLK_LENGTH = 4 + + MPU6050_CLOCK_DIV_348 = 0x0 + MPU6050_CLOCK_DIV_333 = 0x1 + MPU6050_CLOCK_DIV_320 = 0x2 + MPU6050_CLOCK_DIV_308 = 0x3 + MPU6050_CLOCK_DIV_296 = 0x4 + MPU6050_CLOCK_DIV_286 = 0x5 + MPU6050_CLOCK_DIV_276 = 0x6 + MPU6050_CLOCK_DIV_267 = 0x7 + MPU6050_CLOCK_DIV_258 = 0x8 + MPU6050_CLOCK_DIV_500 = 0x9 + MPU6050_CLOCK_DIV_471 = 0xA + MPU6050_CLOCK_DIV_444 = 0xB + MPU6050_CLOCK_DIV_421 = 0xC + MPU6050_CLOCK_DIV_400 = 0xD + MPU6050_CLOCK_DIV_381 = 0xE + MPU6050_CLOCK_DIV_364 = 0xF + + MPU6050_I2C_SLV_RW_BIT = 7 + MPU6050_I2C_SLV_ADDR_BIT = 6 + MPU6050_I2C_SLV_ADDR_LENGTH = 7 + MPU6050_I2C_SLV_EN_BIT = 7 + MPU6050_I2C_SLV_BYTE_SW_BIT = 6 + MPU6050_I2C_SLV_REG_DIS_BIT = 5 + MPU6050_I2C_SLV_GRP_BIT = 4 + MPU6050_I2C_SLV_LEN_BIT = 3 + MPU6050_I2C_SLV_LEN_LENGTH = 4 + + MPU6050_I2C_SLV4_RW_BIT = 7 + MPU6050_I2C_SLV4_ADDR_BIT = 6 + MPU6050_I2C_SLV4_ADDR_LENGTH = 7 + MPU6050_I2C_SLV4_EN_BIT = 7 + MPU6050_I2C_SLV4_INT_EN_BIT = 6 + MPU6050_I2C_SLV4_REG_DIS_BIT = 5 + MPU6050_I2C_SLV4_MST_DLY_BIT = 4 + MPU6050_I2C_SLV4_MST_DLY_LENGTH = 5 + + MPU6050_MST_PASS_THROUGH_BIT = 7 + MPU6050_MST_I2C_SLV4_DONE_BIT = 6 + MPU6050_MST_I2C_LOST_ARB_BIT = 5 + MPU6050_MST_I2C_SLV4_NACK_BIT = 4 + MPU6050_MST_I2C_SLV3_NACK_BIT = 3 + MPU6050_MST_I2C_SLV2_NACK_BIT = 2 + MPU6050_MST_I2C_SLV1_NACK_BIT = 1 + MPU6050_MST_I2C_SLV0_NACK_BIT = 0 + + MPU6050_INTCFG_INT_LEVEL_BIT = 7 + MPU6050_INTCFG_INT_OPEN_BIT = 6 + MPU6050_INTCFG_LATCH_INT_EN_BIT = 5 + MPU6050_INTCFG_INT_RD_CLEAR_BIT = 4 + MPU6050_INTCFG_FSYNC_INT_LEVEL_BIT = 3 + MPU6050_INTCFG_FSYNC_INT_EN_BIT = 2 + MPU6050_INTCFG_I2C_BYPASS_EN_BIT = 1 + MPU6050_INTCFG_CLKOUT_EN_BIT = 0 + + MPU6050_INTMODE_ACTIVEHIGH = 0x00 + MPU6050_INTMODE_ACTIVELOW = 0x01 + + MPU6050_INTDRV_PUSHPULL = 0x00 + MPU6050_INTDRV_OPENDRAIN = 0x01 + + MPU6050_INTLATCH_50USPULSE = 0x00 + MPU6050_INTLATCH_WAITCLEAR = 0x01 + + MPU6050_INTCLEAR_STATUSREAD = 0x00 + MPU6050_INTCLEAR_ANYREAD = 0x01 + + MPU6050_INTERRUPT_FF_BIT = 7 + MPU6050_INTERRUPT_MOT_BIT = 6 + MPU6050_INTERRUPT_ZMOT_BIT = 5 + MPU6050_INTERRUPT_FIFO_OFLOW_BIT = 4 + MPU6050_INTERRUPT_I2C_MST_INT_BIT = 3 + MPU6050_INTERRUPT_PLL_RDY_INT_BIT = 2 + MPU6050_INTERRUPT_DMP_INT_BIT = 1 + MPU6050_INTERRUPT_DATA_RDY_BIT = 0 + + # TODO: figure out what these actually do + # UMPL source code is not very obivous + MPU6050_DMPINT_5_BIT = 5 + MPU6050_DMPINT_4_BIT = 4 + MPU6050_DMPINT_3_BIT = 3 + MPU6050_DMPINT_2_BIT = 2 + MPU6050_DMPINT_1_BIT = 1 + MPU6050_DMPINT_0_BIT = 0 + + MPU6050_MOTION_MOT_XNEG_BIT = 7 + MPU6050_MOTION_MOT_XPOS_BIT = 6 + MPU6050_MOTION_MOT_YNEG_BIT = 5 + MPU6050_MOTION_MOT_YPOS_BIT = 4 + MPU6050_MOTION_MOT_ZNEG_BIT = 3 + MPU6050_MOTION_MOT_ZPOS_BIT = 2 + MPU6050_MOTION_MOT_ZRMOT_BIT = 0 + + MPU6050_DELAYCTRL_DELAY_ES_SHADOW_BIT = 7 + MPU6050_DELAYCTRL_I2C_SLV4_DLY_EN_BIT = 4 + MPU6050_DELAYCTRL_I2C_SLV3_DLY_EN_BIT = 3 + MPU6050_DELAYCTRL_I2C_SLV2_DLY_EN_BIT = 2 + MPU6050_DELAYCTRL_I2C_SLV1_DLY_EN_BIT = 1 + MPU6050_DELAYCTRL_I2C_SLV0_DLY_EN_BIT = 0 + + MPU6050_PATHRESET_GYRO_RESET_BIT = 2 + MPU6050_PATHRESET_ACCEL_RESET_BIT = 1 + MPU6050_PATHRESET_TEMP_RESET_BIT = 0 + + MPU6050_DETECT_ACCEL_ON_DELAY_BIT = 5 + MPU6050_DETECT_ACCEL_ON_DELAY_LENGTH = 2 + MPU6050_DETECT_FF_COUNT_BIT = 3 + MPU6050_DETECT_FF_COUNT_LENGTH = 2 + MPU6050_DETECT_MOT_COUNT_BIT = 1 + MPU6050_DETECT_MOT_COUNT_LENGTH = 2 + + MPU6050_DETECT_DECREMENT_RESET = 0x0 + MPU6050_DETECT_DECREMENT_1 = 0x1 + MPU6050_DETECT_DECREMENT_2 = 0x2 + MPU6050_DETECT_DECREMENT_4 = 0x3 + + MPU6050_USERCTRL_DMP_EN_BIT = 7 + MPU6050_USERCTRL_FIFO_EN_BIT = 6 + MPU6050_USERCTRL_I2C_MST_EN_BIT = 5 + MPU6050_USERCTRL_I2C_IF_DIS_BIT = 4 + MPU6050_USERCTRL_DMP_RESET_BIT = 3 + MPU6050_USERCTRL_FIFO_RESET_BIT = 2 + MPU6050_USERCTRL_I2C_MST_RESET_BIT = 1 + MPU6050_USERCTRL_SIG_COND_RESET_BIT = 0 + + MPU6050_PWR1_DEVICE_RESET_BIT = 7 + MPU6050_PWR1_SLEEP_BIT = 6 + MPU6050_PWR1_CYCLE_BIT = 5 + MPU6050_PWR1_TEMP_DIS_BIT = 3 + MPU6050_PWR1_CLKSEL_BIT = 2 + MPU6050_PWR1_CLKSEL_LENGTH = 3 + + MPU6050_CLOCK_INTERNAL = 0x00 + MPU6050_CLOCK_PLL_XGYRO = 0x01 + MPU6050_CLOCK_PLL_YGYRO = 0x02 + MPU6050_CLOCK_PLL_ZGYRO = 0x03 + MPU6050_CLOCK_PLL_EXT32K = 0x04 + MPU6050_CLOCK_PLL_EXT19M = 0x05 + MPU6050_CLOCK_KEEP_RESET = 0x07 + + MPU6050_PWR2_LP_WAKE_CTRL_BIT = 7 + MPU6050_PWR2_LP_WAKE_CTRL_LENGTH = 2 + MPU6050_PWR2_STBY_XA_BIT = 5 + MPU6050_PWR2_STBY_YA_BIT = 4 + MPU6050_PWR2_STBY_ZA_BIT = 3 + MPU6050_PWR2_STBY_XG_BIT = 2 + MPU6050_PWR2_STBY_YG_BIT = 1 + MPU6050_PWR2_STBY_ZG_BIT = 0 + + MPU6050_WAKE_FREQ_1P25 = 0x0 + MPU6050_WAKE_FREQ_2P5 = 0x1 + MPU6050_WAKE_FREQ_5 = 0x2 + MPU6050_WAKE_FREQ_10 = 0x3 + + MPU6050_BANKSEL_PRFTCH_EN_BIT = 6 + MPU6050_BANKSEL_CFG_USER_BANK_BIT = 5 + MPU6050_BANKSEL_MEM_SEL_BIT = 4 + MPU6050_BANKSEL_MEM_SEL_LENGTH = 5 + + MPU6050_WHO_AM_I_BIT = 6 + MPU6050_WHO_AM_I_LENGTH = 6 + + MPU6050_DMP_MEMORY_BANKS = 8 + MPU6050_DMP_MEMORY_BANK_SIZE = 256 + MPU6050_DMP_MEMORY_CHUNK_SIZE = 16 + + # From MPU6050_6Axis_MotionApps20.h + MPU6050_DMP_CODE_SIZE = 1929 # dmpMemory[] + MPU6050_DMP_CONFIG_SIZE = 192 # dmpConfig[] + MPU6050_DMP_UPDATES_SIZE = 47 # dmpUpdates[] + ''' + * ================================================================================================ * + | Default MotionApps v2.0 42-byte FIFO packet structure: | + | | + | [QUAT W][ ][QUAT X][ ][QUAT Y][ ][QUAT Z][ ][GYRO X][ ][GYRO Y][ ] | + | 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | + | | + | [GYRO Z][ ][ACC X ][ ][ACC Y ][ ][ACC Z ][ ][ ] | + | 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | + * ================================================================================================ * + ''' + # dmpMemory has size MPU6050_DMP_CODE_SIZE = 1929 + dmpMemory = [ + # bank 0, 256 bytes + 0xFB, 0x00, 0x00, 0x3E, 0x00, 0x0B, 0x00, 0x36, 0x00, 0x01, 0x00, 0x02, + 0x00, 0x03, 0x00, 0x00, + 0x00, 0x65, 0x00, 0x54, 0xFF, 0xEF, 0x00, 0x00, 0xFA, 0x80, 0x00, 0x0B, + 0x12, 0x82, 0x00, 0x01, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x28, 0x00, 0x00, 0xFF, 0xFF, 0x45, 0x81, 0xFF, 0xFF, 0xFA, 0x72, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0xE8, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x7F, 0xFF, + 0xFF, 0xFE, 0x80, 0x01, + 0x00, 0x1B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3E, 0x03, 0x30, 0x40, 0x00, 0x00, 0x00, 0x02, 0xCA, 0xE3, 0x09, + 0x3E, 0x80, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, + 0x60, 0x00, 0x00, 0x00, + 0x41, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x0B, 0x2A, 0x00, 0x00, 0x16, 0x55, + 0x00, 0x00, 0x21, 0x82, + 0xFD, 0x87, 0x26, 0x50, 0xFD, 0x80, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, + 0x00, 0x05, 0x80, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x03, 0x00, 0x00, + 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x6F, 0x00, 0x02, 0x65, 0x32, + 0x00, 0x00, 0x5E, 0xC0, + 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0xFB, 0x8C, 0x6F, 0x5D, 0xFD, 0x5D, 0x08, 0xD9, 0x00, 0x7C, 0x73, 0x3B, + 0x00, 0x6C, 0x12, 0xCC, + 0x32, 0x00, 0x13, 0x9D, 0x32, 0x00, 0xD0, 0xD6, 0x32, 0x00, 0x08, 0x00, + 0x40, 0x00, 0x01, 0xF4, + 0xFF, 0xE6, 0x80, 0x79, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD0, 0xD6, + 0x00, 0x00, 0x27, 0x10, + + # bank 1, 256 bytes + 0xFB, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFA, 0x36, 0xFF, 0xBC, 0x30, 0x8E, 0x00, 0x05, 0xFB, 0xF0, + 0xFF, 0xD9, 0x5B, 0xC8, + 0xFF, 0xD0, 0x9A, 0xBE, 0x00, 0x00, 0x10, 0xA9, 0xFF, 0xF4, 0x1E, 0xB2, + 0x00, 0xCE, 0xBB, 0xF7, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x02, + 0x02, 0x00, 0x00, 0x0C, + 0xFF, 0xC2, 0x80, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0xCF, 0x80, 0x00, + 0x40, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, + 0x00, 0x00, 0x00, 0x14, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x3F, 0x68, 0xB6, 0x79, 0x35, 0x28, 0xBC, + 0xC6, 0x7E, 0xD1, 0x6C, + 0x80, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB2, 0x6A, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xF0, + 0x00, 0x00, 0x00, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x25, 0x4D, 0x00, 0x2F, 0x70, 0x6D, 0x00, 0x00, 0x05, 0xAE, + 0x00, 0x0C, 0x02, 0xD0, + + # bank 2, 256 bytes + 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x00, 0x54, 0xFF, 0xEF, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, + 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, + 0xFF, 0xEF, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x40, 0x00, 0x00, 0x00, + 0x00, 0x1B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + # bank 3, 256 bytes + 0xD8, 0xDC, 0xBA, 0xA2, 0xF1, 0xDE, 0xB2, 0xB8, 0xB4, 0xA8, 0x81, 0x91, + 0xF7, 0x4A, 0x90, 0x7F, + 0x91, 0x6A, 0xF3, 0xF9, 0xDB, 0xA8, 0xF9, 0xB0, 0xBA, 0xA0, 0x80, 0xF2, + 0xCE, 0x81, 0xF3, 0xC2, + 0xF1, 0xC1, 0xF2, 0xC3, 0xF3, 0xCC, 0xA2, 0xB2, 0x80, 0xF1, 0xC6, 0xD8, + 0x80, 0xBA, 0xA7, 0xDF, + 0xDF, 0xDF, 0xF2, 0xA7, 0xC3, 0xCB, 0xC5, 0xB6, 0xF0, 0x87, 0xA2, 0x94, + 0x24, 0x48, 0x70, 0x3C, + 0x95, 0x40, 0x68, 0x34, 0x58, 0x9B, 0x78, 0xA2, 0xF1, 0x83, 0x92, 0x2D, + 0x55, 0x7D, 0xD8, 0xB1, + 0xB4, 0xB8, 0xA1, 0xD0, 0x91, 0x80, 0xF2, 0x70, 0xF3, 0x70, 0xF2, 0x7C, + 0x80, 0xA8, 0xF1, 0x01, + 0xB0, 0x98, 0x87, 0xD9, 0x43, 0xD8, 0x86, 0xC9, 0x88, 0xBA, 0xA1, 0xF2, + 0x0E, 0xB8, 0x97, 0x80, + 0xF1, 0xA9, 0xDF, 0xDF, 0xDF, 0xAA, 0xDF, 0xDF, 0xDF, 0xF2, 0xAA, 0xC5, + 0xCD, 0xC7, 0xA9, 0x0C, + 0xC9, 0x2C, 0x97, 0x97, 0x97, 0x97, 0xF1, 0xA9, 0x89, 0x26, 0x46, 0x66, + 0xB0, 0xB4, 0xBA, 0x80, + 0xAC, 0xDE, 0xF2, 0xCA, 0xF1, 0xB2, 0x8C, 0x02, 0xA9, 0xB6, 0x98, 0x00, + 0x89, 0x0E, 0x16, 0x1E, + 0xB8, 0xA9, 0xB4, 0x99, 0x2C, 0x54, 0x7C, 0xB0, 0x8A, 0xA8, 0x96, 0x36, + 0x56, 0x76, 0xF1, 0xB9, + 0xAF, 0xB4, 0xB0, 0x83, 0xC0, 0xB8, 0xA8, 0x97, 0x11, 0xB1, 0x8F, 0x98, + 0xB9, 0xAF, 0xF0, 0x24, + 0x08, 0x44, 0x10, 0x64, 0x18, 0xF1, 0xA3, 0x29, 0x55, 0x7D, 0xAF, 0x83, + 0xB5, 0x93, 0xAF, 0xF0, + 0x00, 0x28, 0x50, 0xF1, 0xA3, 0x86, 0x9F, 0x61, 0xA6, 0xDA, 0xDE, 0xDF, + 0xD9, 0xFA, 0xA3, 0x86, + 0x96, 0xDB, 0x31, 0xA6, 0xD9, 0xF8, 0xDF, 0xBA, 0xA6, 0x8F, 0xC2, 0xC5, + 0xC7, 0xB2, 0x8C, 0xC1, + 0xB8, 0xA2, 0xDF, 0xDF, 0xDF, 0xA3, 0xDF, 0xDF, 0xDF, 0xD8, 0xD8, 0xF1, + 0xB8, 0xA8, 0xB2, 0x86, + + # bank 4, 256 bytes + 0xB4, 0x98, 0x0D, 0x35, 0x5D, 0xB8, 0xAA, 0x98, 0xB0, 0x87, 0x2D, 0x35, + 0x3D, 0xB2, 0xB6, 0xBA, + 0xAF, 0x8C, 0x96, 0x19, 0x8F, 0x9F, 0xA7, 0x0E, 0x16, 0x1E, 0xB4, 0x9A, + 0xB8, 0xAA, 0x87, 0x2C, + 0x54, 0x7C, 0xB9, 0xA3, 0xDE, 0xDF, 0xDF, 0xA3, 0xB1, 0x80, 0xF2, 0xC4, + 0xCD, 0xC9, 0xF1, 0xB8, + 0xA9, 0xB4, 0x99, 0x83, 0x0D, 0x35, 0x5D, 0x89, 0xB9, 0xA3, 0x2D, 0x55, + 0x7D, 0xB5, 0x93, 0xA3, + 0x0E, 0x16, 0x1E, 0xA9, 0x2C, 0x54, 0x7C, 0xB8, 0xB4, 0xB0, 0xF1, 0x97, + 0x83, 0xA8, 0x11, 0x84, + 0xA5, 0x09, 0x98, 0xA3, 0x83, 0xF0, 0xDA, 0x24, 0x08, 0x44, 0x10, 0x64, + 0x18, 0xD8, 0xF1, 0xA5, + 0x29, 0x55, 0x7D, 0xA5, 0x85, 0x95, 0x02, 0x1A, 0x2E, 0x3A, 0x56, 0x5A, + 0x40, 0x48, 0xF9, 0xF3, + 0xA3, 0xD9, 0xF8, 0xF0, 0x98, 0x83, 0x24, 0x08, 0x44, 0x10, 0x64, 0x18, + 0x97, 0x82, 0xA8, 0xF1, + 0x11, 0xF0, 0x98, 0xA2, 0x24, 0x08, 0x44, 0x10, 0x64, 0x18, 0xDA, 0xF3, + 0xDE, 0xD8, 0x83, 0xA5, + 0x94, 0x01, 0xD9, 0xA3, 0x02, 0xF1, 0xA2, 0xC3, 0xC5, 0xC7, 0xD8, 0xF1, + 0x84, 0x92, 0xA2, 0x4D, + 0xDA, 0x2A, 0xD8, 0x48, 0x69, 0xD9, 0x2A, 0xD8, 0x68, 0x55, 0xDA, 0x32, + 0xD8, 0x50, 0x71, 0xD9, + 0x32, 0xD8, 0x70, 0x5D, 0xDA, 0x3A, 0xD8, 0x58, 0x79, 0xD9, 0x3A, 0xD8, + 0x78, 0x93, 0xA3, 0x4D, + 0xDA, 0x2A, 0xD8, 0x48, 0x69, 0xD9, 0x2A, 0xD8, 0x68, 0x55, 0xDA, 0x32, + 0xD8, 0x50, 0x71, 0xD9, + 0x32, 0xD8, 0x70, 0x5D, 0xDA, 0x3A, 0xD8, 0x58, 0x79, 0xD9, 0x3A, 0xD8, + 0x78, 0xA8, 0x8A, 0x9A, + 0xF0, 0x28, 0x50, 0x78, 0x9E, 0xF3, 0x88, 0x18, 0xF1, 0x9F, 0x1D, 0x98, + 0xA8, 0xD9, 0x08, 0xD8, + 0xC8, 0x9F, 0x12, 0x9E, 0xF3, 0x15, 0xA8, 0xDA, 0x12, 0x10, 0xD8, 0xF1, + 0xAF, 0xC8, 0x97, 0x87, + + # bank 5, 256 bytes + 0x34, 0xB5, 0xB9, 0x94, 0xA4, 0x21, 0xF3, 0xD9, 0x22, 0xD8, 0xF2, 0x2D, + 0xF3, 0xD9, 0x2A, 0xD8, + 0xF2, 0x35, 0xF3, 0xD9, 0x32, 0xD8, 0x81, 0xA4, 0x60, 0x60, 0x61, 0xD9, + 0x61, 0xD8, 0x6C, 0x68, + 0x69, 0xD9, 0x69, 0xD8, 0x74, 0x70, 0x71, 0xD9, 0x71, 0xD8, 0xB1, 0xA3, + 0x84, 0x19, 0x3D, 0x5D, + 0xA3, 0x83, 0x1A, 0x3E, 0x5E, 0x93, 0x10, 0x30, 0x81, 0x10, 0x11, 0xB8, + 0xB0, 0xAF, 0x8F, 0x94, + 0xF2, 0xDA, 0x3E, 0xD8, 0xB4, 0x9A, 0xA8, 0x87, 0x29, 0xDA, 0xF8, 0xD8, + 0x87, 0x9A, 0x35, 0xDA, + 0xF8, 0xD8, 0x87, 0x9A, 0x3D, 0xDA, 0xF8, 0xD8, 0xB1, 0xB9, 0xA4, 0x98, + 0x85, 0x02, 0x2E, 0x56, + 0xA5, 0x81, 0x00, 0x0C, 0x14, 0xA3, 0x97, 0xB0, 0x8A, 0xF1, 0x2D, 0xD9, + 0x28, 0xD8, 0x4D, 0xD9, + 0x48, 0xD8, 0x6D, 0xD9, 0x68, 0xD8, 0xB1, 0x84, 0x0D, 0xDA, 0x0E, 0xD8, + 0xA3, 0x29, 0x83, 0xDA, + 0x2C, 0x0E, 0xD8, 0xA3, 0x84, 0x49, 0x83, 0xDA, 0x2C, 0x4C, 0x0E, 0xD8, + 0xB8, 0xB0, 0xA8, 0x8A, + 0x9A, 0xF5, 0x20, 0xAA, 0xDA, 0xDF, 0xD8, 0xA8, 0x40, 0xAA, 0xD0, 0xDA, + 0xDE, 0xD8, 0xA8, 0x60, + 0xAA, 0xDA, 0xD0, 0xDF, 0xD8, 0xF1, 0x97, 0x86, 0xA8, 0x31, 0x9B, 0x06, + 0x99, 0x07, 0xAB, 0x97, + 0x28, 0x88, 0x9B, 0xF0, 0x0C, 0x20, 0x14, 0x40, 0xB8, 0xB0, 0xB4, 0xA8, + 0x8C, 0x9C, 0xF0, 0x04, + 0x28, 0x51, 0x79, 0x1D, 0x30, 0x14, 0x38, 0xB2, 0x82, 0xAB, 0xD0, 0x98, + 0x2C, 0x50, 0x50, 0x78, + 0x78, 0x9B, 0xF1, 0x1A, 0xB0, 0xF0, 0x8A, 0x9C, 0xA8, 0x29, 0x51, 0x79, + 0x8B, 0x29, 0x51, 0x79, + 0x8A, 0x24, 0x70, 0x59, 0x8B, 0x20, 0x58, 0x71, 0x8A, 0x44, 0x69, 0x38, + 0x8B, 0x39, 0x40, 0x68, + 0x8A, 0x64, 0x48, 0x31, 0x8B, 0x30, 0x49, 0x60, 0xA5, 0x88, 0x20, 0x09, + 0x71, 0x58, 0x44, 0x68, + + # bank 6, 256 bytes + 0x11, 0x39, 0x64, 0x49, 0x30, 0x19, 0xF1, 0xAC, 0x00, 0x2C, 0x54, 0x7C, + 0xF0, 0x8C, 0xA8, 0x04, + 0x28, 0x50, 0x78, 0xF1, 0x88, 0x97, 0x26, 0xA8, 0x59, 0x98, 0xAC, 0x8C, + 0x02, 0x26, 0x46, 0x66, + 0xF0, 0x89, 0x9C, 0xA8, 0x29, 0x51, 0x79, 0x24, 0x70, 0x59, 0x44, 0x69, + 0x38, 0x64, 0x48, 0x31, + 0xA9, 0x88, 0x09, 0x20, 0x59, 0x70, 0xAB, 0x11, 0x38, 0x40, 0x69, 0xA8, + 0x19, 0x31, 0x48, 0x60, + 0x8C, 0xA8, 0x3C, 0x41, 0x5C, 0x20, 0x7C, 0x00, 0xF1, 0x87, 0x98, 0x19, + 0x86, 0xA8, 0x6E, 0x76, + 0x7E, 0xA9, 0x99, 0x88, 0x2D, 0x55, 0x7D, 0x9E, 0xB9, 0xA3, 0x8A, 0x22, + 0x8A, 0x6E, 0x8A, 0x56, + 0x8A, 0x5E, 0x9F, 0xB1, 0x83, 0x06, 0x26, 0x46, 0x66, 0x0E, 0x2E, 0x4E, + 0x6E, 0x9D, 0xB8, 0xAD, + 0x00, 0x2C, 0x54, 0x7C, 0xF2, 0xB1, 0x8C, 0xB4, 0x99, 0xB9, 0xA3, 0x2D, + 0x55, 0x7D, 0x81, 0x91, + 0xAC, 0x38, 0xAD, 0x3A, 0xB5, 0x83, 0x91, 0xAC, 0x2D, 0xD9, 0x28, 0xD8, + 0x4D, 0xD9, 0x48, 0xD8, + 0x6D, 0xD9, 0x68, 0xD8, 0x8C, 0x9D, 0xAE, 0x29, 0xD9, 0x04, 0xAE, 0xD8, + 0x51, 0xD9, 0x04, 0xAE, + 0xD8, 0x79, 0xD9, 0x04, 0xD8, 0x81, 0xF3, 0x9D, 0xAD, 0x00, 0x8D, 0xAE, + 0x19, 0x81, 0xAD, 0xD9, + 0x01, 0xD8, 0xF2, 0xAE, 0xDA, 0x26, 0xD8, 0x8E, 0x91, 0x29, 0x83, 0xA7, + 0xD9, 0xAD, 0xAD, 0xAD, + 0xAD, 0xF3, 0x2A, 0xD8, 0xD8, 0xF1, 0xB0, 0xAC, 0x89, 0x91, 0x3E, 0x5E, + 0x76, 0xF3, 0xAC, 0x2E, + 0x2E, 0xF1, 0xB1, 0x8C, 0x5A, 0x9C, 0xAC, 0x2C, 0x28, 0x28, 0x28, 0x9C, + 0xAC, 0x30, 0x18, 0xA8, + 0x98, 0x81, 0x28, 0x34, 0x3C, 0x97, 0x24, 0xA7, 0x28, 0x34, 0x3C, 0x9C, + 0x24, 0xF2, 0xB0, 0x89, + 0xAC, 0x91, 0x2C, 0x4C, 0x6C, 0x8A, 0x9B, 0x2D, 0xD9, 0xD8, 0xD8, 0x51, + 0xD9, 0xD8, 0xD8, 0x79, + + # bank 7, 138 bytes (remainder) + 0xD9, 0xD8, 0xD8, 0xF1, 0x9E, 0x88, 0xA3, 0x31, 0xDA, 0xD8, 0xD8, 0x91, + 0x2D, 0xD9, 0x28, 0xD8, + 0x4D, 0xD9, 0x48, 0xD8, 0x6D, 0xD9, 0x68, 0xD8, 0xB1, 0x83, 0x93, 0x35, + 0x3D, 0x80, 0x25, 0xDA, + 0xD8, 0xD8, 0x85, 0x69, 0xDA, 0xD8, 0xD8, 0xB4, 0x93, 0x81, 0xA3, 0x28, + 0x34, 0x3C, 0xF3, 0xAB, + 0x8B, 0xF8, 0xA3, 0x91, 0xB6, 0x09, 0xB4, 0xD9, 0xAB, 0xDE, 0xFA, 0xB0, + 0x87, 0x9C, 0xB9, 0xA3, + 0xDD, 0xF1, 0xA3, 0xA3, 0xA3, 0xA3, 0x95, 0xF1, 0xA3, 0xA3, 0xA3, 0x9D, + 0xF1, 0xA3, 0xA3, 0xA3, + 0xA3, 0xF2, 0xA3, 0xB4, 0x90, 0x80, 0xF2, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, + 0xA3, 0xA3, 0xA3, 0xA3, + 0xA3, 0xB2, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xB0, 0x87, 0xB5, 0x99, + 0xF1, 0xA3, 0xA3, 0xA3, + 0x98, 0xF1, 0xA3, 0xA3, 0xA3, 0xA3, 0x97, 0xA3, 0xA3, 0xA3, 0xA3, 0xF3, + 0x9B, 0xA3, 0xA3, 0xDC, + 0xB9, 0xA7, 0xF1, 0x26, 0x26, 0x26, 0xD8, 0xD8, 0xFF] + + # dmpConfig has size MPU6050_DMP_CONFIG_SIZE = 192 + dmpConfig = [ + # BANK OFFSET LENGTH [DATA] + 0x03, 0x7B, 0x03, 0x4C, 0xCD, 0x6C, # FCFG_1 inv_set_gyro_calibration + 0x03, 0xAB, 0x03, 0x36, 0x56, 0x76, # FCFG_3 inv_set_gyro_calibration + 0x00, 0x68, 0x04, 0x02, 0xCB, 0x47, 0xA2, + # D_0_104 inv_set_gyro_calibration + 0x02, 0x18, 0x04, 0x00, 0x05, 0x8B, 0xC1, + # D_0_24 inv_set_gyro_calibration + 0x01, 0x0C, 0x04, 0x00, 0x00, 0x00, 0x00, + # D_1_152 inv_set_accel_calibration + 0x03, 0x7F, 0x06, 0x0C, 0xC9, 0x2C, 0x97, 0x97, 0x97, + # FCFG_2 inv_set_accel_calibration + 0x03, 0x89, 0x03, 0x26, 0x46, 0x66, # FCFG_7 inv_set_accel_calibration + 0x00, 0x6C, 0x02, 0x20, 0x00, # D_0_108 inv_set_accel_calibration + 0x02, 0x40, 0x04, 0x00, 0x00, 0x00, 0x00, + # CPASS_MTX_00 inv_set_compass_calibration + 0x02, 0x44, 0x04, 0x00, 0x00, 0x00, 0x00, # CPASS_MTX_01 + 0x02, 0x48, 0x04, 0x00, 0x00, 0x00, 0x00, # CPASS_MTX_02 + 0x02, 0x4C, 0x04, 0x00, 0x00, 0x00, 0x00, # CPASS_MTX_10 + 0x02, 0x50, 0x04, 0x00, 0x00, 0x00, 0x00, # CPASS_MTX_11 + 0x02, 0x54, 0x04, 0x00, 0x00, 0x00, 0x00, # CPASS_MTX_12 + 0x02, 0x58, 0x04, 0x00, 0x00, 0x00, 0x00, # CPASS_MTX_20 + 0x02, 0x5C, 0x04, 0x00, 0x00, 0x00, 0x00, # CPASS_MTX_21 + 0x02, 0xBC, 0x04, 0x00, 0x00, 0x00, 0x00, # CPASS_MTX_22 + 0x01, 0xEC, 0x04, 0x00, 0x00, 0x40, 0x00, + # D_1_236 inv_apply_endian_accel + 0x03, 0x7F, 0x06, 0x0C, 0xC9, 0x2C, 0x97, 0x97, 0x97, + # FCFG_2 inv_set_mpu_sensors + 0x04, 0x02, 0x03, 0x0D, 0x35, 0x5D, + # CFG_MOTION_BIAS inv_turn_on_bias_from_no_motion + 0x04, 0x09, 0x04, 0x87, 0x2D, 0x35, 0x3D, # FCFG_5 inv_set_bias_update + 0x00, 0xA3, 0x01, 0x00, # D_0_163 inv_set_dead_zone + # SPECIAL 0x01 = enable interrupts + 0x00, 0x00, 0x00, 0x01, # SET INT_ENABLE at i=22, SPECIAL INSTRUCTION + 0x07, 0x86, 0x01, 0xFE, # CFG_6 inv_set_fifo_interupt + 0x07, 0x41, 0x05, 0xF1, 0x20, 0x28, 0x30, 0x38, + # CFG_8 inv_send_quaternion + 0x07, 0x7E, 0x01, 0x30, # CFG_16 inv_set_footer + 0x07, 0x46, 0x01, 0x9A, # CFG_GYRO_SOURCE inv_send_gyro + 0x07, 0x47, 0x04, 0xF1, 0x28, 0x30, 0x38, + # CFG_9 inv_send_gyro -> inv_construct3_fifo + 0x07, 0x6C, 0x04, 0xF1, 0x28, 0x30, 0x38, + # CFG_12 inv_send_accel -> inv_construct3_fifo + 0x02, 0x16, 0x02, 0x00, 0x01] # D_0_22 inv_set_fifo_rate + + # This very last 0x01 WAS a 0x09, which drops the FIFO rate down to 20 Hz. + # 0x07 is 25 Hz, 0x01 is 100Hz. Going faster than 100Hz (0x00=200Hz) tends + # to result in very noisy data. DMP output frequency is calculated easily + # using this equation: (200Hz / (1 + value)) + + # It is important to make sure the host processor can keep up with reading + # and processing the FIFO output at the desired rate. Handling FIFO overflow + # cleanly is also a good idea. + + # dmpUpdates has size MPU6050_DMP_UPDATES_SIZE = 47 + dmpUpdates = [ + 0x01, 0xB2, 0x02, 0xFF, 0xFF, + 0x01, 0x90, 0x04, 0x09, 0x23, 0xA1, 0x35, + 0x01, 0x6A, 0x02, 0x06, 0x00, + 0x01, 0x60, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x04, 0x40, 0x00, 0x00, 0x00, + 0x01, 0x62, 0x02, 0x00, 0x00, + 0x00, 0x60, 0x04, 0x00, 0x40, 0x00, 0x00] diff --git a/Code/Python_GPIOZero_Code/25.1.1_MPU6050/Quaternion.py b/Code/Python_GPIOZero_Code/25.1.1_MPU6050/Quaternion.py new file mode 100644 index 0000000..6f8b9bd --- /dev/null +++ b/Code/Python_GPIOZero_Code/25.1.1_MPU6050/Quaternion.py @@ -0,0 +1,135 @@ +__author__ = 'Geir Istad' +""" +MPU6050 Python I2C Class +Copyright (c) 2015 Geir Istad + +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. + + +Code based on +I2Cdev library collection - 3D math helper +by Jeff Rowberg +============================================ +I2Cdev device library code is placed under the MIT license +Copyright (c) 2012 Jeff Rowberg +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. +=============================================== +""" +from math import sqrt + + +class Quaternion: + w = 0.0 + x = 0.0 + y = 0.0 + z = 0.0 + + def __init__(self, a_w=1.0, a_x=0.0, a_y=0.0, a_z=0.0): + self.w = a_w + self.x = a_x + self.y = a_y + self.z = a_z + + def get_product(self, a_quat): + result = Quaternion( + self.w * a_quat.w - self.x * a_quat.x - + self.y * a_quat.y - self.z * a_quat.z, + + self.w * a_quat.x + self.x * a_quat.w + + self.y * a_quat.z - self.z * a_quat.y, + + self.w * a_quat.y - self.x * a_quat.z + + self.y * a_quat.w + self.z * a_quat.x, + + self.w * a_quat.z + self.x * a_quat.y - + self.y * a_quat.x + self.z * a_quat.w) + return result + + def get_conjugate(self): + result = Quaternion(self.w, -self.x, -self.y, -self.z) + return result + + def get_magnitude(self): + return sqrt(self.w * self.w + self.x * self.x + self.y * self.y + + self.z * self.z) + + def normalize(self): + m = self.get_magnitude() + self.w = self.w / m + self.x = self.x / m + self.y = self.y / m + self.z = self.z / m + + def get_normalized(self): + result = Quaternion(self.w, self.x, self.y, self.z) + result.normalize() + return result + + +class XYZVector: + x = 0.0 + y = 0.0 + z = 0.0 + + def __init__(self, a_x=0.0, a_y=0.0, a_z=0.0): + self.x = a_x + self.y = a_y + self.z = a_z + + def get_magnitude(self): + return sqrt(self.x*self.x + self.y*self.y + self.z*self.z) + + def normalize(self): + m = self.get_magnitude() + self.x = self.x / m + self.y = self.y / m + self.z = self.z / m + + def get_normalized(self): + result = XYZVector(self.x, self.y, self.z) + result.normalize() + return result + + def rotate(self, a_quat): + p = Quaternion(0.0, self.x, self.y, self.z) + p = a_quat.get_product(p) + p = p.get_product(a_quat.get_conjugate()) + # By magic quaternion p is now [0, x', y', z'] + self.x = p.x + self.y = p.y + self.z = p.z + + def get_rotated(self, a_quat): + r = XYZVector(self.x, self.y, self.z) + r.rotate(a_quat) + return r diff --git a/Code/Python_GPIOZero_Code/26.1.1_WebIO/WebIO.py b/Code/Python_GPIOZero_Code/26.1.1_WebIO/WebIO.py new file mode 100644 index 0000000..2136356 --- /dev/null +++ b/Code/Python_GPIOZero_Code/26.1.1_WebIO/WebIO.py @@ -0,0 +1,68 @@ +from gpiozero import LED +import os +from http.server import BaseHTTPRequestHandler, HTTPServer + +host_name = '192.168.1.147' # Change this to your Raspberry Pi IP address +host_port = 8000 +led = LED(17) # define LED pin according to BCM Numbering + +class MyServer(BaseHTTPRequestHandler): + """ A special implementation of BaseHTTPRequestHander for reading data from + and control GPIO of a Raspberry Pi + """ + def do_HEAD(self): + """ do_HEAD() can be tested use curl command + 'curl -I http://server-ip-address:port' + """ + self.send_response(200) + self.send_header('Content-type', 'text/html') + self.end_headers() + def _redirect(self, path): + self.send_response(303) + self.send_header('Content-type', 'text/html') + self.send_header('Location', path) + self.end_headers() + def do_GET(self): + """ do_GET() can be tested using curl command + 'curl http://server-ip-address:port' + """ + html = ''' + + +

Welcome to my Raspberry Pi

+

Current GPU temperature is {}

+
+ Turn LED : + + +
+ + + ''' + temp = os.popen("vcgencmd measure_temp").read() +# temp = os.popen("/opt/vc/bin/vcgencmd measure_temp").read() + self.do_HEAD() + self.wfile.write(html.format(temp[5:]).encode("utf-8")) + def do_POST(self): + """ do_POST() can be tested using curl command + 'curl -d "submit=On" http://server-ip-address:port' + """ + content_length = int(self.headers['Content-Length']) # Get the size of data + post_data = self.rfile.read(content_length).decode("utf-8") # Get the data + post_data = post_data.split("=")[1] # Only keep the value + + if post_data == 'On': + led.on() + else: + led.off() + print("LED is {}".format(post_data)) + self._redirect('/') # Redirect back to the root url + +if __name__ == '__main__': + http_server = HTTPServer((host_name, host_port), MyServer) + print("Server Starts - %s:%s" % (host_name, host_port)) + try: + http_server.serve_forever() + except KeyboardInterrupt: + http_server.server_close() + diff --git a/Code/Python_GPIOZero_Code/27.2.1_LightWater03/LightWater03.py b/Code/Python_GPIOZero_Code/27.2.1_LightWater03/LightWater03.py new file mode 100644 index 0000000..95ad2d9 --- /dev/null +++ b/Code/Python_GPIOZero_Code/27.2.1_LightWater03/LightWater03.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 +############################################################################# +# Filename : LightWater03.py +# Description : Control LED with 74HC595 on the DIY circuit board +# auther : www.freenove.com +# modification: 2023/05/15 +######################################################################## +from gpiozero import OutputDevice +import time + +LSBFIRST = 1 +MSBFIRST = 2 + +# define the pins for 74HC595 +dataPin = OutputDevice(17) # DS Pin of 74HC595(Pin14) +latchPin = OutputDevice(27) # ST_CP Pin of 74HC595(Pin12) +clockPin = OutputDevice(22) # CH_CP Pin of 74HC595(Pin11) + +# Define an array to store the pulse width of LED +pluseWidth = [0,0,0,0,0,0,0,0,64,32,16,8,4,2,1,0,0,0,0,0,0,0,0] + +# shiftOut function, use bit serial transmission. +def shiftOut(order,val): + for i in range(0,8): + clockPin.off() + if(order == LSBFIRST): + dataPin.on() if (0x01&(val>>i)==0x01) else dataPin.off() + elif(order == MSBFIRST): + dataPin.on() if (0x80&(val< moveSpeed): # control speed + lastMove = time.time() # Record the time point of the move + index +=1 # move to next + if(index > 15): # index to 0 + index = 0 + + for i in range(0,64): # The cycle of PWM is 64 cycles + data = 0 + for j in range(0,8): #Calculate the output state of this loop + if(i < pluseWidth[j+index]): #Calculate the LED state according to the pulse width + data |= 1< + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Processing/Lib/io/examples/AnalogDigital_I2C_ADS1X15/ADS1X15.pde b/Processing/Lib/io/examples/AnalogDigital_I2C_ADS1X15/ADS1X15.pde new file mode 100644 index 0000000..91caaf9 --- /dev/null +++ b/Processing/Lib/io/examples/AnalogDigital_I2C_ADS1X15/ADS1X15.pde @@ -0,0 +1,107 @@ +import processing.io.I2C; + +// ADS1015 and ADS1115 are Analog-to-Digital converters using I2C +// they have four channels and 12 and 16 bits of resolution respectively +// datasheets: http://www.ti.com/lit/ds/symlink/ads1015.pdf +// http://www.ti.com/lit/ds/symlink/ads1115.pdf + +class ADS1015 extends ADS1X15 { + ADS1015(String dev, int address) { + super(dev, address); + bitShift = 4; + conversionDelay = 1; + } + + // returns a number between -1.0 and 1.0 + float analogRead(int channel) { + return readSingleEnded(channel) / 2047.0; + } +} + +class ADS1115 extends ADS1X15 { + ADS1115(String dev, int address) { + super(dev, address); + bitShift = 0; + conversionDelay = 8; + } + + // returns a number between -1.0 and 1.0 + float analogRead(int channel) { + return readSingleEnded(channel) / 32767.0; + } +} + + +class ADS1X15 extends I2C { + int address; + int bitShift; // bits to shift the result to the right + int conversionDelay; // in ms + int channel; // last channel used + int range; // see below + + // possible voltage ranges + static final int INTERNAL_6V144 = 0; // +/- 6.144V + static final int INTERNAL_4V096 = 1; // +/- 4.096V (library default) + static final int INTERNAL_2V048 = 2; // +/- 2.048V + static final int INTERNAL_1V024 = 3; // +/- 1.024V + static final int INTERNAL_0V512 = 4; // +/- 0.512V + static final int INTERNAL_0V256 = 5; // +/- 0.256V + + ADS1X15(String dev, int address) { + super(dev); + this.address = address; + this.channel = -1; + this.range = INTERNAL_4V096; + } + + // be careful not to make the input voltage exceed VCC + 0.3V + // this is regardless of the selected input range + void analogReference(int type) { + if (type < 0 || 7 < type) { + throw new RuntimeException("Invalid range setting"); + } + range = type; + } + + int readSingleEnded(int channel) { + if (channel < 0 || 3 < channel) { + System.err.println("The channel needs to be from 0 to 3"); + throw new IllegalArgumentException("Unexpected channel"); + } + + if (channel != this.channel) { + int config = 0x0183; // start with the default value from datasheet + config &= ~0x100; // enable continuous readings + config |= (range << 9); // set selected range (gain) + config |= (1 << 14) | (channel << 12); // set single-ended and channel + config |= (1 << 15); // start a single conversion + writeRegister(0x01, config); // write to the configuration register at 0x01 + + // when the channel switched we need to wait for the upcoming + // conversion to finish + delay(conversionDelay); + + // save the channel so that we don't need to do the same for + // subsequent reads from the same channel + this.channel = channel; + } + + return readS16(0x00) >> bitShift; // read from the conversion register at 0x00 + // the ADS1015 will have its 12-bit result in the upper bits, shift those right by four + } + + protected void writeRegister(int register, int value) { + beginTransmission(address); + write(register); + write(value >> 8); + write(value & 0xFF); + endTransmission(); + } + + protected int readS16(int register) { + beginTransmission(address); + write(register); + byte[] in = read(2); + return (in[0] << 8) | in[1]; + } +} diff --git a/Processing/Lib/io/examples/AnalogDigital_I2C_ADS1X15/AnalogDigital_I2C_ADS1X15.pde b/Processing/Lib/io/examples/AnalogDigital_I2C_ADS1X15/AnalogDigital_I2C_ADS1X15.pde new file mode 100644 index 0000000..31e5cec --- /dev/null +++ b/Processing/Lib/io/examples/AnalogDigital_I2C_ADS1X15/AnalogDigital_I2C_ADS1X15.pde @@ -0,0 +1,38 @@ +import processing.io.*; +ADS1015 adc; +// or, alternatively: +// ADS1115 adc; + +// see setup.png in the sketch folder for wiring details + +void setup() { + //printArray(I2C.list()); + + adc = new ADS1015("i2c-1", 0x48); + //adc = new ADS1115("i2c-1", 0x48); + + // this sets the measuring range to +/- 4.096 Volts + // other ranges supported by this chip: + // INTERNAL_6V144, INTERNAL_2V048, INTERNAL_1V024, + // INTERNAL_0V512, INTERNAL_0V256 + adc.analogReference(ADS1X15.INTERNAL_4V096); + + // Important: do not attempt to measure voltages higher than + // the supply voltage (VCC) + 0.3V, meaning that 3.6V is the + // absolut maximum voltage on the Raspberry Pi. This is + // irrespective of the analogReference() setting above. +} + +void draw() { + // this will return a number between 0 and 1 + // (as long as your voltage is positive) + float measured = adc.analogRead(0); + + // multiply with the selected range to get the absolut voltage + float volts = measured * 4.096; + println("Analog Input 0 is " + volts + "V"); + + background(255); + fill(measured * 255); + ellipse(width/2, height/2, width * 0.75, width * 0.75); +} diff --git a/Processing/Lib/io/examples/AnalogDigital_I2C_ADS1X15/setup.png b/Processing/Lib/io/examples/AnalogDigital_I2C_ADS1X15/setup.png new file mode 100644 index 0000000..f57950d Binary files /dev/null and b/Processing/Lib/io/examples/AnalogDigital_I2C_ADS1X15/setup.png differ diff --git a/Processing/Lib/io/examples/AnalogDigital_SPI_MCP3001/AnalogDigital_SPI_MCP3001.pde b/Processing/Lib/io/examples/AnalogDigital_SPI_MCP3001/AnalogDigital_SPI_MCP3001.pde new file mode 100644 index 0000000..fd7e49d --- /dev/null +++ b/Processing/Lib/io/examples/AnalogDigital_SPI_MCP3001/AnalogDigital_SPI_MCP3001.pde @@ -0,0 +1,20 @@ +import processing.io.*; +MCP3001 adc; + +// see setup.png in the sketch folder for wiring details + +void setup() { + //printArray(SPI.list()); + adc = new MCP3001(SPI.list()[0]); +} + +void draw() { + // this will return a number between 0 and 1 + float measured = adc.analogRead(); + + // multiply with the supply voltage to get an absolute value + float volts = 3.3 * measured; + println("Analog Input is " + volts + "V"); + + background(measured * 255); +} diff --git a/Processing/Lib/io/examples/AnalogDigital_SPI_MCP3001/MCP3001.pde b/Processing/Lib/io/examples/AnalogDigital_SPI_MCP3001/MCP3001.pde new file mode 100644 index 0000000..2ffecad --- /dev/null +++ b/Processing/Lib/io/examples/AnalogDigital_SPI_MCP3001/MCP3001.pde @@ -0,0 +1,23 @@ +import processing.io.SPI; + +// MCP3001 is a Analog-to-Digital converter using SPI +// datasheet: http://ww1.microchip.com/downloads/en/DeviceDoc/21293C.pdf + +class MCP3001 extends SPI { + + MCP3001(String dev) { + super(dev); + settings(500000, SPI.MSBFIRST, SPI.MODE0); + } + + // returns a number between 0.0 and 1.0 + float analogRead() { + // dummy write, actual values don't matter + byte[] out = { 0, 0 }; + byte[] in = transfer(out); + // some input bit shifting according to the datasheet p. 16 + int val = ((in[0] & 0x1f) << 5) | ((in[1] & 0xf8) >> 3); + // val is between 0 and 1023 + return val/1023.0; + } +} diff --git a/Processing/Lib/io/examples/AnalogDigital_SPI_MCP3001/setup.png b/Processing/Lib/io/examples/AnalogDigital_SPI_MCP3001/setup.png new file mode 100644 index 0000000..c52f67f Binary files /dev/null and b/Processing/Lib/io/examples/AnalogDigital_SPI_MCP3001/setup.png differ diff --git a/Processing/Lib/io/examples/AnalogDigital_SPI_MCP3008/AnalogDigital_SPI_MCP3008.pde b/Processing/Lib/io/examples/AnalogDigital_SPI_MCP3008/AnalogDigital_SPI_MCP3008.pde new file mode 100644 index 0000000..2e3ea41 --- /dev/null +++ b/Processing/Lib/io/examples/AnalogDigital_SPI_MCP3008/AnalogDigital_SPI_MCP3008.pde @@ -0,0 +1,22 @@ +import processing.io.*; +MCP3008 adc; + +// see setup.png in the sketch folder for wiring details + +void setup() { + //printArray(SPI.list()); + adc = new MCP3008(SPI.list()[0]); +} + +void draw() { + // this will return a number between 0 and 1 + float measured = adc.analogRead(0); + + // multiply with the supply voltage to get an absolute value + float volts = 3.3 * measured; + println("Analog Input 0 is " + volts + "V"); + + background(255); + fill(measured * 255); + ellipse(width/2, height/2, width * 0.75, width * 0.75); +} diff --git a/Processing/Lib/io/examples/AnalogDigital_SPI_MCP3008/MCP3008.pde b/Processing/Lib/io/examples/AnalogDigital_SPI_MCP3008/MCP3008.pde new file mode 100644 index 0000000..1c003d9 --- /dev/null +++ b/Processing/Lib/io/examples/AnalogDigital_SPI_MCP3008/MCP3008.pde @@ -0,0 +1,28 @@ +import processing.io.SPI; + +// MCP3008 is a Analog-to-Digital converter using SPI +// other than the MCP3001, this has 8 input channels +// datasheet: http://ww1.microchip.com/downloads/en/DeviceDoc/21295d.pdf + +class MCP3008 extends SPI { + + MCP3008(String dev) { + super(dev); + settings(500000, SPI.MSBFIRST, SPI.MODE0); + } + + // returns a number between 0.0 and 1.0 + float analogRead(int channel) { + if (channel < 0 || 7 < channel) { + System.err.println("The channel needs to be from 0 to 7"); + throw new IllegalArgumentException("Unexpected channel"); + } + byte[] out = { 0, 0, 0 }; + // encode the channel number in the first byte + out[0] = (byte)(0x18 | channel); + byte[] in = transfer(out); + int val = ((in[1] & 0x03) << 8) | (in[2] & 0xff); + // val is between 0 and 1023 + return val/1023.0; + } +} diff --git a/Processing/Lib/io/examples/AnalogDigital_SPI_MCP3008/setup.png b/Processing/Lib/io/examples/AnalogDigital_SPI_MCP3008/setup.png new file mode 100644 index 0000000..92d1b69 Binary files /dev/null and b/Processing/Lib/io/examples/AnalogDigital_SPI_MCP3008/setup.png differ diff --git a/Processing/Lib/io/examples/Compass_I2C_HMC6352/Compass_I2C_HMC6352.pde b/Processing/Lib/io/examples/Compass_I2C_HMC6352/Compass_I2C_HMC6352.pde new file mode 100644 index 0000000..921fe47 --- /dev/null +++ b/Processing/Lib/io/examples/Compass_I2C_HMC6352/Compass_I2C_HMC6352.pde @@ -0,0 +1,19 @@ +import processing.io.*; +HMC6352 compass; + +// see setup.png in the sketch folder for wiring details + +void setup() { + // the module's I2C address can be changed by modifying values in its EEPROM + // 0x21 is however the default address + + //printArray(I2C.list()); + compass = new HMC6352("i2c-1", 0x21); +} + +void draw() { + background(255); + float deg = compass.heading(); + println(deg + " degrees"); + line(width/2, height/2, width/2+sin(radians(deg))*width/2, height/2-cos(radians(deg))*height/2); +} diff --git a/Processing/Lib/io/examples/Compass_I2C_HMC6352/HMC6352.pde b/Processing/Lib/io/examples/Compass_I2C_HMC6352/HMC6352.pde new file mode 100644 index 0000000..ff925ac --- /dev/null +++ b/Processing/Lib/io/examples/Compass_I2C_HMC6352/HMC6352.pde @@ -0,0 +1,38 @@ +import processing.io.I2C; + +// HMC6352 is a digital compass using I2C +// datasheet: https://www.sparkfun.com/datasheets/Components/HMC6352.pdf + +class HMC6352 extends I2C { + int address; + + HMC6352(String dev, int address) { + super(dev); + this.address = address; + setHeadingMode(); + } + + void setHeadingMode() { + beginTransmission(address); + // command byte for writing to EEPROM + write(0x77); + // address of the output data control byte + write(0x4e); + // give us the plain heading + write(0x00); + endTransmission(); + } + + float heading() { + beginTransmission(address); + // command byte for reading the data + write(0x41); + byte[] in = read(2); + endTransmission(); + // put bytes together to tenth of degrees + // & 0xff makes sure the byte is not interpreted as a negative value + int deg = (in[0] & 0xff) << 8 | (in[1] & 0xff); + // return degrees + return deg / 10.0; + } +} diff --git a/Processing/Lib/io/examples/Compass_I2C_HMC6352/setup.png b/Processing/Lib/io/examples/Compass_I2C_HMC6352/setup.png new file mode 100644 index 0000000..2e322cb Binary files /dev/null and b/Processing/Lib/io/examples/Compass_I2C_HMC6352/setup.png differ diff --git a/Processing/Lib/io/examples/DigitalAnalog_I2C_MCP4725/DigitalAnalog_I2C_MCP4725.pde b/Processing/Lib/io/examples/DigitalAnalog_I2C_MCP4725/DigitalAnalog_I2C_MCP4725.pde new file mode 100644 index 0000000..2c69dae --- /dev/null +++ b/Processing/Lib/io/examples/DigitalAnalog_I2C_MCP4725/DigitalAnalog_I2C_MCP4725.pde @@ -0,0 +1,12 @@ +import processing.io.*; +MCP4725 dac; + +void setup() { + //printArray(I2C.list()); + dac = new MCP4725(I2C.list()[0], 0x60); +} + +void draw() { + background(map(mouseX, 0, width, 0, 255)); + dac.setAnalog(map(mouseX, 0, width, 0.0, 1.0)); +} diff --git a/Processing/Lib/io/examples/DigitalAnalog_I2C_MCP4725/MCP4725.pde b/Processing/Lib/io/examples/DigitalAnalog_I2C_MCP4725/MCP4725.pde new file mode 100644 index 0000000..67c5017 --- /dev/null +++ b/Processing/Lib/io/examples/DigitalAnalog_I2C_MCP4725/MCP4725.pde @@ -0,0 +1,27 @@ +import processing.io.I2C; + +// MCP4725 is a Digital-to-Analog converter using I2C +// datasheet: http://ww1.microchip.com/downloads/en/DeviceDoc/22039d.pdf + +class MCP4725 extends I2C { + int address; + + // there can be more than one device connected to the bus + // as long as they have different addresses + MCP4725(String dev, int address) { + super(dev); + this.address = address; + } + + // outputs voltages from 0V to the supply voltage + // (works with 3.3V and 5V) + void setAnalog(float fac) { + fac = constrain(fac, 0.0, 1.0); + // convert to 12 bit value + int val = int(4095 * fac); + beginTransmission(address); + write(val >> 8); + write(val & 255); + endTransmission(); + } +} diff --git a/Processing/Lib/io/examples/Display_I2C_SSD1306/Display_I2C_SSD1306.pde b/Processing/Lib/io/examples/Display_I2C_SSD1306/Display_I2C_SSD1306.pde new file mode 100644 index 0000000..e16e654 --- /dev/null +++ b/Processing/Lib/io/examples/Display_I2C_SSD1306/Display_I2C_SSD1306.pde @@ -0,0 +1,22 @@ +import processing.io.*; + +// 0.96" 128x64 OLED display ("SKU 346540") +SSD1306 oled; + +void setup() { + size(128, 64); + + // the display can be set to one of these two addresses: 0x3c (default) or 0x3d + // (they might be listed as 0x7a and 0x7b on the circuit board) + + // you might need to use a different interface on other SBCs + oled = new SSD1306("i2c-1", 0x3c); +} + +void draw() { + background(0); + stroke(255); + line(0, 0, 127, 63); + line(0, 63, 127, 0); + oled.sendImage(get()); +} diff --git a/Processing/Lib/io/examples/Display_I2C_SSD1306/SSD1306.pde b/Processing/Lib/io/examples/Display_I2C_SSD1306/SSD1306.pde new file mode 100644 index 0000000..dee79a0 --- /dev/null +++ b/Processing/Lib/io/examples/Display_I2C_SSD1306/SSD1306.pde @@ -0,0 +1,118 @@ +import processing.io.I2C; + +// SSD1306 is a small, inexpensive 128x64 pixels monochrome OLED display +// available online as "0.96" 128x64 OLED display", SKU 346540 +// or from Adafruit +// datasheet: https://www.adafruit.com/datasheets/SSD1306.pdf + +class SSD1306 extends I2C { + int address; + + // there can be more than one device connected to the bus + // as long as they have different addresses + SSD1306(String dev, int address) { + super(dev); + this.address = address; + init(); + } + + protected void init() { + writeCommand(0xae); // turn display off + writeCommand(0xa8, 0x3f); // set multiplex ratio to the highest setting + writeCommand(0x8d, 0x14); // enable charge pump + writeCommand(0x20, 0x00); // set memory addressing mode to horizontal + writeCommand(0xd5, 0x80); // set display clock divide ratio & oscillator frequency to default + writeCommand(0xd3, 0x00); // no display offset + writeCommand(0x40 | 0x00); // set default display start line + + // use the following two lines to flip the display + writeCommand(0xa0 | 0x01); // set segment re-map + writeCommand(0xc8); // set COM output scan direction + + writeCommand(0xda, 0x12); // set COM pins hardware configuration + writeCommand(0xd9, 0xf1); // set pre-charge period to 241x DCLK + writeCommand(0xdB, 0x40); // set VCOMH deselect level + writeCommand(0xa4); // display RAM content (not all-on) + writeCommand(0xa6); // set normal (not-inverted) display + + // set this since we don't have access to the OLED's reset pins (?) + writeCommand(0x21, 0, 127); // set column address + writeCommand(0x22, 0, 7); // set page address + + writeCommand(0x81, 0xcf); // set contrast + writeCommand(0x2e); // deactivate scroll + writeCommand(0xaf); // turn display on + } + + void invert(boolean inverted) { + if (inverted) { + writeCommand(0xa7); + } else { + writeCommand(0xa6); + } + } + + void sendImage(PImage img) { + sendImage(img, 0, 0); + } + + void sendImage(PImage img, int startX, int startY) { + byte[] frame = new byte[1024]; + img.loadPixels(); + for (int y=startY; y < height && y-startY < 64; y++) { + for (int x=startX; x < width && x-startX < 128; x++) { + if (128 <= brightness(img.pixels[y*img.width+x])) { + // this isn't the normal (scanline) mapping, but 8 pixels below each other at a time + // white pixels have their bit turned on + frame[x + (y/8)*128] |= (1 << (y % 8)); + } + } + } + sendFramebuffer(frame); + } + + void sendFramebuffer(byte[] buf) { + if (buf.length != 1024) { + System.err.println("The framebuffer should be 1024 bytes long, with one bit per pixel"); + throw new IllegalArgumentException("Unexpected buffer size"); + } + + writeCommand(0x00 | 0x0); // set start address + writeCommand(0x10 | 0x0); // set higher column start address + writeCommand(0x40 | 0x0); // set start line + + // send the frame buffer as 16 byte long packets + for (int i=0; i < buf.length/16; i++) { + super.beginTransmission(address); + super.write(0x40); // indicates data write + for (int j=0; j < 16; j++) { + super.write(buf[i*16+j]); + } + super.endTransmission(); + } + } + + protected void writeCommand(int arg1) { + super.beginTransmission(address); + super.write(0x00); // indicates command write + super.write(arg1); + super.endTransmission(); + } + + protected void writeCommand(int arg1, int arg2) { + super.beginTransmission(address); + super.write(0x00); + super.write(arg1); + super.write(arg2); + super.endTransmission(); + } + + protected void writeCommand(int arg1, int arg2, int arg3) { + super.beginTransmission(address); + super.write(0x00); + super.write(arg1); + super.write(arg2); + super.write(arg3); + super.endTransmission(); + } +} diff --git a/Processing/Lib/io/examples/Environment_I2C_BME280/BME280.pde b/Processing/Lib/io/examples/Environment_I2C_BME280/BME280.pde new file mode 100644 index 0000000..572e6d7 --- /dev/null +++ b/Processing/Lib/io/examples/Environment_I2C_BME280/BME280.pde @@ -0,0 +1,407 @@ +import processing.io.I2C; + +// BME280 is an integrated environmental sensor +// It can measure temperature, pressure and humidity +// datasheet: https://cdn-shop.adafruit.com/datasheets/BST-BME280_DS001-10.pdf +// code contributed by @OlivierLD + +public class BME280 extends I2C { + + public final static int BME280_I2CADDR = 0x77; // this is the default I2C address + public final static int DEFAULT_ADDR = BME280_I2CADDR; + + // Operating Modes + public final static int BME280_OSAMPLE_1 = 1; + public final static int BME280_OSAMPLE_2 = 2; + public final static int BME280_OSAMPLE_4 = 3; + public final static int BME280_OSAMPLE_8 = 4; + public final static int BME280_OSAMPLE_16 = 5; + + // BME280 Registers + public final static int BME280_REGISTER_DIG_T1 = 0x88; // Trimming parameter registers + public final static int BME280_REGISTER_DIG_T2 = 0x8A; + public final static int BME280_REGISTER_DIG_T3 = 0x8C; + + public final static int BME280_REGISTER_DIG_P1 = 0x8E; + public final static int BME280_REGISTER_DIG_P2 = 0x90; + public final static int BME280_REGISTER_DIG_P3 = 0x92; + public final static int BME280_REGISTER_DIG_P4 = 0x94; + public final static int BME280_REGISTER_DIG_P5 = 0x96; + public final static int BME280_REGISTER_DIG_P6 = 0x98; + public final static int BME280_REGISTER_DIG_P7 = 0x9A; + public final static int BME280_REGISTER_DIG_P8 = 0x9C; + public final static int BME280_REGISTER_DIG_P9 = 0x9E; + + public final static int BME280_REGISTER_DIG_H1 = 0xA1; + public final static int BME280_REGISTER_DIG_H2 = 0xE1; + public final static int BME280_REGISTER_DIG_H3 = 0xE3; + public final static int BME280_REGISTER_DIG_H4 = 0xE4; + public final static int BME280_REGISTER_DIG_H5 = 0xE5; + public final static int BME280_REGISTER_DIG_H6 = 0xE6; + public final static int BME280_REGISTER_DIG_H7 = 0xE7; + + public final static int BME280_REGISTER_CHIPID = 0xD0; + public final static int BME280_REGISTER_VERSION = 0xD1; + public final static int BME280_REGISTER_SOFTRESET = 0xE0; + + public final static int BME280_REGISTER_CONTROL_HUM = 0xF2; + public final static int BME280_REGISTER_CONTROL = 0xF4; + public final static int BME280_REGISTER_CONFIG = 0xF5; + public final static int BME280_REGISTER_PRESSURE_DATA = 0xF7; + public final static int BME280_REGISTER_TEMP_DATA = 0xFA; + public final static int BME280_REGISTER_HUMIDITY_DATA = 0xFD; + + private int dig_T1 = 0; + private int dig_T2 = 0; + private int dig_T3 = 0; + + private int dig_P1 = 0; + private int dig_P2 = 0; + private int dig_P3 = 0; + private int dig_P4 = 0; + private int dig_P5 = 0; + private int dig_P6 = 0; + private int dig_P7 = 0; + private int dig_P8 = 0; + private int dig_P9 = 0; + + private int dig_H1 = 0; + private int dig_H2 = 0; + private int dig_H3 = 0; + private int dig_H4 = 0; + private int dig_H5 = 0; + private int dig_H6 = 0; + + private float tFine = 0.0f; + + private int address; + private int mode = BME280_OSAMPLE_8; + private float standardSeaLevelPressure = 101325.0f; // in Pa (1013.25 hPa) + + protected float temp = 0.0f; // most recent sensor readings, set by update() + protected float press = 0.0f; + protected float hum = 0.0f; + + + public BME280(String dev) { + this(dev, DEFAULT_ADDR); + } + + public BME280(String dev, int address) { + super(dev); + this.address = address; + + // Soft reset + command(BME280_REGISTER_SOFTRESET, (byte)0xB6); + // Wait for the chip to wake up + delay(300); + + try { + readCalibrationData(); + // showCalibrationData(); + } catch (Exception ex) { + ex.printStackTrace(); + } + + command(BME280_REGISTER_CONTROL, (byte)0x3F); + tFine = 0.0f; + } + + + /** + * Read and update all sensors values + */ + public void update() { + // The order used to read the data is important! + // 1.temperature, 2.pressure (analog to altitude), 3.humidity. + + try { + temp = readTemperature(); + } catch (Exception ex) { + System.err.println(ex.getMessage()); + ex.printStackTrace(); + } + + try { + press = readPressure(); + } catch (Exception ex) { + System.err.println(ex.getMessage()); + ex.printStackTrace(); + } + + try { + hum = readHumidity(); + } catch (Exception ex) { + System.err.println(ex.getMessage()); + ex.printStackTrace(); + } + } + + /** + * Returns the temperature in degrees celsius + */ + public float temperature() { + return temp; + } + + /** + * Returns the pressure in Pa + */ + public float pressure() { + return press; + } + + /** + * Returns the altitude in meters + * @param pressure as returned by pressure() + */ + public float altitude(float pressure) { + double altitude = 0.0; + if (standardSeaLevelPressure != 0) { + altitude = 44330.0 * (1.0 - Math.pow(pressure / standardSeaLevelPressure, 0.1903)); + } + return (float)altitude; + } + + /** + * Returns the altitude in meters + * @param pressure as returned by pressure() in Pa + * @param temperature as returned by temperature() in Celcius + */ + public float altitude(float pressure, float temperature) { + double altitude = 0.0; + if (standardSeaLevelPressure != 0) { + altitude = ((Math.pow(standardSeaLevelPressure / pressure, 1 / 5.257) - 1) * (temperature + 273.25)) / 0.0065; + } + return (float)altitude; + } + + /** + * Returns the humidity in percent + */ + public float humidity() { + return hum; + } + + /** + * Set the standard sea level pressure used for calculating altitude() + * Defaults to 101325 Pa (1013.25 hPa) + */ + public void setStandardSeaLevelPressure(float pressure) { + standardSeaLevelPressure = pressure; + } + + + protected float readTemperature() { + // Returns the compensated temperature in degrees celcius + float UT = readRawTemp(); + float var1 = 0.0f; + float var2 = 0.0f; + float temp = 0.0f; + + // Read raw temp before aligning it with the calibration values + var1 = (UT / 16384.0f - dig_T1 / 1024.0f) * (float) dig_T2; + var2 = ((UT / 131072.0f - dig_T1 / 8192.0f) * (UT / 131072.0f - dig_T1 / 8192.0f)) * (float) dig_T3; + tFine = (int) (var1 + var2); + temp = (var1 + var2) / 5120.0f; + // println("DBG: Calibrated temperature = " + temp + " C"); + return temp; + } + + protected float readPressure() { + // Returns the compensated pressure in Pascal + int adc = readRawPressure(); + // println("ADC:" + adc + ", tFine:" + tFine); + float var1 = (tFine / 2.0f) - 64000.0f; + float var2 = var1 * var1 * (dig_P6 / 32768.0f); + var2 = var2 + var1 * dig_P5 * 2.0f; + var2 = (var2 / 4.0f) + (dig_P4 * 65536.0f); + var1 = (dig_P3 * var1 * var1 / 524288.0f + dig_P2 * var1) / 524288.0f; + var1 = (1.0f + var1 / 32768.0f) * dig_P1; + if (var1 == 0f) { + return 0.0f; + } + float p = 1048576.0f - adc; + p = ((p - var2 / 4096.0f) * 6250.0f) / var1; + var1 = dig_P9 * p * p / 2147483648.0f; + var2 = p * dig_P8 / 32768.0f; + p = p + (var1 + var2 + dig_P7) / 16.0f; + // println("DBG: Pressure = " + p + " Pa"); + return p; + } + + protected float readHumidity() { + // Returns the compensated humidity in percent + int adc = readRawHumidity(); + float h = tFine - 76800.0f; + h = (adc - (dig_H4 * 64.0f + dig_H5 / 16384.8f * h)) * + (dig_H2 / 65536.0f * (1.0f + dig_H6 / 67108864.0f * h * (1.0f + dig_H3 / 67108864.0f * h))); + h = h * (1.0f - dig_H1 * h / 524288.0f); + if (h > 100) { + h = 100; + } else if (h < 0) { + h = 0; + } + // println("DBG: Humidity = " + h); + return h; + } + + + private void readCalibrationData() { + // Reads the calibration data from the IC + dig_T1 = readU16LE(BME280_REGISTER_DIG_T1); + dig_T2 = readS16LE(BME280_REGISTER_DIG_T2); + dig_T3 = readS16LE(BME280_REGISTER_DIG_T3); + + dig_P1 = readU16LE(BME280_REGISTER_DIG_P1); + dig_P2 = readS16LE(BME280_REGISTER_DIG_P2); + dig_P3 = readS16LE(BME280_REGISTER_DIG_P3); + dig_P4 = readS16LE(BME280_REGISTER_DIG_P4); + dig_P5 = readS16LE(BME280_REGISTER_DIG_P5); + dig_P6 = readS16LE(BME280_REGISTER_DIG_P6); + dig_P7 = readS16LE(BME280_REGISTER_DIG_P7); + dig_P8 = readS16LE(BME280_REGISTER_DIG_P8); + dig_P9 = readS16LE(BME280_REGISTER_DIG_P9); + + dig_H1 = readU8(BME280_REGISTER_DIG_H1); + dig_H2 = readS16LE(BME280_REGISTER_DIG_H2); + dig_H3 = readU8(BME280_REGISTER_DIG_H3); + dig_H6 = readS8(BME280_REGISTER_DIG_H7); + + int h4 = readS8(BME280_REGISTER_DIG_H4); + h4 = (h4 << 24) >> 20; + dig_H4 = h4 | (readU8(BME280_REGISTER_DIG_H5) & 0x0F); + + int h5 = readS8(BME280_REGISTER_DIG_H6); + h5 = (h5 << 24) >> 20; + dig_H5 = h5 | (readU8(BME280_REGISTER_DIG_H5) >> 4 & 0x0F); + } + + private String displayRegister(int reg) { + return String.format("0x%s (%d)", lpad(Integer.toHexString(reg & 0xFFFF).toUpperCase(), 4, "0"), reg); + } + + private void showCalibrationData() { + // Displays the calibration values for debugging purposes + println("======================"); + println("DBG: T1 = " + displayRegister(dig_T1)); + println("DBG: T2 = " + displayRegister(dig_T2)); + println("DBG: T3 = " + displayRegister(dig_T3)); + println("----------------------"); + println("DBG: P1 = " + displayRegister(dig_P1)); + println("DBG: P2 = " + displayRegister(dig_P2)); + println("DBG: P3 = " + displayRegister(dig_P3)); + println("DBG: P4 = " + displayRegister(dig_P4)); + println("DBG: P5 = " + displayRegister(dig_P5)); + println("DBG: P6 = " + displayRegister(dig_P6)); + println("DBG: P7 = " + displayRegister(dig_P7)); + println("DBG: P8 = " + displayRegister(dig_P8)); + println("DBG: P9 = " + displayRegister(dig_P9)); + println("----------------------"); + println("DBG: H1 = " + displayRegister(dig_H1)); + println("DBG: H2 = " + displayRegister(dig_H2)); + println("DBG: H3 = " + displayRegister(dig_H3)); + println("DBG: H4 = " + displayRegister(dig_H4)); + println("DBG: H5 = " + displayRegister(dig_H5)); + println("DBG: H6 = " + displayRegister(dig_H6)); + println("======================"); + } + + private void command(int reg, byte val) { + super.beginTransmission(address); + super.write(reg); + super.write(val); + super.endTransmission(); + } + + private int readRawTemp() { + // Returns the raw (uncompensated) temperature + int meas = mode; + // println(String.format("readRawTemp: 1 - meas=%d", meas)); + command(BME280_REGISTER_CONTROL_HUM, (byte) meas); // HUM ? + meas = mode << 5 | mode << 2 | 1; + // println(String.format("readRawTemp: 2 - meas=%d", meas)); + command(BME280_REGISTER_CONTROL, (byte) meas); + + double sleepTime = 0.00125 + 0.0023 * (1 << mode); + sleepTime = sleepTime + 0.0023 * (1 << mode) + 0.000575; + sleepTime = sleepTime + 0.0023 * (1 << mode) + 0.000575; + delay((int)Math.round(sleepTime * 1000)); + int msb = readU8(BME280_REGISTER_TEMP_DATA); + int lsb = readU8(BME280_REGISTER_TEMP_DATA + 1); + int xlsb = readU8(BME280_REGISTER_TEMP_DATA + 2); + int raw = ((msb << 16) | (lsb << 8) | xlsb) >> 4; + // println("DBG: Raw Temp: " + (raw & 0xFFFF) + ", " + raw + String.format(", msb: 0x%04X lsb: 0x%04X xlsb: 0x%04X", msb, lsb, xlsb)); + return raw; + } + + private int readRawPressure() { + // Returns the raw (uncompensated) pressure + int msb = readU8(BME280_REGISTER_PRESSURE_DATA); + int lsb = readU8(BME280_REGISTER_PRESSURE_DATA + 1); + int xlsb = readU8(BME280_REGISTER_PRESSURE_DATA + 2); + int raw = ((msb << 16) | (lsb << 8) | xlsb) >> 4; + // println("DBG: Raw Press: " + (raw & 0xFFFF) + ", " + raw + String.format(", msb: 0x%04X lsb: 0x%04X xlsb: 0x%04X", msb, lsb, xlsb)); + return raw; + } + + private int readRawHumidity() { + // Returns the raw (uncompensated) humidity + int msb = readU8(BME280_REGISTER_HUMIDITY_DATA); + int lsb = readU8(BME280_REGISTER_HUMIDITY_DATA + 1); + int raw = (msb << 8) | lsb; + return raw; + } + + private int readU16LE(int register) { + super.beginTransmission(address); + super.write((byte)register); + byte[] ba = super.read(2); + super.endTransmission(); + return ((ba[1] & 0xFF) << 8) + (ba[0] & 0xFF); // Little Endian + } + + private int readS16LE(int register) { + super.beginTransmission(address); + super.write((byte)register); + byte[] ba = super.read(2); + super.endTransmission(); + + int lo = ba[0] & 0xFF; + int hi = ba[1] & 0xFF; + if (hi > 127) + hi -= 256; + return (hi << 8) + lo; // Little Endian + } + + private int readU8(int register) { + super.beginTransmission(address); + super.write(register); + byte[] ba = super.read(1); + super.endTransmission(); + return (int)(ba[0] & 0xFF); + } + + private int readS8(int register) { + int val = readU8(register); + if (val > 127) + val -= 256; + return val; + } + + private String rpad(String s, int len, String pad) { + String str = s; + while (str.length() < len) { + str += pad; + } + return str; + } + + private String lpad(String s, int len, String pad) { + String str = s; + while (str.length() < len) { + str = pad + str; + } + return str; + } +} diff --git a/Processing/Lib/io/examples/Environment_I2C_BME280/Environment_I2C_BME280.pde b/Processing/Lib/io/examples/Environment_I2C_BME280/Environment_I2C_BME280.pde new file mode 100644 index 0000000..a5ec806 --- /dev/null +++ b/Processing/Lib/io/examples/Environment_I2C_BME280/Environment_I2C_BME280.pde @@ -0,0 +1,29 @@ +import processing.io.*; +BME280 bme280; + +// see setup.png in the sketch folder for wiring details + +void setup() { + size(720, 320); + textSize(72); + + //printArray(I2C.list()); + bme280 = new BME280("i2c-1", 0x77); +} + +void draw() { + background(0); + stroke(255); + + bme280.update(); + float temp = bme280.temperature(); + float hum = bme280.humidity(); + float press = bme280.pressure(); + text(String.format("Temp: %.02f\272C", temp), 10, 75); + text(String.format("Hum: %.02f %%", hum), 10, 150); + text(String.format("Press: %.02f hPa", press / 100f), 10, 225); + + // pressure can be used to calculate the altitude like so + float alt = bme280.altitude(press, temp); + text(String.format("Alt: %.02f m", alt), 10, 300); +} diff --git a/Processing/Lib/io/examples/Environment_I2C_BME280/setup.png b/Processing/Lib/io/examples/Environment_I2C_BME280/setup.png new file mode 100644 index 0000000..cb37a3a Binary files /dev/null and b/Processing/Lib/io/examples/Environment_I2C_BME280/setup.png differ diff --git a/Processing/Lib/io/examples/Interrupt/Interrupt.pde b/Processing/Lib/io/examples/Interrupt/Interrupt.pde new file mode 100644 index 0000000..15bdc4c --- /dev/null +++ b/Processing/Lib/io/examples/Interrupt/Interrupt.pde @@ -0,0 +1,25 @@ +import processing.io.*; +color bgcolor = 0; + +// GPIO numbers refer to different phyiscal pins on various boards +// On the Raspberry Pi GPIO 4 is physical pin 7 on the header +// see setup.png in the sketch folder for wiring details + +void setup() { + GPIO.pinMode(4, GPIO.INPUT); + GPIO.attachInterrupt(4, this, "pinEvent", GPIO.RISING); +} + +void draw() { + background(bgcolor); +} + +// this function will be called whenever GPIO 4 is brought from LOW to HIGH +void pinEvent(int pin) { + println("Received interrupt"); + if (bgcolor == 0) { + bgcolor = color(255); + } else { + bgcolor = color(0); + } +} diff --git a/Processing/Lib/io/examples/Interrupt/setup.png b/Processing/Lib/io/examples/Interrupt/setup.png new file mode 100644 index 0000000..eb0a003 Binary files /dev/null and b/Processing/Lib/io/examples/Interrupt/setup.png differ diff --git a/Processing/Lib/io/examples/LedCounter/LedCounter.pde b/Processing/Lib/io/examples/LedCounter/LedCounter.pde new file mode 100644 index 0000000..3cd440b --- /dev/null +++ b/Processing/Lib/io/examples/LedCounter/LedCounter.pde @@ -0,0 +1,39 @@ +import processing.io.*; +LED leds[]; + +// the Raspberry Pi has two build-in LEDs we can control +// led0 (green) and led1 (red) + +void setup() { + String available[] = LED.list(); + print("Available: "); + println(available); + + // create an object for each LED and store it in an array + leds = new LED[available.length]; + for (int i=0; i < available.length; i++) { + leds[i] = new LED(available[i]); + } + + frameRate(1); +} + +void draw() { + // make the LEDs count in binary + for (int i=0; i < leds.length; i++) { + if ((frameCount & (1 << i)) != 0) { + leds[i].brightness(1.0); + } else { + leds[i].brightness(0.0); + } + } + println(frameCount); +} + +void keyPressed() { + // cleanup + for (int i=0; i < leds.length; i++) { + leds[i].close(); + } + exit(); +} diff --git a/Processing/Lib/io/examples/Light_I2C_TSL2561/Light_I2C_TSL2561.pde b/Processing/Lib/io/examples/Light_I2C_TSL2561/Light_I2C_TSL2561.pde new file mode 100644 index 0000000..c6c9a28 --- /dev/null +++ b/Processing/Lib/io/examples/Light_I2C_TSL2561/Light_I2C_TSL2561.pde @@ -0,0 +1,27 @@ +import processing.io.*; +TSL2561 sensor; + +// see setup.png in the sketch folder for wiring details + +// this variable will contain the measured brightness +// Lux (lx) is the unit of illuminance +float lux; + +void setup() { + size(700, 100); + textSize(72); + //printArray(I2C.list()); + sensor = new TSL2561("i2c-1", 0x39); +} + +void draw() { + background(0); + stroke(255); + lux = sensor.lux(); + text(String.format("Light: %.02f Lux", lux), 10, 75); +} + +void dispose() { + // turn the sensor off + sensor.stop(); +} diff --git a/Processing/Lib/io/examples/Light_I2C_TSL2561/TSL2561.pde b/Processing/Lib/io/examples/Light_I2C_TSL2561/TSL2561.pde new file mode 100644 index 0000000..775b9d0 --- /dev/null +++ b/Processing/Lib/io/examples/Light_I2C_TSL2561/TSL2561.pde @@ -0,0 +1,187 @@ +import processing.io.I2C; + +// TSL2561 is light sensor using I2C +// datasheet: https://cdn-shop.adafruit.com/datasheets/TSL2561.pdf +// code contributed by @OlivierLD + +public class TSL2561 extends I2C { + + public final static int TSL2561_ADDRESS = 0x39; + + public final static int TSL2561_ADDRESS_LOW = 0x29; + public final static int TSL2561_ADDRESS_FLOAT = 0x39; + public final static int TSL2561_ADDRESS_HIGH = 0x49; + + public final static int TSL2561_COMMAND_BIT = 0x80; + public final static int TSL2561_WORD_BIT = 0x20; + public final static int TSL2561_CONTROL_POWERON = 0x03; + public final static int TSL2561_CONTROL_POWEROFF = 0x00; + + public final static int TSL2561_REGISTER_CONTROL = 0x00; + public final static int TSL2561_REGISTER_TIMING = 0x01; + public final static int TSL2561_REGISTER_CHAN0_LOW = 0x0C; + public final static int TSL2561_REGISTER_CHAN0_HIGH = 0x0D; + public final static int TSL2561_REGISTER_CHAN1_LOW = 0x0E; + public final static int TSL2561_REGISTER_CHAN1_HIGH = 0x0F; + public final static int TSL2561_REGISTER_ID = 0x0A; + + public final static int TSL2561_GAIN_1X = 0x00; + public final static int TSL2561_GAIN_16X = 0x10; + + public final static int TSL2561_INTEGRATIONTIME_13MS = 0x00; // rather 13.7ms + public final static int TSL2561_INTEGRATIONTIME_101MS = 0x01; + public final static int TSL2561_INTEGRATIONTIME_402MS = 0x02; + + public final static double TSL2561_LUX_K1C = 0.130; // (0x0043) // 0.130 * 2^RATIO_SCALE + public final static double TSL2561_LUX_B1C = 0.0315; // (0x0204) // 0.0315 * 2^LUX_SCALE + public final static double TSL2561_LUX_M1C = 0.0262; // (0x01ad) // 0.0262 * 2^LUX_SCALE + public final static double TSL2561_LUX_K2C = 0.260; // (0x0085) // 0.260 * 2^RATIO_SCALE + public final static double TSL2561_LUX_B2C = 0.0337; // (0x0228) // 0.0337 * 2^LUX_SCALE + public final static double TSL2561_LUX_M2C = 0.0430; // (0x02c1) // 0.0430 * 2^LUX_SCALE + public final static double TSL2561_LUX_K3C = 0.390; // (0x00c8) // 0.390 * 2^RATIO_SCALE + public final static double TSL2561_LUX_B3C = 0.0363; // (0x0253) // 0.0363 * 2^LUX_SCALE + public final static double TSL2561_LUX_M3C = 0.0529; // (0x0363) // 0.0529 * 2^LUX_SCALE + public final static double TSL2561_LUX_K4C = 0.520; // (0x010a) // 0.520 * 2^RATIO_SCALE + public final static double TSL2561_LUX_B4C = 0.0392; // (0x0282) // 0.0392 * 2^LUX_SCALE + public final static double TSL2561_LUX_M4C = 0.0605; // (0x03df) // 0.0605 * 2^LUX_SCALE + public final static double TSL2561_LUX_K5C = 0.65; // (0x014d) // 0.65 * 2^RATIO_SCALE + public final static double TSL2561_LUX_B5C = 0.0229; // (0x0177) // 0.0229 * 2^LUX_SCALE + public final static double TSL2561_LUX_M5C = 0.0291; // (0x01dd) // 0.0291 * 2^LUX_SCALE + public final static double TSL2561_LUX_K6C = 0.80; // (0x019a) // 0.80 * 2^RATIO_SCALE + public final static double TSL2561_LUX_B6C = 0.0157; // (0x0101) // 0.0157 * 2^LUX_SCALE + public final static double TSL2561_LUX_M6C = 0.0180; // (0x0127) // 0.0180 * 2^LUX_SCALE + public final static double TSL2561_LUX_K7C = 1.3; // (0x029a) // 1.3 * 2^RATIO_SCALE + public final static double TSL2561_LUX_B7C = 0.00338; // (0x0037) // 0.00338 * 2^LUX_SCALE + public final static double TSL2561_LUX_M7C = 0.00260; // (0x002b) // 0.00260 * 2^LUX_SCALE + public final static double TSL2561_LUX_K8C = 1.3; // (0x029a) // 1.3 * 2^RATIO_SCALE + public final static double TSL2561_LUX_B8C = 0.000; // (0x0000) // 0.000 * 2^LUX_SCALE + public final static double TSL2561_LUX_M8C = 0.000; // (0x0000) // 0.000 * 2^LUX_SCALE + + private int gain = TSL2561_GAIN_1X; + private int integration = TSL2561_INTEGRATIONTIME_402MS; + private int pause = 800; + + private int address; + + + public TSL2561(String dev) { + this(dev, TSL2561_ADDRESS); + } + + public TSL2561(String dev, int address) { + super(dev); + this.address = address; + start(); + } + + public void start() { + command(TSL2561_COMMAND_BIT, (byte) TSL2561_CONTROL_POWERON); + } + + public void stop() { + command(TSL2561_COMMAND_BIT, (byte) TSL2561_CONTROL_POWEROFF); + } + + public void setGain() { + setGain(TSL2561_GAIN_1X); + } + + public void setGain(int gain) { + setGain(gain, TSL2561_INTEGRATIONTIME_402MS); + } + + public void setGain(int gain, int integration) { + if (gain != TSL2561_GAIN_1X && gain != TSL2561_GAIN_16X) { + throw new IllegalArgumentException("Invalid gain value"); + } + if (gain != this.gain || integration != this.integration) { + command(TSL2561_COMMAND_BIT | TSL2561_REGISTER_TIMING, (byte) (gain | integration)); + //println("Setting low gain"); + this.gain = gain; + this.integration = integration; + delay(pause); // pause for integration (pause must be bigger than integration time) + } + } + + /** + * Read visible+IR diode from the I2C device + */ + public int readFull() { + int reg = TSL2561_COMMAND_BIT | TSL2561_REGISTER_CHAN0_LOW; + return readU16(reg); + } + + /** + * Read IR only diode from the I2C device + */ + public int readIR() { + int reg = TSL2561_COMMAND_BIT | TSL2561_REGISTER_CHAN1_LOW; + return readU16(reg); + } + + /** + * Device lux range 0.1 - 40,000+ + * see https://learn.adafruit.com/tsl2561/overview + */ + public float lux() { + int ambient = this.readFull(); + int ir = this.readIR(); + + //println("IR Result: " + ir); + //println("Ambient Result: " + ambient); + + if (ambient >= 0xffff || ir >= 0xffff) { + throw new RuntimeException("Gain too high, values exceed range"); + } + double ratio = (ir / (float) ambient); + + /* + * For the values below, see https://github.com/adafruit/_TSL2561/blob/master/_TSL2561_U.h + */ + float lux = 0.0f; + if ((ratio >= 0) && (ratio <= TSL2561_LUX_K4C)) { + lux = (float)((TSL2561_LUX_B1C * ambient) - (0.0593 * ambient * (Math.pow(ratio, 1.4)))); + } else if (ratio <= TSL2561_LUX_K5C) { + lux = (float)((TSL2561_LUX_B5C * ambient) - (TSL2561_LUX_M5C * ir)); + } else if (ratio <= TSL2561_LUX_K6C) { + lux = (float)((TSL2561_LUX_B6C * ambient) - (TSL2561_LUX_M6C * ir)); + } else if (ratio <= TSL2561_LUX_K7C) { + lux = (float)((TSL2561_LUX_B7C * ambient) - (TSL2561_LUX_M7C * ir)); + } else if (ratio > TSL2561_LUX_K8C) { + lux = 0.0f; + } + return lux; + } + + + private void command(int register, byte value) { + beginTransmission(address); + write(register); + write(value); + endTransmission(); + } + + private int readU8(int register) { + beginTransmission(this.address); + write(register); + byte[] ba = read(1); + endTransmission(); + return (int)(ba[0] & 0xFF); + } + + private int readU16(int register) { + int lo = readU8(register); + int hi = readU8(register + 1); + int result = (hi << 8) + lo; // Big Endian + //println("(U16) I2C: Device " + toHex(TSL2561_ADDRESS) + " returned " + toHex(result) + " from reg " + toHex(register)); + return result; + } + + private String toHex(int i) { + String s = Integer.toString(i, 16).toUpperCase(); + while (s.length() % 2 != 0) { + s = "0" + s; + } + return "0x" + s; + } +} diff --git a/Processing/Lib/io/examples/Light_I2C_TSL2561/setup.png b/Processing/Lib/io/examples/Light_I2C_TSL2561/setup.png new file mode 100644 index 0000000..a0b33fe Binary files /dev/null and b/Processing/Lib/io/examples/Light_I2C_TSL2561/setup.png differ diff --git a/Processing/Lib/io/examples/Servo_I2C_PCA9685/PCA9685.pde b/Processing/Lib/io/examples/Servo_I2C_PCA9685/PCA9685.pde new file mode 100644 index 0000000..67ea815 --- /dev/null +++ b/Processing/Lib/io/examples/Servo_I2C_PCA9685/PCA9685.pde @@ -0,0 +1,148 @@ +import processing.io.I2C; + +// PCA9685 is a 16-channel servo/PWM driver +// datasheet: https://cdn-shop.adafruit.com/datasheets/PCA9685.pdf +// code contributed by @OlivierLD + +public class PCA9685 extends I2C { + public final static int PCA9685_ADDRESS = 0x40; + + // registers used + public final static int MODE1 = 0x00; + public final static int PRESCALE = 0xFE; + public final static int LED0_ON_L = 0x06; + public final static int LED0_ON_H = 0x07; + public final static int LED0_OFF_L = 0x08; + public final static int LED0_OFF_H = 0x09; + + private int address; + private int freq = 200; // 200 Hz default frequency (after power-up) + private boolean hasFreqSet = false; // whether a different frequency has been set + private int minPulses[] = new int[16]; + private int maxPulses[] = new int[16]; + + + public PCA9685(String dev) { + this(dev, PCA9685_ADDRESS); + } + public PCA9685(String dev, int address) { + super(dev); + this.address = address; + // reset device + command(MODE1, (byte) 0x00); + } + + + public void attach(int channel) { + // same as on Arduino + attach(channel, 544, 2400); + } + + public void attach(int channel, int minPulse, int maxPulse) { + if (channel < 0 || 15 < channel) { + throw new IllegalArgumentException("Channel must be between 0 and 15"); + } + minPulses[channel] = minPulse; + maxPulses[channel] = maxPulse; + + // set the PWM frequency to be the same as on Arduino + if (!hasFreqSet) { + frequency(50); + } + } + + public void write(int channel, float angle) { + if (channel < 0 || 15 < channel) { + throw new IllegalArgumentException("Channel must be between 0 and 15"); + } + if (angle < 0 || 180 < angle) { + throw new IllegalArgumentException("Angle must be between 0 and 180"); + } + int us = (int)(minPulses[channel] + (angle/180.0) * (maxPulses[channel]-minPulses[channel])); + + double pulseLength = 1000000; // 1s = 1,000,000 us per pulse + pulseLength /= freq; // 40..1000 Hz + pulseLength /= 4096; // 12 bits of resolution + int pulse = us; + pulse /= pulseLength; + // println(pulseLength + " us per bit, pulse:" + pulse); + pwm(channel, 0, pulse); + } + + public boolean attached(int channel) { + if (channel < 0 || 15 < channel) { + return false; + } + return (maxPulses[channel] != 0) ? true : false; + } + + public void detach(int channel) { + pwm(channel, 0, 0); + minPulses[channel] = 0; + maxPulses[channel] = 0; + } + + + /** + * @param freq 40..1000 Hz + */ + public void frequency(int freq) { + this.freq = freq; + float preScaleVal = 25000000.0f; // 25MHz + preScaleVal /= 4096.0; // 4096: 12-bit + preScaleVal /= freq; + preScaleVal -= 1.0; + // println("Setting PWM frequency to " + freq + " Hz"); + // println("Estimated pre-scale: " + preScaleVal); + double preScale = Math.floor(preScaleVal + 0.5); + // println("Final pre-scale: " + preScale); + byte oldmode = (byte) readU8(MODE1); + byte newmode = (byte) ((oldmode & 0x7F) | 0x10); // sleep + command(MODE1, newmode); // go to sleep + command(PRESCALE, (byte) (Math.floor(preScale))); + command(MODE1, oldmode); + delay(5); + command(MODE1, (byte) (oldmode | 0x80)); + hasFreqSet = true; + } + + /** + * @param channel 0..15 + * @param on cycle offset to turn output on (0..4095) + * @param off cycle offset to turn output off again (0..4095) + */ + public void pwm(int channel, int on, int off) { + if (channel < 0 || 15 < channel) { + throw new IllegalArgumentException("Channel must be between 0 and 15"); + } + if (on < 0 || 4095 < on) { + throw new IllegalArgumentException("On must be between 0 and 4095"); + } + if (off < 0 || 4095 < off) { + throw new IllegalArgumentException("Off must be between 0 and 4095"); + } + if (off < on) { + throw new IllegalArgumentException("Off must be greater than On"); + } + command(LED0_ON_L + 4 * channel, (byte) (on & 0xFF)); + command(LED0_ON_H + 4 * channel, (byte) (on >> 8)); + command(LED0_OFF_L + 4 * channel, (byte) (off & 0xFF)); + command(LED0_OFF_H + 4 * channel, (byte) (off >> 8)); + } + + + private void command(int register, byte value) { + beginTransmission(address); + write(register); + write(value); + endTransmission(); + } + + private byte readU8(int register) { + beginTransmission(address); + write(register); + byte[] ba = read(1); + endTransmission(); + return (byte)(ba[0] & 0xFF); + } +} diff --git a/Processing/Lib/io/examples/Servo_I2C_PCA9685/Servo_I2C_PCA9685.pde b/Processing/Lib/io/examples/Servo_I2C_PCA9685/Servo_I2C_PCA9685.pde new file mode 100644 index 0000000..ede995b --- /dev/null +++ b/Processing/Lib/io/examples/Servo_I2C_PCA9685/Servo_I2C_PCA9685.pde @@ -0,0 +1,41 @@ +import processing.io.*; +PCA9685 servos; + +// see setup.png in the sketch folder for wiring details + +void setup() { + size(400, 300); + //printArray(I2C.list()); + servos = new PCA9685("i2c-1", 0x40); + + // different servo motors will vary in the pulse width they expect + // the lines below set the pulse width for 0 degrees to 544 microseconds (μs) + // and the pulse width for 180 degrees to 2400 microseconds + // these values match the defaults of the Servo library on Arduino + // but you might need to modify this for your particular servo still + servos.attach(0, 544, 2400); + servos.attach(1, 544, 2400); +} + +void draw() { + background(0); + stroke(255); + strokeWeight(3); + + // we don't go right to the edge to prevent + // making the servo unhappy + float angle = 90 + sin(frameCount / 100.0)*85; + servos.write(0, angle); + float y = map(angle, 0, 180, 0, height); + line(0, y, width/2, y); + + angle = 90 + cos(frameCount / 100.0)*85; + servos.write(1, 90 + cos(frameCount / 100.0)*85); + y = map(angle, 0, 180, 0, height); + line(width/2, y, width, y); +} + +void dispose() { + servos.detach(0); + servos.detach(1); +} diff --git a/Processing/Lib/io/examples/Servo_I2C_PCA9685/setup.png b/Processing/Lib/io/examples/Servo_I2C_PCA9685/setup.png new file mode 100644 index 0000000..77b0b74 Binary files /dev/null and b/Processing/Lib/io/examples/Servo_I2C_PCA9685/setup.png differ diff --git a/Processing/Lib/io/examples/SimpleI2C/SimpleI2C.pde b/Processing/Lib/io/examples/SimpleI2C/SimpleI2C.pde new file mode 100644 index 0000000..75a5bac --- /dev/null +++ b/Processing/Lib/io/examples/SimpleI2C/SimpleI2C.pde @@ -0,0 +1,30 @@ +import processing.io.*; +I2C i2c; + +// MCP4725 is a Digital-to-Analog converter using I2C +// datasheet: http://ww1.microchip.com/downloads/en/DeviceDoc/22039d.pdf + +// also see DigitalAnalog_I2C_MCP4725 for how to write the +// same sketch in an object-oriented way + +void setup() { + //printArray(I2C.list()); + i2c = new I2C(I2C.list()[0]); +} + +void draw() { + background(map(mouseX, 0, width, 0, 255)); + setAnalog(map(mouseX, 0, width, 0.0, 1.0)); +} + +// outputs voltages from 0V to the supply voltage +// (works with 3.3V and 5V) +void setAnalog(float fac) { + fac = constrain(fac, 0.0, 1.0); + // convert to 12 bit value + int val = int(4095 * fac); + i2c.beginTransmission(0x60); + i2c.write(val >> 8); + i2c.write(val & 255); + i2c.endTransmission(); +} diff --git a/Processing/Lib/io/examples/SimpleInput/SimpleInput.pde b/Processing/Lib/io/examples/SimpleInput/SimpleInput.pde new file mode 100644 index 0000000..5b6f568 --- /dev/null +++ b/Processing/Lib/io/examples/SimpleInput/SimpleInput.pde @@ -0,0 +1,24 @@ +import processing.io.*; + +// GPIO numbers refer to different phyiscal pins on various boards +// On the Raspberry Pi GPIO 4 is physical pin 7 on the header +// see setup.png in the sketch folder for wiring details + +void setup() { + // INPUT_PULLUP enables the built-in pull-up resistor for this pin + // left alone, the pin will read as HIGH + // connected to ground (via e.g. a button or switch) it will read LOW + GPIO.pinMode(4, GPIO.INPUT_PULLUP); +} + +void draw() { + if (GPIO.digitalRead(4) == GPIO.LOW) { + // button is pressed + fill(255); + } else { + // button is not pressed + fill(204); + } + stroke(255); + ellipse(width/2, height/2, width*0.75, height*0.75); +} diff --git a/Processing/Lib/io/examples/SimpleInput/setup.png b/Processing/Lib/io/examples/SimpleInput/setup.png new file mode 100644 index 0000000..eb0a003 Binary files /dev/null and b/Processing/Lib/io/examples/SimpleInput/setup.png differ diff --git a/Processing/Lib/io/examples/SimpleOutput/SimpleOutput.pde b/Processing/Lib/io/examples/SimpleOutput/SimpleOutput.pde new file mode 100644 index 0000000..af5e05d --- /dev/null +++ b/Processing/Lib/io/examples/SimpleOutput/SimpleOutput.pde @@ -0,0 +1,25 @@ +import processing.io.*; +boolean ledOn = false; + +// GPIO numbers refer to different phyiscal pins on various boards +// On the Raspberry Pi GPIO 4 is physical pin 7 on the header +// see setup.png in the sketch folder for wiring details + +void setup() { + GPIO.pinMode(4, GPIO.OUTPUT); + frameRate(0.5); +} + +void draw() { + // make the LED blink + ledOn = !ledOn; + if (ledOn) { + GPIO.digitalWrite(4, GPIO.LOW); + fill(204); + } else { + GPIO.digitalWrite(4, GPIO.HIGH); + fill(255); + } + stroke(255); + ellipse(width/2, height/2, width*0.75, height*0.75); +} diff --git a/Processing/Lib/io/examples/SimpleOutput/setup.png b/Processing/Lib/io/examples/SimpleOutput/setup.png new file mode 100644 index 0000000..8438aba Binary files /dev/null and b/Processing/Lib/io/examples/SimpleOutput/setup.png differ diff --git a/Processing/Lib/io/examples/SimpleResistorSensor/SimpleResistorSensor.pde b/Processing/Lib/io/examples/SimpleResistorSensor/SimpleResistorSensor.pde new file mode 100644 index 0000000..874b699 --- /dev/null +++ b/Processing/Lib/io/examples/SimpleResistorSensor/SimpleResistorSensor.pde @@ -0,0 +1,56 @@ +import processing.io.*; + +// using a capacitor that gets charged and discharged, while +// measuring the time it takes, is an inexpensive way to +// read the value of an (analog) resistive sensor, such as +// a photocell +// kudos to ladyada for the original tutorial + +// see setup.png in the sketch folder for wiring details + +int max = 0; +int min = 9999; + +void setup() { +} + +void draw() { + int val = sensorRead(4); + println(val); + + // track largest and smallest reading, to get a sense + // how we compare + if (max < val) { + max = val; + } + if (val < min) { + min = val; + } + + // convert current reading into a number between 0.0 and 1.0 + float frac = map(val, min, max, 0.0, 1.0); + + background(255 * frac); +} + +int sensorRead(int pin) { + // discharge the capacitor + GPIO.pinMode(pin, GPIO.OUTPUT); + GPIO.digitalWrite(pin, GPIO.LOW); + delay(100); + // now the capacitor should be empty + + // measure the time takes to fill it + // up to ~ 1.4V again + GPIO.pinMode(pin, GPIO.INPUT); + int start = millis(); + while (GPIO.digitalRead(pin) == GPIO.LOW) { + // wait + } + + // return the time elapsed + // this will vary based on the value of the + // resistive sensor (lower resistance will + // make the capacitor charge faster) + return millis() - start; +} diff --git a/Processing/Lib/io/examples/SimpleResistorSensor/setup.png b/Processing/Lib/io/examples/SimpleResistorSensor/setup.png new file mode 100644 index 0000000..bcad945 Binary files /dev/null and b/Processing/Lib/io/examples/SimpleResistorSensor/setup.png differ diff --git a/Processing/Lib/io/examples/SimpleSPI/SimpleSPI.pde b/Processing/Lib/io/examples/SimpleSPI/SimpleSPI.pde new file mode 100644 index 0000000..a1b4e2b --- /dev/null +++ b/Processing/Lib/io/examples/SimpleSPI/SimpleSPI.pde @@ -0,0 +1,25 @@ +import processing.io.*; +SPI spi; + +// MCP3001 is a Analog-to-Digital converter using SPI +// datasheet: http://ww1.microchip.com/downloads/en/DeviceDoc/21293C.pdf +// see setup.png in the sketch folder for wiring details + +// also see AnalogDigital_SPI_MCP3001 for how to write the +// same sketch in an object-oriented way + +void setup() { + //printArray(SPI.list()); + spi = new SPI(SPI.list()[0]); + spi.settings(500000, SPI.MSBFIRST, SPI.MODE0); +} + +void draw() { + // dummy write, actual values don't matter + byte[] out = { 0, 0 }; + byte[] in = spi.transfer(out); + // some input bit shifting according to the datasheet p. 16 + int val = ((in[0] & 0x1f) << 5) | ((in[1] & 0xf8) >> 3); + // val is between 0 and 1023 + background(map(val, 0, 1023, 0, 255)); +} diff --git a/Processing/Lib/io/examples/SimpleSPI/setup.png b/Processing/Lib/io/examples/SimpleSPI/setup.png new file mode 100644 index 0000000..c52f67f Binary files /dev/null and b/Processing/Lib/io/examples/SimpleSPI/setup.png differ diff --git a/Processing/Lib/io/examples/SoftwareServoSweep/SoftwareServoSweep.pde b/Processing/Lib/io/examples/SoftwareServoSweep/SoftwareServoSweep.pde new file mode 100644 index 0000000..a09d52a --- /dev/null +++ b/Processing/Lib/io/examples/SoftwareServoSweep/SoftwareServoSweep.pde @@ -0,0 +1,34 @@ +import processing.io.*; + +// see setup.png in the sketch folder for wiring details +// for more reliable operation it is recommended to power +// the servo from an external power source, see setup_better.png + +SoftwareServo servo1; +SoftwareServo servo2; + +void setup() { + size(400, 300); + servo1 = new SoftwareServo(this); + servo1.attach(17); + servo2 = new SoftwareServo(this); + servo2.attach(4); +} + +void draw() { + background(0); + stroke(255); + strokeWeight(3); + + // we don't go right to the edge to prevent + // making the servo unhappy + float angle = 90 + sin(frameCount / 100.0)*85; + servo1.write(angle); + float y = map(angle, 0, 180, 0, height); + line(0, y, width/2, y); + + angle = 90 + cos(frameCount / 100.0)*85; + servo2.write(90 + cos(frameCount / 100.0)*85); + y = map(angle, 0, 180, 0, height); + line(width/2, y, width, y); +} diff --git a/Processing/Lib/io/examples/SoftwareServoSweep/setup.png b/Processing/Lib/io/examples/SoftwareServoSweep/setup.png new file mode 100644 index 0000000..2370f37 Binary files /dev/null and b/Processing/Lib/io/examples/SoftwareServoSweep/setup.png differ diff --git a/Processing/Lib/io/examples/SoftwareServoSweep/setup_better.png b/Processing/Lib/io/examples/SoftwareServoSweep/setup_better.png new file mode 100644 index 0000000..7931e0d Binary files /dev/null and b/Processing/Lib/io/examples/SoftwareServoSweep/setup_better.png differ diff --git a/Processing/Lib/io/examples/Touch_I2C_MPR121/MPR121.pde b/Processing/Lib/io/examples/Touch_I2C_MPR121/MPR121.pde new file mode 100644 index 0000000..0ef0238 --- /dev/null +++ b/Processing/Lib/io/examples/Touch_I2C_MPR121/MPR121.pde @@ -0,0 +1,112 @@ +import processing.io.I2C; + +// MPR121 is a capacitive-touch sensor controller with 12 channels +// datasheet: https://www.nxp.com/docs/en/data-sheet/MPR121.pdf + +class MPR121 extends I2C { + int address; + int touched; + + // registers used (there are more) + static final int EFD0LB = 0x04; // ELE0 Electrode Filtered Data LSB + static final int E0TTH = 0x41; // ELE0 Touch Threshold + static final int E0RTH = 0x42; // ELE0 Release Threshold + static final int E0BV = 0x1e; // ELE0 Baseline Value + static final int MHDR = 0x2b; // MHD Rising + static final int NHDR = 0x2c; // NHD Amount Rising + static final int NCLR = 0x2d; // NCL Rising + static final int MHDF = 0x2f; // MHD Falling + static final int NHDF = 0x30; // NHD Amount Falling + static final int NCLF = 0x31; // NCL Falling + static final int CDT = 0x5d; // Filter/Global CDT Configuration + static final int ECR = 0x5e; // Electrode Configuration + static final int SRST = 0x80; // Soft Reset + + // there can be more than one device connected to the bus + // as long as they have different addresses + // possible addresses: 0x5a (default) - 0x5d + MPR121(String dev, int address) { + super(dev); + this.address = address; + reset(); + } + + void update() { + beginTransmission(address); + write(0x00); + byte[] in = read(2); + // & 0xff makes sure the byte is not interpreted as a negative value + touched = (in[1] & 0xff) << 8 | (in[0] & 0xff); + } + + boolean touched(int channel) { + if (channel < 0 || 11 < channel) { + return false; + } + if ((touched & (1 << channel)) != 0) { + return true; + } else { + return false; + } + } + + void threshold(int touch, int release) { + for (int i=0; i < 12; i++) { + threshold(touch, release, i); + } + } + + void threshold(int touch, int release, int channel) { + if (channel < 0 || 11 < channel) { + return; + } + touch = constrain(touch, 0, 255); + release = constrain(release, 0, 255); + writeRegister(E0TTH + 2*channel, touch); + writeRegister(E0RTH + 2*channel, release); + } + + int analogRead(int channel) { + if (channel < 0 || 11 < channel) { + return 0; + } + beginTransmission(address); + write(EFD0LB + 2*channel); + byte[] in = read(2); + return (in[1] & 0xff) << 8 | (in[0] & 0xff); + } + + int analogReadBaseline(int channel) { + if (channel < 0 || 11 < channel) { + return 0; + } + beginTransmission(address); + write(E0BV + channel); + byte[] in = read(1); + return (in[0] & 0xff) << 2; + } + + void reset() { + writeRegister(SRST, 0x63); + delay(1); + threshold(12, 6); + // set baseline filtering control registers (see p. 12) + writeRegister(MHDR, 0x01); + writeRegister(NHDR, 0x01); + writeRegister(NCLR, 0x0e); + writeRegister(MHDF, 0x01); + writeRegister(NHDF, 0x05); + writeRegister(NCLF, 0x01); + // change sample interval to 1ms period from default 16ms + writeRegister(CDT, 0x20); + // start sampling + writeRegister(ECR, 0x8f); + } + + void writeRegister(int register, int value) { + beginTransmission(address); + write(register); + write(value); + endTransmission(); + } +} diff --git a/Processing/Lib/io/examples/Touch_I2C_MPR121/Touch_I2C_MPR121.pde b/Processing/Lib/io/examples/Touch_I2C_MPR121/Touch_I2C_MPR121.pde new file mode 100644 index 0000000..58f69ad --- /dev/null +++ b/Processing/Lib/io/examples/Touch_I2C_MPR121/Touch_I2C_MPR121.pde @@ -0,0 +1,26 @@ +import processing.io.*; +MPR121 touch; + +// see setup.png in the sketch folder for wiring details + +void setup() { + size(600, 200); + //printArray(I2C.list()); + touch = new MPR121("i2c-1", 0x5a); +} + +void draw() { + background(204); + noStroke(); + + touch.update(); + + for (int i=0; i < 12; i++) { + if (touch.touched(i)) { + fill(255, 0, 0); + } else { + fill(255, 255, 255); + } + ellipse((width/12) * (i+0.5), height/2, 20, 20); + } +} diff --git a/Processing/Lib/io/examples/Touch_I2C_MPR121/setup.png b/Processing/Lib/io/examples/Touch_I2C_MPR121/setup.png new file mode 100644 index 0000000..65bb7a0 Binary files /dev/null and b/Processing/Lib/io/examples/Touch_I2C_MPR121/setup.png differ diff --git a/Processing/Lib/io/library.properties b/Processing/Lib/io/library.properties new file mode 100644 index 0000000..519c341 --- /dev/null +++ b/Processing/Lib/io/library.properties @@ -0,0 +1,9 @@ +name = Hardware I/O +authors = The Processing Foundation +url = http://processing.org/reference/libraries/io/index.html +categories = Hardware +sentence = Access peripherals on the Raspberry Pi and other Linux-based computers. +paragraph = For other platforms, this is solely provided in order to build and export sketches that require processing.io. +version = 1 +prettyVersion = 1 +minRevision = 247 diff --git a/Processing/Lib/io/library/export.txt b/Processing/Lib/io/library/export.txt new file mode 100644 index 0000000..4626bbf --- /dev/null +++ b/Processing/Lib/io/library/export.txt @@ -0,0 +1 @@ +name = Hardware I/O for Raspberry Pi and other Linux-based computers diff --git a/Processing/Lib/io/library/io.jar b/Processing/Lib/io/library/io.jar new file mode 100644 index 0000000..4e42f8c Binary files /dev/null and b/Processing/Lib/io/library/io.jar differ diff --git a/Processing/Lib/io/library/linux-arm64/libprocessing-io.so b/Processing/Lib/io/library/linux-arm64/libprocessing-io.so new file mode 100644 index 0000000..9659c68 Binary files /dev/null and b/Processing/Lib/io/library/linux-arm64/libprocessing-io.so differ diff --git a/Processing/Lib/io/library/linux-armv6hf/libprocessing-io.so b/Processing/Lib/io/library/linux-armv6hf/libprocessing-io.so new file mode 100644 index 0000000..9d272c4 Binary files /dev/null and b/Processing/Lib/io/library/linux-armv6hf/libprocessing-io.so differ diff --git a/Processing/Lib/io/library/linux32/libprocessing-io.so b/Processing/Lib/io/library/linux32/libprocessing-io.so new file mode 100644 index 0000000..acc2bd6 Binary files /dev/null and b/Processing/Lib/io/library/linux32/libprocessing-io.so differ diff --git a/Processing/Lib/io/library/linux64/libprocessing-io.so b/Processing/Lib/io/library/linux64/libprocessing-io.so new file mode 100644 index 0000000..4c2d715 Binary files /dev/null and b/Processing/Lib/io/library/linux64/libprocessing-io.so differ diff --git a/Processing/Lib/io/src/native/Makefile b/Processing/Lib/io/src/native/Makefile new file mode 100644 index 0000000..6a2c003 --- /dev/null +++ b/Processing/Lib/io/src/native/Makefile @@ -0,0 +1,20 @@ +TARGET := libprocessing-io.so +OBJS := impl.o +CC := gcc + +# prefix with -m32 to compile for linux32 +CFLAGS := -std=gnu99 -fPIC -g -ffast-math +CFLAGS += -I$(shell dirname $(shell realpath $(shell which javac)))/../include +CFLAGS += -I$(shell dirname $(shell realpath $(shell which javac)))/../include/linux +LDFLAGS := -shared + +$(TARGET): $(OBJS) + $(CC) $(CFLAGS) $(LDFLAGS) $^ -o $@ + +iface.h: + javah -classpath .. -o iface.h processing.io.NativeInterface + +clean: + rm -f $(TARGET) $(OBJS) + +.PHONY: iface.h clean diff --git a/Processing/Lib/io/src/native/iface.h b/Processing/Lib/io/src/native/iface.h new file mode 100644 index 0000000..74decc1 --- /dev/null +++ b/Processing/Lib/io/src/native/iface.h @@ -0,0 +1,133 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include +/* Header for class processing_io_NativeInterface */ + +#ifndef _Included_processing_io_NativeInterface +#define _Included_processing_io_NativeInterface +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: processing_io_NativeInterface + * Method: openDevice + * Signature: (Ljava/lang/String;)I + */ +JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_openDevice + (JNIEnv *, jclass, jstring); + +/* + * Class: processing_io_NativeInterface + * Method: getError + * Signature: (I)Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_processing_io_NativeInterface_getError + (JNIEnv *, jclass, jint); + +/* + * Class: processing_io_NativeInterface + * Method: closeDevice + * Signature: (I)I + */ +JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_closeDevice + (JNIEnv *, jclass, jint); + +/* + * Class: processing_io_NativeInterface + * Method: readFile + * Signature: (Ljava/lang/String;[B)I + */ +JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_readFile + (JNIEnv *, jclass, jstring, jbyteArray); + +/* + * Class: processing_io_NativeInterface + * Method: writeFile + * Signature: (Ljava/lang/String;[B)I + */ +JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_writeFile + (JNIEnv *, jclass, jstring, jbyteArray); + +/* + * Class: processing_io_NativeInterface + * Method: raspbianGpioMemRead + * Signature: (I)I + */ +JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_raspbianGpioMemRead + (JNIEnv *, jclass, jint); + +/* + * Class: processing_io_NativeInterface + * Method: raspbianGpioMemWrite + * Signature: (III)I + */ +JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_raspbianGpioMemWrite + (JNIEnv *, jclass, jint, jint, jint); + +/* + * Class: processing_io_NativeInterface + * Method: raspbianGpioMemWrite + * Signature: (II)I + */ +JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_raspbianGpioMemSetPinBias + (JNIEnv *, jclass, jint, jint); + +/* + * Class: processing_io_NativeInterface + * Method: pollDevice + * Signature: (Ljava/lang/String;I)I + */ +JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_pollDevice + (JNIEnv *, jclass, jstring, jint); + +/* + * Class: processing_io_NativeInterface + * Method: transferI2c + * Signature: (II[B[B)I + */ +JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_transferI2c + (JNIEnv *, jclass, jint, jint, jbyteArray, jbyteArray); + +/* + * Class: processing_io_NativeInterface + * Method: servoStartThread + * Signature: (III)J + */ +JNIEXPORT jlong JNICALL Java_processing_io_NativeInterface_servoStartThread + (JNIEnv *, jclass, jint, jint, jint); + +/* + * Class: processing_io_NativeInterface + * Method: servoUpdateThread + * Signature: (JII)I + */ +JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_servoUpdateThread + (JNIEnv *, jclass, jlong, jint, jint); + +/* + * Class: processing_io_NativeInterface + * Method: servoStopThread + * Signature: (J)I + */ +JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_servoStopThread + (JNIEnv *, jclass, jlong); + +/* + * Class: processing_io_NativeInterface + * Method: setSpiSettings + * Signature: (IIII)I + */ +JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_setSpiSettings + (JNIEnv *, jclass, jint, jint, jint, jint); + +/* + * Class: processing_io_NativeInterface + * Method: transferSpi + * Signature: (I[B[B)I + */ +JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_transferSpi + (JNIEnv *, jclass, jint, jbyteArray, jbyteArray); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/Processing/Lib/io/src/native/impl.c b/Processing/Lib/io/src/native/impl.c new file mode 100644 index 0000000..522aa31 --- /dev/null +++ b/Processing/Lib/io/src/native/impl.c @@ -0,0 +1,489 @@ +/* + Copyright (c) The Processing Foundation 2015 + Hardware I/O library developed by Gottfried Haider as part of GSoC 2015 + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "iface.h" + + +static const int servo_pulse_oversleep = 35; // amount of uS to account for when sleeping + + +JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_openDevice + (JNIEnv *env, jclass cls, jstring _fn) +{ + const char *fn = (*env)->GetStringUTFChars(env, _fn, JNI_FALSE); + int file = open(fn, O_RDWR); + (*env)->ReleaseStringUTFChars(env, _fn, fn); + if (file < 0) { + return -errno; + } else { + return file; + } +} + + +JNIEXPORT jstring JNICALL Java_processing_io_NativeInterface_getError + (JNIEnv *env, jclass cls, jint _errno) +{ + char *msg = strerror(abs(_errno)); + if (msg) { + return (*env)->NewStringUTF(env, msg); + } else { + return NULL; + } +} + + +JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_closeDevice + (JNIEnv *env, jclass cls, jint handle) +{ + if (close(handle) < 0) { + return -errno; + } else { + return 0; + } +} + + +JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_readFile + (JNIEnv *env, jclass cls, jstring _fn, jbyteArray _in) +{ + const char *fn = (*env)->GetStringUTFChars(env, _fn, JNI_FALSE); + int file = open(fn, O_RDONLY); + (*env)->ReleaseStringUTFChars(env, _fn, fn); + if (file < 0) { + return -errno; + } + + jbyte *in = (*env)->GetByteArrayElements(env, _in, NULL); + int len = read(file, in, (*env)->GetArrayLength(env, _in)); + if (len < 0) { + len = -errno; + } + (*env)->ReleaseByteArrayElements(env, _in, in, 0); + + close(file); + return len; +} + + +JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_writeFile + (JNIEnv *env, jclass cls, jstring _fn, jbyteArray _out) +{ + const char *fn = (*env)->GetStringUTFChars(env, _fn, JNI_FALSE); + int file = open(fn, O_WRONLY); + (*env)->ReleaseStringUTFChars(env, _fn, fn); + if (file < 0) { + return -errno; + } + + jbyte *out = (*env)->GetByteArrayElements(env, _out, JNI_FALSE); + int len = write(file, out, (*env)->GetArrayLength(env, _out)); + if (len < 0) { + len = -errno; + } + (*env)->ReleaseByteArrayElements(env, _out, out, JNI_ABORT); + + close(file); + return len; +} + + +JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_raspbianGpioMemRead + (JNIEnv *env, jclass cls, jint offset) +{ + // validate offset + if (4096 <= offset) { + return -EINVAL; + } + + int file = open("/dev/gpiomem", O_RDWR|O_SYNC); + if (file < 0) { + return -errno; + } + + uint32_t *mem = mmap(NULL, 4096, PROT_READ, MAP_SHARED, file, 0); + if (mem == MAP_FAILED) { + close(file); + return -errno; + } + + uint32_t value = mem[offset]; + + munmap(mem, 4096); + close(file); + return value; +} + + +JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_raspbianGpioMemWrite + (JNIEnv *env, jclass cls, jint offset, jint mask, jint value) +{ + // validate offset + if (4096 <= offset) { + return -EINVAL; + } + + int file = open("/dev/gpiomem", O_RDWR|O_SYNC); + if (file < 0) { + return -errno; + } + + uint32_t *mem = mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_SHARED, file, 0); + if (mem == MAP_FAILED) { + close(file); + return -errno; + } + + mem[offset] = (mem[offset] & ~mask) | (value & mask); + + munmap(mem, 4096); + close(file); + return 1; // number of bytes written +} + + +#define BCM2835_GPPUD_OFFSET (0x94 >> 2) +#define BCM2835_GPPUDCLK0_OFFSET (0x98 >> 2) +#define BCM2835_GPPUDCLK1_OFFSET (0x9c >> 2) + +JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_raspbianGpioMemSetPinBias + (JNIEnv *env, jclass cls, jint gpio, jint mode) +{ + int ret = 0; // success + + int file = open("/dev/gpiomem", O_RDWR|O_SYNC); + if (file < 0) { + return -errno; + } + + uint32_t *mem = mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_SHARED, file, 0); + if (mem == MAP_FAILED) { + close(file); + return -errno; + } + + // validate arguments + if (gpio < 0 || 53 < gpio) { + ret = -EINVAL; + goto out; + } + + // see BCM2835 datasheet, p. 101 + uint32_t pud; + if (mode == 0) { + pud = 0; // floating + } else if (mode == 2) { + pud = 2; // pull-up + } else if (mode == 3) { + pud = 1; // pull-down + } else { + ret = -EINVAL; + goto out; + } + + /* + * From the BCM2835 datasheet, p. 101: + * + * The following sequence of events is required: + * 1. Write to GPPUD to set the required control signal (i.e. Pull-up or + * Pull-Down or neither to remove the current Pull-up/down) + * 2. Wait 150 cycles – this provides the required set-up time for the + * control signal + * 3. Write to GPPUDCLK0/1 to clock the control signal into the GPIO pads + * you wish to modify – NOTE only the pads which receive a clock will + * be modified, all others will retain their previous state. + * 4. Wait 150 cycles – this provides the required hold time for the + * control signal + * 5. Write to GPPUD to remove the control signal + * 6. Write to GPPUDCLK0/1 to remove the clock + */ + + // python-gpiozero uses a delay of 214 ns, so we do the same + struct timespec wait; + wait.tv_sec = 0; + wait.tv_nsec = 214; + + mem[BCM2835_GPPUD_OFFSET] = pud; + nanosleep(&wait, NULL); + if (gpio < 32) { + mem[BCM2835_GPPUDCLK0_OFFSET] = 1 << gpio; + } else { + mem[BCM2835_GPPUDCLK1_OFFSET] = 1 << (gpio-32); + } + nanosleep(&wait, NULL); + mem[BCM2835_GPPUD_OFFSET] = 0; + if (gpio < 32) { + mem[BCM2835_GPPUDCLK0_OFFSET] = 0; + } else { + mem[BCM2835_GPPUDCLK1_OFFSET] = 0; + } + +out: + munmap(mem, 4096); + close(file); + return ret; +} + + +JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_pollDevice + (JNIEnv *env, jclass cls, jstring _fn, jint timeout) +{ + const char *fn = (*env)->GetStringUTFChars(env, _fn, JNI_FALSE); + int file = open(fn, O_RDONLY|O_NONBLOCK); + (*env)->ReleaseStringUTFChars(env, _fn, fn); + if (file < 0) { + return -errno; + } + + // dummy read + char tmp; + while (0 < read(file, &tmp, 1)); + + struct pollfd fds[1]; + memset(fds, 0, sizeof(fds)); + fds[0].fd = file; + fds[0].events = POLLPRI|POLLERR; + + // and wait + int ret = poll(fds, 1, timeout); + close(file); + + if (ret < 0) { + return -errno; + } else if (ret == 0) { + // timeout + return 0; + } else if (fds[0].revents & POLLPRI) { + // interrupt + return 1; + } else { + // POLLERR? + return -ENOMSG; + } +} + + +JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_transferI2c + (JNIEnv *env, jclass cls, jint handle, jint slave, jbyteArray _out, jbyteArray _in) +{ + struct i2c_rdwr_ioctl_data packets; + struct i2c_msg msgs[2]; + jbyte *out, *in; + + packets.msgs = msgs; + packets.nmsgs = 0; + + if (_out != NULL) { + msgs[packets.nmsgs].addr = slave; + msgs[packets.nmsgs].flags = 0; + msgs[packets.nmsgs].len = (*env)->GetArrayLength(env, _out); + out = (*env)->GetByteArrayElements(env, _out, NULL); + msgs[packets.nmsgs].buf = out; + packets.nmsgs++; + } + if (_in != NULL) { + msgs[packets.nmsgs].addr = slave; + msgs[packets.nmsgs].flags = I2C_M_RD; // I2C_M_RECV_LEN is not supported + msgs[packets.nmsgs].len = (*env)->GetArrayLength(env, _in); + in = (*env)->GetByteArrayElements(env, _in, NULL); + msgs[packets.nmsgs].buf = in; + packets.nmsgs++; + } + + // set the timeout to 100ms - this helps slow devices such as the + // Arduino Uno to keep up + ioctl(handle, I2C_TIMEOUT, 10); + int ret = ioctl(handle, I2C_RDWR, &packets); + if (ret < 0) { + ret = -errno; + } + + if (_out != NULL) { + (*env)->ReleaseByteArrayElements(env, _out, out, JNI_ABORT); + } + if (_in != NULL) { + (*env)->ReleaseByteArrayElements(env, _in, in, 0); + } + + return ret; +} + + +typedef struct { + int fd; + pthread_t thread; + int pulse; + int period; +} SERVO_STATE_T; + + +static void* servoThread(void *ptr) { + SERVO_STATE_T *state = (SERVO_STATE_T*)ptr; + struct timespec on, off; + on.tv_sec = 0; + off.tv_sec = 0; + + do { + write(state->fd, "1", 1); + + on.tv_nsec = state->pulse * 1000; + nanosleep(&on, NULL); + + write(state->fd, "0", 1); + + off.tv_nsec = (state->period - state->pulse) * 1000; + nanosleep(&off, NULL); + } while (1); +} + + +JNIEXPORT jlong JNICALL Java_processing_io_NativeInterface_servoStartThread + (JNIEnv *env, jclass cls, jint gpio, jint pulse, jint period) +{ + char path[26 + 19 + 1]; + int fd; + pthread_t thread; + + // setup struct holding our state + SERVO_STATE_T *state = malloc(sizeof(SERVO_STATE_T)); + if (!state) { + return -ENOMEM; + } + memset(state, 0, sizeof(*state)); + state->pulse = (pulse - servo_pulse_oversleep > 0) ? pulse - servo_pulse_oversleep : 0; + // we're obviously also oversleeping in the general period case + // but other than the pulse, this doesn't seem to be crucial with servos + state->period = period; + + // open gpio + sprintf(path, "/sys/class/gpio/gpio%d/value", gpio); + state->fd = open(path, O_WRONLY); + if (state->fd < 0) { + free(state); + return -errno; + } + + // start thread + int ret = pthread_create(&state->thread, NULL, servoThread, state); + if (ret != 0) { + free(state); + return -ret; + } + + // set scheduling policy and priority + struct sched_param param; + param.sched_priority = 75; + ret = pthread_setschedparam(state->thread, SCHED_FIFO, ¶m); + if (ret != 0) { + fprintf(stderr, "Error setting thread policy: %s\n", strerror(ret)); + } + + return (intptr_t)state; +} + + +JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_servoUpdateThread + (JNIEnv *env, jclass cls, jlong handle, jint pulse, jint period) +{ + SERVO_STATE_T *state = (SERVO_STATE_T*)(intptr_t)handle; + state->pulse = (pulse - servo_pulse_oversleep > 0) ? pulse - servo_pulse_oversleep : 0; + state->period = period; + return 0; +} + + +JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_servoStopThread + (JNIEnv *env, jclass cls, jlong handle) +{ + SERVO_STATE_T *state = (SERVO_STATE_T*)(intptr_t)handle; + + // signal thread to stop + pthread_cancel(state->thread); + pthread_join(state->thread, NULL); + + close(state->fd); + free(state); + return 0; +} + + +JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_setSpiSettings + (JNIEnv *env, jclass cls, jint handle, jint _maxSpeed, jint dataOrder, jint mode) +{ + uint8_t tmp; + uint32_t maxSpeed; + + tmp = (uint8_t)mode; + int ret = ioctl(handle, SPI_IOC_WR_MODE, &tmp); + if (ret < 0) { + return ret; + } + + tmp = (uint8_t)dataOrder; + ret = ioctl(handle, SPI_IOC_WR_LSB_FIRST, &tmp); + if (ret < 0) { + return ret; + } + + maxSpeed = (uint32_t)_maxSpeed; + ret = ioctl(handle, SPI_IOC_WR_MAX_SPEED_HZ, &maxSpeed); + if (ret < 0) { + return ret; + } + + return 0; +} + + +JNIEXPORT jint JNICALL Java_processing_io_NativeInterface_transferSpi + (JNIEnv *env, jclass cls, jint handle, jbyteArray _out, jbyteArray _in) +{ + jbyte* out = (*env)->GetByteArrayElements(env, _out, NULL); + jbyte* in = (*env)->GetByteArrayElements(env, _in, NULL); + + struct spi_ioc_transfer xfer = { + .tx_buf = (unsigned long)out, + .rx_buf = (unsigned long)in, + .len = MIN((*env)->GetArrayLength(env, _out), (*env)->GetArrayLength(env, _in)), + }; + + int ret = ioctl(handle, SPI_IOC_MESSAGE(1), &xfer); + + (*env)->ReleaseByteArrayElements(env, _out, out, JNI_ABORT); + (*env)->ReleaseByteArrayElements(env, _in, in, 0); + + return ret; +} diff --git a/Processing/Lib/io/src/processing/io/GPIO.java b/Processing/Lib/io/src/processing/io/GPIO.java new file mode 100644 index 0000000..711aafd --- /dev/null +++ b/Processing/Lib/io/src/processing/io/GPIO.java @@ -0,0 +1,529 @@ +/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + Copyright (c) The Processing Foundation 2015 + Hardware I/O library developed by Gottfried Haider as part of GSoC 2015 + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA +*/ + +package processing.io; + +import processing.core.*; +import processing.io.NativeInterface; + +import java.lang.reflect.Method; +import java.util.BitSet; +import java.util.HashMap; +import java.util.Map; + + +/** + * @webref + */ +public class GPIO { + + // those constants are generally the same as in Arduino.h + public static final int INPUT = 0; + public static final int OUTPUT = 1; + public static final int INPUT_PULLUP = 2; + public static final int INPUT_PULLDOWN = 3; + + public static final int LOW = 0; + public static final int HIGH = 1; + + public static final int NONE = 0; + /** + * trigger when level changes + */ + public static final int CHANGE = 1; + /** + * trigger when level changes from high to low + */ + public static final int FALLING = 2; + /** + * trigger when level changes from low to high + */ + public static final int RISING = 3; + + protected static Map irqThreads = new HashMap(); + protected static boolean serveInterrupts = true; + protected static BitSet values = new BitSet(); + + + static { + NativeInterface.loadLibrary(); + } + + + public static void analogWrite(int pin, int value) { + // currently this can't be done in a non-platform-specific way + // the best way forward would be implementing a generic, "soft" + // PWM in the kernel that uses high resolution timers, similiar + // to the patch Bill Gatliff posted, which unfortunately didn't + // get picked up, see + // https://dev.openwrt.org/browser/trunk/target/linux/generic/files/drivers/pwm/gpio-pwm.c?rev=35328 + + // additionally, there currently doesn't seem to be a way to link + // a PWM channel back to the GPIO pin it is associated with + + // alternatively, this could be implemented in user-space to some + // degree + // see http://stackoverflow.com/a/13371570/3030124 + // see http://raspberrypi.stackexchange.com/a/304 + throw new RuntimeException("Not yet implemented"); + } + + + /** + * Calls a function when the value of an input pin changes + * @param pin GPIO pin + * @param parent typically use "this" + * @param method name of sketch method to call + * @param mode when to call: GPIO.CHANGE, GPIO.FALLING or GPIO.RISING + * @see noInterrupts + * @see interrupts + * @see releaseInterrupt + * @webref + */ + public static void attachInterrupt(int pin, PApplet parent, String method, int mode) { + if (irqThreads.containsKey(pin)) { + throw new RuntimeException("You must call releaseInterrupt before attaching another interrupt on the same pin"); + } + + enableInterrupt(pin, mode); + + final int irqPin = pin; + final PApplet irqObject = parent; + final Method irqMethod; + try { + irqMethod = parent.getClass().getMethod(method, int.class); + } catch (NoSuchMethodException e) { + throw new RuntimeException("Method " + method + " does not exist"); + } + + // it might be worth checking how Java threads compare to pthreads in terms + // of latency + Thread t = new Thread(new Runnable() { + public void run() { + boolean gotInterrupt = false; + try { + do { + try { + if (waitForInterrupt(irqPin, 100)) { + gotInterrupt = true; + } + if (gotInterrupt && serveInterrupts) { + irqMethod.invoke(irqObject, irqPin); + gotInterrupt = false; + } + // if we received an interrupt while interrupts were disabled + // we still deliver it the next time interrupts get enabled + // not sure if everyone agrees with this logic though + } catch (RuntimeException e) { + // make sure we're not busy spinning on error + Thread.sleep(100); + } + } while (!Thread.currentThread().isInterrupted()); + } catch (Exception e) { + // terminate the thread on any unexpected exception that might occur + System.err.println("Terminating interrupt handling for pin " + irqPin + " after catching: " + e.getMessage()); + } + } + }, "GPIO" + pin + " IRQ"); + + t.setPriority(Thread.MAX_PRIORITY); + t.start(); + + irqThreads.put(pin, t); + } + + + /** + * Checks if the GPIO pin number can be valid + * + * Board-specific classes, such as RPI, assign -1 to pins that carry power, + * ground and the like. + * @param pin GPIO pin + */ + protected static void checkValidPin(int pin) { + if (pin < 0) { + throw new RuntimeException("Operation not supported on this pin"); + } + } + + + /** + * Returns the value of an input pin + * @param pin GPIO pin + * @return GPIO.HIGH (1) or GPIO.LOW (0) + * @see pinMode + * @see digitalWrite + * @webref + */ + public static int digitalRead(int pin) { + checkValidPin(pin); + + if (NativeInterface.isSimulated()) { + return LOW; + } + + String fn = String.format("/sys/class/gpio/gpio%d/value", pin); + byte in[] = new byte[2]; + int ret = NativeInterface.readFile(fn, in); + if (ret < 0) { + throw new RuntimeException(NativeInterface.getError(ret)); + } else if (1 <= ret && in[0] == '0') { + return LOW; + } else if (1 <= ret && in[0] == '1') { + return HIGH; + } else { + System.err.print("Read " + ret + " bytes"); + if (0 < ret) { + System.err.format(", first byte is 0x%02x" + in[0]); + } + System.err.println(); + throw new RuntimeException("Unexpected value"); + } + } + + + /** + * Sets an output pin to be either high or low + * @param pin GPIO pin + * @param value GPIO.HIGH (1) or GPIO.LOW (0) + * @see pinMode + * @see digitalRead + * @webref + */ + public static void digitalWrite(int pin, int value) { + checkValidPin(pin); + + String out; + if (value == LOW) { + // values are also stored in a bitmap to make it possible to set a + // default level per pin before enabling the output + values.clear(pin); + out = "0"; + } else if (value == HIGH) { + values.set(pin); + out = "1"; + } else { + System.err.println("Only GPIO.LOW and GPIO.HIGH, 0 and 1, or true and false, can be used."); + throw new IllegalArgumentException("Illegal value"); + } + + if (NativeInterface.isSimulated()) { + return; + } + + String fn = String.format("/sys/class/gpio/gpio%d/value", pin); + int ret = NativeInterface.writeFile(fn, out); + if (ret < 0) { + if (ret != -2) { // ENOENT, pin might not yet be exported + throw new RuntimeException(NativeInterface.getError(ret)); + } + } + } + + + /** + * @param value true or false + */ + public static void digitalWrite(int pin, boolean value) { + if (value) { + digitalWrite(pin, HIGH); + } else { + digitalWrite(pin, LOW); + } + } + + + /** + * Disables an interrupt for an input pin + * @param pin GPIO pin + * @see enableInterrupt + * @see waitForInterrupt + */ + protected static void disableInterrupt(int pin) { + enableInterrupt(pin, NONE); + } + + + /** + * Enables an interrupt for an input pin + * @param pin GPIO pin + * @param mode what to wait for: GPIO.CHANGE, GPIO.FALLING or GPIO.RISING + * @see waitForInterrupt + * @see disableInterrupt + */ + protected static void enableInterrupt(int pin, int mode) { + checkValidPin(pin); + + String out; + if (mode == NONE) { + out = "none"; + } else if (mode == CHANGE) { + out = "both"; + } else if (mode == FALLING) { + out = "falling"; + } else if (mode == RISING) { + out = "rising"; + } else { + throw new IllegalArgumentException("Unknown mode"); + } + + if (NativeInterface.isSimulated()) { + return; + } + + String fn = String.format("/sys/class/gpio/gpio%d/edge", pin); + int ret = NativeInterface.writeFile(fn, out); + if (ret < 0) { + if (ret == -2) { // ENOENT + System.err.println("Make sure your called pinMode on the input pin"); + } + throw new RuntimeException(NativeInterface.getError(ret)); + } + } + + + /** + * Allows interrupts to happen + * @see attachInterrupt + * @see noInterrupts + * @see releaseInterrupt + * @webref + */ + public static void interrupts() { + serveInterrupts = true; + } + + + /** + * Prevents interrupts from happpening + * @see attachInterrupt + * @see interrupts + * @see releaseInterrupt + * @webref + */ + public static void noInterrupts() { + serveInterrupts = false; + } + + + /** + * Configures a pin to act either as input or output + * @param pin GPIO pin + * @param mode GPIO.INPUT, GPIO.INPUT_PULLUP, GPIO.INPUT_PULLDOWN, or GPIO.OUTPUT + * @see digitalRead + * @see digitalWrite + * @see releasePin + * @webref + */ + public static void pinMode(int pin, int mode) { + checkValidPin(pin); + + if (NativeInterface.isSimulated()) { + return; + } + + // export pin through sysfs + String fn = "/sys/class/gpio/export"; + int ret = NativeInterface.writeFile(fn, Integer.toString(pin)); + if (ret < 0) { + if (ret == -2) { // ENOENT + System.err.println("Make sure your kernel is compiled with GPIO_SYSFS enabled"); + } + if (ret == -22) { // EINVAL + System.err.println("GPIO pin " + pin + " does not seem to be available on your platform"); + } + if (ret != -16) { // EBUSY, returned when the pin is already exported + throw new RuntimeException(fn + ": " + NativeInterface.getError(ret)); + } + } + + // set direction and default level for outputs + fn = String.format("/sys/class/gpio/gpio%d/direction", pin); + String out; + if (mode == INPUT) { + out = "in"; + + // attempt to disable any pre-set pullups on the Raspberry Pi + NativeInterface.raspbianGpioMemSetPinBias(pin, mode); + + } else if (mode == OUTPUT) { + if (values.get(pin)) { + out = "high"; + } else { + out = "low"; + } + } else if (mode == INPUT_PULLUP || mode == INPUT_PULLDOWN) { + out = "in"; + + // attempt to set pullups on the Raspberry Pi + ret = NativeInterface.raspbianGpioMemSetPinBias(pin, mode); + if (ret == -2) { // NOENT + System.err.println("Setting pullup or pulldown resistors is currently only supported on the Raspberry Pi running Raspbian. Continuing without."); + } else if (ret < 0) { + System.err.println("Error setting pullup or pulldown resistors: " + NativeInterface.getError(ret) + ". Continuing without."); + } + // currently this can't be done in a non-platform-specific way, see + // http://lists.infradead.org/pipermail/linux-rpi-kernel/2015-August/002146.html + + } else { + throw new IllegalArgumentException("Unknown mode"); + } + + // we need to give udev some time to change the file permissions behind our back + // retry for 500ms when writing to the file fails with -EACCES + long start = System.currentTimeMillis(); + do { + ret = NativeInterface.writeFile(fn, out); + if (ret == -13) { + Thread.yield(); + } + } while (ret == -13 && System.currentTimeMillis()-start < 500); + + if (ret < 0) { + throw new RuntimeException(fn + ": " + NativeInterface.getError(ret)); + } + } + + + /** + * Stops listening for interrupts on an input pin + * @param pin GPIO pin + * @see attachInterrupt + * @see noInterrupts + * @see interrupts + * @webref + */ + public static void releaseInterrupt(int pin) { + Thread t = irqThreads.get(pin); + if (t == null) { + return; + } + + t.interrupt(); + try { + t.join(); + } catch (InterruptedException e) { + System.err.println("Error joining thread in releaseInterrupt: " + e.getMessage()); + } + t = null; + irqThreads.remove(pin); + + disableInterrupt(pin); + } + + + /** + * Gives ownership of a pin back to the operating system + * @param pin GPIO pin + * @see pinMode + * @webref + */ + public static void releasePin(int pin) { + checkValidPin(pin); + + if (NativeInterface.isSimulated()) { + return; + } + + String fn = "/sys/class/gpio/unexport"; + int ret = NativeInterface.writeFile(fn, Integer.toString(pin)); + if (ret < 0) { + if (ret == -2) { // ENOENT + System.err.println("Make sure your kernel is compiled with GPIO_SYSFS enabled"); + } + // EINVAL is returned when trying to unexport pins that weren't exported to begin with, ignore this case + if (ret != -22) { + throw new RuntimeException(NativeInterface.getError(ret)); + } + } + } + + + /** + * Waits for the value of an input pin to change + * @param pin GPIO pin + * @param mode what to wait for: GPIO.CHANGE, GPIO.FALLING or GPIO.RISING + * @webref + */ + public static void waitFor(int pin, int mode) { + waitFor(pin, mode, -1); + } + + + /** + * Waits for the value of an input pin to change + * + * This function will throw a RuntimeException in case of a timeout. + * @param timeout don't wait more than timeout milliseconds + * @webref + */ + public static void waitFor(int pin, int mode, int timeout) { + enableInterrupt(pin, mode); + if (waitForInterrupt(pin, timeout) == false) { + throw new RuntimeException("Timeout occurred"); + } + } + + + public static boolean waitForInterrupt(int pin, int mode, int timeout) { + throw new RuntimeException("The waitForInterrupt function has been renamed to waitFor. Please update your sketch accordingly."); + } + + + /** + * Waits for the value of an input pin to change + * + * Make sure to setup the interrupt with enableInterrupt() before calling + * this function. A timeout value of -1 waits indefinitely. + * @param pin GPIO pin + * @param timeout don't wait more than timeout milliseconds + * @return true if the interrupt occured, false if the timeout occured + * @see enableInterrupt + * @see disableInterrupt + */ + protected static boolean waitForInterrupt(int pin, int timeout) { + checkValidPin(pin); + + if (NativeInterface.isSimulated()) { + // pretend the interrupt happens after 200ms + try { + Thread.sleep(200); + } catch (InterruptedException e) {} + return true; + } + + String fn = String.format("/sys/class/gpio/gpio%d/value", pin); + int ret = NativeInterface.pollDevice(fn, timeout); + if (ret < 0) { + if (ret == -2) { // ENOENT + System.err.println("Make sure your called pinMode on the input pin"); + } + throw new RuntimeException(NativeInterface.getError(ret)); + } else if (ret == 0) { + // timeout + return false; + } else { + // interrupt + return true; + } + } +} diff --git a/Processing/Lib/io/src/processing/io/I2C.java b/Processing/Lib/io/src/processing/io/I2C.java new file mode 100644 index 0000000..2e5dedb --- /dev/null +++ b/Processing/Lib/io/src/processing/io/I2C.java @@ -0,0 +1,262 @@ +/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + Copyright (c) The Processing Foundation 2015 + Hardware I/O library developed by Gottfried Haider as part of GSoC 2015 + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA +*/ + +package processing.io; + +import processing.io.NativeInterface; + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; + + +/** + * @webref + */ +public class I2C { + + protected String dev; + protected int handle; + protected int slave; + protected byte[] out; + protected boolean transmitting; + + + /** + * Opens an I2C interface as master + * @param dev interface name + * @see list + * @webref + */ + public I2C(String dev) { + NativeInterface.loadLibrary(); + this.dev = dev; + + if (NativeInterface.isSimulated()) { + return; + } + + handle = NativeInterface.openDevice("/dev/" + dev); + if (handle < 0) { + throw new RuntimeException(NativeInterface.getError(handle)); + } + } + + + /** + * Begins a transmission to an attached device + * @see write + * @see read + * @see endTransmission + * @webref + */ + public void beginTransmission(int slave) { + // addresses 120 (0x78) to 127 are additionally reserved + if (0x78 <= slave) { + System.err.println("beginTransmission expects a 7 bit address, try shifting one bit to the right"); + throw new IllegalArgumentException("Illegal address"); + } + this.slave = slave; + transmitting = true; + out = null; + } + + + /** + * Closes the I2C device + * @webref + */ + public void close() { + if (NativeInterface.isSimulated()) { + return; + } + + NativeInterface.closeDevice(handle); + handle = 0; + } + + + protected void finalize() throws Throwable { + try { + close(); + } finally { + super.finalize(); + } + } + + + /** + * Ends the current transmissions + * @see beginTransmission + * @see write + * @webref + */ + public void endTransmission() { + if (!transmitting) { + // silently ignore this case + return; + } + + if (NativeInterface.isSimulated()) { + return; + } + + // implement these flags if needed: https://github.com/raspberrypi/linux/blob/rpi-patches/Documentation/i2c/i2c-protocol + int ret = NativeInterface.transferI2c(handle, slave, out, null); + transmitting = false; + out = null; + if (ret < 0) { + if (ret == -5 | ret == -121) { // EIO | EREMOTEIO + System.err.println("The device did not respond. Check the cabling and whether you are using the correct address."); + } + throw new RuntimeException(NativeInterface.getError(ret)); + } + } + + + /** + * Lists all available I2C interfaces + * @return String array + * @webref + */ + public static String[] list() { + if (NativeInterface.isSimulated()) { + // as on the Raspberry Pi + return new String[]{ "i2c-1" }; + } + + ArrayList devs = new ArrayList(); + File dir = new File("/dev"); + File[] files = dir.listFiles(); + if (files != null) { + for (File file : files) { + if (file.getName().startsWith("i2c-")) { + devs.add(file.getName()); + } + } + } + // listFiles() does not guarantee ordering + String[] tmp = devs.toArray(new String[devs.size()]); + Arrays.sort(tmp); + return tmp; + } + + + /** + * Reads bytes from the attached device + * @param len number of bytes to read + * @return bytes read from device + * @see beginTransmission + * @see write + * @see endTransmission + * @webref + */ + public byte[] read(int len) { + if (!transmitting) { + throw new RuntimeException("beginTransmisson has not been called"); + } + + byte[] in = new byte[len]; + + if (NativeInterface.isSimulated()) { + return in; + } + + int ret = NativeInterface.transferI2c(handle, slave, out, in); + transmitting = false; + out = null; + if (ret < 0) { + if (ret == -5 | ret == -121) { // EIO | EREMOTEIO + System.err.println("The device did not respond. Check the cabling and whether you are using the correct address."); + } + throw new RuntimeException(NativeInterface.getError(ret)); + } + + return in; + } + + + /** + * Adds bytes to be written to the device + * @param out bytes to be written + * @see beginTransmission + * @see read + * @see endTransmission + * @webref + */ + public void write(byte[] out) { + if (!transmitting) { + throw new RuntimeException("beginTransmisson has not been called"); + } + + if (this.out == null) { + this.out = out; + } else { + byte[] tmp = new byte[this.out.length + out.length]; + System.arraycopy(this.out, 0, tmp, 0, this.out.length); + System.arraycopy(out, 0, tmp, this.out.length, out.length); + this.out = tmp; + } + } + + + /** + * Adds bytes to be written to the attached device + * @param out string to be written + * @see beginTransmission + * @see read + * @see endTransmission + */ + public void write(String out) { + write(out.getBytes()); + } + + + /** + * Adds a byte to be written to the attached device + * @param out single byte to be written, e.g. numeric literal (0 to 255, or -128 to 127) + * @see beginTransmission + * @see read + * @see endTransmission + */ + public void write(int out) { + if (out < -128 || 255 < out) { + System.err.println("The write function can only operate on a single byte at a time. Call it with a value from 0 to 255, or -128 to 127."); + throw new RuntimeException("Argument does not fit into a single byte"); + } + byte[] tmp = new byte[1]; + tmp[0] = (byte)out; + write(tmp); + } + + /** + * Adds a byte to be written to the attached device + * @param out single byte to be written + * @see beginTransmission + * @see read + * @see endTransmission + */ + public void write(byte out) { + // cast to (unsigned) int + write(out & 0xff); + } +} diff --git a/Processing/Lib/io/src/processing/io/LED.java b/Processing/Lib/io/src/processing/io/LED.java new file mode 100644 index 0000000..32925e6 --- /dev/null +++ b/Processing/Lib/io/src/processing/io/LED.java @@ -0,0 +1,177 @@ +/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + Copyright (c) The Processing Foundation 2015 + Hardware I/O library developed by Gottfried Haider as part of GSoC 2015 + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA +*/ + +package processing.io; + +import processing.io.NativeInterface; + +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; + + +/** + * @webref + */ +public class LED { + + protected String dev; + protected int maxBrightness; + protected int prevBrightness; + protected String prevTrigger; + + + /** + * Opens a LED device + * @param dev device name + * @see list + * @webref + */ + public LED(String dev) { + NativeInterface.loadLibrary(); + this.dev = dev; + + if (NativeInterface.isSimulated()) { + return; + } + + // read maximum brightness + try { + Path path = Paths.get("/sys/class/leds/" + dev + "/max_brightness"); + String tmp = new String(Files.readAllBytes(path)); + maxBrightness = Integer.parseInt(tmp.trim()); + } catch (Exception e) { + System.err.println(e.getMessage()); + throw new RuntimeException("Unable to read maximum brightness"); + } + + // read current trigger setting to be able to restore it later + try { + Path path = Paths.get("/sys/class/leds/" + dev + "/trigger"); + String tmp = new String(Files.readAllBytes(path)); + int start = tmp.indexOf('['); + int end = tmp.indexOf(']', start); + if (start != -1 && end != -1) { + prevTrigger = tmp.substring(start+1, end); + } + } catch (Exception e) { + System.err.println(e.getMessage()); + throw new RuntimeException("Unable to read trigger setting"); + } + + // read current brightness to be able to restore it later + try { + Path path = Paths.get("/sys/class/leds/" + dev + "/brightness"); + String tmp = new String(Files.readAllBytes(path)); + prevBrightness = Integer.parseInt(tmp.trim()); + } catch (Exception e) { + System.err.println(e.getMessage()); + throw new RuntimeException("Unable to read current brightness"); + } + + // disable trigger + String fn = "/sys/class/leds/" + dev + "/trigger"; + int ret = NativeInterface.writeFile(fn, "none"); + if (ret < 0) { + if (ret == -13) { // EACCES + System.err.println("You might need to install a custom udev rule to allow regular users to modify /sys/class/leds/*."); + } + throw new RuntimeException(NativeInterface.getError(ret)); + } + } + + + /** + * Sets the brightness + * @param bright 0.0 (off) to 1.0 (maximum) + * @webref + */ + public void brightness(float bright) { + if (bright < 0.0 || 1.0 < bright) { + System.err.println("Brightness must be between 0.0 and 1.0."); + throw new IllegalArgumentException("Illegal argument"); + } + + if (NativeInterface.isSimulated()) { + return; + } + + String fn = "/sys/class/leds/" + dev + "/brightness"; + int ret = NativeInterface.writeFile(fn, Integer.toString((int)(bright * maxBrightness))); + if (ret < 0) { + throw new RuntimeException(fn + ": " + NativeInterface.getError(ret)); + } + } + + + /** + * Restores the previous state + * @webref + */ + public void close() { + if (NativeInterface.isSimulated()) { + return; + } + + // restore previous settings + String fn = "/sys/class/leds/" + dev + "/brightness"; + int ret = NativeInterface.writeFile(fn, Integer.toString(prevBrightness)); + if (ret < 0) { + throw new RuntimeException(fn + ": " + NativeInterface.getError(ret)); + } + + fn = "/sys/class/leds/" + dev + "/trigger"; + ret = NativeInterface.writeFile(fn, prevTrigger); + if (ret < 0) { + throw new RuntimeException(fn + ": " + NativeInterface.getError(ret)); + } + } + + + /** + * Lists all available LED devices + * @return String array + * @webref + */ + public static String[] list() { + if (NativeInterface.isSimulated()) { + // as on the Raspberry Pi + return new String[]{ "led0", "led1" }; + } + + ArrayList devs = new ArrayList<>(); + File dir = new File("/sys/class/leds"); + File[] files = dir.listFiles(); + if (files != null) { + for (File file : files) { + devs.add(file.getName()); + } + } + // listFiles() does not guarantee ordering + String[] tmp = devs.toArray(new String[devs.size()]); + Arrays.sort(tmp); + return tmp; + } +} diff --git a/Processing/Lib/io/src/processing/io/NativeInterface.java b/Processing/Lib/io/src/processing/io/NativeInterface.java new file mode 100644 index 0000000..a05a71e --- /dev/null +++ b/Processing/Lib/io/src/processing/io/NativeInterface.java @@ -0,0 +1,78 @@ +/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + Copyright (c) The Processing Foundation 2015 + Hardware I/O library developed by Gottfried Haider as part of GSoC 2015 + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA +*/ + +package processing.io; + + +public class NativeInterface { + + protected static boolean loaded = false; + protected static boolean alwaysSimulate = false; + + public static void loadLibrary() { + if (!loaded) { + if (isSimulated()) { + System.err.println("The Processing I/O library is not supported on this platform. Instead of values from actual hardware ports, your sketch will only receive stand-in values that allow you to test the remainder of its functionality."); + } else { + System.loadLibrary("processing-io"); + } + loaded = true; + } + } + + public static void alwaysSimulate() { + alwaysSimulate = true; + } + + public static boolean isSimulated() { + return alwaysSimulate || + !"Linux".equals(System.getProperty("os.name")); + } + + + public static native int openDevice(String fn); + public static native String getError(int errno); + public static native int closeDevice(int handle); + + // the following two functions were done in native code to get access to the + // specifc error number (errno) that might occur + public static native int readFile(String fn, byte[] in); + public static native int writeFile(String fn, byte[] out); + public static int writeFile(String fn, String out) { + return writeFile(fn, out.getBytes()); + } + + /* GPIO */ + public static native int raspbianGpioMemRead(int offset); + public static native int raspbianGpioMemWrite(int offset, int mask, int value); + public static native int raspbianGpioMemSetPinBias(int gpio, int mode); + public static native int pollDevice(String fn, int timeout); + /* I2C */ + public static native int transferI2c(int handle, int slave, byte[] out, byte[] in); + /* SoftwareServo */ + public static native long servoStartThread(int gpio, int pulse, int period); + public static native int servoUpdateThread(long handle, int pulse, int period); + public static native int servoStopThread(long handle); + /* SPI */ + public static native int setSpiSettings(int handle, int maxSpeed, int dataOrder, int mode); + public static native int transferSpi(int handle, byte[] out, byte[] in); +} diff --git a/Processing/Lib/io/src/processing/io/PWM.java b/Processing/Lib/io/src/processing/io/PWM.java new file mode 100644 index 0000000..c33c910 --- /dev/null +++ b/Processing/Lib/io/src/processing/io/PWM.java @@ -0,0 +1,214 @@ +/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + Copyright (c) The Processing Foundation 2015 + Hardware I/O library developed by Gottfried Haider as part of GSoC 2015 + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA +*/ + +package processing.io; + +import processing.io.NativeInterface; + +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; + + +/** + * @webref + */ +public class PWM { + + int channel; + String chip; + + + /** + * Opens a PWM channel + * @param channel PWM channel + * @see list + * @webref + */ + public PWM(String channel) { + NativeInterface.loadLibrary(); + + int pos = channel.indexOf("/pwm"); + if (pos == -1) { + throw new IllegalArgumentException("Unsupported channel"); + } + chip = channel.substring(0, pos); + this.channel = Integer.parseInt(channel.substring(pos+4)); + + if (NativeInterface.isSimulated()) { + return; + } + + // export channel through sysfs + String fn = "/sys/class/pwm/" + chip + "/export"; + int ret = NativeInterface.writeFile(fn, Integer.toString(this.channel)); + if (ret < 0) { + if (ret == -2) { // ENOENT + System.err.println("Make sure your kernel is compiled with PWM_SYSFS enabled and you have the necessary PWM driver for your platform"); + } + // XXX: check + if (ret == -22) { // EINVAL + System.err.println("PWM channel " + channel + " does not seem to be available on your platform"); + } + // XXX: check + if (ret != -16) { // EBUSY, returned when the pin is already exported + throw new RuntimeException(fn + ": " + NativeInterface.getError(ret)); + } + } + + // delay to give udev a chance to change the file permissions behind our back + // there should really be a cleaner way for this + try { + Thread.sleep(500); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + + + /** + * Disables the PWM output + * @webref + */ + public void clear() { + if (NativeInterface.isSimulated()) { + return; + } + + String fn = String.format("/sys/class/pwm/%s/pwm%d/enable", chip, channel); + int ret = NativeInterface.writeFile(fn, "0"); + if (ret < 0) { + throw new RuntimeException(NativeInterface.getError(ret)); + } + } + + + /** + * Gives ownership of a channel back to the operating system + * @webref + */ + public void close() { + if (NativeInterface.isSimulated()) { + return; + } + + // XXX: implicit clear()? + // XXX: also check GPIO + + String fn = "/sys/class/pwm/" + chip + "/unexport"; + int ret = NativeInterface.writeFile(fn, Integer.toString(channel)); + if (ret < 0) { + if (ret == -2) { // ENOENT + System.err.println("Make sure your kernel is compiled with PWM_SYSFS enabled and you have the necessary PWM driver for your platform"); + } + // XXX: check + // EINVAL is also returned when trying to unexport pins that weren't exported to begin with + throw new RuntimeException(NativeInterface.getError(ret)); + } + } + + + /** + * Lists all available PWM channels + * @return String array + * @webref + */ + public static String[] list() { + if (NativeInterface.isSimulated()) { + return new String[]{ "pwmchip0/pwm0", "pwmchip0/pwm1" }; + } + + ArrayList devs = new ArrayList(); + File dir = new File("/sys/class/pwm"); + File[] chips = dir.listFiles(); + if (chips != null) { + for (File chip : chips) { + // get the number of supported channels + try { + Path path = Paths.get("/sys/class/pwm/" + chip.getName() + "/npwm"); + String tmp = new String(Files.readAllBytes(path)); + int npwm = Integer.parseInt(tmp.trim()); + for (int i=0; i < npwm; i++) { + devs.add(chip.getName() + "/pwm" + i); + } + } catch (Exception e) { + } + } + } + // listFiles() does not guarantee ordering + String[] tmp = devs.toArray(new String[devs.size()]); + Arrays.sort(tmp); + return tmp; + } + + + /** + * Enables the PWM output + * @param period cycle period in Hz + * @param duty duty cycle, 0.0 (always off) to 1.0 (always on) + * @webref + */ + public void set(int period, float duty) { + if (NativeInterface.isSimulated()) { + return; + } + + // set period + String fn = fn = String.format("/sys/class/pwm/%s/pwm%d/period", chip, channel); + // convert to nanoseconds + int ret = NativeInterface.writeFile(fn, String.format("%d", (int)(1000000000 / period))); + if (ret < 0) { + throw new RuntimeException(fn + ": " + NativeInterface.getError(ret)); + } + + // set duty cycle + fn = fn = String.format("/sys/class/pwm/%s/pwm%d/duty_cycle", chip, channel); + if (duty < 0.0 || 1.0 < duty) { + System.err.println("Duty cycle must be between 0.0 and 1.0."); + throw new IllegalArgumentException("Illegal argument"); + } + // convert to nanoseconds + ret = NativeInterface.writeFile(fn, String.format("%d", (int)((1000000000 * duty) / period))); + if (ret < 0) { + throw new RuntimeException(fn + ": " + NativeInterface.getError(ret)); + } + + // enable output + fn = String.format("/sys/class/pwm/%s/pwm%d/enable", chip, channel); + ret = NativeInterface.writeFile(fn, "1"); + if (ret < 0) { + throw new RuntimeException(fn + ": " + NativeInterface.getError(ret)); + } + } + + + /** + * Enables the PWM output with a preset period of 1 kHz + * @webref + */ + public void set(float duty) { + set(1000, duty); + } +} diff --git a/Processing/Lib/io/src/processing/io/SPI.java b/Processing/Lib/io/src/processing/io/SPI.java new file mode 100644 index 0000000..b1a8369 --- /dev/null +++ b/Processing/Lib/io/src/processing/io/SPI.java @@ -0,0 +1,226 @@ +/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + Copyright (c) The Processing Foundation 2015 + Hardware I/O library developed by Gottfried Haider as part of GSoC 2015 + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA +*/ + +package processing.io; + +import processing.io.NativeInterface; + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + + +/** + * @webref + */ +public class SPI { + + /** + * CPOL=0, CPHA=0, most common + */ + public static final int MODE0 = 0; + /** + * CPOL=0, CPHA=1 + */ + public static final int MODE1 = 1; + /** + * CPOL=1, CPHA=0 + */ + public static final int MODE2 = 2; + /** + * CPOL=1, CPHA=1 + */ + public static final int MODE3 = 3; + /** + * most significant bit first, most common + */ + public static final int MSBFIRST = 0; + /** + * least significant bit first + */ + public static final int LSBFIRST = 1; + + protected int dataOrder = 0; + protected String dev; + protected int handle; + protected int maxSpeed = 500000; + protected int mode = 0; + protected static Map settings = new HashMap(); + + + /** + * Opens an SPI interface as master + * @param dev device name + * @see list + * @webref + */ + public SPI(String dev) { + NativeInterface.loadLibrary(); + this.dev = dev; + + if (NativeInterface.isSimulated()) { + return; + } + + handle = NativeInterface.openDevice("/dev/" + dev); + if (handle < 0) { + throw new RuntimeException(NativeInterface.getError(handle)); + } + } + + + /** + * Closes the SPI interface + * @webref + */ + public void close() { + if (NativeInterface.isSimulated()) { + return; + } + + NativeInterface.closeDevice(handle); + handle = 0; + } + + + protected void finalize() throws Throwable { + try { + close(); + } finally { + super.finalize(); + } + } + + + /** + * Lists all available SPI interfaces + * @return String array + * @webref + */ + public static String[] list() { + if (NativeInterface.isSimulated()) { + // as on the Raspberry Pi + return new String[]{ "spidev0.0", "spidev0.1" }; + } + + ArrayList devs = new ArrayList(); + File dir = new File("/dev"); + File[] files = dir.listFiles(); + if (files != null) { + for (File file : files) { + if (file.getName().startsWith("spidev")) { + devs.add(file.getName()); + } + } + } + // listFiles() does not guarantee ordering + String[] tmp = devs.toArray(new String[devs.size()]); + Arrays.sort(tmp); + return tmp; + } + + + /** + * Configures the SPI interface + * @param maxSpeed maximum transmission rate in Hz, 500000 (500 kHz) is a resonable default + * @param dataOrder whether data is send with the first- or least-significant bit first (SPI.MSBFIRST or SPI.LSBFIRST, the former is more common) + * @param mode SPI.MODE0 to SPI.MODE3 + * @webref + */ + public void settings(int maxSpeed, int dataOrder, int mode) { + this.maxSpeed = maxSpeed; + this.dataOrder = dataOrder; + this.mode = mode; + } + + + /** + * Transfers data over the SPI bus + * @param out bytes to send + * @return bytes read in (array is the same length as out) + * @webref + */ + public byte[] transfer(byte[] out) { + if (NativeInterface.isSimulated()) { + return new byte[out.length]; + } + + // track the current setting per device across multiple instances + String curSettings = maxSpeed + "-" + dataOrder + "-" + mode; + if (!curSettings.equals(settings.get(dev))) { + int ret = NativeInterface.setSpiSettings(handle, maxSpeed, dataOrder, mode); + if (ret < 0) { + System.err.println(NativeInterface.getError(handle)); + throw new RuntimeException("Error updating device configuration"); + } + settings.put(dev, curSettings); + } + + byte[] in = new byte[out.length]; + int transferred = NativeInterface.transferSpi(handle, out, in); + if (transferred < 0) { + throw new RuntimeException(NativeInterface.getError(transferred)); + } else if (transferred < out.length) { + throw new RuntimeException("Fewer bytes transferred than requested: " + transferred); + } + return in; + } + + + /** + * Transfers data over the SPI bus + * @param out string to send + * @return bytes read in (array is the same length as out) + */ + public byte[] transfer(String out) { + return transfer(out.getBytes()); + } + + + /** + * Transfers data over the SPI bus + * @param out single byte to send, e.g. numeric literal (0 to 255, or -128 to 127) + * @return bytes read in (array is the same length as out) + */ + public byte[] transfer(int out) { + if (out < -128 || 255 < out) { + System.err.println("The transfer function can only operate on a single byte at a time. Call it with a value from 0 to 255, or -128 to 127."); + throw new RuntimeException("Argument does not fit into a single byte"); + } + byte[] tmp = new byte[1]; + tmp[0] = (byte)out; + return transfer(tmp); + } + + + /** + * Transfers data over the SPI bus + * @param out single byte to send + * @return bytes read in (array is the same length as out) + */ + public byte[] transfer(byte out) { + // cast to (unsigned) int + return transfer(out & 0xff); + } +} diff --git a/Processing/Lib/io/src/processing/io/SoftwareServo.java b/Processing/Lib/io/src/processing/io/SoftwareServo.java new file mode 100644 index 0000000..db5cbdc --- /dev/null +++ b/Processing/Lib/io/src/processing/io/SoftwareServo.java @@ -0,0 +1,162 @@ +/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + Copyright (c) The Processing Foundation 2015 + Hardware I/O library developed by Gottfried Haider + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA +*/ + +package processing.io; + +import processing.core.*; + + +/** + * @webref + */ +public class SoftwareServo { + + public static final int DEFAULT_MIN_PULSE = 544; + public static final int DEFAULT_MAX_PULSE = 2400; + + protected int pin = -1; // gpio number (-1 .. not attached) + protected long handle = -1; // native thread id (<0 .. not started) + protected int period = 20000; // 20 ms (50 Hz) + protected int minPulse = 0; // minimum pulse width in microseconds + protected int maxPulse = 0; // maximum pulse width in microseconds + protected int pulse = 0; // current pulse in microseconds + + + /** + * Opens a servo motor + * @param parent typically use "this" + * @webref + */ + public SoftwareServo(PApplet parent) { + NativeInterface.loadLibrary(); + } + + + /** + * Closes a servo motor + * @webref + */ + public void close() { + detach(); + } + + + protected void finalize() throws Throwable { + try { + close(); + } finally { + super.finalize(); + } + } + + + /** + * Attaches a servo motor to a GPIO pin + * @param pin GPIO pin + * @webref + */ + public void attach(int pin) { + detach(); + this.pin = pin; + this.minPulse = DEFAULT_MIN_PULSE; + this.maxPulse = DEFAULT_MAX_PULSE; + } + + + /** + * Attaches a servo motor to a GPIO pin using custom pulse widths + * @param minPulse minimum pulse width in microseconds (default: 544, same as on Arduino) + * @param maxPulse maximum pulse width in microseconds (default: 2400, same as on Arduino) + * @webref + */ + public void attach(int pin, int minPulse, int maxPulse) { + detach(); + this.pin = pin; + this.minPulse = minPulse; + this.maxPulse = maxPulse; + } + + + /** + * Moves a servo motor to a given orientation + * @param angle angle in degrees (controls speed and direction on continuous-rotation servos) + * @webref + */ + public void write(float angle) { + if (attached() == false) { + System.err.println("You need to call attach(pin) before write(angle)."); + throw new RuntimeException("Servo is not attached"); + } + + if (angle < 0 || 180 < angle) { + System.err.println("Only degree values between 0 and 180 can be used."); + throw new IllegalArgumentException("Illegal value"); + } + pulse = (int)(minPulse + (angle/180.0) * (maxPulse-minPulse)); + + if (handle < 0) { + // start a new thread + GPIO.pinMode(pin, GPIO.OUTPUT); + if (NativeInterface.isSimulated()) { + return; + } + handle = NativeInterface.servoStartThread(pin, pulse, period); + if (handle < 0) { + throw new RuntimeException(NativeInterface.getError((int)handle)); + } + } else { + // thread already running + int ret = NativeInterface.servoUpdateThread(handle, pulse, period); + if (ret < 0) { + throw new RuntimeException(NativeInterface.getError(ret)); + } + } + } + + + /** + * Returns whether a servo motor is attached to a pin + * @return true if attached, false is not + * @webref + */ + public boolean attached() { + return (pin != -1); + } + + + /** + * Detatches a servo motor from a GPIO pin + * @webref + */ + public void detach() { + if (0 <= handle) { + // stop thread + int ret = NativeInterface.servoStopThread(handle); + GPIO.pinMode(pin, GPIO.INPUT); + handle = -1; + pin = -1; + if (ret < 0) { + throw new RuntimeException(NativeInterface.getError(ret)); + } + } + } +} diff --git a/Processing/Processing.pdf b/Processing/Processing.pdf index 2c97c37..9d06aba 100644 Binary files a/Processing/Processing.pdf and b/Processing/Processing.pdf differ diff --git a/Processing/Sketches/Sketch_06_1_1_ADC/ADCDevice.pde b/Processing/Sketches/Sketch_06_1_1_ADC/ADCDevice.pde index 59be0db..058c5d6 100644 --- a/Processing/Sketches/Sketch_06_1_1_ADC/ADCDevice.pde +++ b/Processing/Sketches/Sketch_06_1_1_ADC/ADCDevice.pde @@ -10,7 +10,8 @@ class ADCDevice { public int cmd = 0; public I2C i2c; public ADCDevice() { - i2c = new I2C(I2C.list()[0]); + //Note that if you are running on a version 1 Raspberry Pi, you need to change the subscript index to 0. + i2c = new I2C(I2C.list()[1]); } public boolean detectI2C(int addr) { diff --git a/Processing/Sketches/Sketch_07_1_1_SoftLight/ADCDevice.pde b/Processing/Sketches/Sketch_07_1_1_SoftLight/ADCDevice.pde index 59be0db..058c5d6 100644 --- a/Processing/Sketches/Sketch_07_1_1_SoftLight/ADCDevice.pde +++ b/Processing/Sketches/Sketch_07_1_1_SoftLight/ADCDevice.pde @@ -10,7 +10,8 @@ class ADCDevice { public int cmd = 0; public I2C i2c; public ADCDevice() { - i2c = new I2C(I2C.list()[0]); + //Note that if you are running on a version 1 Raspberry Pi, you need to change the subscript index to 0. + i2c = new I2C(I2C.list()[1]); } public boolean detectI2C(int addr) { diff --git a/Processing/Sketches/Sketch_08_1_1_Thermometer/ADCDevice.pde b/Processing/Sketches/Sketch_08_1_1_Thermometer/ADCDevice.pde index 59be0db..058c5d6 100644 --- a/Processing/Sketches/Sketch_08_1_1_Thermometer/ADCDevice.pde +++ b/Processing/Sketches/Sketch_08_1_1_Thermometer/ADCDevice.pde @@ -10,7 +10,8 @@ class ADCDevice { public int cmd = 0; public I2C i2c; public ADCDevice() { - i2c = new I2C(I2C.list()[0]); + //Note that if you are running on a version 1 Raspberry Pi, you need to change the subscript index to 0. + i2c = new I2C(I2C.list()[1]); } public boolean detectI2C(int addr) { diff --git a/Processing/Sketches/Sketch_14_1_1_Joystick/ADCDevice.pde b/Processing/Sketches/Sketch_14_1_1_Joystick/ADCDevice.pde index 59be0db..058c5d6 100644 --- a/Processing/Sketches/Sketch_14_1_1_Joystick/ADCDevice.pde +++ b/Processing/Sketches/Sketch_14_1_1_Joystick/ADCDevice.pde @@ -10,7 +10,8 @@ class ADCDevice { public int cmd = 0; public I2C i2c; public ADCDevice() { - i2c = new I2C(I2C.list()[0]); + //Note that if you are running on a version 1 Raspberry Pi, you need to change the subscript index to 0. + i2c = new I2C(I2C.list()[1]); } public boolean detectI2C(int addr) { diff --git a/Processing/Sketches/Sketch_14_1_1_Joystick/SingleKey.pde b/Processing/Sketches/Sketch_14_1_1_Joystick/SingleKey.pde new file mode 100644 index 0000000..11a111d --- /dev/null +++ b/Processing/Sketches/Sketch_14_1_1_Joystick/SingleKey.pde @@ -0,0 +1,101 @@ +/* + ****************************************************************************** + * class SingleKey + * Author Freenove (http://www.freenove.com) + * Date 2016/08/27 + ****************************************************************************** + * Brief + * This class is used to get a single button key value (GPIO numbering) + ****************************************************************************** + * Copyright + * Copyright © Freenove (http://www.freenove.com) + * License + * Creative Commons Attribution ShareAlike 3.0 + * (http://creativecommons.org/licenses/by-sa/3.0/legalcode) + ****************************************************************************** + */ +int keyValue = -1; +class SingleKey { + final int IDLE = 0, + PRESSED = 1, + HOLD = 2, + RELEASED = 3; + int btnState = IDLE; + boolean isPressed = false; + boolean isHold = false; + long holdTimer = 0; + final int holdTime = 100; + boolean changeState = false; + int lastButtonIOState = GPIO.HIGH; + int buttonIOState=GPIO.HIGH; + int nowButtonState; + boolean buttonChanged = false; + int lastChangeTime; + int decounceTime = 20; + int pin; + public SingleKey(int Pin) { + pin = Pin; + GPIO.pinMode(pin, GPIO.INPUT); + } + + void keyScan() { + nowButtonState =GPIO.digitalRead(pin); + if (nowButtonState != lastButtonIOState) { + lastChangeTime = millis(); + } + if (millis() - lastChangeTime > decounceTime) { + if (buttonIOState != nowButtonState) { + buttonIOState = nowButtonState; + changeState = true; + if (buttonIOState == GPIO.LOW) { + //btnState = PRESSED; + //keyValue = pin; + //println("Key is Pressed !! "); + } else if (buttonIOState == GPIO.HIGH) { + //println("Key is Released !! "); + } + } + } + switch(btnState) { + case IDLE: + if (changeState) { + changeState = false; + btnState = PRESSED; + holdTimer = millis(); + keyValue = pin; + isPressed = true; + } + break; + case PRESSED: + if (millis() - holdTimer > holdTime) { + btnState = HOLD; + keyValue = pin; + isPressed = true; + isHold = true; + } else if (changeState) { + changeState = false; + btnState = RELEASED; + } else { + keyValue = -1; + isPressed = false; + } + break; + case HOLD: + keyValue = pin; + isPressed = true; + isHold = true; + if (changeState) { + changeState = false; + btnState = RELEASED; + } + break; + case RELEASED: + keyValue = -1; + isPressed = false; + isHold = false; + btnState = IDLE; + break; + } + lastButtonIOState = nowButtonState; + } +} diff --git a/Processing/Sketches/Sketch_14_1_1_Joystick/Sketch_14_1_1_Joystick.pde b/Processing/Sketches/Sketch_14_1_1_Joystick/Sketch_14_1_1_Joystick.pde index 009e58d..98480b0 100644 --- a/Processing/Sketches/Sketch_14_1_1_Joystick/Sketch_14_1_1_Joystick.pde +++ b/Processing/Sketches/Sketch_14_1_1_Joystick/Sketch_14_1_1_Joystick.pde @@ -5,10 +5,12 @@ * modification: 2020/03/11 *****************************************************/ import processing.io.*; -//Create a object of class ADCDevice +//Create an object of class ADCDevice ADCDevice adc = new ADCDevice(); -int cx, cy, cd, cr; //define the center point,side length & half. +int cx, cy, cd, cr; //define the center point, side length & half. +int buttonPin = 18; +SingleKey skey = new SingleKey(buttonPin); void setup() { size(640, 360); if (adc.detectI2C(0x48)) { @@ -28,7 +30,13 @@ void draw() { int x=0, y=0, z=0; x = adc.analogRead(0); //read the ADC of joystick y = adc.analogRead(1); // - z = adc.analogRead(2); + //z = adc.analogRead(2); + skey.keyScan(); //key scan + if (skey.isPressed) { //key is pressed + z=0; + } else { + z = 255; + } background(102); titleAndSiteInfo(); fill(0); diff --git a/Tutorial.pdf b/Tutorial.pdf index 502b7de..d1adc96 100644 Binary files a/Tutorial.pdf and b/Tutorial.pdf differ diff --git a/Tutorial_GPIOZero.pdf b/Tutorial_GPIOZero.pdf new file mode 100644 index 0000000..e6ef8b5 Binary files /dev/null and b/Tutorial_GPIOZero.pdf differ