powered-up
powered-up copied to clipboard
Investigate Impact of supporting WeDo 2.0
Lesson Learnt from #48
Note: Note to everyone: I do not have the hardware and have in the foreseeable future no interest in purchasing them. If you want support for this hardware in this library, please contribute. I will guide you through the process and assist with everything needed.
Managed to get this working with my Bluegiga dongle and the new 88012 hubs that came with the Liebherr Excavator, but it was not simple to make work as the Bluegiga acts as a singleton on a serial port and this meant some of the async connection tasks broke... I got around it by making a separate repository and translating to/from the sharpbrick objects, but this will need cleaning up. The TechnicMediumHub ports work for the internals (LED light, etc), but the motors appear to be on different ports, so am probing the HubAttachedIO to try and work out where they are... Anyway, https://github.com/Sodoshi/SharpBrick.PoweredUp.Bluegiga is where I am at...
@Sodoshi can you open a separate Issue for this. Very interested in how this further evolves. I was confused first but then I realized you talk about Control+ and not WeDo 2.0.
The motors however, should work if the internals work. A bit concerned about that. Maybe post a example. The motors self-register at the beginning of the connection. Maybe you loose some characteristics notification at the beginning.
I also took a mental note that the Bluetooth Abstraction is now an external contract. At the current early state, I cannot promise that I - or you 😀 - will not break it.
I've just started using the library with my Mercedes Zetros Truck .
I think it has this motor (it's the bottom one):
It is reported on the hub as device type 'Motor (id 1)'. Which doesn't seem supported.
I would love to help adding support for this motor, I am an experienced c# programmer, but I have no clue what needs to be done here :)
@mennolodder
So this is the "LEGO 6290182 - 21980 - Electric, Motor WeDo 2.0 Medium". But it seems to operate on a Technic Medium Hub. Which is good since the communication partner of this library is the hub not the motor.
The first thing you should do, is use the command line to dump the motor self-description. Let us see what that provides.
https://github.com/sharpbrick/powered-up/blob/master/docs/development/adding-new-device.md
I'm afraid for my system the commandline tool hangs on receiving messages (also if I do poweredup device list):
I'm sure it is on port 2, the others motors I can access.
>poweredup device dump-static-port -p 2
Scan Started. Please select the Hub (using a number keys or 'q' to terminate):
1: TechnicMediumHub (with address 158897338045109)
1
Selected TechnicMediumHub with key 1
Discover Port 2. Receiving Messages ...
........
That is ugly. I do not have a device and my knowledge is a bit rusty to read raw traces. Maybe you can try first running the it on the raw Lego Wireless Protocol (that is the Bluetooth Library) and write power on/off into the device.
The technic motors have three levels. Try using the raw messages against the WeDo 2.0 motor. That might work. The README has a tutorial.
BasicMotorwhich is purely power based usingStartPowerAsyncwithPortOutputCommandStartPowerMessageTachoMotorwhich extend with speed usingStartSpeedAsyncwithPortOutputCommandStartSpeedMessageAbsoluteMotorwhich extend with position.
Yeah, the poweredup device list fails always, unless I unplug all motors (so just the hub with nothing attached).
I will give that a try, might take a while to get back to you though. Thx for all the quick responses.
I just run the tool on TechnicXLargeMotor on the TechnicMediumHub on port A. Still works here. No idea if they screwed up the firmware maybe meanwhile.
I have checked the code with the JavaScript library. They support the WeDo 2.0 motor. However, that library is not as "sophisticated" as this library (I tried to implement the protocol not just make the devices work). That is the reason that library works. Which is good news, because it can work in theory.
I also checked there the code (~~either 1 or~~ 2) and both only support power messages with this function. In this library the equivalent function is here. So I am quite confident that this message will work on the raw protocol.
Once we have evaluated which functions work, we can try to build a device which ignores the discoverability and provides just the methods. Let us create then a new issue (other than this) and run a release (v5.0 hopefully) including it.
ps: there was a "typo" above it was PortOutputCommandStartPower2Message but it should be PortOutputCommandStartPowerMessage
I tried your suggestions, I think :)
With respect to the raw messages, do you mean: 'Connect to Hub and Send a Message and retrieving answers (directly on protocol layer)'. This seems to be a bit outdated, as it doesn't compile (and the examples dir doesn't have a compiling example that does the same).
For instance: BluetoothKernel does not have a property BluetoothAddress SendMessageAsync requires a LegoWirelessMessage
EDIT the issue below seems to be the same as #190
I adapted it like this, but it fails to connect:
var kernel = scope.ServiceProvider.GetService<BluetoothKernel>(); // .BluetoothAddress = bluetoothAddress;
kernel.BluetoothDeviceInfo = new PoweredUpBluetoothDeviceInfoWithMacAddress()
{
MacAddressAsUInt64 = adressAsLong
};
var protocol = scope.ServiceProvider.GetService<ILegoWirelessProtocol>();
await protocol.ConnectAsync(); // nullpointer thrown here
The error is:
System.ArgumentNullException
HResult=0x80004003
Message=Value cannot be null. Arg_ParamName_Name
Source=SharpBrick.PoweredUp.WinRT
StackTrace:
at SharpBrick.PoweredUp.WinRT.WinRTPoweredUpBluetoothDevice..ctor(BluetoothLEDevice device)
at SharpBrick.PoweredUp.WinRT.WinRTPoweredUpBluetoothAdapter.<GetDeviceAsync>d__1.MoveNext()
at SharpBrick.PoweredUp.Bluetooth.BluetoothKernel.<ConnectAsync>d__12.MoveNext()
at SharpBrick.PoweredUp.Protocol.LegoWirelessProtocol.<ConnectAsync>d__15.MoveNext()
at LegoCar.Program.<RunRawSingleMotor>d__4.MoveNext() in C:\Repos\lego_automated_car\LegoCar\LegoCar\Program.cs:line 78
at LegoCar.Program.<MainAsync>d__2.MoveNext() in C:\Repos\lego_automated_car\LegoCar\LegoCar\Program.cs:line 41
at LegoCar.Program.Main(String[] args) in C:\Repos\lego_automated_car\LegoCar\LegoCar\Program.cs:line 26
I'm pretty sure I know my devices mac adress, I converted it to long using the helper class BlueGigaBLEHelper, double checked it with the Hub.PrimaryMacAddress and it is the same as the hex input there. (note that I use the default WinRt bluetooth). However, I also can't connect to it using but it also fails when I try to connect it in the way the ExampleTwoHubsMotorControl does:
var host = serviceProvider.GetService<PoweredUpHost>();
//var hub = await host.DiscoverAsync<TechnicMediumHub>();
var hub = await host.CreateByStateAsync<TechnicMediumHub>(adressAsLong);
await hub.ConnectAsync();
Also after that I can't use the style var motor = technicMediumHub.A.GetDevice<BasicMotor>();, because BasicMotor is not a IPoweredUpDevice.
Yeah seems like the README needs some update. Discovery and DI scope creation works easiest when re-using host logic and host functions. I justed tested this with a TechnicMediumHub and a TechnicXLargeMotor.
using SharpBrick.PoweredUp;
using Microsoft.Extensions.DependencyInjection; // SharpBrick.PoweredUp uses the DI system
using Microsoft.Extensions.Logging;
using SharpBrick.PoweredUp.Bluetooth;
using SharpBrick.PoweredUp.Protocol;
using SharpBrick.PoweredUp.Protocol.Messages; // SharpBrick.PoweredUp also logs stuff
var serviceProvider = new ServiceCollection()
.AddLogging()
.AddPoweredUp()
#if WINDOWS
.AddWinRTBluetooth() // using WinRT Bluetooth on Windows (separate NuGet SharpBrick.PoweredUp.WinRT; others are available)
#endif
.BuildServiceProvider();
// getting utilities
var bt = serviceProvider.GetService<IPoweredUpBluetoothAdapter>();
var host = serviceProvider.GetService<PoweredUpHost>();
// discover a LWP bluetooth device
var tcs = new TaskCompletionSource<ILegoWirelessProtocol>();
bt.Discover(async deviceInfo =>
{
if (!tcs.Task.IsCompleted)
{
var p = host.CreateProtocol(deviceInfo);
tcs.SetResult(p);
}
});
var protocol = await tcs.Task;
// connect the protocol
await protocol.ConnectAsync();
// send a raw message which should work with ANY motor connected to a hub
var response = await protocol.SendPortOutputCommandAsync(new PortOutputCommandStartPowerMessage(
0, // PORT A
PortOutputCommandStartupInformation.ExecuteImmediately, PortOutputCommandCompletionInformation.CommandFeedback,
100
)
{
HubId = 0, // as if we ever see another one
});
await Task.Delay(2000);
await protocol.DisconnectAsync();
with the following csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks Condition=" '$(OS)' == 'Windows_NT' ">net8.0-windows10.0.19041.0;net8.0</TargetFrameworks>
<TargetFrameworks Condition=" '$(OS)' != 'Windows_NT' ">net8.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\SharpBrick.PoweredUp\SharpBrick.PoweredUp.csproj" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net8.0-windows10.0.19041.0' ">
<ProjectReference Include="..\..\src\SharpBrick.PoweredUp.WinRT\SharpBrick.PoweredUp.WinRT.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration.CommandLine" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="8.0.0" />
</ItemGroup>
</Project>
and the following invocation
dotnet run -f net8.0-windows10.0.19041.0
I fixed the problem with the manual connection, the address conversion is wrong (at least for the WinRT version). I just used the commandline logged one from poweredup device list. that works.
I ran your example, it works!
I get the following errors (I updated the not supported messages in the Device.GetStaticPortInfoMessages to add some info, I can make a PR if you want it):
// Device 'RgbLight' does not support Systemtype 'LegoWeDo20_WeDoHub' on Hardware '1.0.0.0' and Software '1.0.0.0' // Device 'Current' does not support Systemtype 'LegoWeDo20_WeDoHub' on Hardware '1.0.0.0' and Software '1.0.0.0' // Device 'Voltage' does not support Systemtype 'LegoWeDo20_WeDoHub' on Hardware '1.0.0.0' and Software '1.0.0.0'
You also get those? (I also get them if I run without any motors, should be the same as you have).
Added PR #194 which is needed to support this.
The output of discovery now works (for this, not yet for my large lineair motors.
Discover Ports Function: 9 / 9
LegoTechnic_MediumHub
##################################################
0B-00-43-02-01-01-01-00-00-01-00
05-00-43-02-02
11-00-44-02-00-00-4C-50-46-32-2D-4D-4D-4F-54-4F-52
0E-00-44-02-00-01-00-00-C8-C2-00-00-C8-42
0E-00-44-02-00-02-00-00-C8-C2-00-00-C8-42
0E-00-44-02-00-03-00-00-C8-C2-00-00-C8-42
0A-00-44-02-00-04-00-00-00-00
08-00-44-02-00-05-00-10
0A-00-44-02-00-80-01-00-04-00
##################################################
Are you interested in a PR that adds some improved error messages?
And here is the output from device list:
- Port: 0x02 / 2
- IOTypeId: Motor / 0x0001 / 1
Revision: SW: 0.0.0.0, HW: 0.0.0.0
Capabilities: Output
ModeCombinations: []
- Mode 0: Name: LPF2-MMOTOR, Symbol: , Capability: Output
- DataSet: 1x SByte, TotalFigures: 4, Decimals: 0
Output Mapping: Absolute
Raw Min: -100, Max: 100
Pct Min: -100, Max: 100 (pass-through)
SI Min: -100, Max: 100 (pass-through)
I lost a bit oversight of what you all did. Let me try to summarize
- The device enumeration hung because of the string termination bug you fixed in #194. Is that closing #193 ?
- In #193 you talk about the technic linear motors showing an error during discovery. That would be a different issue than what you originally describe in #193 and fix in #194, correct? If yes, we should put it in a separate issue. I tend to agree, that this is a firmware version issue. There are unknown / undocumented modes in the motor I used originally (see docs) and patching them out over time makes sense. Please consider also updating the docs.
- Since discovery works, we could now create a new device for the motor. It would be awesome if you file a pull request for this! I can not test it due to lack of hardware.
- Improved Error Messages or any type of ideas are always appreciated.
Yeah I can image, I'm doing everything at the same time and didn't really know what I am doing lol :)
- [x] WeDo devicelist / port dump hang and threw an error because of the fix in #194
- [x] The TechnicLargeLinearMotor device list hang because it used different static port info messages, described here #193 . I will make a PR for that later.
- [x] a, Perhaps the port dump device list would also be helped if it didn't use the static messages here, creating issue
- [ ] I made good progress with supporting the WeDo motor, I guess we should name this 'SimpleMediumLinearMotor'. Can you easily see from the dump /device list if this is a BasicMotor or anything of the other motor types? If not I will investigate.
- [x] Will make a separate PR for some better error messages.
used different static port info messages
This should not be. Either we discover the device OR use static port inform messages. Do not remember why the behavior is like that. Device enumeration not necessarily need to discover the devices but my just pretty print our existing knowledge.
Fix what you find, do PRs. I completely open to it. If you want to do some major rework, discuss it first.
Ok I'll make a separate ticket for that then, I really think it did both. It was requesting port mode information and got a reply for 5 modes, but actually requested information for 6 modes, which is the amount in the static port info.