adafruit tuple index out of range - python

I am writing code that reads a file and sends it to an 20x4 LCD and I have buttons so I can move up and down the file
`
import math
import time
import linecache
import Adafruit_CharLCD as LCD
lcd = LCD.Adafruit_CharLCDPlate()
lcd.set_backlight (1)
end = 0
page = 0
load = 0
lineno = 0
txtfilename = ('file.txt' % spot)
while (end != 2):
if lcd.is_pressed(LCD.UP) or lcd.is_pressed(LCD.DOWN):
lcd.clear()
lineno += 1 if lcd.is_pressed(LCD.UP) else -1
text1 = linecache.getline(txtfilename, lineno)
text2 = linecache.getline(txtfilename, lineno +1)
text3 = linecache.getline(txtfilename, lineno +2)
text4 = linecache.getline(txtfilename, lineno +3)
print lineno
lcd.message(("%s%s%s%s") % (text1, text2, text3, text4))
print "%s" % text1
time.sleep (0.5)
end = 0
elif lcd.is_pressed(LCD.SELECT):
lcd.clear()
lcd.message ("Press agine to quit")
end +=1
#load
time.sleep (0.5)
lcd.clear()
lcd.message ('Goodbye')
time.sleep (0.5)
lcd.clear()
lcd.set_backlight (0)
`
The file that I will be reading will have lots of lines in it but each line will be under the 20 character
When I run this the 1st 4 lines are displayed but then I get the following
File "spot.py", line 54, in <module>
lcd.message(("%s%s%s%s") % (text1, text2, text3, text4))
File "/home/pi/me/Adafruit_CharLCD.py", line 246, in message
self.set_cursor(col, line)
File "/home/pi/me/Adafruit_CharLCD.py", line 182, in set_cursor
self.write8(LCD_SETDDRAMADDR | (col + LCD_ROW_OFFSETS[row]))
IndexError: tuple index out of range
I have seen other posts like this problem but I can't get them to work. Can anyone help me please?
Here is the Adafruit code I am using
# Copyright (c) 2014 Adafruit Industries
# Author: Tony DiCola
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
import time
import Adafruit_GPIO as GPIO
import Adafruit_GPIO.I2C as I2C
import Adafruit_GPIO.MCP230xx as MCP
import Adafruit_GPIO.PWM as PWM
# Commands
LCD_CLEARDISPLAY = 0x01
LCD_RETURNHOME = 0x02
LCD_ENTRYMODESET = 0x04
LCD_DISPLAYCONTROL = 0x08
LCD_CURSORSHIFT = 0x10
LCD_FUNCTIONSET = 0x20
LCD_SETCGRAMADDR = 0x40
LCD_SETDDRAMADDR = 0x80
# Entry flags
LCD_ENTRYRIGHT = 0x00
LCD_ENTRYLEFT = 0x02
LCD_ENTRYSHIFTINCREMENT = 0x01
LCD_ENTRYSHIFTDECREMENT = 0x00
# Control flags
LCD_DISPLAYON = 0x04
LCD_DISPLAYOFF = 0x00
LCD_CURSORON = 0x02
LCD_CURSOROFF = 0x00
LCD_BLINKON = 0x01
LCD_BLINKOFF = 0x00
# Move flags
LCD_DISPLAYMOVE = 0x08
LCD_CURSORMOVE = 0x00
LCD_MOVERIGHT = 0x04
LCD_MOVELEFT = 0x00
# Function set flags
LCD_8BITMODE = 0x10
LCD_4BITMODE = 0x00
LCD_4LINE = 0x54
LCD_3LINE = 0x14
LCD_2LINE = 0x40
LCD_1LINE = 0x00
LCD_5x10DOTS = 0x04
LCD_5x8DOTS = 0x00
# Offset for up to 4 rows.
LCD_ROW_OFFSETS = (0x00, 0x40, 0x14, 0x54)
# Char LCD plate GPIO numbers.
LCD_PLATE_RS = 15
LCD_PLATE_RW = 14
LCD_PLATE_EN = 13
LCD_PLATE_D4 = 12
LCD_PLATE_D5 = 11
LCD_PLATE_D6 = 10
LCD_PLATE_D7 = 9
LCD_PLATE_RED = 6
LCD_PLATE_GREEN = 7
LCD_PLATE_BLUE = 8
# Char LCD plate button names.
SELECT = 0
RIGHT = 1
DOWN = 2
UP = 3
LEFT = 4
class Adafruit_CharLCD(object):
"""Class to represent and interact with an HD44780 character LCD display."""
def __init__(self, rs, en, d4, d5, d6, d7, cols, lines, backlight=None,
invert_polarity=True,
enable_pwm=False,
gpio=GPIO.get_platform_gpio(),
pwm=PWM.get_platform_pwm(),
initial_backlight=1.0):
"""Initialize the LCD. RS, EN, and D4...D7 parameters should be the pins
connected to the LCD RS, clock enable, and data line 4 through 7 connections.
The LCD will be used in its 4-bit mode so these 6 lines are the only ones
required to use the LCD. You must also pass in the number of columns and
lines on the LCD.
If you would like to control the backlight, pass in the pin connected to
the backlight with the backlight parameter. The invert_polarity boolean
controls if the backlight is one with a LOW signal or HIGH signal. The
default invert_polarity value is True, i.e. the backlight is on with a
LOW signal.
You can enable PWM of the backlight pin to have finer control on the
brightness. To enable PWM make sure your hardware supports PWM on the
provided backlight pin and set enable_pwm to True (the default is False).
The appropriate PWM library will be used depending on the platform, but
you can provide an explicit one with the pwm parameter.
The initial state of the backlight is ON, but you can set it to an
explicit initial state with the initial_backlight parameter (0 is off,
1 is on/full bright).
You can optionally pass in an explicit GPIO class,
for example if you want to use an MCP230xx GPIO extender. If you don't
pass in an GPIO instance, the default GPIO for the running platform will
be used.
"""
# Save column and line state.
self._cols = cols
self._lines = lines
# Save GPIO state and pin numbers.
self._gpio = gpio
self._rs = rs
self._en = en
self._d4 = d4
self._d5 = d5
self._d6 = d6
self._d7 = d7
# Save backlight state.
self._backlight = backlight
self._pwm_enabled = enable_pwm
self._pwm = pwm
self._blpol = not invert_polarity
# Setup all pins as outputs.
for pin in (rs, en, d4, d5, d6, d7):
gpio.setup(pin, GPIO.OUT)
# Setup backlight.
if backlight is not None:
if enable_pwm:
pwm.start(backlight, self._pwm_duty_cycle(initial_backlight))
else:
gpio.setup(backlight, GPIO.OUT)
gpio.output(backlight, self._blpol if initial_backlight else not self._blpol)
# Initialize the display.
self.write8(0x33)
self.write8(0x32)
# Initialize display control, function, and mode registers.
self.displaycontrol = LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF
self.displayfunction = LCD_4BITMODE | LCD_1LINE | LCD_2LINE | LCD_5x8DOTS
self.displaymode = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT
# Write registers.
self.write8(LCD_DISPLAYCONTROL | self.displaycontrol)
self.write8(LCD_FUNCTIONSET | self.displayfunction)
self.write8(LCD_ENTRYMODESET | self.displaymode) # set the entry mode
self.clear()
def home(self):
"""Move the cursor back to its home (first line and first column)."""
self.write8(LCD_RETURNHOME) # set cursor position to zero
self._delay_microseconds(3000) # this command takes a long time!
def clear(self):
"""Clear the LCD."""
self.write8(LCD_CLEARDISPLAY) # command to clear display
self._delay_microseconds(3000) # 3000 microsecond sleep, clearing the display takes a long time
def set_cursor(self, col, row):
"""Move the cursor to an explicit column and row position."""
# Clamp row to the last row of the display.
if row > self._lines:
row = self._lines - 1
# Set location.
self.write8(LCD_SETDDRAMADDR | (col + LCD_ROW_OFFSETS[row]))
def enable_display(self, enable):
"""Enable or disable the display. Set enable to True to enable."""
if enable:
self.displaycontrol |= LCD_DISPLAYON
else:
self.displaycontrol &= ~LCD_DISPLAYON
self.write8(LCD_DISPLAYCONTROL | self.displaycontrol)
def show_cursor(self, show):
"""Show or hide the cursor. Cursor is shown if show is True."""
if show:
self.displaycontrol |= LCD_CURSORON
else:
self.displaycontrol &= ~LCD_CURSORON
self.write8(LCD_DISPLAYCONTROL | self.displaycontrol)
def blink(self, blink):
"""Turn on or off cursor blinking. Set blink to True to enable blinking."""
if blink:
self.displaycontrol |= LCD_BLINKON
else:
self.displaycontrol &= ~LCD_BLINKON
self.write8(LCD_DISPLAYCONTROL | self.displaycontrol)
def move_left(self):
"""Move display left one position."""
self.write8(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVELEFT)
def move_right(self):
"""Move display right one position."""
self.write8(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVERIGHT)
def set_left_to_right(self):
"""Set text direction left to right."""
self.displaymode |= LCD_ENTRYLEFT
self.write8(LCD_ENTRYMODESET | self.displaymode)
def set_right_to_left(self):
"""Set text direction right to left."""
self.displaymode &= ~LCD_ENTRYLEFT
self.write8(LCD_ENTRYMODESET | self.displaymode)
def autoscroll(self, autoscroll):
"""Autoscroll will 'right justify' text from the cursor if set True,
otherwise it will 'left justify' the text.
"""
if autoscroll:
self.displaymode |= LCD_ENTRYSHIFTINCREMENT
else:
self.displaymode &= ~LCD_ENTRYSHIFTINCREMENT
self.write8(LCD_ENTRYMODESET | self.displaymode)
def message(self, text):
"""Write text to display. Note that text can include newlines."""
line = 0
# Iterate through each character.
for char in text:
# Advance to next line if character is a new line.
if char == '\n':
line += 1
# Move to left or right side depending on text direction.
col = 0 if self.displaymode & LCD_ENTRYLEFT > 0 else self._cols-1
self.set_cursor(col, line)
# Write the character to the display.
else:
self.write8(ord(char), True)
def set_backlight(self, backlight):
"""Enable or disable the backlight. If PWM is not enabled (default), a
non-zero backlight value will turn on the backlight and a zero value will
turn it off. If PWM is enabled, backlight can be any value from 0.0 to
1.0, with 1.0 being full intensity backlight.
"""
if self._backlight is not None:
if self._pwm_enabled:
self._pwm.set_duty_cycle(self._backlight, self._pwm_duty_cycle(backlight))
else:
self._gpio.output(self._backlight, self._blpol if backlight else not self._blpol)
def write8(self, value, char_mode=False):
"""Write 8-bit value in character or data mode. Value should be an int
value from 0-255, and char_mode is True if character data or False if
non-character data (default).
"""
# One millisecond delay to prevent writing too quickly.
self._delay_microseconds(1000)
# Set character / data bit.
self._gpio.output(self._rs, char_mode)
# Write upper 4 bits.
self._gpio.output_pins({ self._d4: ((value >> 4) & 1) > 0,
self._d5: ((value >> 5) & 1) > 0,
self._d6: ((value >> 6) & 1) > 0,
self._d7: ((value >> 7) & 1) > 0 })
self._pulse_enable()
# Write lower 4 bits.
self._gpio.output_pins({ self._d4: (value & 1) > 0,
self._d5: ((value >> 1) & 1) > 0,
self._d6: ((value >> 2) & 1) > 0,
self._d7: ((value >> 3) & 1) > 0 })
self._pulse_enable()
def create_char(self, location, pattern):
"""Fill one of the first 8 CGRAM locations with custom characters.
The location parameter should be between 0 and 7 and pattern should
provide an array of 8 bytes containing the pattern. E.g. you can easyly
design your custom character at http://www.quinapalus.com/hd44780udg.html
To show your custom character use eg. lcd.message('\x01')
"""
# only position 0..7 are allowed
location &= 0x7
self.write8(LCD_SETCGRAMADDR | (location << 3))
for i in range(8):
self.write8(pattern[i], char_mode=True)
def _delay_microseconds(self, microseconds):
# Busy wait in loop because delays are generally very short (few microseconds).
end = time.time() + (microseconds/1000000.0)
while time.time() < end:
pass
def _pulse_enable(self):
# Pulse the clock enable line off, on, off to send command.
self._gpio.output(self._en, False)
self._delay_microseconds(1) # 1 microsecond pause - enable pulse must be > 450ns
self._gpio.output(self._en, True)
self._delay_microseconds(1) # 1 microsecond pause - enable pulse must be > 450ns
self._gpio.output(self._en, False)
self._delay_microseconds(1) # commands need > 37us to settle
def _pwm_duty_cycle(self, intensity):
# Convert intensity value of 0.0 to 1.0 to a duty cycle of 0.0 to 100.0
intensity = 100.0*intensity
# Invert polarity if required.
if not self._blpol:
intensity = 100.0-intensity
return intensity
class Adafruit_RGBCharLCD(Adafruit_CharLCD):
"""Class to represent and interact with an HD44780 character LCD display with
an RGB backlight."""
def __init__(self, rs, en, d4, d5, d6, d7, cols, lines, red, green, blue,
gpio=GPIO.get_platform_gpio(),
invert_polarity=True,
enable_pwm=False,
pwm=PWM.get_platform_pwm(),
initial_color=(1.0, 1.0, 1.0)):
"""Initialize the LCD with RGB backlight. RS, EN, and D4...D7 parameters
should be the pins connected to the LCD RS, clock enable, and data line
4 through 7 connections. The LCD will be used in its 4-bit mode so these
6 lines are the only ones required to use the LCD. You must also pass in
the number of columns and lines on the LCD.
The red, green, and blue parameters define the pins which are connected
to the appropriate backlight LEDs. The invert_polarity parameter is a
boolean that controls if the LEDs are on with a LOW or HIGH signal. By
default invert_polarity is True, i.e. the backlight LEDs are on with a
low signal. If you want to enable PWM on the backlight LEDs (for finer
control of colors) and the hardware supports PWM on the provided pins,
set enable_pwm to True. Finally you can set an explicit initial backlight
color with the initial_color parameter. The default initial color is
white (all LEDs lit).
You can optionally pass in an explicit GPIO class,
for example if you want to use an MCP230xx GPIO extender. If you don't
pass in an GPIO instance, the default GPIO for the running platform will
be used.
"""
super(Adafruit_RGBCharLCD, self).__init__(rs, en, d4, d5, d6, d7,
cols,
lines,
enable_pwm=enable_pwm,
backlight=None,
invert_polarity=invert_polarity,
gpio=gpio,
pwm=pwm)
self._red = red
self._green = green
self._blue = blue
# Setup backlight pins.
if enable_pwm:
# Determine initial backlight duty cycles.
rdc, gdc, bdc = self._rgb_to_duty_cycle(initial_color)
pwm.start(red, rdc)
pwm.start(green, gdc)
pwm.start(blue, bdc)
else:
gpio.setup(red, GPIO.OUT)
gpio.setup(green, GPIO.OUT)
gpio.setup(blue, GPIO.OUT)
self._gpio.output_pins(self._rgb_to_pins(initial_color))
def _rgb_to_duty_cycle(self, rgb):
# Convert tuple of RGB 0-1 values to tuple of duty cycles (0-100).
red, green, blue = rgb
# Clamp colors between 0.0 and 1.0
red = max(0.0, min(1.0, red))
green = max(0.0, min(1.0, green))
blue = max(0.0, min(1.0, blue))
return (self._pwm_duty_cycle(red),
self._pwm_duty_cycle(green),
self._pwm_duty_cycle(blue))
def _rgb_to_pins(self, rgb):
# Convert tuple of RGB 0-1 values to dict of pin values.
red, green, blue = rgb
return { self._red: self._blpol if red else not self._blpol,
self._green: self._blpol if green else not self._blpol,
self._blue: self._blpol if blue else not self._blpol }
def set_color(self, red, green, blue):
"""Set backlight color to provided red, green, and blue values. If PWM
is enabled then color components can be values from 0.0 to 1.0, otherwise
components should be zero for off and non-zero for on.
"""
if self._pwm_enabled:
# Set duty cycle of PWM pins.
rdc, gdc, bdc = self._rgb_to_duty_cycle((red, green, blue))
self._pwm.set_duty_cycle(self._red, rdc)
self._pwm.set_duty_cycle(self._green, gdc)
self._pwm.set_duty_cycle(self._blue, bdc)
else:
# Set appropriate backlight pins based on polarity and enabled colors.
self._gpio.output_pins({self._red: self._blpol if red else not self._blpol,
self._green: self._blpol if green else not self._blpol,
self._blue: self._blpol if blue else not self._blpol })
def set_backlight(self, backlight):
"""Enable or disable the backlight. If PWM is not enabled (default), a
non-zero backlight value will turn on the backlight and a zero value will
turn it off. If PWM is enabled, backlight can be any value from 0.0 to
1.0, with 1.0 being full intensity backlight. On an RGB display this
function will set the backlight to all white.
"""
self.set_color(backlight, backlight, backlight)
class Adafruit_CharLCDPlate(Adafruit_RGBCharLCD):
"""Class to represent and interact with an Adafruit Raspberry Pi character
LCD plate."""
def __init__(self, address=0x20, busnum=I2C.get_default_bus(), cols=20, lines=4):
"""Initialize the character LCD plate. Can optionally specify a separate
I2C address or bus number, but the defaults should suffice for most needs.
Can also optionally specify the number of columns and lines on the LCD
(default is 20x4).
"""
# Configure MCP23017 device.
self._mcp = MCP.MCP23017(address=address, busnum=busnum)
# Set LCD R/W pin to low for writing only.
self._mcp.setup(LCD_PLATE_RW, GPIO.OUT)
self._mcp.output(LCD_PLATE_RW, GPIO.LOW)
# Set buttons as inputs with pull-ups enabled.
for button in (SELECT, RIGHT, DOWN, UP, LEFT):
self._mcp.setup(button, GPIO.IN)
self._mcp.pullup(button, True)
# Initialize LCD (with no PWM support).
super(Adafruit_CharLCDPlate, self).__init__(LCD_PLATE_RS, LCD_PLATE_EN,
LCD_PLATE_D4, LCD_PLATE_D5, LCD_PLATE_D6, LCD_PLATE_D7, cols, lines,
LCD_PLATE_RED, LCD_PLATE_GREEN, LCD_PLATE_BLUE, enable_pwm=False,
gpio=self._mcp)
def is_pressed(self, button):
"""Return True if the provided button is pressed, False otherwise."""
if button not in set((SELECT, RIGHT, DOWN, UP, LEFT)):
raise ValueError('Unknown button, must be SELECT, RIGHT, DOWN, UP, or LEFT.')
return self._mcp.input(button) == GPIO.LOW

The text you pass to lcd.message has four newlines in it due to being composed of four lines read from the file. But the LCD only has four lines, so it supports only text with up to three newlines. The fourth newline tries to move the cursor past the last row and causes the error. Remove the last newline (e.g. with .rstrip('\n')) to fix this.
msg = "%s%s%s%s" % (text1, text2, text3, text4)
lcd.message(msg.rstrip('\n'))
Edit: The library you're using tries to prevent this from being a problem even if you pass in too many newlines, but there's a bug in their check. In set_cursor:
# Clamp row to the last row of the display.
if row > self._lines:
row = self._lines - 1
The > should be >=.

Related

IDS Cameras: Pyueye python package set exposure parameter (is_SetAutoParameter function)

I am working with IDS-3240CP-NIR cameras and operating them with the pyueye package that accompanies them. Because these cameras are being used to calculate NDVI from imagery between two different cameras, I need to ensure they have equivalent parameters except for exposure, which will be auto adjusted for lighting conditions and adjusted for in post processing. The trouble is, I'm not sure how to set the auto exposure in pyueye to produce good results. When I use the IDS GUI software and flip on auto exposure, things look great. But when I try to do this in software, the imagery is way over exposed.
From what I can tell in the documentation, there are two ways to set the auto exposure: the pyueye.Is_SetAutoParameter function (https://en.ids-imaging.com/manuals/ids-software-suite/ueye-manual/4.93/en/is_setautoparameter.html?q=set+auto+parameter) and the pyueye.Is_AutoParameter function (https://en.ids-imaging.com/manuals/ids-software-suite/ueye-manual/4.93/en/is_autoparameterautoexposure.html?q=aes). The Is_AutoParameter function may be the superior method based on how the documentation talks about it, but I have not been able to figure out how to use this function (I believe I am sending the wrong type of pval's). Instead, I've been setting the auto shutter speed through the Is_SetAutoParameter, but results are still overexposed.
Here is some of the code I am running:
hCam = ueye.HIDS(1)
sInfo = ueye.SENSORINFO()
cInfo = ueye.CAMINFO()
pcImageMemory = ueye.c_mem_p()
MemID = ueye.int()
rectAOI = ueye.IS_RECT()
pitch = ueye.INT()
nBitsPerPixel = ueye.INT(32)
channels = 4
m_nColorMode = ueye.IS_CM_RGBY8_PACKED
bytes_per_pixel = int(nBitsPerPixel / 8)
# Camera Init
nRet = ueye.is_InitCamera(hCam, None)
if nRet != ueye.IS_SUCCESS:
print("is_InitCamera1 ERROR")
# Get Sensor Info
nRet = ueye.is_GetSensorInfo(hCam, sInfo)
if nRet != ueye.IS_SUCCESS:
print("is_GetSensorInfo ERROR")
print(sInfo.strSensorName)
# Set Display Mode
nRet = ueye.is_SetDisplayMode(hCam, ueye.IS_SET_DM_DIB)
# Set Color mode
nRet = ueye.is_SetColorMode(hCam, ueye.IS_CM_RGBY8_PACKED)
# Area of Interest
print("rectAOI type:")
print(type(rectAOI))
nRet = ueye.is_AOI(hCam, ueye.IS_AOI_IMAGE_GET_AOI, rectAOI, ueye.sizeof(rectAOI))
if nRet != ueye.IS_SUCCESS:
print("is_AOI ERROR")
# Define Width and Height
width = rectAOI.s32Width
height = rectAOI.s32Height
# Prints out some information about the camera and the sensor
print("Camera 1")
print("Camera model:\t\t", sInfo.strSensorName.decode('utf-8'))
print("Camera serial no.:\t", cInfo.SerNo.decode('utf-8'))
print("Maximum image width:\t", width)
print("Maximum image height:\t", height)
print()
# Allocate Image Memory
nRet = ueye.is_AllocImageMem(hCam, width, height, nBitsPerPixel, pcImageMemory, MemID)
if nRet != ueye.IS_SUCCESS:
print("is_AllocImageMem ERROR")
# Add to Sequence
nRet = ueye.is_AddToSequence(hCam, pcImageMemory, MemID)
if nRet != ueye.IS_SUCCESS:
print("is_AddToSequence ERROR")
# Capture Video
nRet = ueye.is_CaptureVideo(hCam, ueye.IS_DONT_WAIT)
if nRet != ueye.IS_SUCCESS:
print("is_CaptureVideo ERROR")
# Inquire Image Memory
nRet = ueye.is_InquireImageMem(hCam, pcImageMemory, MemID, width, height, nBitsPerPixel, pitch)
if nRet != ueye.IS_SUCCESS:
print("is_InquireImageMem ERROR")
# Image Display
array = [0]
# Generate Image File Name
timestr = str(datetime.now().strftime('%Y-%m-%d_%H:%M:%S'))
image_name = "/home/pi/LakeWheelerTurf/TurfImages/turfcam1_" + timestr + ".bmp"
pval1 = ctypes.c_double(1)
pval2 = ctypes.c_double(0)
nRet = ueye.is_SetAutoParameter(hCam, ueye.IS_SET_ENABLE_AUTO_SHUTTER, pval1, pval2)
# Continuous image display
photo_counter = 0
while (nRet == ueye.IS_SUCCESS):
pval1 = ctypes.c_double(1)
pval2 = ctypes.c_double(0)
nRet = ueye.is_SetAutoParameter(hCam, ueye.IS_SET_ENABLE_AUTO_SHUTTER, pval1, pval2)
nRet = ueye.is_SetAutoParameter(hCam, ueye.IS_GET_ENABLE_AUTO_SHUTTER, pval3, pval2)
print(pval3.value)
#Below is my attempt to get the is_AutoParameter function to work (unsuccessful)
#pval1 = ctypes.c_void_p(1)
#pval2 = ctypes.sizeof(pval1)
#nRet = ueye.is_AutoParameter(hCam, ueye.IS_AES_CMD_SET_ENABLE, pval1, pval_sz)
#if nRet != ueye.IS_SUCCESS:
# print("AES ERROR")
while array[0] == 0:
array = ueye.get_data(pcImageMemory, width.value, height.value, nBitsPerPixel.value, pitch.value,
copy=False)
frame = np.reshape(array, (height.value, width.value, bytes_per_pixel))
photo_counter += 1
time.sleep(1)
if photo_counter >= 10:
break
matplotlib.pyplot.imsave(image_name, frame)
ueye.is_FreeImageMem(hCam, pcImageMemory, MemID)
ueye.is_ExitCamera(hCam)
return image_name
I'd be hugely appreciative if anyone can help me figure this out, I've been working on this for ages to no avail. Below are some samples of the imagery I'm able to get out of the GUI vs pyueye. Thanks!
The pyueye Python porting is missing an implementation for is_SetExposureTime, which support both explicit and automatic exposure control. I assume it's a bug.
I provide here my own implementation for IdsCamera class which contains the missing implementation, plus easy-to-use methods for getting and setting the exposure.
class IdsCamera:
_is_SetExposureTime = ueye._bind("is_SetExposureTime",
[ueye.ctypes.c_uint, ueye.ctypes.c_double,
ueye.ctypes.POINTER(ueye.ctypes.c_double)], ueye.ctypes.c_int)
IS_GET_EXPOSURE_TIME = 0x8000
#staticmethod
def is_SetExposureTime(hCam, EXP, newEXP):
"""
Description
The function is_SetExposureTime() sets the with EXP indicated exposure time in ms. Since this
is adjustable only in multiples of the time, a line needs, the actually used time can deviate from
the desired value.
The actual duration adjusted after the call of this function is readout with the parameter newEXP.
By changing the window size or the readout timing (pixel clock) the exposure time set before is changed also.
Therefore is_SetExposureTime() must be called again thereafter.
Exposure-time interacting functions:
- is_SetImageSize()
- is_SetPixelClock()
- is_SetFrameRate() (only if the new image time will be shorter than the exposure time)
Which minimum and maximum values are possible and the dependence of the individual
sensors is explained in detail in the description to the uEye timing.
Depending on the time of the change of the exposure time this affects only with the recording of
the next image.
:param hCam: c_uint (aka c-type: HIDS)
:param EXP: c_double (aka c-type: DOUBLE) - New desired exposure-time.
:param newEXP: c_double (aka c-type: double *) - Actual exposure time.
:returns: IS_SUCCESS, IS_NO_SUCCESS
Notes for EXP values:
- IS_GET_EXPOSURE_TIME Returns the actual exposure-time through parameter newEXP.
- If EXP = 0.0 is passed, an exposure time of (1/frame rate) is used.
- IS_GET_DEFAULT_EXPOSURE Returns the default exposure time newEXP Actual exposure time
- IS_SET_ENABLE_AUTO_SHUTTER : activates the AutoExposure functionality.
Setting a value will deactivate the functionality.
(see also 4.86 is_SetAutoParameter).
"""
_hCam = ueye._value_cast(hCam, ueye.ctypes.c_uint)
_EXP = ueye._value_cast(EXP, ueye.ctypes.c_double)
ret = IdsCamera._is_SetExposureTime(_hCam, _EXP, ueye.ctypes.byref(newEXP) if newEXP is not None else None)
return ret
def __init__(self, selector=''):
self.hCam = ueye.HIDS(0) # 0: first available camera; 1-254: The camera with the specified camera ID
self.sInfo = ueye.SENSORINFO()
self.cInfo = ueye.CAMINFO()
self.pcImageMemory = ueye.c_mem_p()
self.MemID = ueye.INT()
self.rectAOI = ueye.IS_RECT()
self.pitch = ueye.INT()
self.nBitsPerPixel = ueye.INT()
self.m_nColorMode = ueye.INT()
self.bytes_per_pixel = 0
self.width = ueye.INT()
self.height = ueye.INT()
self.size = (-1, -1)
self.ok = False
self.error_str = ''
self.last_frame = None
def _error(self, err_str):
self.error_str = err_str
return
def connect(self):
self.error_str = ''
# Starts the driver and establishes the connection to the camera
rc = ueye.is_InitCamera(self.hCam, None)
if rc != ueye.IS_SUCCESS:
return self._error("is_InitCamera ERROR")
# Reads out the data hard-coded in the non-volatile camera memory
# and writes it to the data structure that cInfo points to
rc = ueye.is_GetCameraInfo(self.hCam, self.cInfo)
if rc != ueye.IS_SUCCESS:
return self._error("is_GetCameraInfo ERROR")
# You can query additional information about the sensor type used in the camera
rc = ueye.is_GetSensorInfo(self.hCam, self.sInfo)
if rc != ueye.IS_SUCCESS:
return self._error("is_GetSensorInfo ERROR")
rc = ueye.is_ResetToDefault(self.hCam)
if rc != ueye.IS_SUCCESS:
return self._error("is_ResetToDefault ERROR")
# Set display mode to DIB
rc = ueye.is_SetDisplayMode(self.hCam, ueye.IS_SET_DM_DIB)
# Set the right color mode
if int.from_bytes(self.sInfo.nColorMode.value, byteorder='big') == ueye.IS_COLORMODE_BAYER:
# setup the color depth to the current windows setting
ueye.is_GetColorDepth(self.hCam, self.nBitsPerPixel, self.m_nColorMode)
self.bytes_per_pixel = int(self.nBitsPerPixel / 8)
print("IS_COLORMODE_BAYER: ", )
elif int.from_bytes(self.sInfo.nColorMode.value, byteorder='big') == ueye.IS_COLORMODE_CBYCRY:
# for color camera models use RGB32 mode
self.m_nColorMode = ueye.IS_CM_BGRA8_PACKED
self.nBitsPerPixel = ueye.INT(32)
self.bytes_per_pixel = int(self.nBitsPerPixel / 8)
print("IS_COLORMODE_CBYCRY: ", )
elif int.from_bytes(self.sInfo.nColorMode.value, byteorder='big') == ueye.IS_COLORMODE_MONOCHROME:
# for color camera models use RGB32 mode
self.m_nColorMode = ueye.IS_CM_MONO8
self.nBitsPerPixel = ueye.INT(8)
self.bytes_per_pixel = int(self.nBitsPerPixel / 8)
print("IS_COLORMODE_MONOCHROME: ", )
else:
# for monochrome camera models use Y8 mode
self.m_nColorMode = ueye.IS_CM_MONO8
self.nBitsPerPixel = ueye.INT(8)
self.bytes_per_pixel = int(self.nBitsPerPixel / 8)
print("Color mode: not identified")
print("\tm_nColorMode: \t\t", self.m_nColorMode)
print("\tnBitsPerPixel: \t\t", self.nBitsPerPixel)
print("\tbytes_per_pixel: \t\t", self.bytes_per_pixel)
print()
# Can be used to set the size and position of an "area of interest"(AOI) within an image
rc = ueye.is_AOI(self.hCam, ueye.IS_AOI_IMAGE_GET_AOI, self.rectAOI, ueye.sizeof(self.rectAOI))
if rc != ueye.IS_SUCCESS:
return self._error("is_AOI ERROR")
self.width = self.rectAOI.s32Width
self.height = self.rectAOI.s32Height
self.size = (self.width.value, self.height.value)
# Prints out some information about the camera and the sensor
print("Camera model:\t\t", self.sInfo.strSensorName.decode('utf-8'))
print("Camera serial no.:\t", self.cInfo.SerNo.decode('utf-8'))
print("Camera image size:\t", str(self.size))
print()
# Allocates an image memory for an image having its dimensions defined by width and height
# and its color depth defined by nBitsPerPixel
rc = ueye.is_AllocImageMem(self.hCam, self.width, self.height,
self.nBitsPerPixel, self.pcImageMemory, self.MemID)
if rc != ueye.IS_SUCCESS:
return self._error("is_AllocImageMem ERROR")
else:
# Makes the specified image memory the active memory
rc = ueye.is_SetImageMem(self.hCam, self.pcImageMemory, self.MemID)
if rc != ueye.IS_SUCCESS:
return self._error("is_SetImageMem ERROR")
else:
# Set the desired color mode
rc = ueye.is_SetColorMode(self.hCam, self.m_nColorMode)
# Activates the camera's live video mode (free run mode)
rc = ueye.is_CaptureVideo(self.hCam, ueye.IS_DONT_WAIT)
if rc != ueye.IS_SUCCESS:
return self._error("is_CaptureVideo ERROR")
# Enables the queue mode for existing image memory sequences
rc = ueye.is_InquireImageMem(self.hCam, self.pcImageMemory, self.MemID,
self.width, self.height, self.nBitsPerPixel, self.pitch)
if rc != ueye.IS_SUCCESS:
return self._error("is_InquireImageMem ERROR")
else:
print("IDS camera: connection ok")
self.ok = True
def grab_image(self):
if not self.ok:
return None
# In order to display the image in an OpenCV window we need to...
# ...extract the data of our image memory
array = ueye.get_data(self.pcImageMemory, self.width, self.height, self.nBitsPerPixel, self.pitch, copy=False)
# ...reshape it in an numpy array...
frame = np.reshape(array, (self.height.value, self.width.value, self.bytes_per_pixel))
# ...resize the image by a half
# frame = cv2.resize(frame, (0, 0), fx=0.5, fy=0.5)
self.last_frame = frame
return frame
def disconnect(self):
# Releases an image memory that was allocated using is_AllocImageMem() and removes it from the driver management
ueye.is_FreeImageMem(self.hCam, self.pcImageMemory, self.MemID)
# Disables the hCam camera handle and releases the data structures and memory areas taken up by the uEye camera
ueye.is_ExitCamera(self.hCam)
def set_camera_exposure(self, level_us):
"""
:param level_us: exposure level in micro-seconds, or zero for auto exposure
note that you can never exceed 1000000/fps, but it is possible to change the fps
"""
p1 = ueye.DOUBLE()
if level_us == 0:
rc = IdsCamera._is_SetExposureTime(self.hCam, ueye.IS_SET_ENABLE_AUTO_SHUTTER, p1)
print(f'set_camera_exposure: set to auto')
else:
ms = ueye.DOUBLE(level_us / 1000)
rc = IdsCamera._is_SetExposureTime(self.hCam, ms, p1)
print(f'set_camera_exposure: requested {ms.value}, got {p1.value}')
def get_camera_exposure(self, force_val=False):
"""
returns the current exposure time in micro-seconds, or zero if auto exposure is on
:param force_val: if True, will return level of exposure even if auto exposure is on
"""
p1 = ueye.DOUBLE()
p2 = ueye.DOUBLE()
# we dump both auto-gain and auto exposure states:
rc = ueye.is_SetAutoParameter(self.hCam, ueye.IS_GET_ENABLE_AUTO_GAIN, p1, p2)
print(f'IS_GET_ENABLE_AUTO_GAIN={p1.value == 1}')
rc = ueye.is_SetAutoParameter(self.hCam, ueye.IS_GET_ENABLE_AUTO_SHUTTER, p1, p2)
print(f'IS_GET_ENABLE_AUTO_SHUTTER={p1.value == 1}')
if (not force_val) and p1.value == 1:
return 0 # auto exposure
rc = IdsCamera._is_SetExposureTime(self.hCam, IdsCamera.IS_GET_EXPOSURE_TIME, p1)
print(f'IS_GET_EXPOSURE_TIME={p1.value}')
return p1.value * 1000

Stepper motor quits working after a short while, even though the code still works. (Raspberry Pi)

I am running the following code on my Raspberry Pi 4B, using Python 3.7.3:
from time import sleep
import RPi.GPIO as GPIO
import math
from watchgod import watch
g=open("/home/pi/Desktop/Int2/DesHeight.txt","r")
DesHeight = g.readline()
DesHeight1=float(DesHeight)
print(DesHeight1)
GPIO.cleanup()
DIR = 20
STEP = 21
CW = 0
CCW = 1
TX_ENC = 15
SPR = 200 # Steps per Rev [CONSTANT]
delay = .001 #Seconds per stepper pulse [CONSTANT]
ratio=24 #gear ratio [CONSTANT]
f = open("height.txt", "r")
y0 = f.readline()
y0 = float(y0)
d=1.25
r=d/2
theta0=y0/(2*math.pi*r)
GPIO.setmode(GPIO.BCM) # GPIO numbering
GPIO.setup(12,GPIO.OUT)
GPIO.output(12,1) # Turning on the "Enable Input"
GPIO.setup(DIR, GPIO.OUT)
GPIO.setup(STEP, GPIO.OUT)
GPIO.output(DIR, CW) # Setting CW Direction
GPIO.setup(TX_ENC, GPIO.IN) # Encoder input setup
GPIO.add_event_detect(TX_ENC, GPIO.BOTH)
Tx = 0
MODE = (16,17,18,19) # GPIO 16 is the standby input. It needs to be high for anything to move
GPIO.setup(MODE,GPIO.OUT)
RESOLUTION = {'Standby':(0,0,0,0),
'Full':(1,0,0,0),
'Half':(1,1,0,0),
'1/4':(1,0,1,0),
'1/8':(1,1,1,0),
'1/16':(1,0,0,1),
'1/32':(1,1,0,1),
'1/64':(1,0,1,1),
'1/128':(1,1,1,1)}
GPIO.output(MODE,RESOLUTION['Full'])
ass = (0,0,0,0)
pp = list(ass)
pp[0] = GPIO.input(MODE[0])
pp[1] = GPIO.input(MODE[1])
pp[2] = GPIO.input(MODE[2])
pp[3] = GPIO.input(MODE[3])
ass = tuple(pp)
if(ass == RESOLUTION['Standby']):
res = 0
elif(ass == RESOLUTION['Full']):
res = 200
elif(ass == RESOLUTION['Half']):
res = 400
elif(ass == RESOLUTION['1/4']):
res = 800
elif(ass == RESOLUTION['1/8']):
res = 1600
elif(ass == RESOLUTION['1/16']):
res = 3200
elif(ass == RESOLUTION['1/32']):
res = 6400
elif(ass == RESOLUTION['1/64']):
res=12800
elif(ass == RESOLUTION['1/128']):
res=25600
else:
print("Whoops lol")
while(True):
for changes in watch('/home/pi/Desktop/Int2/DesHeight.txt'):
g=open("/home/pi/Desktop/Int2/DesHeight.txt","r")
DesHeight = g.readline()
DesHeight1=float(DesHeight)
f = open("height.txt", "r")
y0 = f.readline()
y0=float(y0)
while(abs(y0-DesHeight1)>.001):
if(y0 < DesHeight1):
while(y0 < DesHeight1):
GPIO.output(DIR,CCW)
GPIO.output(STEP, GPIO.HIGH)
sleep(delay)
GPIO.output(STEP, GPIO.LOW)
sleep(delay)
Tx = Tx + 1
theta0=theta0+1/res*1/ratio#*1/gearratio
y0 = y0+2*5/4*14/15*.9944*math.pi*(1/res*1/ratio)*r
else:
while(y0 > DesHeight1):
if(y0>0):
GPIO.output(DIR,CW)
GPIO.output(STEP, GPIO.HIGH)
sleep(delay)
GPIO.output(STEP, GPIO.LOW)
sleep(delay)
Tx = Tx - 1
theta0=theta0-1/res*1/ratio#*1/gearratio
y0 = y0-2*5/4*14/15*.9944*math.pi*(1/res*1/ratio)*r
y0 = str(y0)
print(y0)
f.close()
f = open('height.txt', 'w')
f.write(y0)
f.close()
Essentially, what I am trying to do is read the height of a machine from a text file, then compare it with the desired height, as written in a separate text file. When the code detects a change in the desired height, it checks to make sure that the actual height and the desired height are within 1/1000 of an inch of each other, and if not, it moves a NEMA-17 motor until this condition is met.
The problem I am encountering is that if this code is left to run for a little bit (usually around 40 seconds) the stepper motor ceases to run when I change the desired height. The code itself runs, taking as long as expected to "move" the motor and also calculating the height and returning to the top of the while loop, but the motor itself remains stagnant. This does not occur if new changes to the desired height file are implemented immediately. I am at a loss as to what this could be and could use some help.
Okay, so following the advice of RootTwo, and learning how to use an oscilloscope, I was able to isolate part of the problem and determine a solution. Either the pi or the driver quits supplying a voltage to the motor after about 90 seconds of inactivity. I was not able to find a way to continuously keep the motor at holding torque, but I was able to make it so that the code will reinitialize after a long break by moving the while(True): and watchdog command to the beginning of the code. My problem is technically solved, but my question is yet unanswered: Why does my pi stop giving a signal to the driver board?

How to Auto scale y and x axis of a graph in real time python

I modified the code of this tutorial to create my own real time plot:
https://learn.sparkfun.com/tutorials/graph-sensor-data-with-python-and-matplotlib/speeding-up-the-plot-animation
I needed to plot the data from a proximity sensor in real time, the data is sent through USB cable to the computer and I read it with the serial port, so the code is already working how I wanted to, but I also want to modify the y-axis and x-axis, not let it static, because sometimes the peaks are 3000 and sometimes 2000 and when the sensor is not being touched, the peaks are at around 200 because it also detects the ambient light. any clue how can I make it?
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import serial
# Data from serial port
port = 'COM8'
baudrate = 9600
tout = 0.01 # Miliseconds
# Time to update the data of the sensor signal real time Rs=9600baud T=1/Rs
tiempo = (1 / baudrate) * 1000
# Parameters
x_len = 200 # Number of points to display
y_range = [20000, 40000] # Range of Y values to display
# Create figure for plotting
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
xs = list(range(0, x_len))
ys = [0] * x_len
ax.set_ylim(y_range)
# Create a blank line. We will update the line in animate
line, = ax.plot(xs, ys)
# Markers
startMarker = 60 # Start marker "<"
endMarker = 62 # End marker ">"
# Begin Arduino communication, Port COM8, speed 9600
serialPort = serial.Serial(port, baudrate, timeout=tout)
# Begin to save the arduino data
def arduinodata():
global startMarker, endMarker
ck = ""
x = "z" # any value that is not an end- or startMarker
bytecount = -1 # to allow for the fact that the last increment will be one too many
# wait for the start character
while ord(x) != startMarker:
x = serialPort.read()
# save data until the end marker is found
while ord(x) != endMarker:
if ord(x) != startMarker:
ck = ck + x.decode()
bytecount += 1
x = serialPort.read()
return ck
def readarduino():
# Wait until the Arduino sends '<Arduino Ready>' - allows time for Arduino reset
# It also ensures that any bytes left over from a previous message are discarded
msg = ""
while msg.find("<Arduino is ready>") == -1:
while serialPort.inWaiting() == 0:
pass
# delete for example the "\r\n" that may contain the message
msg = arduinodata()
msg = msg.split("\r\n")
msg = ''.join(msg)
# If the sensor send very big numbers over 90000 they will be deleted
if msg and len(msg) <= 5:
msg = int(msg)
return msg
elif msg and len(msg) >= 4:
msg = int(msg)
return msg
# This function is called periodically from FuncAnimation
def animate(i, ys):
# Read pulse from PALS2
pulse = readarduino()
# Add y to list
ys.append(pulse)
# Limit x and y lists to set number of items
ys = ys[-x_len:]
# Update line with new Y values
line.set_ydata(ys)
return line,
# Plot labels
plt.title('Heart frequency vs Time')
plt.ylabel('frequency ')
plt.xlabel('Samples')
# Set up plot to call animate() function periodically
ani = animation.FuncAnimation(fig, animate, fargs=(ys,), interval=tiempo, blit=True)
plt.show()
plt.close()
serialPort.close()
This is how the graph looks like, the x and y axis are always the same:
If you want to autoscale the y-axis, then you can simply adjust the y-axis limits in your animate() function:
def animate(i, ys):
# Read pulse from PALS2
pulse = readarduino()
# Add y to list
ys.append(pulse)
# Limit x and y lists to set number of items
ys = ys[-x_len:]
ymin = np.min(ys)
ymax = np.max(ys)
ax.set_ylim(ymin, ymax)
# Update line with new Y values
line.set_ydata(ys)
return line,
HOWEVER, the result will not be what you expect if you use blit=True. This is because blitting is attempting to only draw the parts of the graphs that have changed, and the ticks on the axes are excluded from that. If you need to change the limits and therefore the ticks, you should use blit=False in your call to FuncAnimation. Note that you will encounter a performance hit since matplotlib will have to redraw the whole plot at every frame, but if you want to change the limits, there is no way around that.
So I made some changes to the last code of this link https://www.learnpyqt.com/courses/graphics-plotting/plotting-pyqtgraph/
and I could solve the problem. x and Y axis are now autoscaling
import PyQt5
from PyQt5 import QtWidgets, QtCore
from pyqtgraph import PlotWidget, plot
import pyqtgraph as pg
import sys # We need sys so that we can pass argv to QApplication
import os
from random import randint
import serial
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, *args, **kwargs):
super(MainWindow, self).__init__(*args, **kwargs)
self.graphWidget = pg.PlotWidget()
self.setCentralWidget(self.graphWidget)
# Data from serial port
self.port = 'COM8'
self.baudrate = 9600
self.tout = 0.01 # Miliseconds
# Time to update the data of the sensor signal Rs=9600baud T=1/Rs
self.tiempo = (1 / self.baudrate) * 1000
self.x_len = 200
self.x = list(range(0, self.x_len)) # 100 time points
self.y = [0] * self.x_len # 100 data points
self.graphWidget.setBackground('w')
# Markers
self.startMarker = 60 # Start marker "<"
self.endMarker = 62 # End marker ">"
# Begin Arduino communication, Port COM8, speed 9600
self.serialPort = serial.Serial(self.port, self.baudrate, timeout=self.tout)
pen = pg.mkPen(color=(255, 0, 0))
self.data_line = self.graphWidget.plot(self.x, self.y, pen=pen)
self.timer = QtCore.QTimer()
self.timer.setInterval(self.tiempo)
self.timer.timeout.connect(self.update_plot_data)
self.timer.start()
# Begin to save the arduino data
def arduinodata(self):
ck = ""
x = "z" # any value that is not an end- or startMarker
bytecount = -1 # to allow for the fact that the last increment will be one too many
# wait for the start character
while ord(x) != self.startMarker:
x = self.serialPort.read()
# save data until the end marker is found
while ord(x) != self.endMarker:
if ord(x) != self.startMarker:
ck = ck + x.decode()
bytecount += 1
x = self.serialPort.read()
return ck
def readarduino(self):
# Wait until the Arduino sends '<Arduino Ready>' - allows time for Arduino reset
# It also ensures that any bytes left over from a previous message are discarded
msg = ""
while msg.find("<Arduino is ready>") == -1:
while self.serialPort.inWaiting() == 0:
pass
# delete for example the "\r\n" that may contain the message
msg = self.arduinodata()
msg = msg.split("\r\n")
msg = ''.join(msg)
# If the sensor send very big numbers over 90000 they will be deleted
if msg and len(msg) <= 5:
msg = int(msg)
return msg
elif msg and len(msg) >= 4:
msg = int(msg)
return msg
def update_plot_data(self):
pulse = self.readarduino()
self.x = self.x[1:] # Remove the first y element.
self.x.append(self.x[-1] + 1) # Add a new value 1 higher than the last.
self.y = self.y[1:] # Remove the first
self.y.append(pulse) # Add a new random value.
self.data_line.setData(self.x, self.y) # Update the data.
app = QtWidgets.QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())

Read PT1000 with Max31865 on Raspberry

Using github and a tutorial I adapt the given Code to read the temperature on my raspberry using an PT1000 sensor.
I am using the mode "GPIO.BOARD", adapt the "writeRegister(0, 0xB2)" to "writeRegister(0, 0xA2)" and I also changed the programstart.
Here's my current code:
#!/usr/bin/python
# The MIT License (MIT)
#
# Copyright (c) 2015 Stephen P. Smith
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import time, math
import RPi.GPIO as GPIO
# import numpy
"""Reading Temperature from the MAX31865 with GPIO using
the Raspberry Pi. Any pins can be used.
Numpy can be used to completely solve the Callendar-Van Dusen equation
but it slows the temp reading down. I commented it out in the code.
Both the quadratic formula using Callendar-Van Dusen equation (ignoring the
3rd and 4th degree parts of the polynomial) and the straight line approx.
temperature is calculated with the quadratic formula one being the most accurate.
"""
def setupGPIO():
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BOARD)
GPIO.setup(csPin, GPIO.OUT)
GPIO.setup(misoPin, GPIO.IN)
GPIO.setup(mosiPin, GPIO.OUT)
GPIO.setup(clkPin, GPIO.OUT)
GPIO.output(csPin, GPIO.HIGH)
GPIO.output(clkPin, GPIO.LOW)
GPIO.output(mosiPin, GPIO.LOW)
def readTemp():
# b10000000 = 0x80
# 0x8x to specify 'write register value'
# 0xx0 to specify 'configuration register'
#
# 0b10110010 = 0xB2
# Config Register - https://datasheets.maximintegrated.com/en/ds/MAX31865.pdf
# ---------------
# bit 7: Vbias -> 1 (ON)
# bit 6: Conversion Mode -> 0 (MANUAL)
# bit 5: 1-shot ->1 (ON)
# bit 4: 3-wire select -> 1 (3 wire config) (0 for 2-/4-wire)
# bit 3-2: fault detection cycle -> 0 (none)
# bit 1: fault status clear -> 1 (clear any fault)
# bit 0: 50/60 Hz filter select -> 0 (60Hz)
#
# 0b11010010 or 0xD2 for continuous auto conversion
# at 60Hz (faster conversion)
# one shot
writeRegister(0, 0xA2)
# conversion time is less than 100ms
time.sleep(.1) # give it 100ms for conversion
# read all registers
out = readRegisters(0, 8)
conf_reg = out[0]
print("Config register byte: %x" % conf_reg , " [HEX]")
[rtd_msb, rtd_lsb] = [out[1], out[2]]
rtd_ADC_Code = ((rtd_msb << 8) | rtd_lsb) >> 1
temp_C = calcPT1000Temp(rtd_ADC_Code)
# High fault threshold
[hft_msb, hft_lsb] = [out[3], out[4]]
hft = ((hft_msb << 8) | hft_lsb) >> 1
# Low fault threshold
[lft_msb, lft_lsb] = [out[5], out[6]]
lft = ((lft_msb << 8) | lft_lsb) >> 1
print ("High fault threshold: ", hft , " --- Low fault threshold: " , lft)
status = out[7]
#
# 10 Mohm resistor is on breakout board to help
# detect cable faults
# bit 7: RTD High Threshold / cable fault open
# bit 6: RTD Low Threshold / cable fault short
# bit 5: REFIN- > 0.85 x VBias -> must be requested
# bit 4: REFIN- < 0.85 x VBias (FORCE- open) -> must be requested
# bit 3: RTDIN- < 0.85 x VBias (FORCE- open) -> must be requested
# bit 2: Overvoltage / undervoltage fault
# bits 1,0 don't care
# print "Status byte: %x" % status
if ((status & 0x80) == 1):
raise FaultError("High threshold limit (Cable fault/open)")
if ((status & 0x40) == 1):
raise FaultError("Low threshold limit (Cable fault/short)")
if ((status & 0x04) == 1):
raise FaultError("Overvoltage or Undervoltage Error")
def writeRegister(regNum, dataByte):
GPIO.output(csPin, GPIO.LOW)
# 0x8x to specify 'write register value'
addressByte = 0x80 | regNum;
# first byte is address byte
sendByte(addressByte)
# the rest are data bytes
sendByte(dataByte)
GPIO.output(csPin, GPIO.HIGH)
def readRegisters(regNumStart, numRegisters):
out = []
GPIO.output(csPin, GPIO.LOW)
# 0x to specify 'read register value'
sendByte(regNumStart)
for byte in range(numRegisters):
data = recvByte()
out.append(data)
GPIO.output(csPin, GPIO.HIGH)
return out
def sendByte(byte):
for bit in range(8):
GPIO.output(clkPin, GPIO.HIGH)
if (byte & 0x80):
GPIO.output(mosiPin, GPIO.HIGH)
else:
GPIO.output(mosiPin, GPIO.LOW)
byte <<= 1
GPIO.output(clkPin, GPIO.LOW)
def recvByte():
byte = 0x00
for bit in range(8):
GPIO.output(clkPin, GPIO.HIGH)
byte <<= 1
if GPIO.input(misoPin):
byte |= 0x1
GPIO.output(clkPin, GPIO.LOW)
return byte
def calcPT1000Temp(RTD_ADC_Code):
############# PT1000 #############
R_REF = 400.0 # Reference Resistor
Res0 = 1000.0; # Resistance at 0 degC for 400ohm R_Ref
a = .00381
b = -.000000602
# c = -4.18301e-12 # for -200 <= T <= 0 (degC)
c = -0.000000000006
# c = 0 # for 0 <= T <= 850 (degC)
# c = 0 # for 0 <= T <= 850 (degC)
"""
############# PT100 #############
R_REF = 400.0 # Reference Resistor
Res0 = 100.0; # Resistance at 0 degC for 400ohm R_Ref
a = .00390830
b = -.000000577500
# c = -4.18301e-12 # for -200 <= T <= 0 (degC)
c = -0.00000000000418301
# c = 0 # for 0 <= T <= 850 (degC)
"""
Res_RTD = (RTD_ADC_Code * R_REF) / 32768.0 # PT1000 Resistance
Res_RTD = round(Res_RTD, 3)
print("RTD ADC Code: ", RTD_ADC_Code , " --- PT1000 Resistance: ", Res_RTD , " Ohms")
# Callendar-Van Dusen equation
# Res_RTD = Res0 * (1 + a*T + b*T**2 + c*(T-100)*T**3)
# Res_RTD = Res0 + a*Res0*T + b*Res0*T**2 # c = 0
# (c*Res0)T**4 - (c*Res0)*100*T**3
# + (b*Res0)*T**2 + (a*Res0)*T + (Res0 - Res_RTD) = 0
#
# quadratic formula:
# for 0 <= T <= 850 (degC)
temp_C = -(a * Res0) + math.sqrt(a * a * Res0 * Res0 - 4 * (b * Res0) * (Res0 - Res_RTD))
temp_C = temp_C / (2 * (b * Res0))
temp_C_line = (RTD_ADC_Code / 32.0) - 256.0
# removing numpy.roots will greatly speed things up
# temp_C_numpy = numpy.roots([c*Res0, -c*Res0*100, b*Res0, a*Res0, (Res0 - Res_RTD)])
# temp_C_numpy = abs(temp_C_numpy[-1])
print ("Straight Line Approx. Temp: ", temp_C_line , " --- Callendar-Van Dusen Temp (degC > 0): " , temp_C)
# print "Solving Full Callendar-Van Dusen using numpy: %f" % temp_C_numpy
if (temp_C < 0): # use straight line approximation if less than 0
# Can also use python lib numpy to solve cubic
# Should never get here in this application
temp_C = (RTD_ADC_Code / 32) - 256
return temp_C
###############################################################################################################################
# Programstart #
###############################################################################################################################
# Pin-Setup: (BCM: 8, 9, 10, 11)
csPin = 24
misoPin = 21
mosiPin = 19
clkPin = 23
setupGPIO()
while True:
tempC = readTemp()
time.sleep(1)
GPIO.cleanup()
class FaultError(Exception):
pass
The output is the following:
Config register byte: 80 [HEX]
RTD ADC Code: 1299 --- PT1000 Resistance: 15.857 Ohms
Straight Line Approx. Temp: -215.40625 --- Callendar-Van Dusen Temp (degC > 0): -248.54456939832218
The values of the "RTD ADC Code" and the "PT1000 Resistance" increase continuously.
I dont know whats the reason for this wrong behavior. I also tried different wiring-settings - Currently is use this one.

Why I cannot call func in another python file?

I make 2 python files: MineSweeping.py and CmdColor.py
And in MineSweeping.py I want to call a func defined in CmdColor.py to make the shell with color output, but failed.
The code of CmdColor.py is below:
#!/usr/bin/env python
#encoding: utf-8
import ctypes
STD_INPUT_HANDLE = -10
STD_OUTPUT_HANDLE= -11
STD_ERROR_HANDLE = -12
FOREGROUND_BLACK = 0x0
FOREGROUND_BLUE = 0x01 # text color contains blue.
FOREGROUND_GREEN= 0x02 # text color contains green.
FOREGROUND_RED = 0x04 # text color contains red.
FOREGROUND_INTENSITY = 0x08 # text color is intensified.
BACKGROUND_BLUE = 0x10 # background color contains blue.
BACKGROUND_GREEN= 0x20 # background color contains green.
BACKGROUND_RED = 0x40 # background color contains red.
BACKGROUND_INTENSITY = 0x80 # background color is intensified.
class Color:
std_out_handle = ctypes.windll.kernel32.GetStdHandle(STD_OUTPUT_HANDLE)
def set_cmd_color(self, color, handle=std_out_handle):
"""(color) -> bit
Example: set_cmd_color(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY)
"""
bool = ctypes.windll.kernel32.SetConsoleTextAttribute(handle, color)
return bool
def reset_color(self):
self.set_cmd_color(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE)
def print_red_text(self, print_text):
self.set_cmd_color(FOREGROUND_RED | FOREGROUND_INTENSITY)
print print_text
self.reset_color()
if __name__ == "__main__":
clr = Color()
clr.print_red_text('red')
clr.print_green_text('green')
clr.print_blue_text('blue')
clr.print_red_text_with_blue_bg('background')
And in MineSweeping.py I do so to call the func:
import CmdColor
...
clr = CmdColor.Color()
clr.print_red_text('red')
But the output string 'red' is white
If I merge the CmdColor.py into abc.py, then it can output a red string of 'red'.
The code can be found here:
https://github.com/tomxuetoy/Python_MineSweeping
Why?
Thanks!

Categories