Skip to content

Commit

Permalink
Merge pull request #40 from MWLCDev/RFC31_RealyControlGPIO
Browse files Browse the repository at this point in the history
RFC31_Control Relays through GPIO socket on Nano and Pi
Change folders directory 
Add a python script wrapper to deal with balena CLI
Update docker image for `pi` services
  • Loading branch information
Ahelsamahy authored Oct 17, 2024
2 parents 2cd5a8e + cd520cb commit 0212c78
Show file tree
Hide file tree
Showing 222 changed files with 1,252 additions and 1,631 deletions.
42 changes: 0 additions & 42 deletions .github/workflows/docker-compose-test.yml

This file was deleted.

16 changes: 9 additions & 7 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
# mkdocs documentation
/site

# folders
data_root
docs/
pi4_runtime/BYODR_utils
jetson_runtime/BYODR_utils

# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
Expand Down Expand Up @@ -103,16 +112,9 @@ venv.bak/
# Rope project settings
.ropeproject

# mkdocs documentation
/site

# mypy
.mypy_cache/

# folders
data_root
docs/

# Google protocol buffers
*.pb
/.balena/
5 changes: 4 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
{
"cSpell.words": [
"teleop"
"BYODR",
"GPIO",
"Jetson",
"teleop"
],
"python.analysis.typeCheckingMode": "off",
"liveServer.settings.port": 5501,
Expand Down
41 changes: 41 additions & 0 deletions BYODR_utils/JETSON_specific/gpio_relay.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
from __future__ import absolute_import

import threading

import Jetson.GPIO as GPIO # type: ignore


class ThreadSafeJetsonGpioRelay(object):
"""
Thread-safe class for managing a GPIO relay on a Jetson Nano.
"""

def __init__(self, pin=15):
self.pin = pin
self.state = False # False for OFF, True for ON
self.lock = threading.Lock()
GPIO.setmode(GPIO.BOARD) # Set the pin numbering system to BOARD
GPIO.setup(self.pin, GPIO.OUT, initial=GPIO.LOW)

def open(self):
"""Turns the relay ON (sets the GPIO pin LOW)."""
with self.lock:
GPIO.output(self.pin, GPIO.LOW)
self.state = False

def close(self):
"""Turns the relay OFF (sets the GPIO pin HIGH)."""
with self.lock:
GPIO.output(self.pin, GPIO.HIGH)
self.state = True

def toggle(self):
"""Toggles the relay state."""
with self.lock:
self.state = not self.state
GPIO.output(self.pin, GPIO.LOW if self.state else GPIO.HIGH)

def states(self):
"""Returns the current state of the relay."""
with self.lock:
return self.state
20 changes: 20 additions & 0 deletions BYODR_utils/JETSON_specific/utilities.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import subprocess


class Nano:
@staticmethod
def get_ip_address():
try:
ip_addresses = (
subprocess.check_output(
"hostname -I | awk '{for (i=1; i<=NF; i++) if ($i ~ /^192\\.168\\./) print $i}'",
shell=True,
)
.decode()
.strip()
)
# Split in case there are multiple local IP addresses
return ip_addresses
except subprocess.CalledProcessError as e:
print(f"An error occurred: {e}")
return None
48 changes: 48 additions & 0 deletions BYODR_utils/PI_specific/gpio_relay.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
from __future__ import absolute_import

import threading

import RPi.GPIO as GPIO # type: ignore


class ThreadSafePi4GpioRelay:
"""Thread-safe class for managing a GPIO relay on a Raspberry Pi."""

def __init__(self, pin=15):
self.pin = pin
self.state = False # False for OFF, True for ON
self.lock = threading.Lock()
GPIO.setmode(GPIO.BOARD) # Set the pin numbering system to BOARD
GPIO.setwarnings(False)
GPIO.setup(self.pin, GPIO.OUT, initial=GPIO.LOW)

def open(self):
"""Turns the relay ON (sets the GPIO pin LOW)."""
with self.lock:
print("opened the relay")
GPIO.output(self.pin, GPIO.LOW)
self.state = False

def close(self):
"""Turns the relay OFF (sets the GPIO pin HIGH)."""
with self.lock:
GPIO.output(self.pin, GPIO.HIGH)
current_state = GPIO.input(self.pin)
print(f"closed the relay, current state: {current_state}")
self.state = True

def toggle(self):
"""Toggles the relay state."""
with self.lock:
self.state = not self.state
GPIO.output(self.pin, GPIO.LOW if self.state else GPIO.HIGH)

def get_state(self):
"""Returns the current state of the relay."""
with self.lock:
return self.state

def cleanup(self):
"""Cleans up the GPIO state."""
GPIO.cleanup(self.pin) # Reset the specific pin before setup
GPIO.setup(self.pin, GPIO.OUT, initial=GPIO.LOW)
20 changes: 20 additions & 0 deletions BYODR_utils/PI_specific/utilities.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import subprocess


class RaspberryPi:
@staticmethod
def get_ip_address():
try:
ip_addresses = (
subprocess.check_output(
"hostname -I | awk '{for (i=1; i<=NF; i++) if ($i ~ /^192\\.168\\./) print $i}'",
shell=True,
)
.decode()
.strip()
)
# Split in case there are multiple local IP addresses
return ip_addresses
except subprocess.CalledProcessError as e:
print(f"An error occurred: {e}")
return None
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import numpy as np
import six

from byodr.utils.option import hash_dict
from BYODR_utils.common.option import hash_dict

logger = logging.getLogger(__name__)

Expand Down
2 changes: 1 addition & 1 deletion common/byodr/utils/ipc.py → BYODR_utils/common/ipc.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import numpy as np
import zmq

from byodr.utils import timestamp
from BYODR_utils.common import timestamp

if sys.version_info > (3,):
# noinspection PyShadowingBuiltins
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
import threading
from abc import ABCMeta, abstractmethod

from byodr.utils import timestamp
from BYODR_utils.common import timestamp


logger = logging.getLogger(__name__)

Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from __future__ import absolute_import

from byodr.utils import timestamp
from BYODR_utils.common import timestamp



class MessageStreamProtocol(object):
Expand Down
18 changes: 0 additions & 18 deletions common/byodr/utils/ssh.py → BYODR_utils/common/ssh.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,21 +126,3 @@ def fetch_ssid(self):
time.sleep(1)
return output


class Nano:
@staticmethod
def get_ip_address():
try:
ip_addresses = (
subprocess.check_output(
"hostname -I | awk '{for (i=1; i<=NF; i++) if ($i ~ /^192\\.168\\./) print $i}'",
shell=True,
)
.decode()
.strip()
)
# Split in case there are multiple local IP addresses
return ip_addresses
except subprocess.CalledProcessError as e:
print(f"An error occurred: {e}")
return None
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -91,15 +91,17 @@ def is_attached(self):
return self._device is not None

def open(self, channel=0):
assert self.is_attached(), "The device is not attached."
# assert self.is_attached(), "The device is not attached."
self._device.ctrl_transfer(0x21, 0x09, 0x0300, 0x0000, "".join(chr(n) for n in self._device_off[channel]), 1000)

def close(self, channel=0):
assert self.is_attached(), "The device is not attached."
# assert self.is_attached(), "The device is not attached."
self._device.ctrl_transfer(0x21, 0x09, 0x0300, 0x0000, "".join(chr(n) for n in self._device_on[channel]), 1000)


class TransientMemoryRelay(object):
"""Fake class, I suppose"""

def __init__(self, num_channels=4):
self._state = [0] * num_channels

Expand Down Expand Up @@ -166,11 +168,11 @@ def is_attached(self):
return self._device is not None

def _query(self, request, value, index, length):
assert self.is_attached(), "The device is not attached."
# assert self.is_attached(), "The device is not attached."
return self._device.ctrl_transfer(self.CP210X_REQUEST_TYPE_READ, request, value, index, length)

def _write(self, request, value, index, data):
assert self.is_attached(), "The device is not attached."
# assert self.is_attached(), "The device is not attached."
return self._device.ctrl_transfer(self.CP210X_REQUEST_TYPE_WRITE, request, value, index, data)

def _set_gpio(self, index, value):
Expand Down Expand Up @@ -231,15 +233,15 @@ def set_pulse_channels(self, channels):

def open(self, channels=None):
with self._lock:
[self._relay.open(ch) for ch in self._arg_(channels)]
[self._relay.open() for ch in self._arg_(channels)]

def close(self, channels=None):
with self._lock:
for ch in self._arg_(channels):
self._relay.close(ch)
self._relay.close()
if ch in self._pulse_channels:
time.sleep(0.100)
self._relay.open(ch)
self._relay.open()

def states(self):
with self._lock:
Expand Down
File renamed without changes.
File renamed without changes.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,8 @@ We went ahead and designed a rover that can be assembled from **generally availa
* Community driven self-driving models
* Dockerized
* Free for personal use
* Ability to work with modular hardware

## License

This project is licensed under the MIT License. See the [LICENSE](./LICENSE) file for more information.
Loading

0 comments on commit 0212c78

Please sign in to comment.