microsoft / jacdac

Device and service catalogs for Jacdac.
https://aka.ms/jacdac
Creative Commons Attribution 4.0 International
66 stars 25 forks source link

Role manager (servo?) assignment behaviour is different when Makecode is/isn't connected #1335

Closed ross-inksmith closed 10 months ago

ross-inksmith commented 10 months ago

In the attached video (https://github.com/microsoft/jacdac/assets/88154489/bf2d7dc8-3e97-45f6-be6d-22576faf6d69), I've already downloaded the code (https://makecode.microbit.org/_JKyP30b9TY8a) to the microbit. With the computer disconnected I turn the breakout board on. Only the left servo turns (in firmware they're created in order 0:left 1:middle 2:right). Once I connect the computer/Makecode, the left and middle servos start turning. They will keep doing this even if I disconnect the computer again. If I start with the microbit connected to Makecode, then both servos will work on startup.

Additional detail

In the above build, using our wrapper extension I've included code to wait for roleManagerServer.isConnected() in the extension's main.ts before querying (in order) left-, middle-, and rightServo.getAngle() in an attempt to bias the roleManager's assignments. This seems to work in Makecode, but a disconnected microbit will assign "leftServo" to the first service, while the other two roles can be assigned in either order with no obvious pattern.

Our issue has a lot in common with #1293; we can control the servos using the dashboard controls and they move within their full range without delay, but only the first service will respond to the first block (e.g. servo1.run(x) will always work). Other services on the board only start repsonding after the second block (e.g. servo2.run(x) has no effect the first time, but subseuqent calls to servo2.anything work as normal)

There may be something in the physical init process that influences this; when the board is powered on any servo on the left pin (first one in firmware) moves to 0° while the others do not move. I can turn the servos easily so they're unpowered. Our board doesn't do any detection of servos, so sends signals based on pin regardless of what is plugged in.

Another demonstration is here (https://makecode.microbit.org/_e4UJXP23ER3j) using the MS jacdac extension. If I use the Add Simulators button, it will create three simulators and assign them like this: servo-assignment-sims Note that the simulators have a 0-180° range but alternate between servo1:0/45° and servo3:45/113° instead of servo1:90/135° and servo3:45/90° Using the same code and plugging in our breakout board (which has 270° servos), it will now lay itself like this: servo-assignment-fwd The same code now causes the servos to alternate between 0/90° and 90/180° instead of 67.5/135° and 135/202.5°

The above might be related to #1327? I've also found during testing that the starting ranges for our servos initially default to 0-180° before getting updated later.

tballmsft commented 10 months ago

@ross-inksmith - please try swapping the order of the updates to servo1 and servo3 in the program, if you see that servo3 is responding and servo1 is not, then we likely have some packet loss on the STM side.

let _ = modules.servo1.angle()
_ = modules.servo2.angle()
_ = modules.servo3.angle()
basic.forever(function () {
    modules.servo3.run(-50)
    modules.servo1.run(50)
    basic.pause(1000)
    modules.servo3.run(0)
    modules.servo1.run(0)
    basic.pause(1000)
})
tballmsft commented 10 months ago

You can also try putting a pause between the updates to the two servos:

let _ = modules.servo1.angle()
_ = modules.servo2.angle()
_ = modules.servo3.angle()
basic.forever(function () {
    modules.servo1.run(50)
   basic.pause(10)
    modules.servo3.run(-50)
    basic.pause(1000)
    modules.servo1.run(0)
   basic.pause(10)
    modules.servo3.run(0)
    basic.pause(1000)
})
tballmsft commented 10 months ago

Also try this and look in console (when connected over WebUSB)

let _ = modules.servo1.angle()
_ = modules.servo2.angle()
_ = modules.servo3.angle()

basic.forever(function () {
    console.log("servo 1 " + modules.servo1.isConnected())
    console.log("servo 2 " + modules.servo2.isConnected())
    console.log("servo 3 " + modules.servo3.isConnected())
    modules.servo1.run(50)
    modules.servo3.run(-50)
    basic.pause(1000)
    modules.servo1.run(0)
    modules.servo3.run(0)
    basic.pause(1000)
})
tballmsft commented 10 months ago

@ross-inksmith - I got the servos and am having issues with power on the round board. The green light is on but none of the lights on servos are on. I'm powering the board via its USB port (not Lipo) and power the microbit separately via battery. Any ideas?

Wait a second, the green light is on, with the switch in the OFF position. When I switch it ON, the led turns red.

I am very confused. I think we need a call.

tballmsft commented 10 months ago

Running the following program (observations follow) on the micro:bit, plugged into the round board, with no servos plugged in:

input.onButtonPressed(Button.A, function () {
    modules.servo1.run(50)
    modules.servo3.run(-50)
    basic.pause(1000)
    modules.servo1.run(0)
    modules.servo3.run(0)
    basic.pause(1000)
})

modules.servo1.start()
modules.servo2.start()
modules.servo3.start()

input.onButtonPressed(Button.B, function () {
    console.log("servo 1 " + modules.servo1.isConnected())
    console.log("servo 2 " + modules.servo2.isConnected())
    console.log("servo 3 " + modules.servo3.isConnected())
})

When there is no power to the STM, on a press of B button, I get

binding: 3 servers
servo 1 false
servo 2 false
servo 3 false

which is expected.

When I give power to the STM, on press of B button, I get

binding: 3 servers
servo 1 true
servo 2 true
servo 3 true

This is expected, even without servos plugged in, because the STM is hard coded to advertised three servo services, per design, regardless of what is plugged in. So, it seems role management is working fine.

tballmsft commented 10 months ago

OK. All three servos are working with the following program (note the delay between commands) in code below. Make sure you are running micro:bit off of battery (not USB).

https://github.com/microsoft/jacdac/assets/10767146/6aef58da-c255-4668-bd5e-35953d6c3133

input.onButtonPressed(Button.A, function () {
    modules.servo1.run(50)
    basic.pause(100)
    modules.servo2.run(50)
    basic.pause(100)
    modules.servo3.run(50)
})

input.onButtonPressed(Button.B, function () {
    modules.servo1.run(0)
    basic.pause(100)
    modules.servo2.run(0)
    basic.pause(100)
    modules.servo3.run(0)
})

modules.servo1.start()
modules.servo2.start()
modules.servo3.start()

input.onButtonPressed(Button.AB, function () {
    console.log("servo 1 " + modules.servo1.isConnected())
    console.log("servo 2 " + modules.servo2.isConnected())
    console.log("servo 3 " + modules.servo3.isConnected())
})

So, as I expected, we are losing packets somewhere. Based on the trace files provided before, which showed all expected packets on the bus, my bet is that the STM is dropping packets. But we should analyze more. In the meantime, we can figure out how much of a delay to introduce between commands to different servos.

I will send a video shortly. The other thing I discovered is that it seems that the servos are only powered by the Lipo. When I unplug the Lipo and put power on the round board's USB, there doesn't seem to be any power to the servo.

tballmsft commented 10 months ago

OK, now for bringing the delay between run commands down (run off of battery only):

const delay = 2

input.onButtonPressed(Button.A, function () {
    modules.servo1.run(50)
    basic.pause(delay)
    modules.servo2.run(50)
    basic.pause(delay)
    modules.servo3.run(50)
})

input.onButtonPressed(Button.B, function () {
    modules.servo1.run(0)
    basic.pause(delay)
    modules.servo2.run(0)
    basic.pause(delay)
    modules.servo3.run(0)
})

modules.servo1.start()
modules.servo2.start()
modules.servo3.start()
tballmsft commented 10 months ago

Note that when micro:bit is connected via USB, first servo often doesn't start. Seems like more packet loss. Works fine when micro:bit on battery.

tballmsft commented 10 months ago

Another thing to consider: if we want to send same command to all three servos, we can use a broadcast, which will use just a single packet.

ross-inksmith commented 10 months ago

@ross-inksmith - I got the servos and am having issues with power on the round board. The green light is on but none of the lights on servos are on. I'm powering the board via its USB port (not Lipo) and power the microbit separately via battery. Any ideas?

Wait a second, the green light is on, with the switch in the OFF position. When I switch it ON, the led turns red.

I am very confused. I think we need a call.

There are two LEDs by the port on the board. One is green to indicate a charge, the other is a low battery/low voltage light. When driving multiple power hungry devices (two servos and a LED ring, for example), or if the servos draw extra current to create more torque, or if some combination of those things happen when the battery isn't fully charged, that can also turn the red light on.

And that's right, the board only works when switched on regardless of whether or not it's charging - it only draws power from the battery or port; it won't charge through the microbit/edge connector.

So, as I expected, we are losing packets somewhere. Based on the trace files provided before, which showed all expected packets on the bus, my bet is that the STM is dropping packets. But we should analyze more. In the meantime, we can figure out how much of a delay to introduce between commands to different servos.

We were wondering if this was an issue in #1312, which is also STM

pelikhan commented 10 months ago

Try again; fix pushed.

tballmsft commented 10 months ago

It is working now - Peli made pxt-jacdac/servo last chatty - please update to latest pxt-jacdac

https://makecode.microbit.org/_5MLWYMf5WX97

tballmsft commented 10 months ago

This was not a problem with the STM - pxt-jacdac was too chatty.

brian-inksmith commented 10 months ago

Initial tests seem good. Continuing to test.

ross-inksmith commented 10 months ago

I'm unexpectedly out of office today and can't test with our devices today. Reading the diff looks cool though

Creating a new project and adding the jacdac extension includes v1.9.19, but using our extension loads a cached version (in this case, v1.9.13). Emptying the cache via dev tools doesn't clear it. I can only get it by through chrome://settings/content/all?search=data

Unfortunately, a lot of classrooms use chromebooks that don't have access to chrome settings or incognito mode. Is there another way? I don't see anything on https://makecode.com/extensions/pxt-json or in https://github.com/microsoft/pxt/blob/master/pxtlib/package.ts to add a version annotation

pelikhan commented 10 months ago

how do you publish release of your extension? do you use mkc bump? The mkc cli bumps the cloud to flush its caches otherwise the makecode cloud has a 24h cache for extensions.

ross-inksmith commented 10 months ago

I do use mkc bump. I don't think the cloud cache is the issue, since it would use the current version of jacdac in incognito and the issue went away by clearing local data. I've created an issue with pxt https://github.com/microsoft/pxt/issues/9740

pelikhan commented 10 months ago

@ross-inksmith can you share again, it won't load

ross-inksmith commented 10 months ago

sorry, which?

pelikhan commented 10 months ago

i have the repro. THe makecode share link in your original description broke somehow.

ross-inksmith commented 10 months ago

I think it broke because I cleaned up the branch it was using. I've made this to the best of my memory https://makecode.microbit.org/_df5Udy6drWiE. It uses the version of our extension from two weeks ago but jacdac v1.9.20

pelikhan commented 10 months ago

In 1.9.23, the ?srvo=yy hint is now used when binding roles. So you'll want to add those to your role names as such:

https://github.com/microsoft/pxt-jacdac/blob/master/servo/test.microbit.ts#L4

This gives me a stable assignment with 1, 2 or 3 servos when the shield is connected. Let's how far we go with this. Eventually we could also add the product id hint; it's just that i might not have at hand when assigning roles.

@ross-inksmith

ross-inksmith commented 10 months ago

This is excellent! Working as advertised for me so far

pelikhan commented 10 months ago

Let's see how far you get with testing with that fix.

ramseyball commented 10 months ago

If there are other problems, please file a separate issue for each problem.