Compare commits
No commits in common. "bb8420553107161ae70b9359271cc13457454678" and "1fa1149170a9a093f65ba3c19ba60ba210dee5f3" have entirely different histories.
bb84205531
...
1fa1149170
@ -3,11 +3,10 @@ import sys
|
|||||||
import time
|
import time
|
||||||
import RPi.GPIO as GPIO
|
import RPi.GPIO as GPIO
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Import pin assignments and custom libraries
|
|
||||||
# Add the parent directory to the module search path
|
# Add the parent directory to the module search path
|
||||||
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
|
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
|
||||||
|
|
||||||
|
# Import pin assignments and custom libraries
|
||||||
from interface_board_pins import GPIO_DIGITAL_INPUTS # Input GPIO mapping
|
from interface_board_pins import GPIO_DIGITAL_INPUTS # Input GPIO mapping
|
||||||
from interface_board_pins import ( # Shift register pin assignment
|
from interface_board_pins import ( # Shift register pin assignment
|
||||||
GPIO_SHIFT_REG_DATA,
|
GPIO_SHIFT_REG_DATA,
|
||||||
@ -16,21 +15,15 @@ from interface_board_pins import ( # Shift register pin assignment
|
|||||||
)
|
)
|
||||||
from interface_board_libs.shift_register import ShiftRegister # Custom shift register class
|
from interface_board_libs.shift_register import ShiftRegister # Custom shift register class
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Initialize GPIO for digital inputs
|
# Initialize GPIO for digital inputs
|
||||||
GPIO.setmode(GPIO.BCM)
|
GPIO.setmode(GPIO.BCM)
|
||||||
for pin in GPIO_DIGITAL_INPUTS.values():
|
for pin in GPIO_DIGITAL_INPUTS.values():
|
||||||
print(f"Configuring GPIO pin {pin} as input")
|
print(f"Configuring GPIO pin {pin} as input")
|
||||||
GPIO.setup(pin, GPIO.IN)
|
GPIO.setup(pin, GPIO.IN)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Initialize shift register
|
# Initialize shift register
|
||||||
sr = ShiftRegister(GPIO_SHIFT_REG_DATA, GPIO_SHIFT_REG_LATCH, GPIO_SHIFT_REG_CLOCK)
|
sr = ShiftRegister(GPIO_SHIFT_REG_DATA, GPIO_SHIFT_REG_LATCH, GPIO_SHIFT_REG_CLOCK)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Main loop: Read inputs and write to shift register
|
# Main loop: Read inputs and write to shift register
|
||||||
print("\nStarting passthrough mode: Digital inputs → Shift register outputs")
|
print("\nStarting passthrough mode: Digital inputs → Shift register outputs")
|
||||||
try:
|
try:
|
||||||
@ -50,9 +43,9 @@ try:
|
|||||||
|
|
||||||
# Write the final byte to the shift register
|
# Write the final byte to the shift register
|
||||||
sr.write_byte(shift_register_value)
|
sr.write_byte(shift_register_value)
|
||||||
print(f"Shift Register Output: {format(shift_register_value, '08b')[::-1]}") # Print binary representation (mirrored, lsb first = terminal order)
|
print(f"Shift Register Output: {bin(shift_register_value)}")
|
||||||
|
|
||||||
time.sleep(0.3) # Small delay to prevent excessive polling
|
time.sleep(0.5) # Small delay to prevent excessive polling
|
||||||
|
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
print("\nExiting...")
|
print("\nExiting...")
|
||||||
|
@ -7,70 +7,14 @@ import time
|
|||||||
# Include custom files
|
# Include custom files
|
||||||
# Add the parent directory to the module search path
|
# Add the parent directory to the module search path
|
||||||
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
|
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
|
||||||
from interface_board_libs.adc_mcp3208 import MCP3208 # custom driver for ADC-IC
|
from interface_board_libs.adc_mcp3208 import MCP3208
|
||||||
from interface_board_pins import (
|
from interface_board_pins import ADC_CHANNELS
|
||||||
ADC_CHANNELS,
|
|
||||||
|
|
||||||
ADC_CHANNEL_T0__0_TO_3V3,
|
|
||||||
ADC_CHANNEL_T1__0_TO_3V3,
|
|
||||||
ADC_CHANNEL_T2__0_TO_5V,
|
|
||||||
ADC_CHANNEL_T3__0_TO_5V,
|
|
||||||
ADC_CHANNEL_T4__0_TO_12V,
|
|
||||||
ADC_CHANNEL_T5__0_TO_12V,
|
|
||||||
ADC_CHANNEL_T6__0_TO_20MA,
|
|
||||||
ADC_CHANNEL_T7__0_TO_20MA,
|
|
||||||
) # mapping of ADC channels to terminals
|
|
||||||
|
|
||||||
|
|
||||||
# create ADC instance
|
|
||||||
adc = MCP3208()
|
adc = MCP3208()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# local helper function to scale the adc value to an actual voltage
|
|
||||||
def adc2value(adc_value, max_value):
|
|
||||||
max_adc = 4095
|
|
||||||
return adc_value * max_value / max_adc
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
print ("")
|
print ("==================")
|
||||||
|
for i in range(8):
|
||||||
# Read all available channels in a loop according to terminal order / map
|
print('ADC[{}]: {:.2f}'.format(i, adc.read(i)))
|
||||||
values = []
|
time.sleep(0.5)
|
||||||
for terminal, adc_channel in ADC_CHANNELS.items():
|
|
||||||
value = adc.read(adc_channel)
|
|
||||||
print(f"T{terminal}: ADC={value:04d} => U_ADC = {adc2value(value, 3.3):5.3f}V")
|
|
||||||
|
|
||||||
|
|
||||||
# Read channels one by one using defined constants (more intuitive)
|
|
||||||
print("-" * 40)
|
|
||||||
value = adc.read(ADC_CHANNEL_T0__0_TO_3V3)
|
|
||||||
print(f"Terminal 0 (0 to 3.3V): ADC={value:04d} => Terminal={adc2value(value, 3.3):4.2f}V")
|
|
||||||
|
|
||||||
value = adc.read(ADC_CHANNEL_T1__0_TO_3V3)
|
|
||||||
print(f"Terminal 1 (0 to 3.3V): ADC={value:04d} => Terminal={adc2value(value, 3.3):4.2f}V")
|
|
||||||
|
|
||||||
value = adc.read(ADC_CHANNEL_T2__0_TO_5V)
|
|
||||||
print(f"Terminal 2 (0 to 5V): ADC={value:04d} => Terminal={adc2value(value, 5):4.2f}V")
|
|
||||||
|
|
||||||
value = adc.read(ADC_CHANNEL_T3__0_TO_5V)
|
|
||||||
print(f"Terminal 3 (0 to 5V): ADC={value:04d} => Terminal={adc2value(value, 5):4.2f}V")
|
|
||||||
|
|
||||||
value = adc.read(ADC_CHANNEL_T4__0_TO_12V)
|
|
||||||
print(f"Terminal 4 (0 to 12V): ADC={value:04d} => Terminal={adc2value(value, 12):05.2f}V")
|
|
||||||
|
|
||||||
value = adc.read(ADC_CHANNEL_T5__0_TO_12V)
|
|
||||||
print(f"Terminal 5 (0 to 12V): ADC={value:04d} => Terminal={adc2value(value, 12):05.2f}V")
|
|
||||||
|
|
||||||
value = adc.read(ADC_CHANNEL_T6__0_TO_20MA)
|
|
||||||
print(f"Terminal 6 (0 to 20mA): ADC={value:04d} => Terminal={adc2value(value, 20):05.2f}mA")
|
|
||||||
|
|
||||||
value = adc.read(ADC_CHANNEL_T7__0_TO_20MA)
|
|
||||||
print(f"Terminal 7 (0 to 20mA): ADC={value:04d} => Terminal={adc2value(value, 20):05.2f}mA")
|
|
||||||
|
|
||||||
print("-" * 40)
|
|
||||||
time.sleep(1) # Delay before next read cycle
|
|
||||||
|
|
||||||
|
@ -1,13 +1,12 @@
|
|||||||
# Include external libraries
|
# Include external libraries
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import RPi.GPIO as GPIO
|
|
||||||
from time import sleep
|
from time import sleep
|
||||||
|
|
||||||
|
|
||||||
# Include custom files
|
|
||||||
# Add the parent directory to the module search path
|
# Add the parent directory to the module search path
|
||||||
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
|
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
|
||||||
|
|
||||||
|
# Include custom files
|
||||||
from interface_board_libs.shift_register import ShiftRegister # custom shift register class
|
from interface_board_libs.shift_register import ShiftRegister # custom shift register class
|
||||||
from interface_board_pins import ( # pin / channel assignment
|
from interface_board_pins import ( # pin / channel assignment
|
||||||
GPIO_SHIFT_REG_DATA,
|
GPIO_SHIFT_REG_DATA,
|
||||||
@ -21,9 +20,7 @@ from interface_board_pins import ( # pin / channel assignment
|
|||||||
|
|
||||||
|
|
||||||
# Config
|
# Config
|
||||||
COUNT_UP_TEST_ENABLED = False
|
COUNT_UP_TEST_ENABLED = True
|
||||||
DELAY_COUNT_UP = 0.008
|
|
||||||
DELAY_TOGGLE = 0.3
|
|
||||||
|
|
||||||
|
|
||||||
# Initialize the shift register
|
# Initialize the shift register
|
||||||
@ -32,7 +29,6 @@ sr = ShiftRegister(GPIO_SHIFT_REG_DATA, GPIO_SHIFT_REG_LATCH, GPIO_SHIFT_REG_CLO
|
|||||||
try:
|
try:
|
||||||
print("Writing to shift register...")
|
print("Writing to shift register...")
|
||||||
|
|
||||||
|
|
||||||
# repeatedly write to shift register
|
# repeatedly write to shift register
|
||||||
while True:
|
while True:
|
||||||
# Cycle through all combinations of pin states (write entire byte)
|
# Cycle through all combinations of pin states (write entire byte)
|
||||||
@ -40,10 +36,10 @@ try:
|
|||||||
print("Writing all byte values (0-255)...")
|
print("Writing all byte values (0-255)...")
|
||||||
for value in range(256):
|
for value in range(256):
|
||||||
sr.write_byte(value) # Write the current value
|
sr.write_byte(value) # Write the current value
|
||||||
print(f"Output: {'{0:08b}'.format(value)}") # Print binary representation
|
print(f"Output: {bin(value)}") # Print binary representation
|
||||||
sleep(DELAY_COUNT_UP) # Delay between each byte
|
sleep(0.01) # Delay between each byte
|
||||||
|
|
||||||
sleep(0.5)
|
sleep(1)
|
||||||
sr.clear() # Clear the shift register
|
sr.clear() # Clear the shift register
|
||||||
|
|
||||||
|
|
||||||
@ -52,17 +48,17 @@ try:
|
|||||||
# channels 0-2 are connected to buzzer and relays:
|
# channels 0-2 are connected to buzzer and relays:
|
||||||
print(f"Num {SHIFT_REG_CHANNEL_BUZZER}: Toggling buzzer...") # channel 0
|
print(f"Num {SHIFT_REG_CHANNEL_BUZZER}: Toggling buzzer...") # channel 0
|
||||||
sr.set_pin(SHIFT_REG_CHANNEL_BUZZER, True) # Turn buzzer ON
|
sr.set_pin(SHIFT_REG_CHANNEL_BUZZER, True) # Turn buzzer ON
|
||||||
sleep(DELAY_TOGGLE)
|
sleep(0.5)
|
||||||
sr.set_pin(SHIFT_REG_CHANNEL_BUZZER, False) # Turn buzzer OFF
|
sr.set_pin(SHIFT_REG_CHANNEL_BUZZER, False) # Turn buzzer OFF
|
||||||
|
|
||||||
print(f"Num {SHIFT_REG_CHANNEL_RELAY1}: Toggling Relay 1...") # channel 1
|
print(f"Num {SHIFT_REG_CHANNEL_RELAY1}: Toggling Relay 1...") # channel 1
|
||||||
sr.toggle_pin(SHIFT_REG_CHANNEL_RELAY1)
|
sr.toggle_pin(SHIFT_REG_CHANNEL_RELAY1)
|
||||||
sleep(DELAY_TOGGLE)
|
sleep(0.5)
|
||||||
sr.toggle_pin(SHIFT_REG_CHANNEL_RELAY1)
|
sr.toggle_pin(SHIFT_REG_CHANNEL_RELAY1)
|
||||||
|
|
||||||
print(f"Num {SHIFT_REG_CHANNEL_RELAY2}: Toggling Relay 2...") # channel 2
|
print(f"Num {SHIFT_REG_CHANNEL_RELAY2}: Toggling Relay 2...") # channel 2
|
||||||
sr.set_pin(SHIFT_REG_CHANNEL_RELAY2, True) # Turn relay ON
|
sr.set_pin(SHIFT_REG_CHANNEL_RELAY2, True) # Turn relay ON
|
||||||
sleep(DELAY_TOGGLE)
|
sleep(0.5)
|
||||||
sr.set_pin(SHIFT_REG_CHANNEL_RELAY2, False) # Turn relay OFF
|
sr.set_pin(SHIFT_REG_CHANNEL_RELAY2, False) # Turn relay OFF
|
||||||
|
|
||||||
|
|
||||||
@ -70,7 +66,7 @@ try:
|
|||||||
for pin in range(3,8): # 3-7 are connected to terminal only
|
for pin in range(3,8): # 3-7 are connected to terminal only
|
||||||
print(f"Num: {pin}: Toggling Terminal")
|
print(f"Num: {pin}: Toggling Terminal")
|
||||||
sr.set_pin(pin, True) # Set the pin HIGH
|
sr.set_pin(pin, True) # Set the pin HIGH
|
||||||
sleep(DELAY_TOGGLE)
|
sleep(0.5)
|
||||||
#print(f"Setting pin {pin} LOW.")
|
#print(f"Setting pin {pin} LOW.")
|
||||||
sr.set_pin(pin, False) # Set the pin LOW
|
sr.set_pin(pin, False) # Set the pin LOW
|
||||||
#sleep(0.5)
|
#sleep(0.5)
|
||||||
|
@ -1,76 +0,0 @@
|
|||||||
# Include external libraries
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import RPi.GPIO as GPIO
|
|
||||||
from time import sleep
|
|
||||||
|
|
||||||
|
|
||||||
# Include custom files
|
|
||||||
# Add the parent directory to the module search path
|
|
||||||
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
|
|
||||||
from interface_board_pins import ( # pin / channel assignment
|
|
||||||
GPIO_PWM1, # RPI_PWM0
|
|
||||||
GPIO_PWM2 # RPI_PWM0 too
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Config
|
|
||||||
BLINK_ONLY = False
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# PWM Settings
|
|
||||||
FREQ = 1000 # PWM frequency in Hz
|
|
||||||
STEP = 2 # Step size for fading
|
|
||||||
DELAY = 0.03 # Delay between steps
|
|
||||||
|
|
||||||
try:
|
|
||||||
print("Configuring PWM pins...")
|
|
||||||
GPIO.setmode(GPIO.BCM)
|
|
||||||
GPIO.setup(GPIO_PWM1, GPIO.OUT)
|
|
||||||
GPIO.setup(GPIO_PWM2, GPIO.OUT)
|
|
||||||
|
|
||||||
|
|
||||||
if BLINK_ONLY:
|
|
||||||
while True:
|
|
||||||
print(f"PWM1 ON (GPIO {GPIO_PWM1})")
|
|
||||||
GPIO.output(GPIO_PWM1, 1)
|
|
||||||
sleep(2)
|
|
||||||
print("PWM1 OFF")
|
|
||||||
GPIO.output(GPIO_PWM1, 0)
|
|
||||||
print(f"PWM2 ON (GPIO {GPIO_PWM2})")
|
|
||||||
GPIO.output(GPIO_PWM2, 1)
|
|
||||||
sleep(2)
|
|
||||||
print("PWM2 OFF")
|
|
||||||
GPIO.output(GPIO_PWM2, 0)
|
|
||||||
|
|
||||||
|
|
||||||
# Initialize PWM on both pins
|
|
||||||
pwm1 = GPIO.PWM(GPIO_PWM1, FREQ)
|
|
||||||
pwm2 = GPIO.PWM(GPIO_PWM2, FREQ)
|
|
||||||
|
|
||||||
pwm1.start(0) # Start with 0% duty cycle
|
|
||||||
pwm2.start(100) # Start with 100% duty cycle
|
|
||||||
|
|
||||||
print("Starting PWM fade effect...")
|
|
||||||
while True:
|
|
||||||
# Fade up PWM1 and fade down PWM2
|
|
||||||
for duty in range(0, 101, STEP): # Duty cycle from 0% to 100%
|
|
||||||
pwm1.ChangeDutyCycle(duty)
|
|
||||||
pwm2.ChangeDutyCycle(100 - duty) # Opposite fade
|
|
||||||
print(f"PWM1: {duty}% | PWM2: {100 - duty}%")
|
|
||||||
sleep(DELAY)
|
|
||||||
|
|
||||||
# Fade down PWM1 and fade up PWM2
|
|
||||||
for duty in range(100, -1, -STEP): # Duty cycle from 100% to 0%
|
|
||||||
pwm1.ChangeDutyCycle(duty)
|
|
||||||
pwm2.ChangeDutyCycle(100 - duty) # Opposite fade
|
|
||||||
print(f"PWM1: {duty}% | PWM2: {100 - duty}%")
|
|
||||||
sleep(DELAY)
|
|
||||||
|
|
||||||
finally:
|
|
||||||
print("Exiting, stopping PWM and cleaning up...")
|
|
||||||
pwm1.stop()
|
|
||||||
pwm2.stop()
|
|
||||||
GPIO.cleanup() # Clean up GPIO settings
|
|
@ -1,47 +0,0 @@
|
|||||||
import os
|
|
||||||
import sys
|
|
||||||
import tkinter as tk
|
|
||||||
from tkinter import ttk
|
|
||||||
import RPi.GPIO as GPIO
|
|
||||||
|
|
||||||
# Add the parent directory to the module search path
|
|
||||||
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
|
|
||||||
from interface_board_libs.adc_mcp3208 import MCP3208
|
|
||||||
from interface_board_libs.shift_register import ShiftRegister
|
|
||||||
from interface_board_pins import * # Import pin assignments
|
|
||||||
from tab_control import create_control_tab
|
|
||||||
from tab_adc_plot import create_adc_plot_tab
|
|
||||||
from tab_digital_plot import create_digital_plot_tab
|
|
||||||
|
|
||||||
# Initialize ADC & Shift Register
|
|
||||||
adc = MCP3208()
|
|
||||||
shift_reg = ShiftRegister(GPIO_SHIFT_REG_DATA, GPIO_SHIFT_REG_LATCH, GPIO_SHIFT_REG_CLOCK)
|
|
||||||
|
|
||||||
# GPIO Setup
|
|
||||||
GPIO.setmode(GPIO.BCM)
|
|
||||||
for pin in GPIO_DIGITAL_INPUTS.values():
|
|
||||||
GPIO.setup(pin, GPIO.IN)
|
|
||||||
GPIO.setup(GPIO_PWM1, GPIO.OUT)
|
|
||||||
GPIO.setup(GPIO_PWM2, GPIO.OUT)
|
|
||||||
pwm1 = GPIO.PWM(GPIO_PWM1, 1000)
|
|
||||||
pwm2 = GPIO.PWM(GPIO_PWM2, 1000)
|
|
||||||
pwm1.start(0)
|
|
||||||
pwm2.start(0)
|
|
||||||
|
|
||||||
# Tkinter GUI
|
|
||||||
root = tk.Tk()
|
|
||||||
root.title("Raspberry Pi Interface Board")
|
|
||||||
root.attributes('-fullscreen', True)
|
|
||||||
root.configure(bg="black")
|
|
||||||
|
|
||||||
# Tabbed Interface
|
|
||||||
notebook = ttk.Notebook(root)
|
|
||||||
notebook.pack(expand=True, fill="both")
|
|
||||||
|
|
||||||
# Add tabs
|
|
||||||
create_control_tab(notebook, adc, shift_reg, pwm1, pwm2)
|
|
||||||
create_adc_plot_tab(notebook, adc)
|
|
||||||
create_digital_plot_tab(notebook)
|
|
||||||
|
|
||||||
# Run GUI
|
|
||||||
root.mainloop()
|
|
@ -1,52 +0,0 @@
|
|||||||
import tkinter as tk
|
|
||||||
from tkinter import ttk
|
|
||||||
from matplotlib.figure import Figure
|
|
||||||
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
|
|
||||||
import time
|
|
||||||
|
|
||||||
ADC_PLOT_UPDATE_INTERVAL = 100
|
|
||||||
|
|
||||||
def create_adc_plot_tab(notebook, adc):
|
|
||||||
frame = ttk.Frame(notebook)
|
|
||||||
notebook.add(frame, text="ADC Plot")
|
|
||||||
|
|
||||||
figure = Figure(figsize=(8, 5), dpi=100)
|
|
||||||
ax = figure.add_subplot(1, 1, 1)
|
|
||||||
ax.set_title("ADC Readings Over Time")
|
|
||||||
ax.set_xlabel("Time (s)")
|
|
||||||
ax.set_ylabel("Voltage (V)")
|
|
||||||
ax.set_ylim(0, 12)
|
|
||||||
|
|
||||||
canvas = FigureCanvasTkAgg(figure, master=frame)
|
|
||||||
canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True)
|
|
||||||
|
|
||||||
adc_channels = list(range(8))
|
|
||||||
data = {ch: [] for ch in adc_channels}
|
|
||||||
time_data = []
|
|
||||||
|
|
||||||
def update_plot():
|
|
||||||
current_time = time.time()
|
|
||||||
if len(time_data) > 50:
|
|
||||||
for ch in adc_channels:
|
|
||||||
data[ch].pop(0)
|
|
||||||
time_data.pop(0)
|
|
||||||
|
|
||||||
time_data.append(current_time)
|
|
||||||
for ch in adc_channels:
|
|
||||||
voltage = round(adc.read(ch) * 12 / 4095, 2)
|
|
||||||
data[ch].append(voltage)
|
|
||||||
|
|
||||||
ax.clear()
|
|
||||||
ax.set_title("ADC Readings Over Time")
|
|
||||||
ax.set_xlabel("Time (s)")
|
|
||||||
ax.set_ylabel("Voltage (V)")
|
|
||||||
ax.set_ylim(0, 12)
|
|
||||||
|
|
||||||
for ch in adc_channels:
|
|
||||||
ax.plot(time_data, data[ch], label=f"ADC {ch+1}")
|
|
||||||
|
|
||||||
ax.legend(loc="upper right")
|
|
||||||
canvas.draw()
|
|
||||||
frame.after(ADC_PLOT_UPDATE_INTERVAL, update_plot)
|
|
||||||
|
|
||||||
update_plot()
|
|
@ -1,62 +0,0 @@
|
|||||||
import sys
|
|
||||||
import os
|
|
||||||
import tkinter as tk
|
|
||||||
from tkinter import ttk
|
|
||||||
import RPi.GPIO as GPIO
|
|
||||||
|
|
||||||
# Add the parent directory to the module search path
|
|
||||||
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
|
|
||||||
from interface_board_pins import * # Import pin assignments
|
|
||||||
|
|
||||||
def create_control_tab(notebook, adc, shift_reg, pwm1, pwm2):
|
|
||||||
frame = ttk.Frame(notebook)
|
|
||||||
notebook.add(frame, text="Controls")
|
|
||||||
|
|
||||||
digital_input_states = [tk.StringVar(value="LOW") for _ in range(8)]
|
|
||||||
digital_output_states = [tk.BooleanVar(value=False) for _ in range(8)]
|
|
||||||
adc_values = [tk.StringVar(value="0.00V") for _ in range(8)]
|
|
||||||
pwm_values = [tk.IntVar(value=0), tk.IntVar(value=0)]
|
|
||||||
|
|
||||||
def update_inputs():
|
|
||||||
for i, pin in enumerate(GPIO_DIGITAL_INPUTS.values()):
|
|
||||||
digital_input_states[i].set("HIGH" if GPIO.input(pin) else "LOW")
|
|
||||||
frame.after(500, update_inputs)
|
|
||||||
|
|
||||||
def update_adc():
|
|
||||||
for i, adc_channel in enumerate(ADC_CHANNELS.values()):
|
|
||||||
value = adc.read(adc_channel)
|
|
||||||
adc_values[i].set(f"{round(value * 12 / 4095, 2)}V")
|
|
||||||
frame.after(1000, update_adc)
|
|
||||||
|
|
||||||
def toggle_output(index):
|
|
||||||
shift_reg.set_pin(index, digital_output_states[index].get())
|
|
||||||
|
|
||||||
def update_pwm(channel, value):
|
|
||||||
duty_cycle = int(float(value))
|
|
||||||
if channel == 0:
|
|
||||||
pwm1.ChangeDutyCycle(duty_cycle)
|
|
||||||
else:
|
|
||||||
pwm2.ChangeDutyCycle(duty_cycle)
|
|
||||||
|
|
||||||
# UI Layout
|
|
||||||
style = ttk.Style()
|
|
||||||
style.configure("TScale", thickness=30) # Increases slider thickness
|
|
||||||
|
|
||||||
control_frame = ttk.Frame(frame, padding=30)
|
|
||||||
control_frame.pack(expand=True, fill="both")
|
|
||||||
|
|
||||||
for i in range(8):
|
|
||||||
ttk.Label(control_frame, text=f"ADC {i+1}:", font=("Arial", 14)).grid(row=i, column=0, sticky="e")
|
|
||||||
ttk.Label(control_frame, textvariable=adc_values[i], width=10, font=("Arial", 14)).grid(row=i, column=1, sticky="w")
|
|
||||||
ttk.Label(control_frame, text=f"IN {i+1}:", font=("Arial", 14)).grid(row=i, column=2, sticky="e")
|
|
||||||
ttk.Label(control_frame, textvariable=digital_input_states[i], width=6, font=("Arial", 14)).grid(row=i, column=3, sticky="w")
|
|
||||||
btn = ttk.Checkbutton(control_frame, text=f"OUT {i+1}", variable=digital_output_states[i], command=lambda i=i: toggle_output(i))
|
|
||||||
btn.grid(row=i, column=4, sticky="w")
|
|
||||||
|
|
||||||
for i in range(2):
|
|
||||||
ttk.Label(control_frame, text=f"PWM{i+1}:", font=("Arial", 14)).grid(row=i, column=5, sticky="e")
|
|
||||||
slider = ttk.Scale(control_frame, from_=0, to=100, orient="horizontal", length=400, variable=pwm_values[i], command=lambda val, i=i: update_pwm(i, val), style="TScale")
|
|
||||||
slider.grid(row=i, column=6, sticky="w", pady=10) # Added spacing with `pady=10`
|
|
||||||
|
|
||||||
update_inputs()
|
|
||||||
update_adc()
|
|
@ -1,56 +0,0 @@
|
|||||||
import sys
|
|
||||||
import os
|
|
||||||
import tkinter as tk
|
|
||||||
from tkinter import ttk
|
|
||||||
import RPi.GPIO as GPIO
|
|
||||||
from matplotlib.figure import Figure
|
|
||||||
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
|
|
||||||
import time
|
|
||||||
# Add the parent directory to the module search path
|
|
||||||
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
|
|
||||||
from interface_board_pins import * # Import pin assignments
|
|
||||||
|
|
||||||
def create_digital_plot_tab(notebook):
|
|
||||||
frame = ttk.Frame(notebook)
|
|
||||||
notebook.add(frame, text="Digital Inputs")
|
|
||||||
|
|
||||||
figure = Figure(figsize=(8, 5), dpi=100)
|
|
||||||
ax = figure.add_subplot(1, 1, 1)
|
|
||||||
ax.set_title("Digital Input States Over Time")
|
|
||||||
ax.set_xlabel("Time (s)")
|
|
||||||
ax.set_ylabel("State (0=LOW, 1=HIGH)")
|
|
||||||
ax.set_ylim(-0.2, 1.2)
|
|
||||||
|
|
||||||
canvas = FigureCanvasTkAgg(figure, master=frame)
|
|
||||||
canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True)
|
|
||||||
|
|
||||||
input_channels = list(range(8))
|
|
||||||
data = {ch: [] for ch in input_channels}
|
|
||||||
time_data = []
|
|
||||||
|
|
||||||
def update_plot():
|
|
||||||
current_time = time.time()
|
|
||||||
if len(time_data) > 50:
|
|
||||||
for ch in input_channels:
|
|
||||||
data[ch].pop(0)
|
|
||||||
time_data.pop(0)
|
|
||||||
|
|
||||||
time_data.append(current_time)
|
|
||||||
for ch in input_channels:
|
|
||||||
state = GPIO.input(GPIO_DIGITAL_INPUTS[ch])
|
|
||||||
data[ch].append(state)
|
|
||||||
|
|
||||||
ax.clear()
|
|
||||||
ax.set_title("Digital Input States Over Time")
|
|
||||||
ax.set_xlabel("Time (s)")
|
|
||||||
ax.set_ylabel("State (0=LOW, 1=HIGH)")
|
|
||||||
ax.set_ylim(-0.2, 1.2)
|
|
||||||
|
|
||||||
for ch in input_channels:
|
|
||||||
ax.step(time_data, data[ch], label=f"IN {ch+1}", where="post")
|
|
||||||
|
|
||||||
ax.legend(loc="upper right")
|
|
||||||
canvas.draw()
|
|
||||||
frame.after(500, update_plot)
|
|
||||||
|
|
||||||
update_plot()
|
|
@ -1,9 +1,6 @@
|
|||||||
import spidev
|
import spidev
|
||||||
import time
|
import time
|
||||||
|
|
||||||
DEBUG = False
|
|
||||||
|
|
||||||
|
|
||||||
class MCP3208:
|
class MCP3208:
|
||||||
def __init__(self, bus=0, device=0):
|
def __init__(self, bus=0, device=0):
|
||||||
# Initialize SPI bus and device
|
# Initialize SPI bus and device
|
||||||
@ -12,55 +9,36 @@ class MCP3208:
|
|||||||
self.spi.max_speed_hz = 1000000 # Adjust based on your needs (e.g., 1MHz)
|
self.spi.max_speed_hz = 1000000 # Adjust based on your needs (e.g., 1MHz)
|
||||||
self.spi.mode = 0b00 # Set SPI mode (Mode 0 for MCP3208)
|
self.spi.mode = 0b00 # Set SPI mode (Mode 0 for MCP3208)
|
||||||
|
|
||||||
|
|
||||||
def read(self, channel):
|
def read(self, channel):
|
||||||
"""
|
"""
|
||||||
Read the ADC value from the specified channel (0-7).
|
Read the ADC value from the specified channel (0-7).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# MCP3208 is a 12-bit SPI ADC. Communication requires sending a 3-byte command and receiving a 3-byte response.
|
|
||||||
# The bits in the command sequence are structured as follows:
|
|
||||||
|
|
||||||
# Command Byte (8 bits):
|
|
||||||
# | Start (1) | Mode (1) | Channel (3) | Padding (3) |
|
|
||||||
|
|
||||||
# The MCP3208 responds with 3 bytes:
|
|
||||||
# - Byte 1: Contains the highest bit (bit 11) of the 12-bit ADC result.
|
|
||||||
# - Byte 2: Contains bits 10-3 of the ADC result.
|
|
||||||
# - Byte 3: Contains bits 2-0 of the ADC result.
|
|
||||||
|
|
||||||
# Ensure the channel is valid (0-7)
|
|
||||||
if channel < 0 or channel > 7:
|
if channel < 0 or channel > 7:
|
||||||
raise ValueError(f"Channel must be between 0 and 7. (current channel={channel})")
|
raise ValueError("Channel must be between 0 and 7.")
|
||||||
|
|
||||||
# Construct the command byte sequence:
|
# MCP3208 sends a 3-byte response, which needs to be processed
|
||||||
# - Start bit (1) -> 1000 0000 (0x80)
|
# Start with the single bit control byte, followed by the channel selection
|
||||||
# - Single-ended (1) -> 1100 0000 (0xC0)
|
# MCP3208 uses 3 bits for the channel: 0-7
|
||||||
# - Channel (3 bits) shifted into position
|
# The command byte looks like this:
|
||||||
cmd = 0x80 # Start bit: 1000 0000
|
# | Start | Single-ended | Channel (3 bits) | Don't Care (1 bit) |
|
||||||
cmd |= 0x40 # Single-ended mode: 1100 0000
|
|
||||||
cmd |= (channel & 0x07) << 3 # Move channel number to bits 5-3
|
# Construct the 3-byte command
|
||||||
|
# Start bit (1) | Single-ended (1) | Channel (3 bits) | Don't care (1) | End (1)
|
||||||
|
command = [1, (8 + channel) << 4, 0]
|
||||||
|
|
||||||
# Send the command and receive the 3-byte response
|
# Send command and receive the 3-byte result
|
||||||
ret = self.spi.xfer2([cmd, 0x00, 0x00]) # Send 3 bytes, receive 3 bytes
|
result = self.spi.xfer2(command)
|
||||||
|
|
||||||
if DEBUG:
|
# Combine the result bytes (result is a list of 3 bytes)
|
||||||
print(f"result[0]: {bin(ret[0])}, result[1]: {bin(ret[1])}, result[2]: {bin(ret[2])}")
|
# First byte is ignored, we want the 2nd and 3rd bytes for our result
|
||||||
|
value = ((result[1] & 0x03) << 8) | result[2] # Convert to 10-bit value (0-1023)
|
||||||
|
|
||||||
# Extract the 12-bit ADC result from the received bytes:
|
return value
|
||||||
val = (ret[0] & 0x01) << 11 # Extract bit 11 (MSB)
|
|
||||||
val |= ret[1] << 3 # Extract bits 10-3
|
|
||||||
val |= ret[2] >> 5 # Extract bits 2-0 (shift down)
|
|
||||||
|
|
||||||
return val & 0x0FFF # Mask to ensure only the lower 12 bits are used
|
|
||||||
|
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
"""Close SPI connection."""
|
"""Close SPI connection."""
|
||||||
self.spi.close()
|
self.spi.close()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Example usage:
|
# Example usage:
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
adc = MCP3208()
|
adc = MCP3208()
|
||||||
@ -72,3 +50,4 @@ if __name__ == "__main__":
|
|||||||
time.sleep(0.5)
|
time.sleep(0.5)
|
||||||
finally:
|
finally:
|
||||||
adc.close()
|
adc.close()
|
||||||
|
|
||||||
|
@ -2,44 +2,38 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# ======================
|
|
||||||
# === Digital Inputs ===
|
# === Digital Inputs ===
|
||||||
# ======================
|
|
||||||
# Pin mappings for digital inputs (labeled on housing as 1-8)
|
# Pin mappings for digital inputs (labeled on housing as 1-8)
|
||||||
GPIO_DIGITAL_INPUTS = {
|
GPIO_DIGITAL_INPUTS = {
|
||||||
0: 25, # Dig-IN_1 is connected to GPIO_25
|
1: 25, # Dig-IN_1 is connected to GPIO_25
|
||||||
1: 16, # Dig-IN_2 is connected to GPIO_16
|
2: 16, # Dig-IN_2 is connected to GPIO_16
|
||||||
2: 26, # Dig-IN_3 is connected to GPIO_26
|
3: 26, # Dig-IN_3 is connected to GPIO_26
|
||||||
3: 13, # Dig-IN_4 is connected to GPIO_13
|
4: 13, # Dig-IN_4 is connected to GPIO_13
|
||||||
4: 6, # Dig-IN_5 is connected to GPIO_6
|
5: 6, # Dig-IN_5 is connected to GPIO_6
|
||||||
5: 5, # Dig-IN_6 is connected to GPIO_5
|
6: 5, # Dig-IN_6 is connected to GPIO_5
|
||||||
6: 22, # Dig-IN_7 is connected to GPIO_22
|
7: 22, # Dig-IN_7 is connected to GPIO_22
|
||||||
7: 24, # Dig-IN_8 is connected to GPIO_24
|
8: 24, # Dig-IN_8 is connected to GPIO_24
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# ======================
|
|
||||||
# === Shift Register ===
|
# === Shift Register ===
|
||||||
# ======================
|
|
||||||
GPIO_SHIFT_REG_DATA = 27
|
GPIO_SHIFT_REG_DATA = 27
|
||||||
GPIO_SHIFT_REG_LATCH = 17
|
GPIO_SHIFT_REG_LATCH = 17
|
||||||
GPIO_SHIFT_REG_CLOCK = 4
|
GPIO_SHIFT_REG_CLOCK = 4
|
||||||
|
|
||||||
|
|
||||||
# FIXME: numbering in schematic is wrong (inverse) layout / terminal order matches the shift register though (left to right -> 0-7)
|
# FIXME: numbering in schematic is wrong (inverse) layout / terminal order matches the shift register though (left to right -> 0-7)
|
||||||
# Shift Register Channel Assignments
|
# Shift Register Channel Assignments
|
||||||
SHIFT_REG_CHANNEL_BUZZER = 0 # Buzzer connected to shift register channel 0
|
SHIFT_REG_CHANNEL_BUZZER = 0 # Buzzer connected to shift register channel 7
|
||||||
SHIFT_REG_CHANNEL_RELAY1 = 2 # Relay 1 connected to shift register channel 2
|
SHIFT_REG_CHANNEL_RELAY1 = 1 # Relay 1 connected to shift register channel 6
|
||||||
SHIFT_REG_CHANNEL_RELAY2 = 1 # Relay 2 connected to shift register channel 1
|
SHIFT_REG_CHANNEL_RELAY2 = 2 # Relay 2 connected to shift register channel 5
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# ===============
|
# === ADC ===
|
||||||
# ===== ADC =====
|
|
||||||
# ===============
|
|
||||||
# ADC IC is connected to RPI SPI interface 0 (pins below)
|
# ADC IC is connected to RPI SPI interface 0 (pins below)
|
||||||
ADC_SPI_BUS_NUM = 0
|
ADC_SPI_BUS_NUM = 0
|
||||||
ADC_SPI_DEVICE_NUM = 0
|
ADC_SPI_DEVICE_NUM = 0
|
||||||
@ -49,34 +43,21 @@ ADC_SPI_CS_PIN = 8 # SPI Chip Select for MCP3208
|
|||||||
# SCLK_0: GPIO_11
|
# SCLK_0: GPIO_11
|
||||||
# CE_0: GPIO# MCP3208 (ADC)
|
# CE_0: GPIO# MCP3208 (ADC)
|
||||||
|
|
||||||
# Pin mappings for Terminal number to actual ADC channels (due to routing they do not match)
|
|
||||||
ADC_CHANNELS = {
|
ADC_CHANNELS = {
|
||||||
0: 1, # Terminal 0 = ADC channel 1
|
1: 0, # Channel 1 = ADC channel 0
|
||||||
1: 0, # Terminal 1 = ADC channel 0
|
2: 1, # Channel 2 = ADC channel 1
|
||||||
2: 3, # ...
|
3: 2, # ...
|
||||||
3: 2,
|
4: 3,
|
||||||
4: 5,
|
|
||||||
5: 4,
|
5: 4,
|
||||||
6: 7,
|
6: 5,
|
||||||
7: 6
|
7: 6,
|
||||||
|
8: 7
|
||||||
}
|
}
|
||||||
|
|
||||||
# Alternative to ADC_CHANNELS list have separate constants for the channels (more intuitive)
|
|
||||||
ADC_CHANNEL_T0__0_TO_3V3 = 1
|
|
||||||
ADC_CHANNEL_T1__0_TO_3V3 = 0
|
|
||||||
ADC_CHANNEL_T2__0_TO_5V = 3
|
|
||||||
ADC_CHANNEL_T3__0_TO_5V = 2
|
|
||||||
ADC_CHANNEL_T4__0_TO_12V = 5
|
|
||||||
ADC_CHANNEL_T5__0_TO_12V = 4
|
|
||||||
ADC_CHANNEL_T6__0_TO_20MA = 7
|
|
||||||
ADC_CHANNEL_T7__0_TO_20MA = 6
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# ====================
|
|
||||||
# === SPI Terminal ===
|
# === SPI Terminal ===
|
||||||
# ====================
|
|
||||||
SPI_BUS_NUM = 1
|
SPI_BUS_NUM = 1
|
||||||
# MISO_1: GPIO_19
|
# MISO_1: GPIO_19
|
||||||
# MOSI_1: GPIO_20
|
# MOSI_1: GPIO_20
|
||||||
@ -86,27 +67,21 @@ SPI_BUS_NUM = 1
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
# ====================
|
|
||||||
# === I2C Terminal ===
|
# === I2C Terminal ===
|
||||||
# ====================
|
|
||||||
GPIO_I2C_SDA = 2
|
GPIO_I2C_SDA = 2
|
||||||
GPIO_I2C_SCL = 3
|
GPIO_I2C_SCL = 3
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# ===================
|
|
||||||
# === PWM outputs ===
|
# === PWM outputs ===
|
||||||
# ===================
|
GPIO_PWM1 = 12 # RPI_PWM0
|
||||||
GPIO_PWM1 = 18 # RPI_PWM0
|
GPIO_PWM2 = 18 # RPI_PWM0 too
|
||||||
GPIO_PWM2 = 12 # RPI_PWM0 too
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# ====================
|
|
||||||
# === UART / RS485 ===
|
# === UART / RS485 ===
|
||||||
# ====================
|
|
||||||
GPIO_UART_TX = 14 # RPI TXD
|
GPIO_UART_TX = 14 # RPI TXD
|
||||||
GPIO_UART_RX = 15 # RPI RXD
|
GPIO_UART_RX = 15 # RPI RXD
|
||||||
GPIO_UART_DIR = 23
|
GPIO_UART_DIR = 23
|
||||||
|
Loading…
x
Reference in New Issue
Block a user