How does the stalled() function work in Pybricks-MicroPython - micropython

MicroPython 2.0 beta 5
Trying to understand how the stalled() function on the motor works. I run a motor at dc of 100, and hold the wheel so that it cannot move.
But the stalled function doesn't fire, indeed whatever I do I don't seem able to get it to return True?
I tried with less power, but still not able to get anything out of this function.
#!/usr/bin/env pybricks-micropython
from pybricks import ev3brick as brick
from pybricks.ev3devices import Motor
from pybricks.parameters import Port, Stop
left_motor = Motor(Port.B)
speed = 800
# option 1
left_motor.dc(100)
# option 2
#left_motor.run_until_stalled(speed, Stop.HOLD, 100)
while True:
if left_motor.stalled():
print("stalled")
If I use option 1: the motor runs, I hold it until it stops, nothing reported. I let go and off it goes again.
If I use option 2: the motor runs, I hold it, it stops. But at no point do I see a report saying it stalled.

A motor is stalled when it can't reach its target speed or angle, despite applying the maximum duty cycle.
Your example can be adapted like this:
#!/usr/bin/env pybricks-micropython
from pybricks.ev3devices import Motor
from pybricks.parameters import Port
from pybricks.tools import wait
# Initialize the motor
left_motor = Motor(Port.B)
# Start running the motor at 500 deg/s
left_motor.run(500)
# Do nothing until we are stalled
while not left_motor.stalled():
wait(10)
# Stop the motor
left_motor.stop()
This example is equivalent to the one-liner left_motor.run_until_stalled(500). The manual approach can be useful if you want to extend it to multiple motors.
The dc() method in the question does not set a target speed or angle; it sets the duty cycle directly, so there is no stall information.
Note: the left_motor.stalled() method is instead accessible through left_motor.control.stalled() as of Pybricks version Pybricks 2.0. It is in public beta only as of March 2020, so I'm not sure the version reported in the original post in August 2019 is correct.

Related

How do I get immediate response between motors and sensors?

I am new to pybricks and have found very little documentation to help answer my own query. I have written what I thought would be a simple program to spin my robot on the spot until the UltrasonicSensor sees something. It will then push forwards. If it is pushed backwards and sees a black line, it should try and swing out of the way.
The following code "works", but it's response to the Ultrasonic and Light sensors is significantly delayed:
#!/usr/bin/env pybricks-micropython
from pybricks.hubs import EV3Brick
from pybricks.ev3devices import Motor, ColorSensor, UltrasonicSensor
from pybricks.parameters import Port
from pybricks.tools import wait
ev3 = EV3Brick()
eyes = UltrasonicSensor(Port.S2)
left_motor = Motor(Port.B)
right_motor = Motor(Port.A)
right_light = ColorSensor(Port.S1)
left_light = ColorSensor(Port.S4)
while True:
if right_light.reflection() < 50:
ev3.speaker.say('black')
left_motor.run(500)
right_motor.run(-100)
wait(2000)
left_motor.run(500)
right_motor.run(500)
wait(1000)
if eyes.distance() > 200:
left_motor.run(500)
right_motor.run(-500)
else:
left_motor.run(-500)
right_motor.run(-500)
I can see in the (limited) documentation that you can apparently change motor settings but I can't find direction on how to do this (or even if it would be useful). Any help would be appreciated.
ev3.speaker.say(text) synthesizes speech as it goes. This is fun, but it is very slow. This is especially noticeable in a control loop like yours.
I'd recommend using ev3.speaker.beep() instead. You could even select the frequency based on the reflection value so you can "hear" what the sensor "sees".
So the problem was that I used the "run" command to move the motors. Run appears to have an acceleration and deceleration component.
I used "dc" instead of "run" and the motors instantly respond to the sensor data now.

Problems with Micropython command time_pulse_us on Microbit

I would like to ask some help from you. I'm trying to connect a distance sensor to my microbit but when I use the command "time_pulse_us" it always gives -2 or -1. I read the documentation, I understand the meaning of those numbers but I think there's a problem with the command or probably I'm using it the wrong way.
In that regard, I wrote a simple snippet to test the command. Could you tell me what's wrong with it?
from microbit import * //to import microbit modules
from machine import * //to import the time_pulse_us command
while True:
pin1.write_digital(0)
time = time_pulse_us(pin2, 1) //to begin the timing
pin1.write_digital(1) //this pin is connected to an LED
sleep(1000)
value = pin2.read_digital() //gives 1, as this pin is reading the voltage from the led
pin1.write_digital(0) //this will make the time_pulse command to end timing
display.scroll(time) //it should display the duration of the pulse.
//Displays -2 instead.
display.scroll(value) //gives 1, as expected
Why is this not working?
time_pulse_us() runs sequentially, not in the background, so at the call it will wait 1 second for the pin to reach 1, which it will not do, hence time will be set to -2, before the program goes on to the next command write_digital(1).

Best way to catch Low State of GPIO in an endless script

Can you please tell me what's the best way to catch a low state (or falling-edge more precisely) of GPIO in an endless script?
To be clear I will run this script at boot (in the bg) and everytime when a user will push a button (connected to this GPIO) this will put this pin at LOW state. I want to detect each one of them and perform actions at every push.
I already have this code but it will consume to much CPU I think... I need sth like an interrupt in my mind :
import RPi.GPIO as GPIO
#Set GPIO numbering scheme to pinnumber
GPIO.setmode(GPIO.BCM)
#setup pin 4 as an input
GPIO.setup(4,GPIO.IN)
# To read the state
While true:
state = GPIO.input(4)
if state:
print('on')
else:
print('off')
EDIT
Here the pinout by BCM or BOARD, I will work with BCM
As you can the the pin number is 4 because my push button is on GPIO4.
Still get off all the time with your code or constant detection of edge event with the code of #jp-jee
EDIT
#!/usr/bin/env python3
import time
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BCM)
GPIO.setup(4,GPIO.IN)
def Callback(channel):
print('pushed')
GPIO.add_event_detect(4, GPIO.FALLING, callback = Callback, bouncetime = 300)
while(True):
time.sleep(1)
Now my code print always pushed when the button is released and print nothing when I push it...
Take a look at the documentation of raspberry-gpio-python.
What you want is GPIO.add_event_detect(channel, GPIO.RISING) in combination with a callback function.
Since you're using a button, you also need to consider bouncing.
In the end, you will end up with something like this (taken from the linked website):
def my_callback(channel):
print('This is a edge event callback function!')
GPIO.add_event_detect(channel, GPIO.FALLING, callback=my_callback, bouncetime=200)
Have you tried to use interrupts?
import time
import RPi.GPIO as GPIO
GPIO.setup(4, GPIO.IN)
def Callback(channel):
state = GPIO.input(channel)
if state:
print('on')
else:
print('off')
GPIO.add_event_detect(4, GPIO.FALLING, callback = Callback, bouncetime = 300)
while(True):
time.sleep(1)

How to add conditional statement to a flame sensor?

I wanted to output 'flame detected' or 'no flame detected' but I don't know how to implement this in a code. I've seen codes from google but they are using
GPIO.add_event_detect(17,GPIO.RISING, callback = mycallback, bouncetime = 600)
which only works when it is detecting flame.
In your example you are detecting a rising edge (the pin goes from low to high). A falling edge is the opposite so you could fire a separate callback with:
GPIO.add_event_detect(17, GPIO.FALLING, callback=other_callback, bouncetime=600)
However, I'd suggest you use GPIO Zero instead of RPi.GPIO as you may find it more intuitive:
from gpiozero import InputDevice
sensor = InputDevice(17)
while True:
sensor.wait_for_active()
print("Fire!")
sensor.wait_for_inactive()
print("No fire")
You might find that the logic is reversed (says "fire" when there's no fire) in which case use InputDevice(17, pull_up=True)
Alternatively to the code above you could use if sensor.is_active or use the callbacks mechanism sensor.when_activated = callback. See the gpiozero docs for more info.

Running two or more motors at the same time with PiFace Relay Plus and Motor Extra

I have a question. I have a Raspberry Pi connected to PiFace Relay Plus and PiFace Motor Extra. Is it possible to run two or more motors at the same time?
I have no problem with running one motor:
import pifacerelayplus
import time
pfr = pifacerelayplus.PiFaceRelayPlus(pifacerelayplus.MOTOR_DC)
pfr.motors[0].forward()
time.sleep(5)
pfr.motors[0].coast()
I also managed to run one motor and than next one:
import pifacerelayplus
import time
pfr = pifacerelayplus.PiFaceRelayPlus(pifacerelayplus.MOTOR_DC)
pfr.motors[0].forward()
time.sleep(5)
pfr.motors[0].coast()
time.sleep(2)
pfr.motors[1].forward()
time.sleep(5)
time.motors[1].coast()
But I can't find out how to make both motors run at the same time. I tried this code, but that only runs the first motor, than the program ends and the first motor still runs and never stops. The second motor never starts to run.
import pifacerelayplus
import time
pfr = pifacerelayplus.PiFaceRelayPlus(pifacerelayplus.MOTOR_DC)
pfr.motors[0].forward()
pfr.motors[1].forward()
time.sleep(5)
pfr.motors[0].coast()
pfr.motors[1].coast()
I even tried to create another variable "prf2" for second motor, but it didn't help neither. I'm glad for any help.
Are you getting any error when running the program? Looking at the source code of pifacerelayplus, it is INTENDED to fail if you give two motor commands within 0.15 seconds, on the grounds that the startup surge of two motors at once is likely to be more than your power supply can handle. A couple of short sleep()s should avoid this issue.