hub to hub communication #1139
Replies: 2 comments 4 replies
-
This is one of the intended use cases.
For now, yes. We try all our new features there first. When they work well enough, we publish them on the stable edition for everyone to use.
Yes, you can open two windows of Pybricks Code, one for each hub.
I think you got it about right, so I'm looking forward to seeing what you'll make! |
Beta Was this translation helpful? Give feedback.
-
A simple example of a Technic hub commanded by a remote, This was meant to be just a short note about hub-to-hub. It turned out to be loooong, Sorry.IntroductionAfter the great build of "MOC-112837 FPJ Fire Engine" from Rebrickable.com, RationaleThere needs to be a siren on a fire truck. So here comes Pybricks hub-to-hub communication into play:
The sourcesBelow is the link, but they are not so large, so I will add them here, (Click on the little triangle to expand the sources) The main program to run on the Technic hub# author: Bert Lindeman
# Date 2023-07-03
# used on pybricks Firmware: v3.3.0b6
#
# Main program to control MOC-112837 Fire engine
# Meant to run on the Technic Hub built in the model
"""
The Technic hub connects with the remote controller to have a siren available.
Buttons on the remote:
Center stop the program state does not matter for this button
Right-middle-red Switch between state GREEN and RED
Left-middle-red Switch cabin light on/off (Optional)
- in state GREEN:
right.plus right.minus drive fwd and bwd
left.plus left.mius steer
- in state RED:
right.plus right.minus functions motor
left.plus left.minus Siren_volume 0 ... 100
"""
from pybricks.hubs import TechnicHub
from pybricks.parameters import Color, Port, Stop, Button, Direction
from pybricks.pupdevices import Motor, Remote, Light
from pybricks.tools import wait, StopWatch
from pybricks import version
print(version)
hub = TechnicHub(broadcast_channel=1)
watch = StopWatch()
STEERANGLE = 75 # steer motor angle
FUNCTIONSSPEED = 500 # Finctions motor speed
DRIVESPEED = 1000 # max is (was?) 1200 as reported by motor.control.limits()
CAL_MOTOR_SPEED = 200 # motorspeed to use in calibration
STATUSGREEN = 0 # 0 = state GREEN - default is GREEN
STATUSRED = 1 # 1 = state RED
MINBLETIME = 250 # Do not send a new packet within this duration (in mSec)
DEBUG = False # or False to print status messages
def use_remote(): # noqa
print("Starting 'use_remote()'")
remote_state = STATUSGREEN # the default
sirenvolume = 0 # start silent
watch.reset()
if cabin_light_available:
cabin_light.on(100)
cabin_light_on = True
# Now we can start driving, etc!
while True:
# Check which button(s) are pressed.
pressed = remote.buttons.pressed()
if Button.CENTER in pressed:
print("Got stop signal from center button:", pressed, "; So ending program at line 65")
remote.light.on(Color.CYAN)
hub.light.on(Color.CYAN)
for __ in range(5):
hub.ble.broadcast(-1)
wait(MINBLETIME) # just to be sure this packet will be sent before stopping here
raise SystemExit()
if Button.LEFT in pressed: # left MIDDLE RED button
if cabin_light_available:
if DEBUG:
print("switch cabin light from", cabin_light_on, end=" ")
cabin_light_on = not cabin_light_on
if DEBUG:
print("to ", cabin_light_on)
# cabin_light boolean already has been switched, so set the light
if not cabin_light_on:
cabin_light.off()
if DEBUG:
print("cabin light should be OFF now")
wait(10)
else:
cabin_light.on(100)
if DEBUG:
print("cabin light should be ON now")
wait(10)
while Button.LEFT in remote.buttons.pressed(): # wait until released
wait(10)
# why would I do this?
# pressed = remote.buttons.pressed()
if Button.RIGHT in pressed: # Center RED button right
remote_state += 1
if remote_state > 1:
remote_state = 0
remote.light.on(Color.GREEN)
hub.light.on(Color.GREEN)
else:
remote.light.on(Color.RED)
hub.light.on(Color.RED)
while Button.RIGHT in remote.buttons.pressed(): # wait until released
wait(10)
pressed = remote.buttons.pressed()
if remote_state == STATUSGREEN: # swap state by using the center button right (red)
# steer
steer_angle = 0
if Button.LEFT_PLUS in pressed: # turn right
steer_angle = STEERANGLE
if Button.LEFT_MINUS in pressed: # turn left
steer_angle = -STEERANGLE
# Steer to the selected angle.
steer.run_target(500, steer_angle, wait=False)
# drive
drive_speed = 0
if Button.RIGHT_PLUS in pressed: # forward
drive_speed = DRIVESPEED
if Button.RIGHT_MINUS in pressed: # backward
drive_speed = -DRIVESPEED
# Steer to the selected angle.
drive.run(drive_speed)
else: # status not GREEN, but RED
if Button.LEFT_PLUS in pressed:
if MINBLETIME < watch.time():
sirenvolume += 10
if sirenvolume > 100:
sirenvolume = 100
print("broadcast volume:", sirenvolume)
# Set the broadcast data and start broadcasting if not already doing so.
hub.ble.broadcast(sirenvolume) # simply send zero to turn off
watch.reset()
wait(10)
if Button.LEFT_MINUS in pressed:
if MINBLETIME < watch.time():
sirenvolume -= 10
if sirenvolume < 0:
sirenvolume = 0
print("broadcast volume:", sirenvolume)
# Set the broadcast data and start broadcasting if not already doing so.
hub.ble.broadcast(sirenvolume) # simply zero to turn off
watch.reset()
wait(10)
# wait until this button is no longer pressed
if not (Button.LEFT_PLUS in pressed and Button.LEFT_MINUS in pressed):
pressed = remote.buttons.pressed()
wait(10)
functions_speed = 0
if Button.RIGHT_PLUS in pressed:
functions_speed = FUNCTIONSSPEED
if Button.RIGHT_MINUS in pressed:
functions_speed = -FUNCTIONSSPEED
functions.run(functions_speed)
wait(10)
def calibrate_steer():
# Find the steering endpoint on the left and right.
# The middle is in between.
left_end = steer.run_until_stalled(0 - CAL_MOTOR_SPEED, then=Stop.HOLD)
right_end = steer.run_until_stalled(CAL_MOTOR_SPEED, then=Stop.HOLD)
# We are now at the right. Reset this angle to be half the difference.
# That puts zero in the middle.
steer.reset_angle((right_end - left_end) / 2)
steer.run_target(speed=CAL_MOTOR_SPEED, target_angle=0, wait=False)
try:
remote = Remote()
# If you want to verify the correct remote,
# no need to do that now, maybe in other situations it can be handy to check
# expect_remote_name = "Handset left"
# if remote.name() == expect_remote_name:
# pass
# else:
# print("connected to wrong remote, got '" + remote.name() + "' expected: '" + expect_remote_name)
# show we are connected
remote.light.on(Color.GREEN)
hub.light.on(Color.GREEN)
if DEBUG:
print("Connected to remote '" + remote.name() + "'")
except Exception as e: # noqa
print("NOT connected to remote, quit")
raise SystemExit()
# create instances for hub ports
functions = Motor(Port.A)
drive = Motor(Port.D, positive_direction=Direction.COUNTERCLOCKWISE)
steer = Motor(Port.C)
try:
cabin_light = Light(Port.B)
cabin_light_available = True
cabin_light.on(100)
cabin_light_on = True
except OSError:
cabin_light_available = False
cabin_light_on = False
calibrate_steer()
use_remote() The siren program to run on the Primehub# source
# /home/bert/py/pybricks/MOC-112837 - Fire_engine/MOC112837_brandweer_sirene.py
# author: Bert Lindeman
# Date 2023-07-03
# used on pybricks Firmware: v3.3.0b6
#
# Notes: This program should run on a Spike or Robot Inventor hub, as these have a speaker.
# The Technic hub in the model has no speaker.
# So the program running on the Technic hub broadcasts the requested volume
# to set the siren at the volume.
# Bluetooth BLE is used to communicate.
from pybricks.hubs import InventorHub
from pybricks.tools import wait
# Initialize the Inventor Hub
hub = InventorHub(observe_channels=[1])
# Define the siren sound notes
siren_notes_prev = [
"F4/4._", "B4/4._"
# "Gb4/4._", "B4/4._", "Gb4/4._", "B4/4.",
# "R/32"
]
siren_notes = [
"G#4/4._", "B4/4.", "R/32"
# , "G#4/4_", "B4/4.", "R/16",
]
siren_volume = 20
# Set the sound settings. Start at 20%
hub.speaker.volume(siren_volume) # Set the volume
def sound_siren(volume):
hub.speaker.volume(volume)
# Play the siren notes
for note in siren_notes:
hub.speaker.play_notes([note])
# wait(5)
while True:
# Receive broadcast from the "main" hub.
data = hub.ble.observe(1)
if data is not None:
# Data has been received in the last 1 second.
# Data was received and is less that one second old.
# (How to know how long ago???)
# *data* contains the same values in the same order
# that were passed to hub.ble.broadcast() on the sending hub.
siren_volume = data[0]
print(data, siren_volume, end=" ")
if siren_volume == -1:
print("Received stop command")
raise SystemExit()
hub.display.number(siren_volume)
sound_siren(siren_volume)
else:
sound_siren(siren_volume)
hub.display.off()
# Broadcasts are only sent every 100 milliseconds, so there is
# no reason to call the observe() method more often than that.
# but I do not want the siren to wait that long . . . .
sound_siren(siren_volume)
wait(50) OperatingRun the programs at least once on the hub ther are intended to run on. After that, the best order to start things:
If all goes well, the Technic hub connects with the remote and both lights get green. Default the hub starts in the drive mode - Green state. On the remote in Drive mode (Green state):
On the remote in Functions mode (Red state)
RemarkSetting the siren volume seems slow, but I did not attempt to make it snappier. LinksPybricks website Bert |
Beta Was this translation helpful? Give feedback.
-
I've been looking around for multi hub communication and found that hub to hub communication existed (only sense may... probably part of why I didn't see it earlier lol) and I've got a few questions on it. One, how much data is too much data? say I have two hubs (duh) one also connected to a remote and I want to take input from the remote and send it to the other hub with four motors, would I run into problems giving the speed of each motor individually or would I have to get smart with how I do my controls? two, hub to hub is just on the pybricks beta correct? three, can I control two hubs well while still being connected to a computer to get easier input than though the train remote? four, am I thinking about how this works completely wrong and need to go look into hub to hub communication more thoroughly? Thanks!
Beta Was this translation helpful? Give feedback.
All reactions