├── .gitignore ├── Adafruit_ADS1x15 ├── Adafruit_ADS1x15.py ├── Adafruit_I2C.py ├── ads1x15_ex_comparator.py ├── ads1x15_ex_differential.py └── ads1x15_ex_singleended.py ├── Adafruit_I2C └── Adafruit_I2C.py ├── Adafruit_Python_BMP ├── .gitignore ├── Adafruit_BMP │ ├── BMP085.py │ └── __init__.py ├── LICENSE ├── README.md ├── examples │ ├── google_spreadsheet.py │ └── simpletest.py ├── ez_setup.py └── setup.py ├── Adafruit_Python_GPIO ├── .gitignore ├── Adafruit_GPIO │ ├── FT232H.py │ ├── GPIO.py │ ├── I2C.py │ ├── MCP230xx.py │ ├── PWM.py │ ├── Platform.py │ ├── SPI.py │ └── __init__.py ├── LICENSE ├── README.md ├── ez_setup.py ├── setup.py └── test │ ├── MockGPIO.py │ ├── test_GPIO.py │ ├── test_I2C.py │ ├── test_PWM.py │ ├── test_Platform.py │ └── test_SPI.py ├── MySQLFiles └── WeatherPi.sql ├── README.md ├── RTC_SDL_DS3231 ├── README.md ├── SDL_DS3231.py └── testSDL_DS3231.py ├── RaspberryPi-AS3935 ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── RPi_AS3935 │ ├── RPi_AS3935.py │ └── __init__.py ├── demo.py ├── requirements.txt ├── requirements │ ├── base.txt │ ├── flake8.txt │ └── pep8.txt ├── setup.cfg ├── setup.py ├── tests │ ├── conftest.py │ ├── test_disp_lco.py │ ├── test_disturbers.py │ ├── test_indoors.py │ ├── test_min_strikes.py │ └── test_noise_floor.py └── tox.ini ├── SDL_Pi_FRAM ├── README.md ├── SDL_Pi_FRAM.py └── testSDL_Pi_FRAM.py ├── WeatherPi.py ├── conf.py ├── doAllGraphs.py ├── graphs ├── BarometerLightningGraph.py ├── PowerCurrentGraph.py ├── PowerVoltageGraph.py ├── TemperatureHumidityGraph.py └── testTemp.py ├── htu21dflib ├── .gitignore ├── LICENSE ├── README.md ├── build.sh ├── dweetio ├── dweetio.c ├── htu21dflib ├── htu21dflib.c ├── htu21dflib.h ├── mqtt └── mqtt.c ├── pclogging.py ├── sendemail.py ├── startserver.sh ├── state ├── SunAirPlusStats.txt ├── WeatherCommand.txt └── WeatherStats.txt └── testAM2315.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | tx* 3 | .*.swp 4 | *.xml 5 | *.temp 6 | *.test 7 | nohup.out 8 | .*DS_Store 9 | conflocal.py 10 | -------------------------------------------------------------------------------- /Adafruit_ADS1x15/Adafruit_I2C.py: -------------------------------------------------------------------------------- 1 | ../Adafruit_I2C/Adafruit_I2C.py -------------------------------------------------------------------------------- /Adafruit_ADS1x15/ads1x15_ex_comparator.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import time, signal, sys 4 | from Adafruit_ADS1x15 import ADS1x15 5 | 6 | def signal_handler(signal, frame): 7 | print 'You pressed Ctrl+C!' 8 | print adc.getLastConversionResults()/1000.0 9 | adc.stopContinuousConversion() 10 | sys.exit(0) 11 | 12 | signal.signal(signal.SIGINT, signal_handler) 13 | # Print 'Press Ctrl+C to exit' 14 | 15 | ADS1015 = 0x00 # 12-bit ADC 16 | ADS1115 = 0x01 # 16-bit ADC 17 | 18 | # Initialise the ADC using the default mode (use default I2C address) 19 | # Set this to ADS1015 or ADS1115 depending on the ADC you are using! 20 | adc = ADS1x15(ic=ADS1115) 21 | 22 | # start comparator on channel 2 with a thresholdHigh=200mV and low=100mV 23 | # in traditional mode, non-latching, +/-1.024V and 250sps 24 | adc.startSingleEndedComparator(2, 200, 100, pga=1024, sps=250, activeLow=True, traditionalMode=True, latching=False, numReadings=1) 25 | 26 | while True: 27 | print adc.getLastConversionResults()/1000.0 28 | time.sleep(0.25) 29 | 30 | #time.sleep(0.1) 31 | -------------------------------------------------------------------------------- /Adafruit_ADS1x15/ads1x15_ex_differential.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import time, signal, sys 4 | from Adafruit_ADS1x15 import ADS1x15 5 | 6 | def signal_handler(signal, frame): 7 | #print 'You pressed Ctrl+C!' 8 | sys.exit(0) 9 | signal.signal(signal.SIGINT, signal_handler) 10 | #print 'Press Ctrl+C to exit' 11 | 12 | ADS1015 = 0x00 # 12-bit ADC 13 | ADS1115 = 0x01 # 16-bit ADC 14 | 15 | # Initialise the ADC using the default mode (use default I2C address) 16 | # Set this to ADS1015 or ADS1115 depending on the ADC you are using! 17 | adc = ADS1x15(ic=ADS1115) 18 | 19 | # Read channels 2 and 3 in single-ended mode, at +/-4.096V and 250sps 20 | volts2 = adc.readADCSingleEnded(2, 4096, 250)/1000.0 21 | volts3 = adc.readADCSingleEnded(3, 4096, 250)/1000.0 22 | 23 | # Now do a differential reading of channels 2 and 3 24 | voltsdiff = adc.readADCDifferential23(4096, 250)/1000.0 25 | 26 | # Display the two different reading for comparison purposes 27 | print "%.8f %.8f %.8f %.8f" % (volts2, volts3, volts3-volts2, -voltsdiff) 28 | -------------------------------------------------------------------------------- /Adafruit_ADS1x15/ads1x15_ex_singleended.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import time, signal, sys 4 | from Adafruit_ADS1x15 import ADS1x15 5 | 6 | def signal_handler(signal, frame): 7 | print 'You pressed Ctrl+C!' 8 | sys.exit(0) 9 | signal.signal(signal.SIGINT, signal_handler) 10 | #print 'Press Ctrl+C to exit' 11 | 12 | ADS1015 = 0x00 # 12-bit ADC 13 | ADS1115 = 0x01 # 16-bit ADC 14 | 15 | # Select the gain 16 | # gain = 6144 # +/- 6.144V 17 | gain = 4096 # +/- 4.096V 18 | # gain = 2048 # +/- 2.048V 19 | # gain = 1024 # +/- 1.024V 20 | # gain = 512 # +/- 0.512V 21 | # gain = 256 # +/- 0.256V 22 | 23 | # Select the sample rate 24 | # sps = 8 # 8 samples per second 25 | # sps = 16 # 16 samples per second 26 | # sps = 32 # 32 samples per second 27 | # sps = 64 # 64 samples per second 28 | # sps = 128 # 128 samples per second 29 | sps = 250 # 250 samples per second 30 | # sps = 475 # 475 samples per second 31 | # sps = 860 # 860 samples per second 32 | 33 | # Initialise the ADC using the default mode (use default I2C address) 34 | # Set this to ADS1015 or ADS1115 depending on the ADC you are using! 35 | adc = ADS1x15(ic=ADS1115) 36 | 37 | # Read channel 0 in single-ended mode using the settings above 38 | volts = adc.readADCSingleEnded(0, gain, sps) / 1000 39 | 40 | # To read channel 3 in single-ended mode, +/- 1.024V, 860 sps use: 41 | # volts = adc.readADCSingleEnded(3, 1024, 860) 42 | 43 | print "%.6f" % (volts) 44 | -------------------------------------------------------------------------------- /Adafruit_I2C/Adafruit_I2C.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import re 3 | import smbus 4 | 5 | # =========================================================================== 6 | # Adafruit_I2C Class 7 | # =========================================================================== 8 | 9 | class Adafruit_I2C(object): 10 | 11 | @staticmethod 12 | def getPiRevision(): 13 | "Gets the version number of the Raspberry Pi board" 14 | # Revision list available at: http://elinux.org/RPi_HardwareHistory#Board_Revision_History 15 | try: 16 | with open('/proc/cpuinfo', 'r') as infile: 17 | for line in infile: 18 | # Match a line of the form "Revision : 0002" while ignoring extra 19 | # info in front of the revsion (like 1000 when the Pi was over-volted). 20 | match = re.match('Revision\s+:\s+.*(\w{4})$', line) 21 | if match and match.group(1) in ['0000', '0002', '0003']: 22 | # Return revision 1 if revision ends with 0000, 0002 or 0003. 23 | return 1 24 | elif match: 25 | # Assume revision 2 if revision ends with any other 4 chars. 26 | return 2 27 | # Couldn't find the revision, assume revision 0 like older code for compatibility. 28 | return 0 29 | except: 30 | return 0 31 | 32 | @staticmethod 33 | def getPiI2CBusNumber(): 34 | # Gets the I2C bus number /dev/i2c# 35 | return 1 if Adafruit_I2C.getPiRevision() > 1 else 0 36 | 37 | def __init__(self, address, busnum=-1, debug=False): 38 | self.address = address 39 | # By default, the correct I2C bus is auto-detected using /proc/cpuinfo 40 | # Alternatively, you can hard-code the bus version below: 41 | # self.bus = smbus.SMBus(0); # Force I2C0 (early 256MB Pi's) 42 | # self.bus = smbus.SMBus(1); # Force I2C1 (512MB Pi's) 43 | self.bus = smbus.SMBus(busnum if busnum >= 0 else Adafruit_I2C.getPiI2CBusNumber()) 44 | self.debug = debug 45 | 46 | def reverseByteOrder(self, data): 47 | "Reverses the byte order of an int (16-bit) or long (32-bit) value" 48 | # Courtesy Vishal Sapre 49 | byteCount = len(hex(data)[2:].replace('L','')[::2]) 50 | val = 0 51 | for i in range(byteCount): 52 | val = (val << 8) | (data & 0xff) 53 | data >>= 8 54 | return val 55 | 56 | def errMsg(self): 57 | print "Error accessing 0x%02X: Check your I2C address" % self.address 58 | return -1 59 | 60 | def write8(self, reg, value): 61 | "Writes an 8-bit value to the specified register/address" 62 | try: 63 | self.bus.write_byte_data(self.address, reg, value) 64 | if self.debug: 65 | print "I2C: Wrote 0x%02X to register 0x%02X" % (value, reg) 66 | except IOError, err: 67 | return self.errMsg() 68 | 69 | def write16(self, reg, value): 70 | "Writes a 16-bit value to the specified register/address pair" 71 | try: 72 | self.bus.write_word_data(self.address, reg, value) 73 | if self.debug: 74 | print ("I2C: Wrote 0x%02X to register pair 0x%02X,0x%02X" % 75 | (value, reg, reg+1)) 76 | except IOError, err: 77 | return self.errMsg() 78 | 79 | def writeRaw8(self, value): 80 | "Writes an 8-bit value on the bus" 81 | try: 82 | self.bus.write_byte(self.address, value) 83 | if self.debug: 84 | print "I2C: Wrote 0x%02X" % value 85 | except IOError, err: 86 | return self.errMsg() 87 | 88 | def writeList(self, reg, list): 89 | "Writes an array of bytes using I2C format" 90 | try: 91 | if self.debug: 92 | print "I2C: Writing list to register 0x%02X:" % reg 93 | print list 94 | self.bus.write_i2c_block_data(self.address, reg, list) 95 | except IOError, err: 96 | return self.errMsg() 97 | 98 | def readList(self, reg, length): 99 | "Read a list of bytes from the I2C device" 100 | try: 101 | results = self.bus.read_i2c_block_data(self.address, reg, length) 102 | if self.debug: 103 | print ("I2C: Device 0x%02X returned the following from reg 0x%02X" % 104 | (self.address, reg)) 105 | print results 106 | return results 107 | except IOError, err: 108 | return self.errMsg() 109 | 110 | def readU8(self, reg): 111 | "Read an unsigned byte from the I2C device" 112 | try: 113 | result = self.bus.read_byte_data(self.address, reg) 114 | if self.debug: 115 | print ("I2C: Device 0x%02X returned 0x%02X from reg 0x%02X" % 116 | (self.address, result & 0xFF, reg)) 117 | return result 118 | except IOError, err: 119 | return self.errMsg() 120 | 121 | def readS8(self, reg): 122 | "Reads a signed byte from the I2C device" 123 | try: 124 | result = self.bus.read_byte_data(self.address, reg) 125 | if result > 127: result -= 256 126 | if self.debug: 127 | print ("I2C: Device 0x%02X returned 0x%02X from reg 0x%02X" % 128 | (self.address, result & 0xFF, reg)) 129 | return result 130 | except IOError, err: 131 | return self.errMsg() 132 | 133 | def readU16(self, reg, little_endian=True): 134 | "Reads an unsigned 16-bit value from the I2C device" 135 | try: 136 | result = self.bus.read_word_data(self.address,reg) 137 | # Swap bytes if using big endian because read_word_data assumes little 138 | # endian on ARM (little endian) systems. 139 | if not little_endian: 140 | result = ((result << 8) & 0xFF00) + (result >> 8) 141 | if (self.debug): 142 | print "I2C: Device 0x%02X returned 0x%04X from reg 0x%02X" % (self.address, result & 0xFFFF, reg) 143 | return result 144 | except IOError, err: 145 | return self.errMsg() 146 | 147 | def readS16(self, reg, little_endian=True): 148 | "Reads a signed 16-bit value from the I2C device" 149 | try: 150 | result = self.readU16(reg,little_endian) 151 | if result > 32767: result -= 65536 152 | return result 153 | except IOError, err: 154 | return self.errMsg() 155 | 156 | if __name__ == '__main__': 157 | try: 158 | bus = Adafruit_I2C(address=0) 159 | print "Default I2C bus is accessible" 160 | except: 161 | print "Error accessing default I2C bus" 162 | -------------------------------------------------------------------------------- /Adafruit_Python_BMP/.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | dist/ 3 | *.egg-info 4 | *.pyc 5 | -------------------------------------------------------------------------------- /Adafruit_Python_BMP/Adafruit_BMP/BMP085.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2014 Adafruit Industries 2 | # Author: Tony DiCola 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to deal 6 | # in the Software without restriction, including without limitation the rights 7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | # copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in 12 | # all copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | # THE SOFTWARE. 21 | import logging 22 | import time 23 | 24 | 25 | # BMP085 default address. 26 | BMP085_I2CADDR = 0x77 27 | 28 | # Operating Modes 29 | BMP085_ULTRALOWPOWER = 0 30 | BMP085_STANDARD = 1 31 | BMP085_HIGHRES = 2 32 | BMP085_ULTRAHIGHRES = 3 33 | 34 | # BMP085 Registers 35 | BMP085_CAL_AC1 = 0xAA # R Calibration data (16 bits) 36 | BMP085_CAL_AC2 = 0xAC # R Calibration data (16 bits) 37 | BMP085_CAL_AC3 = 0xAE # R Calibration data (16 bits) 38 | BMP085_CAL_AC4 = 0xB0 # R Calibration data (16 bits) 39 | BMP085_CAL_AC5 = 0xB2 # R Calibration data (16 bits) 40 | BMP085_CAL_AC6 = 0xB4 # R Calibration data (16 bits) 41 | BMP085_CAL_B1 = 0xB6 # R Calibration data (16 bits) 42 | BMP085_CAL_B2 = 0xB8 # R Calibration data (16 bits) 43 | BMP085_CAL_MB = 0xBA # R Calibration data (16 bits) 44 | BMP085_CAL_MC = 0xBC # R Calibration data (16 bits) 45 | BMP085_CAL_MD = 0xBE # R Calibration data (16 bits) 46 | BMP085_CONTROL = 0xF4 47 | BMP085_TEMPDATA = 0xF6 48 | BMP085_PRESSUREDATA = 0xF6 49 | 50 | # Commands 51 | BMP085_READTEMPCMD = 0x2E 52 | BMP085_READPRESSURECMD = 0x34 53 | 54 | 55 | class BMP085(object): 56 | def __init__(self, mode=BMP085_STANDARD, address=BMP085_I2CADDR, i2c=None, **kwargs): 57 | self._logger = logging.getLogger('Adafruit_BMP.BMP085') 58 | # Check that mode is valid. 59 | if mode not in [BMP085_ULTRALOWPOWER, BMP085_STANDARD, BMP085_HIGHRES, BMP085_ULTRAHIGHRES]: 60 | raise ValueError('Unexpected mode value {0}. Set mode to one of BMP085_ULTRALOWPOWER, BMP085_STANDARD, BMP085_HIGHRES, or BMP085_ULTRAHIGHRES'.format(mode)) 61 | self._mode = mode 62 | # Create I2C device. 63 | if i2c is None: 64 | import Adafruit_GPIO.I2C as I2C 65 | i2c = I2C 66 | self._device = i2c.get_i2c_device(address, **kwargs) 67 | # Load calibration values. 68 | self._load_calibration() 69 | 70 | def _load_calibration(self): 71 | self.cal_AC1 = self._device.readS16BE(BMP085_CAL_AC1) # INT16 72 | self.cal_AC2 = self._device.readS16BE(BMP085_CAL_AC2) # INT16 73 | self.cal_AC3 = self._device.readS16BE(BMP085_CAL_AC3) # INT16 74 | self.cal_AC4 = self._device.readU16BE(BMP085_CAL_AC4) # UINT16 75 | self.cal_AC5 = self._device.readU16BE(BMP085_CAL_AC5) # UINT16 76 | self.cal_AC6 = self._device.readU16BE(BMP085_CAL_AC6) # UINT16 77 | self.cal_B1 = self._device.readS16BE(BMP085_CAL_B1) # INT16 78 | self.cal_B2 = self._device.readS16BE(BMP085_CAL_B2) # INT16 79 | self.cal_MB = self._device.readS16BE(BMP085_CAL_MB) # INT16 80 | self.cal_MC = self._device.readS16BE(BMP085_CAL_MC) # INT16 81 | self.cal_MD = self._device.readS16BE(BMP085_CAL_MD) # INT16 82 | self._logger.debug('AC1 = {0:6d}'.format(self.cal_AC1)) 83 | self._logger.debug('AC2 = {0:6d}'.format(self.cal_AC2)) 84 | self._logger.debug('AC3 = {0:6d}'.format(self.cal_AC3)) 85 | self._logger.debug('AC4 = {0:6d}'.format(self.cal_AC4)) 86 | self._logger.debug('AC5 = {0:6d}'.format(self.cal_AC5)) 87 | self._logger.debug('AC6 = {0:6d}'.format(self.cal_AC6)) 88 | self._logger.debug('B1 = {0:6d}'.format(self.cal_B1)) 89 | self._logger.debug('B2 = {0:6d}'.format(self.cal_B2)) 90 | self._logger.debug('MB = {0:6d}'.format(self.cal_MB)) 91 | self._logger.debug('MC = {0:6d}'.format(self.cal_MC)) 92 | self._logger.debug('MD = {0:6d}'.format(self.cal_MD)) 93 | 94 | def _load_datasheet_calibration(self): 95 | # Set calibration from values in the datasheet example. Useful for debugging the 96 | # temp and pressure calculation accuracy. 97 | self.cal_AC1 = 408 98 | self.cal_AC2 = -72 99 | self.cal_AC3 = -14383 100 | self.cal_AC4 = 32741 101 | self.cal_AC5 = 32757 102 | self.cal_AC6 = 23153 103 | self.cal_B1 = 6190 104 | self.cal_B2 = 4 105 | self.cal_MB = -32767 106 | self.cal_MC = -8711 107 | self.cal_MD = 2868 108 | 109 | def read_raw_temp(self): 110 | """Reads the raw (uncompensated) temperature from the sensor.""" 111 | self._device.write8(BMP085_CONTROL, BMP085_READTEMPCMD) 112 | time.sleep(0.005) # Wait 5ms 113 | raw = self._device.readU16BE(BMP085_TEMPDATA) 114 | self._logger.debug('Raw temp 0x{0:X} ({1})'.format(raw & 0xFFFF, raw)) 115 | return raw 116 | 117 | def read_raw_pressure(self): 118 | """Reads the raw (uncompensated) pressure level from the sensor.""" 119 | self._device.write8(BMP085_CONTROL, BMP085_READPRESSURECMD + (self._mode << 6)) 120 | if self._mode == BMP085_ULTRALOWPOWER: 121 | time.sleep(0.005) 122 | elif self._mode == BMP085_HIGHRES: 123 | time.sleep(0.014) 124 | elif self._mode == BMP085_ULTRAHIGHRES: 125 | time.sleep(0.026) 126 | else: 127 | time.sleep(0.008) 128 | msb = self._device.readU8(BMP085_PRESSUREDATA) 129 | lsb = self._device.readU8(BMP085_PRESSUREDATA+1) 130 | xlsb = self._device.readU8(BMP085_PRESSUREDATA+2) 131 | raw = ((msb << 16) + (lsb << 8) + xlsb) >> (8 - self._mode) 132 | self._logger.debug('Raw pressure 0x{0:04X} ({1})'.format(raw & 0xFFFF, raw)) 133 | return raw 134 | 135 | def read_temperature(self): 136 | """Gets the compensated temperature in degrees celsius.""" 137 | UT = self.read_raw_temp() 138 | # Datasheet value for debugging: 139 | #UT = 27898 140 | # Calculations below are taken straight from section 3.5 of the datasheet. 141 | X1 = ((UT - self.cal_AC6) * self.cal_AC5) >> 15 142 | X2 = (self.cal_MC << 11) / (X1 + self.cal_MD) 143 | B5 = X1 + X2 144 | temp = ((B5 + 8) >> 4) / 10.0 145 | self._logger.debug('Calibrated temperature {0} C'.format(temp)) 146 | return temp 147 | 148 | def read_pressure(self): 149 | """Gets the compensated pressure in Pascals.""" 150 | UT = self.read_raw_temp() 151 | UP = self.read_raw_pressure() 152 | # Datasheet values for debugging: 153 | #UT = 27898 154 | #UP = 23843 155 | # Calculations below are taken straight from section 3.5 of the datasheet. 156 | # Calculate true temperature coefficient B5. 157 | X1 = ((UT - self.cal_AC6) * self.cal_AC5) >> 15 158 | X2 = (self.cal_MC << 11) / (X1 + self.cal_MD) 159 | B5 = X1 + X2 160 | self._logger.debug('B5 = {0}'.format(B5)) 161 | # Pressure Calculations 162 | B6 = B5 - 4000 163 | self._logger.debug('B6 = {0}'.format(B6)) 164 | X1 = (self.cal_B2 * (B6 * B6) >> 12) >> 11 165 | X2 = (self.cal_AC2 * B6) >> 11 166 | X3 = X1 + X2 167 | B3 = (((self.cal_AC1 * 4 + X3) << self._mode) + 2) / 4 168 | self._logger.debug('B3 = {0}'.format(B3)) 169 | X1 = (self.cal_AC3 * B6) >> 13 170 | X2 = (self.cal_B1 * ((B6 * B6) >> 12)) >> 16 171 | X3 = ((X1 + X2) + 2) >> 2 172 | B4 = (self.cal_AC4 * (X3 + 32768)) >> 15 173 | self._logger.debug('B4 = {0}'.format(B4)) 174 | B7 = (UP - B3) * (50000 >> self._mode) 175 | self._logger.debug('B7 = {0}'.format(B7)) 176 | if B7 < 0x80000000: 177 | p = (B7 * 2) / B4 178 | else: 179 | p = (B7 / B4) * 2 180 | X1 = (p >> 8) * (p >> 8) 181 | X1 = (X1 * 3038) >> 16 182 | X2 = (-7357 * p) >> 16 183 | p = p + ((X1 + X2 + 3791) >> 4) 184 | self._logger.debug('Pressure {0} Pa'.format(p)) 185 | return p 186 | 187 | def read_altitude(self, sealevel_pa=101325.0): 188 | """Calculates the altitude in meters.""" 189 | # Calculation taken straight from section 3.6 of the datasheet. 190 | pressure = float(self.read_pressure()) 191 | altitude = 44330.0 * (1.0 - pow(pressure / sealevel_pa, (1.0/5.255))) 192 | self._logger.debug('Altitude {0} m'.format(altitude)) 193 | return altitude 194 | 195 | def read_sealevel_pressure(self, altitude_m=0.0): 196 | """Calculates the pressure at sealevel when given a known altitude in 197 | meters. Returns a value in Pascals.""" 198 | pressure = float(self.read_pressure()) 199 | p0 = pressure / pow(1.0 - altitude_m/44330.0, 5.255) 200 | self._logger.debug('Sealevel pressure {0} Pa'.format(p0)) 201 | return p0 202 | -------------------------------------------------------------------------------- /Adafruit_Python_BMP/Adafruit_BMP/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/switchdoclabs/WeatherPi/87027860195698521da8bd33cc0cd81115da0d59/Adafruit_Python_BMP/Adafruit_BMP/__init__.py -------------------------------------------------------------------------------- /Adafruit_Python_BMP/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Adafruit Industries 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /Adafruit_Python_BMP/README.md: -------------------------------------------------------------------------------- 1 | Adafruit Python BMP 2 | =================== 3 | 4 | Python library for accessing the BMP series pressure and temperature sensors like the BMP085/BMP180 on a Raspberry Pi or Beaglebone Black. 5 | 6 | Designed specifically to work with the Adafruit BMP085/BMP180 pressure sensors ----> https://www.adafruit.com/products/1603 7 | 8 | To install, download the library by clicking the download zip link to the right and unzip the archive somewhere on your Raspberry Pi or Beaglebone Black. Then execute the following command in the directory of the library: 9 | 10 | ```` 11 | sudo python setup.py install 12 | ```` 13 | 14 | Make sure you have internet access on the device so it can download the required dependencies. 15 | 16 | See examples of usage in the examples folder. 17 | 18 | Adafruit invests time and resources providing this open source code, please support Adafruit and open-source hardware by purchasing products from Adafruit! 19 | 20 | Written by Tony DiCola for Adafruit Industries. 21 | MIT license, all text above must be included in any redistribution 22 | -------------------------------------------------------------------------------- /Adafruit_Python_BMP/examples/google_spreadsheet.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | # Google Spreadsheet BMP Sensor Data-logging Example 4 | 5 | # Depends on the 'gspread' package being installed. If you have pip installed 6 | # execute: 7 | # sudo pip install gspread 8 | 9 | # Copyright (c) 2014 Adafruit Industries 10 | # Author: Tony DiCola 11 | 12 | # Permission is hereby granted, free of charge, to any person obtaining a copy 13 | # of this software and associated documentation files (the "Software"), to deal 14 | # in the Software without restriction, including without limitation the rights 15 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 16 | # copies of the Software, and to permit persons to whom the Software is 17 | # furnished to do so, subject to the following conditions: 18 | 19 | # The above copyright notice and this permission notice shall be included in all 20 | # copies or substantial portions of the Software. 21 | 22 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 27 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 28 | # SOFTWARE. 29 | import sys 30 | import time 31 | import datetime 32 | 33 | import Adafruit_BMP.BMP085 as BMP085 34 | import gspread 35 | 36 | # Google Docs account email, password, and spreadsheet name. 37 | GDOCS_EMAIL = 'your google docs account email address' 38 | GDOCS_PASSWORD = 'your google docs account password' 39 | GDOCS_SPREADSHEET_NAME = 'your google docs spreadsheet name' 40 | 41 | # How long to wait (in seconds) between measurements. 42 | FREQUENCY_SECONDS = 30 43 | 44 | 45 | def login_open_sheet(email, password, spreadsheet): 46 | """Connect to Google Docs spreadsheet and return the first worksheet.""" 47 | try: 48 | gc = gspread.login(email, password) 49 | worksheet = gc.open(spreadsheet).sheet1 50 | return worksheet 51 | except: 52 | print 'Unable to login and get spreadsheet. Check email, password, spreadsheet name.' 53 | sys.exit(1) 54 | 55 | 56 | # Create sensor instance with default I2C bus (On Raspberry Pi either 0 or 57 | # 1 based on the revision, on Beaglebone Black default to 1). 58 | bmp = BMP085.BMP085() 59 | 60 | print 'Logging sensor measurements to {0} every {1} seconds.'.format(GDOCS_SPREADSHEET_NAME, FREQUENCY_SECONDS) 61 | print 'Press Ctrl-C to quit.' 62 | worksheet = None 63 | while True: 64 | # Login if necessary. 65 | if worksheet is None: 66 | worksheet = login_open_sheet(GDOCS_EMAIL, GDOCS_PASSWORD, GDOCS_SPREADSHEET_NAME) 67 | 68 | # Attempt to get sensor readings. 69 | temp = bmp.read_temperature() 70 | pressure = bmp.read_pressure() 71 | altitude = bmp.read_altitude() 72 | 73 | print 'Temperature: {0:0.1f} C'.format(temp) 74 | print 'Pressure: {0:0.1f} Pa'.format(pressure) 75 | print 'Altitude: {0:0.1f} m'.format(altitude) 76 | 77 | # Append the data in the spreadsheet, including a timestamp 78 | try: 79 | worksheet.append_row((datetime.datetime.now(), temp, pressure, altitude)) 80 | except: 81 | # Error appending data, most likely because credentials are stale. 82 | # Null out the worksheet so a login is performed at the top of the loop. 83 | print 'Append error, logging in again' 84 | worksheet = None 85 | time.sleep(FREQUENCY_SECONDS) 86 | continue 87 | 88 | # Wait 30 seconds before continuing 89 | print 'Wrote a row to {0}'.format(GDOCS_SPREADSHEET_NAME) 90 | time.sleep(FREQUENCY_SECONDS) 91 | -------------------------------------------------------------------------------- /Adafruit_Python_BMP/examples/simpletest.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # Copyright (c) 2014 Adafruit Industries 3 | # Author: Tony DiCola 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | # THE SOFTWARE. 22 | 23 | # Can enable debug output by uncommenting: 24 | #import logging 25 | #logging.basicConfig(level=logging.DEBUG) 26 | 27 | import Adafruit_BMP.BMP085 as BMP085 28 | 29 | # Default constructor will pick a default I2C bus. 30 | # 31 | # For the Raspberry Pi this means you should hook up to the only exposed I2C bus 32 | # from the main GPIO header and the library will figure out the bus number based 33 | # on the Pi's revision. 34 | # 35 | # For the Beaglebone Black the library will assume bus 1 by default, which is 36 | # exposed with SCL = P9_19 and SDA = P9_20. 37 | sensor = BMP085.BMP085() 38 | 39 | # Optionally you can override the bus number: 40 | #sensor = BMP085.BMP085(busnum=2) 41 | 42 | # You can also optionally change the BMP085 mode to one of BMP085_ULTRALOWPOWER, 43 | # BMP085_STANDARD, BMP085_HIGHRES, or BMP085_ULTRAHIGHRES. See the BMP085 44 | # datasheet for more details on the meanings of each mode (accuracy and power 45 | # consumption are primarily the differences). The default mode is STANDARD. 46 | #sensor = BMP085.BMP085(mode=BMP085.BMP085_ULTRAHIGHRES) 47 | 48 | print 'Temp = {0:0.2f} *C'.format(sensor.read_temperature()) 49 | print 'Pressure = {0:0.2f} Pa'.format(sensor.read_pressure()) 50 | print 'Altitude = {0:0.2f} m'.format(sensor.read_altitude()) 51 | print 'Sealevel Pressure = {0:0.2f} Pa'.format(sensor.read_sealevel_pressure()) 52 | -------------------------------------------------------------------------------- /Adafruit_Python_BMP/ez_setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Bootstrap setuptools installation 3 | 4 | To use setuptools in your package's setup.py, include this 5 | file in the same directory and add this to the top of your setup.py:: 6 | 7 | from ez_setup import use_setuptools 8 | use_setuptools() 9 | 10 | To require a specific version of setuptools, set a download 11 | mirror, or use an alternate download directory, simply supply 12 | the appropriate options to ``use_setuptools()``. 13 | 14 | This file can also be run as a script to install or upgrade setuptools. 15 | """ 16 | import os 17 | import shutil 18 | import sys 19 | import tempfile 20 | import zipfile 21 | import optparse 22 | import subprocess 23 | import platform 24 | import textwrap 25 | import contextlib 26 | 27 | from distutils import log 28 | 29 | try: 30 | from site import USER_SITE 31 | except ImportError: 32 | USER_SITE = None 33 | 34 | DEFAULT_VERSION = "3.5.1" 35 | DEFAULT_URL = "https://pypi.python.org/packages/source/s/setuptools/" 36 | 37 | def _python_cmd(*args): 38 | """ 39 | Return True if the command succeeded. 40 | """ 41 | args = (sys.executable,) + args 42 | return subprocess.call(args) == 0 43 | 44 | 45 | def _install(archive_filename, install_args=()): 46 | with archive_context(archive_filename): 47 | # installing 48 | log.warn('Installing Setuptools') 49 | if not _python_cmd('setup.py', 'install', *install_args): 50 | log.warn('Something went wrong during the installation.') 51 | log.warn('See the error message above.') 52 | # exitcode will be 2 53 | return 2 54 | 55 | 56 | def _build_egg(egg, archive_filename, to_dir): 57 | with archive_context(archive_filename): 58 | # building an egg 59 | log.warn('Building a Setuptools egg in %s', to_dir) 60 | _python_cmd('setup.py', '-q', 'bdist_egg', '--dist-dir', to_dir) 61 | # returning the result 62 | log.warn(egg) 63 | if not os.path.exists(egg): 64 | raise IOError('Could not build the egg.') 65 | 66 | 67 | def get_zip_class(): 68 | """ 69 | Supplement ZipFile class to support context manager for Python 2.6 70 | """ 71 | class ContextualZipFile(zipfile.ZipFile): 72 | def __enter__(self): 73 | return self 74 | def __exit__(self, type, value, traceback): 75 | self.close 76 | return zipfile.ZipFile if hasattr(zipfile.ZipFile, '__exit__') else \ 77 | ContextualZipFile 78 | 79 | 80 | @contextlib.contextmanager 81 | def archive_context(filename): 82 | # extracting the archive 83 | tmpdir = tempfile.mkdtemp() 84 | log.warn('Extracting in %s', tmpdir) 85 | old_wd = os.getcwd() 86 | try: 87 | os.chdir(tmpdir) 88 | with get_zip_class()(filename) as archive: 89 | archive.extractall() 90 | 91 | # going in the directory 92 | subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0]) 93 | os.chdir(subdir) 94 | log.warn('Now working in %s', subdir) 95 | yield 96 | 97 | finally: 98 | os.chdir(old_wd) 99 | shutil.rmtree(tmpdir) 100 | 101 | 102 | def _do_download(version, download_base, to_dir, download_delay): 103 | egg = os.path.join(to_dir, 'setuptools-%s-py%d.%d.egg' 104 | % (version, sys.version_info[0], sys.version_info[1])) 105 | if not os.path.exists(egg): 106 | archive = download_setuptools(version, download_base, 107 | to_dir, download_delay) 108 | _build_egg(egg, archive, to_dir) 109 | sys.path.insert(0, egg) 110 | 111 | # Remove previously-imported pkg_resources if present (see 112 | # https://bitbucket.org/pypa/setuptools/pull-request/7/ for details). 113 | if 'pkg_resources' in sys.modules: 114 | del sys.modules['pkg_resources'] 115 | 116 | import setuptools 117 | setuptools.bootstrap_install_from = egg 118 | 119 | 120 | def use_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL, 121 | to_dir=os.curdir, download_delay=15): 122 | to_dir = os.path.abspath(to_dir) 123 | rep_modules = 'pkg_resources', 'setuptools' 124 | imported = set(sys.modules).intersection(rep_modules) 125 | try: 126 | import pkg_resources 127 | except ImportError: 128 | return _do_download(version, download_base, to_dir, download_delay) 129 | try: 130 | pkg_resources.require("setuptools>=" + version) 131 | return 132 | except pkg_resources.DistributionNotFound: 133 | return _do_download(version, download_base, to_dir, download_delay) 134 | except pkg_resources.VersionConflict as VC_err: 135 | if imported: 136 | msg = textwrap.dedent(""" 137 | The required version of setuptools (>={version}) is not available, 138 | and can't be installed while this script is running. Please 139 | install a more recent version first, using 140 | 'easy_install -U setuptools'. 141 | 142 | (Currently using {VC_err.args[0]!r}) 143 | """).format(VC_err=VC_err, version=version) 144 | sys.stderr.write(msg) 145 | sys.exit(2) 146 | 147 | # otherwise, reload ok 148 | del pkg_resources, sys.modules['pkg_resources'] 149 | return _do_download(version, download_base, to_dir, download_delay) 150 | 151 | def _clean_check(cmd, target): 152 | """ 153 | Run the command to download target. If the command fails, clean up before 154 | re-raising the error. 155 | """ 156 | try: 157 | subprocess.check_call(cmd) 158 | except subprocess.CalledProcessError: 159 | if os.access(target, os.F_OK): 160 | os.unlink(target) 161 | raise 162 | 163 | def download_file_powershell(url, target): 164 | """ 165 | Download the file at url to target using Powershell (which will validate 166 | trust). Raise an exception if the command cannot complete. 167 | """ 168 | target = os.path.abspath(target) 169 | cmd = [ 170 | 'powershell', 171 | '-Command', 172 | "(new-object System.Net.WebClient).DownloadFile(%(url)r, %(target)r)" % vars(), 173 | ] 174 | _clean_check(cmd, target) 175 | 176 | def has_powershell(): 177 | if platform.system() != 'Windows': 178 | return False 179 | cmd = ['powershell', '-Command', 'echo test'] 180 | devnull = open(os.path.devnull, 'wb') 181 | try: 182 | try: 183 | subprocess.check_call(cmd, stdout=devnull, stderr=devnull) 184 | except Exception: 185 | return False 186 | finally: 187 | devnull.close() 188 | return True 189 | 190 | download_file_powershell.viable = has_powershell 191 | 192 | def download_file_curl(url, target): 193 | cmd = ['curl', url, '--silent', '--output', target] 194 | _clean_check(cmd, target) 195 | 196 | def has_curl(): 197 | cmd = ['curl', '--version'] 198 | devnull = open(os.path.devnull, 'wb') 199 | try: 200 | try: 201 | subprocess.check_call(cmd, stdout=devnull, stderr=devnull) 202 | except Exception: 203 | return False 204 | finally: 205 | devnull.close() 206 | return True 207 | 208 | download_file_curl.viable = has_curl 209 | 210 | def download_file_wget(url, target): 211 | cmd = ['wget', url, '--quiet', '--output-document', target] 212 | _clean_check(cmd, target) 213 | 214 | def has_wget(): 215 | cmd = ['wget', '--version'] 216 | devnull = open(os.path.devnull, 'wb') 217 | try: 218 | try: 219 | subprocess.check_call(cmd, stdout=devnull, stderr=devnull) 220 | except Exception: 221 | return False 222 | finally: 223 | devnull.close() 224 | return True 225 | 226 | download_file_wget.viable = has_wget 227 | 228 | def download_file_insecure(url, target): 229 | """ 230 | Use Python to download the file, even though it cannot authenticate the 231 | connection. 232 | """ 233 | try: 234 | from urllib.request import urlopen 235 | except ImportError: 236 | from urllib2 import urlopen 237 | src = dst = None 238 | try: 239 | src = urlopen(url) 240 | # Read/write all in one block, so we don't create a corrupt file 241 | # if the download is interrupted. 242 | data = src.read() 243 | dst = open(target, "wb") 244 | dst.write(data) 245 | finally: 246 | if src: 247 | src.close() 248 | if dst: 249 | dst.close() 250 | 251 | download_file_insecure.viable = lambda: True 252 | 253 | def get_best_downloader(): 254 | downloaders = [ 255 | download_file_powershell, 256 | download_file_curl, 257 | download_file_wget, 258 | download_file_insecure, 259 | ] 260 | 261 | for dl in downloaders: 262 | if dl.viable(): 263 | return dl 264 | 265 | def download_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL, 266 | to_dir=os.curdir, delay=15, downloader_factory=get_best_downloader): 267 | """ 268 | Download setuptools from a specified location and return its filename 269 | 270 | `version` should be a valid setuptools version number that is available 271 | as an egg for download under the `download_base` URL (which should end 272 | with a '/'). `to_dir` is the directory where the egg will be downloaded. 273 | `delay` is the number of seconds to pause before an actual download 274 | attempt. 275 | 276 | ``downloader_factory`` should be a function taking no arguments and 277 | returning a function for downloading a URL to a target. 278 | """ 279 | # making sure we use the absolute path 280 | to_dir = os.path.abspath(to_dir) 281 | zip_name = "setuptools-%s.zip" % version 282 | url = download_base + zip_name 283 | saveto = os.path.join(to_dir, zip_name) 284 | if not os.path.exists(saveto): # Avoid repeated downloads 285 | log.warn("Downloading %s", url) 286 | downloader = downloader_factory() 287 | downloader(url, saveto) 288 | return os.path.realpath(saveto) 289 | 290 | def _build_install_args(options): 291 | """ 292 | Build the arguments to 'python setup.py install' on the setuptools package 293 | """ 294 | return ['--user'] if options.user_install else [] 295 | 296 | def _parse_args(): 297 | """ 298 | Parse the command line for options 299 | """ 300 | parser = optparse.OptionParser() 301 | parser.add_option( 302 | '--user', dest='user_install', action='store_true', default=False, 303 | help='install in user site package (requires Python 2.6 or later)') 304 | parser.add_option( 305 | '--download-base', dest='download_base', metavar="URL", 306 | default=DEFAULT_URL, 307 | help='alternative URL from where to download the setuptools package') 308 | parser.add_option( 309 | '--insecure', dest='downloader_factory', action='store_const', 310 | const=lambda: download_file_insecure, default=get_best_downloader, 311 | help='Use internal, non-validating downloader' 312 | ) 313 | parser.add_option( 314 | '--version', help="Specify which version to download", 315 | default=DEFAULT_VERSION, 316 | ) 317 | options, args = parser.parse_args() 318 | # positional arguments are ignored 319 | return options 320 | 321 | def main(): 322 | """Install or upgrade setuptools and EasyInstall""" 323 | options = _parse_args() 324 | archive = download_setuptools( 325 | version=options.version, 326 | download_base=options.download_base, 327 | downloader_factory=options.downloader_factory, 328 | ) 329 | return _install(archive, _build_install_args(options)) 330 | 331 | if __name__ == '__main__': 332 | sys.exit(main()) 333 | -------------------------------------------------------------------------------- /Adafruit_Python_BMP/setup.py: -------------------------------------------------------------------------------- 1 | from ez_setup import use_setuptools 2 | use_setuptools() 3 | from setuptools import setup, find_packages 4 | 5 | setup(name = 'Adafruit_BMP', 6 | version = '1.5.0', 7 | author = 'Tony DiCola', 8 | author_email = 'tdicola@adafruit.com', 9 | description = 'Library for accessing the BMP series pressure and temperature sensors like the BMP085/BMP180 on a Raspberry Pi or Beaglebone Black.', 10 | license = 'MIT', 11 | url = 'https://github.com/adafruit/Adafruit_Python_BMP/', 12 | dependency_links = ['https://github.com/adafruit/Adafruit_Python_GPIO/tarball/master#egg=Adafruit-GPIO-0.6.5'], 13 | install_requires = ['Adafruit-GPIO>=0.6.5'], 14 | packages = find_packages()) 15 | -------------------------------------------------------------------------------- /Adafruit_Python_GPIO/.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | dist/ 3 | *.egg-info 4 | *.pyc 5 | setuptools* 6 | 7 | .idea -------------------------------------------------------------------------------- /Adafruit_Python_GPIO/Adafruit_GPIO/I2C.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2014 Adafruit Industries 2 | # Author: Tony DiCola 3 | # Based on Adafruit_I2C.py created by Kevin Townsend. 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | # THE SOFTWARE. 22 | import logging 23 | import subprocess 24 | 25 | import smbus 26 | 27 | import Adafruit_GPIO.Platform as Platform 28 | 29 | 30 | def reverseByteOrder(data): 31 | """Reverses the byte order of an int (16-bit) or long (32-bit) value.""" 32 | # Courtesy Vishal Sapre 33 | byteCount = len(hex(data)[2:].replace('L','')[::2]) 34 | val = 0 35 | for i in range(byteCount): 36 | val = (val << 8) | (data & 0xff) 37 | data >>= 8 38 | return val 39 | 40 | def get_default_bus(): 41 | """Return the default bus number based on the device platform. For a 42 | Raspberry Pi either bus 0 or 1 (based on the Pi revision) will be returned. 43 | For a Beaglebone Black the first user accessible bus, 1, will be returned. 44 | """ 45 | plat = Platform.platform_detect() 46 | if plat == Platform.RASPBERRY_PI: 47 | if Platform.pi_revision() == 1: 48 | # Revision 1 Pi uses I2C bus 0. 49 | return 0 50 | else: 51 | # Revision 2 Pi uses I2C bus 1. 52 | return 1 53 | elif plat == Platform.BEAGLEBONE_BLACK: 54 | # Beaglebone Black has multiple I2C buses, default to 1 (P9_19 and P9_20). 55 | return 1 56 | else: 57 | raise RuntimeError('Could not determine default I2C bus for platform.') 58 | 59 | def get_i2c_device(address, busnum=None, **kwargs): 60 | """Return an I2C device for the specified address and on the specified bus. 61 | If busnum isn't specified, the default I2C bus for the platform will attempt 62 | to be detected. 63 | """ 64 | if busnum is None: 65 | busnum = get_default_bus() 66 | return Device(address, busnum, **kwargs) 67 | 68 | def require_repeated_start(): 69 | """Enable repeated start conditions for I2C register reads. This is the 70 | normal behavior for I2C, however on some platforms like the Raspberry Pi 71 | there are bugs which disable repeated starts unless explicitly enabled with 72 | this function. See this thread for more details: 73 | http://www.raspberrypi.org/forums/viewtopic.php?f=44&t=15840 74 | """ 75 | plat = Platform.platform_detect() 76 | if plat == Platform.RASPBERRY_PI: 77 | # On the Raspberry Pi there is a bug where register reads don't send a 78 | # repeated start condition like the kernel smbus I2C driver functions 79 | # define. As a workaround this bit in the BCM2708 driver sysfs tree can 80 | # be changed to enable I2C repeated starts. 81 | subprocess.check_call('chmod 666 /sys/module/i2c_bcm2708/parameters/combined', shell=True) 82 | subprocess.check_call('echo -n 1 > /sys/module/i2c_bcm2708/parameters/combined', shell=True) 83 | # Other platforms are a no-op because they (presumably) have the correct 84 | # behavior and send repeated starts. 85 | 86 | 87 | class Device(object): 88 | """Class for communicating with an I2C device using the smbus library. 89 | Allows reading and writing 8-bit, 16-bit, and byte array values to registers 90 | on the device.""" 91 | def __init__(self, address, busnum): 92 | """Create an instance of the I2C device at the specified address on the 93 | specified I2C bus number.""" 94 | self._address = address 95 | self._bus = smbus.SMBus(busnum) 96 | self._logger = logging.getLogger('Adafruit_I2C.Device.Bus.{0}.Address.{1:#0X}' \ 97 | .format(busnum, address)) 98 | 99 | def writeRaw8(self, value): 100 | """Write an 8-bit value on the bus (without register).""" 101 | value = value & 0xFF 102 | self._bus.write_byte(self._address, value) 103 | self._logger.debug("Wrote 0x%02X", 104 | value) 105 | 106 | def write8(self, register, value): 107 | """Write an 8-bit value to the specified register.""" 108 | value = value & 0xFF 109 | self._bus.write_byte_data(self._address, register, value) 110 | self._logger.debug("Wrote 0x%02X to register 0x%02X", 111 | value, register) 112 | 113 | def write16(self, register, value): 114 | """Write a 16-bit value to the specified register.""" 115 | value = value & 0xFFFF 116 | self._bus.write_word_data(self._address, register, value) 117 | self._logger.debug("Wrote 0x%04X to register pair 0x%02X, 0x%02X", 118 | value, register, register+1) 119 | 120 | def writeList(self, register, data): 121 | """Write bytes to the specified register.""" 122 | self._bus.write_i2c_block_data(self._address, register, data) 123 | self._logger.debug("Wrote to register 0x%02X: %s", 124 | register, data) 125 | 126 | def readList(self, register, length): 127 | """Read a length number of bytes from the specified register. Results 128 | will be returned as a bytearray.""" 129 | results = self._bus.read_i2c_block_data(self._address, register, length) 130 | self._logger.debug("Read the following from register 0x%02X: %s", 131 | register, results) 132 | return results 133 | 134 | def readRaw8(self): 135 | """Read an 8-bit value on the bus (without register).""" 136 | result = self._bus.read_byte(self._address) & 0xFF 137 | self._logger.debug("Read 0x%02X", 138 | result) 139 | return result 140 | 141 | def readU8(self, register): 142 | """Read an unsigned byte from the specified register.""" 143 | result = self._bus.read_byte_data(self._address, register) & 0xFF 144 | self._logger.debug("Read 0x%02X from register 0x%02X", 145 | result, register) 146 | return result 147 | 148 | def readS8(self, register): 149 | """Read a signed byte from the specified register.""" 150 | result = self.readU8(register) 151 | if result > 127: 152 | result -= 256 153 | return result 154 | 155 | def readU16(self, register, little_endian=True): 156 | """Read an unsigned 16-bit value from the specified register, with the 157 | specified endianness (default little endian, or least significant byte 158 | first).""" 159 | result = self._bus.read_word_data(self._address,register) & 0xFFFF 160 | self._logger.debug("Read 0x%04X from register pair 0x%02X, 0x%02X", 161 | result, register, register+1) 162 | # Swap bytes if using big endian because read_word_data assumes little 163 | # endian on ARM (little endian) systems. 164 | if not little_endian: 165 | result = ((result << 8) & 0xFF00) + (result >> 8) 166 | return result 167 | 168 | def readS16(self, register, little_endian=True): 169 | """Read a signed 16-bit value from the specified register, with the 170 | specified endianness (default little endian, or least significant byte 171 | first).""" 172 | result = self.readU16(register, little_endian) 173 | if result > 32767: 174 | result -= 65536 175 | return result 176 | 177 | def readU16LE(self, register): 178 | """Read an unsigned 16-bit value from the specified register, in little 179 | endian byte order.""" 180 | return self.readU16(register, little_endian=True) 181 | 182 | def readU16BE(self, register): 183 | """Read an unsigned 16-bit value from the specified register, in big 184 | endian byte order.""" 185 | return self.readU16(register, little_endian=False) 186 | 187 | def readS16LE(self, register): 188 | """Read a signed 16-bit value from the specified register, in little 189 | endian byte order.""" 190 | return self.readS16(register, little_endian=True) 191 | 192 | def readS16BE(self, register): 193 | """Read a signed 16-bit value from the specified register, in big 194 | endian byte order.""" 195 | return self.readS16(register, little_endian=False) 196 | -------------------------------------------------------------------------------- /Adafruit_Python_GPIO/Adafruit_GPIO/MCP230xx.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2014 Adafruit Industries 2 | # Author: Tony DiCola 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to deal 6 | # in the Software without restriction, including without limitation the rights 7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | # copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in 12 | # all copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | # THE SOFTWARE. 21 | import math 22 | 23 | import Adafruit_GPIO as GPIO 24 | import Adafruit_GPIO.I2C as I2C 25 | 26 | 27 | class MCP230xxBase(GPIO.BaseGPIO): 28 | """Base class to represent an MCP230xx series GPIO extender. Is compatible 29 | with the Adafruit_GPIO BaseGPIO class so it can be used as a custom GPIO 30 | class for interacting with device. 31 | """ 32 | 33 | def __init__(self, address, i2c=None, **kwargs): 34 | """Initialize MCP230xx at specified I2C address and bus number. If bus 35 | is not specified it will default to the appropriate platform detected bus. 36 | """ 37 | # Create I2C device. 38 | if i2c is None: 39 | import Adafruit_GPIO.I2C as I2C 40 | i2c = I2C 41 | self._device = i2c.get_i2c_device(address, **kwargs) 42 | # Assume starting in ICON.BANK = 0 mode (sequential access). 43 | # Compute how many bytes are needed to store count of GPIO. 44 | self.gpio_bytes = int(math.ceil(self.NUM_GPIO/8.0)) 45 | # Buffer register values so they can be changed without reading. 46 | self.iodir = [0x00]*self.gpio_bytes # Default direction to all inputs. 47 | self.gppu = [0x00]*self.gpio_bytes # Default to pullups disabled. 48 | self.gpio = [0x00]*self.gpio_bytes 49 | # Write current direction and pullup buffer state. 50 | self.write_iodir() 51 | self.write_gppu() 52 | 53 | def _validate_pin(self, pin): 54 | # Raise an exception if pin is outside the range of allowed values. 55 | if pin < 0 or pin >= self.NUM_GPIO: 56 | raise ValueError('Invalid GPIO value, must be between 0 and {0}.'.format(self.NUM_GPIO)) 57 | 58 | def setup(self, pin, value): 59 | """Set the input or output mode for a specified pin. Mode should be 60 | either GPIO.OUT or GPIO.IN. 61 | """ 62 | self._validate_pin(pin) 63 | # Set bit to 1 for input or 0 for output. 64 | if value == GPIO.IN: 65 | self.iodir[int(pin/8)] |= 1 << (int(pin%8)) 66 | elif value == GPIO.OUT: 67 | self.iodir[int(pin/8)] &= ~(1 << (int(pin%8))) 68 | else: 69 | raise ValueError('Unexpected value. Must be GPIO.IN or GPIO.OUT.') 70 | self.write_iodir() 71 | 72 | def output(self, pin, value): 73 | """Set the specified pin the provided high/low value. Value should be 74 | either GPIO.HIGH/GPIO.LOW or a boolean (True = high). 75 | """ 76 | self._validate_pin(pin) 77 | # Set bit on or off. 78 | if value: 79 | self.gpio[int(pin/8)] |= 1 << (int(pin%8)) 80 | else: 81 | self.gpio[int(pin/8)] &= ~(1 << (int(pin%8))) 82 | # Write GPIO state. 83 | self.write_gpio() 84 | 85 | def output_pins(self, pins): 86 | """Set multiple pins high or low at once. Pins should be a dict of pin 87 | name to pin value (HIGH/True for 1, LOW/False for 0). All provided pins 88 | will be set to the given values. 89 | """ 90 | # Set each changed pin's bit. 91 | for pin, value in pins.iteritems(): 92 | if value: 93 | self.gpio[int(pin/8)] |= 1 << (int(pin%8)) 94 | else: 95 | self.gpio[int(pin/8)] &= ~(1 << (int(pin%8))) 96 | # Write GPIO state. 97 | self.write_gpio() 98 | 99 | def input(self, pin): 100 | """Read the specified pin and return GPIO.HIGH/True if the pin is pulled 101 | high, or GPIO.LOW/False if pulled low. 102 | """ 103 | self._validate_pin(pin) 104 | # Get GPIO state. 105 | gpio = self._device.readList(self.GPIO, self.gpio_bytes) 106 | # Return True if pin's bit is set. 107 | return (gpio[int(pin/8)] & 1 << (int(pin%8))) > 0 108 | 109 | def pullup(self, pin, enabled): 110 | """Turn on the pull-up resistor for the specified pin if enabled is True, 111 | otherwise turn off the pull-up resistor. 112 | """ 113 | self._validate_pin(pin) 114 | if enabled: 115 | self.gppu[int(pin/8)] |= 1 << (int(pin%8)) 116 | else: 117 | self.gppu[int(pin/8)] &= ~(1 << (int(pin%8))) 118 | self.write_gppu() 119 | 120 | def write_gpio(self, gpio=None): 121 | """Write the specified byte value to the GPIO registor. If no value 122 | specified the current buffered value will be written. 123 | """ 124 | if gpio is not None: 125 | self.gpio = gpio 126 | self._device.writeList(self.GPIO, self.gpio) 127 | 128 | def write_iodir(self, iodir=None): 129 | """Write the specified byte value to the IODIR registor. If no value 130 | specified the current buffered value will be written. 131 | """ 132 | if iodir is not None: 133 | self.iodir = iodir 134 | self._device.writeList(self.IODIR, self.iodir) 135 | 136 | def write_gppu(self, gppu=None): 137 | """Write the specified byte value to the GPPU registor. If no value 138 | specified the current buffered value will be written. 139 | """ 140 | if gppu is not None: 141 | self.gppu = gppu 142 | self._device.writeList(self.GPPU, self.gppu) 143 | 144 | 145 | class MCP23017(MCP230xxBase): 146 | """MCP23017-based GPIO class with 16 GPIO pins.""" 147 | # Define number of pins and registor addresses. 148 | NUM_GPIO = 16 149 | IODIR = 0x00 150 | GPIO = 0x12 151 | GPPU = 0x0C 152 | 153 | def __init__(self, address=0x20, **kwargs): 154 | super(MCP23017, self).__init__(address, **kwargs) 155 | 156 | 157 | class MCP23008(MCP230xxBase): 158 | """MCP23008-based GPIO class with 8 GPIO pins.""" 159 | # Define number of pins and registor addresses. 160 | NUM_GPIO = 8 161 | IODIR = 0x00 162 | GPIO = 0x09 163 | GPPU = 0x06 164 | 165 | def __init__(self, address=0x20, **kwargs): 166 | super(MCP23008, self).__init__(address, **kwargs) 167 | -------------------------------------------------------------------------------- /Adafruit_Python_GPIO/Adafruit_GPIO/PWM.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2014 Adafruit Industries 2 | # Author: Tony DiCola 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to deal 6 | # in the Software without restriction, including without limitation the rights 7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | # copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in 12 | # all copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | # THE SOFTWARE. 21 | import Adafruit_GPIO.Platform as Platform 22 | 23 | 24 | class RPi_PWM_Adapter(object): 25 | """PWM implementation for the Raspberry Pi using the RPi.GPIO PWM library.""" 26 | 27 | def __init__(self, rpi_gpio, mode=None): 28 | self.rpi_gpio = rpi_gpio 29 | # Suppress warnings about GPIO in use. 30 | rpi_gpio.setwarnings(False) 31 | # Set board or BCM pin numbering. 32 | if mode == rpi_gpio.BOARD or mode == rpi_gpio.BCM: 33 | rpi_gpio.setmode(mode) 34 | elif mode is not None: 35 | raise ValueError('Unexpected value for mode. Must be BOARD or BCM.') 36 | else: 37 | # Default to BCM numbering if not told otherwise. 38 | rpi_gpio.setmode(rpi_gpio.BCM) 39 | # Store reference to each created PWM instance. 40 | self.pwm = {} 41 | 42 | def start(self, pin, dutycycle, frequency_hz=2000): 43 | """Enable PWM output on specified pin. Set to intiial percent duty cycle 44 | value (0.0 to 100.0) and frequency (in Hz). 45 | """ 46 | if dutycycle < 0.0 or dutycycle > 100.0: 47 | raise ValueError('Invalid duty cycle value, must be between 0.0 to 100.0 (inclusive).') 48 | # Make pin an output. 49 | self.rpi_gpio.setup(pin, self.rpi_gpio.OUT) 50 | # Create PWM instance and save a reference for later access. 51 | self.pwm[pin] = self.rpi_gpio.PWM(pin, frequency_hz) 52 | # Start the PWM at the specified duty cycle. 53 | self.pwm[pin].start(dutycycle) 54 | 55 | def set_duty_cycle(self, pin, dutycycle): 56 | """Set percent duty cycle of PWM output on specified pin. Duty cycle must 57 | be a value 0.0 to 100.0 (inclusive). 58 | """ 59 | if dutycycle < 0.0 or dutycycle > 100.0: 60 | raise ValueError('Invalid duty cycle value, must be between 0.0 to 100.0 (inclusive).') 61 | if pin not in self.pwm: 62 | raise ValueError('Pin {0} is not configured as a PWM. Make sure to first call start for the pin.'.format(pin)) 63 | self.pwm[pin].ChangeDutyCycle(dutycycle) 64 | 65 | def set_frequency(self, pin, frequency_hz): 66 | """Set frequency (in Hz) of PWM output on specified pin.""" 67 | if pin not in self.pwm: 68 | raise ValueError('Pin {0} is not configured as a PWM. Make sure to first call start for the pin.'.format(pin)) 69 | self.pwm[pin].ChangeFrequency(frequency_hz) 70 | 71 | def stop(self, pin): 72 | """Stop PWM output on specified pin.""" 73 | if pin not in self.pwm: 74 | raise ValueError('Pin {0} is not configured as a PWM. Make sure to first call start for the pin.'.format(pin)) 75 | self.pwm[pin].stop() 76 | del self.pwm[pin] 77 | 78 | 79 | class BBIO_PWM_Adapter(object): 80 | """PWM implementation for the BeagleBone Black using the Adafruit_BBIO.PWM 81 | library. 82 | """ 83 | 84 | def __init__(self, bbio_pwm): 85 | self.bbio_pwm = bbio_pwm 86 | 87 | def start(self, pin, dutycycle, frequency_hz=2000): 88 | """Enable PWM output on specified pin. Set to intiial percent duty cycle 89 | value (0.0 to 100.0) and frequency (in Hz). 90 | """ 91 | if dutycycle < 0.0 or dutycycle > 100.0: 92 | raise ValueError('Invalid duty cycle value, must be between 0.0 to 100.0 (inclusive).') 93 | self.bbio_pwm.start(pin, dutycycle, frequency_hz) 94 | 95 | def set_duty_cycle(self, pin, dutycycle): 96 | """Set percent duty cycle of PWM output on specified pin. Duty cycle must 97 | be a value 0.0 to 100.0 (inclusive). 98 | """ 99 | if dutycycle < 0.0 or dutycycle > 100.0: 100 | raise ValueError('Invalid duty cycle value, must be between 0.0 to 100.0 (inclusive).') 101 | self.bbio_pwm.set_duty_cycle(pin, dutycycle) 102 | 103 | def set_frequency(self, pin, frequency_hz): 104 | """Set frequency (in Hz) of PWM output on specified pin.""" 105 | self.bbio_pwm.set_frequency(pin, frequency_hz) 106 | 107 | def stop(self, pin): 108 | """Stop PWM output on specified pin.""" 109 | self.bbio_pwm.stop(pin) 110 | 111 | 112 | def get_platform_pwm(**keywords): 113 | """Attempt to return a PWM instance for the platform which the code is being 114 | executed on. Currently supports only the Raspberry Pi using the RPi.GPIO 115 | library and Beaglebone Black using the Adafruit_BBIO library. Will throw an 116 | exception if a PWM instance can't be created for the current platform. The 117 | returned PWM object has the same interface as the RPi_PWM_Adapter and 118 | BBIO_PWM_Adapter classes. 119 | """ 120 | plat = Platform.platform_detect() 121 | if plat == Platform.RASPBERRY_PI: 122 | import RPi.GPIO 123 | return RPi_PWM_Adapter(RPi.GPIO, **keywords) 124 | elif plat == Platform.BEAGLEBONE_BLACK: 125 | import Adafruit_BBIO.PWM 126 | return BBIO_PWM_Adapter(Adafruit_BBIO.PWM, **keywords) 127 | elif plat == Platform.UNKNOWN: 128 | raise RuntimeError('Could not determine platform.') 129 | -------------------------------------------------------------------------------- /Adafruit_Python_GPIO/Adafruit_GPIO/Platform.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2014 Adafruit Industries 2 | # Author: Tony DiCola 3 | 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to deal 6 | # in the Software without restriction, including without limitation the rights 7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | # copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | 11 | # The above copyright notice and this permission notice shall be included in all 12 | # copies or substantial portions of the Software. 13 | 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | # SOFTWARE. 21 | import platform 22 | import re 23 | 24 | # Platform identification constants. 25 | UNKNOWN = 0 26 | RASPBERRY_PI = 1 27 | BEAGLEBONE_BLACK = 2 28 | 29 | 30 | def platform_detect(): 31 | """Detect if running on the Raspberry Pi or Beaglebone Black and return the 32 | platform type. Will return RASPBERRY_PI, BEAGLEBONE_BLACK, or UNKNOWN.""" 33 | # Handle Raspberry Pi 34 | pi = pi_version() 35 | if pi is not None: 36 | return RASPBERRY_PI 37 | 38 | # Handle Beaglebone Black 39 | # TODO: Check the Beaglebone Black /proc/cpuinfo value instead of reading 40 | # the platform. 41 | plat = platform.platform() 42 | if plat.lower().find('armv7l-with-debian') > -1: 43 | return BEAGLEBONE_BLACK 44 | elif plat.lower().find('armv7l-with-ubuntu') > -1: 45 | return BEAGLEBONE_BLACK 46 | elif plat.lower().find('armv7l-with-glibc2.4') > -1: 47 | return BEAGLEBONE_BLACK 48 | 49 | # Couldn't figure out the platform, just return unknown. 50 | return UNKNOWN 51 | 52 | 53 | def pi_revision(): 54 | """Detect the revision number of a Raspberry Pi, useful for changing 55 | functionality like default I2C bus based on revision.""" 56 | # Revision list available at: http://elinux.org/RPi_HardwareHistory#Board_Revision_History 57 | with open('/proc/cpuinfo', 'r') as infile: 58 | for line in infile: 59 | # Match a line of the form "Revision : 0002" while ignoring extra 60 | # info in front of the revsion (like 1000 when the Pi was over-volted). 61 | match = re.match('Revision\s+:\s+.*(\w{4})$', line, flags=re.IGNORECASE) 62 | if match and match.group(1) in ['0000', '0002', '0003']: 63 | # Return revision 1 if revision ends with 0000, 0002 or 0003. 64 | return 1 65 | elif match: 66 | # Assume revision 2 if revision ends with any other 4 chars. 67 | return 2 68 | # Couldn't find the revision, throw an exception. 69 | raise RuntimeError('Could not determine Raspberry Pi revision.') 70 | 71 | 72 | def pi_version(): 73 | """Detect the version of the Raspberry Pi. Returns either 1, 2 or 74 | None depending on if it's a Raspberry Pi 1 (model A, B, A+, B+), 75 | Raspberry Pi 2 (model B+), or not a Raspberry Pi. 76 | """ 77 | # Check /proc/cpuinfo for the Hardware field value. 78 | # 2708 is pi 1 79 | # 2709 is pi 2 80 | # Anything else is not a pi. 81 | with open('/proc/cpuinfo', 'r') as infile: 82 | cpuinfo = infile.read() 83 | # Match a line like 'Hardware : BCM2709' 84 | match = re.search('^Hardware\s+:\s+(\w+)$', cpuinfo, 85 | flags=re.MULTILINE | re.IGNORECASE) 86 | if not match: 87 | # Couldn't find the hardware, assume it isn't a pi. 88 | return None 89 | if match.group(1) == 'BCM2708': 90 | # Pi 1 91 | return 1 92 | elif match.group(1) == 'BCM2709': 93 | # Pi 2 94 | return 2 95 | else: 96 | # Something else, not a pi. 97 | return None 98 | -------------------------------------------------------------------------------- /Adafruit_Python_GPIO/Adafruit_GPIO/SPI.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2014 Adafruit Industries 2 | # Author: Tony DiCola 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to deal 6 | # in the Software without restriction, including without limitation the rights 7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | # copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in 12 | # all copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | # THE SOFTWARE. 21 | 22 | import operator 23 | import time 24 | 25 | import Adafruit_GPIO as GPIO 26 | 27 | 28 | MSBFIRST = 0 29 | LSBFIRST = 1 30 | 31 | 32 | class SpiDev(object): 33 | """Hardware-based SPI implementation using the spidev interface.""" 34 | 35 | def __init__(self, port, device, max_speed_hz=500000): 36 | """Initialize an SPI device using the SPIdev interface. Port and device 37 | identify the device, for example the device /dev/spidev1.0 would be port 38 | 1 and device 0. 39 | """ 40 | import spidev 41 | self._device = spidev.SpiDev() 42 | self._device.open(port, device) 43 | self._device.max_speed_hz=max_speed_hz 44 | # Default to mode 0. 45 | self._device.mode = 0 46 | 47 | def set_clock_hz(self, hz): 48 | """Set the speed of the SPI clock in hertz. Note that not all speeds 49 | are supported and a lower speed might be chosen by the hardware. 50 | """ 51 | self._device.max_speed_hz=hz 52 | 53 | def set_mode(self, mode): 54 | """Set SPI mode which controls clock polarity and phase. Should be a 55 | numeric value 0, 1, 2, or 3. See wikipedia page for details on meaning: 56 | http://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus 57 | """ 58 | if mode < 0 or mode > 3: 59 | raise ValueError('Mode must be a value 0, 1, 2, or 3.') 60 | self._device.mode = mode 61 | 62 | def set_bit_order(self, order): 63 | """Set order of bits to be read/written over serial lines. Should be 64 | either MSBFIRST for most-significant first, or LSBFIRST for 65 | least-signifcant first. 66 | """ 67 | if order == MSBFIRST: 68 | self._device.lsbfirst = False 69 | elif order == LSBFIRST: 70 | self._device.lsbfirst = True 71 | else: 72 | raise ValueError('Order must be MSBFIRST or LSBFIRST.') 73 | 74 | def close(self): 75 | """Close communication with the SPI device.""" 76 | self._device.close() 77 | 78 | def write(self, data): 79 | """Half-duplex SPI write. The specified array of bytes will be clocked 80 | out the MOSI line. 81 | """ 82 | self._device.writebytes(data) 83 | 84 | def read(self, length): 85 | """Half-duplex SPI read. The specified length of bytes will be clocked 86 | in the MISO line and returned as a bytearray object. 87 | """ 88 | return bytearray(self._device.readbytes(length)) 89 | 90 | def transfer(self, data): 91 | """Full-duplex SPI read and write. The specified array of bytes will be 92 | clocked out the MOSI line, while simultaneously bytes will be read from 93 | the MISO line. Read bytes will be returned as a bytearray object. 94 | """ 95 | return bytearray(self._device.xfer2(data)) 96 | 97 | 98 | class BitBang(object): 99 | """Software-based implementation of the SPI protocol over GPIO pins.""" 100 | 101 | def __init__(self, gpio, sclk, mosi, miso, ss): 102 | """Initialize bit bang (or software) based SPI. Must provide a BaseGPIO 103 | class, the SPI clock, and optionally MOSI, MISO, and SS (slave select) 104 | pin numbers. If MOSI is set to None then writes will be disabled and fail 105 | with an error, likewise for MISO reads will be disabled. If SS is set to 106 | None then SS will not be asserted high/low by the library when 107 | transfering data. 108 | """ 109 | self._gpio = gpio 110 | self._sclk = sclk 111 | self._mosi = mosi 112 | self._miso = miso 113 | self._ss = ss 114 | # Set pins as outputs/inputs. 115 | gpio.setup(sclk, GPIO.OUT) 116 | if mosi is not None: 117 | gpio.setup(mosi, GPIO.OUT) 118 | if miso is not None: 119 | gpio.setup(miso, GPIO.IN) 120 | if ss is not None: 121 | gpio.setup(ss, GPIO.OUT) 122 | # Assert SS high to start with device communication off. 123 | gpio.set_high(ss) 124 | # Assume mode 0. 125 | self.set_mode(0) 126 | # Assume most significant bit first order. 127 | self.set_bit_order(MSBFIRST) 128 | 129 | def set_clock_hz(self, hz): 130 | """Set the speed of the SPI clock. This is unsupported with the bit 131 | bang SPI class and will be ignored. 132 | """ 133 | pass 134 | 135 | def set_mode(self, mode): 136 | """Set SPI mode which controls clock polarity and phase. Should be a 137 | numeric value 0, 1, 2, or 3. See wikipedia page for details on meaning: 138 | http://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus 139 | """ 140 | if mode < 0 or mode > 3: 141 | raise ValueError('Mode must be a value 0, 1, 2, or 3.') 142 | if mode & 0x02: 143 | # Clock is normally high in mode 2 and 3. 144 | self._clock_base = GPIO.HIGH 145 | else: 146 | # Clock is normally low in mode 0 and 1. 147 | self._clock_base = GPIO.LOW 148 | if mode & 0x01: 149 | # Read on trailing edge in mode 1 and 3. 150 | self._read_leading = False 151 | else: 152 | # Read on leading edge in mode 0 and 2. 153 | self._read_leading = True 154 | # Put clock into its base state. 155 | self._gpio.output(self._sclk, self._clock_base) 156 | 157 | def set_bit_order(self, order): 158 | """Set order of bits to be read/written over serial lines. Should be 159 | either MSBFIRST for most-significant first, or LSBFIRST for 160 | least-signifcant first. 161 | """ 162 | # Set self._mask to the bitmask which points at the appropriate bit to 163 | # read or write, and appropriate left/right shift operator function for 164 | # reading/writing. 165 | if order == MSBFIRST: 166 | self._mask = 0x80 167 | self._write_shift = operator.lshift 168 | self._read_shift = operator.rshift 169 | elif order == LSBFIRST: 170 | self._mask = 0x01 171 | self._write_shift = operator.rshift 172 | self._read_shift = operator.lshift 173 | else: 174 | raise ValueError('Order must be MSBFIRST or LSBFIRST.') 175 | 176 | def close(self): 177 | """Close the SPI connection. Unused in the bit bang implementation.""" 178 | pass 179 | 180 | def write(self, data, assert_ss=True, deassert_ss=True): 181 | """Half-duplex SPI write. If assert_ss is True, the SS line will be 182 | asserted low, the specified bytes will be clocked out the MOSI line, and 183 | if deassert_ss is True the SS line be put back high. 184 | """ 185 | # Fail MOSI is not specified. 186 | if self._mosi is None: 187 | raise RuntimeError('Write attempted with no MOSI pin specified.') 188 | if assert_ss and self._ss is not None: 189 | self._gpio.set_low(self._ss) 190 | for byte in data: 191 | for i in range(8): 192 | # Write bit to MOSI. 193 | if self._write_shift(byte, i) & self._mask: 194 | self._gpio.set_high(self._mosi) 195 | else: 196 | self._gpio.set_low(self._mosi) 197 | # Flip clock off base. 198 | self._gpio.output(self._sclk, not self._clock_base) 199 | # Return clock to base. 200 | self._gpio.output(self._sclk, self._clock_base) 201 | if deassert_ss and self._ss is not None: 202 | self._gpio.set_high(self._ss) 203 | 204 | def read(self, length, assert_ss=True, deassert_ss=True): 205 | """Half-duplex SPI read. If assert_ss is true, the SS line will be 206 | asserted low, the specified length of bytes will be clocked in the MISO 207 | line, and if deassert_ss is true the SS line will be put back high. 208 | Bytes which are read will be returned as a bytearray object. 209 | """ 210 | if self._miso is None: 211 | raise RuntimeError('Read attempted with no MISO pin specified.') 212 | if assert_ss and self._ss is not None: 213 | self._gpio.set_low(self._ss) 214 | result = bytearray(length) 215 | for i in range(length): 216 | for j in range(8): 217 | # Flip clock off base. 218 | self._gpio.output(self._sclk, not self._clock_base) 219 | # Handle read on leading edge of clock. 220 | if self._read_leading: 221 | if self._gpio.is_high(self._miso): 222 | # Set bit to 1 at appropriate location. 223 | result[i] |= self._read_shift(self._mask, j) 224 | else: 225 | # Set bit to 0 at appropriate location. 226 | result[i] &= ~self._read_shift(self._mask, j) 227 | # Return clock to base. 228 | self._gpio.output(self._sclk, self._clock_base) 229 | # Handle read on trailing edge of clock. 230 | if not self._read_leading: 231 | if self._gpio.is_high(self._miso): 232 | # Set bit to 1 at appropriate location. 233 | result[i] |= self._read_shift(self._mask, j) 234 | else: 235 | # Set bit to 0 at appropriate location. 236 | result[i] &= ~self._read_shift(self._mask, j) 237 | if deassert_ss and self._ss is not None: 238 | self._gpio.set_high(self._ss) 239 | return result 240 | 241 | def transfer(self, data, assert_ss=True, deassert_ss=True): 242 | """Full-duplex SPI read and write. If assert_ss is true, the SS line 243 | will be asserted low, the specified bytes will be clocked out the MOSI 244 | line while bytes will also be read from the MISO line, and if 245 | deassert_ss is true the SS line will be put back high. Bytes which are 246 | read will be returned as a bytearray object. 247 | """ 248 | if self._mosi is None: 249 | raise RuntimeError('Write attempted with no MOSI pin specified.') 250 | if self._mosi is None: 251 | raise RuntimeError('Read attempted with no MISO pin specified.') 252 | if assert_ss and self._ss is not None: 253 | self._gpio.set_low(self._ss) 254 | result = bytearray(len(data)) 255 | for i in range(len(data)): 256 | for j in range(8): 257 | # Write bit to MOSI. 258 | if self._write_shift(data[i], j) & self._mask: 259 | self._gpio.set_high(self._mosi) 260 | else: 261 | self._gpio.set_low(self._mosi) 262 | # Flip clock off base. 263 | self._gpio.output(self._sclk, not self._clock_base) 264 | # Handle read on leading edge of clock. 265 | if self._read_leading: 266 | if self._gpio.is_high(self._miso): 267 | # Set bit to 1 at appropriate location. 268 | result[i] |= self._read_shift(self._mask, j) 269 | else: 270 | # Set bit to 0 at appropriate location. 271 | result[i] &= ~self._read_shift(self._mask, j) 272 | # Return clock to base. 273 | self._gpio.output(self._sclk, self._clock_base) 274 | # Handle read on trailing edge of clock. 275 | if not self._read_leading: 276 | if self._gpio.is_high(self._miso): 277 | # Set bit to 1 at appropriate location. 278 | result[i] |= self._read_shift(self._mask, j) 279 | else: 280 | # Set bit to 0 at appropriate location. 281 | result[i] &= ~self._read_shift(self._mask, j) 282 | if deassert_ss and self._ss is not None: 283 | self._gpio.set_high(self._ss) 284 | return result 285 | -------------------------------------------------------------------------------- /Adafruit_Python_GPIO/Adafruit_GPIO/__init__.py: -------------------------------------------------------------------------------- 1 | from GPIO import * -------------------------------------------------------------------------------- /Adafruit_Python_GPIO/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Adafruit Industries 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /Adafruit_Python_GPIO/README.md: -------------------------------------------------------------------------------- 1 | Adafruit Python GPIO Library 2 | ============================ 3 | 4 | Library to provide a cross-platform GPIO interface on the Raspberry Pi and Beaglebone Black using the [RPi.GPIO](https://pypi.python.org/pypi/RPi.GPIO) and [Adafruit_BBIO](https://pypi.python.org/pypi/Adafruit_BBIO) libraries. 5 | 6 | The library is currently in an early stage, but you can see how its used in the [Adafruit Nokia LCD library](https://github.com/adafruit/Adafruit_Nokia_LCD) to write Python code that is easily portable between the Raspberry Pi and Beaglebone Black. 7 | -------------------------------------------------------------------------------- /Adafruit_Python_GPIO/ez_setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Bootstrap setuptools installation 3 | 4 | To use setuptools in your package's setup.py, include this 5 | file in the same directory and add this to the top of your setup.py:: 6 | 7 | from ez_setup import use_setuptools 8 | use_setuptools() 9 | 10 | To require a specific version of setuptools, set a download 11 | mirror, or use an alternate download directory, simply supply 12 | the appropriate options to ``use_setuptools()``. 13 | 14 | This file can also be run as a script to install or upgrade setuptools. 15 | """ 16 | import os 17 | import shutil 18 | import sys 19 | import tempfile 20 | import zipfile 21 | import optparse 22 | import subprocess 23 | import platform 24 | import textwrap 25 | import contextlib 26 | 27 | from distutils import log 28 | 29 | try: 30 | from site import USER_SITE 31 | except ImportError: 32 | USER_SITE = None 33 | 34 | DEFAULT_VERSION = "3.5.1" 35 | DEFAULT_URL = "https://pypi.python.org/packages/source/s/setuptools/" 36 | 37 | def _python_cmd(*args): 38 | """ 39 | Return True if the command succeeded. 40 | """ 41 | args = (sys.executable,) + args 42 | return subprocess.call(args) == 0 43 | 44 | 45 | def _install(archive_filename, install_args=()): 46 | with archive_context(archive_filename): 47 | # installing 48 | log.warn('Installing Setuptools') 49 | if not _python_cmd('setup.py', 'install', *install_args): 50 | log.warn('Something went wrong during the installation.') 51 | log.warn('See the error message above.') 52 | # exitcode will be 2 53 | return 2 54 | 55 | 56 | def _build_egg(egg, archive_filename, to_dir): 57 | with archive_context(archive_filename): 58 | # building an egg 59 | log.warn('Building a Setuptools egg in %s', to_dir) 60 | _python_cmd('setup.py', '-q', 'bdist_egg', '--dist-dir', to_dir) 61 | # returning the result 62 | log.warn(egg) 63 | if not os.path.exists(egg): 64 | raise IOError('Could not build the egg.') 65 | 66 | 67 | def get_zip_class(): 68 | """ 69 | Supplement ZipFile class to support context manager for Python 2.6 70 | """ 71 | class ContextualZipFile(zipfile.ZipFile): 72 | def __enter__(self): 73 | return self 74 | def __exit__(self, type, value, traceback): 75 | self.close 76 | return zipfile.ZipFile if hasattr(zipfile.ZipFile, '__exit__') else \ 77 | ContextualZipFile 78 | 79 | 80 | @contextlib.contextmanager 81 | def archive_context(filename): 82 | # extracting the archive 83 | tmpdir = tempfile.mkdtemp() 84 | log.warn('Extracting in %s', tmpdir) 85 | old_wd = os.getcwd() 86 | try: 87 | os.chdir(tmpdir) 88 | with get_zip_class()(filename) as archive: 89 | archive.extractall() 90 | 91 | # going in the directory 92 | subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0]) 93 | os.chdir(subdir) 94 | log.warn('Now working in %s', subdir) 95 | yield 96 | 97 | finally: 98 | os.chdir(old_wd) 99 | shutil.rmtree(tmpdir) 100 | 101 | 102 | def _do_download(version, download_base, to_dir, download_delay): 103 | egg = os.path.join(to_dir, 'setuptools-%s-py%d.%d.egg' 104 | % (version, sys.version_info[0], sys.version_info[1])) 105 | if not os.path.exists(egg): 106 | archive = download_setuptools(version, download_base, 107 | to_dir, download_delay) 108 | _build_egg(egg, archive, to_dir) 109 | sys.path.insert(0, egg) 110 | 111 | # Remove previously-imported pkg_resources if present (see 112 | # https://bitbucket.org/pypa/setuptools/pull-request/7/ for details). 113 | if 'pkg_resources' in sys.modules: 114 | del sys.modules['pkg_resources'] 115 | 116 | import setuptools 117 | setuptools.bootstrap_install_from = egg 118 | 119 | 120 | def use_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL, 121 | to_dir=os.curdir, download_delay=15): 122 | to_dir = os.path.abspath(to_dir) 123 | rep_modules = 'pkg_resources', 'setuptools' 124 | imported = set(sys.modules).intersection(rep_modules) 125 | try: 126 | import pkg_resources 127 | except ImportError: 128 | return _do_download(version, download_base, to_dir, download_delay) 129 | try: 130 | pkg_resources.require("setuptools>=" + version) 131 | return 132 | except pkg_resources.DistributionNotFound: 133 | return _do_download(version, download_base, to_dir, download_delay) 134 | except pkg_resources.VersionConflict as VC_err: 135 | if imported: 136 | msg = textwrap.dedent(""" 137 | The required version of setuptools (>={version}) is not available, 138 | and can't be installed while this script is running. Please 139 | install a more recent version first, using 140 | 'easy_install -U setuptools'. 141 | 142 | (Currently using {VC_err.args[0]!r}) 143 | """).format(VC_err=VC_err, version=version) 144 | sys.stderr.write(msg) 145 | sys.exit(2) 146 | 147 | # otherwise, reload ok 148 | del pkg_resources, sys.modules['pkg_resources'] 149 | return _do_download(version, download_base, to_dir, download_delay) 150 | 151 | def _clean_check(cmd, target): 152 | """ 153 | Run the command to download target. If the command fails, clean up before 154 | re-raising the error. 155 | """ 156 | try: 157 | subprocess.check_call(cmd) 158 | except subprocess.CalledProcessError: 159 | if os.access(target, os.F_OK): 160 | os.unlink(target) 161 | raise 162 | 163 | def download_file_powershell(url, target): 164 | """ 165 | Download the file at url to target using Powershell (which will validate 166 | trust). Raise an exception if the command cannot complete. 167 | """ 168 | target = os.path.abspath(target) 169 | cmd = [ 170 | 'powershell', 171 | '-Command', 172 | "(new-object System.Net.WebClient).DownloadFile(%(url)r, %(target)r)" % vars(), 173 | ] 174 | _clean_check(cmd, target) 175 | 176 | def has_powershell(): 177 | if platform.system() != 'Windows': 178 | return False 179 | cmd = ['powershell', '-Command', 'echo test'] 180 | devnull = open(os.path.devnull, 'wb') 181 | try: 182 | try: 183 | subprocess.check_call(cmd, stdout=devnull, stderr=devnull) 184 | except Exception: 185 | return False 186 | finally: 187 | devnull.close() 188 | return True 189 | 190 | download_file_powershell.viable = has_powershell 191 | 192 | def download_file_curl(url, target): 193 | cmd = ['curl', url, '--silent', '--output', target] 194 | _clean_check(cmd, target) 195 | 196 | def has_curl(): 197 | cmd = ['curl', '--version'] 198 | devnull = open(os.path.devnull, 'wb') 199 | try: 200 | try: 201 | subprocess.check_call(cmd, stdout=devnull, stderr=devnull) 202 | except Exception: 203 | return False 204 | finally: 205 | devnull.close() 206 | return True 207 | 208 | download_file_curl.viable = has_curl 209 | 210 | def download_file_wget(url, target): 211 | cmd = ['wget', url, '--quiet', '--output-document', target] 212 | _clean_check(cmd, target) 213 | 214 | def has_wget(): 215 | cmd = ['wget', '--version'] 216 | devnull = open(os.path.devnull, 'wb') 217 | try: 218 | try: 219 | subprocess.check_call(cmd, stdout=devnull, stderr=devnull) 220 | except Exception: 221 | return False 222 | finally: 223 | devnull.close() 224 | return True 225 | 226 | download_file_wget.viable = has_wget 227 | 228 | def download_file_insecure(url, target): 229 | """ 230 | Use Python to download the file, even though it cannot authenticate the 231 | connection. 232 | """ 233 | try: 234 | from urllib.request import urlopen 235 | except ImportError: 236 | from urllib2 import urlopen 237 | src = dst = None 238 | try: 239 | src = urlopen(url) 240 | # Read/write all in one block, so we don't create a corrupt file 241 | # if the download is interrupted. 242 | data = src.read() 243 | dst = open(target, "wb") 244 | dst.write(data) 245 | finally: 246 | if src: 247 | src.close() 248 | if dst: 249 | dst.close() 250 | 251 | download_file_insecure.viable = lambda: True 252 | 253 | def get_best_downloader(): 254 | downloaders = [ 255 | download_file_powershell, 256 | download_file_curl, 257 | download_file_wget, 258 | download_file_insecure, 259 | ] 260 | 261 | for dl in downloaders: 262 | if dl.viable(): 263 | return dl 264 | 265 | def download_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL, 266 | to_dir=os.curdir, delay=15, downloader_factory=get_best_downloader): 267 | """ 268 | Download setuptools from a specified location and return its filename 269 | 270 | `version` should be a valid setuptools version number that is available 271 | as an egg for download under the `download_base` URL (which should end 272 | with a '/'). `to_dir` is the directory where the egg will be downloaded. 273 | `delay` is the number of seconds to pause before an actual download 274 | attempt. 275 | 276 | ``downloader_factory`` should be a function taking no arguments and 277 | returning a function for downloading a URL to a target. 278 | """ 279 | # making sure we use the absolute path 280 | to_dir = os.path.abspath(to_dir) 281 | zip_name = "setuptools-%s.zip" % version 282 | url = download_base + zip_name 283 | saveto = os.path.join(to_dir, zip_name) 284 | if not os.path.exists(saveto): # Avoid repeated downloads 285 | log.warn("Downloading %s", url) 286 | downloader = downloader_factory() 287 | downloader(url, saveto) 288 | return os.path.realpath(saveto) 289 | 290 | def _build_install_args(options): 291 | """ 292 | Build the arguments to 'python setup.py install' on the setuptools package 293 | """ 294 | return ['--user'] if options.user_install else [] 295 | 296 | def _parse_args(): 297 | """ 298 | Parse the command line for options 299 | """ 300 | parser = optparse.OptionParser() 301 | parser.add_option( 302 | '--user', dest='user_install', action='store_true', default=False, 303 | help='install in user site package (requires Python 2.6 or later)') 304 | parser.add_option( 305 | '--download-base', dest='download_base', metavar="URL", 306 | default=DEFAULT_URL, 307 | help='alternative URL from where to download the setuptools package') 308 | parser.add_option( 309 | '--insecure', dest='downloader_factory', action='store_const', 310 | const=lambda: download_file_insecure, default=get_best_downloader, 311 | help='Use internal, non-validating downloader' 312 | ) 313 | parser.add_option( 314 | '--version', help="Specify which version to download", 315 | default=DEFAULT_VERSION, 316 | ) 317 | options, args = parser.parse_args() 318 | # positional arguments are ignored 319 | return options 320 | 321 | def main(): 322 | """Install or upgrade setuptools and EasyInstall""" 323 | options = _parse_args() 324 | archive = download_setuptools( 325 | version=options.version, 326 | download_base=options.download_base, 327 | downloader_factory=options.downloader_factory, 328 | ) 329 | return _install(archive, _build_install_args(options)) 330 | 331 | if __name__ == '__main__': 332 | sys.exit(main()) 333 | -------------------------------------------------------------------------------- /Adafruit_Python_GPIO/setup.py: -------------------------------------------------------------------------------- 1 | from ez_setup import use_setuptools 2 | use_setuptools() 3 | from setuptools import setup, find_packages 4 | 5 | import sys 6 | 7 | # Define required packages. 8 | requires = [] 9 | # Assume spidev is required on non-windows & non-mac platforms (i.e. linux). 10 | if sys.platform != 'win32' and sys.platform != 'darwin': 11 | requires.append('spidev') 12 | 13 | setup(name = 'Adafruit_GPIO', 14 | version = '0.8.0', 15 | author = 'Tony DiCola', 16 | author_email = 'tdicola@adafruit.com', 17 | description = 'Library to provide a cross-platform GPIO interface on the Raspberry Pi and Beaglebone Black using the RPi.GPIO and Adafruit_BBIO libraries.', 18 | license = 'MIT', 19 | url = 'https://github.com/adafruit/Adafruit_Python_GPIO/', 20 | install_requires = requires, 21 | packages = find_packages()) 22 | -------------------------------------------------------------------------------- /Adafruit_Python_GPIO/test/MockGPIO.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2014 Adafruit Industries 2 | # Author: Tony DiCola 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to deal 6 | # in the Software without restriction, including without limitation the rights 7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | # copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in 12 | # all copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | # THE SOFTWARE. 21 | 22 | import Adafruit_GPIO as GPIO 23 | 24 | 25 | class MockGPIO(GPIO.BaseGPIO): 26 | def __init__(self): 27 | self.pin_mode = {} 28 | self.pin_written = {} 29 | self.pin_read = {} 30 | 31 | def setup(self, pin, mode): 32 | self.pin_mode[pin] = mode 33 | 34 | def output(self, pin, bit): 35 | self.pin_written.setdefault(pin, []).append(1 if bit else 0) 36 | 37 | def input(self, pin): 38 | if pin not in self.pin_read: 39 | raise RuntimeError('No mock GPIO data to read for pin {0}'.format(pin)) 40 | return self.pin_read[pin].pop(0) == 1 41 | -------------------------------------------------------------------------------- /Adafruit_Python_GPIO/test/test_GPIO.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2014 Adafruit Industries 2 | # Author: Tony DiCola 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to deal 6 | # in the Software without restriction, including without limitation the rights 7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | # copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in 12 | # all copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | # THE SOFTWARE. 21 | 22 | import unittest 23 | 24 | from mock import Mock, patch 25 | 26 | import Adafruit_GPIO as GPIO 27 | import Adafruit_GPIO.SPI as SPI 28 | 29 | from MockGPIO import MockGPIO 30 | 31 | 32 | class TestBaseGPIO(unittest.TestCase): 33 | def test_set_high_and_set_low(self): 34 | gpio = MockGPIO() 35 | gpio.set_high(1) 36 | gpio.set_low(1) 37 | self.assertDictEqual(gpio.pin_written, {1: [1, 0]}) 38 | 39 | def test_is_high_and_is_low(self): 40 | gpio = MockGPIO() 41 | gpio.pin_read[1] = [0, 0, 1, 1] 42 | self.assertTrue(gpio.is_low(1)) 43 | self.assertFalse(gpio.is_high(1)) 44 | self.assertFalse(gpio.is_low(1)) 45 | self.assertTrue(gpio.is_high(1)) 46 | 47 | def test_output_pins(self): 48 | gpio = MockGPIO() 49 | gpio.output_pins({0: True, 1: False, 7: True}) 50 | self.assertDictEqual(gpio.pin_written, {0: [1], 1: [0], 7: [1]}) 51 | 52 | 53 | class TestRPiGPIOAdapter(unittest.TestCase): 54 | def test_setup(self): 55 | rpi_gpio = Mock() 56 | adapter = GPIO.RPiGPIOAdapter(rpi_gpio) 57 | adapter.setup(1, GPIO.OUT) 58 | rpi_gpio.setup.assert_called_with(1, rpi_gpio.OUT, pull_up_down=rpi_gpio.PUD_OFF) 59 | adapter.setup(1, GPIO.IN) 60 | rpi_gpio.setup.assert_called_with(1, rpi_gpio.IN, pull_up_down=rpi_gpio.PUD_OFF) 61 | adapter.setup(1, GPIO.IN, GPIO.PUD_DOWN) 62 | rpi_gpio.setup.assert_called_with(1, rpi_gpio.IN, pull_up_down=rpi_gpio.PUD_DOWN) 63 | adapter.setup(1, GPIO.IN, GPIO.PUD_UP) 64 | rpi_gpio.setup.assert_called_with(1, rpi_gpio.IN, pull_up_down=rpi_gpio.PUD_UP) 65 | 66 | def test_output(self): 67 | rpi_gpio = Mock() 68 | adapter = GPIO.RPiGPIOAdapter(rpi_gpio) 69 | adapter.output(1, True) 70 | rpi_gpio.output.assert_called_with(1, True) 71 | adapter.output(1, False) 72 | rpi_gpio.output.assert_called_with(1, False) 73 | 74 | def test_input(self): 75 | rpi_gpio = Mock() 76 | adapter = GPIO.RPiGPIOAdapter(rpi_gpio) 77 | rpi_gpio.input = Mock(return_value=True) 78 | val = adapter.input(1) 79 | self.assertTrue(val) 80 | rpi_gpio.input.assert_called_with(1) 81 | 82 | def test_setmode(self): 83 | rpi_gpio = Mock() 84 | adapter = GPIO.RPiGPIOAdapter(rpi_gpio, mode=rpi_gpio.BCM) 85 | rpi_gpio.setmode.assert_called_with(rpi_gpio.BCM) 86 | adapter = GPIO.RPiGPIOAdapter(rpi_gpio, mode=rpi_gpio.BOARD) 87 | rpi_gpio.setmode.assert_called_with(rpi_gpio.BOARD) 88 | adapter = GPIO.RPiGPIOAdapter(rpi_gpio) 89 | rpi_gpio.setmode.assert_called_with(rpi_gpio.BCM) 90 | 91 | def test_add_event_detect(self): 92 | rpi_gpio = Mock() 93 | adapter = GPIO.RPiGPIOAdapter(rpi_gpio) 94 | adapter.add_event_detect(1, GPIO.RISING) 95 | rpi_gpio.add_event_detect.assert_called_with(1, rpi_gpio.RISING) 96 | 97 | def test_remove_event_detect(self): 98 | rpi_gpio = Mock() 99 | adapter = GPIO.RPiGPIOAdapter(rpi_gpio) 100 | adapter.remove_event_detect(1) 101 | rpi_gpio.remove_event_detect.assert_called_with(1) 102 | 103 | def test_add_event_callback(self): 104 | rpi_gpio = Mock() 105 | adapter = GPIO.RPiGPIOAdapter(rpi_gpio) 106 | adapter.add_event_callback(1, callback=self.test_add_event_callback) 107 | rpi_gpio.add_event_callback.assert_called_with(1, self.test_add_event_callback) 108 | 109 | def test_event_detected(self): 110 | rpi_gpio = Mock() 111 | adapter = GPIO.RPiGPIOAdapter(rpi_gpio) 112 | adapter.event_detected(1) 113 | rpi_gpio.event_detected.assert_called_with(1) 114 | 115 | def test_wait_for_edge(self): 116 | rpi_gpio = Mock() 117 | adapter = GPIO.RPiGPIOAdapter(rpi_gpio) 118 | adapter.wait_for_edge(1, GPIO.FALLING) 119 | rpi_gpio.wait_for_edge.assert_called_with(1, rpi_gpio.FALLING) 120 | 121 | def test_cleanup(self): 122 | rpi_gpio = Mock() 123 | adapter = GPIO.AdafruitBBIOAdapter(rpi_gpio) 124 | adapter.cleanup() 125 | rpi_gpio.cleanup.assert_called() 126 | 127 | def test_cleanup_pin(self): 128 | rpi_gpio = Mock() 129 | adapter = GPIO.AdafruitBBIOAdapter(rpi_gpio) 130 | adapter.cleanup(1) 131 | rpi_gpio.cleanup.assert_called_with(1) 132 | 133 | 134 | class TestAdafruitBBIOAdapter(unittest.TestCase): 135 | def test_setup(self): 136 | bbio_gpio = Mock() 137 | adapter = GPIO.AdafruitBBIOAdapter(bbio_gpio) 138 | adapter.setup(1, GPIO.OUT) 139 | bbio_gpio.setup.assert_called_with(1, bbio_gpio.OUT, pull_up_down=bbio_gpio.PUD_OFF) 140 | adapter.setup(1, GPIO.IN) 141 | bbio_gpio.setup.assert_called_with(1, bbio_gpio.IN, pull_up_down=bbio_gpio.PUD_OFF) 142 | adapter.setup(1, GPIO.IN, GPIO.PUD_DOWN) 143 | bbio_gpio.setup.assert_called_with(1, bbio_gpio.IN, pull_up_down=bbio_gpio.PUD_DOWN) 144 | adapter.setup(1, GPIO.IN, GPIO.PUD_UP) 145 | bbio_gpio.setup.assert_called_with(1, bbio_gpio.IN, pull_up_down=bbio_gpio.PUD_UP) 146 | 147 | def test_output(self): 148 | bbio_gpio = Mock() 149 | adapter = GPIO.AdafruitBBIOAdapter(bbio_gpio) 150 | adapter.output(1, True) 151 | bbio_gpio.output.assert_called_with(1, True) 152 | adapter.output(1, False) 153 | bbio_gpio.output.assert_called_with(1, False) 154 | 155 | def test_input(self): 156 | bbio_gpio = Mock() 157 | adapter = GPIO.AdafruitBBIOAdapter(bbio_gpio) 158 | bbio_gpio.input = Mock(return_value=True) 159 | val = adapter.input(1) 160 | self.assertTrue(val) 161 | bbio_gpio.input.assert_called_with(1) 162 | 163 | def test_add_event_detect(self): 164 | bbio_gpio = Mock() 165 | adapter = GPIO.AdafruitBBIOAdapter(bbio_gpio) 166 | adapter.add_event_detect(1, GPIO.RISING) 167 | bbio_gpio.add_event_detect.assert_called_with(1, bbio_gpio.RISING) 168 | 169 | def test_add_event_detect(self): 170 | bbio_gpio = Mock() 171 | adapter = GPIO.AdafruitBBIOAdapter(bbio_gpio) 172 | adapter.add_event_detect(1, GPIO.RISING) 173 | bbio_gpio.add_event_detect.assert_called_with(1, bbio_gpio.RISING) 174 | 175 | def test_remove_event_detect(self): 176 | bbio_gpio = Mock() 177 | adapter = GPIO.AdafruitBBIOAdapter(bbio_gpio) 178 | adapter.remove_event_detect(1) 179 | bbio_gpio.remove_event_detect.assert_called_with(1) 180 | 181 | def test_add_event_callback(self): 182 | bbio_gpio = Mock() 183 | adapter = GPIO.AdafruitBBIOAdapter(bbio_gpio) 184 | adapter.add_event_callback(1, callback=self.test_add_event_callback) 185 | bbio_gpio.add_event_callback.assert_called_with(1, self.test_add_event_callback) 186 | 187 | def test_event_detected(self): 188 | bbio_gpio = Mock() 189 | adapter = GPIO.AdafruitBBIOAdapter(bbio_gpio) 190 | adapter.event_detected(1) 191 | bbio_gpio.event_detected.assert_called_with(1) 192 | 193 | def test_wait_for_edge(self): 194 | bbio_gpio = Mock() 195 | adapter = GPIO.AdafruitBBIOAdapter(bbio_gpio) 196 | adapter.wait_for_edge(1, GPIO.FALLING) 197 | bbio_gpio.wait_for_edge.assert_called_with(1, bbio_gpio.FALLING) 198 | 199 | def test_cleanup(self): 200 | bbio_gpio = Mock() 201 | adapter = GPIO.AdafruitBBIOAdapter(bbio_gpio) 202 | adapter.cleanup() 203 | bbio_gpio.cleanup.assert_called() 204 | 205 | def test_cleanup_pin(self): 206 | bbio_gpio = Mock() 207 | adapter = GPIO.AdafruitBBIOAdapter(bbio_gpio) 208 | adapter.cleanup(1) 209 | bbio_gpio.cleanup.assert_called_with(1) 210 | 211 | 212 | class TestGetPlatformGPIO(unittest.TestCase): 213 | @patch.dict('sys.modules', {'RPi': Mock(), 'RPi.GPIO': Mock()}) 214 | @patch('platform.platform', Mock(return_value='Linux-3.10.25+-armv6l-with-debian-7.4')) 215 | def test_raspberrypi(self): 216 | gpio = GPIO.get_platform_gpio() 217 | self.assertIsInstance(gpio, GPIO.RPiGPIOAdapter) 218 | 219 | @patch.dict('sys.modules', {'Adafruit_BBIO': Mock(), 'Adafruit_BBIO.GPIO': Mock()}) 220 | @patch('platform.platform', Mock(return_value='Linux-3.8.13-bone47-armv7l-with-debian-7.4')) 221 | def test_beagleboneblack(self): 222 | gpio = GPIO.get_platform_gpio() 223 | self.assertIsInstance(gpio, GPIO.AdafruitBBIOAdapter) 224 | 225 | @patch('platform.platform', Mock(return_value='Darwin-13.2.0-x86_64-i386-64bit')) 226 | def test_otherplatform(self): 227 | self.assertRaises(RuntimeError, GPIO.get_platform_gpio) 228 | -------------------------------------------------------------------------------- /Adafruit_Python_GPIO/test/test_I2C.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2014 Adafruit Industries 2 | # Author: Tony DiCola 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to deal 6 | # in the Software without restriction, including without limitation the rights 7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | # copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in 12 | # all copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | # THE SOFTWARE. 21 | 22 | import logging 23 | import unittest 24 | 25 | from mock import Mock, patch 26 | 27 | import Adafruit_GPIO.Platform as Platform 28 | 29 | 30 | # Enable debug logging to stdout during tests. 31 | logging.basicConfig() 32 | logging.getLogger().setLevel(logging.DEBUG) 33 | 34 | 35 | class MockSMBus(object): 36 | # Mock the smbus.SMBus class to record all data written to specific 37 | # addresses and registers in the _written member. 38 | def __init__(self): 39 | # _written will store a dictionary of address to register dictionary. 40 | # Each register dictionary will store a mapping of register value to 41 | # an array of all written values (in sequential write order). 42 | self._written = {} 43 | self._read = {} 44 | 45 | def _write_register(self, address, register, value): 46 | self._written.setdefault(address, {}).setdefault(register, []).append(value) 47 | 48 | def _read_register(self, address, register): 49 | return self._read.get(address).get(register).pop(0) 50 | 51 | def write_byte_data(self, address, register, value): 52 | self._write_register(address, register, value) 53 | 54 | def write_word_data(self, address, register, value): 55 | self._write_register(address, register, value >> 8 & 0xFF) 56 | self._write_register(address, register+1, value & 0xFF) 57 | 58 | def write_i2c_block_data(self, address, register, values): 59 | for i, value in enumerate(values): 60 | self._write_register(address, register+i, value & 0xFF) 61 | 62 | def read_byte_data(self, address, register): 63 | return self._read_register(address, register) 64 | 65 | def read_word_data(self, address, register): 66 | high = self._read_register(address, register) 67 | low = self._read_register(address, register+1) 68 | return (high << 8) | low 69 | 70 | def read_i2c_block_data(self, address, length): 71 | return [self._read_register(address+i) for i in range(length)] 72 | 73 | 74 | def create_device(address, busnum): 75 | # Mock the smbus module and inject it into the global namespace so the 76 | # Adafruit_GPIO.I2C module can be imported. Also inject a mock SMBus 77 | # instance to be returned by smbus.SMBus function calls. 78 | smbus = Mock() 79 | mockbus = MockSMBus() 80 | smbus.SMBus.return_value = mockbus 81 | with patch.dict('sys.modules', {'smbus': smbus}): 82 | import Adafruit_GPIO.I2C as I2C 83 | return (I2C.Device(address, busnum), smbus, mockbus) 84 | 85 | def safe_import_i2c(): 86 | # Mock the smbus module and inject it into the global namespace so the 87 | # Adafruit_GPIO.I2C module can be imported. The imported I2C module is 88 | # returned so global functions can be called on it. 89 | with patch.dict('sys.modules', {'smbus': Mock() }): 90 | import Adafruit_GPIO.I2C as I2C 91 | return I2C 92 | 93 | 94 | class TestI2CDevice(unittest.TestCase): 95 | 96 | def test_address_and_bus_set_correctly(self): 97 | device, smbus, mockbus = create_device(0x1F, 1) 98 | self.assertEqual(device._bus, mockbus) 99 | smbus.SMBus.assert_called_with(1) 100 | self.assertEqual(device._address, 0x1F) 101 | 102 | def test_write8(self): 103 | device, smbus, mockbus = create_device(0x1F, 1) 104 | device.write8(0xFE, 0xED) 105 | self.assertDictEqual(mockbus._written, { 0x1F: { 0xFE: [0xED] }}) 106 | 107 | def test_write8_truncates_to_8bits(self): 108 | device, smbus, mockbus = create_device(0x1F, 1) 109 | device.write8(0xFE, 0xBEEFED) 110 | self.assertDictEqual(mockbus._written, { 0x1F: { 0xFE: [0xED] }}) 111 | 112 | def test_write16(self): 113 | device, smbus, mockbus = create_device(0x1F, 1) 114 | device.write16(0xFE, 0xBEEF) 115 | self.assertDictEqual(mockbus._written, { 0x1F: { 0xFE: [0xBE], 116 | 0xFF: [0xEF] }}) 117 | 118 | def test_write16_truncates_to_8bits(self): 119 | device, smbus, mockbus = create_device(0x1F, 1) 120 | device.write16(0xFE, 0xFEEDBEEF) 121 | self.assertDictEqual(mockbus._written, { 0x1F: { 0xFE: [0xBE], 122 | 0xFF: [0xEF] }}) 123 | 124 | def test_writeList(self): 125 | device, smbus, mockbus = create_device(0x1F, 1) 126 | device.writeList(0x00, [0xFE, 0xED, 0xBE, 0xEF]) 127 | self.assertDictEqual(mockbus._written, { 0x1F: { 0x00: [0xFE], 128 | 0x01: [0xED], 129 | 0x02: [0xBE], 130 | 0x03: [0xEF] }}) 131 | 132 | def test_readU8(self): 133 | device, smbus, mockbus = create_device(0x1F, 1) 134 | mockbus._read[0x1F] = { 0xFE: [0xED] } 135 | value = device.readU8(0xFE) 136 | self.assertEqual(value, 0xED) 137 | 138 | def test_readS8(self): 139 | device, smbus, mockbus = create_device(0x1F, 1) 140 | mockbus._read[0x1F] = { 0xFE: [0xED] } 141 | value = device.readS8(0xFE) 142 | self.assertEqual(value, -19) 143 | 144 | def test_readU16(self): 145 | device, smbus, mockbus = create_device(0x1F, 1) 146 | mockbus._read[0x1F] = { 0xFE: [0xED], 0xFF: [0x01] } 147 | value = device.readU16(0xFE) 148 | self.assertEqual(value, 0xED01) 149 | 150 | def test_readS16(self): 151 | device, smbus, mockbus = create_device(0x1F, 1) 152 | mockbus._read[0x1F] = { 0xFE: [0xED], 0xFF: [0x01] } 153 | value = device.readS16(0xFE) 154 | self.assertEqual(value, -4863) 155 | 156 | 157 | class TestGetDefaultBus(unittest.TestCase): 158 | @patch('Adafruit_GPIO.Platform.pi_revision', Mock(return_value=1)) 159 | @patch('Adafruit_GPIO.Platform.platform_detect', Mock(return_value=Platform.RASPBERRY_PI)) 160 | def test_raspberry_pi_rev1(self): 161 | I2C = safe_import_i2c() 162 | bus = I2C.get_default_bus() 163 | self.assertEqual(bus, 0) 164 | 165 | @patch('Adafruit_GPIO.Platform.pi_revision', Mock(return_value=2)) 166 | @patch('Adafruit_GPIO.Platform.platform_detect', Mock(return_value=Platform.RASPBERRY_PI)) 167 | def test_raspberry_pi_rev2(self): 168 | I2C = safe_import_i2c() 169 | bus = I2C.get_default_bus() 170 | self.assertEqual(bus, 1) 171 | 172 | @patch('Adafruit_GPIO.Platform.platform_detect', Mock(return_value=Platform.BEAGLEBONE_BLACK)) 173 | def test_beaglebone_black(self): 174 | I2C = safe_import_i2c() 175 | bus = I2C.get_default_bus() 176 | self.assertEqual(bus, 1) 177 | 178 | @patch('Adafruit_GPIO.Platform.platform_detect', Mock(return_value=Platform.UNKNOWN)) 179 | def test_unknown(self): 180 | I2C = safe_import_i2c() 181 | self.assertRaises(RuntimeError, I2C.get_default_bus) 182 | -------------------------------------------------------------------------------- /Adafruit_Python_GPIO/test/test_PWM.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2014 Adafruit Industries 2 | # Author: Tony DiCola 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to deal 6 | # in the Software without restriction, including without limitation the rights 7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | # copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in 12 | # all copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | # THE SOFTWARE. 21 | 22 | import unittest 23 | 24 | from mock import Mock, patch 25 | 26 | import Adafruit_GPIO.PWM as PWM 27 | 28 | 29 | class TestRPi_PWM_Adapter(unittest.TestCase): 30 | def test_setup(self): 31 | rpi_gpio = Mock() 32 | pwm = PWM.RPi_PWM_Adapter(rpi_gpio) 33 | pwm.start(1, 50) 34 | rpi_gpio.PWM.assert_called_with(1, 2000) 35 | 36 | def test_set_duty_cycle_valid(self): 37 | rpi_gpio = Mock() 38 | pwm = PWM.RPi_PWM_Adapter(rpi_gpio) 39 | pwm.start(1, 50) 40 | pwm.set_duty_cycle(1, 75) 41 | # Implicit verification that no assertion or other error thrown. 42 | 43 | def test_set_duty_cycle_invalid(self): 44 | rpi_gpio = Mock() 45 | pwm = PWM.RPi_PWM_Adapter(rpi_gpio) 46 | pwm.start(1, 50) 47 | self.assertRaises(ValueError, pwm.set_duty_cycle, 1, 150) 48 | self.assertRaises(ValueError, pwm.set_duty_cycle, 1, -10) 49 | 50 | def test_set_frequency(self): 51 | rpi_gpio = Mock() 52 | pwm = PWM.RPi_PWM_Adapter(rpi_gpio) 53 | pwm.start(1, 50) 54 | pwm.set_frequency(1, 1000) 55 | # Implicit verification that no assertion or other error thrown. 56 | 57 | 58 | class TestBBIO_PWM_Adapter(unittest.TestCase): 59 | def test_setup(self): 60 | bbio_pwm = Mock() 61 | pwm = PWM.BBIO_PWM_Adapter(bbio_pwm) 62 | pwm.start('P9_16', 50) 63 | bbio_pwm.start.assert_called_with('P9_16', 50, 2000) 64 | 65 | def test_set_duty_cycle_valid(self): 66 | bbio_pwm = Mock() 67 | pwm = PWM.BBIO_PWM_Adapter(bbio_pwm) 68 | pwm.start('P9_16', 50) 69 | pwm.set_duty_cycle('P9_16', 75) 70 | bbio_pwm.set_duty_cycle.assert_called_with('P9_16', 75) 71 | 72 | def test_set_duty_cycle_invalid(self): 73 | bbio_pwm = Mock() 74 | pwm = PWM.BBIO_PWM_Adapter(bbio_pwm) 75 | pwm.start('P9_16', 50) 76 | self.assertRaises(ValueError, pwm.set_duty_cycle, 'P9_16', 150) 77 | self.assertRaises(ValueError, pwm.set_duty_cycle, 'P9_16', -10) 78 | 79 | def test_set_frequency(self): 80 | bbio_pwm = Mock() 81 | pwm = PWM.BBIO_PWM_Adapter(bbio_pwm) 82 | pwm.start('P9_16', 50) 83 | pwm.set_frequency('P9_16', 1000) 84 | bbio_pwm.set_frequency.assert_called_with('P9_16', 1000) 85 | 86 | 87 | class TestGetPlatformPWM(unittest.TestCase): 88 | @patch.dict('sys.modules', {'RPi': Mock(), 'RPi.GPIO': Mock()}) 89 | @patch('platform.platform', Mock(return_value='Linux-3.10.25+-armv6l-with-debian-7.4')) 90 | def test_raspberrypi(self): 91 | pwm = PWM.get_platform_pwm() 92 | self.assertIsInstance(pwm, PWM.RPi_PWM_Adapter) 93 | 94 | @patch.dict('sys.modules', {'Adafruit_BBIO': Mock(), 'Adafruit_BBIO.PWM': Mock()}) 95 | @patch('platform.platform', Mock(return_value='Linux-3.8.13-bone47-armv7l-with-debian-7.4')) 96 | def test_beagleboneblack(self): 97 | pwm = PWM.get_platform_pwm() 98 | self.assertIsInstance(pwm, PWM.BBIO_PWM_Adapter) 99 | 100 | @patch('platform.platform', Mock(return_value='Darwin-13.2.0-x86_64-i386-64bit')) 101 | def test_otherplatform(self): 102 | self.assertRaises(RuntimeError, PWM.get_platform_pwm) 103 | -------------------------------------------------------------------------------- /Adafruit_Python_GPIO/test/test_Platform.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2014 Adafruit Industries 2 | # Author: Tony DiCola 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to deal 6 | # in the Software without restriction, including without limitation the rights 7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | # copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in 12 | # all copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | # THE SOFTWARE. 21 | import unittest 22 | 23 | from mock import Mock, patch 24 | 25 | import Adafruit_GPIO.Platform as Platform 26 | 27 | 28 | class TestPlatformDetect(unittest.TestCase): 29 | @patch('platform.platform', Mock(return_value='Linux-3.10.25+-armv6l-with-debian-7.4')) 30 | def test_raspberry_pi(self): 31 | result = Platform.platform_detect() 32 | self.assertEquals(result, Platform.RASPBERRY_PI) 33 | 34 | @patch('platform.platform', Mock(return_value='Linux-3.8.13-bone47-armv7l-with-debian-7.4')) 35 | def test_beaglebone_black(self): 36 | result = Platform.platform_detect() 37 | self.assertEquals(result, Platform.BEAGLEBONE_BLACK) 38 | 39 | @patch('platform.platform', Mock(return_value='Darwin-13.2.0-x86_64-i386-64bit')) 40 | def test_unknown(self): 41 | result = Platform.platform_detect() 42 | self.assertEquals(result, Platform.UNKNOWN) 43 | 44 | 45 | class TestPiRevision(unittest.TestCase): 46 | def test_revision_1(self): 47 | with patch('__builtin__.open') as mock_open: 48 | handle = mock_open.return_value.__enter__.return_value 49 | handle.__iter__.return_value = iter(['Revision : 0000']) 50 | rev = Platform.pi_revision() 51 | self.assertEquals(rev, 1) 52 | with patch('__builtin__.open') as mock_open: 53 | handle = mock_open.return_value.__enter__.return_value 54 | handle.__iter__.return_value = iter(['Revision : 0002']) 55 | rev = Platform.pi_revision() 56 | self.assertEquals(rev, 1) 57 | with patch('__builtin__.open') as mock_open: 58 | handle = mock_open.return_value.__enter__.return_value 59 | handle.__iter__.return_value = iter(['Revision : 0003']) 60 | rev = Platform.pi_revision() 61 | self.assertEquals(rev, 1) 62 | 63 | def test_revision_2(self): 64 | with patch('__builtin__.open') as mock_open: 65 | handle = mock_open.return_value.__enter__.return_value 66 | handle.__iter__.return_value = iter(['Revision : 000e']) 67 | rev = Platform.pi_revision() 68 | self.assertEquals(rev, 2) 69 | 70 | def test_unknown_revision(self): 71 | with patch('__builtin__.open') as mock_open: 72 | handle = mock_open.return_value.__enter__.return_value 73 | handle.__iter__.return_value = iter(['foobar']) 74 | self.assertRaises(RuntimeError, Platform.pi_revision) 75 | 76 | -------------------------------------------------------------------------------- /Adafruit_Python_GPIO/test/test_SPI.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2014 Adafruit Industries 2 | # Author: Tony DiCola 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to deal 6 | # in the Software without restriction, including without limitation the rights 7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | # copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in 12 | # all copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | # THE SOFTWARE. 21 | 22 | import unittest 23 | 24 | import Adafruit_GPIO as GPIO 25 | import Adafruit_GPIO.SPI as SPI 26 | 27 | from MockGPIO import MockGPIO 28 | 29 | 30 | class TestBitBangSPI(unittest.TestCase): 31 | def test_pin_modes_set_correctly(self): 32 | gpio = MockGPIO() 33 | device = SPI.BitBang(gpio, 1, 2, 3, 4) 34 | self.assertDictEqual(gpio.pin_mode, { 1: GPIO.OUT, 35 | 2: GPIO.OUT, 36 | 3: GPIO.IN, 37 | 4: GPIO.OUT }) 38 | 39 | def test_ss_set_high_after_initialization(self): 40 | gpio = MockGPIO() 41 | device = SPI.BitBang(gpio, 1, 2, 3, 4) 42 | self.assertListEqual(gpio.pin_written[4], [1]) 43 | 44 | def test_mode_0_write(self): 45 | gpio = MockGPIO() 46 | device = SPI.BitBang(gpio, 1, 2, 3, 4) 47 | device.write([0x1F]) 48 | # Verify clock 49 | self.assertListEqual(gpio.pin_written[1], [0, 1, 0, 1, 0, 1, 0, 1, 50 | 0, 1, 0, 1, 0, 1, 0, 1, 0]) 51 | # Verify MOSI 52 | self.assertListEqual(gpio.pin_written[2], [0, 0, 0, 1, 1, 1, 1, 1]) 53 | # Verify MISO 54 | self.assertNotIn(3, gpio.pin_written) 55 | # Verify SS 56 | self.assertListEqual(gpio.pin_written[4], [1, 0, 1]) 57 | 58 | def test_write_assert_deassert_ss_false(self): 59 | gpio = MockGPIO() 60 | device = SPI.BitBang(gpio, 1, 2, 3, 4) 61 | device.write([0x1F], assert_ss=False, deassert_ss=False) 62 | self.assertListEqual(gpio.pin_written[4], [1]) 63 | 64 | def test_write_lsbfirst(self): 65 | gpio = MockGPIO() 66 | device = SPI.BitBang(gpio, 1, 2, 3, 4) 67 | device.set_bit_order(SPI.LSBFIRST) 68 | device.write([0x1F]) 69 | self.assertListEqual(gpio.pin_written[2], [1, 1, 1, 1, 1, 0, 0, 0]) 70 | 71 | def test_invalid_mode_fails(self): 72 | gpio = MockGPIO() 73 | device = SPI.BitBang(gpio, 1, 2, 3, 4) 74 | self.assertRaises(ValueError, device.set_mode, -1) 75 | self.assertRaises(ValueError, device.set_mode, 4) 76 | 77 | def test_invalid_bit_order_fails(self): 78 | gpio = MockGPIO() 79 | device = SPI.BitBang(gpio, 1, 2, 3, 4) 80 | self.assertRaises(ValueError, device.set_bit_order, -1) 81 | self.assertRaises(ValueError, device.set_bit_order, 2) 82 | 83 | def test_mode_0_read(self): 84 | gpio = MockGPIO() 85 | device = SPI.BitBang(gpio, 1, 2, 3, 4) 86 | gpio.pin_read[3] = [0, 0, 0, 1, 1, 1, 1, 1] 87 | result = device.read(1) 88 | # Verify clock 89 | self.assertListEqual(gpio.pin_written[1], [0, 1, 0, 1, 0, 1, 0, 1, 90 | 0, 1, 0, 1, 0, 1, 0, 1, 0]) 91 | # Verify MOSI 92 | self.assertNotIn(2, gpio.pin_written) 93 | # Verify MISO 94 | self.assertNotIn(3, gpio.pin_written) 95 | # Verify SS 96 | self.assertListEqual(gpio.pin_written[4], [1, 0, 1]) 97 | # Verify result 98 | self.assertEqual(result, bytearray([0x1F])) 99 | 100 | def test_read_assert_deassert_ss_false(self): 101 | gpio = MockGPIO() 102 | device = SPI.BitBang(gpio, 1, 2, 3, 4) 103 | gpio.pin_read[3] = [0, 0, 0, 1, 1, 1, 1, 1] 104 | result = device.read(1, assert_ss=False, deassert_ss=False) 105 | self.assertListEqual(gpio.pin_written[4], [1]) 106 | 107 | def test_read_multiple_bytes(self): 108 | gpio = MockGPIO() 109 | device = SPI.BitBang(gpio, 1, 2, 3, 4) 110 | gpio.pin_read[3] = [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 111 | 0, 0, 0, 1, 1, 1, 1, 1] 112 | result = device.read(3) 113 | # Verify clock 114 | self.assertListEqual(gpio.pin_written[1], [0, 1, 0, 1, 0, 1, 0, 1, 115 | 0, 1, 0, 1, 0, 1, 0, 1, 116 | 0, 1, 0, 1, 0, 1, 0, 1, 117 | 0, 1, 0, 1, 0, 1, 0, 1, 118 | 0, 1, 0, 1, 0, 1, 0, 1, 119 | 0, 1, 0, 1, 0, 1, 0, 1, 0]) 120 | # Verify MOSI 121 | self.assertNotIn(2, gpio.pin_written) 122 | # Verify MISO 123 | self.assertNotIn(3, gpio.pin_written) 124 | # Verify SS 125 | self.assertListEqual(gpio.pin_written[4], [1, 0, 1]) 126 | # Verify result 127 | self.assertEqual(result, bytearray([0x1F, 0xF8, 0x1F])) 128 | 129 | def test_write_multiple_bytes(self): 130 | gpio = MockGPIO() 131 | device = SPI.BitBang(gpio, 1, 2, 3, 4) 132 | device.write([0x1F, 0xF8, 0x1F]) 133 | # Verify clock 134 | self.assertListEqual(gpio.pin_written[1], [0, 1, 0, 1, 0, 1, 0, 1, 135 | 0, 1, 0, 1, 0, 1, 0, 1, 136 | 0, 1, 0, 1, 0, 1, 0, 1, 137 | 0, 1, 0, 1, 0, 1, 0, 1, 138 | 0, 1, 0, 1, 0, 1, 0, 1, 139 | 0, 1, 0, 1, 0, 1, 0, 1, 0]) 140 | # Verify MOSI 141 | self.assertListEqual(gpio.pin_written[2], [0, 0, 0, 1, 1, 1, 1, 1, 142 | 1, 1, 1, 1, 1, 0, 0, 0, 143 | 0, 0, 0, 1, 1, 1, 1, 1]) 144 | # Verify MISO 145 | self.assertNotIn(3, gpio.pin_written) 146 | # Verify SS 147 | self.assertListEqual(gpio.pin_written[4], [1, 0, 1]) 148 | 149 | def test_mode_0_transfer(self): 150 | gpio = MockGPIO() 151 | device = SPI.BitBang(gpio, 1, 2, 3, 4) 152 | gpio.pin_read[3] = [0, 0, 0, 1, 1, 1, 1, 1] 153 | result = device.transfer([0xF8]) 154 | # Verify clock 155 | self.assertListEqual(gpio.pin_written[1], [0, 1, 0, 1, 0, 1, 0, 1, 156 | 0, 1, 0, 1, 0, 1, 0, 1, 0]) 157 | # Verify MOSI 158 | self.assertListEqual(gpio.pin_written[2], [1, 1, 1, 1, 1, 0, 0, 0]) 159 | # Verify MISO 160 | self.assertNotIn(3, gpio.pin_written) 161 | # Verify SS 162 | self.assertListEqual(gpio.pin_written[4], [1, 0, 1]) 163 | # Verify result 164 | self.assertEqual(result, bytearray([0x1F])) 165 | 166 | def test_transfer_multiple_bytes(self): 167 | gpio = MockGPIO() 168 | device = SPI.BitBang(gpio, 1, 2, 3, 4) 169 | gpio.pin_read[3] = [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 170 | 0, 0, 0, 1, 1, 1, 1, 1] 171 | result = device.transfer([0xF8, 0x1F, 0xF8]) 172 | # Verify clock 173 | self.assertListEqual(gpio.pin_written[1], [0, 1, 0, 1, 0, 1, 0, 1, 174 | 0, 1, 0, 1, 0, 1, 0, 1, 175 | 0, 1, 0, 1, 0, 1, 0, 1, 176 | 0, 1, 0, 1, 0, 1, 0, 1, 177 | 0, 1, 0, 1, 0, 1, 0, 1, 178 | 0, 1, 0, 1, 0, 1, 0, 1, 0]) 179 | # Verify MOSI 180 | self.assertListEqual(gpio.pin_written[2], [1, 1, 1, 1, 1, 0, 0, 0, 181 | 0, 0, 0, 1, 1, 1, 1, 1, 182 | 1, 1, 1, 1, 1, 0, 0, 0]) 183 | # Verify MISO 184 | self.assertNotIn(3, gpio.pin_written) 185 | # Verify SS 186 | self.assertListEqual(gpio.pin_written[4], [1, 0, 1]) 187 | # Verify result 188 | self.assertEqual(result, bytearray([0x1F, 0xF8, 0x1F])) 189 | 190 | #TODO: Test mode 1, 2, 3 191 | 192 | #TODO: Test null MOSI, MISO, SS 193 | -------------------------------------------------------------------------------- /MySQLFiles/WeatherPi.sql: -------------------------------------------------------------------------------- 1 | -- phpMyAdmin SQL Dump 2 | -- version 3.4.11.1deb2+deb7u1 3 | -- http://www.phpmyadmin.net 4 | -- 5 | -- Host: localhost 6 | -- Generation Time: May 10, 2015 at 05:31 PM 7 | -- Server version: 5.5.41 8 | -- PHP Version: 5.4.4-14+deb7u12 9 | 10 | SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO"; 11 | SET time_zone = "+00:00"; 12 | 13 | 14 | /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; 15 | /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; 16 | /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; 17 | /*!40101 SET NAMES utf8 */; 18 | 19 | -- 20 | -- Database: `WeatherPi` 21 | -- 22 | 23 | -- -------------------------------------------------------- 24 | 25 | -- 26 | -- Table structure for table `PowerSystem` 27 | -- 28 | 29 | CREATE TABLE IF NOT EXISTS `PowerSystem` ( 30 | `ID` int(11) NOT NULL AUTO_INCREMENT, 31 | `TimeStamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 32 | `batteryVoltage` float NOT NULL, 33 | `batteryCurrent` float NOT NULL, 34 | `solarVoltage` float NOT NULL, 35 | `solarCurrent` float NOT NULL, 36 | `loadVoltage` float NOT NULL, 37 | `loadCurrent` float NOT NULL, 38 | `batteryPower` float NOT NULL, 39 | `solarPower` float NOT NULL, 40 | `loadPower` float NOT NULL, 41 | `batteryCharge` float NOT NULL, 42 | PRIMARY KEY (`ID`) 43 | ) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=6381 ; 44 | 45 | -- -------------------------------------------------------- 46 | 47 | -- 48 | -- Table structure for table `systemlog` 49 | -- 50 | 51 | CREATE TABLE IF NOT EXISTS `systemlog` ( 52 | `ID` int(11) NOT NULL AUTO_INCREMENT, 53 | `TimeStamp` datetime NOT NULL, 54 | `Level` int(11) NOT NULL, 55 | `Source` varchar(250) NOT NULL, 56 | `Message` varchar(250) NOT NULL, 57 | PRIMARY KEY (`ID`) 58 | ) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=295 ; 59 | 60 | -- -------------------------------------------------------- 61 | 62 | -- 63 | -- Table structure for table `WeatherData` 64 | -- 65 | 66 | CREATE TABLE IF NOT EXISTS `WeatherData` ( 67 | `ID` int(20) NOT NULL AUTO_INCREMENT, 68 | `TimeStamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 69 | `as3935LightningCount` float NOT NULL, 70 | `as3935LastInterrupt` float NOT NULL, 71 | `as3935LastDistance` float NOT NULL, 72 | `as3935LastStatus` varchar(200) NOT NULL, 73 | `currentWindSpeed` float NOT NULL, 74 | `currentWindGust` float NOT NULL, 75 | `currentWindDirection` float NOT NULL, 76 | `currentWindDirectionVoltage` float NOT NULL, 77 | `totalRain` float NOT NULL, 78 | `bmp180Temperature` int(20) NOT NULL, 79 | `bmp180Pressure` float NOT NULL, 80 | `bmp180Altitude` float NOT NULL, 81 | `bmp180SeaLevel` float NOT NULL, 82 | `outsideTemperature` float NOT NULL, 83 | `outsideHumidity` float NOT NULL, 84 | `insideTemperature` float NOT NULL, 85 | `insideHumidity` float NOT NULL, 86 | PRIMARY KEY (`ID`) 87 | ) ENGINE=InnoDB DEFAULT CHARSET=latin1 COMMENT='Weather Data' AUTO_INCREMENT=6019 ; 88 | 89 | /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; 90 | /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; 91 | /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; 92 | 93 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | WeatherPi Libraries and Example for Raspberry Pi Solar Powered Weather Station 2 | 3 | Supports SwitchDoc Labs WeatherRack WeatherPiArduino 4 | 5 | Version 1.4 6 | 7 | Other installations required for AM2315: 8 | 9 | sudo apt-get install python-pip 10 | sudo apt-get install libi2c-dev 11 | sudo pip install tentacle_pi 12 | 13 | SwitchDocLabs Documentation for WeatherRack/WeatherPiArduino under products on: 14 | 15 | http://www.switchdoc.com/ 16 | 17 | March 28, 2015 - added subdirectories 18 | May 9, 2015 - Updated software for WatchDog Timer and Email 19 | May 10, 2015 - Added mysql table SQL files for database building 20 | 21 | -------------------------------------------------------------------------------- /RTC_SDL_DS3231/README.md: -------------------------------------------------------------------------------- 1 | 2 | Raspberry Pi Python Library for SwitchDoc Labs DS3231/AT24C32 RTC Module 3 | 4 | SwitchDoc Labs, LLC December 19, 2014 5 | 6 | Clone respository and run testDS3231.py to test 7 | 8 | More Information on www.switchdoc.com 9 | 10 | Runs RTC and EEPROM 11 | 12 | 13 | -------------------------------------------------------------------------------- /RTC_SDL_DS3231/SDL_DS3231.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # SDL_DS3231.py Python Driver Code 4 | # SwitchDoc Labs 12/19/2014 5 | # V 1.2 6 | # only works in 24 hour mode 7 | # now includes reading and writing the AT24C32 included on the SwitchDoc Labs 8 | # DS3231 / AT24C32 Module (www.switchdoc.com 9 | 10 | 11 | #encoding: utf-8 12 | 13 | # Copyright (C) 2013 @XiErCh 14 | # 15 | # Permission is hereby granted, free of charge, to any person obtaining a copy 16 | # of this software and associated documentation files (the "Software"), to deal 17 | # in the Software without restriction, including without limitation the rights 18 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 19 | # copies of the Software, and to permit persons to whom the Software is 20 | # furnished to do so, subject to the following conditions: 21 | # 22 | # The above copyright notice and this permission notice shall be included in all 23 | # copies or substantial portions of the Software. 24 | # 25 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 26 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 27 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 28 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 29 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 30 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 31 | # SOFTWARE. 32 | 33 | from datetime import datetime 34 | 35 | import time 36 | import smbus 37 | 38 | 39 | def _bcd_to_int(bcd): 40 | """Decode a 2x4bit BCD to a integer. 41 | """ 42 | out = 0 43 | for d in (bcd >> 4, bcd): 44 | for p in (1, 2, 4 ,8): 45 | if d & 1: 46 | out += p 47 | d >>= 1 48 | out *= 10 49 | return out / 10 50 | 51 | 52 | def _int_to_bcd(n): 53 | """Encode a one or two digits number to the BCD. 54 | """ 55 | bcd = 0 56 | for i in (n // 10, n % 10): 57 | for p in (8, 4, 2, 1): 58 | if i >= p: 59 | bcd += 1 60 | i -= p 61 | bcd <<= 1 62 | return bcd >> 1 63 | 64 | 65 | class SDL_DS3231(): 66 | _REG_SECONDS = 0x00 67 | _REG_MINUTES = 0x01 68 | _REG_HOURS = 0x02 69 | _REG_DAY = 0x03 70 | _REG_DATE = 0x04 71 | _REG_MONTH = 0x05 72 | _REG_YEAR = 0x06 73 | _REG_CONTROL = 0x07 74 | 75 | 76 | 77 | ########################### 78 | # DS3231 Code 79 | ########################### 80 | def __init__(self, twi=1, addr=0x68, at24c32_addr=0x56): 81 | self._bus = smbus.SMBus(twi) 82 | self._addr = addr 83 | self._at24c32_addr = at24c32_addr 84 | 85 | 86 | def _write(self, register, data): 87 | #print "addr =0x%x register = 0x%x data = 0x%x %i " % (self._addr, register, data,_bcd_to_int(data)) 88 | self._bus.write_byte_data(self._addr, register, data) 89 | 90 | 91 | def _read(self, data): 92 | 93 | returndata = self._bus.read_byte_data(self._addr, data) 94 | #print "addr = 0x%x data = 0x%x %i returndata = 0x%x %i " % (self._addr, data, data, returndata, _bcd_to_int(returndata)) 95 | return returndata 96 | 97 | 98 | 99 | 100 | def _read_seconds(self): 101 | return _bcd_to_int(self._read(self._REG_SECONDS)& 0x7F) # wipe out the oscillator on bit 102 | 103 | 104 | def _read_minutes(self): 105 | return _bcd_to_int(self._read(self._REG_MINUTES)) 106 | 107 | 108 | def _read_hours(self): 109 | d = self._read(self._REG_HOURS) 110 | if (d == 0x64): 111 | d = 0x40 112 | return _bcd_to_int(d & 0x3F) 113 | 114 | 115 | def _read_day(self): 116 | return _bcd_to_int(self._read(self._REG_DAY)) 117 | 118 | 119 | def _read_date(self): 120 | return _bcd_to_int(self._read(self._REG_DATE)) 121 | 122 | 123 | def _read_month(self): 124 | return _bcd_to_int(self._read(self._REG_MONTH)) 125 | 126 | 127 | def _read_year(self): 128 | return _bcd_to_int(self._read(self._REG_YEAR)) 129 | 130 | 131 | def read_all(self): 132 | """Return a tuple such as (year, month, date, day, hours, minutes, 133 | seconds). 134 | """ 135 | return (self._read_year(), self._read_month(), self._read_date(), 136 | self._read_day(), self._read_hours(), self._read_minutes(), 137 | self._read_seconds()) 138 | 139 | 140 | def read_str(self): 141 | """Return a string such as 'YY-DD-MMTHH-MM-SS'. 142 | """ 143 | return '%02d-%02d-%02dT%02d:%02d:%02d' % (self._read_year(), 144 | self._read_month(), self._read_date(), self._read_hours(), 145 | self._read_minutes(), self._read_seconds()) 146 | 147 | 148 | def read_datetime(self, century=21, tzinfo=None): 149 | """Return the datetime.datetime object. 150 | """ 151 | return datetime((century - 1) * 100 + self._read_year(), 152 | self._read_month(), self._read_date(), self._read_hours(), 153 | self._read_minutes(), self._read_seconds(), 0, tzinfo=tzinfo) 154 | 155 | 156 | def write_all(self, seconds=None, minutes=None, hours=None, day=None, 157 | date=None, month=None, year=None, save_as_24h=True): 158 | """Direct write un-none value. 159 | Range: seconds [0,59], minutes [0,59], hours [0,23], 160 | day [0,7], date [1-31], month [1-12], year [0-99]. 161 | """ 162 | if seconds is not None: 163 | if seconds < 0 or seconds > 59: 164 | raise ValueError('Seconds is out of range [0,59].') 165 | seconds_reg = _int_to_bcd(seconds) 166 | self._write(self._REG_SECONDS, seconds_reg) 167 | 168 | if minutes is not None: 169 | if minutes < 0 or minutes > 59: 170 | raise ValueError('Minutes is out of range [0,59].') 171 | self._write(self._REG_MINUTES, _int_to_bcd(minutes)) 172 | 173 | if hours is not None: 174 | if hours < 0 or hours > 23: 175 | raise ValueError('Hours is out of range [0,23].') 176 | self._write(self._REG_HOURS, _int_to_bcd(hours) ) # not | 0x40 according to datasheet 177 | 178 | if year is not None: 179 | if year < 0 or year > 99: 180 | raise ValueError('Years is out of range [0,99].') 181 | self._write(self._REG_YEAR, _int_to_bcd(year)) 182 | 183 | if month is not None: 184 | if month < 1 or month > 12: 185 | raise ValueError('Month is out of range [1,12].') 186 | self._write(self._REG_MONTH, _int_to_bcd(month)) 187 | 188 | if date is not None: 189 | if date < 1 or date > 31: 190 | raise ValueError('Date is out of range [1,31].') 191 | self._write(self._REG_DATE, _int_to_bcd(date)) 192 | 193 | if day is not None: 194 | if day < 1 or day > 7: 195 | raise ValueError('Day is out of range [1,7].') 196 | self._write(self._REG_DAY, _int_to_bcd(day)) 197 | 198 | 199 | def write_datetime(self, dt): 200 | """Write from a datetime.datetime object. 201 | """ 202 | self.write_all(dt.second, dt.minute, dt.hour, 203 | dt.isoweekday(), dt.day, dt.month, dt.year % 100) 204 | 205 | 206 | def write_now(self): 207 | """Equal to DS3231.write_datetime(datetime.datetime.now()). 208 | """ 209 | self.write_datetime(datetime.now()) 210 | 211 | 212 | 213 | def getTemp(self): 214 | byte_tmsb = self._bus.read_byte_data(self._addr,0x11) 215 | byte_tlsb = bin(self._bus.read_byte_data(self._addr,0x12))[2:].zfill(8) 216 | return byte_tmsb+int(byte_tlsb[0])*2**(-1)+int(byte_tlsb[1])*2**(-2) 217 | 218 | ########################### 219 | # AT24C32 Code 220 | ########################### 221 | 222 | def set_current_AT24C32_address(self,address): 223 | a1=address/256; 224 | a0=address%256; 225 | self._bus.write_i2c_block_data(self._at24c32_addr,a1,[a0]) 226 | 227 | 228 | def read_AT24C32_byte(self, address): 229 | #print "i2c_address =0x%x eepromaddress = 0x%x " % (self._at24c32_addr, address) 230 | 231 | self.set_current_AT24C32_address(address) 232 | return self._bus.read_byte(self._at24c32_addr) 233 | 234 | 235 | 236 | def write_AT24C32_byte(self, address, value): 237 | #print "i2c_address =0x%x eepromaddress = 0x%x value = 0x%x %i " % (self._at24c32_addr, address, value, value) 238 | 239 | 240 | a1=address/256; 241 | a0=address%256; 242 | self._bus.write_i2c_block_data(self._at24c32_addr,a1,[a0, value]) 243 | time.sleep(0.20) 244 | 245 | 246 | 247 | -------------------------------------------------------------------------------- /RTC_SDL_DS3231/testSDL_DS3231.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Test SDL_DS3231 4 | # John C. Shovic, SwitchDoc Labs 5 | # 08/03/2014 6 | # 7 | # 8 | 9 | # imports 10 | 11 | import sys 12 | import time 13 | import datetime 14 | import random 15 | import SDL_DS3231 16 | 17 | # Main Program 18 | 19 | print "" 20 | print "Test SDL_DS3231 Version 1.0 - SwitchDoc Labs" 21 | print "" 22 | print "" 23 | print "Program Started at:"+ time.strftime("%Y-%m-%d %H:%M:%S") 24 | print "" 25 | 26 | filename = time.strftime("%Y-%m-%d%H:%M:%SRTCTest") + ".txt" 27 | starttime = datetime.datetime.utcnow() 28 | 29 | ds3231 = SDL_DS3231.SDL_DS3231(1, 0x68) 30 | #comment out the next line after the clock has been initialized 31 | ds3231.write_now() 32 | 33 | # Main Loop - sleeps 10 seconds, then reads and prints values of all clocks 34 | # Also reads two bytes of EEPROM and writes the next value to the two bytes 35 | 36 | # do the AT24C32 eeprom 37 | 38 | print "----------------- " 39 | print "----------------- " 40 | print " Test the AT24C32 EEPROM" 41 | print "----------------- " 42 | print "writing first 10 addresses with random data" 43 | for x in range(0,10): 44 | value = random.randint(0,255) 45 | print "address = %i writing value=%i" % (x, value) 46 | ds3231.write_AT24C32_byte(x, value) 47 | print "----------------- " 48 | 49 | print "reading first 10 addresses" 50 | for x in range(0,10): 51 | print "address = %i value = %i" %(x, ds3231.read_AT24C32_byte(x)) 52 | print "----------------- " 53 | print "----------------- " 54 | 55 | 56 | while True: 57 | 58 | 59 | 60 | 61 | # 62 | currenttime = datetime.datetime.utcnow() 63 | 64 | deltatime = currenttime - starttime 65 | 66 | print "" 67 | print "Raspberry Pi=\t" + time.strftime("%Y-%m-%d %H:%M:%S") 68 | 69 | print "DS3231=\t\t%s" % ds3231.read_datetime() 70 | 71 | print "DS3231 Temp=", ds3231.getTemp() 72 | time.sleep(10.0) 73 | -------------------------------------------------------------------------------- /RaspberryPi-AS3935/.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.swp 3 | build/ 4 | MANIFEST 5 | dist/ 6 | __pycache__/ 7 | venv/ 8 | RPi_AS3935.egg-info/ 9 | .tox/ 10 | .coverage 11 | -------------------------------------------------------------------------------- /RaspberryPi-AS3935/.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 2.7 3 | env: 4 | - TOX_ENV=pep8 5 | - TOX_ENV=py26 6 | - TOX_ENV=py27 7 | - TOX_ENV=py34 8 | - TOX_ENV=flake8 9 | install: 10 | - pip install --upgrade pip setuptools tox virtualenv . 11 | matrix: 12 | allow_failures: 13 | - env: TOX_ENV=py26 14 | - env: TOX_ENV=py34 15 | script: 16 | - "tox -e $TOX_ENV" 17 | notifications: 18 | email: false 19 | -------------------------------------------------------------------------------- /RaspberryPi-AS3935/LICENSE: -------------------------------------------------------------------------------- 1 | Licensed under the Apache License, Version 2.0 (the "License"); 2 | you may not use this file except in compliance with the License. 3 | You may obtain a copy of the License at 4 | 5 | http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | -------------------------------------------------------------------------------- /RaspberryPi-AS3935/README.md: -------------------------------------------------------------------------------- 1 | RaspberryPi-AS3935 2 | ================== 3 | 4 | [![Build Status](https://travis-ci.org/pcfens/RaspberryPi-AS3935.png?branch=master)](https://travis-ci.org/pcfens/RaspberryPi-AS3935) 5 | 6 | A python library and demo script for interacting with the 7 | [AMS Franklin Lightning Sensor](http://www.ams.com/eng/Products/RF-Products/Lightning-Sensor/AS3935). 8 | 9 | This script will only work if the correct kernel modules are loaded 10 | on your Pi. Adafruit has a nice [tutorial](http://learn.adafruit.com/adafruits-raspberry-pi-lesson-4-gpio-setup/configuring-i2c) 11 | set up, though depending on the breakout board that you use, you may 12 | not see anything show up when you run `i2cdetect`. 13 | 14 | ## Installation 15 | 16 | You can install this module by running 17 | ``` 18 | pip install RPi_AS3935 19 | ``` 20 | 21 | or you can clone this repository and run 22 | ``` 23 | python setup.py install 24 | ``` 25 | 26 | ## Breakout Board 27 | 28 | The AS3935 is a small chip, and rather than trying to solder it myself 29 | I purchased a (v2) breakout board from [Embedded Adventures](http://www.embeddedadventures.com/as3935_lightning_sensor_module_mod-1016.html). 30 | 31 | 32 | ## Connecting the AS3935 33 | 34 | In my test setup I connected my breakout board to the Pi as shown 35 | 36 | | AS3935 Pin | Raspberry Pi Pin | 37 | | ---------: | :--------------- | 38 | | 4 (GND) | 25 (Ground) | 39 | | 5 (VDD) | 1 (3v3 Power) | 40 | | 10 (IRQ) | 11 (GPIO 17) | 41 | | 11 (I2CL) | 5 (SCL) | 42 | | 13 (I2CD) | 3 (SDA) | 43 | 44 | ## Known Issues 45 | 46 | ### Addressing 47 | 48 | You may need edit line 12 of demo.py so that the correct address is read. 49 | 50 | | Breakout Board | Default Address | 51 | | :------------- | :-------------- | 52 | | Embedded Adeventures v2 | 0x00 | 53 | | Embedded Adeventures v4 (untested) | 0x03 | 54 | | Tautic Electronics (untested) | 0x00 | 55 | 56 | ### RaspberryPi Model 57 | 58 | If you have one of the newer 512MB Pi boards, then you need to adjust line 12 59 | in the demo script to read 60 | ```python 61 | sensor = RPi_AS3935(address = 0x00, bus = 1) 62 | ``` 63 | 64 | ## Implementations 65 | 66 | * [Push based web interface](https://github.com/pcfens/RPi-AS3935-Web) 67 | 68 | Send a Pull Request if you'd like to get your original implementation listed here. 69 | -------------------------------------------------------------------------------- /RaspberryPi-AS3935/RPi_AS3935/RPi_AS3935.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | 4 | class RPi_AS3935: 5 | """A basic class used for interacting with the AS3935 lightning 6 | sensor from a Raspberry Pi over I2C""" 7 | 8 | def __init__(self, address, bus=0): 9 | self.address = address 10 | import smbus 11 | self.i2cbus = smbus.SMBus(bus) 12 | 13 | def calibrate(self, tun_cap=None): 14 | """Calibrate the lightning sensor - this takes up to half a second 15 | and is blocking. 16 | 17 | The value of tun_cap should be between 0 and 15, and is used to set 18 | the internal tuning capacitors (0-120pF in steps of 8pF) 19 | """ 20 | time.sleep(0.08) 21 | self.read_data() 22 | if tun_cap is not None: 23 | if tun_cap < 0x10 and tun_cap > -1: 24 | self.set_byte(0x08, (self.registers[0x08] & 0xF0) | tun_cap) 25 | time.sleep(0.002) 26 | else: 27 | raise Exception("Value of TUN_CAP must be between 0 and 15") 28 | self.set_byte(0x3D, 0x96) 29 | time.sleep(0.002) 30 | self.set_byte(0x08, self.registers[0x08] | 0x20) 31 | time.sleep(0.002) 32 | self.read_data() 33 | self.set_byte(0x08, self.registers[0x08] & 0xDF) 34 | time.sleep(0.002) 35 | 36 | def reset(self): 37 | """Reset all registers to their default power on values 38 | """ 39 | self.set_byte(0x3C, 0x96) 40 | 41 | def get_interrupt(self): 42 | """Get the value of the interrupt register 43 | 44 | 0x01 - Too much noise 45 | 0x04 - Disturber 46 | 0x08 - Lightning 47 | """ 48 | self.read_data() 49 | return self.registers[0x03] & 0x0F 50 | 51 | def get_distance(self): 52 | """Get the estimated distance of the most recent lightning event 53 | """ 54 | self.read_data() 55 | if self.registers[0x07] & 0x3F == 0x3F: 56 | return False 57 | else: 58 | return self.registers[0x07] & 0x3F 59 | 60 | def get_noise_floor(self): 61 | """Get the noise floor value. 62 | 63 | Actual voltage levels used in the sensor are located in Table 16 64 | of the data sheet. 65 | """ 66 | self.read_data() 67 | return (self.registers[0x01] & 0x70) >> 4 68 | 69 | def set_noise_floor(self, noisefloor): 70 | """Set the noise floor value. 71 | 72 | Actual voltage levels used in the sensor are located in Table 16 73 | of the data sheet. 74 | """ 75 | self.read_data() 76 | noisefloor = (noisefloor & 0x07) << 4 77 | write_data = (self.registers[0x01] & 0x8F) + noisefloor 78 | self.set_byte(0x01, write_data) 79 | 80 | def lower_noise_floor(self, min_noise=0): 81 | """Lower the noise floor by one step. 82 | 83 | min_noise is the minimum step that the noise_floor should be 84 | lowered to. 85 | """ 86 | floor = self.get_noise_floor() 87 | if floor > min_noise: 88 | floor = floor - 1 89 | self.set_noise_floor(floor) 90 | return floor 91 | 92 | def raise_noise_floor(self, max_noise=7): 93 | """Raise the noise floor by one step 94 | 95 | max_noise is the maximum step that the noise_floor should be 96 | raised to. 97 | """ 98 | floor = self.get_noise_floor() 99 | if floor < max_noise: 100 | floor = floor + 1 101 | self.set_noise_floor(floor) 102 | return floor 103 | 104 | def get_min_strikes(self): 105 | """Get the number of lightning detections required before an 106 | interrupt is raised. 107 | """ 108 | self.read_data() 109 | value = (self.registers[0x02] >> 4) & 0x03 110 | if value == 0: 111 | return 1 112 | elif value == 1: 113 | return 5 114 | elif value == 2: 115 | return 9 116 | elif value == 3: 117 | return 16 118 | 119 | def set_min_strikes(self, minstrikes): 120 | """Set the number of lightning detections required before an 121 | interrupt is raised. 122 | 123 | Valid values are 1, 5, 9, and 16, any other raises an exception. 124 | """ 125 | if minstrikes == 1: 126 | minstrikes = 0 127 | elif minstrikes == 5: 128 | minstrikes = 1 129 | elif minstrikes == 9: 130 | minstrikes = 2 131 | elif minstrikes == 16: 132 | minstrikes = 3 133 | else: 134 | raise Exception("Value must be 1, 5, 9, or 16") 135 | 136 | self.read_data() 137 | minstrikes = (minstrikes & 0x03) << 4 138 | write_data = (self.registers[0x02] & 0xCF) + minstrikes 139 | self.set_byte(0x02, write_data) 140 | 141 | def get_indoors(self): 142 | """Determine whether or not the sensor is configured for indoor 143 | use or not. 144 | 145 | Returns True if configured to be indoors, otherwise False. 146 | """ 147 | self.read_data() 148 | if self.registers[0x00] & 0x10 == 0x10: 149 | return True 150 | else: 151 | return False 152 | 153 | def set_indoors(self, indoors): 154 | """Set whether or not the sensor should use an indoor configuration. 155 | """ 156 | self.read_data() 157 | if indoors: 158 | write_value = (self.registers[0x00] & 0xE0) + 0x12 159 | else: 160 | write_value = (self.registers[0x00] & 0xE0) + 0x0E 161 | self.set_byte(0x00, write_value) 162 | 163 | def set_mask_disturber(self, mask_dist): 164 | """Set whether or not disturbers should be masked (no interrupts for 165 | what the sensor determines are man-made events) 166 | """ 167 | self.read_data() 168 | if mask_dist: 169 | write_value = self.registers[0x03] | 0x20 170 | else: 171 | write_value = self.registers[0x03] & 0xDF 172 | self.set_byte(0x03, write_value) 173 | 174 | def get_mask_disturber(self): 175 | """Get whether or not disturbers are masked or not. 176 | 177 | Returns True if interrupts are masked, false otherwise 178 | """ 179 | self.read_data() 180 | if self.registers[0x03] & 0x20 == 0x20: 181 | return True 182 | else: 183 | return False 184 | 185 | def set_disp_lco(self, display_lco): 186 | """Have the internal LC oscillator signal displayed on the interrupt pin for 187 | measurement. 188 | 189 | Passing display_lco=True enables the output, False disables it. 190 | """ 191 | self.read_data() 192 | if display_lco: 193 | self.set_byte(0x08, (self.registers[0x08] | 0x80)) 194 | else: 195 | self.set_byte(0x08, (self.registers[0x08] & 0x7F)) 196 | time.sleep(0.002) 197 | 198 | def get_disp_lco(self): 199 | """Determine whether or not the internal LC oscillator is displayed on the 200 | interrupt pin. 201 | 202 | Returns True if the LC oscillator is being displayed on the interrupt pin, 203 | False otherwise 204 | """ 205 | self.read_data() 206 | if self.registers[0x08] & 0x80 == 0x80: 207 | return True 208 | else: 209 | return False 210 | 211 | def set_byte(self, register, value): 212 | """Write a byte to a particular address on the sensor. 213 | 214 | This method should rarely be used directly. 215 | """ 216 | self.i2cbus.write_byte_data(self.address, register, value) 217 | 218 | def read_data(self): 219 | """ 220 | Read a block of data from the sensor and store it. 221 | 222 | This doesn't read exact registers because the library used by 223 | smbus doesn't support repeated I2C starts (required to read 224 | registers directly on the sensor) 225 | 226 | This method should rarely be called directly. 227 | """ 228 | self.registers = self.i2cbus.read_i2c_block_data(self.address, 0x00) 229 | -------------------------------------------------------------------------------- /RaspberryPi-AS3935/RPi_AS3935/__init__.py: -------------------------------------------------------------------------------- 1 | """A module to make communication between the Raspberry Pi and the 2 | AS3935 lightning sensor (over I2C) as easy as possible. It should be 3 | easily adaptable to make work on non-Raspberry Pi things too. 4 | 5 | .. moduleauthor:: Phil Fenstermacher 6 | 7 | """ 8 | 9 | from RPi_AS3935 import RPi_AS3935 10 | -------------------------------------------------------------------------------- /RaspberryPi-AS3935/demo.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from RPi_AS3935 import RPi_AS3935 3 | 4 | import RPi.GPIO as GPIO 5 | import time 6 | from datetime import datetime 7 | 8 | GPIO.setmode(GPIO.BCM) 9 | 10 | # Rev. 1 Raspberry Pis should leave bus set at 0, while rev. 2 Pis should set 11 | # bus equal to 1. The address should be changed to match the address of the 12 | # sensor. (Common implementations are in README.md) 13 | sensor = RPi_AS3935(address=0x03, bus=1) 14 | 15 | sensor.set_indoors(True) 16 | sensor.set_noise_floor(0) 17 | sensor.calibrate(tun_cap=0x0F) 18 | 19 | 20 | def handle_interrupt(channel): 21 | time.sleep(0.003) 22 | global sensor 23 | reason = sensor.get_interrupt() 24 | if reason == 0x01: 25 | print "Noise level too high - adjusting" 26 | sensor.raise_noise_floor() 27 | elif reason == 0x04: 28 | print "Disturber detected - masking" 29 | sensor.set_mask_disturber(True) 30 | elif reason == 0x08: 31 | now = datetime.now().strftime('%H:%M:%S - %Y/%m/%d') 32 | distance = sensor.get_distance() 33 | print "We sensed lightning!" 34 | print "It was " + str(distance) + "km away. (%s)" % now 35 | print "" 36 | 37 | pin = 25 38 | 39 | GPIO.setup(pin, GPIO.IN) 40 | GPIO.add_event_detect(pin, GPIO.RISING, callback=handle_interrupt) 41 | 42 | print "Waiting for lightning - or at least something that looks like it" 43 | 44 | while True: 45 | time.sleep(1.0) 46 | -------------------------------------------------------------------------------- /RaspberryPi-AS3935/requirements.txt: -------------------------------------------------------------------------------- 1 | -e . 2 | 3 | pytest 4 | coverage 5 | -------------------------------------------------------------------------------- /RaspberryPi-AS3935/requirements/base.txt: -------------------------------------------------------------------------------- 1 | RPi_AS3935 >= 0.0.8 2 | -------------------------------------------------------------------------------- /RaspberryPi-AS3935/requirements/flake8.txt: -------------------------------------------------------------------------------- 1 | -r base.txt 2 | flake8 3 | -------------------------------------------------------------------------------- /RaspberryPi-AS3935/requirements/pep8.txt: -------------------------------------------------------------------------------- 1 | -r base.txt 2 | pep8 3 | -------------------------------------------------------------------------------- /RaspberryPi-AS3935/setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | description-file = README.md 3 | -------------------------------------------------------------------------------- /RaspberryPi-AS3935/setup.py: -------------------------------------------------------------------------------- 1 | from distutils.core import setup 2 | 3 | setup( 4 | name='RPi_AS3935', 5 | version='0.0.9', 6 | description='Raspberry Pi <-I2C-> AS3935 lightning sensor communication library', 7 | author='Phil Fenstermacher', 8 | author_email='phillip.fenstermacher@gmail.com', 9 | url='https://github.com/pcfens/RaspberryPi-AS3935', 10 | packages=['RPi_AS3935'], 11 | keywords=['RaspberryPi', 'AS3935', 'lightning', 'weather'], 12 | license='Apache-2.0', 13 | classifiers=[ 14 | 'Development Status :: 4 - Beta', 15 | 'License :: OSI Approved :: Apache Software License', 16 | 'Topic :: Utilities', 17 | ], 18 | ) 19 | -------------------------------------------------------------------------------- /RaspberryPi-AS3935/tests/conftest.py: -------------------------------------------------------------------------------- 1 | from RPi_AS3935 import RPi_AS3935 2 | import pytest 3 | 4 | 5 | class RPi_AS3935Proxy(RPi_AS3935): 6 | def __init__(self, address, bus=0): 7 | self.address = address 8 | self.i2cbus = bus 9 | self.registers = [50, 2, 194, 32, 0, 0, 0, 63, 0, 173, 0, 37, 3, 1, 34, 131, 1, 31, 67, 2, 27, 99, 3, 24, 20, 5, 20, 157, 7, 17, 106, 11] 10 | 11 | def set_byte(self, register, value): 12 | self.registers[register] = value 13 | print value 14 | 15 | def read_data(self): 16 | self.registers = self.registers 17 | 18 | 19 | @pytest.fixture(scope='module') 20 | def as3935(): 21 | return RPi_AS3935Proxy(address=0x00, bus=0) 22 | -------------------------------------------------------------------------------- /RaspberryPi-AS3935/tests/test_disp_lco.py: -------------------------------------------------------------------------------- 1 | def test_set_disp_lco_true(as3935): 2 | as3935.set_disp_lco(True) 3 | assert (as3935.registers[0x08] & 0x80) == 0x80 4 | 5 | 6 | def test_get_disp_lco_true(as3935): 7 | disp_lco = as3935.get_disp_lco() 8 | assert disp_lco is True 9 | 10 | 11 | def test_set_disp_lco_false(as3935): 12 | as3935.set_disp_lco(False) 13 | assert (as3935.registers[0x08] & 0x80) == 0x00 14 | 15 | 16 | def test_get_disp_lco_false(as3935): 17 | disp_lco = as3935.get_disp_lco() 18 | assert disp_lco is False 19 | -------------------------------------------------------------------------------- /RaspberryPi-AS3935/tests/test_disturbers.py: -------------------------------------------------------------------------------- 1 | def test_set_mask_disturber_true(as3935): 2 | as3935.set_mask_disturber(True) 3 | assert (as3935.registers[0x03] & 0x20) == 0x20 4 | 5 | 6 | def test_get_mask_disturber_true(as3935): 7 | mask_disturber = as3935.get_mask_disturber() 8 | assert mask_disturber is True 9 | 10 | 11 | def test_set_mask_disturber_false(as3935): 12 | as3935.set_mask_disturber(False) 13 | assert (as3935.registers[0x03] & 0x20) == 0x00 14 | 15 | 16 | def test_get_mask_disturber_false(as3935): 17 | mask_disturber = as3935.get_mask_disturber() 18 | assert mask_disturber is False 19 | -------------------------------------------------------------------------------- /RaspberryPi-AS3935/tests/test_indoors.py: -------------------------------------------------------------------------------- 1 | def test_set_indoors_true(as3935): 2 | as3935.set_indoors(True) 3 | assert (as3935.registers[0x00] & 0x10) == 0x10 4 | 5 | 6 | def test_get_indoors_true(as3935): 7 | indoors = as3935.get_indoors() 8 | assert indoors is True 9 | 10 | 11 | def test_set_indoors_false(as3935): 12 | as3935.set_indoors(False) 13 | assert (as3935.registers[0x00] & 0x10) == 0x00 14 | 15 | 16 | def test_get_indoors_false(as3935): 17 | indoors = as3935.get_indoors() 18 | assert indoors is False 19 | -------------------------------------------------------------------------------- /RaspberryPi-AS3935/tests/test_min_strikes.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | 4 | def test_set_min_strikes_5(as3935): 5 | as3935.set_min_strikes(5) 6 | assert (as3935.registers[0x02] & 0x30) == 0x10 7 | 8 | 9 | def test_get_min_strikes_5(as3935): 10 | min_strikes = as3935.get_min_strikes() 11 | assert min_strikes == 5 12 | 13 | 14 | def test_set_invalid_min_strikes(as3935): 15 | with pytest.raises(Exception): 16 | as3935.set_min_strikes(2) 17 | 18 | 19 | def test_set_min_strikes_9(as3935): 20 | as3935.set_min_strikes(9) 21 | assert (as3935.registers[0x02] & 0x30) == 0x20 22 | 23 | 24 | def test_get_min_strikes_9(as3935): 25 | min_strikes = as3935.get_min_strikes() 26 | assert min_strikes == 9 27 | 28 | 29 | def test_set_min_strikes_16(as3935): 30 | as3935.set_min_strikes(16) 31 | assert (as3935.registers[0x02] & 0x30) == 0x30 32 | 33 | 34 | def test_get_min_strikes_16(as3935): 35 | min_strikes = as3935.get_min_strikes() 36 | assert min_strikes == 16 37 | 38 | 39 | def test_set_min_strikes_1(as3935): 40 | as3935.set_min_strikes(1) 41 | assert (as3935.registers[0x02] & 0x30) == 0x00 42 | 43 | 44 | def test_get_min_strikes_1(as3935): 45 | min_strikes = as3935.get_min_strikes() 46 | assert min_strikes == 1 47 | -------------------------------------------------------------------------------- /RaspberryPi-AS3935/tests/test_noise_floor.py: -------------------------------------------------------------------------------- 1 | def test_get_noise_floor(as3935): 2 | noise_floor = as3935.get_noise_floor() 3 | assert noise_floor == 0 4 | 5 | 6 | def test_set_noise_floor(as3935): 7 | as3935.set_noise_floor(2) 8 | assert (as3935.registers[0x01] & 0x70) == 0x20 9 | 10 | 11 | def test_raise_noise_floor(as3935): 12 | as3935.raise_noise_floor() 13 | assert (as3935.registers[0x01] & 0x70) == 0x30 14 | 15 | 16 | def test_lower_noise_floor(as3935): 17 | as3935.lower_noise_floor() 18 | assert (as3935.registers[0x01] & 0x70) == 0x20 19 | -------------------------------------------------------------------------------- /RaspberryPi-AS3935/tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = pep8,flake8,py27 3 | 4 | [flake8] 5 | exclude = *.egg*,.env,.git,.hg,.tox,_*,build*,dist*,venv* 6 | ignore = E261 7 | max-line-length = 150 8 | 9 | [pep8] 10 | count = 1 11 | exclude = *.egg*,.env,.git,.hg,.tox,_*,build*,dist*,venv* 12 | ignore = E261 13 | max-line-length = 150 14 | show-source = 1 15 | statistics = 1 16 | 17 | [testenv] 18 | deps = -rrequirements.txt 19 | commands = python -m coverage run --source RPi_AS3935 -m pytest 20 | python -m coverage report -m 21 | 22 | [testenv:pep8] 23 | basepython = python2.7 24 | deps = -rrequirements/pep8.txt 25 | commands = 26 | pep8 tests/ RPi_AS3935/ 27 | 28 | [testenv:flake8] 29 | basepython = python2.7 30 | deps = -rrequirements/flake8.txt 31 | commands = 32 | flake8 tests/ RPi_AS3935/ 33 | -------------------------------------------------------------------------------- /SDL_Pi_FRAM/README.md: -------------------------------------------------------------------------------- 1 | # 2 | # Raspberry Pi I2C FRAM Reading Library 3 | # SwitchDoc Labs 4 | # February 16, 2015 5 | # 6 | # SwitchDoc Labs www.switchdoc.com 7 | # Designed for WeatherPiArduino 8 | # 9 | 10 | 11 | -------------------------------------------------------------------------------- /SDL_Pi_FRAM/SDL_Pi_FRAM.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # SDL_Pi_FRAM.py Python Driver Code 4 | # SwitchDoc Labs 02/16/2014 5 | # V 1.2 6 | 7 | 8 | from datetime import datetime 9 | 10 | import time 11 | import smbus 12 | 13 | 14 | 15 | class SDL_Pi_FRAM(): 16 | 17 | 18 | ########################### 19 | # SDL_Pi_FRAM Code 20 | ########################### 21 | def __init__(self, twi=1, addr=0x50): 22 | self._bus = smbus.SMBus(twi) 23 | self._addr = addr 24 | 25 | 26 | 27 | def write8(self, address, data): 28 | #print "addr =0x%x address = 0x%x data = 0x%x " % (self._addr, address, data) 29 | self._bus.write_i2c_block_data(self._addr,address>>8,[address%256, data]) 30 | 31 | 32 | 33 | def read8(self, address): 34 | 35 | self._bus.write_i2c_block_data(self._addr,address>>8,[address%256]) 36 | returndata = self._bus.read_byte(self._addr) # this will read at the current address pointer, which we on the previous line 37 | #print "addr = 0x%x address = 0x%x %i returndata = 0x%x " % (self._addr, address, address, returndata) 38 | return returndata 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /SDL_Pi_FRAM/testSDL_Pi_FRAM.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Test SDL_Pi_FRAM 4 | # SwitchDoc Labs 5 | # For WeatherPiArduino 6 | # February 16, 2015 7 | # 8 | # 9 | 10 | # imports 11 | 12 | import sys 13 | import time 14 | import random 15 | import SDL_Pi_FRAM 16 | 17 | # Main Program 18 | 19 | print "" 20 | print "Test SDL_Pi_FRAM Version 1.0 - SwitchDoc Labs" 21 | print "" 22 | print "" 23 | print "Program Started at:"+ time.strftime("%Y-%m-%d %H:%M:%S") 24 | print "" 25 | 26 | fram = SDL_Pi_FRAM.SDL_Pi_FRAM(addr = 0x50) 27 | 28 | while True: 29 | print "----------------- " 30 | print "----------------- " 31 | print " Reading and Writing to FRAM " 32 | print "----------------- " 33 | print "writing first 10 addresses with random data" 34 | for x in range(0,10): 35 | value = random.randint(0,255) 36 | print "address = %i writing value=%i" % (x, value) 37 | fram.write8(x, value) 38 | print "----------------- " 39 | 40 | print "reading first 10 addresses" 41 | for x in range(0,10): 42 | print "address = %i value = %i" %(x, fram.read8(x)) 43 | print "----------------- " 44 | print "----------------- " 45 | 46 | time.sleep(10.0) 47 | 48 | 49 | -------------------------------------------------------------------------------- /conf.py: -------------------------------------------------------------------------------- 1 | 2 | # 3 | # 4 | # configuration file - contains customization for exact system 5 | # JCS 11/8/2013 6 | # 7 | 8 | mailUser = "yourusename" 9 | mailPassword = "yourmailpassword" 10 | 11 | notifyAddress ="you@example.com" 12 | 13 | fromAddress = "yourfromaddress@example.com" 14 | 15 | -------------------------------------------------------------------------------- /doAllGraphs.py: -------------------------------------------------------------------------------- 1 | # 2 | # calculate all graphs 3 | # 4 | # SwitchDoc Labs March 30, 2015 5 | 6 | import sys 7 | sys.path.append('/home/pi/WeatherPiSolarPoweredWeather/graphs') 8 | 9 | 10 | 11 | 12 | import TemperatureHumidityGraph 13 | import PowerCurrentGraph 14 | import PowerVoltageGraph 15 | import BarometerLightningGraph 16 | 17 | def doAllGraphs(): 18 | 19 | BarometerLightningGraph.BarometerLightningGraph('test', 10, 0) 20 | TemperatureHumidityGraph.TemperatureHumidityGraph('test', 10, 0) 21 | PowerCurrentGraph.PowerCurrentGraph('test', 10, 0) 22 | PowerVoltageGraph.PowerVoltageGraph('test', 10, 0) 23 | 24 | -------------------------------------------------------------------------------- /graphs/BarometerLightningGraph.py: -------------------------------------------------------------------------------- 1 | # BarometerLightningGraph 2 | # filename: BarometerLightningGraph.py 3 | # Version 1.1 03/30/15 4 | # 5 | # contains graphing code 6 | # 7 | # 8 | 9 | import sys 10 | import time 11 | import RPi.GPIO as GPIO 12 | 13 | import gc 14 | import datetime 15 | 16 | import matplotlib 17 | # Force matplotlib to not use any Xwindows backend. 18 | matplotlib.use('Agg') 19 | 20 | from matplotlib import pyplot 21 | from matplotlib import dates 22 | 23 | import pylab 24 | 25 | import MySQLdb as mdb 26 | 27 | DATABASEPASSWORD = "rmysqlpassword" 28 | 29 | def BarometerLightningGraph(source,days,delay): 30 | 31 | 32 | 33 | print("BarometerLightningGraph source:%s days:%s" % (source,days)) 34 | print("sleeping seconds:", delay) 35 | time.sleep(delay) 36 | print("BarometerLightningGraph running now") 37 | 38 | # blink GPIO LED when it's run 39 | GPIO.setup(18, GPIO.OUT) 40 | GPIO.output(18, True) 41 | time.sleep(0.2) 42 | GPIO.output(18, False) 43 | 44 | 45 | # now we have get the data, stuff it in the graph 46 | 47 | try: 48 | print("trying database") 49 | db = mdb.connect('localhost', 'root', DATABASEPASSWORD, 'WeatherPi'); 50 | 51 | cursor = db.cursor() 52 | 53 | query = "SELECT TimeStamp, bmp180SeaLevel, as3935LastInterrupt, as3935LastDistance FROM WeatherData where now() - interval %i hour < TimeStamp" % (days*24) 54 | print "query=", query 55 | cursor.execute(query) 56 | result = cursor.fetchall() 57 | 58 | t = [] 59 | s = [] 60 | u = [] 61 | v = [] 62 | 63 | for record in result: 64 | t.append(record[0]) 65 | s.append(record[1]) 66 | u.append(record[2]) 67 | v.append(record[3]) 68 | 69 | 70 | fig = pyplot.figure() 71 | 72 | print ("count of t=",len(t)) 73 | if (len(t) == 0): 74 | return 75 | #dts = map(datetime.datetime.fromtimestamp, s) 76 | #fds = dates.date2num(t) # converted 77 | # matplotlib date format object 78 | hfmt = dates.DateFormatter('%m/%d-%H') 79 | 80 | 81 | ax = fig.add_subplot(111) 82 | for i in range(len(s)): 83 | s[i] = s[i] * 10 84 | 85 | #ax.vlines(fds, -200.0, 1000.0,colors='w') 86 | ax.xaxis.set_major_locator(dates.HourLocator(interval=6)) 87 | ax.xaxis.set_major_formatter(hfmt) 88 | pylab.xticks(rotation='vertical') 89 | 90 | pyplot.subplots_adjust(bottom=.3) 91 | pylab.plot(t, s, color='b',label="Barometric Pressure (mb) ",linestyle="-",marker=".") 92 | pylab.xlabel("Hours") 93 | pylab.ylabel("millibars") 94 | pylab.legend(loc='upper left') 95 | pylab.axis([min(t), max(t), 900, 1100]) 96 | ax2 = pylab.twinx() 97 | pylab.ylabel("Last Interrupt / Distance ") 98 | 99 | # scale array 100 | 101 | for i in range(len(v)): 102 | v[i] = v[i] * 10 103 | for i in range(len(u)): 104 | u[i] = u[i] * 10 105 | 106 | 107 | pylab.plot(t, u, color='y',label="as3935 Last Interrupt",linestyle="-",marker=".") 108 | pylab.plot(t, v, color='r',label="as3935 Last Distance",linestyle="-",marker=".") 109 | pylab.axis([min(t), max(t), 0, max(u)]) 110 | pylab.legend(loc='lower left') 111 | pylab.figtext(.5, .05, ("Barometer and Lightning Statistics Last %i Days" % days),fontsize=18,ha='center') 112 | 113 | #pylab.grid(True) 114 | 115 | pyplot.setp( ax.xaxis.get_majorticklabels(), rotation=70) 116 | ax.xaxis.set_major_formatter(dates.DateFormatter('%m/%d-%H')) 117 | pyplot.show() 118 | pyplot.savefig("/home/pi/WeatherPiRasPiConnectServer/static/BarometerLightningGraph.png") 119 | 120 | except mdb.Error, e: 121 | 122 | print "Error %d: %s" % (e.args[0],e.args[1]) 123 | 124 | finally: 125 | 126 | cursor.close() 127 | db.close() 128 | 129 | del cursor 130 | del db 131 | 132 | fig.clf() 133 | pyplot.close() 134 | pylab.close() 135 | del t, s, u, v 136 | gc.collect() 137 | print("BarometerLightningGraph finished now") 138 | -------------------------------------------------------------------------------- /graphs/PowerCurrentGraph.py: -------------------------------------------------------------------------------- 1 | # PowerCurrentGraph 2 | # filename: PowerCurrentGraph.py 3 | # Version 1.3 09/12/13 4 | # Version 1.4 03/30/15 5 | # 6 | # contains event routines for data collection 7 | # 8 | # 9 | 10 | import sys 11 | import time 12 | import RPi.GPIO as GPIO 13 | 14 | import gc 15 | import datetime 16 | 17 | import matplotlib 18 | # Force matplotlib to not use any Xwindows backend. 19 | matplotlib.use('Agg') 20 | 21 | from matplotlib import pyplot 22 | from matplotlib import dates 23 | 24 | import pylab 25 | 26 | import MySQLdb as mdb 27 | 28 | DATABASEPASSWORD = "rmysqlpassword" 29 | 30 | def PowerCurrentGraph(source,days,delay): 31 | 32 | 33 | 34 | print("PowerCurrentGraph source:%s days:%s delay:%i" % (source,days,delay)) 35 | print("sleeping :",delay) 36 | time.sleep(delay) 37 | print("PowerCurrentGraph running now") 38 | 39 | # blink GPIO LED when it's run 40 | GPIO.setup(18, GPIO.OUT) 41 | GPIO.output(18, True) 42 | time.sleep(0.2) 43 | GPIO.output(18, False) 44 | 45 | # now we have get the data, stuff it in the graph 46 | 47 | try: 48 | print("trying database") 49 | db = mdb.connect('localhost', 'root', DATABASEPASSWORD, 'WeatherPi'); 50 | 51 | cursor = db.cursor() 52 | 53 | query = "SELECT TimeStamp, solarCurrent, batteryCurrent, loadCurrent FROM PowerSystem where now() - interval %i hour < TimeStamp" % (days*24) 54 | cursor.execute(query) 55 | result = cursor.fetchall() 56 | 57 | t = [] 58 | s = [] 59 | u = [] 60 | v = [] 61 | #x = [] 62 | 63 | for record in result: 64 | t.append(record[0]) 65 | s.append(record[1]) 66 | u.append(record[2]) 67 | v.append(record[3]) 68 | #x.append(record[4]) 69 | fig = pyplot.figure() 70 | 71 | 72 | print ("count of t=",len(t)) 73 | #print (t) 74 | if (len(t) == 0): 75 | return 76 | #dts = map(datetime.datetime.fromtimestamp, t) 77 | #print dts 78 | #fds = dates.date2num(t) # converted 79 | # matplotlib date format object 80 | hfmt = dates.DateFormatter('%m/%d-%H') 81 | 82 | fig.set_facecolor('white') 83 | ax = fig.add_subplot(111,axisbg = 'white') 84 | #ax.vlines(fds, -200.0, 1000.0,colors='w') 85 | 86 | ax.xaxis.set_major_locator(dates.HourLocator(interval=6)) 87 | ax.xaxis.set_major_formatter(hfmt) 88 | ax.set_ylim(bottom = -200.0) 89 | pyplot.xticks(rotation='vertical') 90 | pyplot.subplots_adjust(bottom=.3) 91 | pylab.plot(t, s, color='b',label="Solar",linestyle="-",marker=".") 92 | pylab.plot(t, u, color='r',label="Battery",linestyle="-",marker=".") 93 | pylab.plot(t, v, color='g',label="Load",linestyle="-",marker=".") 94 | #pylab.plot(t, x, color='m',label="Power Eff",linestyle="-",marker=".") 95 | pylab.xlabel("Hours") 96 | pylab.ylabel("Current ma") 97 | pylab.legend(loc='upper left') 98 | 99 | if (max(u) > max(s)): 100 | myMax = max(u)+ 100.0 101 | else: 102 | myMax = max(s) 103 | pylab.axis([min(t), max(t), min(u), myMax]) 104 | pylab.figtext(.5, .05, ("WeatherPi Power Current Last %i Days" % days),fontsize=18,ha='center') 105 | pyplot.setp( ax.xaxis.get_majorticklabels(), rotation=70) 106 | 107 | pylab.grid(True) 108 | 109 | pyplot.show() 110 | pyplot.savefig("/home/pi/WeatherPiRasPiConnectServer/static/PowerCurrentGraph.png",facecolor=fig.get_facecolor()) 111 | 112 | 113 | except mdb.Error, e: 114 | 115 | print "Error %d: %s" % (e.args[0],e.args[1]) 116 | 117 | finally: 118 | 119 | cursor.close() 120 | db.close() 121 | 122 | del cursor 123 | del db 124 | 125 | fig.clf() 126 | pyplot.close() 127 | pylab.close() 128 | del t, s, u, v 129 | gc.collect() 130 | print("PowerCurrentGrapGraph finished now") 131 | -------------------------------------------------------------------------------- /graphs/PowerVoltageGraph.py: -------------------------------------------------------------------------------- 1 | # PowerVoltageGraph 2 | # filename: VoltageGraph.py 3 | # Version 1.3 09/12/13 4 | # Version 1.4 03/30/15 5 | # 6 | # contains event routines for data collection 7 | # 8 | # 9 | 10 | import sys 11 | import time 12 | import RPi.GPIO as GPIO 13 | 14 | import gc 15 | import datetime 16 | 17 | import matplotlib 18 | # Force matplotlib to not use any Xwindows backend. 19 | matplotlib.use('Agg') 20 | 21 | from matplotlib import pyplot 22 | from matplotlib import dates 23 | 24 | import pylab 25 | 26 | import MySQLdb as mdb 27 | 28 | DATABASEPASSWORD = "rmysqlpassword" 29 | 30 | def PowerVoltageGraph(source,days,delay): 31 | 32 | 33 | 34 | print("PowerVoltageGraph source:%s days:%s delay:%i" % (source,days,delay)) 35 | print("sleeping :",delay) 36 | time.sleep(delay) 37 | print("PowerVoltageGraph running now") 38 | 39 | # blink GPIO LED when it's run 40 | GPIO.setup(18, GPIO.OUT) 41 | GPIO.output(18, True) 42 | time.sleep(0.2) 43 | GPIO.output(18, False) 44 | 45 | # now we have get the data, stuff it in the graph 46 | 47 | try: 48 | print("trying database") 49 | db = mdb.connect('localhost', 'root', DATABASEPASSWORD, 'WeatherPi'); 50 | 51 | cursor = db.cursor() 52 | 53 | query = "SELECT TimeStamp, solarVoltage, batteryVoltage, loadVoltage FROM PowerSystem where now() - interval %i hour < TimeStamp" % (days*24) 54 | cursor.execute(query) 55 | result = cursor.fetchall() 56 | 57 | t = [] 58 | s = [] 59 | u = [] 60 | v = [] 61 | #x = [] 62 | 63 | for record in result: 64 | t.append(record[0]) 65 | s.append(record[1]) 66 | u.append(record[2]) 67 | v.append(record[3]) 68 | #x.append(record[4]) 69 | fig = pyplot.figure() 70 | 71 | print ("count of t=",len(t)) 72 | #print (t) 73 | if (len(t) == 0): 74 | return 75 | #dts = map(datetime.datetime.fromtimestamp, t) 76 | #print dts 77 | #fds = dates.date2num(t) # converted 78 | # matplotlib date format object 79 | hfmt = dates.DateFormatter('%m/%d-%H') 80 | 81 | fig = pyplot.figure() 82 | fig.set_facecolor('white') 83 | ax = fig.add_subplot(111,axisbg = 'white') 84 | #ax.vlines(fds, -200.0, 1000.0,colors='w') 85 | 86 | ax.xaxis.set_major_locator(dates.HourLocator(interval=6)) 87 | ax.xaxis.set_major_formatter(hfmt) 88 | ax.set_ylim(bottom = -200.0) 89 | pyplot.xticks(rotation='vertical') 90 | pyplot.subplots_adjust(bottom=.3) 91 | pylab.plot(t, s, color='b',label="Solar",linestyle="-",marker=".") 92 | pylab.plot(t, u, color='r',label="Battery",linestyle="-",marker=".") 93 | pylab.plot(t, v, color='g',label="Load",linestyle="-",marker=".") 94 | #pylab.plot(t, x, color='m',label="Power Eff",linestyle="-",marker=".") 95 | pylab.xlabel("Hours") 96 | pylab.ylabel("Voltage V") 97 | pylab.legend(loc='upper left') 98 | 99 | if (max(u) > max(s)): 100 | myMax = max(u)+ 100.0 101 | else: 102 | myMax = max(s) 103 | pylab.axis([min(t), max(t), min(u), myMax]) 104 | pylab.figtext(.5, .05, ("WeatherPi Power Voltage Last %i Days" % days),fontsize=18,ha='center') 105 | pyplot.setp( ax.xaxis.get_majorticklabels(), rotation=70) 106 | 107 | pylab.grid(True) 108 | 109 | pyplot.show() 110 | pyplot.savefig("/home/pi/WeatherPiRasPiConnectServer/static/PowerVoltageGraph.png",facecolor=fig.get_facecolor()) 111 | 112 | 113 | except mdb.Error, e: 114 | 115 | print "Error %d: %s" % (e.args[0],e.args[1]) 116 | 117 | finally: 118 | 119 | cursor.close() 120 | db.close() 121 | 122 | del cursor 123 | del db 124 | 125 | fig.clf() 126 | pyplot.close() 127 | pylab.close() 128 | del t, s, u, v 129 | gc.collect() 130 | print("PowerVoltageGraph finished now") 131 | -------------------------------------------------------------------------------- /graphs/TemperatureHumidityGraph.py: -------------------------------------------------------------------------------- 1 | # TemperatureHumidityGraph 2 | # filename:TemperatureHumidityGraph.py 3 | # Version 1.1 03/30/15 4 | # 5 | # contains event routines for data collection 6 | # 7 | # 8 | 9 | import sys 10 | import time 11 | import RPi.GPIO as GPIO 12 | 13 | import gc 14 | import datetime 15 | 16 | import matplotlib 17 | # Force matplotlib to not use any Xwindows backend. 18 | matplotlib.use('Agg') 19 | 20 | from matplotlib import pyplot 21 | from matplotlib import dates 22 | 23 | import pylab 24 | 25 | import MySQLdb as mdb 26 | 27 | 28 | DATABASEPASSWORD = "rmysqlpassword" 29 | 30 | def TemperatureHumidityGraph(source,days,delay): 31 | 32 | 33 | 34 | print("TemperatureHumidityGraph source:%s days:%s" % (source,days)) 35 | print("sleeping seconds:", delay) 36 | time.sleep(delay) 37 | print("TemperatureHumidityGraph running now") 38 | 39 | 40 | # blink GPIO LED when it's run 41 | GPIO.setup(18, GPIO.OUT) 42 | GPIO.output(18, True) 43 | time.sleep(0.2) 44 | GPIO.output(18, False) 45 | 46 | # now we have get the data, stuff it in the graph 47 | 48 | try: 49 | print("trying database") 50 | db = mdb.connect('localhost', 'root', DATABASEPASSWORD, 'WeatherPi'); 51 | 52 | cursor = db.cursor() 53 | 54 | query = "SELECT TimeStamp, bmp180Temperature, outsideTemperature, outsideHumidity, insideHumidity FROM WeatherData where now() - interval %i hour < TimeStamp" % (days*24) 55 | 56 | print "query=", query 57 | cursor.execute(query) 58 | result = cursor.fetchall() 59 | 60 | t = [] 61 | u = [] 62 | v = [] 63 | x = [] 64 | z = [] 65 | 66 | fig = pyplot.figure() 67 | 68 | 69 | 70 | for record in result: 71 | t.append(record[0]) 72 | u.append(record[1]) 73 | v.append(record[2]) 74 | x.append(record[3]) 75 | z.append(record[4]) 76 | 77 | print ("count of t=",len(t)) 78 | if (len(t) == 0): 79 | return 80 | 81 | #dts = map(datetime.datetime.fromtimestamp, s) 82 | #fds = dates.date2num(dts) # converted 83 | # matplotlib date format object 84 | hfmt = dates.DateFormatter('%m/%d-%H') 85 | 86 | 87 | ax = fig.add_subplot(111) 88 | ax.xaxis.set_major_locator(dates.HourLocator(interval=6)) 89 | ax.xaxis.set_major_formatter(hfmt) 90 | pylab.xticks(rotation='vertical') 91 | 92 | pyplot.subplots_adjust(bottom=.3) 93 | pylab.plot(t, v, color='g',label="Outside Temp (C)",linestyle="-",marker=".") 94 | pylab.plot(t, u, color='r',label="Inside Temp (C)",linestyle="-",marker=".") 95 | pylab.xlabel("Hours") 96 | pylab.ylabel("degrees C") 97 | pylab.legend(loc='upper left') 98 | pylab.axis([min(t), max(t), 0, 40]) 99 | ax2 = pylab.twinx() 100 | pylab.ylabel("% ") 101 | pylab.plot(t, x, color='y',label="Outside Hum %",linestyle="-",marker=".") 102 | pylab.plot(t, z, color='b',label="Inside Hum %",linestyle="-",marker=".") 103 | pylab.axis([min(t), max(t), 0, 100]) 104 | pylab.legend(loc='lower left') 105 | pylab.figtext(.5, .05, ("Environmental Statistics Last %i Days" % days),fontsize=18,ha='center') 106 | 107 | #pylab.grid(True) 108 | 109 | pyplot.setp( ax.xaxis.get_majorticklabels(), rotation=70) 110 | ax.xaxis.set_major_formatter(dates.DateFormatter('%m/%d-%H')) 111 | pyplot.show() 112 | pyplot.savefig("/home/pi/WeatherPiRasPiConnectServer/static/TemperatureHumidityGraph.png") 113 | 114 | except mdb.Error, e: 115 | 116 | print "Error %d: %s" % (e.args[0],e.args[1]) 117 | 118 | finally: 119 | 120 | cursor.close() 121 | db.close() 122 | 123 | del cursor 124 | del db 125 | 126 | fig.clf() 127 | pyplot.close() 128 | pylab.close() 129 | del t, u, v, x 130 | gc.collect() 131 | print("TemperatureHumidityGraph finished now") 132 | -------------------------------------------------------------------------------- /graphs/testTemp.py: -------------------------------------------------------------------------------- 1 | 2 | # 3 | # calculate all graphs 4 | # 5 | # SwitchDoc Labs March 30, 2015 6 | 7 | import sys 8 | import RPi.GPIO as GPIO 9 | 10 | 11 | GPIO.setmode(GPIO.BCM) 12 | 13 | 14 | import TemperatureHumidityGraph 15 | 16 | 17 | TemperatureHumidityGraph.TemperatureHumidityGraph('test', 10, 0) 18 | 19 | -------------------------------------------------------------------------------- /htu21dflib/.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | *.o 3 | *.ko 4 | *.obj 5 | *.elf 6 | 7 | # Libraries 8 | *.lib 9 | *.a 10 | 11 | # Shared objects (inc. Windows DLLs) 12 | *.dll 13 | *.so 14 | *.so.* 15 | *.dylib 16 | 17 | # Executables 18 | *.exe 19 | *.out 20 | *.app 21 | *.i*86 22 | *.x86_64 23 | *.hex 24 | 25 | *~ 26 | *.swp 27 | tags 28 | -------------------------------------------------------------------------------- /htu21dflib/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 bbx10node@gmail.com 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /htu21dflib/README.md: -------------------------------------------------------------------------------- 1 | htu21dflib 2 | ========== 3 | 4 | htu21df temperature and humidity sensor support for Raspberry Pi. 5 | 6 | Adafruit HTU21D-F breakout board details. 7 | 8 | http://www.adafruit.com/products/1899 9 | 10 | Adafruit guide to configuring the Pi for i2c. 11 | 12 | https://learn.adafruit.com/using-the-bmp085-with-raspberry-pi/configuring-the-pi-for-i2c 13 | 14 | ## Build it 15 | 16 | ./build.sh 17 | 18 | ## Run test program 19 | 20 | ./htu21dflib 21 | 22 | Sample output 23 | 24 | ``` 25 | 29.1 52.3 26 | 29.1 52.3 27 | 29.1 52.3 28 | 29.1 52.3 29 | ``` 30 | 31 | ## Dweet your data 32 | 33 | ./dweetio 34 | 35 | Login into http://freeboard.io to access your dweet data. Create a real-time 36 | instrumentation panel with temperature and humidity guages without programming. 37 | 38 | -------------------------------------------------------------------------------- /htu21dflib/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Build the code with a standalone test program. 3 | # htu21dflib -- A small test program using the library. Use it verify 4 | # the code and sensor are working. 5 | # htu21dflib.o -- The library code which can be linked into a larger program. 6 | # dweetio -- Demo program that dweets the data to http://dweet.io. 7 | # Create a real-time instrumentation panel with this data 8 | # by connecting to http://freeboard.io 9 | # 10 | gcc -O2 -Wall -o htu21dflib -DHTU21DFTEST htu21dflib.c 11 | gcc -O2 -Wall -c htu21dflib.c 12 | gcc -O2 -Wall htu21dflib.o -o dweetio dweetio.c 13 | gcc -O2 -Wall htu21dflib.o -o mqtt mqtt.c 14 | -------------------------------------------------------------------------------- /htu21dflib/dweetio: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/switchdoclabs/WeatherPi/87027860195698521da8bd33cc0cd81115da0d59/htu21dflib/dweetio -------------------------------------------------------------------------------- /htu21dflib/dweetio.c: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2014 bbx10node@gmail.com 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include "htu21dflib.h" 31 | 32 | static const char I2CDEV[] = "/dev/i2c-1"; // raspberry pi 33 | static const uint8_t I2CADDR = 0x40; // htu21df i2c address 34 | 35 | // dweet thing_name. Be sure to make up your own thing name! If you do not, 36 | // your data will be mixed with everyone else's data using the same thing 37 | // name. 38 | static const char THING_NAME[] = "flippant-boot"; 39 | 40 | int main(int argc, char *argv[]) 41 | { 42 | int rc; // return code 43 | int i2cfd; // i2c file descriptor 44 | float temperature, humidity; 45 | char curlstr[256]; 46 | 47 | i2cfd = i2c_open(I2CDEV); 48 | if (i2cfd < 0) { 49 | printf("i2c_open failed %d\n", i2cfd); 50 | return -1; 51 | } 52 | 53 | rc = htu21df_init(i2cfd, I2CADDR); 54 | if (rc < 0) { 55 | printf("i2c_init failed %d\n", rc); 56 | return -2; 57 | } 58 | 59 | while (1) { 60 | rc = htu21df_read_temperature(i2cfd, &temperature); 61 | if (rc < 0) { 62 | printf("i2c_read_temperature failed %d\n", rc); 63 | return -3; 64 | } 65 | 66 | rc = htu21df_read_humidity(i2cfd, &humidity); 67 | if (rc < 0) { 68 | printf("i2c_read_humidity failed %d\n", rc); 69 | return -4; 70 | } 71 | // Format the command to dweet the data 72 | rc = snprintf(curlstr, sizeof(curlstr), 73 | "curl \"https://dweet.io/dweet/for/%s?temperature=%.1f&humidity=%.1f\"", 74 | THING_NAME, temperature, humidity); 75 | if (rc < 0) { 76 | printf("snprintf failed %d\n", rc); 77 | } 78 | // Run the command to dweet the data 79 | rc = system(curlstr); 80 | if (rc != 0) { 81 | printf("system failed %d\n", rc); 82 | } 83 | // Wait 10 seconds 84 | sleep(10); 85 | } 86 | 87 | rc = i2c_close(i2cfd); 88 | if (rc < 0) { 89 | printf("i2c_close failed %d\n", rc); 90 | return -5; 91 | } 92 | 93 | return 0; 94 | } 95 | -------------------------------------------------------------------------------- /htu21dflib/htu21dflib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/switchdoclabs/WeatherPi/87027860195698521da8bd33cc0cd81115da0d59/htu21dflib/htu21dflib -------------------------------------------------------------------------------- /htu21dflib/htu21dflib.c: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2014 bbx10node@gmail.com 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include "htu21dflib.h" 37 | 38 | static const int MAX_TEMP_CONVERSION = 50; // milliseconds 39 | static const int MAX_HUMI_CONVERSION = 16; // ms 40 | static const int MAX_RESET_DELAY = 15; // ms 41 | 42 | static uint8_t HTU21DF_READTEMP_NH = 0xF3; // NH = no hold 43 | static uint8_t HTU21DF_READHUMI_NH = 0xF5; 44 | static uint8_t HTU21DF_WRITEREG = 0xE6; 45 | static uint8_t HTU21DF_READREG = 0xE7; 46 | static uint8_t HTU21DF_RESET = 0xFE; 47 | 48 | #define sleepms(ms) usleep((ms)*1000) 49 | 50 | static uint8_t I2Caddr; 51 | 52 | static int calc_crc8(const uint8_t *buf, int len); 53 | 54 | int i2c_open(const char *i2cdevname_caller) 55 | { 56 | int i2cfd; // i2c file descriptor 57 | FILE *boardrev; // get raspberry pi board revision 58 | const char *i2cdevname; 59 | 60 | i2cdevname = i2cdevname_caller; 61 | boardrev = fopen("/sys/module/bcm2708/parameters/boardrev", "r"); 62 | if (boardrev) { 63 | char aLine[80]; 64 | if (fgets(aLine, sizeof(aLine), boardrev)) { 65 | long board_revision; 66 | // Older board revisions use i2c-0, newer use i2c-1 67 | board_revision = strtol(aLine, NULL, 10); 68 | if ((board_revision == 2) || (board_revision == 3)) { 69 | i2cdevname = "/dev/i2c-0"; 70 | } 71 | else { 72 | i2cdevname = "/dev/i2c-1"; 73 | } 74 | } 75 | fclose(boardrev); 76 | } 77 | 78 | if ((i2cdevname == NULL) || (*i2cdevname == '\0')) return -1; 79 | 80 | if ((i2cfd = open(i2cdevname, O_RDWR)) < 0) { 81 | printf("%s:Failed to open the i2c bus %s %d/%d\n",i2cdevname, __func__, i2cfd, 82 | errno); 83 | return i2cfd; 84 | } 85 | return i2cfd; 86 | } 87 | 88 | int i2c_close(int i2cfd) 89 | { 90 | return close(i2cfd); 91 | } 92 | 93 | int htu21df_init(int i2cfd, uint8_t i2caddr) 94 | { 95 | uint8_t buf[32]; // i2c messages 96 | int rc; // return code 97 | struct i2c_rdwr_ioctl_data msgbuf; 98 | struct i2c_msg reset[1] = { 99 | {i2caddr, 0, 1, &HTU21DF_RESET}, 100 | }; 101 | struct i2c_msg read_user_reg[2] = { 102 | {i2caddr, 0, 1, &HTU21DF_READREG}, 103 | {i2caddr, I2C_M_RD, 1, buf} 104 | }; 105 | 106 | I2Caddr = i2caddr; 107 | msgbuf.nmsgs = 1; 108 | msgbuf.msgs = reset; 109 | rc = ioctl(i2cfd, I2C_RDWR, &msgbuf); 110 | if (rc < 0) { 111 | printf("%s:htu21df I2C_RDWR failed %d/%d\n", __func__, rc, errno); 112 | return rc; 113 | } 114 | sleepms(MAX_RESET_DELAY); 115 | 116 | msgbuf.nmsgs = 2; 117 | msgbuf.msgs = read_user_reg; 118 | rc = ioctl(i2cfd, I2C_RDWR, &msgbuf); 119 | if (rc < 0) { 120 | printf("%s:htu21df I2C_RDWR failed %d/%d\n", __func__, rc, errno); 121 | return rc; 122 | } 123 | 124 | if (buf[0] != 0x02) { 125 | printf("%s:htu21df did not reset\n", __func__); 126 | return -1; 127 | } 128 | return 0; 129 | } 130 | 131 | int htu21df_read_temperature(int i2cfd, float *temperature) 132 | { 133 | uint8_t buf[32]; // i2c messages 134 | int rc; // return code 135 | uint16_t rawtemp; // raw temperature reading 136 | struct i2c_rdwr_ioctl_data msgbuf; 137 | struct i2c_msg read_temp[2] = { 138 | {I2Caddr, 0, 1, &HTU21DF_READTEMP_NH}, 139 | {I2Caddr, I2C_M_RD, 3, buf} 140 | }; 141 | struct i2c_msg read_temp3[1] = { 142 | {I2Caddr, I2C_M_RD, 3, buf} 143 | }; 144 | 145 | msgbuf.nmsgs = 2; 146 | msgbuf.msgs = read_temp; 147 | rc = ioctl(i2cfd, I2C_RDWR, &msgbuf); 148 | if (rc < 0) { 149 | //printf("I2C_RDWR %d/%d\n", rc, errno); 150 | sleepms(MAX_TEMP_CONVERSION); 151 | msgbuf.nmsgs = 1; 152 | msgbuf.msgs = read_temp3; 153 | rc = ioctl(i2cfd, I2C_RDWR, &msgbuf); 154 | if (rc < 0) { 155 | printf("%s:I2C_RDWR %d/%d\n", __func__, rc, errno); 156 | return rc; 157 | } 158 | } 159 | //printf("READTEMP = 0x%x 0x%x 0x%x\n", buf[0], buf[1], buf[2]); 160 | if (calc_crc8(buf, 3) != 0) { 161 | printf("%s:Bad CRC\n", __func__); 162 | return -1; 163 | } 164 | // Remove low 2 bits because they are status 165 | rawtemp = ((buf[0] << 8) | buf[1]) & 0xFFFC; 166 | //printf("rawtemp %x\n", rawtemp); 167 | *temperature = ((rawtemp / 65536.0) * 175.72) - 46.85; 168 | return 0; 169 | } 170 | 171 | int htu21df_read_humidity(int i2cfd, float *humidity) 172 | { 173 | uint8_t buf[32]; 174 | uint16_t rawhumi; // raw humidity 175 | int rc; // return code 176 | struct i2c_rdwr_ioctl_data msgbuf; 177 | struct i2c_msg read_humi[2] = { 178 | {I2Caddr, 0, 1, &HTU21DF_READHUMI_NH}, 179 | {I2Caddr, I2C_M_RD, 3, buf} 180 | }; 181 | struct i2c_msg read_temp3[1] = { 182 | {I2Caddr, I2C_M_RD, 3, buf} 183 | }; 184 | 185 | msgbuf.nmsgs = 2; 186 | msgbuf.msgs = read_humi; 187 | rc = ioctl(i2cfd, I2C_RDWR, &msgbuf); 188 | if (rc < 0) { 189 | //printf("I2C_RDWR %d/%d\n", rc, errno); 190 | sleepms(MAX_HUMI_CONVERSION); 191 | msgbuf.nmsgs = 1; 192 | msgbuf.msgs = read_temp3; 193 | rc = ioctl(i2cfd, I2C_RDWR, &msgbuf); 194 | if (rc < 0) { 195 | printf("%s:I2C_RDWR %d/%d\n", __func__, rc, errno); 196 | return rc; 197 | } 198 | } 199 | //printf("READHUM= 0x%x 0x%x 0x%x\n", buf[0], buf[1], buf[2]); 200 | if (calc_crc8(buf, 3) != 0) { 201 | printf("%s:Bad CRC\n", __func__); 202 | return -1; 203 | } 204 | // Remove low 2 bits because they are status 205 | rawhumi = ((buf[0] << 8) | buf[1]) & 0xFFFC; 206 | //printf("rawhumi %x\n", rawhumi); 207 | *humidity = ((rawhumi / 65536.0) * 125.0) - 6.0; 208 | return 0; 209 | } 210 | 211 | // buf = 3 bytes from the HTU21DF for temperature or humidity 212 | // 2 data bytes and 1 crc8 byte 213 | // len = number of bytes in buf but it must be 3. 214 | // return value < 0 error 215 | // return value = 0 CRC good 216 | // return value > 0 CRC bad 217 | static int calc_crc8(const uint8_t *buf, int len) 218 | { 219 | uint32_t dataandcrc; 220 | // Generator polynomial: x**8 + x**5 + x**4 + 1 = 1001 1000 1 221 | const uint32_t poly = 0x98800000; 222 | int i; 223 | 224 | if (len != 3) return -1; 225 | if (buf == NULL) return -1; 226 | 227 | // Justify the data on the MSB side. Note the poly is also 228 | // justified the same way. 229 | dataandcrc = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8); 230 | for (i = 0; i < 24; i++) { 231 | if (dataandcrc & 0x80000000UL) 232 | dataandcrc ^= poly; 233 | dataandcrc <<= 1; 234 | } 235 | return (dataandcrc != 0); 236 | } 237 | 238 | #ifdef HTU21DFTEST 239 | 240 | static const char I2CDEV[] = "/dev/i2c-1"; // raspberry pi 241 | static const uint8_t I2CADDR = 0x40; // htu21df i2c address 242 | 243 | int main(int argc, char *argv[]) 244 | { 245 | int rc; // return code 246 | int i2cfd; // i2c file descriptor 247 | int i; 248 | float temperature, humidity; 249 | 250 | i2cfd = i2c_open(I2CDEV); 251 | if (i2cfd < 0) { 252 | printf("i2c_open failed %d\n", i2cfd); 253 | return -1; 254 | } 255 | 256 | rc = htu21df_init(i2cfd, I2CADDR); 257 | if (rc < 0) { 258 | printf("i2c_init failed %d\n", rc); 259 | return -2; 260 | } 261 | 262 | for (i = 0; i < 1; i++) { 263 | rc = htu21df_read_temperature(i2cfd, &temperature); 264 | if (rc < 0) { 265 | printf("i2c_read_temperature failed %d\n", rc); 266 | return -3; 267 | } 268 | 269 | rc = htu21df_read_humidity(i2cfd, &humidity); 270 | if (rc < 0) { 271 | printf("i2c_read_humidity failed %d\n", rc); 272 | return -4; 273 | } 274 | 275 | printf("%.1f %.1f\n", temperature, humidity); 276 | sleep(1); 277 | } 278 | 279 | rc = i2c_close(i2cfd); 280 | if (rc < 0) { 281 | printf("i2c_close failed %d\n", rc); 282 | return -5; 283 | } 284 | return 0; 285 | } 286 | #endif 287 | -------------------------------------------------------------------------------- /htu21dflib/htu21dflib.h: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2014 bbx10 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | int i2c_open(const char *i2cdevname); 26 | 27 | int i2c_close(int i2cfd); 28 | 29 | int htu21df_init(int i2cfd, uint8_t i2caddr); 30 | 31 | int htu21df_read_temperature(int i2cfd, float *temperature); 32 | 33 | int htu21df_read_humidity(int i2cfd, float *humidity); 34 | -------------------------------------------------------------------------------- /htu21dflib/mqtt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/switchdoclabs/WeatherPi/87027860195698521da8bd33cc0cd81115da0d59/htu21dflib/mqtt -------------------------------------------------------------------------------- /htu21dflib/mqtt.c: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) 2014 bbx10node@gmail.com 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include "htu21dflib.h" 31 | 32 | static const char I2CDEV[] = "/dev/i2c-1"; // raspberry pi 33 | static const uint8_t I2CADDR = 0x40; // htu21df i2c address 34 | 35 | static const char MQTT_HOST[] = "bbone"; // mqtt host 36 | static const char MQTT_TOPIC_TEMPERATURE[] = "temperature/mbed"; // mqtt temp topic 37 | static const char MQTT_TOPIC_HUMIDITY[] = "humidity/mbed"; // mqtt humi topic 38 | 39 | int main(int argc, char *argv[]) 40 | { 41 | int rc; // return code 42 | int i2cfd; // i2c file descriptor 43 | float temperature, humidity; 44 | char mqttstr[256]; 45 | 46 | i2cfd = i2c_open(I2CDEV); 47 | if (i2cfd < 0) { 48 | printf("i2c_open failed %d\n", i2cfd); 49 | return -1; 50 | } 51 | 52 | rc = htu21df_init(i2cfd, I2CADDR); 53 | if (rc < 0) { 54 | printf("i2c_init failed %d\n", rc); 55 | return -2; 56 | } 57 | 58 | while (1) { 59 | rc = htu21df_read_temperature(i2cfd, &temperature); 60 | if (rc < 0) { 61 | printf("i2c_read_temperature failed %d\n", rc); 62 | return -3; 63 | } 64 | 65 | rc = htu21df_read_humidity(i2cfd, &humidity); 66 | if (rc < 0) { 67 | printf("i2c_read_humidity failed %d\n", rc); 68 | return -4; 69 | } 70 | // Format the command to mqtt the temperature 71 | rc = snprintf(mqttstr, sizeof(mqttstr), 72 | "mosquitto_pub -h %s -t %s -m %0.1f", 73 | MQTT_HOST, MQTT_TOPIC_TEMPERATURE, temperature); 74 | if (rc < 0) { 75 | printf("snprintf failed %d\n", rc); 76 | } 77 | // Run the command to dweet the data 78 | rc = system(mqttstr); 79 | if (rc != 0) { 80 | printf("system failed %d\n", rc); 81 | } 82 | // Format the command to mqtt the humidity 83 | rc = snprintf(mqttstr, sizeof(mqttstr), 84 | "mosquitto_pub -h %s -t %s -m %0.1f", 85 | MQTT_HOST, MQTT_TOPIC_HUMIDITY, humidity); 86 | if (rc < 0) { 87 | printf("snprintf failed %d\n", rc); 88 | } 89 | // Run the command to dweet the data 90 | rc = system(mqttstr); 91 | if (rc != 0) { 92 | printf("system failed %d\n", rc); 93 | } 94 | // Wait 10 seconds 95 | sleep(10); 96 | } 97 | 98 | rc = i2c_close(i2cfd); 99 | if (rc < 0) { 100 | printf("i2c_close failed %d\n", rc); 101 | return -5; 102 | } 103 | 104 | return 0; 105 | } 106 | -------------------------------------------------------------------------------- /pclogging.py: -------------------------------------------------------------------------------- 1 | # 2 | # 3 | # logging system from Project Curacao 4 | # filename: pclogger.py 5 | # Version 1.0 10/04/13 6 | # 7 | # contains logging data 8 | # 9 | 10 | 11 | CRITICAL=50 12 | ERROR=40 13 | WARNING=30 14 | INFO=20 15 | DEBUG=10 16 | NOTSET=0 17 | 18 | 19 | import sys 20 | import time 21 | import MySQLdb as mdb 22 | 23 | DATABASEPASSWORD = "rmysqlpassword" 24 | 25 | def log(level, source, message): 26 | 27 | LOWESTDEBUG = 0 28 | # open mysql database 29 | 30 | # write log 31 | 32 | 33 | # commit 34 | 35 | 36 | # close 37 | 38 | if (level >= LOWESTDEBUG): 39 | try: 40 | 41 | #print("trying database") 42 | con = mdb.connect('localhost', 'root', DATABASEPASSWORD, 'WeatherPi'); 43 | 44 | cur = con.cursor() 45 | #print "before query" 46 | 47 | query = "INSERT INTO systemlog(TimeStamp, Level, Source, Message) VALUES(UTC_TIMESTAMP(), %i, '%s', '%s')" % (level, source, message) 48 | #print("query=%s" % query) 49 | 50 | cur.execute(query) 51 | 52 | con.commit() 53 | 54 | 55 | except mdb.Error, e: 56 | 57 | print "Error %d: %s" % (e.args[0],e.args[1]) 58 | con.rollback() 59 | #sys.exit(1) 60 | 61 | finally: 62 | cur.close() 63 | con.close() 64 | 65 | del cur 66 | del con 67 | 68 | -------------------------------------------------------------------------------- /sendemail.py: -------------------------------------------------------------------------------- 1 | def sendEmail(source, message, subject, toaddress, fromaddress, filename): 2 | 3 | # if conflocal.py is not found, import default conf.py 4 | 5 | # Check for user imports 6 | try: 7 | import conflocal as conf 8 | except ImportError: 9 | import conf 10 | 11 | # Import smtplib for the actual sending function 12 | import smtplib 13 | 14 | # Here are the email package modules we'll need 15 | from email.mime.image import MIMEImage 16 | from email.mime.multipart import MIMEMultipart 17 | from email.mime.text import MIMEText 18 | 19 | COMMASPACE = ', ' 20 | 21 | # Create the container (outer) email message. 22 | msg = MIMEMultipart() 23 | msg['Subject'] = subject 24 | # me == the sender's email address 25 | # family = the list of all recipients' email addresses 26 | msg['From'] = fromaddress 27 | msg['To'] = toaddress 28 | #msg.attach(message) 29 | 30 | mainbody = MIMEText(message, 'plain') 31 | msg.attach(mainbody) 32 | 33 | # Assume we know that the image files are all in PNG format 34 | # Open the files in binary mode. Let the MIMEImage class automatically 35 | # guess the specific image type. 36 | if (filename != ""): 37 | fp = open(filename, 'rb') 38 | img = MIMEImage(fp.read()) 39 | fp.close() 40 | msg.attach(img) 41 | 42 | # Send the email via our own SMTP server. 43 | 44 | try: 45 | # open up a line with the server 46 | s = smtplib.SMTP("smtp.gmail.com", 587) 47 | s.ehlo() 48 | s.starttls() 49 | s.ehlo() 50 | 51 | # login, send email, logout 52 | s.login(conf.mailUser, conf.mailPassword) 53 | s.sendmail(conf.mailUser, toaddress, msg.as_string()) 54 | #s.close() 55 | 56 | 57 | s.quit() 58 | 59 | except: 60 | 61 | print("sendmail exception raised") 62 | return 0 63 | 64 | 65 | -------------------------------------------------------------------------------- /startserver.sh: -------------------------------------------------------------------------------- 1 | 2 | #!/bin/bash 3 | cd /home/pi/WeatherPiSolarPoweredWeather 4 | sudo python WeatherPi.py 5 | -------------------------------------------------------------------------------- /state/SunAirPlusStats.txt: -------------------------------------------------------------------------------- 1 | 3.8884 2 | -236.0 3 | 4.72308 4 | 529.2 5 | 4.928 6 | 216.0 7 | -0.9176624 8 | 2.499453936 9 | 1.064448 10 | 70.9508936451 11 | -------------------------------------------------------------------------------- /state/WeatherCommand.txt: -------------------------------------------------------------------------------- 1 | DONE -------------------------------------------------------------------------------- /state/WeatherStats.txt: -------------------------------------------------------------------------------- 1 | 100.298 2 | 0 3 | 0 4 | 0 5 | Noise Floor too low. Adjusting 6 | 3.66246497416 7 | 4.50583054472 8 | 100.298 9 | 46.8 10 | 94 11 | 614.981968619 12 | 94.145 13 | 37.0 14 | 25.2999992371 15 | 135.0 16 | 0.603 17 | 46.7 18 | 53.1 19 | -------------------------------------------------------------------------------- /testAM2315.py: -------------------------------------------------------------------------------- 1 | import time 2 | from tentacle_pi.AM2315 import AM2315 3 | am = AM2315(0x5c,"/dev/i2c-1") 4 | 5 | for x in range(0,10): 6 | temperature, humidity, crc_check = am.sense() 7 | print "temperature: %0.1f" % temperature 8 | print "humidity: %0.1f" % humidity 9 | print "crc: %s" % crc_check 10 | print 11 | time.sleep(2.0) 12 | --------------------------------------------------------------------------------