├── .gitignore ├── AtlasI2C.py ├── LICENSE ├── README.md ├── ftdi.py ├── i2c.py └── uart.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | .gitignore 3 | .idea/ 4 | -------------------------------------------------------------------------------- /AtlasI2C.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import io 4 | import sys 5 | import fcntl 6 | import time 7 | import copy 8 | import string 9 | 10 | 11 | class AtlasI2C: 12 | 13 | # the timeout needed to query readings and calibrations 14 | LONG_TIMEOUT = 1.5 15 | # timeout for regular commands 16 | SHORT_TIMEOUT = .3 17 | # the default bus for I2C on the newer Raspberry Pis, 18 | # certain older boards use bus 0 19 | DEFAULT_BUS = 1 20 | # the default address for the sensor 21 | DEFAULT_ADDRESS = 98 22 | LONG_TIMEOUT_COMMANDS = ("R", "CAL") 23 | SLEEP_COMMANDS = ("SLEEP", ) 24 | 25 | def __init__(self, address=None, moduletype = "", name = "", bus=None): 26 | ''' 27 | open two file streams, one for reading and one for writing 28 | the specific I2C channel is selected with bus 29 | it is usually 1, except for older revisions where its 0 30 | wb and rb indicate binary read and write 31 | ''' 32 | self._address = address or self.DEFAULT_ADDRESS 33 | self.bus = bus or self.DEFAULT_BUS 34 | self._long_timeout = self.LONG_TIMEOUT 35 | self._short_timeout = self.SHORT_TIMEOUT 36 | self.file_read = io.open(file="/dev/i2c-{}".format(self.bus), 37 | mode="rb", 38 | buffering=0) 39 | self.file_write = io.open(file="/dev/i2c-{}".format(self.bus), 40 | mode="wb", 41 | buffering=0) 42 | self.set_i2c_address(self._address) 43 | self._name = name 44 | self._module = moduletype 45 | 46 | 47 | @property 48 | def long_timeout(self): 49 | return self._long_timeout 50 | 51 | @property 52 | def short_timeout(self): 53 | return self._short_timeout 54 | 55 | @property 56 | def name(self): 57 | return self._name 58 | 59 | @property 60 | def address(self): 61 | return self._address 62 | 63 | @property 64 | def moduletype(self): 65 | return self._module 66 | 67 | 68 | def set_i2c_address(self, addr): 69 | ''' 70 | set the I2C communications to the slave specified by the address 71 | the commands for I2C dev using the ioctl functions are specified in 72 | the i2c-dev.h file from i2c-tools 73 | ''' 74 | I2C_SLAVE = 0x703 75 | fcntl.ioctl(self.file_read, I2C_SLAVE, addr) 76 | fcntl.ioctl(self.file_write, I2C_SLAVE, addr) 77 | self._address = addr 78 | 79 | def write(self, cmd): 80 | ''' 81 | appends the null character and sends the string over I2C 82 | ''' 83 | cmd += "\00" 84 | self.file_write.write(cmd.encode('latin-1')) 85 | 86 | def handle_raspi_glitch(self, response): 87 | ''' 88 | Change MSB to 0 for all received characters except the first 89 | and get a list of characters 90 | NOTE: having to change the MSB to 0 is a glitch in the raspberry pi, 91 | and you shouldn't have to do this! 92 | ''' 93 | if self.app_using_python_two(): 94 | return list(map(lambda x: chr(ord(x) & ~0x80), list(response))) 95 | else: 96 | return list(map(lambda x: chr(x & ~0x80), list(response))) 97 | 98 | def app_using_python_two(self): 99 | return sys.version_info[0] < 3 100 | 101 | def get_response(self, raw_data): 102 | if self.app_using_python_two(): 103 | response = [i for i in raw_data if i != '\x00'] 104 | else: 105 | response = raw_data 106 | 107 | return response 108 | 109 | def response_valid(self, response): 110 | valid = True 111 | error_code = None 112 | if(len(response) > 0): 113 | 114 | if self.app_using_python_two(): 115 | error_code = str(ord(response[0])) 116 | else: 117 | error_code = str(response[0]) 118 | 119 | if error_code != '1': #1: 120 | valid = False 121 | 122 | return valid, error_code 123 | 124 | def get_device_info(self): 125 | if(self._name == ""): 126 | return self._module + " " + str(self.address) 127 | else: 128 | return self._module + " " + str(self.address) + " " + self._name 129 | 130 | def read(self, num_of_bytes=31): 131 | ''' 132 | reads a specified number of bytes from I2C, then parses and displays the result 133 | ''' 134 | 135 | raw_data = self.file_read.read(num_of_bytes) 136 | response = self.get_response(raw_data=raw_data) 137 | #print(response) 138 | is_valid, error_code = self.response_valid(response=response) 139 | 140 | if is_valid: 141 | char_list = self.handle_raspi_glitch(response[1:]) 142 | result = "Success " + self.get_device_info() + ": " + str(''.join(char_list)) 143 | #result = "Success: " + str(''.join(char_list)) 144 | else: 145 | result = "Error " + self.get_device_info() + ": " + error_code 146 | 147 | return result 148 | 149 | def get_command_timeout(self, command): 150 | timeout = None 151 | if command.upper().startswith(self.LONG_TIMEOUT_COMMANDS): 152 | timeout = self._long_timeout 153 | elif not command.upper().startswith(self.SLEEP_COMMANDS): 154 | timeout = self.short_timeout 155 | 156 | return timeout 157 | 158 | def query(self, command): 159 | ''' 160 | write a command to the board, wait the correct timeout, 161 | and read the response 162 | ''' 163 | self.write(command) 164 | current_timeout = self.get_command_timeout(command=command) 165 | if not current_timeout: 166 | return "sleep mode" 167 | else: 168 | time.sleep(current_timeout) 169 | return self.read() 170 | 171 | def close(self): 172 | self.file_read.close() 173 | self.file_write.close() 174 | 175 | def list_i2c_devices(self): 176 | ''' 177 | save the current address so we can restore it after 178 | ''' 179 | prev_addr = copy.deepcopy(self._address) 180 | i2c_devices = [] 181 | for i in range(0, 128): 182 | try: 183 | self.set_i2c_address(i) 184 | self.read(1) 185 | i2c_devices.append(i) 186 | except IOError: 187 | pass 188 | # restore the address we were using 189 | self.set_i2c_address(prev_addr) 190 | 191 | return i2c_devices 192 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 AtlasScientific 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Preparing the Raspberry Pi # 3 | ### Install the latest Raspberry Pi OS 4 | Follow the instructions on this page to get Raspberry Pi OS running 5 | https://www.raspberrypi.org/downloads/raspberry-pi-os/ 6 | 7 | ### Expand file system 8 | 9 | Expand file system by following this: 10 | https://www.raspberrypi.org/documentation/configuration/raspi-config.md 11 | 12 | ### Update and Upgrade Packages 13 | 14 | sudo apt-get update 15 | sudo apt-get upgrade 16 | 17 | # Download sample code. 18 | 19 | cd ~ 20 | git clone https://github.com/AtlasScientific/Raspberry-Pi-sample-code.git 21 | 22 | 23 | # FTDI MODE # 24 | 25 | ### Installing dependencies for FTDI adaptors ### 26 | 27 | - Install libftdi package. 28 | 29 | sudo apt-get install libftdi-dev 30 | 31 | 32 | - Install pylibftdi python package. 33 | 34 | sudo pip install pylibftdi 35 | 36 | 37 | - Create SYMLINK of the FTDI adaptors. 38 | 39 | The following will allow ordinary users (e.g. ‘pi’ on the RPi) to access to the FTDI device without needing root permissions: 40 | 41 | If you are using device with root permission, just skip this step. 42 | 43 | Create udev rule file by typing `sudo nano /etc/udev/rules.d/99-libftdi.rules` and insert below: 44 | 45 | SUBSYSTEMS=="usb", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6015", GROUP="dialout", MODE="0660", SYMLINK+="FTDISerial_Converter_$attr{serial}" 46 | 47 | Press CTRL+X, Y and hit Enter to save & exit. 48 | 49 | Restart `udev` service to apply changes above. 50 | 51 | sudo service udev restart 52 | 53 | 54 | - Modify FTDI python driver 55 | 56 | Since our FTDI devices use other USB PID(0x6015), we need to tweak the original FTDI Driver. 57 | 58 | sudo nano /usr/local/lib/python2.7/dist-packages/pylibftdi/driver.py 59 | 60 | Move down to the line 70 and add `0x6015` at the end of line. 61 | 62 | Original line: 63 | 64 | USB_PID_LIST = [0x6001, 0x6010, 0x6011, 0x6014] 65 | 66 | Added line: 67 | 68 | USB_PID_LIST = [0x6001, 0x6010, 0x6011, 0x6014, 0x6015] 69 | 70 | 71 | - Testing Installation. 72 | 73 | Connect your device, and run the following (as a regular user): 74 | 75 | python -m pylibftdi.examples.list_devices 76 | 77 | If all goes well, the program should report information about each connected device. 78 | 79 | If no information is printed, but it is when run with sudo, 80 | a possibility is permissions problems - see the section under Linux above regarding udev rules. 81 | 82 | You may get result like this: 83 | 84 | FTDI:FT230X Basic UART:DA00TN73 85 | 86 | FTDI adaptors has its own unique serial number. 87 | 88 | We need this to work with our sensors. 89 | 90 | In the result above, serial number is `DA00TN73`. 91 | 92 | ### Using pylibftdi module for Atlas Sensors. ### 93 | 94 | Please remember the serial number of your device and run the sample code. 95 | 96 | cd ~/Raspberry-Pi-sample-code 97 | sudo python ftdi.py 98 | 99 | Input the serial number and you can see the sensor's information and also sensor's LED status as well. 100 | 101 | For more details on the commands & responses, please refer the Datasheets of Atlas Sensors. 102 | 103 | 104 | # I2C MODE # 105 | 106 | ### Enable I2C bus on the Raspberry Pi ### 107 | 108 | Enable I2C bus on the Raspberry Pi by following this: 109 | 110 | https://learn.adafruit.com/adafruits-raspberry-pi-lesson-4-gpio-setup/configuring-i2c 111 | 112 | You can confirm that the setup worked and sensors are present with the `sudo i2cdetect -y 1` command. 113 | 114 | ### Test Sensor ### 115 | 116 | Run the sample code below: 117 | 118 | cd ~/Raspberry-Pi-sample-code 119 | sudo python i2c.py 120 | 121 | When the code starts up a list of commands will be shown. 122 | 123 | For more details on the commands & responses, please refer to the Datasheets of the Atlas Scientific Sensors. 124 | 125 | 126 | 127 | # UART MODE # 128 | 129 | ### Preventing Raspberry Pi from using the serial port ### 130 | 131 | The Broadcom UART appears as `/dev/ttyS0` under Linux on every Pi. The Pi 4 has additional UARTS, see below for instruction on how to use them 132 | 133 | There are several minor things in the way if you want to have dedicated control of the primary serial port on a Raspberry Pi. 134 | 135 | - Firstly, the kernel will use the port as controlled by kernel command line contained in `/boot/cmdline.txt`. 136 | 137 | The file will look something like this: 138 | 139 | dwc_otg.lpm_enable=0 console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline rootwait 140 | 141 | The console keyword outputs messages during boot, and the kgdboc keyword enables kernel debugging. 142 | 143 | You will need to remove all references to ttyAMA0. 144 | 145 | So, for the example above `/boot/cmdline.txt`, should contain: 146 | 147 | dwc_otg.lpm_enable=0 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline rootwait 148 | 149 | You must be root to edit this (e.g. use `sudo nano /boot/cmdline.txt`). 150 | 151 | Be careful doing this, as a faulty command line can prevent the system booting. 152 | 153 | - Secondly, after booting, a login prompt appears on the serial port. 154 | 155 | This is controlled by the following lines in `/etc/inittab`: 156 | 157 | #Spawn a getty on Raspberry Pi serial line 158 | T0:23:respawn:/sbin/getty -L ttyAMA0 115200 vt100 159 | 160 | You will need to edit this file to comment out the second line, i.e. 161 | 162 | #T0:23:respawn:/sbin/getty -L ttyAMA0 115200 vt100 163 | 164 | - Finally you will need to reboot the Raspberry Pi for the new settings to take effect. 165 | 166 | Once this is done, you can use `/dev/ttyS0` like any normal Linux serial port, and you won't get any unwanted traffic confusing the attached devices. 167 | 168 | To double-check, use 169 | 170 | cat /proc/cmdline 171 | 172 | to show the current kernel command line, and 173 | 174 | ps aux | grep ttyS0 175 | 176 | to search for getty processes using the serial port. 177 | 178 | 179 | ### Ensure PySerial is installed for Python. ### 180 | 181 | sudo pip install pyserial 182 | 183 | ### Run the below Python script: 184 | 185 | cd ~/Raspberry-Pi-sample-code 186 | sudo python uart.py 187 | 188 | ### Alternate UARTS on Pi4: 189 | 190 | The raspberry pi 4 has 6 uarts 191 | 192 | To demonstrate alternate UART usage, we're going to enable UART 5 193 | Note that other UARTs share their pins with other peripherals, so those peripherals may have to be disabled to use them 194 | 195 | UART 5 uses pins 32 (TX) and 33 (RX) on the raspberry pi 40 pin header 196 | 197 | Go into the boot configuration 198 | 199 | sudo nano /boot/config.txt 200 | 201 | and add the lines 202 | 203 | enable_uart=1 204 | dtoverlay=uart5 205 | 206 | then restart the raspberry pi 207 | 208 | To use this port in the uart sample code `uart.py` change line 70 to: 209 | 210 | usbport = '/dev/ttyAMA1' 211 | 212 | Note that it may be a different ttyAMA depending on your setup 213 | 214 | -------------------------------------------------------------------------------- /ftdi.py: -------------------------------------------------------------------------------- 1 | import string 2 | 3 | import pylibftdi 4 | from pylibftdi.device import Device 5 | from pylibftdi.driver import FtdiError 6 | from pylibftdi import Driver 7 | import os 8 | import time 9 | 10 | 11 | class AtlasDevice(Device): 12 | 13 | def __init__(self, sn): 14 | Device.__init__(self, mode='t', device_id=sn) 15 | 16 | 17 | def read_line(self, size=0): 18 | """ 19 | taken from the ftdi library and modified to 20 | use the ezo line separator "\r" 21 | """ 22 | lsl = len('\r') 23 | line_buffer = [] 24 | while True: 25 | next_char = self.read(1) 26 | if next_char == '' or (size > 0 and len(line_buffer) > size): 27 | break 28 | line_buffer.append(next_char) 29 | if (len(line_buffer) >= lsl and 30 | line_buffer[-lsl:] == list('\r')): 31 | break 32 | return ''.join(line_buffer) 33 | 34 | def read_lines(self): 35 | """ 36 | also taken from ftdi lib to work with modified readline function 37 | """ 38 | lines = [] 39 | try: 40 | while True: 41 | line = self.read_line() 42 | if not line: 43 | break 44 | self.flush_input() 45 | lines.append(line) 46 | return lines 47 | 48 | except FtdiError: 49 | print("Failed to read from the sensor.") 50 | return '' 51 | 52 | def send_cmd(self, cmd): 53 | """ 54 | Send command to the Atlas Sensor. 55 | Before sending, add Carriage Return at the end of the command. 56 | :param cmd: 57 | :return: 58 | """ 59 | buf = cmd + "\r" # add carriage return 60 | try: 61 | self.write(buf) 62 | return True 63 | except FtdiError: 64 | print("Failed to send command to the sensor.") 65 | return False 66 | 67 | 68 | 69 | def get_ftdi_device_list(): 70 | """ 71 | return a list of lines, each a colon-separated 72 | vendor:product:serial summary of detected devices 73 | """ 74 | dev_list = [] 75 | 76 | for device in Driver().list_devices(): 77 | # list_devices returns bytes rather than strings 78 | dev_info = map(lambda x: x.decode('latin1'), device) 79 | # device must always be this triple 80 | vendor, product, serial = dev_info 81 | dev_list.append(serial) 82 | return dev_list 83 | 84 | 85 | if __name__ == '__main__': 86 | 87 | real_raw_input = vars(__builtins__).get('raw_input', input) # used to find the correct function for python2/3 88 | 89 | print("\nWelcome to the Atlas Scientific Raspberry Pi FTDI Serial example.\n") 90 | print(" Any commands entered are passed to the board via UART except:") 91 | print(" Poll,xx.x command continuously polls the board every xx.x seconds") 92 | print(" Pressing ctrl-c will stop the polling\n") 93 | print(" Press enter to receive all data in buffer (for continuous mode) \n") 94 | print("Discovered FTDI serial numbers:") 95 | 96 | devices = get_ftdi_device_list() 97 | cnt_all = len(devices) 98 | 99 | #print "\nIndex:\tSerial: " 100 | for i in range(cnt_all): 101 | print( "\nIndex: ", i, " Serial: ", devices[i]) 102 | print( "===================================") 103 | 104 | index = 0 105 | while True: 106 | index = real_raw_input("Please select a device index: ") 107 | 108 | try: 109 | dev = AtlasDevice(devices[int(index)]) 110 | break 111 | except pylibftdi.FtdiError as e: 112 | print( "Error, ", e) 113 | print( "Please input a valid index") 114 | 115 | print( "") 116 | print(">> Opened device ", devices[int(index)]) 117 | print(">> Any commands entered are passed to the board via FTDI:") 118 | 119 | time.sleep(1) 120 | dev.flush() 121 | 122 | 123 | while True: 124 | input_val = real_raw_input("Enter command: ") 125 | 126 | 127 | 128 | # continuous polling command automatically polls the board 129 | if input_val.upper().startswith("POLL"): 130 | delaytime = float(string.split(input_val, ',')[1]) 131 | 132 | dev.send_cmd("C,0") # turn off continuous mode 133 | #clear all previous data 134 | time.sleep(1) 135 | dev.flush() 136 | 137 | # get the information of the board you're polling 138 | print("Polling sensor every %0.2f seconds, press ctrl-c to stop polling" % delaytime) 139 | 140 | try: 141 | while True: 142 | dev.send_cmd("R") 143 | lines = dev.read_lines() 144 | for i in range(len(lines)): 145 | # print lines[i] 146 | if lines[i][0] != '*': 147 | print("Response: " , lines[i]) 148 | time.sleep(delaytime) 149 | 150 | except KeyboardInterrupt: # catches the ctrl-c command, which breaks the loop above 151 | print("Continuous polling stopped") 152 | 153 | else: 154 | # pass commands straight to board 155 | if len(input_val) == 0: 156 | lines = dev.read_lines() 157 | for i in range(len(lines)): 158 | print( lines[i]) 159 | else: 160 | dev.send_cmd(input_val) 161 | time.sleep(1.3) 162 | lines = dev.read_lines() 163 | for i in range(len(lines)): 164 | print( lines[i]) 165 | -------------------------------------------------------------------------------- /i2c.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import io 4 | import sys 5 | import fcntl 6 | import time 7 | import copy 8 | import string 9 | from AtlasI2C import ( 10 | AtlasI2C 11 | ) 12 | 13 | def print_devices(device_list, device): 14 | for i in device_list: 15 | if(i == device): 16 | print("--> " + i.get_device_info()) 17 | else: 18 | print(" - " + i.get_device_info()) 19 | #print("") 20 | 21 | def get_devices(): 22 | device = AtlasI2C() 23 | device_address_list = device.list_i2c_devices() 24 | device_list = [] 25 | 26 | for i in device_address_list: 27 | device.set_i2c_address(i) 28 | response = device.query("I") 29 | try: 30 | moduletype = response.split(",")[1] 31 | response = device.query("name,?").split(",")[1] 32 | except IndexError: 33 | print(">> WARNING: device at I2C address " + str(i) + " has not been identified as an EZO device, and will not be queried") 34 | continue 35 | device_list.append(AtlasI2C(address = i, moduletype = moduletype, name = response)) 36 | return device_list 37 | 38 | def print_help_text(): 39 | print(''' 40 | >> Atlas Scientific I2C sample code 41 | >> Any commands entered are passed to the default target device via I2C except: 42 | - Help 43 | brings up this menu 44 | - List 45 | lists the available I2C circuits. 46 | the --> indicates the target device that will receive individual commands 47 | - xxx:[command] 48 | sends the command to the device at I2C address xxx 49 | and sets future communications to that address 50 | Ex: "102:status" will send the command status to address 102 51 | - all:[command] 52 | sends the command to all devices 53 | - Poll[,x.xx] 54 | command continuously polls all devices 55 | the optional argument [,x.xx] lets you set a polling time 56 | where x.xx is greater than the minimum %0.2f second timeout. 57 | by default it will poll every %0.2f seconds 58 | >> Pressing ctrl-c will stop the polling 59 | ''' % (AtlasI2C.LONG_TIMEOUT, AtlasI2C.LONG_TIMEOUT)) 60 | 61 | def main(): 62 | 63 | print_help_text() 64 | 65 | device_list = get_devices() 66 | 67 | device = device_list[0] 68 | 69 | print_devices(device_list, device) 70 | 71 | real_raw_input = vars(__builtins__).get('raw_input', input) 72 | 73 | while True: 74 | 75 | user_cmd = real_raw_input(">> Enter command: ") 76 | 77 | # show all the available devices 78 | if user_cmd.upper().strip().startswith("LIST"): 79 | print_devices(device_list, device) 80 | 81 | # print the help text 82 | elif user_cmd.upper().startswith("HELP"): 83 | print_help_text() 84 | 85 | # continuous polling command automatically polls the board 86 | elif user_cmd.upper().strip().startswith("POLL"): 87 | cmd_list = user_cmd.split(',') 88 | if len(cmd_list) > 1: 89 | delaytime = float(cmd_list[1]) 90 | else: 91 | delaytime = device.long_timeout 92 | 93 | # check for polling time being too short, change it to the minimum timeout if too short 94 | if delaytime < device.long_timeout: 95 | print("Polling time is shorter than timeout, setting polling time to %0.2f" % device.long_timeout) 96 | delaytime = device.long_timeout 97 | try: 98 | while True: 99 | print("-------press ctrl-c to stop the polling") 100 | for dev in device_list: 101 | dev.write("R") 102 | time.sleep(delaytime) 103 | for dev in device_list: 104 | print(dev.read()) 105 | 106 | except KeyboardInterrupt: # catches the ctrl-c command, which breaks the loop above 107 | print("Continuous polling stopped") 108 | print_devices(device_list, device) 109 | 110 | # send a command to all the available devices 111 | elif user_cmd.upper().strip().startswith("ALL:"): 112 | cmd_list = user_cmd.split(":") 113 | for dev in device_list: 114 | dev.write(cmd_list[1]) 115 | 116 | # figure out how long to wait before reading the response 117 | timeout = device_list[0].get_command_timeout(cmd_list[1].strip()) 118 | # if we dont have a timeout, dont try to read, since it means we issued a sleep command 119 | if(timeout): 120 | time.sleep(timeout) 121 | for dev in device_list: 122 | print(dev.read()) 123 | 124 | # if not a special keyword, see if we change the address, and communicate with that device 125 | else: 126 | try: 127 | cmd_list = user_cmd.split(":") 128 | if(len(cmd_list) > 1): 129 | addr = cmd_list[0] 130 | 131 | # go through the devices to figure out if its available 132 | # and swith to it if it is 133 | switched = False 134 | for i in device_list: 135 | if(i.address == int(addr)): 136 | device = i 137 | switched = True 138 | if(switched): 139 | print(device.query(cmd_list[1])) 140 | else: 141 | print("No device found at address " + addr) 142 | else: 143 | # if no address change, just send the command to the device 144 | print(device.query(user_cmd)) 145 | except IOError: 146 | print("Query failed \n - Address may be invalid, use list command to see available addresses") 147 | 148 | 149 | if __name__ == '__main__': 150 | main() -------------------------------------------------------------------------------- /uart.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import serial 4 | import sys 5 | import time 6 | import string 7 | from serial import SerialException 8 | 9 | def read_line(): 10 | """ 11 | taken from the ftdi library and modified to 12 | use the ezo line separator "\r" 13 | """ 14 | lsl = len(b'\r') 15 | line_buffer = [] 16 | while True: 17 | next_char = ser.read(1) 18 | if next_char == b'': 19 | break 20 | line_buffer.append(next_char) 21 | if (len(line_buffer) >= lsl and 22 | line_buffer[-lsl:] == [b'\r']): 23 | break 24 | return b''.join(line_buffer) 25 | 26 | def read_lines(): 27 | """ 28 | also taken from ftdi lib to work with modified readline function 29 | """ 30 | lines = [] 31 | try: 32 | while True: 33 | line = read_line() 34 | if not line: 35 | break 36 | ser.flush_input() 37 | lines.append(line) 38 | return lines 39 | 40 | except SerialException as e: 41 | print( "Error, ", e) 42 | return None 43 | 44 | def send_cmd(cmd): 45 | """ 46 | Send command to the Atlas Sensor. 47 | Before sending, add Carriage Return at the end of the command. 48 | :param cmd: 49 | :return: 50 | """ 51 | buf = cmd + "\r" # add carriage return 52 | try: 53 | ser.write(buf.encode('utf-8')) 54 | return True 55 | except SerialException as e: 56 | print ("Error, ", e) 57 | return None 58 | 59 | if __name__ == "__main__": 60 | 61 | real_raw_input = vars(__builtins__).get('raw_input', input) # used to find the correct function for python2/3 62 | 63 | print("\nWelcome to the Atlas Scientific Raspberry Pi UART example.\n") 64 | print(" Any commands entered are passed to the board via UART except:") 65 | print(" Poll,xx.x command continuously polls the board every xx.x seconds") 66 | print(" Pressing ctrl-c will stop the polling\n") 67 | print(" Press enter to receive all data in buffer (for continuous mode) \n") 68 | 69 | # to get a list of ports use the command: 70 | # python -m serial.tools.list_ports 71 | # in the terminal 72 | usbport = '/dev/ttyAMA0' # change to match your pi's setup 73 | 74 | print( "Opening serial port now...") 75 | 76 | try: 77 | ser = serial.Serial(usbport, 9600, timeout=0) 78 | except serial.SerialException as e: 79 | print( "Error, ", e) 80 | sys.exit(0) 81 | 82 | while True: 83 | input_val = real_raw_input("Enter command: ") 84 | 85 | # continuous polling command automatically polls the board 86 | if input_val.upper().startswith("POLL"): 87 | delaytime = float(input_val.split(',')[1]) 88 | 89 | send_cmd("C,0") # turn off continuous mode 90 | #clear all previous data 91 | time.sleep(1) 92 | ser.flush() 93 | 94 | # get the information of the board you're polling 95 | print("Polling sensor every %0.2f seconds, press ctrl-c to stop polling" % delaytime) 96 | 97 | try: 98 | while True: 99 | send_cmd("R") 100 | lines = read_lines() 101 | for i in range(len(lines)): 102 | # print lines[i] 103 | if lines[i][0] != b'*'[0]: 104 | print( "Response: " + lines[i].decode('utf-8')) 105 | time.sleep(delaytime) 106 | 107 | except KeyboardInterrupt: # catches the ctrl-c command, which breaks the loop above 108 | print("Continuous polling stopped") 109 | 110 | # if not a special keyword, pass commands straight to board 111 | else: 112 | if len(input_val) == 0: 113 | lines = read_lines() 114 | for i in range(len(lines)): 115 | print( lines[i].decode('utf-8')) 116 | else: 117 | send_cmd(input_val) 118 | time.sleep(1.3) 119 | lines = read_lines() 120 | for i in range(len(lines)): 121 | print( lines[i].decode('utf-8')) 122 | --------------------------------------------------------------------------------