A while ago I started getting into sim racing, but because I can't afford a wheel myself, I searched for ways I could emulate it, so I stumbled upon an application that lets me write scripts in python with certain libraries: https://andersmalmgren.github.io/FreePIE/.
The thing is, this script that I have tends to lag a lot, like, when I hold X (key to alternate between full-braking and braking just a quarter) the more I hold it, the more it delays after I release the key, this also happens when I use the clutch key (C), and the "Smooth Throttle" key (Z), I think this has to do with the way I programmed it (spaghetti pretty much, as I don't know a lot of python) so I thought I could get help from this forum, also It's my first time and this is my fresh new account, so I could be doing something wrong here, but anyways, here's the code:
if starting:
system.setThreadTiming(TimingTypes.HighresSystemTimer)
system.threadExecutionInterval = 5
def set_button(button, key):
if keyboard.getKeyDown(key):
v.setButton(button, True)
else:
v.setButton(button, False)
def calculate_rate(max, time):
if time > 0:
return max / (time / system.threadExecutionInterval)
else:
return max
int32_max = (2 ** 14) - 1
activate = True
int32_min = (( 2** 14) * -1) + 1
v = vJoy[0]
v.x, v.y, v.z, v.rx, v.ry, v.rz, v.slider, v.dial = (int32_min,) * 8
# =============================================================================================
# Axis inversion settings (multiplier): normal = 1; inverted = -1
# =============================================================================================
global throttle_inversion, braking_inversion, clutch_inversion
throttle_inversion = 1
braking_inversion = 1
clutch_inversion = 1
# =============================================================================================
# Mouse settings
# =============================================================================================
global mouse_sensitivity, sensitivity_center_reduction
mouse_sensitivity = 4.0
sensitivity_center_reduction = 1.0
# =============================================================================================
# Ignition cut settings
# =============================================================================================
global ignition_cut_time, ignition_cut_elapsed_time
ignition_cut_enabled = True
ignition_cut_time = 100
ignition_cut_elapsed_time = 0
global ignition_cut, ignition_cut_released
# Init values, do not change
ignition_cut = False
ignition_cut_released = True
# =============================================================================================
# Steering settings
# =============================================================================================
global steering, steering_max, steering_min, steering_center_reduction
# Init values, do not change
steering = 0.0
steering_max = float(int32_max)
steering_min = float(int32_min)
steering_center_reduction = 1.0
# =============================================================================================
# Throttle settings
# =============================================================================================
global throttle_blip_enabled
throttle_blip_enabled = False
# In milliseconds
throttle_increase_time = 300
throttle_increase_time_after_ignition_cut = 0
throttle_increase_time_blip = 50
throttle_decrease_time = 300
global throttle, throttle_max, throttle_min
# Init values, do not change
throttle_max = int32_max * throttle_inversion
throttle_min = int32_min * throttle_inversion
throttle = throttle_min
global throttle_increase_rate, throttle_decrease_rate
# Set throttle behaviour with the increase and decrease time,
# the actual increase and decrease rates are calculated automatically
throttle_increase_rate = calculate_rate(throttle_max, throttle_increase_time)
throttle_increase_rate_after_ignition_cut = calculate_rate(throttle_max, throttle_increase_time_after_ignition_cut)
throttle_increase_rate_blip = calculate_rate(throttle_max, throttle_increase_time_blip)
throttle_decrease_rate = calculate_rate(throttle_max, throttle_decrease_time) * -1
# =============================================================================================
# Braking settings
# =============================================================================================
# In milliseconds
braking_increase_time = 160
braking_decrease_time = 130
global braking, braking_max, braking_min
# Init values, do not change
braking_max = int32_max * braking_inversion
braking_min = int32_min * braking_inversion
braking = braking_min
global braking_increase_rate, braking_decrease_rate
# Set braking behaviour with the increase and decrease time,
# the actual increase and decrease rates are calculated automatically
braking_increase_rate = calculate_rate(braking_max, braking_increase_time)
braking_decrease_rate = calculate_rate(braking_max, braking_decrease_time) * -1
# =============================================================================================
# Clutch settings
# =============================================================================================
# In milliseconds
clutch_increase_time = 0
clutch_decrease_time = 50
global clutch, clutch_max, clutch_min
# Init values, do not change
clutch_max = int32_max * clutch_inversion
clutch_min = int32_min * clutch_inversion
clutch = clutch_min
global clutch_increase_rate, clutch_decrease_rate
# Set clutch behaviour with the increase and decrease time,
# the actual increase and decrease rates are calculated automatically
clutch_increase_rate = calculate_rate(clutch_max, clutch_increase_time)
clutch_decrease_rate = calculate_rate(clutch_max, clutch_decrease_time) * -1
# assign button
# =================================================================================================
# LOOP START
# =================================================================================================
# =================================================================================================
# Activate
# =================================================================================================
if keyboard.getPressed(Key.End):
activate = not activate
if activate == True:
vJoy[0].setButton(0,int(keyboard.getKeyDown(Key.S)))
vJoy[0].setButton(1,int(keyboard.getKeyDown(Key.D)))
vJoy[0].setButton(2,int(keyboard.getKeyDown(Key.C)))
vJoy[0].setButton(3,int(mouse.wheelUp))
vJoy[0].setButton(4,int(keyboard.getKeyDown(Key.Home)))
vJoy[0].setButton(5,int(keyboard.getKeyDown(Key.G)))
vJoy[0].setButton(6,int(keyboard.getKeyDown(Key.H)))
vJoy[0].setButton(7,int(mouse.wheelDown))
else:
vJoy[0].setButton(0,int(keyboard.getKeyDown(Key.Home)))
vJoy[0].setButton(1,int(keyboard.getKeyDown(Key.Home)))
vJoy[0].setButton(2,int(keyboard.getKeyDown(Key.Home)))
vJoy[0].setButton(3,int(keyboard.getKeyDown(Key.Home)))
vJoy[0].setButton(4,int(keyboard.getKeyDown(Key.Home)))
vJoy[0].setButton(5,int(keyboard.getKeyDown(Key.Home)))
vJoy[0].setButton(6,int(keyboard.getKeyDown(Key.Home)))
vJoy[0].setButton(7,int(keyboard.getKeyDown(Key.Home)))
# =================================================================================================
# =================================================================================================
# Steering logic
# =================================================================================================
if steering > 0:
steering_center_reduction = sensitivity_center_reduction ** (1 - (steering / steering_max))
elif steering < 0:
steering_center_reduction = sensitivity_center_reduction ** (1 - (steering / steering_min))
steering = steering + ((float(mouse.deltaX) * mouse_sensitivity) / steering_center_reduction)
if steering > steering_max:
steering = steering_max
elif steering < steering_min:
steering = steering_min
v.x = int(round(steering))
# =================================================================================================
# Clutch logic
# =================================================================================================
if keyboard.getPressed(Key.C):
throttle = -16383
if keyboard.getKeyDown(Key.C):
clutch = clutch + clutch_increase_rate
else:
clutch = clutch + clutch_decrease_rate
if clutch > clutch_max * clutch_inversion:
clutch = clutch_max * clutch_inversion
elif clutch < clutch_min * clutch_inversion:
clutch = clutch_min * clutch_inversion
v.z = clutch
# =================================================================================================
# Smooth Throttle
# =================================================================================================
if keyboard.getKeyDown(Key.Z):
blip = 1
elif keyboard.getKeyUp(Key.Z):
blip = 200
# =================================================================================================
# Brake y Full Brake
# =================================================================================================
if keyboard.getKeyDown(Key.X):
maxBrake = 16383
elif keyboard.getKeyUp(Key.X):
maxBrake = 2048
elif keyboard.getKeyDown(Key.J):
maxBrake = 1
# =================================================================================================
# Throttle logic
# =================================================================================================
if mouse.leftButton:
throttle = throttle + throttle_increase_rate * blip
else:
throttle = throttle + throttle_decrease_rate * blip
if throttle > throttle_max * throttle_inversion:
throttle = throttle_max * throttle_inversion
elif throttle < throttle_min * throttle_inversion:
throttle = throttle_min * throttle_inversion
v.y = throttle
# =================================================================================================
# Braking logic
# =================================================================================================
if mouse.rightButton:
braking = braking + (braking_increase_rate*6)
else:
if braking > 4096:
braking = braking + (braking_decrease_rate * 4)
else:
braking = braking + (braking_decrease_rate * 4)
if braking > maxBrake:
braking = maxBrake
elif braking < braking_min * braking_inversion:
braking = braking_min * braking_inversion
v.rz = braking
# =================================================================================================
# Buttons post-throttle logic
# =================================================================================================
#set_button(look_left_button, look_left_key)
#set_button(look_right_button, look_right_key)
#set_button(look_back_button, look_back_key)
#set_button(change_view_button, change_view_key)
#set_button(indicator_left_button, indicator_left_key)
#set_button(indicator_right_button, indicator_right_key)
# =================================================================================================
# PIE diagnostics logic
# =================================================================================================
diagnostics.watch(v.x)
diagnostics.watch(v.y)
diagnostics.watch(v.rz)
diagnostics.watch(maxBrake)
diagnostics.watch(blip)
diagnostics.watch(activate)
to test the code you simply copy this into a .py file and run it with freePIE, it has a watch tool that is very useful too
I tried to use and modify an already found script for emulating a wheel in Assetto Corsa through freePIE, since the beginning and even before I modified it, It began to lag a lot, like it's buffering a lot of inputs innecesarily.
Related
I'm having a few problems.
First, given wind speeds seem to be low, especially compared to other weather stations reporting close by (apparently there are lots of enthusiasts in my area). Just watching the trees, flag, bushes, small animals flying across my yard, I can tell 1.6mph is a wee bit low. Everything tested fine inside, and when I run the test script outside its picking up the signal as it spins.
the 2nd problem is it always reports the "mean" and "max" speeds as exactly the same. I've tried to adjust the intervals, but no matter what length of time I put in, they always report as the same numbers.
from gpiozero import Button
import requests
import time
import math
import statistics
import database
wind_count = 0
radius_cm = 9.0
wind_interval = 5
ADJUSTMENT = 1.18
interval = 60
gust = 0
def spin():
global wind_count
wind_count = wind_count + 1
def calculate_speed(time_sec):
global wind_count
global gust
circumference_cm = (2 * math.pi) * radius_cm
rotations = wind_count / 2.0
dist_cm = circumference_cm * rotations
dist_km = (circumference_cm * rotations) / 100000
dist_mi = dist_km * 0.621371
mi_per_sec = dist_mi / time_sec
mi_per_hour = mi_per_sec * 3600
return mi_per_hour * ADJUSTMENT
def reset_wind():
global wind_count
wind_count = 0
def reset_gust():
global gust
gust = 0
wind_speed_sensor = Button(16)
wind_speed_sensor.when_activated = spin
while True:
print("Starting Weather Sensor Read Loop...")
start_time = time.time()
while time.time() - start_time <= interval:
print("Start timed loop...")
wind_start_time = time.time()
reset_wind()
reset_gust()
store_speeds = []
time.sleep(wind_interval)
final_speed = calculate_speed(wind_interval)
store_speeds.append(final_speed)
wind_gust_speed = (max(store_speeds))
wind_speed = (statistics.mean(store_speeds))
print(wind_average, wind_speed)
When I comment out "store_speeds = [ ]" the first loop the speeds are reported the same, every loop after I get a "max" reading thats "different" than the mean. This still troubles me, because why on the first loop are they the same? Am I wrong for thinking with the wind_interval set at 5, and the interval set to 60, that its taking 5 second samples over a 60 second period, giving me 12 sample to get the mean and max from?
My goal is every time it reports, I get a mean and max for that "loop" if possible, and not a mean/max over the time the script stays running before its interrupted/stopped.
Here is the working and corrected code:
from gpiozero import Button
import time
import math
import statistics
wind_count = 0
radius_cm = 9.0
wind_interval = 5
interval = 30
CM_IN_A_KM = 100000.0
SECS_IN_AN_HOUR = 3600
ADJUSTMENT = 1.18
store_speeds = []
def reset_wind():
global wind_count
wind_count = 0
def spin():
global wind_count
wind_count = wind_count + 1
def calculate_speed(time_sec):
global wind_count
circumference_cm = (2 * math.pi) * radius_cm
rotations = wind_count / 2.0
dist_km = (circumference_cm * rotations) / CM_IN_A_KM
km_per_sec = dist_km / time_sec
km_per_hour = km_per_sec * SECS_IN_AN_HOUR
mi_per_hour = km_per_hour * 0.6214
return mi_per_hour * ADJUSTMENT
wind_speed_sensor = Button(16)
wind_speed_sensor.when_pressed = spin
while True:
store_speeds = []
for _ in range (interval//wind_interval):
reset_wind()
#reset_gust()
time.sleep(wind_interval) # counter is spinning in background
final_speed = calculate_speed(wind_interval)
store_speeds.append(final_speed)
wind_gust = max(store_speeds)
wind_speed = statistics.mean(store_speeds)
print(wind_speed, wind_gust)
My setup: An Arduino that reads numbers from serial(steps), then it moves the stepper motor that many steps. It uses a4988 driver, a 12 volt power supply, 1.5 amp Nema 17. All of that is fine, my issue is on the Python side.
In Python, I have tried to create a function in a variety of ways.
I used tkinter to get the mouse position on the screen, it moves in the x axis rotating the stepper. The stepper has a flashlight just to shine it at anything I point at with my mouse.
Say there is a camera below the stepper, if I move my mouse over to an object it would move there, within a tolerance of 5 steps either way. +5, -5
The function I have tried to create should work like this.
while True:
#I change direction by writing 400, clockwise, or 401, anti clockwise to serial.
step(10)#move only 10 steps
step(10)#it should do nothing as it has already moved 10 steps.
step(15)#it should move 5 steps as it has moved 10 already
step(5)#change direction and move back 10 steps
I am somewhat decent at python(or so I think)
here is my most promising code so far:
#I have direction changes done by writing a 400 for clockwise or 401 for anti clockwise to serial.
import tkinter as tk
import serial
ser = serial.Serial("COM8", 9600)
root = tk.Tk()
wi = root.winfo_screenwidth() / 2
width = root.winfo_screenwidth()
hi = root.winfo_screenheight() / 2
height = root.winfo_screenheight()
sere = '\r\n'
sender = sere.encode('utf_8')
pps = height / 200
# p.p.s pixels per step divide that by the same thing to get correct.
#how many pixels are for 1 step
print(pps)
#number of pixels difference divided by pps to get steps, then round for ease of use
past = 0
print(height, width, wi) # height divided by pps should always = max steps in my case 200
def send(steps):
global past
global current
sere = '\r\n'
current = steps
neg = past - 5
pos = past + 5
if current != past:
if current > pos or current < neg:
if steps > 1:
sender = sere.encode('utf_8')
e = str(steps).encode('utf_8')
ser.write(e + sender)
past = steps
while True:
pos = root.winfo_pointerx() - wi
steps = round(pos / pps)
print(pos, steps) # shows cursor position
#direction code is not in here as I have no need for it atm.For one simple reason, IT DOESN'T WORK!
Please explain answers if possible, I am a long time lurker, and only made a account just now for this.Thank you all for any assistance.
Edit:
The question is how to make a function that moves a stepper motor to a certain number of steps.If you command it to do 10 steps twice, it only moves 10 steps.
step(10) stepper moves 10 steps, do it again the motor does nothing as it is already at 10 steps.
If I do step(15) it would only move another 5 as it is at 10 steps.
Edit2 Clarification:
By function I mean
def step(steps):
#logic that makes it work
For anyone else who has this problem and people seem to not help a ton, heres my new code to drive it,
past = 0
sender = sere.encode('utf_8')
dir1 = str(400).encode('utf_8')
dir2 = str(401).encode('utf_8')
def send(steps):
global past
global current
global sender
global dir1
global dir2
global stepstaken
sere = '\r\n'
current = steps
neg = past - 5 # tolerance both are unused atm
pos = past + 5 # tolerance
needed = current - past
print(needed, steps)
if current != past:
if needed > 0:
serialsteps = str(needed).encode('utf_8')
ser.write(dir1 + sender)
ser.write(serialsteps + sender)
if needed < 0:
needed = needed * -1
serialstepss = str(needed).encode('utf_8')
ser.write(dir2 + sender)
ser.write(serialstepss + sender)
past = steps
Here is my full code for mouse to stepper motor,
import tkinter as tk
root = tk.Tk()
import serial
import struct
import time
stepstaken = 0
ardunioport = "COM" + str(input("Ardunio Port Number:")) # port number only
ser = serial.Serial(ardunioport, 9600)
wi = root.winfo_screenwidth() / 2
width = root.winfo_screenwidth()
hi = root.winfo_screenheight() / 2
height = root.winfo_screenheight()
sere = '\r\n' #still don't understand why but it does not work without this.
sender = sere.encode('utf_8')
pps = width / 200 #how many pixels are in 1 step
past = 0
sender = sere.encode('utf_8')
dir1 = str(400).encode('utf_8')
dir2 = str(401).encode('utf_8')
def send(steps):
global past
global current
global sender
global dir1
global dir2
global stepstaken
#need overcome overstepping
sere = '\r\n'
current = steps
neg = past - 5 # tolerance
pos = past + 5 # tolerance
needed = current - past
print(needed, steps)
if current != past:
if needed > 0:
serialsteps = str(needed).encode('utf_8')
ser.write(dir1 + sender)
ser.write(serialsteps + sender)
if needed < 0:
needed = needed * -1
serialstepss = str(needed).encode('utf_8')
ser.write(dir2 + sender)
ser.write(serialstepss + sender)
past = steps
while True:
pos = root.winfo_pointerx() - wi
steps = round(pos / pps)
#print("Mouse Position " + str(pos) + " Steps Needed" + str(steps)) # shows cursor position
send(steps)
For me it goes to a Arduino, the Arduino is not programmed to accept negative numbers. Instead it removes the - sign, multiply negative by negative = positive. It sends the opposite direction code/number then the steps needed to get there. Good luck everyone. This code works for me, no promise it will work for you.If it does not I am sorry.
I have a python script for an installation in an art museum that is meant to run continuously playing sounds, driving an LED matrix, and sensing people via OpennCV and a thermal camera.
Each of the parts of the script work and all of them work together but randomly the script locks up and I need to restart it. I want to script to not lock up so no one has to reset it during the exhibition.
I have the code running on a spare Raspberry Pi and a spare LED matrix and it continues to cycle through fine. The only changes that I made were commenting out the start of a thread to check the IR sensor and a call to a function to get the max temp from the sensor.
To be clear, if I leave these bits of code in the script runs fine 1 -3 or sometimes 10 times. But it seems to lock up in the first "state" when IRcount = 0
I am stuck. Any help is greatly appreciated.
```
#!/usr/bin/python
import glob
import queue
import sys
import pygame
import cv2
import random
import math
import colorsys
import time
from rpi_ws281x import *
from PIL import Image
import numpy as np
import threading
global thresh
sys.path.insert(0, "/home/pi/irpython/build/lib.linux-armv7l-3.5")
import MLX90640 as mlx
currentTime = int(round(time.time() * 1000))
InflateWait = int(round(time.time() * 1000))
minTime = 6000
maxTime = 12000
lineHeight1 = 0
lineHue1 = float(random.randrange(1,360))/255
# IR Functions
# Function to just grab the Max Temp detected. If over threshold then start
# the sequence, if not stay in state 0
def maxTemp():
mlx.setup(8) #set frame rate of MLX90640
f = mlx.get_frame()
mlx.cleanup()
# get max and min temps from sensor
# v_min = min(f)
v_max = int(max(f))
return v_max
# Function to detect individual people's heat blob group of pixels
# run in a thread only at the end of the script
def irCounter():
img = Image.new( 'L', (24,32), "black") # make IR image
mlx.setup(8) #set frame rate of MLX90640
f = mlx.get_frame()
mlx.cleanup()
for x in range(24):
row = []
for y in range(32):
val = f[32 * (23-x) + y]
row.append(val)
img.putpixel((x, y), (int(val)))
# convert raw temp data to numpy array
imgIR = np.array(img)
# increase the 24x32 px image to 240x320px for ease of seeing
bigIR = cv2.resize(depth_uint8, dsize=(240,320), interpolation=cv2.INTER_CUBIC)
# Use a bilateral filter to blur while hopefully retaining edges
brightBlurIR = cv2.bilateralFilter(bigIR,9,150,150)
# Threshold the image to black and white
retval, threshIR = cv2.threshold(brightBlurIR, 26, 255, cv2.THRESH_BINARY)
# Define kernal for erosion and dilation and closing operations
kernel = np.ones((5,5),np.uint8)
erosionIR = cv2.erode(threshIR,kernel,iterations = 1)
dilationIR = cv2.dilate(erosionIR,kernel,iterations = 1)
closingIR = cv2.morphologyEx(dilationIR, cv2.MORPH_CLOSE, kernel)
# Detect countours
contours, hierarchy = cv2.findContours(closingIR, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
# Get the number of contours ( contours count when touching edge of image while blobs don't)
ncontours = str(len(contours))
# Show images in window during testing
cv2.imshow("Combined", closingIR)
cv2.waitKey(1)
#initialize pygame
pygame.init()
pygame.mixer.init()
pygame.mixer.set_num_channels(30)
print("pygame initialized")
# assign sound chennels for pygame
channel0 = pygame.mixer.Channel(0)
channel1 = pygame.mixer.Channel(1)
channel2 = pygame.mixer.Channel(2)
channel3 = pygame.mixer.Channel(3)
channel4 = pygame.mixer.Channel(4)
channel5 = pygame.mixer.Channel(5)
channel6 = pygame.mixer.Channel(6)
channel7 = pygame.mixer.Channel(7)
channel8 = pygame.mixer.Channel(8)
channel9 = pygame.mixer.Channel(9)
channel10 = pygame.mixer.Channel(10)
channel11 = pygame.mixer.Channel(11)
channel12 = pygame.mixer.Channel(12)
channel13 = pygame.mixer.Channel(13)
channel14 = pygame.mixer.Channel(14)
channel15 = pygame.mixer.Channel(15)
channel16 = pygame.mixer.Channel(16)
channel17 = pygame.mixer.Channel(17)
channel18 = pygame.mixer.Channel(18)
channel19 = pygame.mixer.Channel(19)
channel20 = pygame.mixer.Channel(20)
channel21 = pygame.mixer.Channel(21)
channel22 = pygame.mixer.Channel(22)
channel23 = pygame.mixer.Channel(23)
channel24 = pygame.mixer.Channel(24)
channel25 = pygame.mixer.Channel(25)
channel26 = pygame.mixer.Channel(26)
channel27 = pygame.mixer.Channel(27)
channel28 = pygame.mixer.Channel(28)
# load soundfiles
echoballs = pygame.mixer.Sound("/home/pi/ruff-wavs/sounds/echo balls FIX.ogg")
organbounce = pygame.mixer.Sound("/home/pi/ruff-wavs/sounds/ORGAN BOUNCE fix.ogg")
jar = pygame.mixer.Sound("/home/pi/ruff-wavs/sounds/jar whoop fix.ogg")
garland = pygame.mixer.Sound("/home/pi/ruff-wavs/sounds/GARLAND_fix.ogg")
dribble= pygame.mixer.Sound("/home/pi/ruff-wavs/sounds/dribble.ogg")
cowbell = pygame.mixer.Sound("/home/pi/ruff-wavs/sounds/cowbell fix.ogg")
clackyballs = pygame.mixer.Sound("/home/pi/ruff-wavs/sounds/clacky balls boucne.ogg")
burpees = pygame.mixer.Sound("/home/pi/ruff-wavs/sounds/burpees_fix.ogg")
brokensynth = pygame.mixer.Sound("/home/pi/ruff-wavs/sounds/broken synth bounce.ogg")
woolballs = pygame.mixer.Sound("/home/pi/ruff-wavs/sounds/wool balls in jar FIX.ogg")
wiimoye = pygame.mixer.Sound("/home/pi/ruff-wavs/sounds/wiimoye_fix.ogg")
warpyorgan = pygame.mixer.Sound("/home/pi/ruff-wavs/sounds/warpy organ bounce#.2.ogg")
vibrate = pygame.mixer.Sound("/home/pi/ruff-wavs/sounds/vibrate fix.ogg")
turtlesbounce = pygame.mixer.Sound("/home/pi/ruff-wavs/sounds/turtles fix.ogg")
timer = pygame.mixer.Sound("/home/pi/ruff-wavs/sounds/timer.ogg")
tape = pygame.mixer.Sound("/home/pi/ruff-wavs/sounds/tape fix.ogg")
tambourine = pygame.mixer.Sound("/home/pi/ruff-wavs/sounds/TAMBOURINE.ogg")
springybounce = pygame.mixer.Sound("/home/pi/ruff-wavs/sounds/springy bounce.ogg")
smash3 = pygame.mixer.Sound("/home/pi/ruff-wavs/sounds/smash fix.ogg")
bristle2 = pygame.mixer.Sound("/home/pi/ruff-wavs/sounds/BRISTLE FIX.ogg")
blackkeys = pygame.mixer.Sound("/home/pi/ruff-wavs/sounds/black keys FIX.ogg")
zipper = pygame.mixer.Sound("/home/pi/ruff-wavs/sounds/zipper.ogg")
presatisfactionsweep = pygame.mixer.Sound("/home/pi/ruff-wavs/sounds/pre-satisfaction sweep .ogg")
satisfaction = pygame.mixer.Sound("/home/pi/ruff-wavs/sounds/SATISFACTION.ogg")
altsatisfaction = pygame.mixer.Sound("/home/pi/ruff-wavs/sounds/alt_satisfaction_trimmed.ogg")
solosatisfaction = pygame.mixer.Sound("/home/pi/ruff-wavs/sounds/SOLO_SATISFACTION.ogg")
print("sound files loaded")
# initializing sounds list
soundsList = [echoballs, organbounce, zipper, jar, garland, dribble, cowbell, clackyballs, burpees, brokensynth, woolballs,
wiimoye, warpyorgan, vibrate, turtlesbounce, timer, tambourine, springybounce, smash3, bristle2, blackkeys, zipper ]
IRcount = 0 # define initial state for main loop
pygame.display.set_mode((32, 8))
print("pygame dispaly open")
# LED strip configuration:
LED_COUNT = 256 # Number of LED pixels.
LED_PIN = 18 # GPIO pin connected to the pixels (18 uses PWM!).
#LED_PIN = 10 # GPIO pin connected to the pixels (10 uses SPI /dev/spidev0.0).
LED_FREQ_HZ = 800000 # LED signal frequency in hertz (usually 800khz)
LED_DMA = 10 # DMA channel to use for generating signal (try 10)
LED_BRIGHTNESS = 100 # Set to 0 for darkest and 255 for brightest
LED_INVERT = False # True to invert the signal (when using NPN transistor level shift)
LED_CHANNEL = 0 # set to '1' for GPIOs 13, 19, 41, 45 or 53
# Define functions which animate LEDs in various ways.
# PNG to LED function used to shuffle througfh folders of numbered PNGs exported
# from animations created
def pngToLED (strip, pngfile):
RGBimage = Image.open(pngfile).convert('RGB')
np_image = np.array(RGBimage)
colours = [Color(x[0],x[1],x[2]) for rows in np_image for x in rows]
colours2d = np.reshape(colours, (32, 8), order='F')
colours2d[1::2, :] = colours2d[1::2, ::-1]
pic = colours2d.flatten('C')
for i in range( 0, strip.numPixels(), 1 ):# iterate over all LEDs - range(start_value, end_value, step)
strip.setPixelColor(i, int(pic[ i ]))
strip.show()
def colorWipe(strip, color,wait_ms=10):
"""Wipe color across display a pixel at a time."""
for i in range(strip.numPixels()):
strip.setPixelColor(i, color)
strip.show()
time.sleep(1)
def theaterChase(strip, color, wait_ms, iterations=10):
"""Movie theater light style chaser animation."""
for j in range(iterations):
for q in range(3):
for i in range(0, strip.numPixels(), 3):
strip.setPixelColor(i+q, color)
strip.show()
time.sleep(wait_ms/1000.0)
for i in range(0, strip.numPixels(), 3):
strip.setPixelColor(i+q, 0)
def wheel(pos):
"""Generate rainbow colors across 0-255 positions."""
if pos < 85:
return Color(pos * 3, 255 - pos * 3, 0)
elif pos < 170:
pos -= 85
return Color(255 - pos * 3, 0, pos * 3)
else:
pos -= 170
return Color(0, pos * 3, 255 - pos * 3)
def rainbow(strip, wait_ms=20, iterations=1):
"""Draw rainbow that fades across all pixels at once."""
for j in range(256*iterations):
for i in range(strip.numPixels()):
strip.setPixelColor(i, wheel((i+j) & 255))
strip.show()
time.sleep(wait_ms/1000.0)
def rainbowCycle(strip, wait_ms=20, iterations=5):
"""Draw rainbow that uniformly distributes itself across all pixels."""
for j in range(256*iterations):
for i in range(strip.numPixels()):
strip.setPixelColor(i, wheel((int(i * 256 / strip.numPixels()) + j) & 255))
strip.show()
time.sleep(wait_ms/1000.0)
def theaterChaseRainbow(strip, wait_ms=90):
"""Rainbow movie theater light style chaser animation."""
for j in range(256):
for q in range(3):
for i in range(0, strip.numPixels(), 3):
strip.setPixelColor(i+q, wheel((i+j) % 255))
strip.show()
time.sleep(wait_ms/1000.0)
for i in range(0, strip.numPixels(), 3):
strip.setPixelColor(i+q, 0)
# Plasma LED Function from Root 42
def plasmaLED (plasmaTime):
h = 8
w = 32
out = [ Color( 0, 0, 0 ) for x in range( h * w ) ]
plasmaBright = 100.0
for x in range( h ):
for y in range( w ):
hue = (4.0 + math.sin( plasmaTime + x ) + math.sin( plasmaTime + y / 4.5 ) \
+ math.sin( x + y + plasmaTime ) + math.sin( math.sqrt( ( x + plasmaTime ) ** 2.0 + ( y + 1.5 * plasmaTime ) ** 2.0 ) / 4.0 ))/8
hsv = colorsys.hsv_to_rgb( hue , 1, 1 )
if y % 2 == 0: #even
out[ x + (h * y)] = Color( *[ int( round( c * plasmaBright ) ) for c in hsv ] )
else: #odd
out[ (y * h) + (h -1 -x) ] = Color( *[ int( round( c * plasmaBright ) ) for c in hsv ] )
for i in range( 0, strip.numPixels(), 1 ):# iterate over all LEDs - range(start_value, end_value, step)
strip.setPixelColor(i, out[ i ]) # set pixel to color in picture
strip.show()
# variables for plasma
plasmaTime = 5.0 # time
plasmaSpeed = 0.05 # speed of time
# thread for IRcounter function
class TempTask:
def __init__(self):
self.ir_temp = 0
self.lock = threading.Lock() #control concurrent access for safe multi thread access
self.thread = threading.Thread(target=self.update_temp)
def update_temp(self):
while True:
with self.lock:
self.ir_temp = irCounter()
time.sleep(0.1)
def start(self):
self.thread.start()
# Millis timer count function
def CheckTime( lastTime, wait):
if currentTime - lastTime >= wait:
lastTime += wait
return True
return False
# Main program logic follows:
if __name__ == '__main__':
# not currently starting the trhead because program is locking up without it
# want to figure out initial problem first
#start thread
#task = TempTask()
#task.start()
# Create NeoPixel object with appropriate configuration.
strip = Adafruit_NeoPixel(LED_COUNT, LED_PIN, LED_FREQ_HZ, LED_DMA, LED_INVERT, LED_BRIGHTNESS, LED_CHANNEL)
# Intialize the library (must be called once before other functions).
strip.begin()
print ('Press Ctrl-C to quit.')
try:
while True:
currentTime = int(round(time.time() * 1000))
if IRcount == 0:
#random solid color
colorWipe(strip, Color(random.randint(60,255), random.randint(60,255), random.randint(60,255)))
# use random.sample() to shuffle sounds list
shuffledSounds = random.sample(soundsList, len(soundsList))
if pygame.mixer.Channel(0).get_busy() == False: channel0.play(shuffledSounds[0],loops = -1)
thresh = 0
'''
# the threshold check below is the only thing I have taken out of
# Program on my test Raspberry Pi. It seems to not lock up without it
# not sure why this would be a problem.
thresh = int(maxTemp())
print (thresh)
if thresh >= 27:
InflateWait = int(round(time.time() * 1000))
print (thresh)
IRcount = 1
print("Threshold Temp Detected: Begin Sound Sequence")
else:
IRcount = 0
'''
if CheckTime(InflateWait,random.randint(minTime, maxTime)):
InflateWait = int(round(time.time() * 1000))
IRcount += 1
print(IRcount)
elif IRcount == 1:
LEDimages = glob.glob("/home/pi/ruff-wavs/Crystal_Mirror/*.png")
for LEDimage in sorted(LEDimages):
pngToLED (strip, LEDimage)
if pygame.mixer.Channel(1).get_busy() == False:
channel1.play(shuffledSounds[1],loops = -1)
waitTime = random.randint(minTime, maxTime)
if CheckTime(InflateWait,waitTime):
InflateWait = int(round(time.time() * 1000))
IRcount += 1
print(IRcount)
elif IRcount == 2:
LEDimages = glob.glob("/home/pi/ruff-wavs/Mercury_Loop/*.png")
for LEDimage in sorted(LEDimages):
pngToLED (strip, LEDimage)
if pygame.mixer.Channel(2).get_busy() == False:
channel2.play(shuffledSounds[2],loops = -1)
waitTime = random.randint(minTime, maxTime)
if CheckTime(InflateWait,waitTime):
InflateWait = int(round(time.time() * 1000))
IRcount += 1
print(IRcount)
elif IRcount == 3:
LEDimages = glob.glob("/home/pi/ruff-wavs/Pink_Lava/*.png")
for LEDimage in sorted(LEDimages):
pngToLED (strip, LEDimage)
if pygame.mixer.Channel(3).get_busy() == False:
channel3.play(shuffledSounds[3],loops = -1)
waitTime = random.randint(minTime, maxTime)
if CheckTime(InflateWait,waitTime):
InflateWait = int(round(time.time() * 1000))
IRcount += 1
print(IRcount)
elif IRcount == 4:
LEDimages = glob.glob("/home/pi/ruff-wavs/Horiz_Mosaic/*.png")
for LEDimage in sorted(LEDimages):
pngToLED (strip, LEDimage)
if pygame.mixer.Channel(4).get_busy() == False:
channel4.play(shuffledSounds[4],loops = -1)
waitTime = random.randint(minTime, maxTime)
if CheckTime(InflateWait,waitTime):
InflateWait = int(round(time.time() * 1000))
IRcount += 1
print(IRcount)
elif IRcount == 5:
plasmaLED()
plasmaTime = plasmaTime + plasmaSpeed # increment plasma time
if pygame.mixer.Channel(5).get_busy() == False:
channel5.play(shuffledSounds[5],loops = -1)
waitTime = random.randint(minTime, maxTime)
if CheckTime(InflateWait,waitTime):
InflateWait = int(round(time.time() * 1000))
IRcount += 1
print(IRcount)
elif IRcount == 6:
LEDimages = glob.glob("/home/pi/ruff-wavs/Radio_Loop/*.png")
for LEDimage in sorted(LEDimages):
pngToLED (strip, LEDimage)
if pygame.mixer.Channel(6).get_busy() == False:
channel6.play(shuffledSounds[6],loops = -1)
waitTime = random.randint(minTime, maxTime)
if CheckTime(InflateWait,waitTime):
InflateWait = int(round(time.time() * 1000))
IRcount += 1
print(IRcount)
elif IRcount == 7:
LEDimages = glob.glob("/home/pi/ruff-wavs/Star_Loop/*.png")
for LEDimage in sorted(LEDimages):
pngToLED (strip, LEDimage)
if pygame.mixer.Channel(7).get_busy() == False:
channel7.play(shuffledSounds[7],loops = -1)
waitTime = random.randint(minTime, maxTime)
if CheckTime(InflateWait,waitTime):
InflateWait = int(round(time.time() * 1000))
IRcount += 1
elif IRcount == 14:
plasmaLED()
plasmaTime = plasmaTime + plasmaSpeed # increment plasma time
if pygame.mixer.Channel(14).get_busy() == False:
channel14.play(shuffledSounds[14],loops = -1)
waitTime = random.randint(minTime, maxTime)
if CheckTime(InflateWait,waitTime):
InflateWait = int(round(time.time() * 1000))
IRcount += 1
print(IRcount)
print (thresh)
elif IRcount == 15:
plasmaLED()
plasmaTime = plasmaTime + plasmaSpeed # increment plasma time
if pygame.mixer.Channel(15).get_busy() == False:
channel15.play(shuffledSounds[15],loops = -1)
waitTime = random.randint(minTime, maxTime)
if CheckTime(InflateWait,waitTime):
InflateWait = int(round(time.time() * 1000))
IRcount += 1
print(IRcount)
elif IRcount == 16:
# random color theater chase increment random ms to speed up with sounds
theaterChase(strip, Color(random.randint(1,255), random.randint(1,255), random.randint(1,255)), random.randint(40,50))
pygame.mixer.fadeout(45000)
if pygame.mixer.Channel(22).get_busy() == False:
channel22.play(presatisfactionsweep)
IRcount = 17
print(IRcount)
print("sweep end start")
elif IRcount == 18:
# random color theater chase increment random ms to speed up with sounds
theaterChase(strip, Color(random.randint(1,255), random.randint(1,255), random.randint(1,255)), random.randint(30,40))
if pygame.mixer.Channel(22).get_busy() == False:
pygame.mixer.stop()
channel23.play(satisfaction)
IRcount = 19
print(IRcount)
print("Play Satisfaction Sount")
elif IRcount == 19:
rainbowCycle(strip, 5)
if pygame.mixer.Channel(23).get_busy() == False: IRcount = 0
except KeyboardInterrupt:
colorWipe(strip, Color(0,0,0), 1)
pygame.mixer.stop()
pygame.quit()
```
Update 1 - Suspected Function(s)
When I left the script run overnight and came to the exhibit in the morning it would be stuck in the 1st state IRcount = 0 The only things that happen in that state is the maxTemp() function to get the max temp, the LED color wipe function to cycle colors.
When I would come in in the morning it would be stuck, playing a single sound from pygame, as it should, but it would not be cycling colors. I removed the maxTemp() from my test Pi and it has been working fine.
def maxTemp():
mlx.setup(8) #set frame rate of MLX90640
f = mlx.get_frame()
mlx.cleanup()
# get max and min temps from sensor
# v_min = min(f)
v_max = int(max(f))
return v_max
Update # 2
I thought that the thread might be the problem so I commented out the thread start call. That is why I made the simpler maxTemp() function to see if that would work better than the thread. So when I was using the max temp then the thread wasn't being called.
I don't understand threads very well. Is it possible to have the max temp variable update continuously and have the simple OpenCV numPy manipulations running continuously? That would be ideal. When I originally added the thread it seemed to stop after a few cycles.
I do not have a join on the thread. I know threads don't "restart" but do I need to call it again as the state machine starts again?
# not currently starting the thread because program is locking up without it
# want to figure out initial problem first
#start thread
#task = TempTask()
#task.start()
Update #3
I Uploaded new code that eliminated the duplicate functions. Everything is handled in the thread temp.task now. That seems to work fine. I also put the github suggestion of polling the thermal sensor if the image is a duplicate but that has not happened.
I left the program run over night and when I came in in the morning it was locked up. The SD card is set to read only mode. I ssh'd into the pi. I have my auto start python script in /etc/profile
It seems to start the script each time I log into ssh. When I logged in this morning to see if the pi was still up it game an out of memory error.
```
Traceback (most recent call last):
File "/home/pi/ruff-wavs/shufflewavdemo.py", line 210, in <module>
altsatisfaction = pygame.mixer.Sound("/home/pi/ruff-wavs/sounds/alt_satisfaction_trimmed.ogg")
pygame.error: Unable to open file '/home/pi/ruff-wavs/sounds/alt_satisfaction_trimmed.ogg'
OSError: [Errno 28] No space left on device
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/usr/lib/python3.5/runpy.py", line 193, in _run_module_as_main
"__main__", mod_spec)
File "/usr/lib/python3.5/runpy.py", line 85, in _run_code
exec(code, run_globals)
File "/usr/local/lib/python3.5/dist-packages/virtualenvwrapper/hook_loader.py", line 223, in <module>
main()
File "/usr/local/lib/python3.5/dist-packages/virtualenvwrapper/hook_loader.py", line 145, in main
output.close()
OSError: [Errno 28] No space left on device
-bash: cannot create temp file for here-document: No space left on device
Could that be because it is in read only mode?
I used this script to switch from writable to read only and back.
[https://github.com/JasperE84/root-ro][1]
[1]: https://github.com/JasperE84/root-ro
I suspect the issue is that you're accessing the mlx device both in the main thread via maxTemp() as well as in the irCounter() thread. The fact that it works when you take out the maxTemp call, and that that call happens in the if IRcount == 0: state supports this.
I would add the maxTemp functionality to the irCounter thread, so that accessing it from only a single thread; and update a global variable (protected by a lock) with the maxTemp results if you need to retain this functionality.
I am using the following code to get the temperature of a PT100 (3-wire) and a PT1000 (2-wire) simultaneously on my raspberry.
According to https://cdn-learn.adafruit.com/downloads/pdf/adafruit-max31865-rtd-pt100-amplifier.pdf my wiring is correct.
from Max31865 import Max31865
maxGarraum = Max31865()
maxGarraum.initPt100_3WireSensor(26, True)
maxFleisch = Max31865()
maxFleisch.initPt1000_2WireSensor(24, True)
while True:
maxGarraum.getCurrentTemp()
time.sleep(1)
My Max31865.py looks like the following:
import time, math
import RPi.GPIO as GPIO
# import numpy
class Max31865():
def initPt1000_2WireSensor(self, iCsPin):
self.csPin = iCsPin
# Fleischsensor Pt1000 (2-wire)
self.registerCode = 0xA2
self.setupGPIO()
def initPt100_3WireSensor(self, iCsPin):
self.csPin = iCsPin
# Garraumsensor Pt100 (3-wire)
self.registerCode = 0xB2
self.setupGPIO()
def setupGPIO(self):
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BOARD)
GPIO.setup(self.csPin, GPIO.OUT)
GPIO.setup(self.misoPin, GPIO.IN)
GPIO.setup(self.mosiPin, GPIO.OUT)
GPIO.setup(self.clkPin, GPIO.OUT)
GPIO.output(self.csPin, GPIO.HIGH)
GPIO.output(self.clkPin, GPIO.LOW)
GPIO.output(self.mosiPin, GPIO.LOW)
def getCurrentTemp(self):is
# 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
self.writeRegister(0, self.registerCode)
# conversion time is less than 100ms
time.sleep(.1) # give it 100ms for conversion
# read all registers
out = self.readRegisters(0, 8)
conf_reg = out[0]
#print("Config register byte: %x" % conf_reg)
[rtd_msb, rtd_lsb] = [out[1], out[2]]
rtd_ADC_Code = ((rtd_msb << 8) | rtd_lsb) >> 1
temp = self.calcTemperature(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")
return temp
def writeRegister(self, regNum, dataByte):
GPIO.output(self.csPin, GPIO.LOW)
# 0x8x to specify 'write register value'
addressByte = 0x80 | regNum;
# first byte is address byte
#print("Cs-Pin: ", self.csPin, "Addresse: ", addressByte)
self.sendByte(addressByte)
# the rest are data bytes
self.sendByte(dataByte)
GPIO.output(self.csPin, GPIO.HIGH)
def readRegisters(self, regNumStart, numRegisters):
out = []
GPIO.output(self.csPin, GPIO.LOW)
# 0x to specify 'read register value'
self.sendByte(regNumStart)
for byte in range(numRegisters):
data = self.recvByte()
out.append(data)
GPIO.output(self.csPin, GPIO.HIGH)
return out
def sendByte(self, byte):
for bit in range(8):
GPIO.output(self.clkPin, GPIO.HIGH)
if (byte & 0x80):
GPIO.output(self.mosiPin, GPIO.HIGH)
else:
GPIO.output(self.mosiPin, GPIO.LOW)
byte <<= 1
GPIO.output(self.clkPin, GPIO.LOW)
def recvByte(self):
byte = 0x00
for bit in range(8):
GPIO.output(self.clkPin, GPIO.HIGH)
byte <<= 1
if GPIO.input(self.misoPin):
byte |= 0x1
GPIO.output(self.clkPin, GPIO.LOW)
return byte
def calcTemperature(self, RTD_ADC_Code):
global temp
R_REF = 0.0 # Reference Resistor
Res0 = 0.0; # Resistance at 0 degC for 400ohm R_Ref
a = 0.0
b = 0.0
c = 0.0
if (self.registerCode == 0xA2):
############# PT1000 #############
R_REF = 4300.0 # Reference Resistor
Res0 = 1000.0; # Resistance at 0 degC for 430ohm 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)
else:
############# PT100 #############
R_REF = 430.0 # Reference Resistor
Res0 = 100.0; # Resistance at 0 degC for 430ohm 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
#print("CS-Pin: " , self.csPin, " --- ADC-Value: ", RTD_ADC_Code , " --- Resistance: ", round(Res_RTD, 2) , " 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 = -(a * Res0) + math.sqrt(a * a * Res0 * Res0 - 4 * (b * Res0) * (Res0 - Res_RTD))
temp = round(temp / (2 * (b * Res0)), 2)
# 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 "Solving Full Callendar-Van Dusen using numpy: %f" % temp_C_numpy
if (temp < 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 = (RTD_ADC_Code / 32) - 256
print ("CSPin " , self.csPin, " - ADC: ", RTD_ADC_Code , " - Resistance: ", round(Res_RTD, 2) , " Ohms - Temp: " , temp)
return temp
##############################################################################################################################
# Programmstart #
###############################################################################################################################
csPin = 0
misoPin = 21
mosiPin = 19
clkPin = 23
registerCode = 0
# BOARD:21,19,23
# BCM: 9,10,11
class FaultError(Exception):
pass
The problem is that I get no data for the PT100-rtd - only for the PT1000.
The wiring with my raspberry must be correct, because I also adapted my code to test the wiring with two PT1000-rtd and it works.
I think that my problem is somewhere at "getCurrentTemp(self)". Maybe at the readRegister(..) and writeRegister(..) with the "regNum" and "regNumStart".
Any idea?
It looks like you are trying to use the circuitPython code.
Look at this location for the latest version of this code.
Follow the instructions and ensure that you have all required
dependencies met on your platform.
Look here for their simple example of how to test.
Good Luck!
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.